mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2025-12-11 00:35:56 +00:00
### Fixed
- P2P Replication got more robust and stable. ### Breaking changes - Send configuration via Peer-to-Peer connection is not compatible with older versions.
This commit is contained in:
2564
package-lock.json
generated
2564
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -94,9 +94,9 @@
|
||||
"fflate": "^0.8.2",
|
||||
"idb": "^8.0.3",
|
||||
"minimatch": "^10.0.2",
|
||||
"octagonal-wheels": "^0.1.41",
|
||||
"octagonal-wheels": "^0.1.42",
|
||||
"qrcode-generator": "^1.4.4",
|
||||
"trystero": "github:vrtmrz/trystero#9e892a93ec14eeb57ce806d272fbb7c3935256d8",
|
||||
"trystero": "^0.22.0",
|
||||
"xxhash-wasm-102": "npm:xxhash-wasm@^1.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,6 +251,9 @@
|
||||
};
|
||||
cmdSync.setConfig(initialDialogStatusKey, JSON.stringify(dialogStatus));
|
||||
});
|
||||
let isObsidian = $derived.by(() => {
|
||||
return plugin.services.API.getPlatform() === "obsidian";
|
||||
});
|
||||
</script>
|
||||
|
||||
<article>
|
||||
@@ -266,95 +269,105 @@
|
||||
{/each}
|
||||
</details>
|
||||
<h2>Connection Settings</h2>
|
||||
<details bind:open={isSettingOpened}>
|
||||
<summary>{eRelay}</summary>
|
||||
<table class="settings">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th> Enable P2P Replicator </th>
|
||||
<td>
|
||||
<label class={{ "is-dirty": isP2PEnabledModified }}>
|
||||
<input type="checkbox" bind:checked={eP2PEnabled} />
|
||||
</label>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<th> Relay settings </th>
|
||||
<td>
|
||||
<label class={{ "is-dirty": isRelayModified }}>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="wss://exp-relay.vrtmrz.net, wss://xxxxx"
|
||||
bind:value={eRelay}
|
||||
autocomplete="off"
|
||||
/>
|
||||
<button onclick={() => useDefaultRelay()}> Use vrtmrz's relay </button>
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th> Room ID </th>
|
||||
<td>
|
||||
<label class={{ "is-dirty": isRoomIdModified }}>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="anything-you-like"
|
||||
bind:value={eRoomId}
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
autocorrect="off"
|
||||
/>
|
||||
<button onclick={() => chooseRandom()}> Use Random Number </button>
|
||||
</label>
|
||||
<span>
|
||||
<small>
|
||||
This can isolate your connections between devices. Use the same Room ID for the same
|
||||
devices.</small
|
||||
>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th> Password </th>
|
||||
<td>
|
||||
<label class={{ "is-dirty": isPasswordModified }}>
|
||||
<input type="password" placeholder="password" bind:value={ePassword} />
|
||||
</label>
|
||||
<span>
|
||||
<small> This password is used to encrypt the connection. Use something long enough. </small>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th> This device name </th>
|
||||
<td>
|
||||
<label class={{ "is-dirty": isDeviceNameModified }}>
|
||||
<input type="text" placeholder="iphone-16" bind:value={eDeviceName} autocomplete="off" />
|
||||
</label>
|
||||
<span>
|
||||
<small>
|
||||
Device name to identify the device. Please use shorter one for the stable peer
|
||||
detection, i.e., "iphone-16" or "macbook-2021".
|
||||
</small>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th> Auto Connect </th>
|
||||
<td>
|
||||
<label class={{ "is-dirty": isAutoStartModified }}>
|
||||
<input type="checkbox" bind:checked={eAutoStart} />
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th> Start change-broadcasting on Connect </th>
|
||||
<td>
|
||||
<label class={{ "is-dirty": isAutoBroadcastModified }}>
|
||||
<input type="checkbox" bind:checked={eAutoBroadcast} />
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- <tr>
|
||||
{#if isObsidian}
|
||||
You can configure in the Obsidian Plugin Settings.
|
||||
{:else}
|
||||
<details bind:open={isSettingOpened}>
|
||||
<summary>{eRelay}</summary>
|
||||
<table class="settings">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th> Enable P2P Replicator </th>
|
||||
<td>
|
||||
<label class={{ "is-dirty": isP2PEnabledModified }}>
|
||||
<input type="checkbox" bind:checked={eP2PEnabled} />
|
||||
</label>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<th> Relay settings </th>
|
||||
<td>
|
||||
<label class={{ "is-dirty": isRelayModified }}>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="wss://exp-relay.vrtmrz.net, wss://xxxxx"
|
||||
bind:value={eRelay}
|
||||
autocomplete="off"
|
||||
/>
|
||||
<button onclick={() => useDefaultRelay()}> Use vrtmrz's relay </button>
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th> Room ID </th>
|
||||
<td>
|
||||
<label class={{ "is-dirty": isRoomIdModified }}>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="anything-you-like"
|
||||
bind:value={eRoomId}
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
autocorrect="off"
|
||||
/>
|
||||
<button onclick={() => chooseRandom()}> Use Random Number </button>
|
||||
</label>
|
||||
<span>
|
||||
<small>
|
||||
This can isolate your connections between devices. Use the same Room ID for the same
|
||||
devices.</small
|
||||
>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th> Password </th>
|
||||
<td>
|
||||
<label class={{ "is-dirty": isPasswordModified }}>
|
||||
<input type="password" placeholder="password" bind:value={ePassword} />
|
||||
</label>
|
||||
<span>
|
||||
<small>
|
||||
This password is used to encrypt the connection. Use something long enough.
|
||||
</small>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th> This device name </th>
|
||||
<td>
|
||||
<label class={{ "is-dirty": isDeviceNameModified }}>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="iphone-16"
|
||||
bind:value={eDeviceName}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</label>
|
||||
<span>
|
||||
<small>
|
||||
Device name to identify the device. Please use shorter one for the stable peer
|
||||
detection, i.e., "iphone-16" or "macbook-2021".
|
||||
</small>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th> Auto Connect </th>
|
||||
<td>
|
||||
<label class={{ "is-dirty": isAutoStartModified }}>
|
||||
<input type="checkbox" bind:checked={eAutoStart} />
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th> Start change-broadcasting on Connect </th>
|
||||
<td>
|
||||
<label class={{ "is-dirty": isAutoBroadcastModified }}>
|
||||
<input type="checkbox" bind:checked={eAutoBroadcast} />
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- <tr>
|
||||
<th> Auto Accepting </th>
|
||||
<td>
|
||||
<label class={{ "is-dirty": isAutoAcceptModified }}>
|
||||
@@ -362,11 +375,12 @@
|
||||
</label>
|
||||
</td>
|
||||
</tr> -->
|
||||
</tbody>
|
||||
</table>
|
||||
<button disabled={!isAnyModified} class="button mod-cta" onclick={saveAndApply}>Save and Apply</button>
|
||||
<button disabled={!isAnyModified} class="button" onclick={revert}>Revert changes</button>
|
||||
</details>
|
||||
</tbody>
|
||||
</table>
|
||||
<button disabled={!isAnyModified} class="button mod-cta" onclick={saveAndApply}>Save and Apply</button>
|
||||
<button disabled={!isAnyModified} class="button" onclick={revert}>Revert changes</button>
|
||||
</details>
|
||||
{/if}
|
||||
|
||||
<div>
|
||||
<h2>Signaling Server Connection</h2>
|
||||
|
||||
2
src/lib
2
src/lib
Submodule src/lib updated: 82c68435a1...b1597d6878
@@ -33,7 +33,8 @@ import { ModuleLog } from "./modules/features/ModuleLog.ts";
|
||||
import { ModuleObsidianSettings } from "./modules/features/ModuleObsidianSetting.ts";
|
||||
import { ModuleRedFlag } from "./modules/coreFeatures/ModuleRedFlag.ts";
|
||||
import { ModuleObsidianMenu } from "./modules/essentialObsidian/ModuleObsidianMenu.ts";
|
||||
import { ModuleSetupObsidian, SetupManager } from "./modules/features/ModuleSetupObsidian.ts";
|
||||
import { ModuleSetupObsidian } from "./modules/features/ModuleSetupObsidian.ts";
|
||||
import { SetupManager } from "./modules/features/SetupManager.ts";
|
||||
import type { StorageAccess } from "./modules/interfaces/StorageAccess.ts";
|
||||
import type { Confirm } from "./lib/src/interfaces/Confirm.ts";
|
||||
import type { Rebuilder } from "./modules/interfaces/DatabaseRebuilder.ts";
|
||||
|
||||
@@ -15,7 +15,7 @@ import { isMetaEntry } from "../../lib/src/common/types.ts";
|
||||
import { isDeletedEntry, isDocContentSame, isLoadedEntry, readAsBlob } from "../../lib/src/common/utils.ts";
|
||||
import { countCompromisedChunks } from "../../lib/src/pouchdb/negotiation.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
import { SetupManager } from "../features/ModuleSetupObsidian.ts";
|
||||
import { SetupManager } from "../features/SetupManager.ts";
|
||||
|
||||
type ErrorInfo = {
|
||||
path: string;
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
DEFAULT_SETTINGS,
|
||||
type ObsidianLiveSyncSettings,
|
||||
SALT_OF_PASSPHRASE,
|
||||
SETTING_KEY_P2P_DEVICE_NAME,
|
||||
} from "../../lib/src/common/types";
|
||||
import { LOG_LEVEL_NOTICE, LOG_LEVEL_URGENT } from "octagonal-wheels/common/logger";
|
||||
import { $msg, setLang } from "../../lib/src/common/i18n.ts";
|
||||
@@ -111,6 +112,11 @@ export class ModuleObsidianSettings extends AbstractObsidianModule {
|
||||
this.services.setting.saveDeviceAndVaultName();
|
||||
const settings = { ...this.settings };
|
||||
settings.deviceAndVaultName = "";
|
||||
if (settings.P2P_DevicePeerName && settings.P2P_DevicePeerName.trim() !== "") {
|
||||
console.log("Saving device peer name to small config");
|
||||
this.services.config.setSmallConfig(SETTING_KEY_P2P_DEVICE_NAME, settings.P2P_DevicePeerName.trim());
|
||||
settings.P2P_DevicePeerName = "";
|
||||
}
|
||||
if (this.usedPassphrase == "" && !(await this.getPassphrase(settings))) {
|
||||
this._log("Failed to retrieve passphrase. data.json contains unencrypted items!", LOG_LEVEL_NOTICE);
|
||||
} else {
|
||||
|
||||
@@ -1,18 +1,10 @@
|
||||
import {
|
||||
type ObsidianLiveSyncSettings,
|
||||
DEFAULT_SETTINGS,
|
||||
LOG_LEVEL_NOTICE,
|
||||
LOG_LEVEL_VERBOSE,
|
||||
REMOTE_COUCHDB,
|
||||
REMOTE_MINIO,
|
||||
REMOTE_P2P,
|
||||
} from "../../lib/src/common/types.ts";
|
||||
import { SETTING_KEY_P2P_DEVICE_NAME } from "../../lib/src/common/types.ts";
|
||||
import { type ObsidianLiveSyncSettings, LOG_LEVEL_NOTICE } from "../../lib/src/common/types.ts";
|
||||
import { configURIBase } from "../../common/types.ts";
|
||||
// import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser.js";
|
||||
import { fireAndForget, generatePatchObj, isObjectDifferent } from "../../lib/src/common/utils.ts";
|
||||
import { fireAndForget } from "../../lib/src/common/utils.ts";
|
||||
import {
|
||||
EVENT_REQUEST_COPY_SETUP_URI,
|
||||
EVENT_REQUEST_OPEN_P2P_SETTINGS,
|
||||
EVENT_REQUEST_OPEN_SETUP_URI,
|
||||
EVENT_REQUEST_SHOW_SETUP_QR,
|
||||
eventHub,
|
||||
@@ -21,268 +13,13 @@ import { AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { $msg } from "../../lib/src/common/i18n.ts";
|
||||
// import { performDoctorConsultation, RebuildOptions } from "@/lib/src/common/configForDoc.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
import { SvelteDialogManager } from "./SetupWizard/ObsidianSvelteDialog.ts";
|
||||
import Intro from "./SetupWizard/dialogs/Intro.svelte";
|
||||
import SelectMethodNewUser from "./SetupWizard/dialogs/SelectMethodNewUser.svelte";
|
||||
import SelectMethodExisting from "./SetupWizard/dialogs/SelectMethodExisting.svelte";
|
||||
import ScanQRCode from "./SetupWizard/dialogs/ScanQRCode.svelte";
|
||||
import UseSetupURI from "./SetupWizard/dialogs/UseSetupURI.svelte";
|
||||
import OutroNewUser from "./SetupWizard/dialogs/OutroNewUser.svelte";
|
||||
import OutroExistingUser from "./SetupWizard/dialogs/OutroExistingUser.svelte";
|
||||
import OutroAskUserMode from "./SetupWizard/dialogs/OutroAskUserMode.svelte";
|
||||
import SetupRemote from "./SetupWizard/dialogs/SetupRemote.svelte";
|
||||
import SetupRemoteCouchDB from "./SetupWizard/dialogs/SetupRemoteCouchDB.svelte";
|
||||
import SetupRemoteBucket from "./SetupWizard/dialogs/SetupRemoteBucket.svelte";
|
||||
import SetupRemoteP2P from "./SetupWizard/dialogs/SetupRemoteP2P.svelte";
|
||||
import SetupRemoteE2EE from "./SetupWizard/dialogs/SetupRemoteE2EE.svelte";
|
||||
import {
|
||||
decodeSettingsFromQRCodeData,
|
||||
encodeQR,
|
||||
encodeSettingsToQRCodeData,
|
||||
encodeSettingsToSetupURI,
|
||||
OutputFormat,
|
||||
} from "../../lib/src/API/processSetting.ts";
|
||||
// import type ObsidianLiveSyncPlugin from "../../main.ts";
|
||||
export const enum UserMode {
|
||||
NewUser = "new-user",
|
||||
ExistingUser = "existing-user",
|
||||
Unknown = "unknown",
|
||||
// eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values
|
||||
Update = "unknown", // Alias for Unknown for better readability
|
||||
}
|
||||
|
||||
export class SetupManager extends AbstractObsidianModule {
|
||||
private dialogManager: SvelteDialogManager = new SvelteDialogManager(this.plugin);
|
||||
|
||||
async startOnBoarding(): Promise<boolean> {
|
||||
const isUserNewOrExisting = await this.dialogManager.openWithExplicitCancel(Intro);
|
||||
if (isUserNewOrExisting === "new-user") {
|
||||
await this.onBoard(UserMode.NewUser);
|
||||
} else if (isUserNewOrExisting === "existing-user") {
|
||||
await this.onBoard(UserMode.ExistingUser);
|
||||
} else if (isUserNewOrExisting === "cancelled") {
|
||||
this._log("Onboarding cancelled by user.", LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async onBoard(userMode: UserMode): Promise<boolean> {
|
||||
const originalSetting = userMode === UserMode.NewUser ? DEFAULT_SETTINGS : this.core.settings;
|
||||
if (userMode === UserMode.NewUser) {
|
||||
//Ask how to apply initial setup
|
||||
const method = await this.dialogManager.openWithExplicitCancel(SelectMethodNewUser);
|
||||
if (method === "use-setup-uri") {
|
||||
await this.onUseSetupURI(userMode);
|
||||
} else if (method === "configure-manually") {
|
||||
await this.onConfigureManually(originalSetting, userMode);
|
||||
} else if (method === "cancelled") {
|
||||
this._log("Onboarding cancelled by user.", LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
} else if (userMode === UserMode.ExistingUser) {
|
||||
const method = await this.dialogManager.openWithExplicitCancel(SelectMethodExisting);
|
||||
if (method === "use-setup-uri") {
|
||||
await this.onUseSetupURI(userMode);
|
||||
} else if (method === "configure-manually") {
|
||||
await this.onConfigureManually(originalSetting, userMode);
|
||||
} else if (method === "scan-qr-code") {
|
||||
await this.onPromptQRCodeInstruction();
|
||||
} else if (method === "cancelled") {
|
||||
this._log("Onboarding cancelled by user.", LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async onUseSetupURI(userMode: UserMode, setupURI: string = ""): Promise<boolean> {
|
||||
const newSetting = await this.dialogManager.openWithExplicitCancel(UseSetupURI, setupURI);
|
||||
if (newSetting === "cancelled") {
|
||||
this._log("Setup URI dialog cancelled.", LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
this._log("Setup URI dialog closed.", LOG_LEVEL_VERBOSE);
|
||||
return await this.confirmApplySettingsFromWizard(newSetting, userMode);
|
||||
}
|
||||
async onCouchDBManualSetup(
|
||||
userMode: UserMode,
|
||||
currentSetting: ObsidianLiveSyncSettings,
|
||||
activate = true
|
||||
): Promise<boolean> {
|
||||
const originalSetting = JSON.parse(JSON.stringify(currentSetting)) as ObsidianLiveSyncSettings;
|
||||
const baseSetting = JSON.parse(JSON.stringify(originalSetting)) as ObsidianLiveSyncSettings;
|
||||
const couchConf = await this.dialogManager.openWithExplicitCancel(SetupRemoteCouchDB, originalSetting);
|
||||
if (couchConf === "cancelled") {
|
||||
this._log("Manual configuration cancelled.", LOG_LEVEL_NOTICE);
|
||||
return await this.onBoard(userMode);
|
||||
}
|
||||
const newSetting = { ...baseSetting, ...couchConf } as ObsidianLiveSyncSettings;
|
||||
if (activate) {
|
||||
newSetting.remoteType = REMOTE_COUCHDB;
|
||||
}
|
||||
return await this.confirmApplySettingsFromWizard(newSetting, userMode, activate);
|
||||
}
|
||||
|
||||
async onBucketManualSetup(
|
||||
userMode: UserMode,
|
||||
currentSetting: ObsidianLiveSyncSettings,
|
||||
activate = true
|
||||
): Promise<boolean> {
|
||||
const bucketConf = await this.dialogManager.openWithExplicitCancel(SetupRemoteBucket, currentSetting);
|
||||
if (bucketConf === "cancelled") {
|
||||
this._log("Manual configuration cancelled.", LOG_LEVEL_NOTICE);
|
||||
return await this.onBoard(userMode);
|
||||
}
|
||||
const newSetting = { ...currentSetting, ...bucketConf } as ObsidianLiveSyncSettings;
|
||||
if (activate) {
|
||||
newSetting.remoteType = REMOTE_MINIO;
|
||||
}
|
||||
return await this.confirmApplySettingsFromWizard(newSetting, userMode, activate);
|
||||
}
|
||||
async onP2PManualSetup(
|
||||
userMode: UserMode,
|
||||
currentSetting: ObsidianLiveSyncSettings,
|
||||
activate = true
|
||||
): Promise<boolean> {
|
||||
const p2pConf = await this.dialogManager.openWithExplicitCancel(SetupRemoteP2P, currentSetting);
|
||||
if (p2pConf === "cancelled") {
|
||||
this._log("Manual configuration cancelled.", LOG_LEVEL_NOTICE);
|
||||
return await this.onBoard(userMode);
|
||||
}
|
||||
const newSetting = { ...currentSetting, ...p2pConf.info } as ObsidianLiveSyncSettings;
|
||||
if (activate) {
|
||||
newSetting.remoteType = REMOTE_P2P;
|
||||
}
|
||||
return await this.confirmApplySettingsFromWizard(newSetting, userMode, activate, () => {
|
||||
this.services.config.setSmallConfig(SETTING_KEY_P2P_DEVICE_NAME, p2pConf.devicePeerId);
|
||||
});
|
||||
}
|
||||
async onlyE2EEConfiguration(userMode: UserMode, currentSetting: ObsidianLiveSyncSettings): Promise<boolean> {
|
||||
const e2eeConf = await this.dialogManager.openWithExplicitCancel(SetupRemoteE2EE, currentSetting);
|
||||
if (e2eeConf === "cancelled") {
|
||||
this._log("E2EE configuration cancelled.", LOG_LEVEL_NOTICE);
|
||||
return await false;
|
||||
}
|
||||
const newSetting = {
|
||||
...currentSetting,
|
||||
...e2eeConf,
|
||||
} as ObsidianLiveSyncSettings;
|
||||
return await this.confirmApplySettingsFromWizard(newSetting, userMode);
|
||||
}
|
||||
async onConfigureManually(originalSetting: ObsidianLiveSyncSettings, userMode: UserMode): Promise<boolean> {
|
||||
const e2eeConf = await this.dialogManager.openWithExplicitCancel(SetupRemoteE2EE, originalSetting);
|
||||
if (e2eeConf === "cancelled") {
|
||||
this._log("Manual configuration cancelled.", LOG_LEVEL_NOTICE);
|
||||
return await this.onBoard(userMode);
|
||||
}
|
||||
const currentSetting = {
|
||||
...originalSetting,
|
||||
...e2eeConf,
|
||||
} as ObsidianLiveSyncSettings;
|
||||
return await this.selectServer(currentSetting, userMode);
|
||||
}
|
||||
|
||||
async selectServer(currentSetting: ObsidianLiveSyncSettings, userMode: UserMode): Promise<boolean> {
|
||||
const method = await this.dialogManager.openWithExplicitCancel(SetupRemote);
|
||||
if (method === "couchdb") {
|
||||
return await this.onCouchDBManualSetup(userMode, currentSetting, true);
|
||||
} else if (method === "bucket") {
|
||||
return await this.onBucketManualSetup(userMode, currentSetting, true);
|
||||
} else if (method === "p2p") {
|
||||
return await this.onP2PManualSetup(userMode, currentSetting, true);
|
||||
} else if (method === "cancelled") {
|
||||
this._log("Manual configuration cancelled.", LOG_LEVEL_NOTICE);
|
||||
if (userMode !== UserMode.Unknown) {
|
||||
return await this.onBoard(userMode);
|
||||
}
|
||||
}
|
||||
// Should not reach here.
|
||||
return false;
|
||||
}
|
||||
async confirmApplySettingsFromWizard(
|
||||
newConf: ObsidianLiveSyncSettings,
|
||||
_userMode: UserMode,
|
||||
activate: boolean = true,
|
||||
extra: () => void = () => {}
|
||||
): Promise<boolean> {
|
||||
let userMode = _userMode;
|
||||
// let rebuildRequired = true;
|
||||
if (userMode === UserMode.Unknown) {
|
||||
if (isObjectDifferent(this.settings, newConf, true) === false) {
|
||||
this._log("No changes in settings detected. Skipping applying settings from wizard.", LOG_LEVEL_NOTICE);
|
||||
return true;
|
||||
}
|
||||
const patch = generatePatchObj(this.settings, newConf);
|
||||
console.log(`Changes:`);
|
||||
console.dir(patch);
|
||||
if (!activate) {
|
||||
extra();
|
||||
await this.applySetting(newConf, UserMode.ExistingUser);
|
||||
this._log("Setting Applied", LOG_LEVEL_NOTICE);
|
||||
return true;
|
||||
}
|
||||
const userModeResult = await this.dialogManager.openWithExplicitCancel(OutroAskUserMode);
|
||||
if (userModeResult === "new-user") {
|
||||
userMode = UserMode.NewUser;
|
||||
} else if (userModeResult === "existing-user") {
|
||||
userMode = UserMode.ExistingUser;
|
||||
} else if (userModeResult === "compatible-existing-user") {
|
||||
extra();
|
||||
await this.applySetting(newConf, UserMode.ExistingUser);
|
||||
this._log("Settings from wizard applied.", LOG_LEVEL_NOTICE);
|
||||
return true;
|
||||
} else if (userModeResult === "cancelled") {
|
||||
this._log("User cancelled applying settings from wizard.", LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const component = userMode === UserMode.NewUser ? OutroNewUser : OutroExistingUser;
|
||||
const confirm = await this.dialogManager.openWithExplicitCancel(component);
|
||||
if (confirm === "cancelled") {
|
||||
this._log("User cancelled applying settings from wizard..", LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
if (confirm) {
|
||||
extra();
|
||||
await this.applySetting(newConf, userMode);
|
||||
if (userMode === UserMode.NewUser) {
|
||||
// For new users, schedule a rebuild everything.
|
||||
await this.core.rebuilder.scheduleRebuild();
|
||||
} else {
|
||||
// For existing users, schedule a fetch.
|
||||
await this.core.rebuilder.scheduleFetch();
|
||||
}
|
||||
}
|
||||
// Settings applied, but may require rebuild to take effect.
|
||||
return false;
|
||||
}
|
||||
|
||||
async onPromptQRCodeInstruction(): Promise<boolean> {
|
||||
const qrResult = await this.dialogManager.open(ScanQRCode);
|
||||
this._log("QR Code dialog closed.", LOG_LEVEL_VERBOSE);
|
||||
// Result is not used, but log it for debugging.
|
||||
this._log(`QR Code result: ${qrResult}`, LOG_LEVEL_VERBOSE);
|
||||
// QR Code instruction dialog never yields settings directly.
|
||||
return false;
|
||||
}
|
||||
|
||||
async decodeQR(qr: string) {
|
||||
const newSettings = decodeSettingsFromQRCodeData(qr);
|
||||
return await this.confirmApplySettingsFromWizard(newSettings, UserMode.Unknown);
|
||||
}
|
||||
|
||||
async applySetting(newConf: ObsidianLiveSyncSettings, userMode: UserMode) {
|
||||
const newSetting = {
|
||||
...this.core.settings,
|
||||
...newConf,
|
||||
};
|
||||
this.core.settings = newSetting;
|
||||
this.services.setting.clearUsedPassphrase();
|
||||
await this.services.setting.saveSettingData();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
import { SetupManager, UserMode } from "./SetupManager.ts";
|
||||
|
||||
export class ModuleSetupObsidian extends AbstractObsidianModule {
|
||||
private _setupManager!: SetupManager;
|
||||
@@ -330,6 +67,11 @@ export class ModuleSetupObsidian extends AbstractObsidianModule {
|
||||
eventHub.onEvent(EVENT_REQUEST_OPEN_SETUP_URI, () => fireAndForget(() => this.command_openSetupURI()));
|
||||
eventHub.onEvent(EVENT_REQUEST_COPY_SETUP_URI, () => fireAndForget(() => this.command_copySetupURI()));
|
||||
eventHub.onEvent(EVENT_REQUEST_SHOW_SETUP_QR, () => fireAndForget(() => this.encodeQR()));
|
||||
eventHub.onEvent(EVENT_REQUEST_OPEN_P2P_SETTINGS, () =>
|
||||
fireAndForget(() => {
|
||||
return this._setupManager.onP2PManualSetup(UserMode.Update, this.settings, false);
|
||||
})
|
||||
);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
async encodeQR() {
|
||||
@@ -380,6 +122,8 @@ export class ModuleSetupObsidian extends AbstractObsidianModule {
|
||||
await this._setupManager.onUseSetupURI(UserMode.Unknown);
|
||||
}
|
||||
|
||||
// TODO: Where to implement these?
|
||||
|
||||
// async askSyncWithRemoteConfig(tryingSettings: ObsidianLiveSyncSettings): Promise<ObsidianLiveSyncSettings> {
|
||||
// const buttons = {
|
||||
// fetch: $msg("Setup.FetchRemoteConf.Buttons.Fetch"),
|
||||
@@ -447,125 +191,6 @@ export class ModuleSetupObsidian extends AbstractObsidianModule {
|
||||
// }
|
||||
// }
|
||||
|
||||
// async applySettingWizard(
|
||||
// oldConf: ObsidianLiveSyncSettings,
|
||||
// newConf: ObsidianLiveSyncSettings,
|
||||
// method = "Setup URI"
|
||||
// ) {
|
||||
// const result = await this.core.confirm.askYesNoDialog(
|
||||
// "Importing Configuration from the " + method + ". Are you sure to proceed ? ",
|
||||
// {}
|
||||
// );
|
||||
// if (result == "yes") {
|
||||
// let newSettingW = Object.assign({}, DEFAULT_SETTINGS, newConf) as ObsidianLiveSyncSettings;
|
||||
// this.core.replicator.closeReplication();
|
||||
// this.settings.suspendFileWatching = true;
|
||||
// newSettingW = await this.askSyncWithRemoteConfig(newSettingW);
|
||||
// const { settings, shouldRebuild, isModified } = await this.askPerformDoctor(newSettingW);
|
||||
// if (isModified) {
|
||||
// newSettingW = settings;
|
||||
// }
|
||||
// // Back into the default method once.
|
||||
// newSettingW.configPassphraseStore = "";
|
||||
// newSettingW.encryptedPassphrase = "";
|
||||
// newSettingW.encryptedCouchDBConnection = "";
|
||||
// newSettingW.additionalSuffixOfDatabaseName = `${"appId" in this.app ? this.app.appId : ""} `;
|
||||
// const setupJustImport = $msg("Setup.Apply.Buttons.OnlyApply");
|
||||
// const setupAsNew = $msg("Setup.Apply.Buttons.ApplyAndFetch");
|
||||
// const setupAsMerge = $msg("Setup.Apply.Buttons.ApplyAndMerge");
|
||||
// const setupAgain = $msg("Setup.Apply.Buttons.ApplyAndRebuild");
|
||||
// const setupCancel = $msg("Setup.Apply.Buttons.Cancel");
|
||||
// newSettingW.syncInternalFiles = false;
|
||||
// newSettingW.usePluginSync = false;
|
||||
// newSettingW.isConfigured = true;
|
||||
// // Migrate completely obsoleted configuration.
|
||||
// if (!newSettingW.useIndexedDBAdapter) {
|
||||
// newSettingW.useIndexedDBAdapter = true;
|
||||
// }
|
||||
// const warn = shouldRebuild ? $msg("Setup.Apply.WarningRebuildRecommended") : "";
|
||||
// const message = $msg("Setup.Apply.Message", {
|
||||
// method,
|
||||
// warn,
|
||||
// });
|
||||
|
||||
// const setupType = await this.core.confirm.askSelectStringDialogue(
|
||||
// message,
|
||||
// [setupAsNew, setupAsMerge, setupAgain, setupJustImport, setupCancel],
|
||||
// { defaultAction: setupAsNew, title: $msg("Setup.Apply.Title", { method }), timeout: 0 }
|
||||
// );
|
||||
// if (setupType == setupJustImport) {
|
||||
// this.core.settings = newSettingW;
|
||||
// this.services.setting.clearUsedPassphrase();
|
||||
// await this.core.saveSettings();
|
||||
// } else if (setupType == setupAsNew) {
|
||||
// this.core.settings = newSettingW;
|
||||
// this.services.setting.clearUsedPassphrase();
|
||||
// await this.core.saveSettings();
|
||||
// await this.core.rebuilder.$fetchLocal();
|
||||
// } else if (setupType == setupAsMerge) {
|
||||
// this.core.settings = newSettingW;
|
||||
// this.services.setting.clearUsedPassphrase();
|
||||
// await this.core.saveSettings();
|
||||
// await this.core.rebuilder.$fetchLocal(true);
|
||||
// } else if (setupType == setupAgain) {
|
||||
// const confirm =
|
||||
// "This operation will rebuild all databases with files on this device. Any files on the remote database not synced here will be lost.";
|
||||
// if (
|
||||
// (await this.core.confirm.askSelectStringDialogue(
|
||||
// "Are you sure you want to do this?",
|
||||
// ["Cancel", confirm],
|
||||
// { defaultAction: "Cancel" }
|
||||
// )) != confirm
|
||||
// ) {
|
||||
// return;
|
||||
// }
|
||||
// this.core.settings = newSettingW;
|
||||
// await this.core.saveSettings();
|
||||
// this.services.setting.clearUsedPassphrase();
|
||||
// await this.core.rebuilder.$rebuildEverything();
|
||||
// } else {
|
||||
// // Explicitly cancel the operation or the dialog was closed.
|
||||
// this._log("Cancelled", LOG_LEVEL_NOTICE);
|
||||
// this.core.settings = oldConf;
|
||||
// return;
|
||||
// }
|
||||
// this._log("Configuration loaded.", LOG_LEVEL_NOTICE);
|
||||
// } else {
|
||||
// this._log("Cancelled", LOG_LEVEL_NOTICE);
|
||||
// this.core.settings = oldConf;
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// async setupWizard(confString: string) {
|
||||
// try {
|
||||
// const oldConf = JSON.parse(JSON.stringify(this.settings));
|
||||
// const encryptingPassphrase = await this.core.confirm.askString(
|
||||
// "Passphrase",
|
||||
// "The passphrase to decrypt your setup URI",
|
||||
// "",
|
||||
// true
|
||||
// );
|
||||
// if (encryptingPassphrase === false) return;
|
||||
// const newConf = await JSON.parse(await decryptString(confString, encryptingPassphrase));
|
||||
// if (newConf) {
|
||||
// await this.applySettingWizard(oldConf, newConf);
|
||||
// this._log("Configuration loaded.", LOG_LEVEL_NOTICE);
|
||||
// } else {
|
||||
// this._log("Cancelled.", LOG_LEVEL_NOTICE);
|
||||
// }
|
||||
// } catch (ex) {
|
||||
// this._log("Couldn't parse or decrypt configuration uri.", LOG_LEVEL_NOTICE);
|
||||
// this._log(ex, LOG_LEVEL_VERBOSE);
|
||||
// }
|
||||
// }
|
||||
|
||||
// async askHowToApplySetupURI() {
|
||||
// const method = await this.dialogManager.openWithExplicitCancel(OutroAskUserMode);
|
||||
// if( method === "new-user") {
|
||||
// return UserMode.NewUser;
|
||||
// }
|
||||
// }
|
||||
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
services.appLifecycle.handleOnLoaded(this._everyOnload.bind(this));
|
||||
}
|
||||
|
||||
@@ -4,77 +4,12 @@
|
||||
* Mostly used in the Setting Dialogue
|
||||
*/
|
||||
import { type SveltePanelProps } from "./SveltePanel";
|
||||
import InfoTable from "@lib/ui/components/InfoTable.svelte";
|
||||
type Props = SveltePanelProps<{
|
||||
info: Record<string, any>;
|
||||
}>;
|
||||
const { port }: Props = $props();
|
||||
const info = $derived.by(() => $port?.info ?? {});
|
||||
const infoEntries = $derived(Object.entries(info ?? {}));
|
||||
</script>
|
||||
|
||||
<div class="info-panel">
|
||||
<div class="info-grid" role="list">
|
||||
{#each infoEntries as [key, value]}
|
||||
<div class="info-entry info-key" role="listitem" aria-label={key}>
|
||||
<div class="key">{key}</div>
|
||||
</div>
|
||||
<div class="info-entry info-item" role="listitem" aria-label={key}>
|
||||
<div class="value">{value}</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.info-panel {
|
||||
padding: 0.6rem;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
/* Main Grid (Info Items) 220px to 1fr, repeat */
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
gap: 0.6rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
.info-entry {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
gap: 0.5rem;
|
||||
border-radius: 6px;
|
||||
box-sizing: border-box;
|
||||
min-height: 1.2em;
|
||||
}
|
||||
|
||||
.info-key {
|
||||
font-weight: 600;
|
||||
align-items: center;
|
||||
border-top: 1px solid var(--background-modifier-hover);
|
||||
border-bottom: 1px solid var(--background-modifier-hover);
|
||||
/* color: var(--text-muted, #6b6b6b); */
|
||||
}
|
||||
.info-item {
|
||||
align-items: start;
|
||||
padding: 0.5rem;
|
||||
background: var(--background-modifier-hover, rgba(0, 0, 0, 0.03));
|
||||
}
|
||||
|
||||
.value {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
color: var(--text-normal, #e6e6e6);
|
||||
min-height: 1em;
|
||||
}
|
||||
|
||||
@media (max-width: 420px) {
|
||||
.info-item {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
/* .label {
|
||||
order: -1;
|
||||
white-space: normal;
|
||||
padding-bottom: 0.25rem;
|
||||
} */
|
||||
}
|
||||
</style>
|
||||
<InfoTable {info} />
|
||||
|
||||
@@ -8,7 +8,7 @@ import { $msg } from "../../../lib/src/common/i18n.ts";
|
||||
import { LiveSyncSetting as Setting } from "./LiveSyncSetting.ts";
|
||||
import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts";
|
||||
import type { PageFunctions } from "./SettingPane.ts";
|
||||
import { visibleOnly } from "./SettingPane.ts";
|
||||
// import { visibleOnly } from "./SettingPane.ts";
|
||||
import InfoPanel from "./InfoPanel.svelte";
|
||||
import { writable } from "svelte/store";
|
||||
import { SveltePanel } from "./SveltePanel.ts";
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
getE2EEConfigSummary,
|
||||
} from "./settingUtils.ts";
|
||||
import { SETTING_KEY_P2P_DEVICE_NAME } from "../../../lib/src/common/types.ts";
|
||||
import { SetupManager, UserMode } from "../ModuleSetupObsidian.ts";
|
||||
import { SetupManager, UserMode } from "../SetupManager.ts";
|
||||
import { OnDialogSettingsDefault, type AllSettings } from "./settingConstants.ts";
|
||||
|
||||
function getSettingsFromEditingSettings(editingSettings: AllSettings): ObsidianLiveSyncSettings {
|
||||
@@ -30,6 +30,14 @@ function getSettingsFromEditingSettings(editingSettings: AllSettings): ObsidianL
|
||||
}
|
||||
return workObj;
|
||||
}
|
||||
const toggleActiveSyncClass = (el: HTMLElement, isActive: () => boolean) => {
|
||||
if (isActive()) {
|
||||
el.addClass("active-pane");
|
||||
} else {
|
||||
el.removeClass("active-pane");
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
export function paneRemoteConfig(
|
||||
this: ObsidianLiveSyncSettingTab,
|
||||
@@ -56,39 +64,46 @@ export function paneRemoteConfig(
|
||||
void addPanel(paneEl, "E2EE Configuration", () => {}).then((paneEl) => {
|
||||
new SveltePanel(InfoPanel, paneEl, E2EESummaryWritable);
|
||||
const setupButton = new Setting(paneEl).setName("Configure E2EE");
|
||||
setupButton.addButton((button) =>
|
||||
button
|
||||
.onClick(async () => {
|
||||
const setupManager = this.plugin.getModule(SetupManager);
|
||||
const originalSettings = getSettingsFromEditingSettings(this.editingSettings);
|
||||
await setupManager.onlyE2EEConfiguration(UserMode.Update, originalSettings);
|
||||
updateE2EESummary();
|
||||
})
|
||||
.setButtonText("Configure")
|
||||
.setWarning()
|
||||
);
|
||||
setupButton
|
||||
.addButton((button) =>
|
||||
button
|
||||
.onClick(async () => {
|
||||
const setupManager = this.plugin.getModule(SetupManager);
|
||||
const originalSettings = getSettingsFromEditingSettings(this.editingSettings);
|
||||
await setupManager.onlyE2EEConfiguration(UserMode.Update, originalSettings);
|
||||
updateE2EESummary();
|
||||
})
|
||||
.setButtonText("Configure")
|
||||
.setWarning()
|
||||
)
|
||||
.addButton((button) =>
|
||||
button
|
||||
.onClick(async () => {
|
||||
const setupManager = this.plugin.getModule(SetupManager);
|
||||
const originalSettings = getSettingsFromEditingSettings(this.editingSettings);
|
||||
await setupManager.onConfigureManually(originalSettings, UserMode.Update);
|
||||
updateE2EESummary();
|
||||
})
|
||||
.setButtonText("Configure And Change Remote")
|
||||
.setWarning()
|
||||
);
|
||||
updateE2EESummary();
|
||||
});
|
||||
}
|
||||
{
|
||||
void addPanel(
|
||||
paneEl,
|
||||
$msg("obsidianLiveSyncSettingTab.titleRemoteServer"),
|
||||
() => {},
|
||||
() => ({ classes: this.editingSettings.remoteType === REMOTE_COUCHDB ? ["active-sync"] : [] })
|
||||
).then((paneEl) => {
|
||||
const nSetting = new Setting(paneEl).setName("Active Remote Configuration");
|
||||
void addPanel(paneEl, $msg("obsidianLiveSyncSettingTab.titleRemoteServer"), () => {}).then((paneEl) => {
|
||||
const setting = new Setting(paneEl).setName("Active Remote Configuration");
|
||||
|
||||
const el = nSetting.controlEl.createDiv({});
|
||||
const el = setting.controlEl.createDiv({});
|
||||
el.setText(`${remoteNameMap[this.editingSettings.remoteType] || " - "}`);
|
||||
nSetting.addButton((button) =>
|
||||
setting.addButton((button) =>
|
||||
button
|
||||
.setButtonText("Change Remote and Setup")
|
||||
.setCta()
|
||||
.onClick(async () => {
|
||||
const setupManager = this.plugin.getModule(SetupManager);
|
||||
const originalSettings = getSettingsFromEditingSettings(this.editingSettings);
|
||||
await setupManager.selectServer(originalSettings, UserMode.Update);
|
||||
await setupManager.onSelectServer(originalSettings, UserMode.Update);
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -103,30 +118,29 @@ export function paneRemoteConfig(
|
||||
info: getCouchDBConfigSummary(this.editingSettings),
|
||||
});
|
||||
};
|
||||
void addPanel(
|
||||
paneEl,
|
||||
$msg("obsidianLiveSyncSettingTab.titleCouchDB"),
|
||||
() => {},
|
||||
() => ({ classes: this.editingSettings.remoteType === REMOTE_COUCHDB ? ["active-sync"] : [] })
|
||||
).then((paneEl) => {
|
||||
void addPanel(paneEl, $msg("obsidianLiveSyncSettingTab.titleCouchDB"), () => {}).then((paneEl) => {
|
||||
new SveltePanel(InfoPanel, paneEl, summaryWritable);
|
||||
const setupButton = new Setting(paneEl).setName("Configure Remote");
|
||||
setupButton.addButton((button) =>
|
||||
button
|
||||
.setButtonText("Configure")
|
||||
.setCta()
|
||||
.onClick(async () => {
|
||||
const setupManager = this.plugin.getModule(SetupManager);
|
||||
const originalSettings = getSettingsFromEditingSettings(this.editingSettings);
|
||||
await setupManager.onCouchDBManualSetup(
|
||||
UserMode.Update,
|
||||
originalSettings,
|
||||
this.editingSettings.remoteType === REMOTE_COUCHDB
|
||||
);
|
||||
setupButton
|
||||
.addButton((button) =>
|
||||
button
|
||||
.setButtonText("Configure")
|
||||
.setCta()
|
||||
.onClick(async () => {
|
||||
const setupManager = this.plugin.getModule(SetupManager);
|
||||
const originalSettings = getSettingsFromEditingSettings(this.editingSettings);
|
||||
await setupManager.onCouchDBManualSetup(
|
||||
UserMode.Update,
|
||||
originalSettings,
|
||||
this.editingSettings.remoteType === REMOTE_COUCHDB
|
||||
);
|
||||
|
||||
updateSummary();
|
||||
})
|
||||
);
|
||||
updateSummary();
|
||||
})
|
||||
)
|
||||
.addOnUpdate(() =>
|
||||
toggleActiveSyncClass(paneEl, () => this.editingSettings.remoteType === REMOTE_COUCHDB)
|
||||
);
|
||||
});
|
||||
}
|
||||
{
|
||||
@@ -139,30 +153,29 @@ export function paneRemoteConfig(
|
||||
info: getBucketConfigSummary(this.editingSettings),
|
||||
});
|
||||
};
|
||||
void addPanel(
|
||||
paneEl,
|
||||
$msg("obsidianLiveSyncSettingTab.titleMinioS3R2"),
|
||||
() => {},
|
||||
() => ({ classes: this.editingSettings.remoteType === REMOTE_MINIO ? ["active-sync"] : [] })
|
||||
).then((paneEl) => {
|
||||
void addPanel(paneEl, $msg("obsidianLiveSyncSettingTab.titleMinioS3R2"), () => {}).then((paneEl) => {
|
||||
new SveltePanel(InfoPanel, paneEl, summaryWritable);
|
||||
const setupButton = new Setting(paneEl).setName("Configure Remote");
|
||||
setupButton.addButton((button) =>
|
||||
button
|
||||
.setButtonText("Configure")
|
||||
.setCta()
|
||||
.onClick(async () => {
|
||||
const setupManager = this.plugin.getModule(SetupManager);
|
||||
const originalSettings = getSettingsFromEditingSettings(this.editingSettings);
|
||||
await setupManager.onBucketManualSetup(
|
||||
UserMode.Update,
|
||||
originalSettings,
|
||||
this.editingSettings.remoteType === REMOTE_MINIO
|
||||
);
|
||||
//TODO
|
||||
updateSummary();
|
||||
})
|
||||
);
|
||||
setupButton
|
||||
.addButton((button) =>
|
||||
button
|
||||
.setButtonText("Configure")
|
||||
.setCta()
|
||||
.onClick(async () => {
|
||||
const setupManager = this.plugin.getModule(SetupManager);
|
||||
const originalSettings = getSettingsFromEditingSettings(this.editingSettings);
|
||||
await setupManager.onBucketManualSetup(
|
||||
UserMode.Update,
|
||||
originalSettings,
|
||||
this.editingSettings.remoteType === REMOTE_MINIO
|
||||
);
|
||||
//TODO
|
||||
updateSummary();
|
||||
})
|
||||
)
|
||||
.addOnUpdate(() =>
|
||||
toggleActiveSyncClass(paneEl, () => this.editingSettings.remoteType === REMOTE_MINIO)
|
||||
);
|
||||
});
|
||||
}
|
||||
{
|
||||
@@ -180,54 +193,35 @@ export function paneRemoteConfig(
|
||||
}),
|
||||
});
|
||||
};
|
||||
void addPanel(
|
||||
paneEl,
|
||||
"Peer-to-Peer Synchronisation",
|
||||
() => {},
|
||||
() => ({ classes: this.editingSettings.remoteType === REMOTE_P2P ? ["active-sync"] : [] })
|
||||
).then((paneEl) => {
|
||||
void addPanel(paneEl, "Peer-to-Peer Synchronisation", () => {}).then((paneEl) => {
|
||||
new SveltePanel(InfoPanel, paneEl, summaryWritable);
|
||||
const setupButton = new Setting(paneEl).setName("Configure Remote");
|
||||
setupButton.addButton((button) =>
|
||||
button
|
||||
.setButtonText("Configure")
|
||||
.setCta()
|
||||
.onClick(async () => {
|
||||
const setupManager = this.plugin.getModule(SetupManager);
|
||||
const originalSettings = getSettingsFromEditingSettings(this.editingSettings);
|
||||
await setupManager.onP2PManualSetup(
|
||||
UserMode.Update,
|
||||
originalSettings,
|
||||
this.editingSettings.remoteType === REMOTE_P2P
|
||||
);
|
||||
//TODO
|
||||
updateSummary();
|
||||
})
|
||||
);
|
||||
setupButton
|
||||
.addButton((button) =>
|
||||
button
|
||||
.setButtonText("Configure")
|
||||
.setCta()
|
||||
.onClick(async () => {
|
||||
const setupManager = this.plugin.getModule(SetupManager);
|
||||
const originalSettings = getSettingsFromEditingSettings(this.editingSettings);
|
||||
await setupManager.onP2PManualSetup(
|
||||
UserMode.Update,
|
||||
originalSettings,
|
||||
this.editingSettings.remoteType === REMOTE_P2P
|
||||
);
|
||||
//TODO
|
||||
updateSummary();
|
||||
})
|
||||
)
|
||||
.addOnUpdate(() =>
|
||||
toggleActiveSyncClass(
|
||||
paneEl,
|
||||
() => this.editingSettings.remoteType === REMOTE_P2P || this.editingSettings.P2P_Enabled
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void addPanel(paneEl, $msg("obsidianLiveSyncSettingTab.titleActiveRemoteServer")).then((paneEl) => {
|
||||
// const containerRemoteDatabaseEl = containerEl.createDiv();
|
||||
this.createEl(
|
||||
paneEl,
|
||||
"div",
|
||||
{
|
||||
text: $msg("obsidianLiveSyncSettingTab.msgSettingsUnchangeableDuringSync"),
|
||||
},
|
||||
undefined,
|
||||
visibleOnly(() => this.isAnySyncEnabled())
|
||||
).addClass("op-warn-info");
|
||||
|
||||
new Setting(paneEl)
|
||||
.autoWireDropDown("remoteType", {
|
||||
holdValue: true,
|
||||
options: remoteNameMap,
|
||||
onUpdate: this.enableOnlySyncDisabled,
|
||||
})
|
||||
.addApplyButton(["remoteType"]);
|
||||
});
|
||||
|
||||
// new Setting(paneEl)
|
||||
// .setDesc("Generate ES256 Keypair for testing")
|
||||
// .addButton((button) =>
|
||||
|
||||
@@ -13,7 +13,7 @@ import type { PageFunctions } from "./SettingPane.ts";
|
||||
import { visibleOnly } from "./SettingPane.ts";
|
||||
import { DEFAULT_SETTINGS } from "../../../lib/src/common/types.ts";
|
||||
import { request } from "obsidian";
|
||||
import { SetupManager, UserMode } from "../ModuleSetupObsidian.ts";
|
||||
import { SetupManager, UserMode } from "../SetupManager.ts";
|
||||
export function paneSetup(
|
||||
this: ObsidianLiveSyncSettingTab,
|
||||
paneEl: HTMLElement,
|
||||
@@ -36,7 +36,7 @@ export function paneSetup(
|
||||
.addButton((text) => {
|
||||
text.setButtonText("Rerun Wizard").onClick(async () => {
|
||||
const setupManager = this.plugin.getModule(SetupManager);
|
||||
await setupManager.onBoard(UserMode.ExistingUser);
|
||||
await setupManager.onOnboard(UserMode.ExistingUser);
|
||||
// await this.plugin.moduleSetupObsidian.onBoardingWizard(true);
|
||||
});
|
||||
});
|
||||
|
||||
378
src/modules/features/SetupManager.ts
Normal file
378
src/modules/features/SetupManager.ts
Normal file
@@ -0,0 +1,378 @@
|
||||
import {
|
||||
type ObsidianLiveSyncSettings,
|
||||
DEFAULT_SETTINGS,
|
||||
LOG_LEVEL_NOTICE,
|
||||
LOG_LEVEL_VERBOSE,
|
||||
REMOTE_COUCHDB,
|
||||
REMOTE_MINIO,
|
||||
REMOTE_P2P,
|
||||
} from "../../lib/src/common/types.ts";
|
||||
import { generatePatchObj, isObjectDifferent } from "../../lib/src/common/utils.ts";
|
||||
import { AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { SvelteDialogManager } from "./SetupWizard/ObsidianSvelteDialog.ts";
|
||||
import Intro from "./SetupWizard/dialogs/Intro.svelte";
|
||||
import SelectMethodNewUser from "./SetupWizard/dialogs/SelectMethodNewUser.svelte";
|
||||
import SelectMethodExisting from "./SetupWizard/dialogs/SelectMethodExisting.svelte";
|
||||
import ScanQRCode from "./SetupWizard/dialogs/ScanQRCode.svelte";
|
||||
import UseSetupURI from "./SetupWizard/dialogs/UseSetupURI.svelte";
|
||||
import OutroNewUser from "./SetupWizard/dialogs/OutroNewUser.svelte";
|
||||
import OutroExistingUser from "./SetupWizard/dialogs/OutroExistingUser.svelte";
|
||||
import OutroAskUserMode from "./SetupWizard/dialogs/OutroAskUserMode.svelte";
|
||||
import SetupRemote from "./SetupWizard/dialogs/SetupRemote.svelte";
|
||||
import SetupRemoteCouchDB from "./SetupWizard/dialogs/SetupRemoteCouchDB.svelte";
|
||||
import SetupRemoteBucket from "./SetupWizard/dialogs/SetupRemoteBucket.svelte";
|
||||
import SetupRemoteP2P from "./SetupWizard/dialogs/SetupRemoteP2P.svelte";
|
||||
import SetupRemoteE2EE from "./SetupWizard/dialogs/SetupRemoteE2EE.svelte";
|
||||
import { decodeSettingsFromQRCodeData } from "../../lib/src/API/processSetting.ts";
|
||||
|
||||
/**
|
||||
* User modes for onboarding and setup
|
||||
*/
|
||||
export const enum UserMode {
|
||||
/**
|
||||
* New User Mode - for users who are new to the plugin
|
||||
*/
|
||||
NewUser = "new-user",
|
||||
/**
|
||||
* Existing User Mode - for users who have used the plugin before, or just configuring again
|
||||
*/
|
||||
ExistingUser = "existing-user",
|
||||
/**
|
||||
* Unknown User Mode - for cases where the user mode is not determined
|
||||
*/
|
||||
Unknown = "unknown",
|
||||
/**
|
||||
* Update User Mode - for users who are updating configuration. May be `existing-user` as well, but possibly they want to treat it differently.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values
|
||||
Update = "unknown", // Alias for Unknown for better readability
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Manager to handle onboarding and configuration setup
|
||||
*/
|
||||
export class SetupManager extends AbstractObsidianModule {
|
||||
/**
|
||||
* Dialog manager for handling Svelte dialogs
|
||||
*/
|
||||
private dialogManager: SvelteDialogManager = new SvelteDialogManager(this.plugin);
|
||||
|
||||
/**
|
||||
* Starts the onboarding process
|
||||
* @returns Promise that resolves to true if onboarding completed successfully, false otherwise
|
||||
*/
|
||||
async startOnBoarding(): Promise<boolean> {
|
||||
const isUserNewOrExisting = await this.dialogManager.openWithExplicitCancel(Intro);
|
||||
if (isUserNewOrExisting === "new-user") {
|
||||
await this.onOnboard(UserMode.NewUser);
|
||||
} else if (isUserNewOrExisting === "existing-user") {
|
||||
await this.onOnboard(UserMode.ExistingUser);
|
||||
} else if (isUserNewOrExisting === "cancelled") {
|
||||
this._log("Onboarding cancelled by user.", LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the onboarding process based on user mode
|
||||
* @param userMode
|
||||
* @returns Promise that resolves to true if onboarding completed successfully, false otherwise
|
||||
*/
|
||||
async onOnboard(userMode: UserMode): Promise<boolean> {
|
||||
const originalSetting = userMode === UserMode.NewUser ? DEFAULT_SETTINGS : this.core.settings;
|
||||
if (userMode === UserMode.NewUser) {
|
||||
//Ask how to apply initial setup
|
||||
const method = await this.dialogManager.openWithExplicitCancel(SelectMethodNewUser);
|
||||
if (method === "use-setup-uri") {
|
||||
await this.onUseSetupURI(userMode);
|
||||
} else if (method === "configure-manually") {
|
||||
await this.onConfigureManually(originalSetting, userMode);
|
||||
} else if (method === "cancelled") {
|
||||
this._log("Onboarding cancelled by user.", LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
} else if (userMode === UserMode.ExistingUser) {
|
||||
const method = await this.dialogManager.openWithExplicitCancel(SelectMethodExisting);
|
||||
if (method === "use-setup-uri") {
|
||||
await this.onUseSetupURI(userMode);
|
||||
} else if (method === "configure-manually") {
|
||||
await this.onConfigureManually(originalSetting, userMode);
|
||||
} else if (method === "scan-qr-code") {
|
||||
await this.onPromptQRCodeInstruction();
|
||||
} else if (method === "cancelled") {
|
||||
this._log("Onboarding cancelled by user.", LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles setup using a setup URI
|
||||
* @param userMode
|
||||
* @param setupURI
|
||||
* @returns Promise that resolves to true if onboarding completed successfully, false otherwise
|
||||
*/
|
||||
async onUseSetupURI(userMode: UserMode, setupURI: string = ""): Promise<boolean> {
|
||||
const newSetting = await this.dialogManager.openWithExplicitCancel(UseSetupURI, setupURI);
|
||||
if (newSetting === "cancelled") {
|
||||
this._log("Setup URI dialog cancelled.", LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
this._log("Setup URI dialog closed.", LOG_LEVEL_VERBOSE);
|
||||
return await this.onConfirmApplySettingsFromWizard(newSetting, userMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles manual setup for CouchDB
|
||||
* @param userMode
|
||||
* @param currentSetting
|
||||
* @param activate Whether to activate the CouchDB as remote type
|
||||
* @returns Promise that resolves to true if setup completed successfully, false otherwise
|
||||
*/
|
||||
async onCouchDBManualSetup(
|
||||
userMode: UserMode,
|
||||
currentSetting: ObsidianLiveSyncSettings,
|
||||
activate = true
|
||||
): Promise<boolean> {
|
||||
const originalSetting = JSON.parse(JSON.stringify(currentSetting)) as ObsidianLiveSyncSettings;
|
||||
const baseSetting = JSON.parse(JSON.stringify(originalSetting)) as ObsidianLiveSyncSettings;
|
||||
const couchConf = await this.dialogManager.openWithExplicitCancel(SetupRemoteCouchDB, originalSetting);
|
||||
if (couchConf === "cancelled") {
|
||||
this._log("Manual configuration cancelled.", LOG_LEVEL_NOTICE);
|
||||
return await this.onOnboard(userMode);
|
||||
}
|
||||
const newSetting = { ...baseSetting, ...couchConf } as ObsidianLiveSyncSettings;
|
||||
if (activate) {
|
||||
newSetting.remoteType = REMOTE_COUCHDB;
|
||||
}
|
||||
return await this.onConfirmApplySettingsFromWizard(newSetting, userMode, activate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles manual setup for S3-compatible bucket
|
||||
* @param userMode
|
||||
* @param currentSetting
|
||||
* @param activate Whether to activate the Bucket as remote type
|
||||
* @returns Promise that resolves to true if setup completed successfully, false otherwise
|
||||
*/
|
||||
async onBucketManualSetup(
|
||||
userMode: UserMode,
|
||||
currentSetting: ObsidianLiveSyncSettings,
|
||||
activate = true
|
||||
): Promise<boolean> {
|
||||
const bucketConf = await this.dialogManager.openWithExplicitCancel(SetupRemoteBucket, currentSetting);
|
||||
if (bucketConf === "cancelled") {
|
||||
this._log("Manual configuration cancelled.", LOG_LEVEL_NOTICE);
|
||||
return await this.onOnboard(userMode);
|
||||
}
|
||||
const newSetting = { ...currentSetting, ...bucketConf } as ObsidianLiveSyncSettings;
|
||||
if (activate) {
|
||||
newSetting.remoteType = REMOTE_MINIO;
|
||||
}
|
||||
return await this.onConfirmApplySettingsFromWizard(newSetting, userMode, activate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles manual setup for P2P
|
||||
* @param userMode
|
||||
* @param currentSetting
|
||||
* @param activate Whether to activate the P2P as remote type (as P2P Only setup)
|
||||
* @returns Promise that resolves to true if setup completed successfully, false otherwise
|
||||
*/
|
||||
async onP2PManualSetup(
|
||||
userMode: UserMode,
|
||||
currentSetting: ObsidianLiveSyncSettings,
|
||||
activate = true
|
||||
): Promise<boolean> {
|
||||
const p2pConf = await this.dialogManager.openWithExplicitCancel(SetupRemoteP2P, currentSetting);
|
||||
if (p2pConf === "cancelled") {
|
||||
this._log("Manual configuration cancelled.", LOG_LEVEL_NOTICE);
|
||||
return await this.onOnboard(userMode);
|
||||
}
|
||||
const newSetting = { ...currentSetting, ...p2pConf } as ObsidianLiveSyncSettings;
|
||||
if (activate) {
|
||||
newSetting.remoteType = REMOTE_P2P;
|
||||
}
|
||||
return await this.onConfirmApplySettingsFromWizard(newSetting, userMode, activate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles only E2EE configuration
|
||||
* @param userMode
|
||||
* @param currentSetting
|
||||
* @returns
|
||||
*/
|
||||
async onlyE2EEConfiguration(userMode: UserMode, currentSetting: ObsidianLiveSyncSettings): Promise<boolean> {
|
||||
const e2eeConf = await this.dialogManager.openWithExplicitCancel(SetupRemoteE2EE, currentSetting);
|
||||
if (e2eeConf === "cancelled") {
|
||||
this._log("E2EE configuration cancelled.", LOG_LEVEL_NOTICE);
|
||||
return await false;
|
||||
}
|
||||
const newSetting = {
|
||||
...currentSetting,
|
||||
...e2eeConf,
|
||||
} as ObsidianLiveSyncSettings;
|
||||
return await this.onConfirmApplySettingsFromWizard(newSetting, userMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles manual configuration flow (E2EE + select server)
|
||||
* @param originalSetting
|
||||
* @param userMode
|
||||
* @returns
|
||||
*/
|
||||
async onConfigureManually(originalSetting: ObsidianLiveSyncSettings, userMode: UserMode): Promise<boolean> {
|
||||
const e2eeConf = await this.dialogManager.openWithExplicitCancel(SetupRemoteE2EE, originalSetting);
|
||||
if (e2eeConf === "cancelled") {
|
||||
this._log("Manual configuration cancelled.", LOG_LEVEL_NOTICE);
|
||||
return await this.onOnboard(userMode);
|
||||
}
|
||||
const currentSetting = {
|
||||
...originalSetting,
|
||||
...e2eeConf,
|
||||
} as ObsidianLiveSyncSettings;
|
||||
return await this.onSelectServer(currentSetting, userMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles server selection during manual configuration
|
||||
* @param currentSetting
|
||||
* @param userMode
|
||||
* @returns
|
||||
*/
|
||||
async onSelectServer(currentSetting: ObsidianLiveSyncSettings, userMode: UserMode): Promise<boolean> {
|
||||
const method = await this.dialogManager.openWithExplicitCancel(SetupRemote);
|
||||
if (method === "couchdb") {
|
||||
return await this.onCouchDBManualSetup(userMode, currentSetting, true);
|
||||
} else if (method === "bucket") {
|
||||
return await this.onBucketManualSetup(userMode, currentSetting, true);
|
||||
} else if (method === "p2p") {
|
||||
return await this.onP2PManualSetup(userMode, currentSetting, true);
|
||||
} else if (method === "cancelled") {
|
||||
this._log("Manual configuration cancelled.", LOG_LEVEL_NOTICE);
|
||||
if (userMode !== UserMode.Unknown) {
|
||||
return await this.onOnboard(userMode);
|
||||
}
|
||||
}
|
||||
// Should not reach here.
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Confirms and applies settings obtained from the wizard
|
||||
* @param newConf
|
||||
* @param _userMode
|
||||
* @param activate Whether to activate the remote type in the new settings
|
||||
* @param extra Extra function to run before applying settings
|
||||
* @returns Promise that resolves to true if settings applied successfully, false otherwise
|
||||
*/
|
||||
async onConfirmApplySettingsFromWizard(
|
||||
newConf: ObsidianLiveSyncSettings,
|
||||
_userMode: UserMode,
|
||||
activate: boolean = true,
|
||||
extra: () => void = () => {}
|
||||
): Promise<boolean> {
|
||||
let userMode = _userMode;
|
||||
if (userMode === UserMode.Unknown) {
|
||||
if (isObjectDifferent(this.settings, newConf, true) === false) {
|
||||
this._log("No changes in settings detected. Skipping applying settings from wizard.", LOG_LEVEL_NOTICE);
|
||||
return true;
|
||||
}
|
||||
const patch = generatePatchObj(this.settings, newConf);
|
||||
console.log(`Changes:`);
|
||||
console.dir(patch);
|
||||
if (!activate) {
|
||||
extra();
|
||||
await this.applySetting(newConf, UserMode.ExistingUser);
|
||||
this._log("Setting Applied", LOG_LEVEL_NOTICE);
|
||||
return true;
|
||||
}
|
||||
// Check virtual changes
|
||||
const original = { ...this.settings, P2P_DevicePeerName: "" } as ObsidianLiveSyncSettings;
|
||||
const modified = { ...newConf, P2P_DevicePeerName: "" } as ObsidianLiveSyncSettings;
|
||||
const isOnlyVirtualChange = isObjectDifferent(original, modified, true) === false;
|
||||
if (isOnlyVirtualChange) {
|
||||
extra();
|
||||
await this.applySetting(newConf, UserMode.ExistingUser);
|
||||
this._log("Settings from wizard applied.", LOG_LEVEL_NOTICE);
|
||||
return true;
|
||||
} else {
|
||||
const userModeResult = await this.dialogManager.openWithExplicitCancel(OutroAskUserMode);
|
||||
if (userModeResult === "new-user") {
|
||||
userMode = UserMode.NewUser;
|
||||
} else if (userModeResult === "existing-user") {
|
||||
userMode = UserMode.ExistingUser;
|
||||
} else if (userModeResult === "compatible-existing-user") {
|
||||
extra();
|
||||
await this.applySetting(newConf, UserMode.ExistingUser);
|
||||
this._log("Settings from wizard applied.", LOG_LEVEL_NOTICE);
|
||||
return true;
|
||||
} else if (userModeResult === "cancelled") {
|
||||
this._log("User cancelled applying settings from wizard.", LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
const component = userMode === UserMode.NewUser ? OutroNewUser : OutroExistingUser;
|
||||
const confirm = await this.dialogManager.openWithExplicitCancel(component);
|
||||
if (confirm === "cancelled") {
|
||||
this._log("User cancelled applying settings from wizard..", LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
if (confirm) {
|
||||
extra();
|
||||
await this.applySetting(newConf, userMode);
|
||||
if (userMode === UserMode.NewUser) {
|
||||
// For new users, schedule a rebuild everything.
|
||||
await this.core.rebuilder.scheduleRebuild();
|
||||
} else {
|
||||
// For existing users, schedule a fetch.
|
||||
await this.core.rebuilder.scheduleFetch();
|
||||
}
|
||||
}
|
||||
// Settings applied, but may require rebuild to take effect.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompts the user with QR code scanning instructions
|
||||
* @returns Promise that resolves to false as QR code instruction dialog does not yield settings directly
|
||||
*/
|
||||
|
||||
async onPromptQRCodeInstruction(): Promise<boolean> {
|
||||
const qrResult = await this.dialogManager.open(ScanQRCode);
|
||||
this._log("QR Code dialog closed.", LOG_LEVEL_VERBOSE);
|
||||
// Result is not used, but log it for debugging.
|
||||
this._log(`QR Code result: ${qrResult}`, LOG_LEVEL_VERBOSE);
|
||||
// QR Code instruction dialog never yields settings directly.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes settings from a QR code string and applies them
|
||||
* @param qr QR code string containing encoded settings
|
||||
* @returns Promise that resolves to true if settings applied successfully, false otherwise
|
||||
*/
|
||||
async decodeQR(qr: string) {
|
||||
const newSettings = decodeSettingsFromQRCodeData(qr);
|
||||
return await this.onConfirmApplySettingsFromWizard(newSettings, UserMode.Unknown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the new settings to the core settings and saves them
|
||||
* @param newConf
|
||||
* @param userMode
|
||||
* @returns Promise that resolves to true if settings applied successfully, false otherwise
|
||||
*/
|
||||
async applySetting(newConf: ObsidianLiveSyncSettings, userMode: UserMode) {
|
||||
const newSetting = {
|
||||
...this.core.settings,
|
||||
...newConf,
|
||||
};
|
||||
this.core.settings = newSetting;
|
||||
this.services.setting.clearUsedPassphrase();
|
||||
await this.services.setting.saveSettingData();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -32,12 +32,8 @@
|
||||
|
||||
const context = getObsidianDialogContext();
|
||||
let error = $state("");
|
||||
let devicePeerId = $state("");
|
||||
const TYPE_CANCELLED = "cancelled";
|
||||
type SettingInfo = {
|
||||
info: P2PConnectionInfo;
|
||||
devicePeerId: string;
|
||||
};
|
||||
type SettingInfo = P2PConnectionInfo;
|
||||
type ResultType = typeof TYPE_CANCELLED | SettingInfo;
|
||||
type Props = GuestDialogProps<ResultType, P2PSyncSetting>;
|
||||
|
||||
@@ -49,7 +45,11 @@
|
||||
copyTo(initialData, syncSetting);
|
||||
}
|
||||
if (context.services.config.getSmallConfig(SETTING_KEY_P2P_DEVICE_NAME)) {
|
||||
devicePeerId = context.services.config.getSmallConfig(SETTING_KEY_P2P_DEVICE_NAME) as string;
|
||||
syncSetting.P2P_DevicePeerName = context.services.config.getSmallConfig(
|
||||
SETTING_KEY_P2P_DEVICE_NAME
|
||||
) as string;
|
||||
} else {
|
||||
syncSetting.P2P_DevicePeerName = "";
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -103,7 +103,7 @@
|
||||
confirm: context.plugin.confirm,
|
||||
db: dummyPouch,
|
||||
simpleStore: store,
|
||||
deviceName: devicePeerId || "unnamed-device",
|
||||
deviceName: syncSetting.P2P_DevicePeerName || "unnamed-device",
|
||||
platform: "setup-wizard",
|
||||
};
|
||||
const replicator = new TrysteroReplicator(env);
|
||||
@@ -164,10 +164,7 @@
|
||||
error = (await checkConnection()) || "";
|
||||
if (!error) {
|
||||
const setting = generateSetting();
|
||||
setResult({
|
||||
info: pickP2PSyncSettings(setting),
|
||||
devicePeerId: devicePeerId,
|
||||
});
|
||||
setResult(pickP2PSyncSettings(setting));
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -177,10 +174,7 @@
|
||||
}
|
||||
function commit() {
|
||||
const setting = pickP2PSyncSettings(generateSetting());
|
||||
setResult({
|
||||
info: setting,
|
||||
devicePeerId: devicePeerId,
|
||||
});
|
||||
setResult(setting);
|
||||
}
|
||||
function cancel() {
|
||||
setResult(TYPE_CANCELLED);
|
||||
@@ -190,13 +184,16 @@
|
||||
syncSetting.P2P_relays.trim() !== "" &&
|
||||
syncSetting.P2P_roomID.trim() !== "" &&
|
||||
syncSetting.P2P_passphrase.trim() !== "" &&
|
||||
devicePeerId.trim() !== ""
|
||||
(syncSetting.P2P_DevicePeerName ?? "").trim() !== ""
|
||||
);
|
||||
});
|
||||
</script>
|
||||
|
||||
<DialogHeader title="P2P Configuration" />
|
||||
<Guidance>Please enter the Peer-to-Peer Synchronisation information below.</Guidance>
|
||||
<InputRow label="Enabled">
|
||||
<input type="checkbox" name="p2p-enabled" bind:checked={syncSetting.P2P_Enabled} />
|
||||
</InputRow>
|
||||
<InputRow label="Relay URL">
|
||||
<input
|
||||
type="text"
|
||||
@@ -237,10 +234,23 @@
|
||||
autocorrect="off"
|
||||
autocapitalize="off"
|
||||
spellcheck="false"
|
||||
bind:value={devicePeerId}
|
||||
bind:value={syncSetting.P2P_DevicePeerName}
|
||||
/>
|
||||
</InputRow>
|
||||
|
||||
<InputRow label="Auto Start P2P Connection">
|
||||
<input type="checkbox" name="p2p-auto-start" bind:checked={syncSetting.P2P_AutoStart} />
|
||||
</InputRow>
|
||||
<InfoNote>
|
||||
If "Auto Start P2P Connection" is enabled, the P2P connection will be started automatically when the plug-in
|
||||
launches.
|
||||
</InfoNote>
|
||||
<InputRow label="Auto Broadcast Changes">
|
||||
<input type="checkbox" name="p2p-auto-broadcast" bind:checked={syncSetting.P2P_AutoBroadcast} />
|
||||
</InputRow>
|
||||
<InfoNote>
|
||||
If "Auto Broadcast Changes" is enabled, changes will be automatically broadcasted to connected peers without
|
||||
requiring manual intervention. This requests peers to fetch this device's changes.
|
||||
</InfoNote>
|
||||
<InfoNote error visible={error !== ""}>
|
||||
{error}
|
||||
</InfoNote>
|
||||
|
||||
@@ -20,7 +20,11 @@ import { ObsidianUIService } from "./ObsidianUIService.ts";
|
||||
// All Services will be migrated to be based on Plain Services, not Injectable Services.
|
||||
// This is a migration step.
|
||||
|
||||
export class ObsidianAPIService extends InjectableAPIService {}
|
||||
export class ObsidianAPIService extends InjectableAPIService {
|
||||
getPlatform(): string {
|
||||
return "obsidian";
|
||||
}
|
||||
}
|
||||
export class ObsidianPathService extends InjectablePathService {}
|
||||
export class ObsidianDatabaseService extends InjectableDatabaseService {}
|
||||
|
||||
|
||||
@@ -393,13 +393,13 @@ span.ls-mark-cr::after {
|
||||
div.workspace-leaf-content[data-type=bases] .livesync-status {
|
||||
top: calc(var(--bases-header-height) + var(--header-height));
|
||||
padding: 5px;
|
||||
padding-right:18px;
|
||||
padding-right: 18px;
|
||||
}
|
||||
|
||||
.is-mobile div.workspace-leaf-content[data-type=bases] .livesync-status {
|
||||
top: calc(var(--bases-header-height) + var(--view-header-height));
|
||||
padding: 6px;
|
||||
padding-right:18px;
|
||||
padding-right: 18px;
|
||||
}
|
||||
|
||||
.livesync-status div {
|
||||
@@ -444,6 +444,10 @@ div.workspace-leaf-content[data-type=bases] .livesync-status {
|
||||
padding: 0.5em 1.0em;
|
||||
}
|
||||
|
||||
.active-pane .sls-setting-panel-title {
|
||||
border: 1px solid var(--interactive-accent);
|
||||
}
|
||||
|
||||
.sls-dialogue-note-wrapper {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"importHelpers": false,
|
||||
"alwaysStrict": true,
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
"lib": ["es2018", "DOM", "ES5", "ES6", "ES7", "es2019.array", "ES2021.WeakRef", "ES2020.BigInt", "ESNext.Intl"],
|
||||
"strictBindCallApply": true,
|
||||
|
||||
Reference in New Issue
Block a user