Implement signin as guest

This commit is contained in:
Takeshi Kimata
2024-05-25 13:22:21 +09:00
parent d6e7c98025
commit 25402bd3ba
9 changed files with 72 additions and 62 deletions

View File

@@ -34,6 +34,7 @@
"signin": "Sign in",
"or_signup": "You need sign up?",
"or_signin": "You need sign in?",
"signin_as_guest": "Or sign in as Guest",
"email": "Email",
"username": "User name",
"password": "Password",

View File

@@ -34,6 +34,7 @@
"signin": "サインイン",
"or_signup": "新規登録ページ",
"or_signin": "サインインページ",
"signin_as_guest": "ゲストとしてサインイン",
"email": "メールアドレス",
"username": "ユーザー名",
"password": "パスワード",

View File

@@ -5,7 +5,7 @@ import { TokenProps } from '@/types/user';
import { useRouter, usePathname } from '@/src/navigation';
const LOCAL_STORAGE_KEY = 'testplat-auth-token';
const privatePaths = ['/account'];
const privatePaths = ['/account', '/projects'];
function storeTokenToLocalStorage(token: TokenType) {
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(token));
@@ -31,6 +31,7 @@ const TokenProvider = ({ locale, children }: TokenProps) => {
const router = useRouter();
const pathname = usePathname();
const [hasRestoreFinished, setHasRestoreFinished] = useState(false);
const [token, setToken] = useState<TokenType>({
access_token: '',
user: null,
@@ -53,25 +54,33 @@ const TokenProvider = ({ locale, children }: TokenProps) => {
if (tokenString) {
const restoredToken = JSON.parse(tokenString);
setToken(restoredToken);
} else {
checkSignInPage();
}
};
const checkSignInPage = () => {
if (
privatePaths.some((path) => {
return path === pathname;
})
) {
router.push(`/account/signin`, { locale: locale });
}
setHasRestoreFinished(true);
};
useEffect(() => {
restoreTokenFromLocalStorage();
}, []);
useEffect(() => {
const isPrivatePath = () => {
return privatePaths.some((path) => {
return path === pathname;
});
};
const checkSignInPage = () => {
if (!hasRestoreFinished) {
return;
}
if (isPrivatePath() && !isSignedIn()) {
router.push(`/account/signin`, { locale: locale });
}
};
checkSignInPage();
}, [pathname, hasRestoreFinished]);
return <TokenContext.Provider value={tokenContext}>{children}</TokenContext.Provider>;
};

View File

@@ -1,5 +1,6 @@
import { UserType } from '@/types/user';
import Config from '@/config/config';
import { roles } from '@/config/selection';
const apiServer = Config.apiServer;
async function signUp(newUser: UserType) {
@@ -50,9 +51,35 @@ async function signIn(signInUser: UserType) {
}
}
async function signInAsGuest() {
const guestUser = {
id: null,
email: generateRandomEmail(),
password: 'guestpassword',
username: 'Guest',
role: roles.findIndex((entry) => entry.uid === 'user'),
avatarPath: '',
};
const token = await signUp(guestUser);
return token;
}
function generateRandomEmail() {
const randomStringLength = 10;
const characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
let randomString = '';
const charactersLength = characters.length;
for (let i = 0; i < randomStringLength; i++) {
randomString += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return `${randomString}@example.com`;
}
function getRandomAvatarPath() {
const randomIndex = Math.floor(Math.random() * avatars.length);
return avatars[randomIndex];
}
export { signUp, signIn, getRandomAvatarPath };
export { signUp, signIn, signInAsGuest, getRandomAvatarPath };

View File

@@ -6,7 +6,7 @@ import { Link } from '@/src/navigation';
import { ChevronRight, Eye, EyeOff } from 'lucide-react';
import { UserType, AuthMessages } from '@/types/user';
import { roles } from '@/config/selection';
import { signUp, signIn } from './authControl';
import { signUp, signIn, signInAsGuest } from './authControl';
import { isValidEmail, isValidPassword } from './validate';
import { TokenContext } from '../TokenProvider';
import { useRouter } from '@/src/navigation';
@@ -83,6 +83,13 @@ export default function AuthPage({ isSignup, messages, locale }: Props) {
router.push('/account', { locale: locale });
};
const handleSignInAsGuest = async () => {
const token = await signInAsGuest();
context.setToken(token);
context.storeTokenToLocalStorage(token);
router.push('/account', { locale: locale });
};
return (
<Card className="w-[480px] mt-16">
<CardHeader className="px-4 pt-4 pb-0 flex justify-between">
@@ -159,10 +166,18 @@ export default function AuthPage({ isSignup, messages, locale }: Props) {
}}
/>
)}
<div className="flex justify-end">
<Button color="primary" className="mt-3" onPress={validate}>
<div className="flex justify-end items-center mt-3">
<Button color="primary" onPress={validate}>
{messages.submitTitle}
</Button>
{!isSignup && (
<Button
className="ms-3 bg-gradient-to-tr from-pink-500 to-yellow-500 text-white shadow-lg"
onPress={handleSignInAsGuest}
>
{messages.signInAsGuest}
</Button>
)}
</div>
</form>
</CardBody>

View File

@@ -7,6 +7,7 @@ export default function Page(params: { locale: string }) {
title: t('signin'),
linkTitle: t('or_signup'),
submitTitle: t('signin'),
signInAsGuest: t('signin_as_guest'),
email: t('email'),
username: t('username'),
password: t('password'),

View File

@@ -1,31 +0,0 @@
'use client';
import { ProjectsMessages } from '@/types/project';
import { Link } from '@/src/navigation';
import { Button, Modal, ModalContent, ModalHeader, ModalFooter } from '@nextui-org/react';
type Props = {
isOpen: boolean;
onCancel: () => void;
messages: ProjectsMessages;
locale: string;
};
export default function NeedSignedInDialog({ isOpen, onCancel, messages, locale }: Props) {
return (
<Modal
isOpen={isOpen}
onOpenChange={() => {
onCancel();
}}
>
<ModalContent>
<ModalHeader className="flex flex-col gap-1">{messages.needSignedIn}</ModalHeader>
<ModalFooter>
<Link href={'/account/signin'} locale={locale}>
<Button color="primary">{messages.signIn}</Button>
</Link>
</ModalFooter>
</ModalContent>
</Modal>
);
}

View File

@@ -6,7 +6,6 @@ import { TokenContext } from '@/src/app/[locale]/TokenProvider';
import { ProjectType, ProjectsMessages } from '@/types/project';
import ProjectsTable from './ProjectsTable';
import ProjectDialog from './ProjectDialog';
import NeedSignedInDialog from './NeedSignedInDialog';
import { fetchProjects, createProject, updateProject, deleteProject } from './projectsControl';
export type Props = {
@@ -35,15 +34,9 @@ export default function ProjectsPage({ messages, locale }: Props) {
}, [context]);
// dialog
const [isNeedSignedInDialogOpen, setIsNeedSignedInDialogOpen] = useState(false);
const [isProjectDialogOpen, setIsProjectDialogOpen] = useState(false);
const [editingProject, setEditingProject] = useState<ProjectType | null>(null);
const openDialogForCreate = () => {
if (!context.isSignedIn()) {
setIsNeedSignedInDialogOpen(true);
return;
}
setIsProjectDialogOpen(true);
setEditingProject(null);
};
@@ -105,13 +98,6 @@ export default function ProjectsPage({ messages, locale }: Props) {
onSubmit={onSubmit}
messages={messages}
/>
<NeedSignedInDialog
isOpen={isNeedSignedInDialogOpen}
onCancel={() => setIsNeedSignedInDialogOpen(false)}
messages={messages}
locale={locale}
/>
</div>
);
}

View File

@@ -32,6 +32,7 @@ export type AuthMessages = {
title: string;
linkTitle: string;
submitTitle: string;
signInAsGuest: string;
email: string;
username: string;
password: string;