Sign in is the default: the landing names the account, and an admin lands on /admin

This commit is contained in:
2026-09-18 08:02:57 +07:00
parent 7b79e49c20
commit 2917c034ed
2 changed files with 52 additions and 22 deletions
+7 -4
View File
@@ -290,10 +290,13 @@ export function Workspace() {
// Recipes made in this session, guest ones included. A guest's copy lives
// here and nowhere else, so reloading the page is what deletes it.
const [created, setCreated] = useState<Recipe[]>([]);
// null = closed. /app?auth=1 opens the studio's dialog straight on the sign-up
// tab; the landing page runs the same dialog in place instead of linking here.
const [authMode, setAuthMode] = useState<'login' | 'signup' | null>(() =>
(new URLSearchParams(window.location.search).has('auth') ? 'signup' : null));
// null = closed. /app?auth=1 opens the studio's dialog straight on the sign-in
// tab (sign-in is the default; ?auth=signup asks for the other one); the
// landing page runs the same dialog in place instead of linking here.
const [authMode, setAuthMode] = useState<'login' | 'signup' | null>(() => {
const asked = new URLSearchParams(window.location.search).get('auth');
return asked ? (asked === 'signup' ? 'signup' : 'login') : null;
});
const renderToken = useRef(0);
// Which previewMax the bytes in previewBytes were made at, so the upgrade
+45 -18
View File
@@ -117,7 +117,7 @@ export function Landing() {
// studio: the visitor keeps their scroll position and lands back on the reel.
const [auth, setAuth] = useState<'login' | 'signup' | null>(() => {
const asked = new URLSearchParams(window.location.search).get('auth');
return asked === 'login' ? 'login' : asked ? 'signup' : null;
return asked === 'signup' ? 'signup' : asked ? 'login' : null;
});
const [uploading, setUploading] = useState(false);
const filePick = useRef<HTMLInputElement>(null);
@@ -138,11 +138,16 @@ export function Landing() {
// The community half of the reel: whoever is signed in gets a picker, and
// everybody sees the result on the next load. A guest is sent to sign up.
const reload = useCallback(() => {
api.me().then((r) => setUser(r.user)).catch(() => undefined);
api.listPhotos().then((r) => setPhotos(r.photos)).catch(() => undefined);
return api.me().then((r) => {
setUser(r.user);
return r.user;
}).catch(() => null);
}, []);
useEffect(reload, [reload]);
useEffect(() => {
reload();
}, [reload]);
const contribute = useCallback(async (file: File) => {
setUploading(true);
@@ -284,10 +289,24 @@ export function Landing() {
{lang.toUpperCase()}
</button>
</span>
{/* Register: the dialog opens here, on the page the visitor is on. */}
<button type="button" className="lp-btn lp-sm" data-key="nav-signup" onClick={() => setAuth('signup')}>
{t('nav.signup')}
</button>
{/* Sign in is the default: the dialog opens here, on the page the
visitor is on, and flips to sign-up from its own toggle. Once
signed in the same slot names the account, so the session is
visible on the page that took the credentials. */}
{user ? (
<a
className="lp-btn lp-sm"
data-key="nav-account"
href={user.admin ? '/admin' : '/app'}
title={user.email}
>
{user.email.split('@')[0]}
</a>
) : (
<button type="button" className="lp-btn lp-sm" data-key="nav-login" onClick={() => setAuth('login')}>
{t('nav.login')}
</button>
)}
<button type="button" className="lp-btn lp-sm lp-primary" onClick={() => showToast(toastDownload)}>
{c({ en: 'Download RecipesCam', vi: 'Tải RecipesCam' })}
</button>
@@ -299,14 +318,20 @@ export function Landing() {
<div className="lp-sheet">
{nav.map((entry) => <a key={entry.href} href={entry.href} onClick={() => setMenu(false)}>{c(entry.label)}</a>)}
<a href="/app" onClick={() => setMenu(false)}>{c({ en: 'Open studio', vi: 'Mở studio' })}</a>
<button
type="button"
className="lp-btn"
data-key="sheet-signup"
onClick={() => { setMenu(false); setAuth('signup'); }}
>
{t('nav.signup')}
</button>
{user ? (
<a className="lp-btn" data-key="sheet-account" href={user.admin ? '/admin' : '/app'} onClick={() => setMenu(false)}>
{user.email.split('@')[0]}
</a>
) : (
<button
type="button"
className="lp-btn"
data-key="sheet-login"
onClick={() => { setMenu(false); setAuth('login'); }}
>
{t('nav.login')}
</button>
)}
<button
type="button"
className="lp-btn lp-primary"
@@ -361,8 +386,8 @@ export function Landing() {
{uploading ? t('photo.uploading') : t('photo.contribute')}
</button>
) : (
/* A guest gets the same button; it opens the sign-up dialog. */
<button type="button" className="lp-btn lp-primary" data-key="contribute" onClick={() => setAuth('signup')}>
/* A guest gets the same button; it opens the sign-in dialog. */
<button type="button" className="lp-btn lp-primary" data-key="contribute" onClick={() => setAuth('login')}>
{t('photo.contribute')}
</button>
)}
@@ -670,7 +695,9 @@ export function Landing() {
onClose={() => setAuth(null)}
onDone={() => {
setAuth(null);
reload();
// An admin signs in here to moderate, so send them on to the page
// that asked for the session in the first place.
reload().then((u) => u?.admin && window.location.assign('/admin'));
}}
/>
) : null}