Skip to content

Commit e932648

Browse files
committed
fix: converts to Show, adds stale-fetch guard and onboarding redirect
1 parent 6b49e32 commit e932648

File tree

4 files changed

+49
-31
lines changed

4 files changed

+49
-31
lines changed

src/app/components/onboarding/OnboardingWizard.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ export default function OnboardingWizard() {
4141
}
4242

4343
onMount(() => {
44+
if (config.onboardingComplete) {
45+
window.location.replace("/dashboard");
46+
return;
47+
}
4448
void loadOrgs();
4549
});
4650

src/app/components/onboarding/RepoSelector.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export default function RepoSelector(props: RepoSelectorProps) {
3131
const [filter, setFilter] = createSignal("");
3232
const [orgStates, setOrgStates] = createSignal<OrgRepoState[]>([]);
3333
const [loadedCount, setLoadedCount] = createSignal(0);
34+
let effectVersion = 0;
3435

3536
// Initialize org states and fetch repos on mount / when selectedOrgs change
3637
createEffect(() => {
@@ -40,6 +41,9 @@ export default function RepoSelector(props: RepoSelectorProps) {
4041
// it happens to work today (before any await) but would silently break
4142
// if the check were moved after an await.
4243
const preloadedEntries = props.orgEntries;
44+
// Version counter: if selectedOrgs changes while fetches are in-flight,
45+
// stale callbacks check this and bail out instead of writing to state.
46+
const version = ++effectVersion;
4347
if (orgs.length === 0) {
4448
setOrgStates([]);
4549
setLoadedCount(0);
@@ -86,6 +90,8 @@ export default function RepoSelector(props: RepoSelectorProps) {
8690
}
8791
}
8892

93+
if (version !== effectVersion) return;
94+
8995
const typeMap = new Map<string, "org" | "user">(
9096
entries.map((e) => [e.login, e.type])
9197
);
@@ -95,12 +101,14 @@ export default function RepoSelector(props: RepoSelectorProps) {
95101
const type = typeMap.get(org) ?? "org";
96102
try {
97103
const repos = await fetchRepos(client, org, type);
104+
if (version !== effectVersion) return;
98105
setOrgStates((prev) =>
99106
prev.map((s) =>
100107
s.org === org ? { ...s, type, repos, loading: false } : s
101108
)
102109
);
103110
} catch (err) {
111+
if (version !== effectVersion) return;
104112
const message =
105113
err instanceof Error ? err.message : "Failed to load repositories";
106114
setOrgStates((prev) =>
@@ -111,7 +119,9 @@ export default function RepoSelector(props: RepoSelectorProps) {
111119
)
112120
);
113121
} finally {
114-
setLoadedCount((c) => c + 1);
122+
if (version === effectVersion) {
123+
setLoadedCount((c) => c + 1);
124+
}
115125
}
116126
});
117127

src/app/pages/LoginPage.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export default function LoginPage() {
2020
</div>
2121

2222
<button
23+
type="button"
2324
onClick={handleLogin}
2425
class="w-full flex items-center justify-center gap-3 px-4 py-2.5 bg-gray-900 dark:bg-gray-700 text-white rounded-lg font-medium hover:bg-gray-700 dark:hover:bg-gray-600 transition-colors focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2"
2526
>

src/app/pages/OAuthCallback.tsx

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createSignal, onMount } from "solid-js";
1+
import { createSignal, onMount, Show } from "solid-js";
22
import { useNavigate } from "@solidjs/router";
33
import { setAuth, validateToken, clearAuth } from "../stores/auth";
44
import { OAUTH_STATE_KEY, OAUTH_RETURN_TO_KEY } from "../lib/oauth";
@@ -77,7 +77,37 @@ export default function OAuthCallback() {
7777
return (
7878
<div class="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900">
7979
<div class="max-w-sm w-full mx-4 text-center">
80-
{error() ? (
80+
<Show
81+
when={error()}
82+
fallback={
83+
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-md p-8 flex flex-col items-center gap-4">
84+
<svg
85+
class="animate-spin h-8 w-8 text-gray-500"
86+
xmlns="http://www.w3.org/2000/svg"
87+
fill="none"
88+
viewBox="0 0 24 24"
89+
aria-label="Loading"
90+
>
91+
<circle
92+
class="opacity-25"
93+
cx="12"
94+
cy="12"
95+
r="10"
96+
stroke="currentColor"
97+
stroke-width="4"
98+
/>
99+
<path
100+
class="opacity-75"
101+
fill="currentColor"
102+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
103+
/>
104+
</svg>
105+
<p class="text-gray-600 dark:text-gray-400">
106+
Completing sign in...
107+
</p>
108+
</div>
109+
}
110+
>
81111
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-md p-8 flex flex-col items-center gap-4">
82112
<p class="text-red-600 dark:text-red-400 font-medium">{error()}</p>
83113
<a
@@ -87,34 +117,7 @@ export default function OAuthCallback() {
87117
Return to sign in
88118
</a>
89119
</div>
90-
) : (
91-
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-md p-8 flex flex-col items-center gap-4">
92-
<svg
93-
class="animate-spin h-8 w-8 text-gray-500"
94-
xmlns="http://www.w3.org/2000/svg"
95-
fill="none"
96-
viewBox="0 0 24 24"
97-
aria-label="Loading"
98-
>
99-
<circle
100-
class="opacity-25"
101-
cx="12"
102-
cy="12"
103-
r="10"
104-
stroke="currentColor"
105-
stroke-width="4"
106-
/>
107-
<path
108-
class="opacity-75"
109-
fill="currentColor"
110-
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
111-
/>
112-
</svg>
113-
<p class="text-gray-600 dark:text-gray-400">
114-
Completing sign in...
115-
</p>
116-
</div>
117-
)}
120+
</Show>
118121
</div>
119122
</div>
120123
);

0 commit comments

Comments
 (0)