mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 21:01:08 +02:00
✨ feat(matrix): add drag & drop file upload to chat
- Add DropZoneOverlay component with visual feedback - Implement drag events on desktop and mobile chat pages - Support multi-file upload (files uploaded sequentially) - Show overlay only when a chat room is selected Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
40a3c89852
commit
7c0bc735b9
4 changed files with 131 additions and 3 deletions
|
|
@ -0,0 +1,27 @@
|
|||
<script lang="ts">
|
||||
import { UploadSimple } from '@manacore/shared-icons';
|
||||
|
||||
interface Props {
|
||||
visible: boolean;
|
||||
}
|
||||
|
||||
let { visible }: Props = $props();
|
||||
</script>
|
||||
|
||||
{#if visible}
|
||||
<div
|
||||
class="fixed inset-0 z-50 bg-primary/10 backdrop-blur-sm flex items-center justify-center pointer-events-none"
|
||||
>
|
||||
<div
|
||||
class="bg-white dark:bg-zinc-800 rounded-2xl p-8 shadow-2xl border-2 border-dashed border-primary flex flex-col items-center gap-4"
|
||||
>
|
||||
<div class="p-4 rounded-full bg-primary/10">
|
||||
<UploadSimple class="h-12 w-12 text-primary" />
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<p class="text-lg font-semibold text-foreground">Datei hier ablegen</p>
|
||||
<p class="text-sm text-muted-foreground mt-1">Bilder, Videos oder Dateien</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
@ -7,3 +7,4 @@ export { default as MessageInput } from './MessageInput.svelte';
|
|||
export { default as TypingIndicator } from './TypingIndicator.svelte';
|
||||
export { default as CreateRoomDialog } from './CreateRoomDialog.svelte';
|
||||
export { default as RoomSettingsPanel } from './RoomSettingsPanel.svelte';
|
||||
export { default as DropZoneOverlay } from './DropZoneOverlay.svelte';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,12 @@
|
|||
<script lang="ts">
|
||||
import { matrixStore, type SimpleMessage } from '$lib/matrix';
|
||||
import { RoomList, RoomHeader, Timeline, MessageInput } from '$lib/components/chat';
|
||||
import {
|
||||
RoomList,
|
||||
RoomHeader,
|
||||
Timeline,
|
||||
MessageInput,
|
||||
DropZoneOverlay,
|
||||
} from '$lib/components/chat';
|
||||
import CreateRoomDialog from '$lib/components/chat/CreateRoomDialog.svelte';
|
||||
import RoomSettingsPanel from '$lib/components/chat/RoomSettingsPanel.svelte';
|
||||
import SearchDialog from '$lib/components/chat/SearchDialog.svelte';
|
||||
|
|
@ -20,6 +26,10 @@
|
|||
let showSearch = $state(false);
|
||||
let showForward = $state(false);
|
||||
|
||||
// Drag & Drop state
|
||||
let isDragging = $state(false);
|
||||
let dragCounter = $state(0);
|
||||
|
||||
// Reply/Edit/Forward state
|
||||
let replyTo = $state<SimpleMessage | null>(null);
|
||||
let editMessage = $state<SimpleMessage | null>(null);
|
||||
|
|
@ -132,6 +142,41 @@
|
|||
function handleCallReject() {
|
||||
// Call rejected - UI will update automatically
|
||||
}
|
||||
|
||||
// Drag & Drop handlers
|
||||
function handleDragEnter(e: DragEvent) {
|
||||
e.preventDefault();
|
||||
dragCounter++;
|
||||
if (e.dataTransfer?.types.includes('Files')) {
|
||||
isDragging = true;
|
||||
}
|
||||
}
|
||||
|
||||
function handleDragLeave(e: DragEvent) {
|
||||
e.preventDefault();
|
||||
dragCounter--;
|
||||
if (dragCounter === 0) {
|
||||
isDragging = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleDragOver(e: DragEvent) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
async function handleDrop(e: DragEvent) {
|
||||
e.preventDefault();
|
||||
isDragging = false;
|
||||
dragCounter = 0;
|
||||
|
||||
const files = e.dataTransfer?.files;
|
||||
if (!files?.length || !matrixStore.currentRoom) return;
|
||||
|
||||
// Upload all dropped files sequentially
|
||||
for (const file of files) {
|
||||
await matrixStore.sendFile(file);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if isMobile}
|
||||
|
|
@ -228,7 +273,16 @@
|
|||
</aside>
|
||||
|
||||
<!-- Main Chat Area -->
|
||||
<main class="flex flex-1 min-h-0 flex-col overflow-hidden bg-background">
|
||||
<main
|
||||
class="flex flex-1 min-h-0 flex-col overflow-hidden bg-background relative"
|
||||
ondragenter={handleDragEnter}
|
||||
ondragleave={handleDragLeave}
|
||||
ondragover={handleDragOver}
|
||||
ondrop={handleDrop}
|
||||
>
|
||||
<!-- Drop Zone Overlay -->
|
||||
<DropZoneOverlay visible={isDragging && !!matrixStore.currentRoom} />
|
||||
|
||||
{#if matrixStore.currentRoom}
|
||||
<!-- Room Header -->
|
||||
<RoomHeader
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { matrixStore, type SimpleMessage } from '$lib/matrix';
|
||||
import { RoomHeader, Timeline, MessageInput } from '$lib/components/chat';
|
||||
import { RoomHeader, Timeline, MessageInput, DropZoneOverlay } from '$lib/components/chat';
|
||||
import RoomSettingsPanel from '$lib/components/chat/RoomSettingsPanel.svelte';
|
||||
import SearchDialog from '$lib/components/chat/SearchDialog.svelte';
|
||||
import ForwardMessageDialog from '$lib/components/chat/ForwardMessageDialog.svelte';
|
||||
|
|
@ -26,6 +26,10 @@
|
|||
let showSearch = $state(false);
|
||||
let showForward = $state(false);
|
||||
|
||||
// Drag & Drop state
|
||||
let isDragging = $state(false);
|
||||
let dragCounter = $state(0);
|
||||
|
||||
// Reply/Edit/Forward state
|
||||
let replyTo = $state<SimpleMessage | null>(null);
|
||||
let editMessage = $state<SimpleMessage | null>(null);
|
||||
|
|
@ -178,6 +182,41 @@
|
|||
function handleCallReject() {
|
||||
// Call rejected - UI will update automatically
|
||||
}
|
||||
|
||||
// Drag & Drop handlers
|
||||
function handleDragEnter(e: DragEvent) {
|
||||
e.preventDefault();
|
||||
dragCounter++;
|
||||
if (e.dataTransfer?.types.includes('Files')) {
|
||||
isDragging = true;
|
||||
}
|
||||
}
|
||||
|
||||
function handleDragLeave(e: DragEvent) {
|
||||
e.preventDefault();
|
||||
dragCounter--;
|
||||
if (dragCounter === 0) {
|
||||
isDragging = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleDragOver(e: DragEvent) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
async function handleDrop(e: DragEvent) {
|
||||
e.preventDefault();
|
||||
isDragging = false;
|
||||
dragCounter = 0;
|
||||
|
||||
const files = e.dataTransfer?.files;
|
||||
if (!files?.length || !matrixStore.currentRoom) return;
|
||||
|
||||
// Upload all dropped files sequentially
|
||||
for (const file of files) {
|
||||
await matrixStore.sendFile(file);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Full-screen chat view for mobile -->
|
||||
|
|
@ -188,7 +227,14 @@
|
|||
ontouchmove={handleTouchMove}
|
||||
ontouchend={handleTouchEnd}
|
||||
ontouchcancel={handleTouchCancel}
|
||||
ondragenter={handleDragEnter}
|
||||
ondragleave={handleDragLeave}
|
||||
ondragover={handleDragOver}
|
||||
ondrop={handleDrop}
|
||||
>
|
||||
<!-- Drop Zone Overlay -->
|
||||
<DropZoneOverlay visible={isDragging && !!matrixStore.currentRoom} />
|
||||
|
||||
<!-- Swipe-back visual indicator -->
|
||||
{#if isSwiping && swipeProgress > 0}
|
||||
<div
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue