feat(frontend): add discord callback page
this only handles existing accounts for now, still need to write an action function
This commit is contained in:
		
							parent
							
								
									116d0577a7
								
							
						
					
					
						commit
						ff22530f0a
					
				
					 13 changed files with 196 additions and 66 deletions
				
			
		|  | @ -6,6 +6,15 @@ export type AuthResponse = { | |||
| 	expires_at: string; | ||||
| }; | ||||
| 
 | ||||
| export type CallbackResponse = { | ||||
| 	has_account: boolean; | ||||
| 	ticket?: string; | ||||
| 	remote_username?: string; | ||||
| 	user?: User; | ||||
| 	token?: string; | ||||
| 	expires_at?: string; | ||||
| }; | ||||
| 
 | ||||
| export type AuthUrls = { | ||||
| 	discord?: string; | ||||
| 	google?: string; | ||||
|  |  | |||
|  | @ -23,6 +23,7 @@ import "./app.scss"; | |||
| import getLocalSettings from "./lib/settings.server"; | ||||
| import { LANGUAGE } from "~/env.server"; | ||||
| import { errorCodeDesc } from "./components/ErrorAlert"; | ||||
| import { Container } from "react-bootstrap"; | ||||
| 
 | ||||
| export const loader = async ({ request }: LoaderFunctionArgs) => { | ||||
| 	const meta = await serverRequest<Meta>("GET", "/meta"); | ||||
|  | @ -141,7 +142,9 @@ export default function App() { | |||
| 	return ( | ||||
| 		<> | ||||
| 			<Navbar meta={meta} user={meUser} settings={settings} /> | ||||
| 			<Outlet /> | ||||
| 			<Container> | ||||
| 				<Outlet /> | ||||
| 			</Container> | ||||
| 		</> | ||||
| 	); | ||||
| } | ||||
|  |  | |||
							
								
								
									
										84
									
								
								Foxnouns.Frontend/app/routes/auth.callback.discord/route.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								Foxnouns.Frontend/app/routes/auth.callback.discord/route.tsx
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,84 @@ | |||
| import { json, LoaderFunctionArgs } from "@remix-run/node"; | ||||
| import { type ApiError, ErrorCode } from "~/lib/api/error"; | ||||
| import serverRequest, { writeCookie } from "~/lib/request.server"; | ||||
| import { CallbackResponse } from "~/lib/api/auth"; | ||||
| import { Form as RemixForm, Link, useLoaderData } from "@remix-run/react"; | ||||
| import { Trans, useTranslation } from "react-i18next"; | ||||
| import Form from "react-bootstrap/Form"; | ||||
| import Button from "react-bootstrap/Button"; | ||||
| 
 | ||||
| export const loader = async ({ request }: LoaderFunctionArgs) => { | ||||
| 	const url = new URL(request.url); | ||||
| 
 | ||||
| 	const code = url.searchParams.get("code"); | ||||
| 	const state = url.searchParams.get("state"); | ||||
| 
 | ||||
| 	if (!code || !state) | ||||
| 		throw { status: 400, code: ErrorCode.BadRequest, message: "Missing code or state" } as ApiError; | ||||
| 
 | ||||
| 	const resp = await serverRequest<CallbackResponse>("POST", "/auth/discord/callback", { | ||||
| 		body: { code, state } | ||||
| 	}); | ||||
| 
 | ||||
| 	if (resp.has_account) { | ||||
| 		return json( | ||||
| 			{ hasAccount: true, user: resp.user!, ticket: null, remoteUser: null }, | ||||
| 			{ | ||||
| 				headers: { | ||||
| 					"Set-Cookie": writeCookie("pronounscc-token", resp.token!) | ||||
| 				} | ||||
| 			} | ||||
| 		); | ||||
| 	} | ||||
| 
 | ||||
| 	return json({ | ||||
| 		hasAccount: false, | ||||
| 		user: null, | ||||
| 		ticket: resp.ticket!, | ||||
| 		remoteUser: resp.remote_username! | ||||
| 	}); | ||||
| }; | ||||
| 
 | ||||
| // TODO: action function
 | ||||
| 
 | ||||
| export default function DiscordCallbackPage() { | ||||
| 	const { t } = useTranslation(); | ||||
| 	const data = useLoaderData<typeof loader>(); | ||||
| 
 | ||||
| 	if (data.hasAccount) { | ||||
| 		const username = data.user!.username; | ||||
| 		 | ||||
| 		return ( | ||||
| 			<> | ||||
| 				<h1>{t("log-in.callback.success")}</h1> | ||||
| 				<p> | ||||
| 					<Trans t={t} i18nKey={"log-in.callback.success-link"} values={{ username: data.user!.username }}> | ||||
| 						{/* @ts-expect-error react-i18next handles interpolation here */} | ||||
| 						Welcome back, <Link to={`/@${data.user!.username}`}>@{{username}}</Link>! | ||||
| 					</Trans> | ||||
| 					<br /> | ||||
| 					{t("log-in.callback.redirect-hint")} | ||||
| 				</p> | ||||
| 			</> | ||||
| 		); | ||||
| 	} | ||||
| 
 | ||||
| 	return ( | ||||
| 		<RemixForm method="POST"> | ||||
| 			<Form as="div"> | ||||
| 				<Form.Group className="mb-3" controlId="remote-username"> | ||||
| 					<Form.Label>{t("log-in.callback.remote-username.discord")}</Form.Label> | ||||
| 					<Form.Control type="text" readOnly={true} value={data.remoteUser!} /> | ||||
| 				</Form.Group> | ||||
| 				<Form.Group className="mb-3" controlId="username"> | ||||
| 					<Form.Label>{t("log-in.callback.username")}</Form.Label> | ||||
| 					<Form.Control name="username" type="text" required /> | ||||
| 				</Form.Group> | ||||
| 				<input type="hidden" name="ticket" value={data.ticket!} /> | ||||
| 				<Button variant="primary" type="submit"> | ||||
| 					{t("log-in.callback.sign-up-button")} | ||||
| 				</Button> | ||||
| 			</Form> | ||||
| 		</RemixForm> | ||||
| 	); | ||||
| } | ||||
|  | @ -10,7 +10,7 @@ import Form from "react-bootstrap/Form"; | |||
| import Button from "react-bootstrap/Button"; | ||||
| import ButtonGroup from "react-bootstrap/ButtonGroup"; | ||||
| import ListGroup from "react-bootstrap/ListGroup"; | ||||
| import { Container, Row, Col } from "react-bootstrap"; | ||||
| import { Row, Col } from "react-bootstrap"; | ||||
| import { useTranslation } from "react-i18next"; | ||||
| import i18n from "~/i18next.server"; | ||||
| import serverRequest, { getToken, writeCookie } from "~/lib/request.server"; | ||||
|  | @ -72,7 +72,7 @@ export default function LoginPage() { | |||
| 	const actionData = useActionData<typeof action>(); | ||||
| 
 | ||||
| 	return ( | ||||
| 		<Container> | ||||
| 		<> | ||||
| 			<Row> | ||||
| 				<Col md className="mb-4"> | ||||
| 					<h2>{t("log-in.form-title")}</h2> | ||||
|  | @ -121,7 +121,7 @@ export default function LoginPage() { | |||
| 					</ListGroup> | ||||
| 				</Col> | ||||
| 			</Row> | ||||
| 		</Container> | ||||
| 		</> | ||||
| 	); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue