feat(web): RegisterPage avatar uses transferStore + waitForTransfer helper

Replaces the legacy /api/uploads call in RegisterPage with the tus-based
transferStore path. Extends Transfer to persist the server-assigned
filename (not just attachmentId) since downstream consumers store
attachment.filename on the user/space record.

- transferStore: rename setAttachmentId -> setAttachmentRef(id, attachmentId, filename)
  and add attachmentFilename field to Transfer
- both startUpload + resumeUpload onSuccess paths now record filename
- new utils/waitForTransfer.ts: waitForTransferAttachment(transferId)
  returns {attachmentId, filename}, with immediate-terminal handling
- RegisterPage: silent (tray:false) upload via transferStore, awaits
  the helper, passes the server filename to api.users.update
This commit is contained in:
Jannis Braun
2026-05-02 16:40:50 +02:00
parent 48d199515a
commit 727c51f659
5 changed files with 54 additions and 10 deletions
+6 -5
View File
@@ -29,6 +29,7 @@ export interface Transfer {
tusExpiresAt?: number;
fileHandleId?: string;
attachmentId?: string;
attachmentFilename?: string;
uploaderUserId?: string;
// Download-specific
@@ -59,7 +60,7 @@ interface TransferStoreActions {
updateProgress: (id: string, loaded: number) => void;
setError: (id: string, error: TransferError) => void;
setTusUrl: (id: string, url: string, expiresAt: number) => void;
setAttachmentId: (id: string, attachmentId: string) => void;
setAttachmentRef: (id: string, attachmentId: string, filename: string) => void;
remove: (id: string) => void;
startUpload: (file: Blob, opts: { channelId?: string; tray?: boolean; origin?: string; fileHandleId?: string }) => Promise<string>;
@@ -181,11 +182,11 @@ export const useTransferStore = create<TransferStore>()(
return { transfers: next };
}),
setAttachmentId: (id, attachmentId) => set((s) => {
setAttachmentRef: (id, attachmentId, filename) => set((s) => {
const t = s.transfers.get(id);
if (!t) return s;
const next = new Map(s.transfers);
next.set(id, { ...t, attachmentId });
next.set(id, { ...t, attachmentId, attachmentFilename: filename });
return { transfers: next };
}),
@@ -245,7 +246,7 @@ export const useTransferStore = create<TransferStore>()(
try {
const body = payload.lastResponse?.getBody?.() ?? '';
const att = JSON.parse(body) as Attachment;
get().setAttachmentId(id, att.id);
get().setAttachmentRef(id, att.id, att.filename);
get().setState_(id, 'completed');
get().updateProgress(id, fileLike.size);
} catch (e) {
@@ -346,7 +347,7 @@ export const useTransferStore = create<TransferStore>()(
try {
const body = payload.lastResponse?.getBody?.() ?? '';
const att = JSON.parse(body) as Attachment;
get().setAttachmentId(id, att.id);
get().setAttachmentRef(id, att.id, att.filename);
get().setState_(id, 'completed');
} catch (e) {
const msg = e instanceof Error ? e.message : 'Resume completed but parse failed';