Skip to content

Commit

Permalink
theme system + device list + device logout + delete account + registe…
Browse files Browse the repository at this point in the history
…r callout + split up settings page components
  • Loading branch information
mrjvs committed Nov 18, 2023
1 parent 0dd73ee commit d8913bb
Show file tree
Hide file tree
Showing 28 changed files with 945 additions and 448 deletions.
9 changes: 8 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,14 @@ module.exports = {
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended"
],
ignorePatterns: ["public/*", "dist/*", "/*.js", "/*.ts", "/plugins/*.ts"],
ignorePatterns: [
"public/*",
"dist/*",
"/*.js",
"/*.ts",
"/plugins/*.ts",
"/themes/**/*.ts"
],
parser: "@typescript-eslint/parser",
parserOptions: {
project: "./tsconfig.json",
Expand Down
14 changes: 0 additions & 14 deletions src/backend/accounts/auth.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { ofetch } from "ofetch";

import { UserResponse } from "@/backend/accounts/user";

export interface SessionResponse {
id: string;
userId: string;
Expand Down Expand Up @@ -35,15 +33,3 @@ export async function accountLogin(
baseURL: url,
});
}

export async function removeSession(
url: string,
token: string,
sessionId: string
): Promise<UserResponse> {
return ofetch<UserResponse>(`/sessions/${sessionId}`, {
method: "DELETE",
headers: getAuthHeaders(token),
baseURL: url,
});
}
5 changes: 1 addition & 4 deletions src/backend/accounts/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,7 @@ export async function encryptData(data: string, secret: Uint8Array) {
)}.${stringBufferToBase64(tag)}` as const;
}

export async function decryptData(
data: `${string}.${string}.${string}`,
secret: Uint8Array
) {
export function decryptData(data: string, secret: Uint8Array) {
if (secret.byteLength !== 32) throw new Error("Secret must be 256-bit");

const [iv, encryptedData, tag] = data.split(".");
Expand Down
32 changes: 32 additions & 0 deletions src/backend/accounts/sessions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { ofetch } from "ofetch";

import { getAuthHeaders } from "@/backend/accounts/auth";
import { AccountWithToken } from "@/stores/auth";

export interface SessionResponse {
id: string;
userId: string;
createdAt: string;
accessedAt: string;
device: string;
userAgent: string;
}

export async function getSessions(url: string, account: AccountWithToken) {
return ofetch<SessionResponse[]>(`/users/${account.userId}/sessions`, {
headers: getAuthHeaders(account.token),
baseURL: url,
});
}

export async function removeSession(
url: string,
token: string,
sessionId: string
) {
return ofetch<SessionResponse[]>(`/sessions/${sessionId}`, {
method: "DELETE",
headers: getAuthHeaders(token),
baseURL: url,
});
}
10 changes: 10 additions & 0 deletions src/backend/accounts/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,16 @@ export async function getUser(
});
}

export async function deleteUser(
url: string,
account: AccountWithToken
): Promise<UserResponse> {
return ofetch<UserResponse>(`/users/${account.userId}`, {
headers: getAuthHeaders(account.token),
baseURL: url,
});
}

export async function getBookmarks(url: string, account: AccountWithToken) {
return ofetch<BookmarkResponse[]>(`/users/${account.userId}/bookmarks`, {
headers: getAuthHeaders(account.token),
Expand Down
37 changes: 37 additions & 0 deletions src/components/layout/SettingsCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import classNames from "classnames";

export function SettingsCard(props: {
children: React.ReactNode;
className?: string;
paddingClass?: string;
}) {
return (
<div
className={classNames(
"w-full rounded-lg bg-settings-card-background bg-opacity-[0.15] border border-settings-card-border",
props.paddingClass ?? "px-8 py-6",
props.className
)}
>
{props.children}
</div>
);
}

export function SolidSettingsCard(props: {
children: React.ReactNode;
className?: string;
paddingClass?: string;
}) {
return (
<div
className={classNames(
"w-full rounded-lg bg-settings-card-altBackground bg-opacity-50",
props.paddingClass ?? "px-8 py-6",
props.className
)}
>
{props.children}
</div>
);
}
45 changes: 45 additions & 0 deletions src/components/layout/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import classNames from "classnames";

import { Icon, Icons } from "@/components/Icon";

export function SidebarSection(props: {
title: string;
children: React.ReactNode;
}) {
return (
<section>
<p className="text-sm font-bold uppercase text-settings-sidebar-type-secondary mb-2">
{props.title}
</p>
{props.children}
</section>
);
}

export function SidebarLink(props: {
children: React.ReactNode;
icon: Icons;
active?: boolean;
onClick?: () => void;
}) {
return (
<div
onClick={props.onClick}
className={classNames(
"w-full px-3 py-2 flex items-center space-x-3 cursor-pointer rounded my-2",
props.active
? "bg-settings-sidebar-activeLink text-settings-sidebar-type-activated"
: null
)}
>
<Icon
className={classNames(
"text-2xl text-settings-sidebar-type-icon",
props.active ? "text-settings-sidebar-type-iconActivated" : null
)}
icon={props.icon}
/>
<span>{props.children}</span>
</div>
);
}
3 changes: 3 additions & 0 deletions src/components/text/SecondaryLabel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function SecondaryLabel(props: { children: React.ReactNode }) {
return <p className="text-type-text">{props.children}</p>;
}
2 changes: 1 addition & 1 deletion src/hooks/auth/useAuth.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useCallback } from "react";

import { removeSession } from "@/backend/accounts/auth";
import {
bytesToBase64,
bytesToBase64Url,
Expand All @@ -13,6 +12,7 @@ import {
getRegisterChallengeToken,
registerAccount,
} from "@/backend/accounts/register";
import { removeSession } from "@/backend/accounts/sessions";
import { getBookmarks, getProgress, getUser } from "@/backend/accounts/user";
import { useAuthData } from "@/hooks/auth/useAuthData";
import { useBackendUrl } from "@/hooks/auth/useBackendUrl";
Expand Down
16 changes: 13 additions & 3 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import i18n from "@/setup/i18n";
import "@/setup/ga";
import "@/setup/index.css";
import { useLanguageStore } from "@/stores/language";
import { useThemeStore } from "@/stores/theme";

import { initializeChromecast } from "./setup/chromecast";
import "./stores/__old/imports";
Expand Down Expand Up @@ -63,14 +64,23 @@ function TheRouter(props: { children: ReactNode }) {
return <HashRouter>{props.children}</HashRouter>;
}

function ThemeProvider(props: { children: ReactNode }) {
const theme = useThemeStore((s) => s.theme);
const themeSelector = theme ? `theme-${theme}` : undefined;

return <div className={themeSelector}>{props.children}</div>;
}

ReactDOM.render(
<React.StrictMode>
<ErrorBoundary>
<HelmetProvider>
<Suspense fallback={<LoadingScreen type="lazy" />}>
<TheRouter>
<MigrationRunner />
</TheRouter>
<ThemeProvider>
<TheRouter>
<MigrationRunner />
</TheRouter>
</ThemeProvider>
</Suspense>
</HelmetProvider>
</ErrorBoundary>
Expand Down
Loading

0 comments on commit d8913bb

Please sign in to comment.