(feat): add theming
This commit is contained in:
parent
87e4a158e3
commit
040d60a35f
6 changed files with 429 additions and 315 deletions
|
|
@ -31,10 +31,10 @@ import NotificationsProvider from "@tensamin/notifications/context";
|
|||
|
||||
import TAuthWrapper from "@tensamin/tauth/context";
|
||||
|
||||
import { ThemeProvider } from "@tensamin/ui";
|
||||
import { ThemeProvider, useTheme } from "@tensamin/ui";
|
||||
import z from "zod";
|
||||
|
||||
import { useEffect, useState, type ReactNode } from "react";
|
||||
import { useEffect, useRef, useState, type ReactNode } from "react";
|
||||
|
||||
import Storage from "@tensamin/storage/context";
|
||||
import Session from "@tensamin/storage/session";
|
||||
|
|
@ -106,12 +106,111 @@ function LoginWrapper({ children }: { children: ReactNode }) {
|
|||
return children;
|
||||
}
|
||||
|
||||
function ThemeStorageBridge() {
|
||||
const { load, save } = useStorage();
|
||||
const {
|
||||
themeColor,
|
||||
setThemeColor,
|
||||
themePalette,
|
||||
setThemePalette,
|
||||
themePrimaryColor,
|
||||
setThemePrimaryColor,
|
||||
themePolarity,
|
||||
setThemePolarity,
|
||||
themeTint,
|
||||
setThemeTint,
|
||||
} = useTheme();
|
||||
const loadedRef = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
let active = true;
|
||||
|
||||
Promise.all([
|
||||
load("theme_color"),
|
||||
load("theme_palette"),
|
||||
load("theme_primary_color"),
|
||||
load("theme_polarity"),
|
||||
load("theme_tint"),
|
||||
]).then(([color, palette, primaryColor, polarity, tint]) => {
|
||||
if (!active) {
|
||||
return;
|
||||
}
|
||||
|
||||
setThemeColor(color);
|
||||
setThemePalette(palette);
|
||||
setThemePrimaryColor(primaryColor);
|
||||
setThemePolarity(polarity);
|
||||
setThemeTint(tint);
|
||||
loadedRef.current = true;
|
||||
});
|
||||
|
||||
return () => {
|
||||
active = false;
|
||||
};
|
||||
}, [
|
||||
load,
|
||||
setThemeColor,
|
||||
setThemePalette,
|
||||
setThemePolarity,
|
||||
setThemePrimaryColor,
|
||||
setThemeTint,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!loadedRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
save("theme_color", themeColor);
|
||||
}, [save, themeColor]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!loadedRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
save("theme_palette", themePalette);
|
||||
}, [save, themePalette]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!loadedRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
save("theme_primary_color", themePrimaryColor);
|
||||
}, [save, themePrimaryColor]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!loadedRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
save("theme_polarity", themePolarity);
|
||||
}, [save, themePolarity]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!loadedRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
save("theme_tint", themeTint);
|
||||
}, [save, themeTint]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function RootShell() {
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
return (
|
||||
<DeeplinkContext>
|
||||
<ThemeProvider>
|
||||
<ThemeProvider
|
||||
storageKey={null}
|
||||
colorStorageKey={null}
|
||||
paletteStorageKey={null}
|
||||
primaryColorStorageKey={null}
|
||||
tintStorageKey={null}
|
||||
>
|
||||
<div className="w-screen h-dvh overflow-hidden">
|
||||
<Toaster
|
||||
position={isMobile ? "top-center" : "bottom-right"}
|
||||
|
|
@ -125,6 +224,7 @@ function RootShell() {
|
|||
/>
|
||||
<TooltipProvider>
|
||||
<Storage>
|
||||
<ThemeStorageBridge />
|
||||
<LoginWrapper>
|
||||
<LegalWrapper>
|
||||
<Crypto>
|
||||
|
|
|
|||
5
apps/web/src/routes/settings/theme.tsx
Normal file
5
apps/web/src/routes/settings/theme.tsx
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import { StylePicker } from "@tensamin/ui";
|
||||
|
||||
export default function Page() {
|
||||
return <StylePicker />;
|
||||
}
|
||||
|
|
@ -44,7 +44,7 @@
|
|||
"typescript-eslint": "^8.59.1"
|
||||
},
|
||||
"overrides": {
|
||||
"@tensamin/ui": "https://git.methanium.net/tensamin/ui/archive/0.0.36.tar.gz",
|
||||
"@tensamin/ui": "https://git.methanium.net/tensamin/ui/archive/0.0.37.tar.gz",
|
||||
"@tensamin/ttp-core": "https://git.methanium.net/tensamin/ttp/archive/0.0.24.tar.gz"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
|
|||
|
|
@ -47,6 +47,26 @@ export type Communities = z.infer<
|
|||
>;
|
||||
export type Calls = z.infer<typeof ttp.challenge_response.response.shape.calls>;
|
||||
|
||||
type Base16Palette = Record<
|
||||
| "base00"
|
||||
| "base01"
|
||||
| "base02"
|
||||
| "base03"
|
||||
| "base04"
|
||||
| "base05"
|
||||
| "base06"
|
||||
| "base07"
|
||||
| "base08"
|
||||
| "base09"
|
||||
| "base0A"
|
||||
| "base0B"
|
||||
| "base0C"
|
||||
| "base0D"
|
||||
| "base0E"
|
||||
| "base0F",
|
||||
string
|
||||
>;
|
||||
|
||||
// TTP
|
||||
const user = z.object({
|
||||
about: z.string().max(255).optional(),
|
||||
|
|
@ -260,6 +280,11 @@ export interface Storage extends SettingsStorageDefaults {
|
|||
ttp_server_cert: string;
|
||||
call_mute_range_start: number;
|
||||
call_mute_range_end: number;
|
||||
theme_color: string;
|
||||
theme_palette: Base16Palette | null;
|
||||
theme_primary_color: string;
|
||||
theme_polarity: "dark" | "light" | "system";
|
||||
theme_tint: "soft" | "hard" | "extreme";
|
||||
chat_trusted_domains: string[];
|
||||
chat_picker_saved_media: string[];
|
||||
chat_picker_last_tab: "gif" | "meme" | "saved";
|
||||
|
|
@ -303,6 +328,11 @@ export const storageDefaults: Storage = {
|
|||
ttp_server_cert: "",
|
||||
call_mute_range_start: -55,
|
||||
call_mute_range_end: -45,
|
||||
theme_color: "",
|
||||
theme_palette: null,
|
||||
theme_primary_color: "",
|
||||
theme_polarity: "system",
|
||||
theme_tint: "soft",
|
||||
chat_trusted_domains: [
|
||||
// Other platforms
|
||||
"cdn.discordapp.com",
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ const settings = {
|
|||
},
|
||||
},
|
||||
application: {
|
||||
theme: {},
|
||||
licenses: {},
|
||||
},
|
||||
} as const satisfies SettingsSchema;
|
||||
|
|
|
|||
Loading…
Reference in a new issue