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 (
-
- );
-}
-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(