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
@@ -7,6 +7,8 @@ import { AVATAR_GRADIENT_MAP } from '../../utils/gradients';
import { AVATAR_COLORS } from '@backspace/shared';
import type { AvatarColor, CheckInviteResponse, InstanceInfoResponse } from '@backspace/shared';
import { api, RateLimitError } from '../../api/client';
import { useTransferStore } from '../../stores/transferStore';
import { waitForTransferAttachment } from '../../utils/waitForTransfer';
// Single-source regex for extracting a bare invite token from a pasted full URL.
// Token format: 22 chars base64url ([A-Za-z0-9_-]).
@@ -355,8 +357,9 @@ export function RegisterPage() {
let finalUser = response.user;
if (!skip && avatarFile) {
try {
const attachment = await api.uploads.upload(avatarFile);
finalUser = await api.users.update({ avatar: attachment.filename });
const tid = await useTransferStore.getState().startUpload(avatarFile, { tray: false });
const { filename } = await waitForTransferAttachment(tid);
finalUser = await api.users.update({ avatar: filename });
} catch {
// Avatar upload failed — user can set it later in settings
}