Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee224bf4f2 | ||
|
|
f1bd0b1ce6 | ||
|
|
300ed213af | ||
|
|
4be67ce491 | ||
|
|
98107ba4ea |
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "remotely-save",
|
"id": "remotely-save",
|
||||||
"name": "Remotely Save",
|
"name": "Remotely Save",
|
||||||
"version": "0.4.12",
|
"version": "0.4.13",
|
||||||
"minAppVersion": "0.13.21",
|
"minAppVersion": "0.13.21",
|
||||||
"description": "Yet another unofficial plugin allowing users to synchronize notes between local device and the cloud service.",
|
"description": "Yet another unofficial plugin allowing users to synchronize notes between local device and the cloud service.",
|
||||||
"author": "fyears",
|
"author": "fyears",
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "remotely-save",
|
"id": "remotely-save",
|
||||||
"name": "Remotely Save",
|
"name": "Remotely Save",
|
||||||
"version": "0.4.12",
|
"version": "0.4.13",
|
||||||
"minAppVersion": "0.13.21",
|
"minAppVersion": "0.13.21",
|
||||||
"description": "Yet another unofficial plugin allowing users to synchronize notes between local device and the cloud service.",
|
"description": "Yet another unofficial plugin allowing users to synchronize notes between local device and the cloud service.",
|
||||||
"author": "fyears",
|
"author": "fyears",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "remotely-save",
|
"name": "remotely-save",
|
||||||
"version": "0.4.12",
|
"version": "0.4.13",
|
||||||
"description": "This is yet another sync plugin for Obsidian app.",
|
"description": "This is yet another sync plugin for Obsidian app.",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev2": "node esbuild.config.mjs --watch",
|
"dev2": "node esbuild.config.mjs --watch",
|
||||||
|
|||||||
+2
-2
@@ -272,9 +272,9 @@ export const DEFAULT_LOG_HISTORY_FILE_PREFIX = "log_hist_exported_on_";
|
|||||||
|
|
||||||
export type SyncTriggerSourceType =
|
export type SyncTriggerSourceType =
|
||||||
| "manual"
|
| "manual"
|
||||||
| "auto"
|
|
||||||
| "dry"
|
| "dry"
|
||||||
| "autoOnceInit"
|
| "auto"
|
||||||
|
| "auto_once_init"
|
||||||
| "auto_sync_on_save";
|
| "auto_sync_on_save";
|
||||||
|
|
||||||
export const REMOTELY_SAVE_VERSION_2022 = "0.3.25";
|
export const REMOTELY_SAVE_VERSION_2022 = "0.3.25";
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
"goback": "Go Back",
|
"goback": "Go Back",
|
||||||
"submit": "Submit",
|
"submit": "Submit",
|
||||||
"sometext": "Here are some texts.",
|
"sometext": "Here are some texts.",
|
||||||
"syncrun_alreadyrunning": "{{pluginName}} already running in stage {{syncStatus}}!",
|
"syncrun_alreadyrunning": "New command {{newTriggerSource}} stops because {{pluginName}} is already running in stage {{syncStatus}}!",
|
||||||
"syncrun_syncingribbon": "{{pluginName}}: syncing from {{triggerSource}}",
|
"syncrun_syncingribbon": "{{pluginName}}: syncing from {{triggerSource}}",
|
||||||
"syncrun_step0": "0/8 Remotely Save is running in dry mode, thus not actual file changes would happen.",
|
"syncrun_step0": "0/8 Remotely Save is running in dry mode, thus not actual file changes would happen.",
|
||||||
"syncrun_step1": "1/8 Remotely Save is preparing ({{serviceType}})",
|
"syncrun_step1": "1/8 Remotely Save is preparing ({{serviceType}})",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"goback": "返回",
|
"goback": "返回",
|
||||||
"submit": "提交",
|
"submit": "提交",
|
||||||
"sometext": "这里有一段文字。",
|
"sometext": "这里有一段文字。",
|
||||||
"syncrun_alreadyrunning": "{{pluginName}} 正处于此阶段:{{syncStatus}}!",
|
"syncrun_alreadyrunning": "{{pluginName}} 正处于此阶段:{{syncStatus}}!中断触发 {{newTriggerSource}}。",
|
||||||
"syncrun_syncingribbon": "{{pluginName}}:正在由 {{triggerSource}} 触发运行",
|
"syncrun_syncingribbon": "{{pluginName}}:正在由 {{triggerSource}} 触发运行",
|
||||||
"syncrun_step0": "0/8 Remotely Save 在空跑(dry run)模式,不会发生实际的文件交换。",
|
"syncrun_step0": "0/8 Remotely Save 在空跑(dry run)模式,不会发生实际的文件交换。",
|
||||||
"syncrun_step1": "1/8 Remotely Save 准备同步({{serviceType}})",
|
"syncrun_step1": "1/8 Remotely Save 准备同步({{serviceType}})",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"goback": "返回",
|
"goback": "返回",
|
||||||
"submit": "提交",
|
"submit": "提交",
|
||||||
"sometext": "這裡有一段文字。",
|
"sometext": "這裡有一段文字。",
|
||||||
"syncrun_alreadyrunning": "{{pluginName}} 正處於此階段:{{syncStatus}}!",
|
"syncrun_alreadyrunning": "{{pluginName}} 正處於此階段:{{syncStatus}}! 中斷觸發 {{newTriggerSource}}。",
|
||||||
"syncrun_syncingribbon": "{{pluginName}}:正在由 {{triggerSource}} 觸發執行",
|
"syncrun_syncingribbon": "{{pluginName}}:正在由 {{triggerSource}} 觸發執行",
|
||||||
"syncrun_step0": "0/8 Remotely Save 在空跑(dry run)模式,不會發生實際的檔案交換。",
|
"syncrun_step0": "0/8 Remotely Save 在空跑(dry run)模式,不會發生實際的檔案交換。",
|
||||||
"syncrun_step1": "1/8 Remotely Save 準備同步({{serviceType}})",
|
"syncrun_step1": "1/8 Remotely Save 準備同步({{serviceType}})",
|
||||||
|
|||||||
+54
-28
@@ -9,6 +9,7 @@ import {
|
|||||||
Platform,
|
Platform,
|
||||||
requestUrl,
|
requestUrl,
|
||||||
requireApiVersion,
|
requireApiVersion,
|
||||||
|
Events,
|
||||||
} from "obsidian";
|
} from "obsidian";
|
||||||
import cloneDeep from "lodash/cloneDeep";
|
import cloneDeep from "lodash/cloneDeep";
|
||||||
import { createElement, RotateCcw, RefreshCcw, FileText } from "lucide";
|
import { createElement, RotateCcw, RefreshCcw, FileText } from "lucide";
|
||||||
@@ -149,6 +150,8 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
i18n!: I18n;
|
i18n!: I18n;
|
||||||
vaultRandomID!: string;
|
vaultRandomID!: string;
|
||||||
debugServerTemp?: string;
|
debugServerTemp?: string;
|
||||||
|
syncEvent?: Events;
|
||||||
|
appContainerObserver?: MutationObserver;
|
||||||
|
|
||||||
async syncRun(triggerSource: SyncTriggerSourceType = "manual") {
|
async syncRun(triggerSource: SyncTriggerSourceType = "manual") {
|
||||||
const t = (x: TransItemType, vars?: any) => {
|
const t = (x: TransItemType, vars?: any) => {
|
||||||
@@ -165,15 +168,17 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (this.syncStatus !== "idle") {
|
if (this.syncStatus !== "idle") {
|
||||||
// here the notice is shown regardless of triggerSource
|
// really, users don't want to see this in auto mode
|
||||||
new Notice(
|
// so we use getNotice to avoid unnecessary show up
|
||||||
|
getNotice(
|
||||||
t("syncrun_alreadyrunning", {
|
t("syncrun_alreadyrunning", {
|
||||||
pluginName: this.manifest.name,
|
pluginName: this.manifest.name,
|
||||||
syncStatus: this.syncStatus,
|
syncStatus: this.syncStatus,
|
||||||
|
newTriggerSource: triggerSource,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
if (this.currSyncMsg !== undefined && this.currSyncMsg !== "") {
|
if (this.currSyncMsg !== undefined && this.currSyncMsg !== "") {
|
||||||
new Notice(this.currSyncMsg);
|
getNotice(this.currSyncMsg);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -376,7 +381,8 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
realCounter,
|
realCounter,
|
||||||
realTotalCount,
|
realTotalCount,
|
||||||
pathName,
|
pathName,
|
||||||
decision
|
decision,
|
||||||
|
triggerSource
|
||||||
),
|
),
|
||||||
this.db
|
this.db
|
||||||
);
|
);
|
||||||
@@ -416,6 +422,7 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
this.updateLastSuccessSyncMsg(lastSuccessSyncMillis);
|
this.updateLastSuccessSyncMsg(lastSuccessSyncMillis);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.syncEvent?.trigger("SYNC_DONE");
|
||||||
console.info(
|
console.info(
|
||||||
`${
|
`${
|
||||||
this.manifest.id
|
this.manifest.id
|
||||||
@@ -465,6 +472,8 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
|
|
||||||
this.currSyncMsg = "";
|
this.currSyncMsg = "";
|
||||||
|
|
||||||
|
this.syncEvent = new Events();
|
||||||
|
|
||||||
await this.loadSettings();
|
await this.loadSettings();
|
||||||
|
|
||||||
// MUST after loadSettings and before prepareDB
|
// MUST after loadSettings and before prepareDB
|
||||||
@@ -812,6 +821,10 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
async onunload() {
|
async onunload() {
|
||||||
console.info(`unloading plugin ${this.manifest.id}`);
|
console.info(`unloading plugin ${this.manifest.id}`);
|
||||||
this.syncRibbon = undefined;
|
this.syncRibbon = undefined;
|
||||||
|
if (this.appContainerObserver !== undefined) {
|
||||||
|
this.appContainerObserver.disconnect();
|
||||||
|
this.appContainerObserver = undefined;
|
||||||
|
}
|
||||||
if (this.oauth2Info !== undefined) {
|
if (this.oauth2Info !== undefined) {
|
||||||
this.oauth2Info.helperModal = undefined;
|
this.oauth2Info.helperModal = undefined;
|
||||||
this.oauth2Info = {
|
this.oauth2Info = {
|
||||||
@@ -1100,7 +1113,7 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
) {
|
) {
|
||||||
this.app.workspace.onLayoutReady(() => {
|
this.app.workspace.onLayoutReady(() => {
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
this.syncRun("autoOnceInit");
|
this.syncRun("auto_once_init");
|
||||||
}, this.settings.initRunAfterMilliseconds);
|
}, this.settings.initRunAfterMilliseconds);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1126,52 +1139,64 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
}, scheduleTimeFromNow);
|
}, scheduleTimeFromNow);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.app.workspace.onLayoutReady(() => {
|
const checkCurrFileModified = async (caller: "SYNC" | "FILE_CHANGES") => {
|
||||||
const intervalID = window.setInterval(() => {
|
|
||||||
const currentFile = this.app.workspace.getActiveFile();
|
const currentFile = this.app.workspace.getActiveFile();
|
||||||
|
|
||||||
if (currentFile) {
|
if (currentFile) {
|
||||||
// get the last modified time of the current file
|
// get the last modified time of the current file
|
||||||
// if it has been modified within the last syncOnSaveAfterMilliseconds
|
// if it has modified after lastSuccessSync
|
||||||
// then schedule a run for syncOnSaveAfterMilliseconds after it was modified
|
// then schedule a run for syncOnSaveAfterMilliseconds after it was modified
|
||||||
const lastModified = currentFile.stat.mtime;
|
const lastModified = currentFile.stat.mtime;
|
||||||
const currentTime = Date.now();
|
const lastSuccessSyncMillis = await getLastSuccessSyncTimeByVault(
|
||||||
|
this.db,
|
||||||
|
this.vaultRandomID
|
||||||
|
);
|
||||||
if (
|
if (
|
||||||
currentTime - lastModified <
|
this.syncStatus === "idle" &&
|
||||||
this.settings!.syncOnSaveAfterMilliseconds!
|
lastModified > lastSuccessSyncMillis &&
|
||||||
|
!runScheduled
|
||||||
) {
|
) {
|
||||||
if (
|
scheduleSyncOnSave(this.settings!.syncOnSaveAfterMilliseconds!);
|
||||||
!needToRunAgain &&
|
|
||||||
!runScheduled &&
|
|
||||||
this.syncStatus === "idle"
|
|
||||||
) {
|
|
||||||
const scheduleTimeFromNow =
|
|
||||||
this.settings!.syncOnSaveAfterMilliseconds! -
|
|
||||||
(currentTime - lastModified);
|
|
||||||
scheduleSyncOnSave(scheduleTimeFromNow);
|
|
||||||
} else if (
|
} else if (
|
||||||
|
this.syncStatus === "idle" &&
|
||||||
needToRunAgain &&
|
needToRunAgain &&
|
||||||
!runScheduled &&
|
!runScheduled
|
||||||
this.syncStatus === "idle"
|
|
||||||
) {
|
) {
|
||||||
scheduleSyncOnSave(this.settings!.syncOnSaveAfterMilliseconds!);
|
scheduleSyncOnSave(this.settings!.syncOnSaveAfterMilliseconds!);
|
||||||
needToRunAgain = false;
|
needToRunAgain = false;
|
||||||
} else {
|
} else {
|
||||||
|
if (caller === "FILE_CHANGES") {
|
||||||
needToRunAgain = true;
|
needToRunAgain = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, this.settings.syncOnSaveAfterMilliseconds);
|
};
|
||||||
this.syncOnSaveIntervalID = intervalID;
|
|
||||||
this.registerInterval(intervalID);
|
this.app.workspace.onLayoutReady(() => {
|
||||||
|
// listen to sync done
|
||||||
|
this.registerEvent(
|
||||||
|
this.syncEvent?.on("SYNC_DONE", () => {
|
||||||
|
checkCurrFileModified("SYNC");
|
||||||
|
})!
|
||||||
|
);
|
||||||
|
|
||||||
|
// listen to current file save changes
|
||||||
|
this.registerEvent(
|
||||||
|
this.app.vault.on("modify", (x) => {
|
||||||
|
// console.debug(`event=modify! file=${x}`);
|
||||||
|
checkCurrFileModified("FILE_CHANGES");
|
||||||
|
})
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enableMobileStatusBarIfSet() {
|
enableMobileStatusBarIfSet() {
|
||||||
|
this.app.workspace.onLayoutReady(() => {
|
||||||
if (Platform.isMobile && this.settings.enableMobileStatusBar) {
|
if (Platform.isMobile && this.settings.enableMobileStatusBar) {
|
||||||
changeMobileStatusBar("enable");
|
this.appContainerObserver = changeMobileStatusBar("enable");
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveAgreeToUseNewSyncAlgorithm() {
|
async saveAgreeToUseNewSyncAlgorithm() {
|
||||||
@@ -1183,9 +1208,10 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
i: number,
|
i: number,
|
||||||
totalCount: number,
|
totalCount: number,
|
||||||
pathName: string,
|
pathName: string,
|
||||||
decision: string
|
decision: string,
|
||||||
|
triggerSource: SyncTriggerSourceType
|
||||||
) {
|
) {
|
||||||
const msg = `syncing progress=${i}/${totalCount},decision=${decision},path=${pathName}`;
|
const msg = `syncing progress=${i}/${totalCount},decision=${decision},path=${pathName},source=${triggerSource}`;
|
||||||
this.currSyncMsg = msg;
|
this.currSyncMsg = msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+67
-7
@@ -517,21 +517,81 @@ export const stringToFragment = (string: string) => {
|
|||||||
* https://forum.obsidian.md/t/css-to-show-status-bar-on-mobile-devices/77185
|
* https://forum.obsidian.md/t/css-to-show-status-bar-on-mobile-devices/77185
|
||||||
* @param op
|
* @param op
|
||||||
*/
|
*/
|
||||||
export const changeMobileStatusBar = (op: "enable" | "disable") => {
|
export const changeMobileStatusBar = (
|
||||||
const bar = document.querySelector(
|
op: "enable" | "disable",
|
||||||
|
oldAppContainerObserver?: MutationObserver
|
||||||
|
) => {
|
||||||
|
const appContainer = document.getElementsByClassName("app-container")[0] as
|
||||||
|
| HTMLElement
|
||||||
|
| undefined;
|
||||||
|
|
||||||
|
const statusbar = document.querySelector(
|
||||||
".is-mobile .app-container .status-bar"
|
".is-mobile .app-container .status-bar"
|
||||||
) as HTMLElement;
|
) as HTMLElement | undefined;
|
||||||
|
|
||||||
|
if (appContainer === undefined || statusbar === undefined) {
|
||||||
|
// give up, exit
|
||||||
|
console.warn(`give up watching appContainer for statusbar`);
|
||||||
|
console.warn(`appContainer=${appContainer}, statusbar=${statusbar}`);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
if (op === "enable") {
|
if (op === "enable") {
|
||||||
bar.style.setProperty("display", "flex");
|
const callback = async (
|
||||||
|
mutationList: MutationRecord[],
|
||||||
|
observer: MutationObserver
|
||||||
|
) => {
|
||||||
|
for (const mutation of mutationList) {
|
||||||
|
// console.debug(mutation);
|
||||||
|
if (mutation.type === "childList" && mutation.addedNodes.length > 0) {
|
||||||
|
const k = mutation.addedNodes[0] as Element;
|
||||||
|
if (
|
||||||
|
k.className.contains("mobile-navbar") ||
|
||||||
|
k.className.contains("mobile-toolbar")
|
||||||
|
) {
|
||||||
|
// have to wait, otherwise the height is not correct??
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
||||||
|
const height = window
|
||||||
|
.getComputedStyle(k as Element)
|
||||||
|
.getPropertyValue("height");
|
||||||
|
|
||||||
|
statusbar.style.setProperty("display", "flex");
|
||||||
|
statusbar.style.setProperty("margin-bottom", height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const observer = new MutationObserver(callback);
|
||||||
|
observer.observe(appContainer, {
|
||||||
|
attributes: false,
|
||||||
|
childList: true,
|
||||||
|
characterData: false,
|
||||||
|
subtree: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
// init, manual call
|
||||||
const navBar = document.getElementsByClassName(
|
const navBar = document.getElementsByClassName(
|
||||||
"mobile-navbar"
|
"mobile-navbar"
|
||||||
)[0] as HTMLElement;
|
)[0] as HTMLElement;
|
||||||
// thanks to community's solution
|
// thanks to community's solution
|
||||||
const height = window.getComputedStyle(navBar).getPropertyValue("height");
|
const height = window.getComputedStyle(navBar).getPropertyValue("height");
|
||||||
bar.style.setProperty("margin-bottom", height);
|
statusbar.style.setProperty("display", "flex");
|
||||||
|
statusbar.style.setProperty("margin-bottom", height);
|
||||||
|
} catch (e) {
|
||||||
|
// skip
|
||||||
|
}
|
||||||
|
|
||||||
|
return observer;
|
||||||
} else {
|
} else {
|
||||||
bar.style.removeProperty("display");
|
if (oldAppContainerObserver !== undefined) {
|
||||||
bar.style.removeProperty("margin-bottom");
|
console.debug(`disconnect oldAppContainerObserver`);
|
||||||
|
oldAppContainerObserver.disconnect();
|
||||||
|
oldAppContainerObserver = undefined;
|
||||||
|
}
|
||||||
|
statusbar.style.removeProperty("display");
|
||||||
|
statusbar.style.removeProperty("margin-bottom");
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+7
-1
@@ -2098,10 +2098,16 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
|
|||||||
.onChange(async (val) => {
|
.onChange(async (val) => {
|
||||||
if (val === "enable") {
|
if (val === "enable") {
|
||||||
this.plugin.settings.enableMobileStatusBar = true;
|
this.plugin.settings.enableMobileStatusBar = true;
|
||||||
|
this.plugin.appContainerObserver =
|
||||||
changeMobileStatusBar("enable");
|
changeMobileStatusBar("enable");
|
||||||
} else {
|
} else {
|
||||||
this.plugin.settings.enableMobileStatusBar = false;
|
this.plugin.settings.enableMobileStatusBar = false;
|
||||||
changeMobileStatusBar("disable");
|
changeMobileStatusBar(
|
||||||
|
"disable",
|
||||||
|
this.plugin.appContainerObserver
|
||||||
|
);
|
||||||
|
this.plugin.appContainerObserver?.disconnect();
|
||||||
|
this.plugin.appContainerObserver = undefined;
|
||||||
}
|
}
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user