From 348800bfcae2d370ae7c74ce38616cf54cb95219 Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Sat, 2 May 2026 17:11:41 +0200 Subject: [PATCH] feat(web): channel-header transfer indicator + global tray panel --- .../web/src/components/layout/MainContent.tsx | 7 + .../components/layout/MobileChatScreen.tsx | 2 + .../components/layout/TransferIndicator.tsx | 183 ++++++++++++++++++ 3 files changed, 192 insertions(+) create mode 100644 packages/web/src/components/layout/TransferIndicator.tsx diff --git a/packages/web/src/components/layout/MainContent.tsx b/packages/web/src/components/layout/MainContent.tsx index 81a2522e..e5b6b5d2 100644 --- a/packages/web/src/components/layout/MainContent.tsx +++ b/packages/web/src/components/layout/MainContent.tsx @@ -15,6 +15,7 @@ import { Avatar } from '../ui/Avatar'; import { useVoiceStore } from '../../stores/voiceStore'; import { wsSend } from '../../hooks/useWebSocket'; import { MemberListToggleButton } from './MemberListToggleButton'; +import { TransferIndicator } from './TransferIndicator'; import { isSelf, parseFederatedUsername } from '../../utils/identity'; import { Tooltip } from '../ui/Tooltip'; import { joinVoiceChannel } from '../../utils/voice'; @@ -131,6 +132,7 @@ export function MainContent() { )}
+
@@ -228,6 +230,7 @@ export function MainContent() { +
@@ -252,6 +255,7 @@ export function MainContent() {
Select a channel
+
@@ -276,6 +280,7 @@ export function MainContent() { {channel.name}
+
@@ -319,6 +324,7 @@ export function MainContent() { )}
+
@@ -369,6 +375,7 @@ export function MainContent() { +
diff --git a/packages/web/src/components/layout/MobileChatScreen.tsx b/packages/web/src/components/layout/MobileChatScreen.tsx index 4086b3ea..d1d85fe8 100644 --- a/packages/web/src/components/layout/MobileChatScreen.tsx +++ b/packages/web/src/components/layout/MobileChatScreen.tsx @@ -6,6 +6,7 @@ import { useAuthStore } from '../../stores/authStore'; import { MessageList } from '../chat/MessageList'; import { MessageInput } from '../chat/MessageInput'; import { TypingIndicator } from '../chat/TypingIndicator'; +import { TransferIndicator } from './TransferIndicator'; interface MobileChatScreenProps { params?: Record; @@ -61,6 +62,7 @@ export function MobileChatScreen({ params }: MobileChatScreenProps) { {isDm ? channelName : `# ${channelName}`} + {!isDm && ( + {open && ( +
+
+ + {visible.length} transfer{visible.length === 1 ? '' : 's'} + + +
+ {visible.length === 0 && ( +
+ No active transfers. +
+ )} +
+ {visible.map((t) => ( + (t.type === 'upload' ? pause(t.id) : pauseDl(t.id))} + onResume={() => { + if (t.type === 'upload') void resume(t.id); + else void resumeDl(t.id); + }} + onAbort={() => (t.type === 'upload' ? abort(t.id) : abortDl(t.id))} + onDismiss={() => remove(t.id)} + /> + ))} +
+
+ )} + + ); +} + +interface TransferRowProps { + transfer: Transfer; + onPause: () => void; + onResume: () => void; + onAbort: () => void; + onDismiss: () => void; +} + +function TransferRow({ transfer, onPause, onResume, onAbort, onDismiss }: TransferRowProps) { + const pct = + transfer.progress.total > 0 + ? Math.min(100, Math.round((transfer.progress.loaded / transfer.progress.total) * 100)) + : 0; + const barColor = + transfer.state === 'failed' + ? 'bg-accent-rose' + : transfer.state === 'completed' + ? 'bg-accent-mint' + : 'bg-accent-mint/70'; + return ( +
+
+ + {transfer.type === 'upload' ? '↑' : '↓'} + + {transfer.file.name} +
+
+
+
+
+ + {transfer.state === 'completed' + ? 'Done' + : transfer.state === 'failed' + ? `Failed: ${transfer.error?.message ?? 'unknown'}` + : transfer.state === 'aborted' + ? 'Aborted' + : `${fmt(transfer.progress.loaded)} / ${fmt(transfer.progress.total)}`} + + + {transfer.state === 'active' && ( + + )} + {transfer.state === 'paused' && ( + + )} + {(transfer.state === 'active' || + transfer.state === 'paused' || + transfer.state === 'queued') && ( + + )} + {(transfer.state === 'completed' || + transfer.state === 'failed' || + transfer.state === 'aborted') && ( + + )} + +
+
+ ); +}