mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-07-27 23:13:03 +00:00
refactor: align platform APIs with community review
This commit is contained in:
@@ -17,6 +17,11 @@ import { isPlainText, stripPrefix } from "@vrtmrz/livesync-commonlib/compat/stri
|
||||
import { scheduleOnceIfDuplicated } from "octagonal-wheels/concurrency/lock";
|
||||
import type { LiveSyncBaseCore } from "@/LiveSyncBaseCore.ts";
|
||||
import { compatGlobal } from "@vrtmrz/livesync-commonlib/compat/common/coreEnvFunctions";
|
||||
import {
|
||||
DOCUMENT_HISTORY_PREFERENCE_KEYS,
|
||||
loadDocumentHistoryPreference,
|
||||
saveDocumentHistoryPreference,
|
||||
} from "./documentHistoryPreferences.ts";
|
||||
|
||||
function isImage(path: string) {
|
||||
const ext = path.split(".").splice(-1)[0].toLowerCase();
|
||||
@@ -99,12 +104,10 @@ export class DocumentHistoryModal extends Modal {
|
||||
if (!file && id) {
|
||||
this.file = this.services.path.id2path(id);
|
||||
}
|
||||
// eslint-disable-next-line obsidianmd/no-unsupported-api -- loadLocalStorage is supported in Obsidian 1.7.2+
|
||||
if (this.app.loadLocalStorage("ols-history-highlightdiff") == "1") {
|
||||
if (loadDocumentHistoryPreference(this.app, DOCUMENT_HISTORY_PREFERENCE_KEYS.highlightDiff)) {
|
||||
this.showDiff = true;
|
||||
}
|
||||
// eslint-disable-next-line obsidianmd/no-unsupported-api -- loadLocalStorage is supported in Obsidian 1.7.2+
|
||||
if (this.app.loadLocalStorage("ols-history-diffonly") == "1") {
|
||||
if (loadDocumentHistoryPreference(this.app, DOCUMENT_HISTORY_PREFERENCE_KEYS.diffOnly)) {
|
||||
this.diffOnly = true;
|
||||
}
|
||||
}
|
||||
@@ -520,10 +523,10 @@ export class DocumentHistoryModal extends Modal {
|
||||
e.addEventListener("click", () => this.navigateSearch("next"));
|
||||
});
|
||||
|
||||
this.searchResultIndicator = searchRow.createEl("span", { text: "" });
|
||||
this.searchResultIndicator = searchRow.createSpan({ text: "" });
|
||||
this.searchResultIndicator.addClass("history-search-result-indicator");
|
||||
|
||||
this.searchProgressIndicator = searchRow.createEl("span", { text: "" });
|
||||
this.searchProgressIndicator = searchRow.createSpan({ text: "" });
|
||||
this.searchProgressIndicator.addClass("history-search-progress-indicator");
|
||||
|
||||
const divView = contentEl.createDiv("");
|
||||
@@ -554,8 +557,11 @@ export class DocumentHistoryModal extends Modal {
|
||||
}
|
||||
checkbox.addEventListener("input", (evt: Event) => {
|
||||
this.showDiff = checkbox.checked;
|
||||
// eslint-disable-next-line obsidianmd/no-unsupported-api -- saveLocalStorage is supported in Obsidian 1.7.2+
|
||||
this.app.saveLocalStorage("ols-history-highlightdiff", this.showDiff == true ? "1" : null);
|
||||
saveDocumentHistoryPreference(
|
||||
this.app,
|
||||
DOCUMENT_HISTORY_PREFERENCE_KEYS.highlightDiff,
|
||||
this.showDiff
|
||||
);
|
||||
this.updateDiffNavVisibility();
|
||||
void scheduleOnceIfDuplicated("loadRevs", () => this.loadRevs());
|
||||
});
|
||||
@@ -570,8 +576,7 @@ export class DocumentHistoryModal extends Modal {
|
||||
}
|
||||
checkbox.addEventListener("input", (evt: Event) => {
|
||||
this.diffOnly = checkbox.checked;
|
||||
// eslint-disable-next-line obsidianmd/no-unsupported-api -- saveLocalStorage is supported in Obsidian 1.7.2+
|
||||
this.app.saveLocalStorage("ols-history-diffonly", this.diffOnly == true ? "1" : null);
|
||||
saveDocumentHistoryPreference(this.app, DOCUMENT_HISTORY_PREFERENCE_KEYS.diffOnly, this.diffOnly);
|
||||
void scheduleOnceIfDuplicated("loadRevs", () => this.loadRevs());
|
||||
});
|
||||
});
|
||||
@@ -597,7 +602,7 @@ export class DocumentHistoryModal extends Modal {
|
||||
this.navigateDiff("next");
|
||||
});
|
||||
});
|
||||
this.diffNavIndicator = this.diffNavContainer.createEl("span", { text: "\u2014" });
|
||||
this.diffNavIndicator = this.diffNavContainer.createSpan({ text: "\u2014" });
|
||||
this.diffNavIndicator.addClass("diff-nav-indicator");
|
||||
|
||||
this.info = contentEl.createDiv("");
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { requireApiVersion, type App } from "@/deps.ts";
|
||||
|
||||
export const DOCUMENT_HISTORY_PREFERENCE_KEYS = {
|
||||
diffOnly: "ols-history-diffonly",
|
||||
highlightDiff: "ols-history-highlightdiff",
|
||||
} as const;
|
||||
|
||||
export type DocumentHistoryPreferenceKey =
|
||||
(typeof DOCUMENT_HISTORY_PREFERENCE_KEYS)[keyof typeof DOCUMENT_HISTORY_PREFERENCE_KEYS];
|
||||
|
||||
export function loadDocumentHistoryPreference(app: App, key: DocumentHistoryPreferenceKey): boolean {
|
||||
if (requireApiVersion("1.8.7")) {
|
||||
return app.loadLocalStorage(key) === "1";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function saveDocumentHistoryPreference(app: App, key: DocumentHistoryPreferenceKey, enabled: boolean): void {
|
||||
if (requireApiVersion("1.8.7")) {
|
||||
app.saveLocalStorage(key, enabled ? "1" : null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const requireApiVersionMock = vi.hoisted(() => vi.fn<(version: string) => boolean>());
|
||||
|
||||
vi.mock("@/deps.ts", () => ({
|
||||
requireApiVersion: requireApiVersionMock,
|
||||
}));
|
||||
|
||||
import {
|
||||
DOCUMENT_HISTORY_PREFERENCE_KEYS,
|
||||
loadDocumentHistoryPreference,
|
||||
saveDocumentHistoryPreference,
|
||||
} from "./documentHistoryPreferences.ts";
|
||||
|
||||
function createAppStorage() {
|
||||
return {
|
||||
loadLocalStorage: vi.fn<(key: string) => unknown>(),
|
||||
saveLocalStorage: vi.fn<(key: string, value: unknown | null) => void>(),
|
||||
};
|
||||
}
|
||||
|
||||
describe("document history preferences", () => {
|
||||
beforeEach(() => {
|
||||
requireApiVersionMock.mockReset();
|
||||
});
|
||||
|
||||
it("falls back without accessing Vault local storage on older Obsidian versions", () => {
|
||||
requireApiVersionMock.mockReturnValue(false);
|
||||
const app = createAppStorage();
|
||||
|
||||
expect(loadDocumentHistoryPreference(app as never, DOCUMENT_HISTORY_PREFERENCE_KEYS.highlightDiff)).toBe(false);
|
||||
saveDocumentHistoryPreference(app as never, DOCUMENT_HISTORY_PREFERENCE_KEYS.diffOnly, true);
|
||||
|
||||
expect(requireApiVersionMock).toHaveBeenCalledWith("1.8.7");
|
||||
expect(app.loadLocalStorage).not.toHaveBeenCalled();
|
||||
expect(app.saveLocalStorage).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("loads and saves Vault-scoped preferences when the API is available", () => {
|
||||
requireApiVersionMock.mockReturnValue(true);
|
||||
const app = createAppStorage();
|
||||
app.loadLocalStorage.mockReturnValue("1");
|
||||
|
||||
expect(loadDocumentHistoryPreference(app as never, DOCUMENT_HISTORY_PREFERENCE_KEYS.diffOnly)).toBe(true);
|
||||
saveDocumentHistoryPreference(app as never, DOCUMENT_HISTORY_PREFERENCE_KEYS.highlightDiff, true);
|
||||
saveDocumentHistoryPreference(app as never, DOCUMENT_HISTORY_PREFERENCE_KEYS.highlightDiff, false);
|
||||
|
||||
expect(app.loadLocalStorage).toHaveBeenCalledWith("ols-history-diffonly");
|
||||
expect(app.saveLocalStorage).toHaveBeenNthCalledWith(1, "ols-history-highlightdiff", "1");
|
||||
expect(app.saveLocalStorage).toHaveBeenNthCalledWith(2, "ols-history-highlightdiff", null);
|
||||
});
|
||||
});
|
||||
@@ -110,7 +110,7 @@ export class ConflictResolveModal extends Modal {
|
||||
contentEl.empty();
|
||||
const diffOptionsRow = contentEl.createDiv("");
|
||||
diffOptionsRow.addClass("diff-options-row");
|
||||
diffOptionsRow.createEl("span", { text: this.filename });
|
||||
diffOptionsRow.createSpan({ text: this.filename });
|
||||
|
||||
const diffNavContainer = diffOptionsRow.createDiv("");
|
||||
diffNavContainer.addClass("diff-nav");
|
||||
@@ -122,7 +122,7 @@ export class ConflictResolveModal extends Modal {
|
||||
e.addClass("diff-nav-btn");
|
||||
e.addEventListener("click", () => this.navigateDiff("next"));
|
||||
});
|
||||
this.diffNavIndicator = diffNavContainer.createEl("span", { text: "\u2014" });
|
||||
this.diffNavIndicator = diffNavContainer.createSpan({ text: "\u2014" });
|
||||
this.diffNavIndicator.addClass("diff-nav-indicator");
|
||||
|
||||
this.diffView = contentEl.createDiv("");
|
||||
|
||||
@@ -736,7 +736,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
value: `${order}`,
|
||||
cls: "sls-setting-tab",
|
||||
} as DomElementInfo);
|
||||
el.createEl("div", {
|
||||
el.createDiv({
|
||||
cls: "sls-setting-menu-btn",
|
||||
text: icon,
|
||||
title: title,
|
||||
|
||||
@@ -188,7 +188,7 @@ export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElemen
|
||||
}
|
||||
this.requestUpdate();
|
||||
};
|
||||
text.inputEl.before((dateEl = activeDocument.createElement("span")));
|
||||
text.inputEl.before((dateEl = activeDocument.createSpan()));
|
||||
text.inputEl.type = "datetime-local";
|
||||
if (this.editingSettings.maxMTimeForReflectEvents > 0) {
|
||||
const date = new Date(this.editingSettings.maxMTimeForReflectEvents);
|
||||
|
||||
@@ -16,13 +16,7 @@ import type { PageFunctions } from "./SettingPane.ts";
|
||||
import InfoPanel from "./InfoPanel.svelte";
|
||||
import { writable } from "svelte/store";
|
||||
import { SveltePanel } from "./SveltePanel.ts";
|
||||
import {
|
||||
getBucketConfigSummary,
|
||||
getP2PConfigSummary,
|
||||
getCouchDBConfigSummary,
|
||||
getE2EEConfigSummary,
|
||||
} from "./settingUtils.ts";
|
||||
import { SETTING_KEY_P2P_DEVICE_NAME } from "@vrtmrz/livesync-commonlib/compat/common/types";
|
||||
import { getE2EEConfigSummary } from "./settingUtils.ts";
|
||||
import { SetupManager, UserMode } from "@/modules/features/SetupManager.ts";
|
||||
import { OnDialogSettingsDefault, type AllSettings } from "./settingConstants.ts";
|
||||
import { activateRemoteConfiguration } from "@vrtmrz/livesync-commonlib/compat/serviceFeatures/remoteConfig";
|
||||
@@ -43,15 +37,6 @@ 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 {};
|
||||
};
|
||||
|
||||
function createRemoteConfigurationId(): string {
|
||||
return `remote-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
}
|
||||
@@ -517,123 +502,6 @@ export function paneRemoteConfig(
|
||||
refreshList();
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
if (false) {
|
||||
const initialProps = {
|
||||
info: getCouchDBConfigSummary(this.editingSettings),
|
||||
};
|
||||
const summaryWritable = writable(initialProps);
|
||||
const updateSummary = () => {
|
||||
summaryWritable.set({
|
||||
info: getCouchDBConfigSummary(this.editingSettings),
|
||||
});
|
||||
};
|
||||
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.core.getModule(SetupManager);
|
||||
const originalSettings = getSettingsFromEditingSettings(this.editingSettings);
|
||||
await setupManager.onCouchDBManualSetup(
|
||||
UserMode.Update,
|
||||
originalSettings,
|
||||
this.editingSettings.remoteType === REMOTE_COUCHDB
|
||||
);
|
||||
|
||||
updateSummary();
|
||||
})
|
||||
)
|
||||
.addOnUpdate(() =>
|
||||
toggleActiveSyncClass(paneEl, () => this.editingSettings.remoteType === REMOTE_COUCHDB)
|
||||
);
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
if (false) {
|
||||
const initialProps = {
|
||||
info: getBucketConfigSummary(this.editingSettings),
|
||||
};
|
||||
const summaryWritable = writable(initialProps);
|
||||
const updateSummary = () => {
|
||||
summaryWritable.set({
|
||||
info: getBucketConfigSummary(this.editingSettings),
|
||||
});
|
||||
};
|
||||
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.core.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)
|
||||
);
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
if (false) {
|
||||
const getDevicePeerId = () => this.services.config.getSmallConfig(SETTING_KEY_P2P_DEVICE_NAME) || "";
|
||||
const initialProps = {
|
||||
info: getP2PConfigSummary(this.editingSettings, {
|
||||
"Device Peer ID": getDevicePeerId(),
|
||||
}),
|
||||
};
|
||||
const summaryWritable = writable(initialProps);
|
||||
const updateSummary = () => {
|
||||
summaryWritable.set({
|
||||
info: getP2PConfigSummary(this.editingSettings, {
|
||||
"Device Peer ID": getDevicePeerId(),
|
||||
}),
|
||||
});
|
||||
};
|
||||
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.core.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
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// new Setting(paneEl)
|
||||
// .setDesc("Generate ES256 Keypair for testing")
|
||||
// .addButton((button) =>
|
||||
|
||||
@@ -60,7 +60,7 @@ export const enum UserMode {
|
||||
/**
|
||||
* 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
|
||||
// eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values -- Update is a semantic alias for the unknown setup mode.
|
||||
Update = "unknown", // Alias for Unknown for better readability
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user