fix: avatar upload during registration completes before redirect
Registration avatar upload raced with AuthRedirect — setting the Zustand token triggered navigation before the upload could finish. Now the token is stored in localStorage (for API auth) but not in Zustand until the avatar upload completes, so the page stays mounted throughout. Extracts initSession() from login/register for reuse.
This commit is contained in:
@@ -40,8 +40,7 @@ export function RegisterPage() {
|
|||||||
const [isRegistering, setIsRegistering] = useState(false);
|
const [isRegistering, setIsRegistering] = useState(false);
|
||||||
const [retryAfter, setRetryAfter] = useState(0);
|
const [retryAfter, setRetryAfter] = useState(0);
|
||||||
|
|
||||||
const register = useAuthStore((s) => s.register);
|
const initSession = useAuthStore((s) => s.initSession);
|
||||||
const updateProfile = useAuthStore((s) => s.updateProfile);
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const redirect = searchParams.get('redirect');
|
const redirect = searchParams.get('redirect');
|
||||||
@@ -192,18 +191,28 @@ export function RegisterPage() {
|
|||||||
try {
|
try {
|
||||||
const dn = skip ? undefined : displayName.trim() || undefined;
|
const dn = skip ? undefined : displayName.trim() || undefined;
|
||||||
const ac = skip ? undefined : avatarColor;
|
const ac = skip ? undefined : avatarColor;
|
||||||
await register(username.trim(), password, dn, ac);
|
|
||||||
|
|
||||||
// Upload avatar if chosen (non-fatal — account already created)
|
// Step 1: Register via API — store token in localStorage for API auth,
|
||||||
|
// but NOT in Zustand yet so AuthRedirect doesn't fire prematurely
|
||||||
|
const response = await api.auth.register({
|
||||||
|
username: username.trim(), password, displayName: dn, avatarColor: ac,
|
||||||
|
});
|
||||||
|
localStorage.setItem('backspace_token', response.token);
|
||||||
|
|
||||||
|
// Step 2: Upload avatar while still on the register page
|
||||||
|
let finalUser = response.user;
|
||||||
if (!skip && avatarFile) {
|
if (!skip && avatarFile) {
|
||||||
try {
|
try {
|
||||||
const attachment = await api.uploads.upload(avatarFile);
|
const attachment = await api.uploads.upload(avatarFile);
|
||||||
await updateProfile({ avatar: attachment.filename });
|
finalUser = await api.users.update({ avatar: attachment.filename });
|
||||||
} catch {
|
} catch {
|
||||||
// Avatar upload failed — user can set it later in settings
|
// Avatar upload failed — user can set it later in settings
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Step 3: Activate session — sets Zustand token, triggers AuthRedirect
|
||||||
|
initSession(response.token, finalUser);
|
||||||
|
|
||||||
if (redirect && redirect.startsWith('/') && !redirect.startsWith('//')) {
|
if (redirect && redirect.startsWith('/') && !redirect.startsWith('//')) {
|
||||||
navigate(redirect);
|
navigate(redirect);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ interface AuthState {
|
|||||||
user: User | null;
|
user: User | null;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
error: string | null;
|
error: string | null;
|
||||||
|
initSession: (token: string, user: User) => void;
|
||||||
login: (username: string, password: string) => Promise<void>;
|
login: (username: string, password: string) => Promise<void>;
|
||||||
register: (username: string, password: string, displayName?: string, avatarColor?: string) => Promise<void>;
|
register: (username: string, password: string, displayName?: string, avatarColor?: string) => Promise<void>;
|
||||||
logout: () => void;
|
logout: () => void;
|
||||||
@@ -42,15 +43,18 @@ export const useAuthStore = create<AuthState>((set, get) => ({
|
|||||||
isLoading: false,
|
isLoading: false,
|
||||||
error: null,
|
error: null,
|
||||||
|
|
||||||
|
initSession: (token: string, user: User) => {
|
||||||
|
resetUserStores();
|
||||||
|
localStorage.setItem('backspace_token', token);
|
||||||
|
set({ token, user, isLoading: false });
|
||||||
|
useInstanceStore.getState().autoConnectAll().catch(() => {});
|
||||||
|
},
|
||||||
|
|
||||||
login: async (username: string, password: string) => {
|
login: async (username: string, password: string) => {
|
||||||
set({ isLoading: true, error: null });
|
set({ isLoading: true, error: null });
|
||||||
try {
|
try {
|
||||||
const response = await api.auth.login({ username, password });
|
const response = await api.auth.login({ username, password });
|
||||||
resetUserStores();
|
get().initSession(response.token, response.user);
|
||||||
localStorage.setItem('backspace_token', response.token);
|
|
||||||
set({ token: response.token, user: response.user, isLoading: false });
|
|
||||||
// Auto-connect to remote instances (fire-and-forget)
|
|
||||||
useInstanceStore.getState().autoConnectAll().catch(() => {});
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
set({ isLoading: false, error: err instanceof Error ? err.message : 'Login failed' });
|
set({ isLoading: false, error: err instanceof Error ? err.message : 'Login failed' });
|
||||||
throw err;
|
throw err;
|
||||||
@@ -61,11 +65,7 @@ export const useAuthStore = create<AuthState>((set, get) => ({
|
|||||||
set({ isLoading: true, error: null });
|
set({ isLoading: true, error: null });
|
||||||
try {
|
try {
|
||||||
const response = await api.auth.register({ username, password, displayName, avatarColor });
|
const response = await api.auth.register({ username, password, displayName, avatarColor });
|
||||||
resetUserStores();
|
get().initSession(response.token, response.user);
|
||||||
localStorage.setItem('backspace_token', response.token);
|
|
||||||
set({ token: response.token, user: response.user, isLoading: false });
|
|
||||||
// Auto-connect to remote instances (fire-and-forget)
|
|
||||||
useInstanceStore.getState().autoConnectAll().catch(() => {});
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
set({ isLoading: false, error: err instanceof Error ? err.message : 'Registration failed' });
|
set({ isLoading: false, error: err instanceof Error ? err.message : 'Registration failed' });
|
||||||
throw err;
|
throw err;
|
||||||
|
|||||||
Reference in New Issue
Block a user