mirror of
https://github.com/Chevron7Locked/kima-hub.git
synced 2026-06-19 07:37:17 +00:00
fix(ux): resolve Soulseek search spinner wedge and onboarding 'already taken' dead-end
- Soulseek search relied solely on an SSE 'complete' event to clear its spinner; if that event was dropped (connection blip, backend never emits it) the search UI spun forever. Add a 45s fallback that force-completes the search so the user sees whatever results arrived; late results still stream in via the store subscription. - Onboarding's 'username already taken' path told the user to refresh, which can't recover the half-created account (the token never persisted). Instead attempt a login with the same credentials and continue: resume at step 2 if onboarding is unfinished, route home if already complete, or send to the normal sign-in for a 2FA account. A genuine password mismatch now gets a clear 'sign in instead' message rather than a dead end.
This commit is contained in:
@@ -104,9 +104,27 @@ export default function OnboardingPage() {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
// Check if user already exists
|
||||
if (message?.includes("already taken")) {
|
||||
setError(
|
||||
"Username already taken. If this is you, please refresh and continue where you left off.",
|
||||
);
|
||||
// Usually a refresh/retry race: the account was created but the
|
||||
// token never persisted client-side. Rather than dead-end on a
|
||||
// "refresh" instruction that can't recover the session, try
|
||||
// logging in with the same credentials and continue.
|
||||
try {
|
||||
const user = await api.login(username, password);
|
||||
if (user.requires2FA) {
|
||||
router.push("/login");
|
||||
return;
|
||||
}
|
||||
if (user.onboardingComplete) {
|
||||
router.push("/");
|
||||
return;
|
||||
}
|
||||
setStep(2);
|
||||
return;
|
||||
} catch {
|
||||
setError(
|
||||
"That username already exists and the password didn't match. If it's your account, sign in instead.",
|
||||
);
|
||||
}
|
||||
} else {
|
||||
setError(message || "Failed to create account");
|
||||
}
|
||||
|
||||
@@ -124,6 +124,18 @@ export function useSoulseekSearch({
|
||||
};
|
||||
}, [query, soulseekEnabled]);
|
||||
|
||||
// Fallback completion: the SSE "complete" event can be dropped (connection
|
||||
// blip, or the backend never emits it). Without a terminal signal
|
||||
// isSoulseekPolling stays true and the search UI spins forever. Force the
|
||||
// search complete after a ceiling so the user sees whatever results arrived
|
||||
// instead of an endless spinner; late results still stream in via the store
|
||||
// subscription, only the spinner is resolved.
|
||||
useEffect(() => {
|
||||
if (!hasActiveSearch || isComplete) return;
|
||||
const timeout = setTimeout(() => setIsComplete(true), 45000);
|
||||
return () => clearTimeout(timeout);
|
||||
}, [hasActiveSearch, isComplete]);
|
||||
|
||||
const handleDownload = useCallback(async (result: SoulseekResult) => {
|
||||
const downloadKey = `${result.username}:${result.path}`;
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user