From d011c703edbeac081f86b1b4fa0874d2dfc53da8 Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 11 Mar 2023 16:52:48 +0100 Subject: [PATCH] fix dark mode, add member page --- src/lib/api/entities.ts | 16 ++++ src/lib/api/fetch.ts | 4 +- src/lib/components/FallbackImage.svelte | 28 ++++++ src/lib/components/PartialMemberCard.svelte | 15 ++++ src/lib/store.ts | 10 ++- src/routes/@[username]/+page.svelte | 27 ++++-- .../@[username]/[memberName]/+page.server.ts | 10 +++ .../@[username]/[memberName]/+page.svelte | 87 +++++++++++++++++++ src/routes/login/+page.server.ts | 3 +- src/routes/login/discord/+page.server.ts | 33 +++++++ src/routes/login/discord/+page.svelte | 17 ++++ src/routes/nav/Navigation.svelte | 79 ++++++++++++----- 12 files changed, 298 insertions(+), 31 deletions(-) create mode 100644 src/lib/components/FallbackImage.svelte create mode 100644 src/lib/components/PartialMemberCard.svelte create mode 100644 src/routes/@[username]/[memberName]/+page.server.ts create mode 100644 src/routes/@[username]/[memberName]/+page.svelte create mode 100644 src/routes/login/discord/+page.server.ts diff --git a/src/lib/api/entities.ts b/src/lib/api/entities.ts index d71bd39..8e31b78 100644 --- a/src/lib/api/entities.ts +++ b/src/lib/api/entities.ts @@ -49,6 +49,22 @@ export interface PartialMember { avatar_urls: string[] | null; } +export interface Member extends PartialMember { + bio: string | null; + links: string | null; + names: FieldEntry[]; + pronouns: Pronoun[]; + fields: Field[]; + user: MemberPartialUser; +} + +export interface MemberPartialUser { + id: string; + name: string; + display_name: string | null; + avatar_urls: string[] | null; +} + export interface APIError { code: ErrorCode; message?: string; diff --git a/src/lib/api/fetch.ts b/src/lib/api/fetch.ts index 08c03df..b147ea5 100644 --- a/src/lib/api/fetch.ts +++ b/src/lib/api/fetch.ts @@ -1,12 +1,12 @@ import type { APIError } from "./entities"; +import { PUBLIC_BASE_URL } from "$env/static/public"; export async function apiFetch( path: string, { method, body, token }: { method?: string; body?: any; token?: string }, ) { - const apiBase = typeof process !== "undefined" ? process.env.ORIGIN : ""; - const resp = await fetch(`${apiBase}/api/v1${path}`, { + const resp = await fetch(`${PUBLIC_BASE_URL}/api/v1${path}`, { method: method || "GET", headers: { ...(token ? { Authorization: token } : {}), diff --git a/src/lib/components/FallbackImage.svelte b/src/lib/components/FallbackImage.svelte new file mode 100644 index 0000000..3370030 --- /dev/null +++ b/src/lib/components/FallbackImage.svelte @@ -0,0 +1,28 @@ + + +{#if urls} + + {#each urls as url} + + {/each} + + +{:else} + + +{/if} diff --git a/src/lib/components/PartialMemberCard.svelte b/src/lib/components/PartialMemberCard.svelte new file mode 100644 index 0000000..4a6c1a7 --- /dev/null +++ b/src/lib/components/PartialMemberCard.svelte @@ -0,0 +1,15 @@ + + +
+ +
{member.display_name ?? member.name}
+
diff --git a/src/lib/store.ts b/src/lib/store.ts index f6a8bfc..3fb9233 100644 --- a/src/lib/store.ts +++ b/src/lib/store.ts @@ -1,6 +1,14 @@ import { writable } from "svelte/store"; +import { browser } from "$app/environment"; import type { MeUser } from "./api/entities"; export const userStore = writable(null); -export const tokenStore = writable(null); \ No newline at end of file +export const tokenStore = writable(null); + +let defaultThemeValue = "dark"; +const initialThemeValue = browser + ? window.localStorage.getItem("pronouns-theme") ?? defaultThemeValue + : defaultThemeValue; + +export const themeStore = writable(initialThemeValue); diff --git a/src/routes/@[username]/+page.svelte b/src/routes/@[username]/+page.svelte index 6ad7061..4df7e0a 100644 --- a/src/routes/@[username]/+page.svelte +++ b/src/routes/@[username]/+page.svelte @@ -6,7 +6,9 @@ import type { PageData } from "./$types"; import StatusIcon from "$lib/components/StatusIcon.svelte"; - import PronounLink from "$lib/components/PronounLink.svelte"; + import PronounLink from "$lib/components/PronounLink.svelte"; + import PartialMemberCard from "$lib/components/PartialMemberCard.svelte"; + import FallbackImage from "$lib/components/FallbackImage.svelte"; export let data: PageData; @@ -21,13 +23,13 @@
- {#if data.avatar_urls} -
- {/if} +
+ +
{#if data.display_name}

{data.display_name}

-

@{data.name}

+
@{data.name}
{:else}

@{data.name}

{/if} @@ -64,7 +66,7 @@ {#each data.pronouns as pronouns}
  • - +
  • {/each} @@ -81,4 +83,17 @@ {/each}
    {/if} + {#if data.members} +
    +
    +
    +

    Members

    +
    +
    +
    + {#each data.members as member} + + {/each} +
    + {/if}
    diff --git a/src/routes/@[username]/[memberName]/+page.server.ts b/src/routes/@[username]/[memberName]/+page.server.ts new file mode 100644 index 0000000..73d9ec2 --- /dev/null +++ b/src/routes/@[username]/[memberName]/+page.server.ts @@ -0,0 +1,10 @@ +import { apiFetch } from "$lib/api/fetch"; +import type { Member } from "$lib/api/entities"; + +export const load = async ({ params }) => { + const resp = await apiFetch(`/users/${params.username}/members/${params.memberName}`, { + method: "GET", + }); + + return resp; +}; diff --git a/src/routes/@[username]/[memberName]/+page.svelte b/src/routes/@[username]/[memberName]/+page.svelte new file mode 100644 index 0000000..505a00a --- /dev/null +++ b/src/routes/@[username]/[memberName]/+page.svelte @@ -0,0 +1,87 @@ + + + + {data.display_name ?? data.name} - @{data.user.name} - pronouns.cc + + +
    +
    + +
    +
    +
    +
    + +
    +
    +

    {data.display_name ?? data.name}

    +
    {data.name} (@{data.user.name})
    +
    + {#if bio} +

    {@html bio}

    + {/if} +
    + {#if data.links} +
    +
      + {#each data.links as link} +
    • {link}
    • + {/each} +
    +
    + {/if} +
    +
    + {#if data.names} +
    +

    Names

    +
      + {#each data.names as name} +
    • {name.value}
    • + {/each} +
    +
    + {/if} + {#if data.pronouns} +
    +

    Pronouns

    +
      + {#each data.pronouns as pronouns} +
    • + + +
    • + {/each} +
    +
    + {/if} +
    +
    + {#if data.fields} +
    + {#each data.fields as field} +
    + +
    + {/each} +
    + {/if} +
    diff --git a/src/routes/login/+page.server.ts b/src/routes/login/+page.server.ts index 2af4941..7595151 100644 --- a/src/routes/login/+page.server.ts +++ b/src/routes/login/+page.server.ts @@ -1,10 +1,11 @@ import { apiFetch } from "$lib/api/fetch"; +import { PUBLIC_BASE_URL } from "$env/static/public"; export const load = async () => { const resp = await apiFetch("/auth/urls", { method: "POST", body: { - callback_domain: process.env.ORIGIN, + callback_domain: PUBLIC_BASE_URL, }, }); diff --git a/src/routes/login/discord/+page.server.ts b/src/routes/login/discord/+page.server.ts new file mode 100644 index 0000000..98e79c7 --- /dev/null +++ b/src/routes/login/discord/+page.server.ts @@ -0,0 +1,33 @@ +import type { MeUser } from "$lib/api/entities"; +import { apiFetch } from "$lib/api/fetch"; +import type { PageServerLoad } from "./$types"; +import { PUBLIC_BASE_URL } from "$env/static/public"; + +export const load = (async (event) => { + try { + const resp = await apiFetch("/auth/discord/callback", { + method: "POST", + body: { + callback_domain: PUBLIC_BASE_URL, + code: event.url.searchParams.get("code"), + state: event.url.searchParams.get("state"), + }, + }); + + return { + ...resp, + }; + } catch (e) { + return { error: e }; + } +}) satisfies PageServerLoad; + +interface CallbackResponse { + has_account: boolean; + token?: string; + user?: MeUser; + + discord?: string; + ticket?: string; + require_invite: boolean; +} diff --git a/src/routes/login/discord/+page.svelte b/src/routes/login/discord/+page.svelte index e69de29..c8b2af5 100644 --- a/src/routes/login/discord/+page.svelte +++ b/src/routes/login/discord/+page.svelte @@ -0,0 +1,17 @@ + + + + Log in with Discord - pronouns.cc + + +

    Log in with Discord

    + +{#if data.error} + +{:else} + +{/if} \ No newline at end of file diff --git a/src/routes/nav/Navigation.svelte b/src/routes/nav/Navigation.svelte index 7d93963..e0c9faf 100644 --- a/src/routes/nav/Navigation.svelte +++ b/src/routes/nav/Navigation.svelte @@ -2,34 +2,65 @@ import { onMount } from "svelte"; import { browser } from "$app/environment"; - import { Collapse,Icon, Nav, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink } from "sveltestrap"; + import { + Collapse, + Icon, + Nav, + Navbar, + NavbarBrand, + NavbarToggler, + NavItem, + NavLink, + } from "sveltestrap"; import Logo from "./Logo.svelte"; + import { userStore, themeStore } from "$lib/store"; + import { ErrorCode, type APIError, type MeUser } from "$lib/api/entities"; + import { apiFetch } from "$lib/api/fetch"; - let darkTheme: boolean = false; + let theme: string; + let currentUser: MeUser | null; let showMenu: boolean = false; + $: currentUser = $userStore; + $: theme = $themeStore; + onMount(() => { - darkTheme = - localStorage.theme === "dark" || - (!("theme" in localStorage) && window.matchMedia("(prefers-color-scheme: dark)").matches); + const localUser = localStorage.getItem("pronouns-user"); + userStore.set(localUser ? JSON.parse(localUser) : null); + + const token = localStorage.getItem("pronouns-token"); + if (token) { + apiFetch("/users/@me", { token }) + .then((user) => { + userStore.set(user); + localStorage.setItem("pronouns-user", JSON.stringify(user)); + }) + .catch((e) => { + console.log("getting /users/@me:", e); + + if ( + (e as APIError).code == ErrorCode.InvalidToken || + (e as APIError).code == ErrorCode.Forbidden + ) { + localStorage.removeItem("pronouns-token"); + localStorage.removeItem("pronouns-user"); + } + }); + } }); - $: updateTheme(darkTheme); + $: updateTheme(theme); - const updateTheme = (isDark: boolean) => { + const updateTheme = (newTheme: string) => { if (!browser) return; - if (isDark) { - document.documentElement.setAttribute("data-bs-theme", "dark"); - } else { - document.documentElement.setAttribute("data-bs-theme", "light"); - } - localStorage.setItem("theme", isDark ? "dark" : "light"); + document.documentElement.setAttribute("data-bs-theme", newTheme); + localStorage.setItem("pronouns-theme", newTheme); }; const toggleTheme = () => { - darkTheme = !darkTheme; + themeStore.set(theme === "dark" ? "light" : "dark") }; const toggleMenu = () => { showMenu = !showMenu; @@ -37,9 +68,9 @@ @@ -48,15 +79,21 @@