You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-06-15 23:00:36 +02:00
All: Refactored so that memory and file sync target use same delta logic
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
const { isHidden } = require('lib/path-utils.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const { shim } = require('lib/shim');
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const JoplinError = require('lib/JoplinError');
|
||||
|
||||
class FileApi {
|
||||
@ -120,4 +121,113 @@ class FileApi {
|
||||
|
||||
}
|
||||
|
||||
module.exports = { FileApi };
|
||||
function basicDeltaContextFromOptions_(options) {
|
||||
let output = {
|
||||
timestamp: 0,
|
||||
filesAtTimestamp: [],
|
||||
statsCache: null,
|
||||
};
|
||||
|
||||
if (!options || !options.context) return output;
|
||||
const d = new Date(options.context.timestamp);
|
||||
|
||||
output.timestamp = isNaN(d.getTime()) ? 0 : options.context.timestamp;
|
||||
output.filesAtTimestamp = Array.isArray(options.context.filesAtTimestamp) ? options.context.filesAtTimestamp.slice() : [];
|
||||
output.statsCache = options.context && options.context.statsCache ? options.context.statsCache : null;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
// This is the basic delta algorithm, which can be used in case the cloud service does not have
|
||||
// a built-on delta API. OneDrive and Dropbox have one for example, but Nextcloud and obviously
|
||||
// the file system do not.
|
||||
async function basicDelta(path, getStatFn, options) {
|
||||
const outputLimit = 1000;
|
||||
const itemIds = await options.allItemIdsHandler();
|
||||
if (!Array.isArray(itemIds)) throw new Error('Delta API not supported - local IDs must be provided');
|
||||
|
||||
const context = basicDeltaContextFromOptions_(options);
|
||||
|
||||
let newContext = {
|
||||
timestamp: context.timestamp,
|
||||
filesAtTimestamp: context.filesAtTimestamp.slice(),
|
||||
statsCache: context.statsCache,
|
||||
};
|
||||
|
||||
// Stats are cached until all items have been processed (until hasMore is false)
|
||||
if (newContext.statsCache === null) {
|
||||
newContext.statsCache = await getStatFn(path);
|
||||
newContext.statsCache.sort(function(a, b) {
|
||||
return a.updated_time - b.updated_time;
|
||||
});
|
||||
}
|
||||
|
||||
let output = [];
|
||||
|
||||
// Find out which files have been changed since the last time. Note that we keep
|
||||
// both the timestamp of the most recent change, *and* the items that exactly match
|
||||
// this timestamp. This to handle cases where an item is modified while this delta
|
||||
// function is running. For example:
|
||||
// t0: Item 1 is changed
|
||||
// t0: Sync items - run delta function
|
||||
// t0: While delta() is running, modify Item 2
|
||||
// Since item 2 was modified within the same millisecond, it would be skipped in the
|
||||
// next sync if we relied exclusively on a timestamp.
|
||||
for (let i = 0; i < newContext.statsCache.length; i++) {
|
||||
const stat = newContext.statsCache[i];
|
||||
|
||||
if (stat.isDir) continue;
|
||||
|
||||
if (stat.updated_time < context.timestamp) continue;
|
||||
|
||||
// Special case for items that exactly match the timestamp
|
||||
if (stat.updated_time === context.timestamp) {
|
||||
if (context.filesAtTimestamp.indexOf(stat.path) >= 0) continue;
|
||||
}
|
||||
|
||||
if (stat.updated_time > newContext.timestamp) {
|
||||
newContext.timestamp = stat.updated_time;
|
||||
newContext.filesAtTimestamp = [];
|
||||
}
|
||||
|
||||
newContext.filesAtTimestamp.push(stat.path);
|
||||
output.push(stat);
|
||||
|
||||
if (output.length >= outputLimit) break;
|
||||
}
|
||||
|
||||
let deletedItems = [];
|
||||
for (let i = 0; i < itemIds.length; i++) {
|
||||
if (output.length + deletedItems.length >= outputLimit) break;
|
||||
|
||||
const itemId = itemIds[i];
|
||||
let found = false;
|
||||
for (let j = 0; j < newContext.statsCache.length; j++) {
|
||||
const item = newContext.statsCache[j];
|
||||
if (BaseItem.pathToId(item.path) == itemId) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
deletedItems.push({
|
||||
path: BaseItem.systemPath(itemId),
|
||||
isDeleted: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
output = output.concat(deletedItems);
|
||||
|
||||
const hasMore = output.length >= outputLimit;
|
||||
if (!hasMore) newContext.statsCache = null;
|
||||
|
||||
return {
|
||||
hasMore: hasMore,
|
||||
context: newContext,
|
||||
items: output,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = { FileApi, basicDelta };
|
Reference in New Issue
Block a user