Skip to content

Commit

Permalink
Add linked accounts to account page (#3489)
Browse files Browse the repository at this point in the history
Does not look the best, but we should add buttons for users to unlink
their accounts (or link more)
  • Loading branch information
AbdBarho committed Jun 18, 2023
1 parent d1abbde commit 21a8391
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 5 deletions.
1 change: 1 addition & 0 deletions website/public/locales/en/account.json
Expand Up @@ -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"
}
39 changes: 34 additions & 5 deletions website/src/pages/account/index.tsx
Expand Up @@ -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<Partial<{ [time in LeaderboardTimeFrame]: LeaderboardEntity }>>(
const { data: userAccounts } = useSWRImmutable<UserAccountResponse>("/api/account", get, { keepPreviousData: true });
const { data: stats } = useSWRImmutable<Partial<{ [time in LeaderboardTimeFrame]: LeaderboardEntity }>>(
"/api/user_stats",
get,
{
fallbackData: {},
}
);
if (!session) {

if (!session || !userAccounts) {
return;
}

const { emailIsVerified, accounts } = userAccounts;

return (
<>
<Head>
Expand All @@ -43,14 +49,27 @@ export default function Account() {
<Divider />
<Grid gridTemplateColumns="repeat(2, max-content)" alignItems="center" gap={6} py={4}>
<Text as="b">{t("username")}</Text>
<Flex gap={2} style={{ overflow: "hidden" }}>
<Flex gap={2}>
{session.user.name ?? t("no_username")}
<Link href="/account/edit">
<Icon boxSize={5} as={Pencil} size="1em" />
</Link>
</Flex>
<Text as="b">Email</Text>
<Text>{session.user.email ?? t("no_email")}</Text>
{accounts.length > 0 && (
<>
<Text as="b" className="col-span-2">
{t("account:linked_accounts")}
</Text>
{accounts.map(({ provider, providerAccountId }) => (
<Fragment key={provider + providerAccountId}>
<ProviderIcon provider={provider} />
<Text>{providerAccountId}</Text>
</Fragment>
))}
</>
)}
</Grid>
<Divider my={4} />
<XPBar />
Expand All @@ -70,3 +89,13 @@ const Title = ({ children }) => (
{children}
</Text>
);

const ProviderIcon = ({ provider }: { provider: ExternalProvider }): JSX.Element => {
if (provider === "discord") {
return <Discord />;
}
if (provider === "google") {
return <Google />;
}
return <div></div>;
};
22 changes: 22 additions & 0 deletions 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;
6 changes: 6 additions & 0 deletions 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 }>;
}

0 comments on commit 21a8391

Please sign in to comment.