Polish: plain unfound word-search chips + no signed-out flash on refresh
- Word Search: unfound words were tinted (accent-soft background) like found ones, so the remaining words were hard to spot as the board filled. Unfound chips are now plain (transparent + a light outline); found words keep their grid colour. Easy to see what's left. - Auth: a refresh briefly flashed the signed-out header until /api/auth/me returned. Now the last-known user is cached and hydrated immediately, so the signed-in UI paints at once; the session is still revalidated every load (a stale/expired one corrects within a beat) and the cache is cleared on logout. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,22 @@
|
|||||||
import { SvelteSet } from 'svelte/reactivity';
|
import { SvelteSet } from 'svelte/reactivity';
|
||||||
import { getJSON, postJSON, delJSON } from './api.js';
|
import { getJSON, postJSON, delJSON } from './api.js';
|
||||||
|
|
||||||
export const auth = $state({ user: null, ready: false });
|
// Cache the last-known user so a refresh paints the signed-in UI immediately
|
||||||
|
// instead of flashing "signed out" for the /api/auth/me round-trip. The session
|
||||||
|
// is still revalidated on every load (see refresh) — a stale/expired one just
|
||||||
|
// corrects within a beat instead of greeting you logged-out every time.
|
||||||
|
const AUTH_CACHE = 'goodnews:auth_user';
|
||||||
|
function _loadUser() {
|
||||||
|
try { return JSON.parse(localStorage.getItem(AUTH_CACHE) || 'null'); } catch { return null; }
|
||||||
|
}
|
||||||
|
function _saveUser(u) {
|
||||||
|
try {
|
||||||
|
if (u) localStorage.setItem(AUTH_CACHE, JSON.stringify(u));
|
||||||
|
else localStorage.removeItem(AUTH_CACHE);
|
||||||
|
} catch { /* ignore */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const auth = $state({ user: _loadUser(), ready: false });
|
||||||
export const savedIds = new SvelteSet(); // reactive set of saved article ids
|
export const savedIds = new SvelteSet(); // reactive set of saved article ids
|
||||||
export const followKeys = new SvelteSet(); // reactive set of "kind:value" follows
|
export const followKeys = new SvelteSet(); // reactive set of "kind:value" follows
|
||||||
|
|
||||||
@@ -16,6 +31,7 @@ export async function refresh() {
|
|||||||
auth.user = null;
|
auth.user = null;
|
||||||
} finally {
|
} finally {
|
||||||
auth.ready = true;
|
auth.ready = true;
|
||||||
|
_saveUser(auth.user);
|
||||||
}
|
}
|
||||||
if (auth.user) { await loadSaved(); await loadFollows(); }
|
if (auth.user) { await loadSaved(); await loadFollows(); }
|
||||||
else { savedIds.clear(); followKeys.clear(); }
|
else { savedIds.clear(); followKeys.clear(); }
|
||||||
@@ -99,6 +115,7 @@ export async function logout() {
|
|||||||
// e.g. after delete-account or sign-out-everywhere).
|
// e.g. after delete-account or sign-out-everywhere).
|
||||||
export function clearLocal() {
|
export function clearLocal() {
|
||||||
auth.user = null;
|
auth.user = null;
|
||||||
|
_saveUser(null);
|
||||||
savedIds.clear();
|
savedIds.clear();
|
||||||
followKeys.clear();
|
followKeys.clear();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -221,8 +221,12 @@
|
|||||||
.plabel { text-transform: uppercase; letter-spacing: 0.12em; font-size: 0.62rem; font-family: var(--label);
|
.plabel { text-transform: uppercase; letter-spacing: 0.12em; font-size: 0.62rem; font-family: var(--label);
|
||||||
color: var(--muted); text-align: center; margin: 0 0 10px; }
|
color: var(--muted); text-align: center; margin: 0 0 10px; }
|
||||||
.words { list-style: none; display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; padding: 0; margin: 0; }
|
.words { list-style: none; display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; padding: 0; margin: 0; }
|
||||||
|
/* Unfound words are plain (transparent + outline) so it's easy to see what's
|
||||||
|
left as the board fills; found words get their grid colour via inline style. */
|
||||||
.words li { font-family: var(--label); font-size: 0.82rem; letter-spacing: 0.04em; color: var(--ink);
|
.words li { font-family: var(--label); font-size: 0.82rem; letter-spacing: 0.04em; color: var(--ink);
|
||||||
padding: 4px 11px; border-radius: 999px; background: var(--accent-soft); transition: background 0.2s ease; }
|
padding: 4px 11px; border-radius: 999px; background: transparent;
|
||||||
|
border: 1px solid var(--line); transition: background 0.2s ease, border-color 0.2s ease; }
|
||||||
|
.words li.got { border-color: transparent; }
|
||||||
.hint { text-align: center; color: var(--muted); font-size: 0.84rem; margin: 0; }
|
.hint { text-align: center; color: var(--muted); font-size: 0.84rem; margin: 0; }
|
||||||
.result { text-align: center; }
|
.result { text-align: center; }
|
||||||
.rmark { font-family: var(--serif); font-style: italic; color: var(--accent-deep); font-size: 1.2rem; margin: 0 0 12px; }
|
.rmark { font-family: var(--serif); font-style: italic; color: var(--accent-deep); font-size: 1.2rem; margin: 0 0 12px; }
|
||||||
|
|||||||
Reference in New Issue
Block a user