From 5bbc0cd45230216b70c2cd47dd8039b907f4efe8 Mon Sep 17 00:00:00 2001 From: Abdullah Atta Date: Fri, 2 Aug 2024 12:05:27 +0500 Subject: [PATCH] web: add support for changing server urls --- apps/web/src/common/db.ts | 4 +- apps/web/src/components/field/index.tsx | 3 + apps/web/src/components/icons/index.tsx | 4 +- apps/web/src/dialogs/loading-dialog.tsx | 57 ----- .../components/servers-configuration.tsx | 197 ++++++++++++++++++ apps/web/src/dialogs/settings/index.tsx | 8 +- .../src/dialogs/settings/servers-settings.ts | 41 ++++ apps/web/src/dialogs/settings/types.ts | 1 + apps/web/src/stores/setting-store.ts | 14 ++ 9 files changed, 268 insertions(+), 61 deletions(-) delete mode 100644 apps/web/src/dialogs/loading-dialog.tsx create mode 100644 apps/web/src/dialogs/settings/components/servers-configuration.tsx create mode 100644 apps/web/src/dialogs/settings/servers-settings.ts diff --git a/apps/web/src/common/db.ts b/apps/web/src/common/db.ts index da16c96de..40eccb6eb 100644 --- a/apps/web/src/common/db.ts +++ b/apps/web/src/common/db.ts @@ -26,6 +26,7 @@ import { isFeatureSupported } from "../utils/feature-check"; import { generatePassword } from "../utils/password-generator"; import { deriveKey, useKeyStore } from "../interfaces/key-store"; import { logManager } from "@notesnook/core/dist/logger"; +import Config from "../utils/config"; const db = database; async function initializeDatabase(persistence: DatabasePersistence) { @@ -45,7 +46,8 @@ async function initializeDatabase(persistence: DatabasePersistence) { AUTH_HOST: "https://auth.streetwriters.co", SSE_HOST: "https://events.streetwriters.co", ISSUES_HOST: "https://issues.streetwriters.co", - SUBSCRIPTIONS_HOST: "https://subscriptions.streetwriters.co" + SUBSCRIPTIONS_HOST: "https://subscriptions.streetwriters.co", + ...Config.get("serverUrls", {}) }); const storage = new NNStorage( diff --git a/apps/web/src/components/field/index.tsx b/apps/web/src/components/field/index.tsx index 21f1c0398..51b0c805b 100644 --- a/apps/web/src/components/field/index.tsx +++ b/apps/web/src/components/field/index.tsx @@ -55,6 +55,7 @@ function Field(props: FieldProps) { type, inputRef, validate, + disabled, ...inputProps } = props; const [isPasswordVisible, setIsPasswordVisible] = useState(false); @@ -66,6 +67,8 @@ function Field(props: FieldProps) { sx={{ m: "2px", mr: "2px", + opacity: disabled ? 0.7 : 1, + pointerEvents: disabled ? "none" : "all", ...sx, flexDirection: "column" }} diff --git a/apps/web/src/components/icons/index.tsx b/apps/web/src/components/icons/index.tsx index 6ce6253c0..9ec0d9bea 100644 --- a/apps/web/src/components/icons/index.tsx +++ b/apps/web/src/components/icons/index.tsx @@ -214,7 +214,8 @@ import { mdiLink, mdiWindowClose, mdiFileMusicOutline, - mdiBroom + mdiBroom, + mdiServerSecurity } from "@mdi/js"; import { useTheme } from "@emotion/react"; import { Theme } from "@notesnook/theme"; @@ -541,6 +542,7 @@ export const Documentation = createIcon(mdiFileDocumentOutline); export const Legal = createIcon(mdiGavel); export const Desktop = createIcon(mdiDesktopClassic); export const Notification = createIcon(mdiBellBadgeOutline); +export const Servers = createIcon(mdiServerSecurity); export const Calendar = createIcon(mdiCalendarBlank); export const WindowMinimize = createIcon("M4 20v-2h16v2H4Z"); diff --git a/apps/web/src/dialogs/loading-dialog.tsx b/apps/web/src/dialogs/loading-dialog.tsx deleted file mode 100644 index 43e1bce54..000000000 --- a/apps/web/src/dialogs/loading-dialog.tsx +++ /dev/null @@ -1,57 +0,0 @@ -/* -This file is part of the Notesnook project (https://notesnook.com/) - -Copyright (C) 2023 Streetwriters (Private) Limited - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -import { useEffect } from "react"; -import { Box, Text } from "@theme-ui/components"; -import Dialog from "../components/dialog"; -import { Loading } from "../components/icons"; - -type LoadingDialogProps = { - onClose: (result: T | boolean) => void; - action: () => T | Promise; - title: string; - description: string; - message?: string; -}; - -function LoadingDialog(props: LoadingDialogProps) { - const { onClose, action, description, message, title } = props; - useEffect(() => { - (async function () { - try { - onClose(await action()); - } catch (e) { - onClose(false); - throw e; - } - })(); - }, [onClose, action]); - - return ( - - - - {message} - - - - - ); -} -export default LoadingDialog; diff --git a/apps/web/src/dialogs/settings/components/servers-configuration.tsx b/apps/web/src/dialogs/settings/components/servers-configuration.tsx new file mode 100644 index 000000000..e8ebea0a5 --- /dev/null +++ b/apps/web/src/dialogs/settings/components/servers-configuration.tsx @@ -0,0 +1,197 @@ +/* +This file is part of the Notesnook project (https://notesnook.com/) + +Copyright (C) 2023 Streetwriters (Private) Limited + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +import { Button, Flex, Text } from "@theme-ui/components"; +import Field from "../../../components/field"; +import { useState } from "react"; +import { HostId, HostIds, useStore } from "../../../stores/setting-store"; +import { useStore as useUserStore } from "../../../stores/user-store"; +import { ErrorText } from "../../../components/error-text"; +import { TaskManager } from "../../../common/task-manager"; + +export const ServerIds = ["notesnook-sync", "auth", "sse"] as const; +export type ServerId = (typeof ServerIds)[number]; +type Server = { + id: ServerId; + host: HostId; + title: string; + example: string; + description: string; +}; +type VersionResponse = { + version: string; + id: string; + instance: string; +}; +const SERVERS: Server[] = [ + { + id: "notesnook-sync", + host: "API_HOST", + title: "Sync server", + example: "http://localhost:4326", + description: "Server used to sync your notes & other data between devices." + }, + { + id: "auth", + host: "AUTH_HOST", + title: "Auth server", + example: "http://localhost:5326", + description: "Server used for login/sign up and authentication." + }, + { + id: "sse", + host: "SSE_HOST", + title: "Events server", + example: "http://localhost:7326", + description: "Server used to receive important notifications & events." + } +]; +export function ServersConfiguration() { + const [error, setError] = useState(); + const [success, setSuccess] = useState(); + const [urls, setUrls] = useState>>( + useStore.getState().serverUrls + ); + const isLoggedIn = useUserStore((store) => store.isLoggedIn); + return ( + <> + {isLoggedIn ? ( + + ) : null} + + {SERVERS.map((server) => ( + URL.canParse(text)} + defaultValue={urls[server.host]} + onChange={(e) => + setUrls((s) => { + s[server.host] = e.target.value; + return s; + }) + } + /> + ))} + + {success === true ? ( + + Connected to all servers sucessfully. + + ) : null} + + + + + + + + ); +} diff --git a/apps/web/src/dialogs/settings/index.tsx b/apps/web/src/dialogs/settings/index.tsx index 1a12a9f78..8bebe336d 100644 --- a/apps/web/src/dialogs/settings/index.tsx +++ b/apps/web/src/dialogs/settings/index.tsx @@ -36,6 +36,7 @@ import { PasswordAndAuth, Privacy, Pro, + Servers, ShieldLock, Sync } from "../../components/icons"; @@ -74,6 +75,7 @@ import { SubscriptionSettings } from "./subscription-settings"; import { ScopedThemeProvider } from "../../components/theme-provider"; import { AppLockSettings } from "./app-lock-settings"; import { BaseDialogProps, DialogManager } from "../../common/dialog-manager"; +import { ServersSettings } from "./servers-settings"; type SettingsDialogProps = BaseDialogProps; @@ -116,7 +118,8 @@ const sectionGroups: SectionGroup[] = [ icon: Desktop, isHidden: () => !IS_DESKTOP_APP }, - { key: "notifications", title: "Notifications", icon: Notification } + { key: "notifications", title: "Notifications", icon: Notification }, + { key: "servers", title: "Servers", icon: Servers } ] }, { @@ -164,7 +167,8 @@ const SettingsGroups = [ ...LegalSettings, ...SupportSettings, ...AboutSettings, - ...SubscriptionSettings + ...SubscriptionSettings, + ...ServersSettings ]; // Thoughts: diff --git a/apps/web/src/dialogs/settings/servers-settings.ts b/apps/web/src/dialogs/settings/servers-settings.ts new file mode 100644 index 000000000..edb61b23f --- /dev/null +++ b/apps/web/src/dialogs/settings/servers-settings.ts @@ -0,0 +1,41 @@ +/* +This file is part of the Notesnook project (https://notesnook.com/) + +Copyright (C) 2023 Streetwriters (Private) Limited + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +import { ServersConfiguration } from "./components/servers-configuration"; +import { SettingsGroup } from "./types"; + +export const ServersSettings: SettingsGroup[] = [ + { + header: "Servers configuration", + key: "servers", + section: "servers", + settings: [ + { + key: "config", + title: "", + components: [ + { + type: "custom", + component: ServersConfiguration + } + ] + } + ] + } +]; diff --git a/apps/web/src/dialogs/settings/types.ts b/apps/web/src/dialogs/settings/types.ts index ed14740b4..621264f92 100644 --- a/apps/web/src/dialogs/settings/types.ts +++ b/apps/web/src/dialogs/settings/types.ts @@ -28,6 +28,7 @@ export type SectionKeys = | "behaviour" | "desktop" | "notifications" + | "servers" | "editor" | "backup-export" | "export" diff --git a/apps/web/src/stores/setting-store.ts b/apps/web/src/stores/setting-store.ts index 316d5fd24..0966a8e15 100644 --- a/apps/web/src/stores/setting-store.ts +++ b/apps/web/src/stores/setting-store.ts @@ -29,6 +29,8 @@ import { setDocumentTitle } from "../utils/dom"; import { TimeFormat } from "@notesnook/core/dist/utils/date"; import { Profile, TrashCleanupInterval } from "@notesnook/core"; +export const HostIds = ["API_HOST", "AUTH_HOST", "SSE_HOST"] as const; +export type HostId = (typeof HostIds)[number]; class SettingStore extends BaseStore { encryptBackups = Config.get("encryptBackups", false); backupReminderOffset = Config.get("backupReminderOffset", 0); @@ -41,6 +43,7 @@ class SettingStore extends BaseStore { markdownShortcuts = Config.get("markdownShortcuts", true); notificationsSettings = Config.get("notifications", { reminder: true }); isFullOfflineMode = Config.get("fullOfflineMode", false); + serverUrls: Partial> = Config.get("serverUrls", {}); zoomFactor = 1.0; privacyMode = false; @@ -217,6 +220,17 @@ class SettingStore extends BaseStore { if (isFullOfflineMode) db.fs().cancel("offline-mode"); else db.attachments.cacheAttachments(); }; + + setServerUrls = (urls?: Partial>) => { + if (!urls) { + Config.set("serverUrls", {}); + this.set({ serverUrls: {} }); + return; + } + const serverUrls = this.get().serverUrls; + this.set({ serverUrls: { ...serverUrls, ...urls } }); + Config.set("serverUrls", { ...serverUrls, ...urls }); + }; } const [useStore, store] = createStore(