From c53bdb68e5ce8cb22342a453bc1b8d9457857eda Mon Sep 17 00:00:00 2001 From: reya Date: Sat, 18 Nov 2023 21:17:37 +0700 Subject: [PATCH 1/3] add change theme function --- src-tauri/Cargo.lock | 26 ++++++++++++++++++ src-tauri/Cargo.toml | 1 + src-tauri/src/main.rs | 5 +++- src/app/settings/general.tsx | 51 +++++++++++++++++++++++++++++------- 4 files changed, 73 insertions(+), 10 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index ca452531..ed827dec 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2633,6 +2633,7 @@ dependencies = [ "tauri-plugin-single-instance", "tauri-plugin-sql", "tauri-plugin-store", + "tauri-plugin-theme", "tauri-plugin-updater", "tauri-plugin-upload", "tauri-plugin-window-state", @@ -5172,6 +5173,21 @@ dependencies = [ "thiserror", ] +[[package]] +name = "tauri-plugin-theme" +version = "0.2.0" +source = "git+https://github.com/reyamir/tauri-plugin-theme?branch=tauri-v2#73e8fc84cb4fb8363fee4edcbbab82c98c1874cc" +dependencies = [ + "cocoa 0.25.0", + "futures-lite", + "gtk", + "once_cell", + "serde", + "tauri", + "tintanum", + "tokio", +] + [[package]] name = "tauri-plugin-updater" version = "2.0.0-alpha.4" @@ -5417,6 +5433,16 @@ dependencies = [ "time-core", ] +[[package]] +name = "tintanum" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abbcf9173afc80733c20b7e27a30bc9284d6535bdbde2a70904032de63e16e8" +dependencies = [ + "futures-lite", + "zbus", +] + [[package]] name = "tinyvec" version = "1.6.0" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 15bced4e..bef4bbe7 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -33,6 +33,7 @@ tauri-plugin-autostart = { git = "https://github.com/tauri-apps/plugins-workspac tauri-plugin-store = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" } tauri-plugin-upload = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" } tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" } +tauri-plugin-theme = { git = "https://github.com/reyamir/tauri-plugin-theme", branch = "tauri-v2" } tauri-plugin-sql = { git = "hhttps://github.com/tauri-apps/plugins-workspace", branch = "v2", features = [ "sqlite", ] } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 8de740b8..520a15f1 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -7,6 +7,7 @@ use keyring::Entry; use std::time::Duration; use tauri_plugin_autostart::MacosLauncher; use tauri_plugin_sql::{Migration, MigrationKind}; +use tauri_plugin_theme::ThemePlugin; use webpage::{Webpage, WebpageOptions}; #[derive(Clone, serde::Serialize)] @@ -105,6 +106,7 @@ fn secure_remove(key: String) -> Result<(), ()> { } fn main() { + let mut ctx = tauri::generate_context!(); tauri::Builder::default() .setup(|app| { #[cfg(desktop)] @@ -113,6 +115,7 @@ fn main() { .plugin(tauri_plugin_updater::Builder::new().build())?; Ok(()) }) + .plugin(ThemePlugin::init(ctx.config_mut())) .plugin( tauri_plugin_sql::Builder::default() .add_migrations( @@ -154,6 +157,6 @@ fn main() { secure_load, secure_remove ]) - .run(tauri::generate_context!()) + .run(ctx) .expect("error while running tauri application"); } diff --git a/src/app/settings/general.tsx b/src/app/settings/general.tsx index 0decafaf..1b823c84 100644 --- a/src/app/settings/general.tsx +++ b/src/app/settings/general.tsx @@ -1,5 +1,8 @@ import * as Switch from '@radix-ui/react-switch'; +import { invoke } from '@tauri-apps/api/primitives'; +import { getCurrent } from '@tauri-apps/api/window'; import { useEffect, useState } from 'react'; +import { twMerge } from 'tailwind-merge'; import { useStorage } from '@libs/storage/provider'; @@ -16,8 +19,20 @@ export function GeneralSettingScreen() { appearance: 'system', }); + const changeTheme = async (theme: 'light' | 'dark' | 'auto') => { + await invoke('plugin:theme|set_theme', { + theme, + }); + await db.createSetting('appearance', theme); + // update state + setSettings((prev) => ({ ...prev, appearance: theme })); + }; + useEffect(() => { async function loadSettings() { + const theme = await getCurrent().theme(); + setSettings((prev) => ({ ...prev, appearance: theme })); + const data = await db.getAllSettings(); if (!data) return; @@ -51,12 +66,6 @@ export function GeneralSettingScreen() { ...prev, notification: item.value === '1' ? true : false, })); - - if (item.key === 'appearance') - setSettings((prev) => ({ - ...prev, - appearance: item.value, - })); }); } @@ -133,9 +142,17 @@ export function GeneralSettingScreen() {
+
+ + Website + + + Report a issue + +
); } diff --git a/src/app/settings/advanced.tsx b/src/app/settings/advanced.tsx index 8a5f8660..456c7056 100644 --- a/src/app/settings/advanced.tsx +++ b/src/app/settings/advanced.tsx @@ -1,21 +1,24 @@ +import { useStorage } from '@libs/storage/provider'; + export function AdvancedSettingScreen() { + const { db } = useStorage(); + + const clearCache = async () => { + await db.clearCache(); + }; + return (
-
Event Caches
+
+
Caches
+
Use for boost up NDK
+
-
-
-
User Caches
- diff --git a/src/app/settings/backup.tsx b/src/app/settings/backup.tsx index c0748879..abf3d77f 100644 --- a/src/app/settings/backup.tsx +++ b/src/app/settings/backup.tsx @@ -1,10 +1,19 @@ +import { nip19 } from 'nostr-tools'; import { useEffect, useState } from 'react'; import { useStorage } from '@libs/storage/provider'; +import { EyeOffIcon } from '@shared/icons'; + export function BackupSettingScreen() { const { db } = useStorage(); + const [privkey, setPrivkey] = useState(null); + const [showPassword, setShowPassword] = useState(false); + + const removePrivkey = async () => { + await db.secureRemove(db.account.pubkey); + }; useEffect(() => { async function loadPrivkey() { @@ -24,12 +33,30 @@ export function BackupSettingScreen() { You've stored private key on Lume
) : ( - + <> +
+ + +
+ + )}
diff --git a/src/app/settings/general.tsx b/src/app/settings/general.tsx index 1b823c84..8494906c 100644 --- a/src/app/settings/general.tsx +++ b/src/app/settings/general.tsx @@ -1,6 +1,8 @@ import * as Switch from '@radix-ui/react-switch'; import { invoke } from '@tauri-apps/api/primitives'; import { getCurrent } from '@tauri-apps/api/window'; +import { disable, enable, isEnabled } from '@tauri-apps/plugin-autostart'; +import { isPermissionGranted, requestPermission } from '@tauri-apps/plugin-notification'; import { useEffect, useState } from 'react'; import { twMerge } from 'tailwind-merge'; @@ -20,51 +22,86 @@ export function GeneralSettingScreen() { }); const changeTheme = async (theme: 'light' | 'dark' | 'auto') => { - await invoke('plugin:theme|set_theme', { - theme, - }); - await db.createSetting('appearance', theme); + await invoke('plugin:theme|set_theme', { theme }); // update state setSettings((prev) => ({ ...prev, appearance: theme })); }; + const toggleAutolaunch = async () => { + if (!settings.autolaunch) { + await enable(); + // update state + setSettings((prev) => ({ ...prev, autolaunch: true })); + } else { + await disable(); + // update state + setSettings((prev) => ({ ...prev, autolaunch: false })); + } + }; + + const toggleOutbox = async () => { + await db.createSetting('outbox', String(+!settings.outbox)); + // update state + setSettings((prev) => ({ ...prev, outbox: !settings.outbox })); + }; + + const toggleMedia = async () => { + await db.createSetting('media', String(+!settings.media)); + // update state + setSettings((prev) => ({ ...prev, media: !settings.media })); + }; + + const toggleHashtag = async () => { + await db.createSetting('hashtag', String(+!settings.hashtag)); + // update state + setSettings((prev) => ({ ...prev, hashtag: !settings.hashtag })); + }; + + const toggleNofitication = async () => { + if (settings.notification) return; + + await requestPermission(); + // update state + setSettings((prev) => ({ ...prev, notification: !settings.notification })); + }; + useEffect(() => { async function loadSettings() { const theme = await getCurrent().theme(); setSettings((prev) => ({ ...prev, appearance: theme })); + const autostart = await isEnabled(); + setSettings((prev) => ({ ...prev, autolaunch: autostart })); + + const permissionGranted = await isPermissionGranted(); + setSettings((prev) => ({ ...prev, notification: permissionGranted })); + const data = await db.getAllSettings(); if (!data) return; data.forEach((item) => { - if (item.key === 'autolaunch') - setSettings((prev) => ({ - ...prev, - autolaunch: item.value === '1' ? true : false, - })); - if (item.key === 'outbox') setSettings((prev) => ({ ...prev, - outbox: item.value === '1' ? true : false, + outbox: !!parseInt(item.value), })); if (item.key === 'media') setSettings((prev) => ({ ...prev, - media: item.value === '1' ? true : false, + media: !!parseInt(item.value), })); if (item.key === 'hashtag') setSettings((prev) => ({ ...prev, - hashtag: item.value === '1' ? true : false, + hashtag: !!parseInt(item.value), })); if (item.key === 'notification') setSettings((prev) => ({ ...prev, - notification: item.value === '1' ? true : false, + notification: !!parseInt(item.value), })); }); } @@ -82,6 +119,7 @@ export function GeneralSettingScreen() { toggleAutolaunch()} className="relative h-7 w-12 cursor-default rounded-full bg-neutral-200 outline-none data-[state=checked]:bg-blue-500 dark:bg-neutral-800" > @@ -94,6 +132,7 @@ export function GeneralSettingScreen() { toggleOutbox()} className="relative h-7 w-12 cursor-default rounded-full bg-neutral-200 outline-none data-[state=checked]:bg-blue-500 dark:bg-neutral-800" > @@ -106,6 +145,7 @@ export function GeneralSettingScreen() { toggleMedia()} className="relative h-7 w-12 cursor-default rounded-full bg-neutral-200 outline-none data-[state=checked]:bg-blue-500 dark:bg-neutral-800" > @@ -118,6 +158,7 @@ export function GeneralSettingScreen() { toggleHashtag()} className="relative h-7 w-12 cursor-default rounded-full bg-neutral-200 outline-none data-[state=checked]:bg-blue-500 dark:bg-neutral-800" > @@ -132,6 +173,8 @@ export function GeneralSettingScreen() { toggleNofitication()} className="relative h-7 w-12 cursor-default rounded-full bg-neutral-200 outline-none data-[state=checked]:bg-blue-500 dark:bg-neutral-800" > diff --git a/src/libs/storage/instance.ts b/src/libs/storage/instance.ts index 58cef831..37bc18c9 100644 --- a/src/libs/storage/instance.ts +++ b/src/libs/storage/instance.ts @@ -429,10 +429,20 @@ export class LumeStorage { } public async createSetting(key: string, value: string) { - return await this.db.execute( - 'INSERT OR IGNORE INTO settings (key, value) VALUES ($1, $2);', - [key, value] - ); + const currentSetting = await this.getSettingValue(key); + + if (!currentSetting) + return await this.db.execute( + 'INSERT OR IGNORE INTO settings (key, value) VALUES ($1, $2);', + [key, value] + ); + + const currentValue = !!parseInt(currentSetting); + + return await this.db.execute('UPDATE settings SET value = $1 WHERE key = $2;', [ + +!currentValue, + key, + ]); } public async getAllSettings() { @@ -452,6 +462,12 @@ export class LumeStorage { return results[0].value; } + public async clearCache() { + await this.db.execute('DELETE FROM ndk_events;'); + await this.db.execute('DELETE FROM ndk_eventtags;'); + await this.db.execute('DELETE FROM ndk_users;'); + } + public async accountLogout() { // update current account status await this.db.execute("UPDATE accounts SET is_active = '0' WHERE id = $1;", [ diff --git a/src/shared/layouts/settings.tsx b/src/shared/layouts/settings.tsx index f64972bb..083227a1 100644 --- a/src/shared/layouts/settings.tsx +++ b/src/shared/layouts/settings.tsx @@ -35,7 +35,8 @@ export function SettingsLayout() {
twMerge( 'flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900', From b7a18bea344fc92a6dc8f47bac20cafe15afbed4 Mon Sep 17 00:00:00 2001 From: reya Date: Sun, 19 Nov 2023 14:50:59 +0700 Subject: [PATCH 3/3] respect user settings --- src/app/settings/general.tsx | 2 ++ src/libs/storage/instance.ts | 2 ++ src/libs/storage/provider.tsx | 15 ++++++++++++--- src/utils/hooks/useRichContent.tsx | 17 ++++++++++++----- 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/app/settings/general.tsx b/src/app/settings/general.tsx index 8494906c..e4cd86e0 100644 --- a/src/app/settings/general.tsx +++ b/src/app/settings/general.tsx @@ -47,12 +47,14 @@ export function GeneralSettingScreen() { const toggleMedia = async () => { await db.createSetting('media', String(+!settings.media)); + db.settings.media = !settings.media; // update state setSettings((prev) => ({ ...prev, media: !settings.media })); }; const toggleHashtag = async () => { await db.createSetting('hashtag', String(+!settings.hashtag)); + db.settings.hashtag = !settings.hashtag; // update state setSettings((prev) => ({ ...prev, hashtag: !settings.hashtag })); }; diff --git a/src/libs/storage/instance.ts b/src/libs/storage/instance.ts index 37bc18c9..929b0e3d 100644 --- a/src/libs/storage/instance.ts +++ b/src/libs/storage/instance.ts @@ -20,11 +20,13 @@ export class LumeStorage { public db: Database; public account: Account | null; public platform: Platform | null; + public settings: { outbox: boolean; media: boolean; hashtag: boolean }; constructor(sqlite: Database, platform: Platform) { this.db = sqlite; this.account = null; this.platform = platform; + this.settings = { outbox: false, media: true, hashtag: true }; } public async secureSave(key: string, value: string) { diff --git a/src/libs/storage/provider.tsx b/src/libs/storage/provider.tsx index 9e398e95..3fdbdc13 100644 --- a/src/libs/storage/provider.tsx +++ b/src/libs/storage/provider.tsx @@ -1,4 +1,3 @@ -import { appConfigDir } from '@tauri-apps/api/path'; import { message } from '@tauri-apps/plugin-dialog'; import { platform } from '@tauri-apps/plugin-os'; import { relaunch } from '@tauri-apps/plugin-process'; @@ -29,11 +28,22 @@ const StorageProvider = ({ children }: PropsWithChildren) => { try { const sqlite = await Database.load('sqlite:lume_v2.db'); const platformName = await platform(); - const dir = await appConfigDir(); const lumeStorage = new LumeStorage(sqlite, platformName); if (!lumeStorage.account) await lumeStorage.getActiveAccount(); + const settings = await lumeStorage.getAllSettings(); + if (settings) { + settings.forEach((item) => { + if (item.key === 'outbox') lumeStorage.settings.outbox = !!parseInt(item.value); + + if (item.key === 'media') lumeStorage.settings.media = !!parseInt(item.value); + + if (item.key === 'hashtag') + lumeStorage.settings.hashtag = !!parseInt(item.value); + }); + } + // check update const update = await check(); if (update) { @@ -44,7 +54,6 @@ const StorageProvider = ({ children }: PropsWithChildren) => { } setDB(lumeStorage); - console.info(dir); } catch (e) { await message(`Cannot initialize database: ${e}`, { title: 'Lume', diff --git a/src/utils/hooks/useRichContent.tsx b/src/utils/hooks/useRichContent.tsx index 1e188ad7..4edeefd1 100644 --- a/src/utils/hooks/useRichContent.tsx +++ b/src/utils/hooks/useRichContent.tsx @@ -4,6 +4,8 @@ import { ReactNode } from 'react'; import { Link } from 'react-router-dom'; import reactStringReplace from 'react-string-replace'; +import { useStorage } from '@libs/storage/provider'; + import { Hashtag, ImagePreview, @@ -44,6 +46,8 @@ const VIDEOS = [ ]; export function useRichContent(content: string, textmode: boolean = false) { + const { db } = useStorage(); + let parsedContent: string | ReactNode[] = content.replace(/\n+/g, '\n'); let linkPreview: string; let images: string[] = []; @@ -54,8 +58,10 @@ export function useRichContent(content: string, textmode: boolean = false) { const words = text.split(/( |\n)/); if (!textmode) { - images = words.filter((word) => IMAGES.some((el) => word.endsWith(el))); - videos = words.filter((word) => VIDEOS.some((el) => word.endsWith(el))); + if (db.settings.media) { + images = words.filter((word) => IMAGES.some((el) => word.endsWith(el))); + videos = words.filter((word) => VIDEOS.some((el) => word.endsWith(el))); + } events = words.filter((word) => NOSTR_EVENTS.some((el) => word.startsWith(el))); } @@ -83,9 +89,10 @@ export function useRichContent(content: string, textmode: boolean = false) { if (hashtags.length) { hashtags.forEach((hashtag) => { - parsedContent = reactStringReplace(parsedContent, hashtag, (match, i) => ( - - )); + parsedContent = reactStringReplace(parsedContent, hashtag, (match, i) => { + if (db.settings.hashtag) return ; + return null; + }); }); }