diff --git a/src/components/configuration_item.svelte b/src/components/configuration_item.svelte
new file mode 100644
index 0000000..e49d123
--- /dev/null
+++ b/src/components/configuration_item.svelte
@@ -0,0 +1,421 @@
+
+
+
+
+
+
+ {device?.name ?? device?.deviceID}
+
+
+
+ {#if folder && !isThisDevice}
+
+
+
+
+ Folder ID
+
+ |
+
+ {folder.id}
+ |
+
+
+
+
+
+ Folder Path
+
+ |
+
+ {folder.path}
+ |
+
+
+
+
+
+ Global State
+
+ |
+
+
+ Files: 100, Folders: 50, Storage: ~40 MiB
+ |
+
+
+
+
+
+ Local State
+
+ |
+
+
+ Files: 100, Folders: 50, Storage: ~40 MiB
+ |
+
+
+
+
+
+ Rescans
+
+ |
+
+
+
+
+ 1h
+
+ Enabled
+
+ |
+
+
+
+
+
+ File Versioning
+
+ |
+
+
+ Staggered
+ |
+
+
+
+
+
+ Shared With
+
+ |
+
+
+
+ {folder.devices
+ .map((device) => device.deviceID.slice(0, 7))
+ .join(", ")}
+
+ |
+
+
+
+
+
+ Last Scan
+
+ |
+
+
+ {new Date().toISOString()}
+ |
+
+
+
+
+
+ Latest Change
+
+ |
+
+ {"Updated .ext"}
+ |
+
+ {:else if device && !isThisDevice}
+
+
+
+
+ Last seen
+
+ |
+
+
+ {new Date().toString()}
+ |
+
+
+
+
+
+ Sync Status
+
+ |
+
+
+ up to date
+ |
+
+
+
+
+
+ Addresses
+
+ |
+
+ {device.address.join(", ")}
+ |
+
+
+
+
+
+ Identification
+
+ |
+
+ {
+ new Notice("Not implemented yet!");
+ }}
+ >
+ {device.deviceID.slice(0, 7)}
+
+ |
+
+
+
+
+
+ Folders
+
+ |
+
+ {device.name}
+ |
+
+ {:else if isThisDevice && device}
+
+
+
+
+ Download Rate
+
+ |
+
+ 0 B/s (0 B)
+ |
+
+
+
+
+
+ Upload Rate
+
+ |
+
+ 0 B/s (0 B)
+ |
+
+
+
+
+
+ Local State (Total)
+
+ |
+
+ Files: 100, Folders: 50, Storage: ~40 MiB
+ |
+
+
+
+
+
+ Listeners
+
+ |
+
+ 3/3
+ |
+
+
+
+
+
+ Discovery
+
+ |
+
+ 4/5
+ |
+
+
+
+
+
+ Uptime
+
+ |
+
+ 10h 23m
+ |
+
+
+
+
+
+ Identification
+
+ |
+
+ new Notice("not implement yet !")}
+ >
+ {device.deviceID.slice(0, 7)}
+
+ |
+
+
+
+
+
+ Version
+
+ |
+
+ v1.23.7, Windows, blabla
+ |
+
+ {/if}
+
+
+
+
+
diff --git a/src/components/configuration_table.svelte b/src/components/configuration_table.svelte
new file mode 100644
index 0000000..1c1a452
--- /dev/null
+++ b/src/components/configuration_table.svelte
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
Folders ({folders.length})
+ {#each folders as folder}
+
+ {/each}
+
+
+
+
+
+
+
+
+
+
This Device
+
+
+
+
+ Remote Devices ({devices.filter((value) => value !== thisDevice)
+ .length})
+
+ {#each devices.filter((value) => value !== thisDevice) as device}
+
+ {/each}
+
+
+
+
+
+
+
+
+
diff --git a/src/components/folder_item.svelte b/src/components/folder_item.svelte
new file mode 100644
index 0000000..9b8a9fc
--- /dev/null
+++ b/src/components/folder_item.svelte
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+ {folder.label}
+ Unshared
+
+
+
+
+
+
diff --git a/src/components/obsidian_lucide_icon.svelte b/src/components/obsidian_lucide_icon.svelte
index b6c2df9..2143bfd 100644
--- a/src/components/obsidian_lucide_icon.svelte
+++ b/src/components/obsidian_lucide_icon.svelte
@@ -11,4 +11,4 @@
});
-
+
diff --git a/src/components/remote_item.svelte b/src/components/remote_item.svelte
new file mode 100644
index 0000000..4d112be
--- /dev/null
+++ b/src/components/remote_item.svelte
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+ {device.name ?? device.deviceID}
+
+
+ Disconnected
+
+
+
+
+
+
+
+
diff --git a/src/components/settings_view.svelte b/src/components/settings_view.svelte
index 152ad7b..4b5f6ea 100644
--- a/src/components/settings_view.svelte
+++ b/src/components/settings_view.svelte
@@ -7,10 +7,12 @@
import ObsidianLucideIcon from "./obsidian_lucide_icon.svelte";
import ObsidianSettingsItem from "./obsidian_settings_item.svelte";
import ObsidianToggle from "./obsidian_toggle.svelte";
+ import { ConfigurationModal } from "src/views/configuration_modal";
export let parent: SyncthingSettingTab;
let hasSyncthing = true;
let apiInputType = "password";
let guiPasswordInputType = "password";
+ let syncthingBaseUrl = "";
onMount(async () => {
hasSyncthing = await parent.syncthingController.hasSyncthing();
@@ -26,6 +28,7 @@
parent.plugin.saveSettings();
});
}
+ $: syncthingBaseUrl = `${parent.plugin.settings.configuration.url?.protocol}://${parent.plugin.settings.configuration.url?.ip_address}:${parent.plugin.settings.configuration.url?.port}`;
@@ -169,22 +172,101 @@
{/if}
-
+
- {
- parent.plugin.settings.configuration.syncthingBaseUrl =
- event.currentTarget.value;
- await parent.plugin.saveSettings();
- }}
+
+ style="display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-end;"
+ >
+
+
+
+ {
+ console.log(
+ "ip address change: ",
+ event,
+ event.currentTarget.value
+ );
+ const valueChange = event.currentTarget.value;
+ if (!parent.plugin.settings.configuration.url) {
+ parent.plugin.settings.configuration.url = {
+ protocol: "http",
+ ip_address: "localhost",
+ port: 8384,
+ };
+ }
+ parent.plugin.settings.configuration.url.ip_address =
+ valueChange === "" || !valueChange
+ ? "localhost"
+ : valueChange;
+ await parent.plugin.saveSettings();
+ }}
+ style="border-radius: 0;"
+ />
+ {
+ console.log(
+ "port change: ",
+ event,
+ event.currentTarget.value
+ );
+ const valueChange = event.currentTarget.value;
+ if (!parent.plugin.settings.configuration.url) {
+ parent.plugin.settings.configuration.url = {
+ protocol: "http",
+ ip_address: "localhost",
+ port: 8384,
+ };
+ }
+ parent.plugin.settings.configuration.url.port =
+ isNaN(parseInt(valueChange)) || !valueChange
+ ? 8384
+ : parseInt(valueChange);
+ await parent.plugin.saveSettings();
+ }}
+ style="border-radius: 0 var(--input-radius) var(--input-radius) 0;"
+ />
+
+
+
{
@@ -204,7 +286,7 @@
/>
{
@@ -250,17 +332,9 @@
parent.plugin.settings.gui_username &&
parent.plugin.settings.gui_password
) {
- url = `https://${parent.plugin.settings.gui_username}:${
- parent.plugin.settings.gui_password
- }@${
- parent.plugin.settings.configuration.syncthingBaseUrl ??
- "localhost:8384"
- }`;
+ url = `https://${parent.plugin.settings.gui_username}:${parent.plugin.settings.gui_password}@${syncthingBaseUrl}`;
} else {
- url = `https://${
- parent.plugin.settings.configuration.syncthingBaseUrl ??
- "localhost:8384"
- }`;
+ url = `https://${syncthingBaseUrl}`;
}
// eslint-disable-next-line no-undef
open(url);
@@ -275,7 +349,17 @@
+>
+
+
{#if Platform.isDesktopApp}
diff --git a/src/components/types.ts b/src/components/types.ts
new file mode 100644
index 0000000..8838688
--- /dev/null
+++ b/src/components/types.ts
@@ -0,0 +1,4 @@
+export type ConfigurationItemData = {
+ icon: string;
+ title: string;
+}[];
diff --git a/src/components/warning_message.svelte b/src/components/warning_message.svelte
new file mode 100644
index 0000000..8eff5e2
--- /dev/null
+++ b/src/components/warning_message.svelte
@@ -0,0 +1,75 @@
+
+
+{#if !dismiss}
+
+
+
+
{title}
+
(dismiss = !dismiss)}
+ on:click={() => (dismiss = !dismiss)}
+ class="close-button"
+ />
+
+
+
+{/if}
+
+
diff --git a/src/controllers/utils.ts b/src/controllers/utils.ts
index ac47d36..cc1005b 100644
--- a/src/controllers/utils.ts
+++ b/src/controllers/utils.ts
@@ -158,6 +158,95 @@ export function isStringArray(value: unknown): value is string[] {
return isArrayOf(value, (item): item is string => typeof item === "string");
}
-export function isNumberArray(value: unknown) {
+export function isNumberArray(value: unknown): value is number[] {
return isArrayOf(value, (item): item is number => typeof item === "number");
}
+
+// /**
+// * Validates that a JSON object has the expected fields and types.
+// * @param json The JSON object to validate.
+// * @param field A field to validate.
+// * @param type The type of the field.
+// * @returns `true` if the JSON object is valid, `false` otherwise.
+// */
+// export function validateField(
+// json: unknown,
+// field: string,
+// type: "string" | "number" | "boolean" | "object" | "array"
+// ): json is object & Record
{
+// if (typeof json !== "object" || json === null) {
+// return false;
+// }
+// for (const [key, type] of Object.entries(fields)) {
+// if (!json.hasOwnProperty(key)) {
+// return false;
+// }
+// if (typeof json[key as keyof typeof json] !== typeof type) {
+// return false;
+// }
+// }
+// return true;
+// }
+
+// let json: unknown = { test: 42 };
+// validateJSON(json, { test: 42 });
+// json;
+
+/**
+ * Validates that a JSON object has the expected field and type.
+ *
+ * @param json an unknown json response to validate
+ * @param field a field to validate in the json response
+ * @param type the type of the field to validate
+ * @returns `true` if the JSON object is valid, `false` otherwise. And gives the json object the correct type.
+ *
+ * @example
+ * ```ts
+ * const json: unknown = { test: 42 };
+ * if (!validateField(json, "test", "")) throw new Error("Type error");
+ * json; // should be of type `object & Record<"test", string>`
+ * ```
+ *
+ */
+export function validateJsonField(
+ json: unknown,
+ field: T,
+ type: K
+): json is object & Record {
+ return (
+ typeof json === "object" &&
+ json !== null &&
+ field in json &&
+ typeof json[field as keyof typeof json] === type
+ );
+}
+
+export function logErrorIfNotValidJson(
+ json: unknown,
+ field: T,
+ type: K
+): asserts json is object & Record {
+ if (!validateJsonField(json, field, type)) {
+ console.error(
+ `Error validating JSON: ${field} is not present or is not of type ${typeof type}. Got ${JSON.stringify(
+ json
+ )}.`
+ );
+ }
+}
+// export function throwIfNotValid(
+// json: unknown,
+// field: T,
+// type: "string" | "number" | "boolean" | "object"
+// ): asserts json is object & Record {
+// let checkedType;
+// if (type === "string") checkedType = "";
+// else if (type === "number") checkedType = 42;
+// else if (type === "boolean") checkedType = true;
+// else if (type === "object") checkedType = {};
+// if (!validateField(json, field, checkedType)) {
+// throw new Error(
+// `Error validating JSON: ${field} is not present or is not of type ${typeof type}. Got ${json}.`
+// );
+// }
+// }
diff --git a/src/data/syncthing_remote_datasource.ts b/src/data/syncthing_remote_datasource.ts
index 24ff26f..a1ab75e 100644
--- a/src/data/syncthing_remote_datasource.ts
+++ b/src/data/syncthing_remote_datasource.ts
@@ -1,4 +1,4 @@
-import { requestUrl } from "obsidian";
+import { Platform, requestUrl } from "obsidian";
import SyncthingPlugin from "src/main";
import { SyncthingDevice } from "src/models/entities";
import { RestFailure } from "src/models/failures";
@@ -27,15 +27,17 @@ export class SyncthingFromREST {
/**
* Get all the folders of Syncthing installation using the REST API.
+ * @see https://docs.syncthing.net/rest/config#rest-config-folders-rest-config-devices
*/
async getAllFolders(): Promise {
- const response = await this.requestEndpoint(
- "/rest/system/config/folders"
- );
+ const response = await this.requestEndpoint("/rest/config/folders");
const foldersModel: SyncthingFolderModel[] = [];
- for (const folder of await response.json()) {
+ console.log("REST: ", response.json);
+ for (const folder of response.json) {
console.log("REST: ", folder);
- foldersModel.push(SyncthingFolderModel.fromJSON(folder));
+ foldersModel.push(
+ SyncthingFolderModel.fromJSON(JSON.stringify(folder))
+ );
}
return foldersModel;
}
@@ -57,14 +59,16 @@ export class SyncthingFromREST {
/**
* Get all the devices of Syncthing installation using the REST API.
+ * @see https://docs.syncthing.net/rest/config#rest-config-folders-rest-config-devices
*/
async getDevices(): Promise {
- const response = await this.requestEndpoint(
- "/rest/system/config/devices"
- );
+ const response = await this.requestEndpoint("/rest/config/devices");
const devicesModel: SyncthingDeviceModel[] = [];
- for (const device of await response.json()) {
- devicesModel.push(SyncthingDeviceModel.fromJSON(device));
+ console.log("REST: ", response.json);
+ for (const device of response.json) {
+ devicesModel.push(
+ SyncthingDeviceModel.fromJSON(JSON.stringify(device))
+ );
}
return devicesModel;
}
@@ -75,14 +79,24 @@ export class SyncthingFromREST {
* @see https://docs.syncthing.net/rest/config.html
*/
async getConfiguration(): Promise {
- const response = await this.requestEndpoint("/rest/system/config");
- return SyncthingConfigurationModel.fromJSON(await response.json());
+ const response = await this.requestEndpoint("/rest/config");
+ return SyncthingConfigurationModel.fromJSON(response.json);
}
+ /**
+ * Private method to request an endpoint of the REST API.
+ * The endpoint should start with a `/`.
+ *
+ * @param endpoint - The REST endpoint to call. @see https://docs.syncthing.net/dev/rest.html
+ * @returns The response of the REST API.
+ */
private async requestEndpoint(endpoint: string) {
// FIXME: Fix the issue when connecting to the REST API. (error 403)
console.log("requestEndpoint: Endpoint", endpoint);
- const url = `${this.plugin.settings.configuration.syncthingBaseUrl}/${endpoint}`;
+ let ip_address = this.plugin.settings.configuration.url?.ip_address;
+ if (ip_address === "localhost" && Platform.isMobileApp)
+ ip_address = "127.0.0.1";
+ const url = `${this.plugin.settings.configuration.url?.protocol}://${ip_address}:${this.plugin.settings.configuration.url?.port}${endpoint}`;
const response = requestUrl({
url: url,
method: "GET",
@@ -91,6 +105,7 @@ export class SyncthingFromREST {
Accept: "*/*",
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
+ redirect: "follow",
},
});
console.log(
diff --git a/src/main.ts b/src/main.ts
index d340b9f..aba2e5b 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -4,9 +4,7 @@ import {
DevModeModal,
PluginDevModeController,
} from "./controllers/plugin_dev_mode";
-import {
- SyncthingFromAndroid
-} from "./data/syncthing_android_datasource";
+import { SyncthingFromAndroid } from "./data/syncthing_android_datasource";
import { SyncthingFromCLI } from "./data/syncthing_local_datasource";
import { SyncthingFromREST } from "./data/syncthing_remote_datasource";
import { SyncthingConfiguration } from "./models/entities";
@@ -23,7 +21,13 @@ interface SyncthingPluginSettings {
}
const DEFAULT_SETTINGS: Partial = {
- configuration: { syncthingBaseUrl: "localhost:8384" },
+ configuration: {
+ url: {
+ protocol: "http",
+ ip_address: "localhost",
+ port: 8384,
+ },
+ },
devMode: false,
};
diff --git a/src/models/entities.ts b/src/models/entities.ts
index 46be081..c3f73d8 100644
--- a/src/models/entities.ts
+++ b/src/models/entities.ts
@@ -14,10 +14,19 @@ export class SyncthingConfiguration {
public version: string,
public folders: SyncthingFolder[],
public devices: SyncthingDevice[],
- public syncthingBaseUrl: string
+ public url: SyncthingURL
) {}
}
+/**
+ * Simple URL object.
+ */
+export type SyncthingURL = {
+ protocol: "https" | "http";
+ ip_address: string | "localhost";
+ port: number;
+};
+
/**
* Available sync types in Syncthing.
* @see https://docs.syncthing.net/users/config.html#config-option-folder.type
@@ -64,7 +73,7 @@ export class SyncthingDevice {
*/
public deviceID: string,
public introducedBy: string,
- public encryptionPassword: string,
+ // public encryptionPassword: string, // TODO: move to the Folder element.
public address: string[],
public paused: boolean,
public ignoredFolders: string[],
@@ -79,7 +88,7 @@ export class ReducedSyncthingDevice
implements
Pick<
SyncthingDevice,
- "deviceID" | "introducedBy" | "encryptionPassword"
+ "deviceID" | "introducedBy" /* | "encryptionPassword" */
>
{
constructor(
diff --git a/src/models/models.ts b/src/models/models.ts
index 40220eb..3aaf863 100644
--- a/src/models/models.ts
+++ b/src/models/models.ts
@@ -1,10 +1,11 @@
-import { isStringArray } from "src/controllers/utils";
+import { isStringArray, logErrorIfNotValidJson } from "src/controllers/utils";
import {
ReducedSyncthingDevice,
SyncTypes,
SyncthingConfiguration,
SyncthingDevice,
SyncthingFolder,
+ SyncthingURL,
} from "src/models/entities";
export class SyncthingConfigurationModel extends SyncthingConfiguration {
@@ -12,9 +13,13 @@ export class SyncthingConfigurationModel extends SyncthingConfiguration {
version: string,
folders: SyncthingFolder[],
devices: SyncthingDevice[],
- syncthingBaseUrl = "localhost:8384"
+ url: SyncthingURL = {
+ protocol: "http",
+ ip_address: "localhost",
+ port: 8384,
+ }
) {
- super(version, folders, devices, syncthingBaseUrl);
+ super(version, folders, devices, url);
}
/**
@@ -26,19 +31,19 @@ export class SyncthingConfigurationModel extends SyncthingConfiguration {
const folders: SyncthingFolder[] = [];
if (!(typeof parsedJSON === "object" && parsedJSON !== null))
throw new Error("JSON is not an object or is null");
+ logErrorIfNotValidJson(parsedJSON, "version", "string");
if (
!(
- "folders" in parsedJSON && Array.isArray(parsedJSON["folders"])
- ) ||
- !(
- "devices" in parsedJSON && Array.isArray(parsedJSON["devices"])
+ (
+ "folders" in parsedJSON &&
+ Array.isArray(parsedJSON["folders"])
+ )
+ // TODO: validate the custom type.
) ||
- !(
- "version" in parsedJSON &&
- typeof parsedJSON["version"] === "string"
- )
+ !("devices" in parsedJSON && Array.isArray(parsedJSON["devices"]))
+ // TODO: validate the custom type.
)
- throw new Error("Error parsing JSON");
+ throw new Error("Error parsing JSON: missing fields or wrong type");
for (const parsedFolder of parsedJSON["folders"]) {
console.log(parsedFolder);
folders.push(
@@ -69,64 +74,23 @@ export class SyncthingConfigurationModel extends SyncthingConfiguration {
export class SyncthingFolderModel extends SyncthingFolder {
static fromJSON(json: string): SyncthingFolderModel {
const parsedJSON = JSON.parse(json);
- const reducedDeviceInfos: ReducedSyncthingDevice[] = [];
+ const reducedDeviceInfos: ReducedSyncthingDeviceModel[] = [];
// TODO: to refactor w/ a function.
if (!(typeof parsedJSON === "object" && parsedJSON !== null))
throw new Error("JSON is not an object or is null");
- if (
- !(
- "folders" in parsedJSON && Array.isArray(parsedJSON["folders"])
- ) ||
- !(
- "devices" in parsedJSON && Array.isArray(parsedJSON["devices"])
- ) ||
- !("id" in parsedJSON && typeof parsedJSON["id"] === "string") ||
- !(
- "label" in parsedJSON && typeof parsedJSON["label"] === "string"
- ) ||
- !("path" in parsedJSON && typeof parsedJSON["path"] === "string") ||
- !(
- "filesystemType" in parsedJSON &&
- typeof parsedJSON["filesystemType"] === "string"
- ) ||
- !("type" in parsedJSON && typeof parsedJSON["type"] === "string") ||
- !(
- "maxConflicts" in parsedJSON &&
- typeof parsedJSON["maxConflicts"] === "number"
- )
- )
- throw new Error("Error parsing JSON");
+ logErrorIfNotValidJson(parsedJSON, "id", "string");
+ logErrorIfNotValidJson(parsedJSON, "label", "string");
+ logErrorIfNotValidJson(parsedJSON, "path", "string");
+ logErrorIfNotValidJson(parsedJSON, "filesystemType", "string");
+ logErrorIfNotValidJson(parsedJSON, "type", "string");
+ logErrorIfNotValidJson(parsedJSON, "maxConflicts", 0);
+ if (!("devices" in parsedJSON && Array.isArray(parsedJSON["devices"])))
+ throw new Error(
+ "Error validating JSON: devices is not present or is not an array"
+ );
for (const device of parsedJSON["devices"]) {
- // TODO: to refactor w/ a function.
- if (!(typeof device === "object" && device !== null))
- throw new Error("Error parsing JSON");
- if (
- !(
- "deviceID" in device &&
- typeof device["deviceID"] === "string"
- )
- )
- throw new Error("Error parsing JSON");
- if (
- !(
- "introducedBy" in device &&
- typeof device["introducedBy"] === "string"
- )
- )
- throw new Error("Error parsing JSON");
- if (
- !(
- "encryptionPassword" in device &&
- typeof device["encryptionPassword"] === "string"
- )
- )
- throw new Error("Error parsing JSON");
reducedDeviceInfos.push(
- new ReducedSyncthingDevice(
- device["deviceID"],
- device["introducedBy"],
- device["encryptionPassword"]
- )
+ ReducedSyncthingDeviceModel.fromJSON(JSON.stringify(device))
);
}
return new SyncthingFolderModel(
@@ -152,50 +116,36 @@ export class SyncthingDeviceModel extends SyncthingDevice {
// TODO: to refactor w/ a function.
if (!(typeof parsedJSON === "object" && parsedJSON !== null))
throw new Error("JSON is not an object or is null");
+ logErrorIfNotValidJson(parsedJSON, "deviceID", "string");
+ logErrorIfNotValidJson(parsedJSON, "introducedBy", "string");
+ logErrorIfNotValidJson(parsedJSON, "paused", true);
if (
!(
"addresses" in parsedJSON &&
isStringArray(parsedJSON["addresses"])
) ||
!(
- "ignoredFolders" in parsedJSON &&
- isStringArray(parsedJSON["ignoredFolders"])
- ) ||
- !(
- "deviceID" in parsedJSON &&
- typeof parsedJSON["deviceID"] === "string"
- ) ||
- !(
- "introducedBy" in parsedJSON &&
- typeof parsedJSON["introducedBy"] === "string"
- ) ||
- !(
- "encryptionPassword" in parsedJSON &&
- typeof parsedJSON["encryptionPassword"] === "string"
+ ("ignoredFolders" in parsedJSON) /*&&*/
+ // (isStringArray(parsedJSON["ignoredFolders"]) ||
+ // Array.isArray(
+ // parsedJSON["ignoredFolders"] &&
+ // (parsedJSON["ignoredFolders"] as Array)
+ // .length === 0
+ // ))
) ||
!(
"name" in parsedJSON &&
- typeof parsedJSON["name"] === "string" &&
- typeof parsedJSON["name"] === "undefined"
- ) ||
- !("type" in parsedJSON && typeof parsedJSON["type"] === "string") ||
- !(
- "maxConflicts" in parsedJSON &&
- typeof parsedJSON["maxConflicts"] === "number"
- ) ||
- !(
- "paused" in parsedJSON &&
- typeof parsedJSON["paused"] === "boolean"
+ (typeof parsedJSON["name"] === "string" ||
+ typeof parsedJSON["name"] === "undefined")
)
)
- throw new Error("Error parsing JSON");
+ throw new Error("Error validating JSON");
return new SyncthingDeviceModel(
parsedJSON["deviceID"],
parsedJSON["introducedBy"],
- parsedJSON["encryptionPassword"],
parsedJSON["addresses"],
parsedJSON["paused"],
- parsedJSON["ignoredFolders"],
+ parsedJSON["ignoredFolders"] as Array, // TODO: refactor this.
parsedJSON["name"] ?? ""
);
}
@@ -204,3 +154,23 @@ export class SyncthingDeviceModel extends SyncthingDevice {
return JSON.stringify(this);
}
}
+
+export class ReducedSyncthingDeviceModel extends ReducedSyncthingDevice {
+ static fromJSON(json: string): ReducedSyncthingDeviceModel {
+ const parsedJSON = JSON.parse(json);
+ if (!(typeof parsedJSON === "object" && parsedJSON !== null))
+ throw new Error("Error parsing JSON");
+ logErrorIfNotValidJson(parsedJSON, "deviceID", "string");
+ logErrorIfNotValidJson(parsedJSON, "introducedBy", "string");
+ logErrorIfNotValidJson(parsedJSON, "encryptionPassword", "string");
+ return new ReducedSyncthingDeviceModel(
+ parsedJSON["deviceID"],
+ parsedJSON["introducedBy"],
+ parsedJSON["encryptionPassword"]
+ );
+ }
+
+ toJSON(): string {
+ throw new Error("Method not implemented.");
+ }
+}
diff --git a/src/views/configuration_modal.ts b/src/views/configuration_modal.ts
new file mode 100644
index 0000000..9d9202b
--- /dev/null
+++ b/src/views/configuration_modal.ts
@@ -0,0 +1,31 @@
+import { App, Modal } from "obsidian";
+import ConfigurationTable from "src/components/configuration_table.svelte";
+import SyncthingPlugin from "src/main";
+import { SvelteComponent } from "svelte";
+
+export class ConfigurationModal extends Modal {
+ private components: SvelteComponent[] = [];
+ constructor(app: App, public plugin: SyncthingPlugin) {
+ super(app);
+ }
+
+ async onOpen() {
+ this.modalEl.style.width = "100%";
+ this.modalEl.style.height = "100%";
+
+ this.components.push(
+ new ConfigurationTable({
+ target: this.contentEl,
+ props: {
+ parent: this,
+ },
+ })
+ );
+ }
+
+ onClose() {
+ const { contentEl } = this;
+ this.components.forEach((component) => component.$destroy());
+ contentEl.empty();
+ }
+}