From 206feb21b8516c7bd2c5b1637817c2b665245f1d Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 11 May 2022 02:23:45 +0200 Subject: [PATCH] feat: add working user page --- frontend/src/lib/FieldCard.tsx | 44 +++++++++++++++++++ frontend/src/lib/fetch.ts | 14 ++++++ frontend/src/lib/types.ts | 18 +++++--- frontend/src/pages/User.tsx | 78 +++++++++++++++++++++++++--------- package.json | 2 + yarn.lock | 27 ++++++++++++ 6 files changed, 155 insertions(+), 28 deletions(-) create mode 100644 frontend/src/lib/FieldCard.tsx create mode 100644 frontend/src/lib/fetch.ts diff --git a/frontend/src/lib/FieldCard.tsx b/frontend/src/lib/FieldCard.tsx new file mode 100644 index 0000000..a06dc3c --- /dev/null +++ b/frontend/src/lib/FieldCard.tsx @@ -0,0 +1,44 @@ +import { + HeartFill, + HandThumbsUp, + HandThumbsDown, + People, + EmojiLaughing, +} from "react-bootstrap-icons"; + +import type { Field } from "./types"; + +export default function FieldCard({ field }: { field: Field }) { + return ( +
+

{field.name}

+
+ {field.favourite.map((entry) => ( +

+ {entry} +

+ ))} + {field.okay.length !== 0 && ( +

+ {field.okay.join(", ")} +

+ )} + {field.jokingly.length !== 0 && ( +

+ {field.jokingly.join(", ")} +

+ )} + {field.friends_only.length !== 0 && ( +

+ {field.friends_only.join(", ")} +

+ )} + {field.avoid.length !== 0 && ( +

+ {field.avoid.join(", ")} +

+ )} +
+
+ ); +} diff --git a/frontend/src/lib/fetch.ts b/frontend/src/lib/fetch.ts new file mode 100644 index 0000000..b69248e --- /dev/null +++ b/frontend/src/lib/fetch.ts @@ -0,0 +1,14 @@ +import axios from "axios"; +import type { APIError } from "./types"; + +export default async function fetchAPI(path: string) { + const resp = await axios.get(`/api/v1${path}`, { + headers: { + Authorization: localStorage.getItem("pronouns-token"), + "Content-Type": "application/json", + }, + }); + if (resp.status !== 200) throw resp.data as APIError; + + return resp.data as T; +} diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index 962cd88..9bc816a 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -1,11 +1,5 @@ export interface MeUser { - id: string; - username: string; - display_name: string | null; - bio: string | null; avatar_source: string | null; - avatar_url: string | null; - links: string[] | null; discord: string | null; discord_username: string | null; } @@ -15,9 +9,10 @@ export interface User { username: string; display_name: string | null; bio: string | null; - avatar_source: string | null; + avatar_url: string | null; links: string[] | null; members: PartialMember[]; + fields: Field[]; } export interface PartialMember { @@ -26,6 +21,15 @@ export interface PartialMember { avatar_url: string | null; } +export interface Field { + name: string; + favourite: string[] | null; + okay: string[] | null; + jokingly: string[] | null; + friends_only: string[] | null; + avoid: string[] | null; +} + export interface APIError { code: ErrorCode; message?: string; diff --git a/frontend/src/pages/User.tsx b/frontend/src/pages/User.tsx index 7ee7294..3f23044 100644 --- a/frontend/src/pages/User.tsx +++ b/frontend/src/pages/User.tsx @@ -1,21 +1,12 @@ import React, { useState, useEffect } from "react"; import { useParams } from "react-router-dom"; import { ArrowClockwise } from "react-bootstrap-icons"; -import { selectorFamily, useRecoilValue } from "recoil"; -import axios from "axios"; +import ReactMarkdown from "react-markdown"; +import { Helmet } from "react-helmet"; + import type { APIError, User } from "../lib/types"; - -const userPageState = selectorFamily({ - key: "userPageState", - get: (username: string) => async () => { - const res = await axios.get(`/api/v1/users/${username}`); - if (res.status !== 200) { - throw res.data as APIError; - } - - return res.data as User; - }, -}); +import fetchAPI from "../lib/fetch"; +import FieldCard from "../lib/FieldCard"; function UserPage() { const params = useParams(); @@ -23,10 +14,8 @@ function UserPage() { const [user, setUser] = useState(null); useEffect(() => { - axios.get(`/api/v1/users/${params.username}`).then((res) => { - if (res.status !== 200) throw res.data as APIError; - - setUser(res.data as User); + fetchAPI(`/users/${params.username}`).then((res) => { + setUser(res); }); }, []); @@ -41,9 +30,56 @@ function UserPage() { return ( <> -

- {user.username} ({user.id}) -

+ + @{user.username} - pronouns.cc + +
+
+ {user.avatar_url && ( + + )} +
+ {user.display_name && ( +

{user.display_name}

+ )} +

+ @{user.username} +

+ {user.bio && ( + + {user.bio} + + )} + {user.links.length !== 0 && ( +
+ {user.links.map((link) => ( + + {link} + + ))} +
+ )} +
+
+
+ {user.fields.map((field) => ( + + ))} +
+
); } diff --git a/package.json b/package.json index d8c4c79..be20c63 100644 --- a/package.json +++ b/package.json @@ -10,10 +10,12 @@ "dependencies": { "@sentry/react": "^6.19.7", "@sentry/tracing": "^6.19.7", + "@types/react-helmet": "^6.1.5", "axios": "^0.27.2", "react": "^18.0.0", "react-bootstrap-icons": "^1.8.2", "react-dom": "^18.0.0", + "react-helmet": "^6.1.0", "react-markdown": "^8.0.3", "react-router-dom": "6", "recoil": "^0.7.2" diff --git a/yarn.lock b/yarn.lock index 2ae2466..5d980e9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -430,6 +430,13 @@ dependencies: "@types/react" "*" +"@types/react-helmet@^6.1.5": + version "6.1.5" + resolved "https://registry.yarnpkg.com/@types/react-helmet/-/react-helmet-6.1.5.tgz#35f89a6b1646ee2bc342a33a9a6c8777933f9083" + integrity sha512-/ICuy7OHZxR0YCAZLNg9r7I9aijWUWvxaPR6uTuyxe8tAj5RL4Sw1+R6NhXUtOsarkGYPmaHdBDvuXh2DIN/uA== + dependencies: + "@types/react" "*" + "@types/react@*", "@types/react@^18.0.0": version "18.0.8" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.8.tgz#a051eb380a9fbcaa404550543c58e1cf5ce4ab87" @@ -1472,6 +1479,21 @@ react-dom@^18.0.0: loose-envify "^1.1.0" scheduler "^0.22.0" +react-fast-compare@^3.1.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" + integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA== + +react-helmet@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-6.1.0.tgz#a750d5165cb13cf213e44747502652e794468726" + integrity sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw== + dependencies: + object-assign "^4.1.1" + prop-types "^15.7.2" + react-fast-compare "^3.1.1" + react-side-effect "^2.1.0" + react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" @@ -1523,6 +1545,11 @@ react-router@6.3.0: dependencies: history "^5.2.0" +react-side-effect@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.1.tgz#66c5701c3e7560ab4822a4ee2742dee215d72eb3" + integrity sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ== + react@^18.0.0: version "18.1.0" resolved "https://registry.yarnpkg.com/react/-/react-18.1.0.tgz#6f8620382decb17fdc5cc223a115e2adbf104890"