feat: add theme switcher

This commit is contained in:
reya 2024-05-26 16:11:27 +07:00
parent 4dc13385a5
commit 5ca9444358
12 changed files with 564 additions and 619 deletions

View File

@ -2,6 +2,7 @@ import { NostrQuery } from "@lume/system";
import type { Settings } from "@lume/types"; import type { Settings } from "@lume/types";
import * as Switch from "@radix-ui/react-switch"; import * as Switch from "@radix-ui/react-switch";
import { createFileRoute } from "@tanstack/react-router"; import { createFileRoute } from "@tanstack/react-router";
import { invoke } from "@tauri-apps/api/core";
import { requestPermission } from "@tauri-apps/plugin-notification"; import { requestPermission } from "@tauri-apps/plugin-notification";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useDebouncedCallback } from "use-debounce"; import { useDebouncedCallback } from "use-debounce";
@ -61,6 +62,19 @@ function Screen() {
})); }));
}; };
const changeTheme = (theme: string) => {
if (theme === "auto" || theme === "light" || theme === "dark") {
invoke("plugin:theme|set_theme", {
theme: theme,
}).then(() =>
setNewSettings((prev) => ({
...prev,
theme,
})),
);
}
};
const updateSettings = useDebouncedCallback(() => { const updateSettings = useDebouncedCallback(() => {
NostrQuery.setSettings(newSettings); NostrQuery.setSettings(newSettings);
}, 200); }, 200);
@ -173,24 +187,42 @@ function Screen() {
Interface Interface
</h2> </h2>
<div className="flex flex-col divide-y divide-black/10 dark:divide-white/10 bg-black/5 dark:bg-white/5 rounded-xl px-3"> <div className="flex flex-col divide-y divide-black/10 dark:divide-white/10 bg-black/5 dark:bg-white/5 rounded-xl px-3">
<div className="flex flex-col gap-4"> <div className="flex w-full items-start justify-between gap-4 py-3">
<div className="flex w-full items-start justify-between gap-4 py-3"> <div className="flex-1">
<div className="flex-1"> <h3 className="font-semibold">Zap</h3>
<h3 className="font-semibold">Zap</h3> <p className="text-sm text-neutral-700 dark:text-neutral-300">
<p className="text-sm text-neutral-700 dark:text-neutral-300"> Show the Zap button in each note and user's profile screen,
Show the Zap button in each note and user's profile screen, use for send bitcoin tip to other users.
use for send bitcoin tip to other users. </p>
</p> </div>
</div> <div className="w-36 flex justify-end shrink-0">
<div className="w-36 flex justify-end shrink-0"> <Switch.Root
<Switch.Root checked={newSettings.zap}
checked={newSettings.zap} onClick={() => toggleZap()}
onClick={() => toggleZap()} className="relative h-7 w-12 shrink-0 cursor-default rounded-full bg-black/10 outline-none data-[state=checked]:bg-blue-500 dark:bg-white/10"
className="relative h-7 w-12 shrink-0 cursor-default rounded-full bg-black/10 outline-none data-[state=checked]:bg-blue-500 dark:bg-white/10" >
> <Switch.Thumb className="block size-6 translate-x-0.5 rounded-full bg-white transition-transform duration-100 will-change-transform data-[state=checked]:translate-x-[19px]" />
<Switch.Thumb className="block size-6 translate-x-0.5 rounded-full bg-white transition-transform duration-100 will-change-transform data-[state=checked]:translate-x-[19px]" /> </Switch.Root>
</Switch.Root> </div>
</div> </div>
<div className="flex w-full items-start justify-between gap-4 py-3">
<div className="flex-1">
<h3 className="font-semibold">Appearance</h3>
<p className="text-sm text-neutral-700 dark:text-neutral-300">
* Require restarting the app to take effect.
</p>
</div>
<div className="w-36 flex justify-end shrink-0">
<select
name="theme"
className="bg-transparent shadow-none outline-none rounded-lg border-1 border-black/10 dark:border-white/10 py-1 w-24"
defaultValue={settings.theme}
onChange={(e) => changeTheme(e.target.value)}
>
<option value="auto">Auto</option>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</div> </div>
</div> </div>
</div> </div>

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,7 @@ import { readFile, readTextFile } from "@tauri-apps/plugin-fs";
import { isPermissionGranted } from "@tauri-apps/plugin-notification"; import { isPermissionGranted } from "@tauri-apps/plugin-notification";
import { open } from "@tauri-apps/plugin-dialog"; import { open } from "@tauri-apps/plugin-dialog";
import { dedupEvents } from "./dedup"; import { dedupEvents } from "./dedup";
import { invoke } from "@tauri-apps/api/core";
enum NSTORE_KEYS { enum NSTORE_KEYS {
settings = "lume_user_settings", settings = "lume_user_settings",
@ -201,8 +202,11 @@ export class NostrQuery {
if (query.status === "ok") { if (query.status === "ok") {
const settings: Settings = query.data ? JSON.parse(query.data) : null; const settings: Settings = query.data ? JSON.parse(query.data) : null;
const isGranted = await isPermissionGranted(); const isGranted = await isPermissionGranted();
const theme: "auto" | "light" | "dark" = await invoke(
"plugin:theme|get_theme",
);
return { ...settings, notification: isGranted }; return { ...settings, theme, notification: isGranted };
} else { } else {
const initial: Settings = { const initial: Settings = {
autoUpdate: false, autoUpdate: false,
@ -210,6 +214,8 @@ export class NostrQuery {
notification: false, notification: false,
zap: false, zap: false,
nsfw: false, nsfw: false,
gossip: false,
theme: "auto",
}; };
return initial; return initial;

View File

@ -5,6 +5,7 @@ export interface Settings {
zap: boolean; zap: boolean;
nsfw: boolean; nsfw: boolean;
gossip: boolean; gossip: boolean;
theme: "auto" | "light" | "dark";
[key: string]: string | number | boolean; [key: string]: string | number | boolean;
} }

29
src-tauri/Cargo.lock generated
View File

@ -2907,6 +2907,7 @@ dependencies = [
"tauri-plugin-os", "tauri-plugin-os",
"tauri-plugin-process", "tauri-plugin-process",
"tauri-plugin-shell", "tauri-plugin-shell",
"tauri-plugin-theme",
"tauri-plugin-updater", "tauri-plugin-updater",
"tauri-plugin-upload", "tauri-plugin-upload",
"tauri-plugin-window-state", "tauri-plugin-window-state",
@ -5514,6 +5515,24 @@ dependencies = [
"tokio", "tokio",
] ]
[[package]]
name = "tauri-plugin-theme"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c23856e93f56cd8d1868e7dd8a4b529b836fd5da315cefd7702b252a850cdf5a"
dependencies = [
"cocoa",
"dirs-next",
"futures-lite 2.3.0",
"gtk",
"once_cell",
"serde",
"tauri",
"tauri-plugin",
"tintanum",
"tokio",
]
[[package]] [[package]]
name = "tauri-plugin-updater" name = "tauri-plugin-updater"
version = "2.0.0-beta.5" version = "2.0.0-beta.5"
@ -5805,6 +5824,16 @@ dependencies = [
"time-core", "time-core",
] ]
[[package]]
name = "tintanum"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7abbcf9173afc80733c20b7e27a30bc9284d6535bdbde2a70904032de63e16e8"
dependencies = [
"futures-lite 1.13.0",
"zbus 3.15.1",
]
[[package]] [[package]]
name = "tinyvec" name = "tinyvec"
version = "1.6.0" version = "1.6.0"

View File

@ -39,6 +39,7 @@ keyring = "2"
keyring-search = "0.2.0" keyring-search = "0.2.0"
specta = "=2.0.0-rc.12" specta = "=2.0.0-rc.12"
tauri-specta = { version = "=2.0.0-rc.10", features = ["typescript"] } tauri-specta = { version = "=2.0.0-rc.10", features = ["typescript"] }
tauri-plugin-theme = "0.4.1"
[target.'cfg(target_os = "macos")'.dependencies] [target.'cfg(target_os = "macos")'.dependencies]
cocoa = "0.25.0" cocoa = "0.25.0"

View File

@ -56,6 +56,8 @@
"dialog:allow-message", "dialog:allow-message",
"process:allow-restart", "process:allow-restart",
"fs:allow-read-file", "fs:allow-read-file",
"theme:allow-set-theme",
"theme:allow-get-theme",
"shell:allow-open", "shell:allow-open",
{ {
"identifier": "http:default", "identifier": "http:default",

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
{"desktop-capability":{"identifier":"desktop-capability","description":"Capability for the desktop","local":true,"windows":["main","splash","settings","search","nwc","activity","zap-*","event-*","user-*","editor-*","column-*"],"permissions":["path:default","event:default","window:default","app:default","resources:default","menu:default","tray:default","notification:allow-is-permission-granted","notification:allow-request-permission","notification:default","os:allow-locale","os:allow-platform","os:allow-os-type","updater:default","updater:allow-check","updater:allow-download-and-install","window:allow-start-dragging","window:allow-create","window:allow-close","window:allow-set-focus","window:allow-center","window:allow-minimize","window:allow-maximize","window:allow-set-size","window:allow-set-focus","window:allow-start-dragging","decorum:allow-show-snap-overlay","clipboard-manager:allow-write-text","clipboard-manager:allow-read-text","webview:allow-create-webview-window","webview:allow-create-webview","webview:allow-set-webview-size","webview:allow-set-webview-position","webview:allow-webview-close","dialog:allow-open","dialog:allow-ask","dialog:allow-message","process:allow-restart","fs:allow-read-file","shell:allow-open",{"identifier":"http:default","allow":[{"url":"http://**/"},{"url":"https://**/"}]},{"identifier":"fs:allow-read-text-file","allow":[{"path":"$RESOURCE/locales/*"},{"path":"$RESOURCE/resources/*"}]}],"platforms":["linux","macOS","windows"]}} {"desktop-capability":{"identifier":"desktop-capability","description":"Capability for the desktop","local":true,"windows":["main","splash","settings","search","nwc","activity","zap-*","event-*","user-*","editor-*","column-*"],"permissions":["path:default","event:default","window:default","app:default","resources:default","menu:default","tray:default","notification:allow-is-permission-granted","notification:allow-request-permission","notification:default","os:allow-locale","os:allow-platform","os:allow-os-type","updater:default","updater:allow-check","updater:allow-download-and-install","window:allow-start-dragging","window:allow-create","window:allow-close","window:allow-set-focus","window:allow-center","window:allow-minimize","window:allow-maximize","window:allow-set-size","window:allow-set-focus","window:allow-start-dragging","decorum:allow-show-snap-overlay","clipboard-manager:allow-write-text","clipboard-manager:allow-read-text","webview:allow-create-webview-window","webview:allow-create-webview","webview:allow-set-webview-size","webview:allow-set-webview-position","webview:allow-webview-close","dialog:allow-open","dialog:allow-ask","dialog:allow-message","process:allow-restart","fs:allow-read-file","theme:allow-set-theme","theme:allow-get-theme","shell:allow-open",{"identifier":"http:default","allow":[{"url":"http://**/"},{"url":"https://**/"}]},{"identifier":"fs:allow-read-text-file","allow":[{"path":"$RESOURCE/locales/*"},{"path":"$RESOURCE/resources/*"}]}],"platforms":["linux","macOS","windows"]}}

View File

@ -5654,6 +5654,40 @@
"shell:deny-stdin-write" "shell:deny-stdin-write"
] ]
}, },
{
"type": "string",
"enum": [
"theme:default"
]
},
{
"description": "theme:allow-get-theme -> Enables the get_theme command without any pre-configured scope.",
"type": "string",
"enum": [
"theme:allow-get-theme"
]
},
{
"description": "theme:allow-set-theme -> Enables the set_theme command without any pre-configured scope.",
"type": "string",
"enum": [
"theme:allow-set-theme"
]
},
{
"description": "theme:deny-get-theme -> Denies the get_theme command without any pre-configured scope.",
"type": "string",
"enum": [
"theme:deny-get-theme"
]
},
{
"description": "theme:deny-set-theme -> Denies the set_theme command without any pre-configured scope.",
"type": "string",
"enum": [
"theme:deny-set-theme"
]
},
{ {
"description": "tray:default -> Default permissions for the plugin.", "description": "tray:default -> Default permissions for the plugin.",
"type": "string", "type": "string",

View File

@ -5654,6 +5654,40 @@
"shell:deny-stdin-write" "shell:deny-stdin-write"
] ]
}, },
{
"type": "string",
"enum": [
"theme:default"
]
},
{
"description": "theme:allow-get-theme -> Enables the get_theme command without any pre-configured scope.",
"type": "string",
"enum": [
"theme:allow-get-theme"
]
},
{
"description": "theme:allow-set-theme -> Enables the set_theme command without any pre-configured scope.",
"type": "string",
"enum": [
"theme:allow-set-theme"
]
},
{
"description": "theme:deny-get-theme -> Denies the get_theme command without any pre-configured scope.",
"type": "string",
"enum": [
"theme:deny-get-theme"
]
},
{
"description": "theme:deny-set-theme -> Denies the set_theme command without any pre-configured scope.",
"type": "string",
"enum": [
"theme:deny-set-theme"
]
},
{ {
"description": "tray:default -> Default permissions for the plugin.", "description": "tray:default -> Default permissions for the plugin.",
"type": "string", "type": "string",

View File

@ -24,6 +24,7 @@ pub struct Nostr {
} }
fn main() { fn main() {
let mut ctx = tauri::generate_context!();
let invoke_handler = { let invoke_handler = {
let builder = tauri_specta::ts::builder().commands(tauri_specta::collect_commands![ let builder = tauri_specta::ts::builder().commands(tauri_specta::collect_commands![
nostr::relay::get_relays, nostr::relay::get_relays,
@ -135,13 +136,7 @@ fn main() {
Ok(()) Ok(())
}) })
.on_window_event(|window, event| match event { .plugin(tauri_plugin_theme::init(ctx.config_mut()))
tauri::WindowEvent::CloseRequested { api, .. } => {
window.hide().unwrap();
api.prevent_close();
}
_ => {}
})
.plugin(tauri_plugin_decorum::init()) .plugin(tauri_plugin_decorum::init())
.plugin(tauri_plugin_clipboard_manager::init()) .plugin(tauri_plugin_clipboard_manager::init())
.plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_dialog::init())
@ -154,6 +149,6 @@ fn main() {
.plugin(tauri_plugin_upload::init()) .plugin(tauri_plugin_upload::init())
.plugin(tauri_plugin_updater::Builder::new().build()) .plugin(tauri_plugin_updater::Builder::new().build())
.invoke_handler(invoke_handler) .invoke_handler(invoke_handler)
.run(tauri::generate_context!()) .run(ctx)
.expect("error while running tauri application") .expect("error while running tauri application")
} }