This commit is contained in:
Andras Schmelczer 2026-04-25 19:13:26 +01:00
parent 7f62273e72
commit bff3f5a5e9
8 changed files with 167 additions and 79 deletions

View file

@ -45,6 +45,7 @@ export async function scheduleOfflineChanges(
}
}
const renamedPaths = new Set<RelativePath>();
for (const path of locallyPossibleCreatedFiles) {
const content = await operations.read(path);
const contentHash = await hash(content);
@ -62,11 +63,12 @@ export async function scheduleOfflineChanges(
relativePath: path
});
removeFromArray(locallyPossiblyDeletedFiles, matchingDeletedFile);
removeFromArray(locallyPossibleCreatedFiles, path);
renamedPaths.add(path);
}
}
for (const path of locallyPossibleCreatedFiles) {
if (renamedPaths.has(path)) continue;
logger.debug(
`File ${path} was created while offline, scheduling sync to create it`
);

View file

@ -141,13 +141,9 @@ export class SyncEventQueue {
}
if (input.type === SyncEventType.LocalDelete) {
const deleteId = pendingDocumentId ?? documentId;
if (deleteId === undefined) {
throw new Error("Unreachable: deleteId must be defined here");
}
this.events.push({
type: SyncEventType.LocalDelete,
documentId: deleteId
documentId: (pendingDocumentId ?? documentId)!
});
return;
}
@ -174,16 +170,11 @@ export class SyncEventQueue {
}
await this.save();
}
return;
}
const updateId = pendingDocumentId ?? documentId;
if (updateId === undefined) {
throw new Error("Unreachable: updateId must be defined here");
}
this.events.push({
type: SyncEventType.LocalUpdate,
documentId: updateId,
documentId: (pendingDocumentId ?? documentId)!,
path,
originalPath: path
});

View file

@ -162,11 +162,6 @@ export class Syncer {
public reset(): void {
this._isFirstSyncStarted = false;
this.queue.clearPending();
// Don't null the reference synchronously — if the scan is
// still in flight, the next reconnect would spawn a second
// concurrent scan racing on the same queue. Defer the
// clear until the in-flight task actually resolves, so a
// fresh scan can only start once the prior one is done.
const current = this.runningScheduleSyncForOfflineChanges;
if (current !== undefined) {
void current.finally(() => {
@ -619,11 +614,17 @@ export class Syncer {
record: DocumentRecord,
remoteVersion: DocumentVersionWithoutContent
): Promise<void> {
if (record.parentVersionId >= remoteVersion.vaultUpdateId) {
this.logger.debug(`Document ${path} is already up-to-date`);
return;
}
// wait for a local edit to do the actual updating here, so we can't even update the lastSeenUpdateId here
const conflictingDoc = this.queue.getSettledDocumentByPath(
remoteVersion.relativePath
);
const actualPath = await this.operations.move(
path,
remoteVersion.relativePath,
(conflictingDoc?.parentVersionId ?? 0) < remoteVersion.vaultUpdateId
? MoveOnConflict.EXISTING
: MoveOnConflict.NEW
);
if (
!this.queue.hasPendingLocalEventsForDocumentId(
remoteVersion.documentId
@ -645,39 +646,45 @@ export class Syncer {
);
this.queue.lastSeenUpdateId = remoteVersion.vaultUpdateId;
} // else we don't need to update the content, a subsequent local update will do that
else {
void this.syncRemotelyUpdatedFile({
// schedule it so that the lastSeenUpdateId remains consistent
document: remoteVersion
});
void this.syncRemotelyUpdatedFile({
// schedule it so that the lastSeenUpdateId remains consistent
document: remoteVersion
});
// wait for a local edit to do the actual updating here, so we can't even update the lastSeenUpdateId here
const conflictingDoc = this.queue.getSettledDocumentByPath(
remoteVersion.relativePath
);
const actualRelativePath = await this.operations.move(
path,
remoteVersion.relativePath,
(conflictingDoc?.parentVersionId ?? 0) < remoteVersion.vaultUpdateId
? MoveOnConflict.EXISTING
: MoveOnConflict.NEW
);
await this.queue.setDocument(actualRelativePath, {
...record,
remoteRelativePath: actualRelativePath
});
await this.queue.setDocument(actualPath, {
...record,
remoteRelativePath: actualPath
});
}
this.history.addHistoryEntry({
status: SyncStatus.SUCCESS,
details: {
type: SyncType.MOVE,
relativePath: actualRelativePath,
movedFrom: path
},
// todo: eh
message: `File was renamed remotely from ${path} to ${actualRelativePath}`
});
if (actualPath !== path) {
this.history.addHistoryEntry({
status: SyncStatus.SUCCESS,
details: {
type: SyncType.MOVE,
relativePath: actualPath,
movedFrom: path
},
message: `File was renamed remotely from ${path} to ${actualPath}`,
author: remoteVersion.userId,
timestamp: new Date(remoteVersion.updatedDate)
});
} else {
this.history.addHistoryEntry({
status: SyncStatus.SUCCESS,
details: {
type: SyncType.UPDATE,
relativePath: actualPath
},
message: "Successfully applied remote update",
author: remoteVersion.userId,
timestamp: new Date(remoteVersion.updatedDate)
});
}
}
private async processRemoteCreateForNewDocument(