update settings page

This commit is contained in:
Ren Amamiya 2023-07-03 11:49:49 +07:00
parent d35c64e28d
commit 6f29df112b
21 changed files with 440 additions and 40 deletions

View File

@ -41,6 +41,7 @@
"slate-history": "^0.93.0",
"slate-react": "^0.94.2",
"tailwind-merge": "^1.13.2",
"tauri-plugin-autostart-api": "github:tauri-apps/tauri-plugin-autostart#v1",
"tauri-plugin-sql-api": "github:tauri-apps/tauri-plugin-sql",
"zustand": "^4.3.8"
},

View File

@ -85,6 +85,9 @@ dependencies:
tailwind-merge:
specifier: ^1.13.2
version: 1.13.2
tauri-plugin-autostart-api:
specifier: github:tauri-apps/tauri-plugin-autostart#v1
version: github.com/tauri-apps/tauri-plugin-autostart/b331e5700ca2a5d340563efab7398c61812c69f7
tauri-plugin-sql-api:
specifier: github:tauri-apps/tauri-plugin-sql
version: github.com/tauri-apps/tauri-plugin-sql/45b46ee428f6c13a831d237d8b602349ad6b17a6
@ -5530,6 +5533,14 @@ packages:
use-sync-external-store: 1.2.0(react@18.2.0)
dev: false
github.com/tauri-apps/tauri-plugin-autostart/b331e5700ca2a5d340563efab7398c61812c69f7:
resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-autostart/tar.gz/b331e5700ca2a5d340563efab7398c61812c69f7}
name: tauri-plugin-autostart-api
version: 0.0.0
dependencies:
'@tauri-apps/api': 1.4.0
dev: false
github.com/tauri-apps/tauri-plugin-sql/45b46ee428f6c13a831d237d8b602349ad6b17a6:
resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-sql/tar.gz/45b46ee428f6c13a831d237d8b602349ad6b17a6}
name: tauri-plugin-sql-api

51
src-tauri/Cargo.lock generated
View File

@ -244,6 +244,17 @@ dependencies = [
"winapi",
]
[[package]]
name = "auto-launch"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5904a4d734f0235edf29aab320a14899f3e090446e594ff96508a6215f76f89c"
dependencies = [
"dirs",
"thiserror",
"winreg 0.10.1",
]
[[package]]
name = "autocfg"
version = "1.1.0"
@ -864,6 +875,15 @@ dependencies = [
"crypto-common",
]
[[package]]
name = "dirs"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-next"
version = "2.0.0"
@ -874,6 +894,17 @@ dependencies = [
"dirs-sys-next",
]
[[package]]
name = "dirs-sys"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
dependencies = [
"libc",
"redox_users",
"winapi",
]
[[package]]
name = "dirs-sys-next"
version = "0.1.2"
@ -2072,6 +2103,7 @@ dependencies = [
"sqlx-cli",
"tauri",
"tauri-build",
"tauri-plugin-autostart",
"tauri-plugin-single-instance",
"tauri-plugin-sql",
"tauri-plugin-store",
@ -4063,10 +4095,23 @@ dependencies = [
"tauri-utils",
]
[[package]]
name = "tauri-plugin-autostart"
version = "0.0.0"
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#36b7296746bf8d41f0790d8ecd9b097430750a47"
dependencies = [
"auto-launch",
"log",
"serde",
"serde_json",
"tauri",
"thiserror",
]
[[package]]
name = "tauri-plugin-single-instance"
version = "0.0.0"
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=dev#dce0f02bc571128308c30278cde3233f341e6a50"
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#36b7296746bf8d41f0790d8ecd9b097430750a47"
dependencies = [
"log",
"serde",
@ -4080,7 +4125,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-sql"
version = "0.0.0"
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=dev#dce0f02bc571128308c30278cde3233f341e6a50"
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#36b7296746bf8d41f0790d8ecd9b097430750a47"
dependencies = [
"futures-core",
"log",
@ -4096,7 +4141,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-store"
version = "0.0.0"
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=dev#dce0f02bc571128308c30278cde3233f341e6a50"
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#36b7296746bf8d41f0790d8ecd9b097430750a47"
dependencies = [
"log",
"serde",

View File

@ -17,13 +17,14 @@ tauri-build = { version = "1.2", features = [] }
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "1.2", features = [ "clipboard-read-text", "clipboard-write-text", "dialog-open", "fs-read-dir", "fs-read-file", "http-all", "http-multipart", "notification-all", "os-all", "process-relaunch", "shell-open", "system-tray", "updater", "window-close", "window-start-dragging"] }
tauri-plugin-single-instance = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "dev" }
tauri-plugin-store = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "dev" }
tauri-plugin-single-instance = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
tauri-plugin-store = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
tauri-plugin-autostart = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
sqlx-cli = {version = "0.6.3", default-features = false, features = ["sqlite"] }
[dependencies.tauri-plugin-sql]
git = "https://github.com/tauri-apps/plugins-workspace"
branch = "dev"
branch = "v1"
features = ["sqlite"]
[target.'cfg(target_os = "macos")'.dependencies]

View File

@ -2,4 +2,13 @@
INSERT INTO
settings (key, value)
VALUES
("last_login", "0");
("last_login", "0"),
(
"relays",
'["wss://relayable.org","wss://relay.damus.io","wss://relay.nostr.band/all","wss://relay.nostrgraph.net","wss://nostr.mutinywallet.com"]'
),
("auto_start", "0"),
("cache_time", "86400"),
("compose_shortcut", "meta+n"),
("add_imageblock_shortcut", "meta+i"),
("add_feedblock_shortcut", "meta+f")

View File

@ -9,6 +9,7 @@ extern crate objc;
use tauri::{Manager, WindowEvent};
use tauri_plugin_sql::{Migration, MigrationKind};
use tauri_plugin_autostart::MacosLauncher;
#[cfg(target_os = "macos")]
use window_ext::WindowExt;
@ -109,6 +110,7 @@ fn main() {
)
.build(),
)
.plugin(tauri_plugin_autostart::init(MacosLauncher::LaunchAgent, Some(vec!["--flag1", "--flag2"])))
.plugin(tauri_plugin_single_instance::init(|app, argv, cwd| {
println!("{}, {argv:?}, {cwd}", app.package_info().name);
app

View File

@ -16,7 +16,6 @@ import { Root } from "@app/root";
import { AccountSettingsScreen } from "@app/settings/account";
import { GeneralSettingsScreen } from "@app/settings/general";
import { ShortcutsSettingsScreen } from "@app/settings/shortcuts";
import { UpdateSettingsScreen } from "@app/settings/update";
import { SpaceScreen } from "@app/space";
import { TrendingScreen } from "@app/trending";
import { UserScreen } from "@app/user";
@ -88,7 +87,6 @@ const router = createBrowserRouter([
{ path: "general", element: <GeneralSettingsScreen /> },
{ path: "shortcuts", element: <ShortcutsSettingsScreen /> },
{ path: "account", element: <AccountSettingsScreen /> },
{ path: "update", element: <UpdateSettingsScreen /> },
],
},
]);

View File

@ -143,7 +143,8 @@ export function Root() {
const chats = await fetchChats();
// const channels = await fetchChannelMessages();
if (chats) {
await updateLastLogin(dateToUnix());
const now = Math.floor(Date.now() / 1000);
await updateLastLogin(now);
navigate("/app/space", { replace: true });
}
}

View File

@ -1,7 +1,84 @@
import { EyeOffIcon, EyeOnIcon } from "@shared/icons";
import { useAccount } from "@utils/hooks/useAccount";
import { useState } from "react";
export function AccountSettingsScreen() {
const { status, account } = useAccount();
const [type, setType] = useState("password");
const showPrivateKey = () => {
if (type === "password") {
setType("text");
} else {
setType("password");
}
};
return (
<div className="w-full h-full flex items-center justify-center">
<h1>Account</h1>
<div className="w-full h-full px-3 pt-12">
<div className="flex flex-col gap-2">
<h1 className="text-lg font-semibold text-zinc-100">Account</h1>
<div className="">
{status === "loading" ? (
<p>Loading...</p>
) : (
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-1">
<label className="text-base font-semibold text-zinc-400">
Public Key
</label>
<input
readOnly
value={account.pubkey}
className="relative w-2/3 rounded-lg py-3 pl-3.5 pr-11 !outline-none placeholder:text-zinc-400 bg-zinc-800 text-zinc-100"
/>
</div>
<div className="flex flex-col gap-1">
<label className="text-base font-semibold text-zinc-400">
Npub
</label>
<input
readOnly
value={account.npub}
className="relative w-2/3 rounded-lg py-3 pl-3.5 pr-11 !outline-none placeholder:text-zinc-400 bg-zinc-800 text-zinc-100"
/>
</div>
<div className="flex flex-col gap-1">
<label className="text-base font-semibold text-zinc-400">
Private Key
</label>
<div className="relative w-2/3">
<input
readOnly
type={type}
value={account.privkey}
className="relative w-full rounded-lg py-3 pl-3.5 pr-11 !outline-none placeholder:text-zinc-400 bg-zinc-800 text-zinc-100"
/>
<button
type="button"
onClick={() => showPrivateKey()}
className="group absolute right-2 top-1/2 -translate-y-1/2 transform rounded p-1 hover:bg-zinc-700"
>
{type === "password" ? (
<EyeOffIcon
width={20}
height={20}
className="text-zinc-500 group-hover:text-zinc-100"
/>
) : (
<EyeOnIcon
width={20}
height={20}
className="text-zinc-500 group-hover:text-zinc-100"
/>
)}
</button>
</div>
</div>
</div>
)}
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,61 @@
import { Switch } from "@headlessui/react";
import { getSetting, updateSetting } from "@libs/storage";
import { useEffect, useState } from "react";
import { twMerge } from "tailwind-merge";
import { disable, enable, isEnabled } from "tauri-plugin-autostart-api";
export function AutoStartSetting() {
const [enabled, setEnabled] = useState(false);
const toggle = async () => {
if (!enabled) {
await enable();
await updateSetting("auto_start", 1);
console.log(`registered for autostart? ${await isEnabled()}`);
} else {
await disable();
await updateSetting("auto_start", 0);
}
setEnabled(!enabled);
};
useEffect(() => {
async function getAppSetting() {
const setting = await getSetting("auto_start");
if (parseInt(setting) === 0) {
setEnabled(false);
} else {
setEnabled(true);
}
}
getAppSetting();
}, []);
return (
<div className="px-5 py-4 inline-flex items-center justify-between">
<div className="flex flex-col gap-1">
<span className="leading-none font-medium text-zinc-200">
Auto start
</span>
<span className="leading-none text-sm text-zinc-400">
Auto start at login
</span>
</div>
<Switch
checked={enabled}
onChange={toggle}
className={twMerge(
"relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-fuchsia-500 focus:ring-offset-2",
enabled ? "bg-fuchsia-500" : "bg-zinc-700",
)}
>
<span
className={twMerge(
"pointer-events-none inline-block h-5 w-5 transform rounded-full bg-zinc-900 shadow ring-0 transition duration-200 ease-in-out",
enabled ? "translate-x-5" : "translate-x-0",
)}
/>
</Switch>
</div>
);
}

View File

@ -0,0 +1,43 @@
import { getSetting, updateSetting } from "@libs/storage";
import { CheckCircleIcon } from "@shared/icons";
import { useState } from "react";
const setting = await getSetting("cache_time");
const cacheTime = setting;
export function CacheTimeSetting() {
const [time, setTime] = useState(cacheTime);
const update = async () => {
await updateSetting("cache_time", time);
};
return (
<div className="px-5 py-4 inline-flex items-center justify-between">
<div className="flex flex-col gap-1">
<span className="leading-none font-medium text-zinc-200">
Cache time
</span>
<span className="leading-none text-sm text-zinc-400">
The length of time before inactive data gets removed from the cache
</span>
</div>
<div className="inline-flex items-center gap-2">
<input
value={time}
onChange={(e) => setTime(e.currentTarget.value)}
autoCapitalize="none"
autoCorrect="none"
className="w-24 h-8 rounded-md px-2 bg-zinc-800 text-zinc-300 text-right font-medium focus:outline-none"
/>
<button
type="button"
onClick={() => update()}
className="w-8 h-8 inline-flex items-center justify-center font-medium bg-zinc-800 hover:bg-fuchsia-500 rounded-md"
>
<CheckCircleIcon className="w-4 h-4 text-zinc-100" />
</button>
</div>
</div>
);
}

View File

@ -0,0 +1,26 @@
import { RefreshIcon } from "@shared/icons";
import { getVersion } from "@tauri-apps/api/app";
const appVersion = await getVersion();
export function VersionSetting() {
return (
<div className="px-5 py-4 inline-flex items-center justify-between">
<div className="flex flex-col gap-1">
<span className="leading-none font-medium text-zinc-200">Version</span>
<span className="leading-none text-sm text-zinc-400">
You're using latest version
</span>
</div>
<div className="inline-flex items-center gap-2">
<span className="text-zinc-300 font-medium">{appVersion}</span>
<button
type="button"
className="w-8 h-8 inline-flex items-center justify-center font-medium bg-zinc-800 hover:bg-fuchsia-500 rounded-md"
>
<RefreshIcon className="w-4 h-4 text-zinc-100" />
</button>
</div>
</div>
);
}

View File

@ -1,7 +1,20 @@
import { AutoStartSetting } from "@app/settings/components/autoStart";
import { CacheTimeSetting } from "@app/settings/components/cacheTime";
import { VersionSetting } from "@app/settings/components/version";
export function GeneralSettingsScreen() {
return (
<div className="w-full h-full flex items-center justify-center">
<h1>General</h1>
<div className="w-full h-full px-3 pt-12">
<div className="flex flex-col gap-2">
<h1 className="text-lg font-semibold text-zinc-100">General</h1>
<div className="w-full bg-zinc-900 border-t border-zinc-800/50 rounded-xl">
<div className="w-full h-full flex flex-col divide-y divide-zinc-800">
<AutoStartSetting />
<CacheTimeSetting />
<VersionSetting />
</div>
</div>
</div>
</div>
);
}

View File

@ -1,7 +1,110 @@
import { CommandIcon } from "@shared/icons";
export function ShortcutsSettingsScreen() {
return (
<div className="w-full h-full flex items-center justify-center">
<h1>Shortcuts</h1>
<div className="w-full h-full px-3 pt-12">
<div className="flex flex-col gap-2">
<h1 className="text-lg font-semibold text-zinc-100">Shortcuts</h1>
<div className="w-full bg-zinc-900 border-t border-zinc-800/50 rounded-xl">
<div className="w-full h-full flex flex-col divide-y divide-zinc-800">
<div className="px-5 py-4 inline-flex items-center justify-between">
<div className="flex flex-col gap-1">
<span className="leading-none font-medium text-zinc-200">
Open composer
</span>
</div>
<div className="flex items-center gap-2">
<div className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded border-t border-zinc-700/50 bg-zinc-800">
<CommandIcon
width={12}
height={12}
className="text-zinc-500"
/>
</div>
<div className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded border-t border-zinc-700/50 bg-zinc-800">
<span className="text-zinc-500 text-sm leading-none">N</span>
</div>
</div>
</div>
<div className="px-5 py-4 inline-flex items-center justify-between">
<div className="flex flex-col gap-1">
<span className="leading-none font-medium text-zinc-200">
Add image block
</span>
</div>
<div className="flex items-center gap-2">
<div className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded border-t border-zinc-700/50 bg-zinc-800">
<CommandIcon
width={12}
height={12}
className="text-zinc-500"
/>
</div>
<div className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded border-t border-zinc-700/50 bg-zinc-800">
<span className="text-zinc-500 text-sm leading-none">I</span>
</div>
</div>
</div>
<div className="px-5 py-4 inline-flex items-center justify-between">
<div className="flex flex-col gap-1">
<span className="leading-none font-medium text-zinc-200">
Add newsfeed block
</span>
</div>
<div className="flex items-center gap-2">
<div className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded border-t border-zinc-700/50 bg-zinc-800">
<CommandIcon
width={12}
height={12}
className="text-zinc-500"
/>
</div>
<div className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded border-t border-zinc-700/50 bg-zinc-800">
<span className="text-zinc-500 text-sm leading-none">F</span>
</div>
</div>
</div>
<div className="px-5 py-4 inline-flex items-center justify-between">
<div className="flex flex-col gap-1">
<span className="leading-none font-medium text-zinc-200">
Open personal page
</span>
</div>
<div className="flex items-center gap-2">
<div className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded border-t border-zinc-700/50 bg-zinc-800">
<CommandIcon
width={12}
height={12}
className="text-zinc-500"
/>
</div>
<div className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded border-t border-zinc-700/50 bg-zinc-800">
<span className="text-zinc-500 text-sm leading-none">P</span>
</div>
</div>
</div>
<div className="px-5 py-4 inline-flex items-center justify-between">
<div className="flex flex-col gap-1">
<span className="leading-none font-medium text-zinc-200">
Open notification
</span>
</div>
<div className="flex items-center gap-2">
<div className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded border-t border-zinc-700/50 bg-zinc-800">
<CommandIcon
width={12}
height={12}
className="text-zinc-500"
/>
</div>
<div className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded border-t border-zinc-700/50 bg-zinc-800">
<span className="text-zinc-500 text-sm leading-none">B</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
}

View File

@ -1,7 +0,0 @@
export function UpdateSettingsScreen() {
return (
<div className="w-full h-full flex items-center justify-center">
<h1>Update</h1>
</div>
);
}

View File

@ -39,7 +39,7 @@ export async function prefetchEvents(
});
relaySetSubscription.on("eose", () => {
setTimeout(() => resolve(new Set(events.values())), 1200);
setTimeout(() => resolve(new Set(events.values())), 3000);
});
});
}

View File

@ -325,13 +325,34 @@ export async function createChat(
return sender_pubkey;
}
// get setting
export async function getSetting(key: string) {
const db = await connect();
const result = await db.select(
`SELECT value FROM settings WHERE key = "${key}";`,
);
return result[0]?.value;
}
// update setting
export async function updateSetting(key: string, value: string | number) {
const db = await connect();
return await db.execute(
`UPDATE settings SET value = "${value}" WHERE key = "${key}";`,
);
}
// get last login
export async function getLastLogin() {
const db = await connect();
const result = await db.select(
`SELECT value FROM settings WHERE key = "last_login";`,
);
return result[0]?.value;
if (result[0]) {
return parseInt(result[0].value);
} else {
return 0;
}
}
// update last login

View File

@ -1,12 +1,15 @@
import App from "./app";
import { getSetting } from "@libs/storage";
import { RelayProvider } from "@shared/relayProvider";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { createRoot } from "react-dom/client";
const cacheTime = await getSetting("cache_time");
const queryClient = new QueryClient({
defaultOptions: {
queries: {
cacheTime: 1000 * 60 * 60 * 24,
cacheTime: parseInt(cacheTime),
},
},
});

View File

@ -8,7 +8,8 @@ export function VideoPreview({ urls }: { urls: string[] }) {
key={url}
url={url}
width="100%"
className="w-full h-auto border border-zinc-800/50 rounded-lg"
height="auto"
className="!h-auto object-fill rounded-lg overflow-hidden"
controls={true}
pip={true}
/>

View File

@ -1,11 +1,13 @@
import { initNDK } from "@libs/ndk";
import { getSetting } from "@libs/storage";
import NDK from "@nostr-dev-kit/ndk";
import { FULL_RELAYS } from "@stores/constants";
import { createContext } from "react";
export const RelayContext = createContext<NDK>(null);
const ndk = await initNDK(FULL_RELAYS);
const relays = await getSetting("relays");
const relaysArray = JSON.parse(relays);
const ndk = await initNDK(relaysArray);
export function RelayProvider({ children }: { children: React.ReactNode }) {
return <RelayContext.Provider value={ndk}>{children}</RelayContext.Provider>;

View File

@ -49,17 +49,6 @@ export function SettingsLayout() {
>
<span className="font-medium">Account</span>
</NavLink>
<NavLink
to="/settings/update"
className={({ isActive }) =>
twMerge(
"flex h-9 items-center gap-2.5 rounded-md px-2.5 text-zinc-200",
isActive ? "bg-zinc-900/50" : "",
)
}
>
<span className="font-medium">Update</span>
</NavLink>
</div>
</div>
</div>