diff --git a/website/public/locales/en/account.json b/website/public/locales/en/account.json index 574c291895..bf40f7abbd 100644 --- a/website/public/locales/en/account.json +++ b/website/public/locales/en/account.json @@ -4,6 +4,7 @@ "delete_account_intro": "Deleting an account entails the following:", "delete_account_leaderboard": "Accounts of deleted users won't show up on the leaderboards", "delete_account_permanent": "This action is permanent and cannot be undone", + "linked_accounts": "Linked accounts", "go_to_dashboard": "Go back to dashboard", "yes_delete": "Yes, delete my account permanently" } diff --git a/website/src/pages/account/index.tsx b/website/src/pages/account/index.tsx index b2e270a8c2..e459a25910 100644 --- a/website/src/pages/account/index.tsx +++ b/website/src/pages/account/index.tsx @@ -2,31 +2,37 @@ import { Divider, Flex, Grid, Icon, Text } from "@chakra-ui/react"; import Head from "next/head"; import Link from "next/link"; import { useSession } from "next-auth/react"; -import React from "react"; +import React, { Fragment } from "react"; export { getStaticProps } from "src/lib/defaultServerSideProps"; +import { Discord, Google } from "@icons-pack/react-simple-icons"; import { Pencil } from "lucide-react"; import { useTranslation } from "next-i18next"; import { UserStats } from "src/components/Account/UserStats"; import { XPBar } from "src/components/Account/XPBar"; import { SurveyCard } from "src/components/Survey/SurveyCard"; import { get } from "src/lib/api"; +import { ExternalProvider, UserAccountResponse } from "src/types/Account"; import { LeaderboardEntity, LeaderboardTimeFrame } from "src/types/Leaderboard"; -import uswSWRImmutable from "swr/immutable"; +import useSWRImmutable from "swr/immutable"; export default function Account() { const { t } = useTranslation(["leaderboard", "account"]); const { data: session } = useSession(); - const { data: stats } = uswSWRImmutable>( + const { data: userAccounts } = useSWRImmutable("/api/account", get, { keepPreviousData: true }); + const { data: stats } = useSWRImmutable>( "/api/user_stats", get, { fallbackData: {}, } ); - if (!session) { + + if (!session || !userAccounts) { return; } + const { emailIsVerified, accounts } = userAccounts; + return ( <> @@ -43,7 +49,7 @@ export default function Account() { {t("username")} - + {session.user.name ?? t("no_username")} @@ -51,6 +57,19 @@ export default function Account() { Email {session.user.email ?? t("no_email")} + {accounts.length > 0 && ( + <> + + {t("account:linked_accounts")} + + {accounts.map(({ provider, providerAccountId }) => ( + + + {providerAccountId} + + ))} + + )} @@ -70,3 +89,13 @@ const Title = ({ children }) => ( {children} ); + +const ProviderIcon = ({ provider }: { provider: ExternalProvider }): JSX.Element => { + if (provider === "discord") { + return ; + } + if (provider === "google") { + return ; + } + return
; +}; diff --git a/website/src/pages/api/account/index.ts b/website/src/pages/api/account/index.ts new file mode 100644 index 0000000000..4eedbab5a7 --- /dev/null +++ b/website/src/pages/api/account/index.ts @@ -0,0 +1,22 @@ +import { withoutRole } from "src/lib/auth"; +import prisma from "src/lib/prismadb"; +import { ExternalProvider, UserAccountResponse } from "src/types/Account"; + +const handler = withoutRole("banned", async (req, res, token) => { + const user = await prisma.user.findFirst({ + where: { id: token.sub }, + select: { emailVerified: true, accounts: true }, + }); + + const emailIsVerified = Boolean(user.emailVerified); + const accounts = user.accounts.map(({ provider, providerAccountId }) => ({ + provider: provider as ExternalProvider, + providerAccountId, + })); + + const response: UserAccountResponse = { emailIsVerified, accounts }; + + return res.status(200).json(response); +}); + +export default handler; diff --git a/website/src/types/Account.ts b/website/src/types/Account.ts new file mode 100644 index 0000000000..8ae26432a8 --- /dev/null +++ b/website/src/types/Account.ts @@ -0,0 +1,6 @@ +export type ExternalProvider = "google" | "discord"; + +export interface UserAccountResponse { + emailIsVerified: boolean; + accounts: Array<{ provider: ExternalProvider; providerAccountId: string }>; +}