Improved

- New Translation: `es` (Spanish) by @zeedif (Thank you so much)!
- Now all of messages can be selectable and copyable, also on the iPhone, iPad, and Android devices. Now we can copy or share the messages easily.

New Feature

- Peer-to-Peer Synchronisation has been implemented!

Fixed

- No longer memory or resource leaks when the plug-in is disabled.
- Now deleted chunks are correctly detected on conflict resolution, and we are guided to resurrect them.
- Hanging issue during the initial synchronisation has been fixed.
- Some unnecessary logs have been removed.
- Now all modal dialogues are correctly closed when the plug-in is disabled.

Refactor

- Several interfaces have been moved to the separated library.
- Translations have been moved to each language file, and during the build, they are merged into one file.
- Non-mobile friendly code has been removed and replaced with the safer code.
- Started writing Platform impedance-matching-layer.
- Svelte has been updated to v5.
- Some function have got more robust type definitions.
- Terser optimisation has slightly improved.
- During the build, analysis meta-file of the bundled codes will be generated.
This commit is contained in:
vorotamoroz
2025-02-13 12:48:00 +00:00
parent 45ceca8bb6
commit 1cd1465f2c
39 changed files with 9209 additions and 632 deletions

View File

@@ -66,7 +66,10 @@
for (const revInfo of reversedRevs) {
if (revInfo.status == "available") {
const doc = (!isPlain && showDiffInfo) || (checkStorageDiff && revInfo.rev == docA._rev) ? await db.getDBEntry(path, { rev: revInfo.rev }, false, false, true) : await db.getDBEntryMeta(path, { rev: revInfo.rev }, true);
const doc =
(!isPlain && showDiffInfo) || (checkStorageDiff && revInfo.rev == docA._rev)
? await db.getDBEntry(path, { rev: revInfo.rev }, false, false, true)
: await db.getDBEntryMeta(path, { rev: revInfo.rev }, true);
if (doc === false) continue;
const rev = revInfo.rev;
@@ -94,7 +97,10 @@
[DIFF_EQUAL]: 0,
[DIFF_INSERT]: 0,
} as { [key: number]: number };
const px = diff.reduce((p, c) => ({ ...p, [c[0]]: (p[c[0]] ?? 0) + c[1].length }), pxInit);
const px = diff.reduce(
(p, c) => ({ ...p, [c[0]]: (p[c[0]] ?? 0) + c[1].length }),
pxInit
);
diffDetail = `-${px[DIFF_DELETE]}, +${px[DIFF_INSERT]}`;
}
}
@@ -104,9 +110,13 @@
}
if (rev == docA._rev) {
if (checkStorageDiff) {
const isExist = await plugin.storageAccess.isExistsIncludeHidden(stripAllPrefixes(getPath(docA)));
const isExist = await plugin.storageAccess.isExistsIncludeHidden(
stripAllPrefixes(getPath(docA))
);
if (isExist) {
const data = await plugin.storageAccess.readHiddenFileBinary(stripAllPrefixes(getPath(docA)));
const data = await plugin.storageAccess.readHiddenFileBinary(
stripAllPrefixes(getPath(docA))
);
const d = readAsBlob(doc);
const result = await isDocContentSame(data, d);
if (result) {
@@ -187,19 +197,28 @@
<div class="globalhistory">
<h1>Vault history</h1>
<div class="control">
<div class="row"><label for="">From:</label><input type="date" bind:value={dispDateFrom} disabled={loading} /></div>
<div class="row">
<label for="">From:</label><input type="date" bind:value={dispDateFrom} disabled={loading} />
</div>
<div class="row"><label for="">To:</label><input type="date" bind:value={dispDateTo} disabled={loading} /></div>
<div class="row">
<label for="">Info:</label>
<label><input type="checkbox" bind:checked={showDiffInfo} disabled={loading} /><span>Diff</span></label>
<label><input type="checkbox" bind:checked={showChunkCorrected} disabled={loading} /><span>Chunks</span></label>
<label><input type="checkbox" bind:checked={checkStorageDiff} disabled={loading} /><span>File integrity</span></label>
<label
><input type="checkbox" bind:checked={showChunkCorrected} disabled={loading} /><span>Chunks</span
></label
>
<label
><input type="checkbox" bind:checked={checkStorageDiff} disabled={loading} /><span>File integrity</span
></label
>
</div>
</div>
{#if loading}
<div class="">Gathering information...</div>
{/if}
<table>
<tbody>
<tr>
<th> Date </th>
<th> Path </th>
@@ -212,7 +231,7 @@
<tr>
<td colspan="5" class="more">
{#if loading}
<div class="" />
<div class=""></div>
{:else}
<div><button on:click={() => nextWeek()}>+1 week</button></div>
{/if}
@@ -257,12 +276,13 @@
<tr>
<td colspan="5" class="more">
{#if loading}
<div class="" />
<div class=""></div>
{:else}
<div><button on:click={() => prevWeek()}>+1 week</button></div>
{/if}
</td>
</tr>
</tbody>
</table>
</div>

View File

@@ -1,10 +1,20 @@
import { ItemView, WorkspaceLeaf } from "../../../deps.ts";
import { WorkspaceLeaf } from "../../../deps.ts";
import GlobalHistoryComponent from "./GlobalHistory.svelte";
import type ObsidianLiveSyncPlugin from "../../../main.ts";
import { SvelteItemView } from "../../../common/SvelteItemView.ts";
import { mount } from "svelte";
export const VIEW_TYPE_GLOBAL_HISTORY = "global-history";
export class GlobalHistoryView extends ItemView {
component?: GlobalHistoryComponent;
export class GlobalHistoryView extends SvelteItemView {
instantiateComponent(target: HTMLElement) {
return mount(GlobalHistoryComponent, {
target: target,
props: {
plugin: this.plugin,
},
});
}
plugin: ObsidianLiveSyncPlugin;
icon = "clock";
title: string = "";
@@ -26,19 +36,4 @@ export class GlobalHistoryView extends ItemView {
getDisplayText() {
return "Vault history";
}
async onOpen() {
this.component = new GlobalHistoryComponent({
target: this.contentEl,
props: {
plugin: this.plugin,
},
});
await Promise.resolve();
}
async onClose() {
this.component?.$destroy();
await Promise.resolve();
}
}

View File

@@ -6,10 +6,16 @@
import { $msg as msg, currentLang as lang } from "../../../lib/src/common/i18n.ts";
let unsubscribe: () => void;
let messages = [] as string[];
let wrapRight = false;
let autoScroll = true;
let suspended = false;
let messages = $state([] as string[]);
let wrapRight = $state(false);
let autoScroll = $state(true);
let suspended = $state(false);
type Props = {
close: () => void;
};
let { close }: Props = $props();
// export let close: () => void;
function updateLog(logs: ReactiveInstance<string[]>) {
const e = logs.value;
if (!suspended) {
@@ -29,6 +35,9 @@
if (unsubscribe) unsubscribe();
});
let scroll: HTMLDivElement;
function closeDialogue() {
close();
}
</script>
<div class="logpane">
@@ -47,6 +56,8 @@
<input type="checkbox" bind:checked={suspended} />
<span>{msg("logPane.pause", {}, lang)}</span>
</label>
<span class="spacer"></span>
<button onclick={() => closeDialogue()}>Close</button>
</div>
</div>
<div class="log" bind:this={scroll}>
@@ -68,6 +79,7 @@
.log {
overflow-y: scroll;
user-select: text;
-webkit-user-select: text;
padding-bottom: 2em;
}
.log > pre {

View File

@@ -1,15 +1,27 @@
import { ItemView, WorkspaceLeaf } from "obsidian";
import { WorkspaceLeaf } from "obsidian";
import LogPaneComponent from "./LogPane.svelte";
import type ObsidianLiveSyncPlugin from "../../../main.ts";
import { SvelteItemView } from "../../../common/SvelteItemView.ts";
import { $msg } from "src/lib/src/common/i18n.ts";
import { mount } from "svelte";
export const VIEW_TYPE_LOG = "log-log";
//Log view
export class LogPaneView extends ItemView {
component?: LogPaneComponent;
export class LogPaneView extends SvelteItemView {
instantiateComponent(target: HTMLElement) {
return mount(LogPaneComponent, {
target: target,
props: {
close: () => {
this.leaf.detach();
},
},
});
}
plugin: ObsidianLiveSyncPlugin;
icon = "view-log";
title: string = "";
navigation = true;
navigation = false;
getIcon(): string {
return "view-log";
@@ -28,17 +40,4 @@ export class LogPaneView extends ItemView {
// TODO: This function is not reactive and does not update the title based on the current language
return $msg("logPane.title");
}
async onOpen() {
this.component = new LogPaneComponent({
target: this.contentEl,
props: {},
});
await Promise.resolve();
}
async onClose() {
this.component?.$destroy();
await Promise.resolve();
}
}

View File

@@ -27,6 +27,14 @@ import { QueueProcessor } from "octagonal-wheels/concurrency/processor";
import { LogPaneView, VIEW_TYPE_LOG } from "./Log/LogPaneView.ts";
import { serialized } from "octagonal-wheels/concurrency/lock";
import { $msg } from "src/lib/src/common/i18n.ts";
import type { P2PReplicationProgress } from "../../lib/src/replication/trystero/TrysteroReplicator.ts";
import {
EVENT_ADVERTISEMENT_RECEIVED,
EVENT_DEVICE_LEAVED,
EVENT_P2P_CONNECTED,
EVENT_P2P_DISCONNECTED,
EVENT_P2P_REPLICATOR_PROGRESS,
} from "src/lib/src/replication/trystero/TrysteroReplicatorP2PServer.ts";
// This module cannot be a core module because it depends on the Obsidian UI.
@@ -168,10 +176,12 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
const queued = queueCountLabel();
const waiting = waitingLabel();
const networkActivity = requestingStatLabel();
const p2p = this.p2pReplicationLine.value;
return {
message: `${networkActivity}Sync: ${w}${sent}${pushLast}${arrived}${pullLast}${waiting}${queued}`,
message: `${networkActivity}Sync: ${w}${sent}${pushLast}${arrived}${pullLast}${waiting}${queued}${p2p == "" ? "" : "\n" + p2p}`,
};
});
const statusBarLabels = reactive(() => {
const scheduleMessage = this.core.$$isReloadingScheduled()
? `WARNING! RESTARTING OBSIDIAN IS SCHEDULED!\n`
@@ -193,9 +203,90 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
statusBarLabels.onChanged((label) => applyToDisplay(label.value));
}
p2pReplicationResult = new Map<string, P2PReplicationProgress>();
updateP2PReplicationLine() {
const p2pReplicationResultX = [...this.p2pReplicationResult.values()].sort((a, b) =>
a.peerId.localeCompare(b.peerId)
);
const renderProgress = (current: number, max: number) => {
if (current == max) return `${current}`;
return `${current} (${max})`;
};
const line = p2pReplicationResultX
.map(
(e) =>
`${e.fetching.isActive || e.sending.isActive ? "⚡" : "💤"} ${e.peerName}${renderProgress(e.sending.current, e.sending.max)}${renderProgress(e.fetching.current, e.fetching.max)} `
)
.join("\n");
this.p2pReplicationLine.value = line;
}
// p2pReplicationResultX = reactiveSource([] as P2PReplicationProgress[]);
p2pReplicationLine = reactiveSource("");
$everyOnload(): Promise<boolean> {
eventHub.onEvent(EVENT_LEAF_ACTIVE_CHANGED, () => this.onActiveLeafChange());
eventHub.onceEvent(EVENT_LAYOUT_READY, () => this.onActiveLeafChange());
eventHub.onEvent(EVENT_ADVERTISEMENT_RECEIVED, (data) => {
this.p2pReplicationResult.set(data.peerId, {
peerId: data.peerId,
peerName: data.name,
fetching: {
current: 0,
max: 0,
isActive: false,
},
sending: {
current: 0,
max: 0,
isActive: false,
},
});
this.updateP2PReplicationLine();
});
eventHub.onEvent(EVENT_P2P_CONNECTED, () => {
this.p2pReplicationResult.clear();
this.updateP2PReplicationLine();
});
eventHub.onEvent(EVENT_P2P_DISCONNECTED, () => {
this.p2pReplicationResult.clear();
this.updateP2PReplicationLine();
});
eventHub.onEvent(EVENT_DEVICE_LEAVED, (peerId) => {
this.p2pReplicationResult.delete(peerId);
this.updateP2PReplicationLine();
});
eventHub.onEvent(EVENT_P2P_REPLICATOR_PROGRESS, (data) => {
const prev = this.p2pReplicationResult.get(data.peerId) || {
peerId: data.peerId,
peerName: data.peerName,
fetching: {
current: 0,
max: 0,
isActive: false,
},
sending: {
current: 0,
max: 0,
isActive: false,
},
};
if ("fetching" in data) {
if (data.fetching.isActive) {
prev.fetching = data.fetching;
} else {
prev.fetching.isActive = false;
}
}
if ("sending" in data) {
if (data.sending.isActive) {
prev.sending = data.sending;
} else {
prev.sending.isActive = false;
}
}
this.p2pReplicationResult.set(data.peerId, prev);
this.updateP2PReplicationLine();
});
return Promise.resolve(true);
}
adjustStatusDivPosition() {

View File

@@ -25,6 +25,7 @@ import {
LEVEL_EDGE_CASE,
type MetaEntry,
type FilePath,
REMOTE_P2P,
} from "../../../lib/src/common/types.ts";
import {
createBlob,
@@ -78,6 +79,7 @@ import { ICHeader, ICXHeader, PSCHeader } from "../../../common/types.ts";
import { HiddenFileSync } from "../../../features/HiddenFileSync/CmdHiddenFileSync.ts";
import { EVENT_REQUEST_SHOW_HISTORY } from "../../../common/obsidianEvents.ts";
import { LocalDatabaseMaintenance } from "../../../features/LocalDatabaseMainte/CmdLocalDatabaseMainte.ts";
import { mount } from "svelte";
export type OnUpdateResult = {
visibility?: boolean;
@@ -811,6 +813,12 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
return false;
};
const enableOnlySyncDisabled = enableOnly(() => !isAnySyncEnabled());
const onlyOnP2POrCouchDB = () =>
({
visibility:
this.isConfiguredAs("remoteType", REMOTE_P2P) || this.isConfiguredAs("remoteType", REMOTE_COUCHDB),
}) as OnUpdateResult;
const onlyOnCouchDB = () =>
({
visibility: this.isConfiguredAs("remoteType", REMOTE_COUCHDB),
@@ -819,7 +827,16 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
({
visibility: this.isConfiguredAs("remoteType", REMOTE_MINIO),
}) as OnUpdateResult;
const onlyOnOnlyP2P = () =>
({
visibility: this.isConfiguredAs("remoteType", REMOTE_P2P),
}) as OnUpdateResult;
const onlyOnCouchDBOrMinIO = () =>
({
visibility:
this.isConfiguredAs("remoteType", REMOTE_COUCHDB) ||
this.isConfiguredAs("remoteType", REMOTE_MINIO),
}) as OnUpdateResult;
// E2EE Function
const checkWorkingPassphrase = async (): Promise<boolean> => {
if (this.editingSettings.remoteType == REMOTE_MINIO) return true;
@@ -1364,10 +1381,35 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
options: {
[REMOTE_COUCHDB]: $msg("obsidianLiveSyncSettingTab.optionCouchDB"),
[REMOTE_MINIO]: $msg("obsidianLiveSyncSettingTab.optionMinioS3R2"),
[REMOTE_P2P]: "Only Peer-to-Peer",
},
onUpdate: enableOnlySyncDisabled,
});
void addPanel(paneEl, "Peer-to-Peer", undefined, onlyOnOnlyP2P).then((paneEl) => {
const syncWarnP2P = this.createEl(paneEl, "div", {
text: "",
});
const p2pMessage = `This feature is a Work In Progress, and configurable on \`P2P Replicator\` Pane.
The pane also can be launched by \`P2P Replicator\` command from the Command Palette.
`;
void MarkdownRenderer.render(this.plugin.app, p2pMessage, syncWarnP2P, "/", this.plugin);
syncWarnP2P.addClass("op-warn-info");
new Setting(paneEl)
.setName("Apply Settings")
.setClass("wizardHidden")
.addApplyButton(["remoteType"]);
// .addOnUpdate(onlyOnMinIO);
// new Setting(paneEl).addButton((button) =>
// button
// .setButtonText("Open P2P Replicator")
// .onClick(() => {
// const addOn = this.plugin.getAddOn<P2PReplicator>(P2PReplicator.name);
// void addOn?.openPane();
// this.closeSetting();
// })
// );
});
void addPanel(
paneEl,
$msg("obsidianLiveSyncSettingTab.titleMinioS3R2"),
@@ -1523,7 +1565,12 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
.addOnUpdate(onlyOnCouchDB);
});
});
void addPanel(paneEl, $msg("obsidianLiveSyncSettingTab.titleNotification")).then((paneEl) => {
void addPanel(
paneEl,
$msg("obsidianLiveSyncSettingTab.titleNotification"),
() => {},
onlyOnCouchDB
).then((paneEl) => {
paneEl.addClass("wizardHidden");
new Setting(paneEl)
.autoWireNumeric("notifyThresholdOfRemoteStorageSize", {})
@@ -2001,7 +2048,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
"(RegExp) Empty to sync all files. Set filter as a regular expression to limit synchronising files."
)
.setClass("wizardHidden");
new MultipleRegExpControl({
mount(MultipleRegExpControl, {
target: syncFilesSetting.controlEl,
props: {
patterns: this.editingSettings.syncOnlyRegEx.split("|[]|"),
@@ -2024,7 +2071,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
)
.setClass("wizardHidden");
new MultipleRegExpControl({
mount(MultipleRegExpControl, {
target: nonSyncFilesSetting.controlEl,
props: {
patterns: this.editingSettings.syncIgnoreRegEx.split("|[]|"),
@@ -2057,7 +2104,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
.filter((x) => x != "");
const patSetting = new Setting(paneEl).setName("Ignore patterns").setClass("wizardHidden").setDesc("");
new MultipleRegExpControl({
mount(MultipleRegExpControl, {
target: patSetting.controlEl,
props: {
patterns: pat,
@@ -2233,9 +2280,14 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
pluginConfig.encryptedCouchDBConnection = REDACTED;
pluginConfig.accessKey = REDACTED;
pluginConfig.secretKey = REDACTED;
pluginConfig.region = `${REDACTED}(${pluginConfig.region.length} letters)`;
pluginConfig.bucket = `${REDACTED}(${pluginConfig.bucket.length} letters)`;
const redact = (source: string) => `${REDACTED}(${source.length} letters)`;
pluginConfig.region = redact(pluginConfig.region);
pluginConfig.bucket = redact(pluginConfig.bucket);
pluginConfig.pluginSyncExtendedSetting = {};
pluginConfig.P2P_AppID = redact(pluginConfig.P2P_AppID);
pluginConfig.P2P_passphrase = redact(pluginConfig.P2P_passphrase);
pluginConfig.P2P_roomID = redact(pluginConfig.P2P_roomID);
pluginConfig.P2P_relays = redact(pluginConfig.P2P_relays);
const endpoint = pluginConfig.endpoint;
if (endpoint == "") {
pluginConfig.endpoint = "Not configured or AWS";
@@ -2918,7 +2970,8 @@ ${stringifyYaml(pluginConfig)}`;
.onClick(async () => {
await this.plugin.$$markRemoteLocked();
})
);
)
.addOnUpdate(onlyOnCouchDBOrMinIO);
new Setting(paneEl)
.setName("Emergency restart")
@@ -2935,7 +2988,7 @@ ${stringifyYaml(pluginConfig)}`;
);
});
void addPanel(paneEl, "Syncing").then((paneEl) => {
void addPanel(paneEl, "Syncing", () => {}, onlyOnCouchDBOrMinIO).then((paneEl) => {
new Setting(paneEl)
.setName("Resend")
.setDesc("Resend all chunks to the remote.")
@@ -2951,6 +3004,7 @@ ${stringifyYaml(pluginConfig)}`;
})
)
.addOnUpdate(onlyOnCouchDB);
new Setting(paneEl)
.setName("Reset journal received history")
.setDesc(
@@ -2994,7 +3048,7 @@ ${stringifyYaml(pluginConfig)}`;
)
.addOnUpdate(onlyOnMinIO);
});
void addPanel(paneEl, "Garbage Collection (Beta)", (e) => e, onlyOnCouchDB).then((paneEl) => {
void addPanel(paneEl, "Garbage Collection (Beta)", (e) => e, onlyOnP2POrCouchDB).then((paneEl) => {
new Setting(paneEl)
.setName("Remove all orphaned chunks")
.setDesc("Remove all orphaned chunks from the local database.")
@@ -3080,7 +3134,7 @@ ${stringifyYaml(pluginConfig)}`;
.addOnUpdate(onlyOnCouchDB);
});
void addPanel(paneEl, "Total Overhaul").then((paneEl) => {
void addPanel(paneEl, "Total Overhaul", () => {}, onlyOnCouchDBOrMinIO).then((paneEl) => {
new Setting(paneEl)
.setName("Rebuild everything")
.setDesc("Rebuild local and remote database with local files.")
@@ -3104,94 +3158,98 @@ ${stringifyYaml(pluginConfig)}`;
})
);
});
void addPanel(paneEl, "Rebuilding Operations (Remote Only)").then((paneEl) => {
new Setting(paneEl)
.setName("Perform cleanup")
.setDesc(
"Reduces storage space by discarding all non-latest revisions. This requires the same amount of free space on the remote server and the local client."
)
.addButton((button) =>
button
.setButtonText("Perform")
.setDisabled(false)
.onClick(async () => {
const replicator = this.plugin.replicator as LiveSyncCouchDBReplicator;
Logger(`Cleanup has been began`, LOG_LEVEL_NOTICE, "compaction");
if (await replicator.compactRemote(this.editingSettings)) {
Logger(`Cleanup has been completed!`, LOG_LEVEL_NOTICE, "compaction");
} else {
Logger(`Cleanup has been failed!`, LOG_LEVEL_NOTICE, "compaction");
}
})
)
.addOnUpdate(onlyOnCouchDB);
void addPanel(paneEl, "Rebuilding Operations (Remote Only)", () => {}, onlyOnCouchDBOrMinIO).then(
(paneEl) => {
new Setting(paneEl)
.setName("Perform cleanup")
.setDesc(
"Reduces storage space by discarding all non-latest revisions. This requires the same amount of free space on the remote server and the local client."
)
.addButton((button) =>
button
.setButtonText("Perform")
.setDisabled(false)
.onClick(async () => {
const replicator = this.plugin.replicator as LiveSyncCouchDBReplicator;
Logger(`Cleanup has been began`, LOG_LEVEL_NOTICE, "compaction");
if (await replicator.compactRemote(this.editingSettings)) {
Logger(`Cleanup has been completed!`, LOG_LEVEL_NOTICE, "compaction");
} else {
Logger(`Cleanup has been failed!`, LOG_LEVEL_NOTICE, "compaction");
}
})
)
.addOnUpdate(onlyOnCouchDB);
new Setting(paneEl)
.setName("Overwrite remote")
.setDesc("Overwrite remote with local DB and passphrase.")
.addButton((button) =>
button
.setButtonText("Send")
.setWarning()
.setDisabled(false)
.onClick(async () => {
await rebuildDB("remoteOnly");
})
);
new Setting(paneEl)
.setName("Overwrite remote")
.setDesc("Overwrite remote with local DB and passphrase.")
.addButton((button) =>
button
.setButtonText("Send")
.setWarning()
.setDisabled(false)
.onClick(async () => {
await rebuildDB("remoteOnly");
})
);
new Setting(paneEl)
.setName("Reset all journal counter")
.setDesc("Initialise all journal history, On the next sync, every item will be received and sent.")
.addButton((button) =>
button
.setButtonText("Reset all")
.setWarning()
.setDisabled(false)
.onClick(async () => {
await this.getMinioJournalSyncClient().resetCheckpointInfo();
Logger(`Journal exchange history has been cleared.`, LOG_LEVEL_NOTICE);
})
)
.addOnUpdate(onlyOnMinIO);
new Setting(paneEl)
.setName("Reset all journal counter")
.setDesc(
"Initialise all journal history, On the next sync, every item will be received and sent."
)
.addButton((button) =>
button
.setButtonText("Reset all")
.setWarning()
.setDisabled(false)
.onClick(async () => {
await this.getMinioJournalSyncClient().resetCheckpointInfo();
Logger(`Journal exchange history has been cleared.`, LOG_LEVEL_NOTICE);
})
)
.addOnUpdate(onlyOnMinIO);
new Setting(paneEl)
.setName("Purge all journal counter")
.setDesc("Purge all download/upload cache.")
.addButton((button) =>
button
.setButtonText("Reset all")
.setWarning()
.setDisabled(false)
.onClick(async () => {
await this.getMinioJournalSyncClient().resetAllCaches();
Logger(`Journal download/upload cache has been cleared.`, LOG_LEVEL_NOTICE);
})
)
.addOnUpdate(onlyOnMinIO);
new Setting(paneEl)
.setName("Purge all journal counter")
.setDesc("Purge all download/upload cache.")
.addButton((button) =>
button
.setButtonText("Reset all")
.setWarning()
.setDisabled(false)
.onClick(async () => {
await this.getMinioJournalSyncClient().resetAllCaches();
Logger(`Journal download/upload cache has been cleared.`, LOG_LEVEL_NOTICE);
})
)
.addOnUpdate(onlyOnMinIO);
new Setting(paneEl)
.setName("Fresh Start Wipe")
.setDesc("Delete all data on the remote server.")
.addButton((button) =>
button
.setButtonText("Delete")
.setWarning()
.setDisabled(false)
.onClick(async () => {
await this.getMinioJournalSyncClient().updateCheckPointInfo((info) => ({
...info,
receivedFiles: new Set(),
knownIDs: new Set(),
lastLocalSeq: 0,
sentIDs: new Set(),
sentFiles: new Set(),
}));
await this.resetRemoteBucket();
Logger(`Deleted all data on remote server`, LOG_LEVEL_NOTICE);
})
)
.addOnUpdate(onlyOnMinIO);
});
new Setting(paneEl)
.setName("Fresh Start Wipe")
.setDesc("Delete all data on the remote server.")
.addButton((button) =>
button
.setButtonText("Delete")
.setWarning()
.setDisabled(false)
.onClick(async () => {
await this.getMinioJournalSyncClient().updateCheckPointInfo((info) => ({
...info,
receivedFiles: new Set(),
knownIDs: new Set(),
lastLocalSeq: 0,
sentIDs: new Set(),
sentFiles: new Set(),
}));
await this.resetRemoteBucket();
Logger(`Deleted all data on remote server`, LOG_LEVEL_NOTICE);
})
)
.addOnUpdate(onlyOnMinIO);
}
);
void addPanel(paneEl, "Reset").then((paneEl) => {
new Setting(paneEl)