From 28bd861c9e274327fbe2829a0acd9580832973c6 Mon Sep 17 00:00:00 2001 From: fyears <1142836+fyears@users.noreply.github.com> Date: Tue, 10 May 2022 21:15:30 +0800 Subject: [PATCH] fix stat on Android, add operations to nan ctime mtime and undefined size --- src/encrypt.ts | 12 +++++++----- src/localdb.ts | 4 ++-- src/misc.ts | 27 +++++++++++++++++++++++---- src/obsFolderLister.ts | 6 ++++-- src/sync.ts | 28 ++++++++++++++++++++-------- 5 files changed, 56 insertions(+), 21 deletions(-) diff --git a/src/encrypt.ts b/src/encrypt.ts index 5ae2dea..542d3ad 100644 --- a/src/encrypt.ts +++ b/src/encrypt.ts @@ -166,18 +166,20 @@ export const decryptBase64urlToString = async ( }; export const getSizeFromOrigToEnc = (x: number) => { - if (x < 0 || !Number.isInteger(x)) { - throw Error(`x=${x} is not a valid size`); + if (x < 0 || Number.isNaN(x) || !Number.isInteger(x)) { + throw Error(`getSizeFromOrigToEnc: x=${x} is not a valid size`); } return (Math.floor(x / 16) + 1) * 16 + 16; }; export const getSizeFromEncToOrig = (x: number) => { - if (x < 32 || !Number.isInteger(x)) { - throw Error(`${x} is not a valid size`); + if (x < 32 || Number.isNaN(x) || !Number.isInteger(x)) { + throw Error(`getSizeFromEncToOrig: ${x} is not a valid size`); } if (x % 16 !== 0) { - throw Error(`${x} is not a valid encrypted file size`); + throw Error( + `getSizeFromEncToOrig: ${x} is not a valid encrypted file size` + ); } return { minSize: ((x - 16) / 16 - 1) * 16, diff --git a/src/localdb.ts b/src/localdb.ts index a124f9a..01b7b51 100644 --- a/src/localdb.ts +++ b/src/localdb.ts @@ -5,7 +5,7 @@ import { requireApiVersion, TAbstractFile, TFile, TFolder } from "obsidian"; import { API_VER_STAT_FOLDER, SUPPORTED_SERVICES_TYPE } from "./baseTypes"; import type { SyncPlanType } from "./sync"; -import { toText, unixTimeToStr } from "./misc"; +import { statFix, toText, unixTimeToStr } from "./misc"; import { log } from "./moreOnLog"; @@ -417,7 +417,7 @@ export const insertRenameRecordByVault = async ( if (requireApiVersion(API_VER_STAT_FOLDER)) { // TAbstractFile does not contain these info // but from API_VER_STAT_FOLDER we can manually stat them by path. - const s = await fileOrFolder.vault.adapter.stat(fileOrFolder.path); + const s = await statFix(fileOrFolder.vault, fileOrFolder.path); ctime = s.ctime; mtime = s.mtime; } diff --git a/src/misc.ts b/src/misc.ts index 686a803..897d3ba 100644 --- a/src/misc.ts +++ b/src/misc.ts @@ -339,12 +339,9 @@ export const checkHasSpecialCharForDir = (x: string) => { }; export const unixTimeToStr = (x: number | undefined | null) => { - if (x === undefined) { + if (x === undefined || x === null || Number.isNaN(x)) { return undefined; } - if (x === null) { - return null; - } return window.moment(x).format() as string; }; @@ -408,3 +405,25 @@ export const toText = (x: any) => { return `${x}`; } }; + +/** + * On Android the stat has bugs for folders. So we need a fixed version. + * @param vault + * @param path + */ +export const statFix = async (vault: Vault, path: string) => { + const s = await vault.adapter.stat(path); + if (s.ctime === undefined || s.ctime === null || Number.isNaN(s.ctime)) { + s.ctime = undefined; + } + if (s.mtime === undefined || s.mtime === null || Number.isNaN(s.mtime)) { + s.mtime = undefined; + } + if ( + (s.size === undefined || s.size === null || Number.isNaN(s.size)) && + s.type === "folder" + ) { + s.size = 0; + } + return s; +}; diff --git a/src/obsFolderLister.ts b/src/obsFolderLister.ts index 3b238e6..31e53aa 100644 --- a/src/obsFolderLister.ts +++ b/src/obsFolderLister.ts @@ -2,6 +2,7 @@ import { Vault, Stat, ListedFiles } from "obsidian"; import { Queue } from "@fyears/tsqueue"; import chunk from "lodash/chunk"; import flatten from "lodash/flatten"; +import { statFix } from "./misc"; export interface ObsConfigDirFileType { key: string; @@ -12,7 +13,7 @@ export interface ObsConfigDirFileType { } const isFolderToSkip = (x: string) => { - let specialFolders = [".git", ".svn", "node_modules"]; + let specialFolders = [".git", ".github", ".gitlab", ".svn", "node_modules"]; for (const iterator of specialFolders) { if ( x === iterator || @@ -75,7 +76,8 @@ export const listFilesInObsFolder = async ( const itemsToFetchChunks = chunk(itemsToFetch, CHUNK_SIZE); for (const singleChunk of itemsToFetchChunks) { const r = singleChunk.map(async (x) => { - const statRes = await vault.adapter.stat(x); + const statRes = await statFix(vault, x); + const isFolder = statRes.type === "folder"; let children: ListedFiles = undefined; if (isFolder) { diff --git a/src/sync.ts b/src/sync.ts index d79be82..61e6fca 100644 --- a/src/sync.ts +++ b/src/sync.ts @@ -37,6 +37,7 @@ import { getParentFolder, atWhichLevel, unixTimeToStr, + statFix, } from "./misc"; import { RemoteClient } from "./remote"; import { @@ -336,7 +337,7 @@ const ensembleMixedStates = async ( // ignore continue; } else if (entry instanceof TFile) { - const mtimeLocal = Math.max(entry.stat.mtime || 0, entry.stat.ctime || 0); + const mtimeLocal = Math.max(entry.stat.mtime ?? 0, entry.stat.ctime ?? 0); r = { key: entry.path, existLocal: true, @@ -380,7 +381,10 @@ const ensembleMixedStates = async ( if (syncConfigDir && localConfigDirContents !== undefined) { for (const entry of localConfigDirContents) { const key = entry.key; - const mtimeLocal = Math.max(entry.mtime, entry.ctime); + let mtimeLocal = Math.max(entry.mtime ?? 0, entry.ctime ?? 0); + if (Number.isNaN(mtimeLocal) || mtimeLocal === 0) { + mtimeLocal = undefined; + } const r: FileOrFolderMixedState = { key: key, existLocal: true, @@ -468,10 +472,13 @@ const ensembleMixedStates = async ( changeLocalMtimeUsingMapping: true, }; if (results.hasOwnProperty(key)) { - const mtimeLocal = Math.max( - r.mtimeLocal || 0, - results[key].mtimeLocal || 0 + let mtimeLocal = Math.max( + r.mtimeLocal ?? 0, + results[key].mtimeLocal ?? 0 ); + if (Number.isNaN(mtimeLocal) || mtimeLocal === 0) { + mtimeLocal = undefined; + } results[key].mtimeLocal = mtimeLocal; results[key].mtimeLocalFmt = unixTimeToStr(mtimeLocal); results[key].changeLocalMtimeUsingMapping = @@ -838,9 +845,14 @@ const assignOperationToFolderInplace = async ( // if it was created after deletion, we should keep it as is if (requireApiVersion(API_VER_STAT_FOLDER)) { if (r.existLocal) { - const { ctime, mtime } = await vault.adapter.stat(r.key); - const cmtime = Math.max(ctime, mtime); - if (cmtime > 0 && cmtime >= deltimeLocal && cmtime >= deltimeRemote) { + const { ctime, mtime } = await statFix(vault, r.key); + const cmtime = Math.max(ctime ?? 0, mtime ?? 0); + if ( + !Number.isNaN(cmtime) && + cmtime > 0 && + cmtime >= deltimeLocal && + cmtime >= deltimeRemote + ) { keptFolder.add(getParentFolder(r.key)); if (r.existLocal && r.existRemote) { r.decision = "skipFolder";