1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-18 09:35:20 +02:00
joplin/packages/lib/services/synchronizer/utils/handleConflictAction.ts

89 lines
3.9 KiB
TypeScript
Raw Normal View History

import { Dispatch } from 'redux';
import Logger from '@joplin/utils/Logger';
import BaseItem from '../../../models/BaseItem';
import ItemChange from '../../../models/ItemChange';
import Note from '../../../models/Note';
import Resource from '../../../models/Resource';
import time from '../../../time';
const logger = Logger.create('handleConflictAction');
export type ConflictAction = 'itemConflict' | 'noteConflict' | 'resourceConflict';
export default async (action: ConflictAction, ItemClass: any, remoteExists: boolean, remoteContent: any, local: any, syncTargetId: number, itemIsReadOnly: boolean, dispatch: Dispatch) => {
logger.debug(`Handling conflict: ${action}`);
logger.debug('remoteExists:', remoteExists);
if (action === 'itemConflict') {
// ------------------------------------------------------------------------------
// For non-note conflicts, we take the remote version (i.e. the version that was
// synced first) and overwrite the local content.
// ------------------------------------------------------------------------------
if (remoteExists) {
local = remoteContent;
const syncTimeQueries = BaseItem.updateSyncTimeQueries(syncTargetId, local, time.unixMs());
await ItemClass.save(local, { autoTimestamp: false, changeSource: ItemChange.SOURCE_SYNC, nextQueries: syncTimeQueries });
} else {
await ItemClass.delete(local.id, {
changeSource: ItemChange.SOURCE_SYNC,
trackDeleted: false,
});
}
} else if (action === 'noteConflict') {
// ------------------------------------------------------------------------------
// First find out if the conflict matters. For example, if the conflict is on the title or body
// we want to preserve all the changes. If it's on todo_completed it doesn't really matter
// so in this case we just take the remote content.
// ------------------------------------------------------------------------------
let mustHandleConflict = true;
if (!itemIsReadOnly && remoteContent) {
mustHandleConflict = Note.mustHandleConflict(local, remoteContent);
}
// ------------------------------------------------------------------------------
// Create a duplicate of local note into Conflicts folder
// (to preserve the user's changes)
// ------------------------------------------------------------------------------
if (mustHandleConflict) {
await Note.createConflictNote(local, ItemChange.SOURCE_SYNC);
}
} else if (action === 'resourceConflict') {
2023-12-13 21:24:58 +02:00
if (!remoteContent || Resource.mustHandleConflict(local, remoteContent)) {
await Resource.createConflictResourceNote(local);
2023-12-13 21:24:58 +02:00
if (remoteExists) {
// The local content we have is no longer valid and should be re-downloaded
await Resource.setLocalState(local.id, {
fetch_status: Resource.FETCH_STATUS_IDLE,
});
}
2023-12-13 21:24:58 +02:00
dispatch({ type: 'SYNC_CREATED_OR_UPDATED_RESOURCE', id: local.id });
}
}
if (['noteConflict', 'resourceConflict'].includes(action)) {
// ------------------------------------------------------------------------------
// For note and resource conflicts, the creation of the conflict item is done
// differently. However the way the local content is handled is the same.
// Either copy the remote content to local or, if the remote content has
// been deleted, delete the local content.
// ------------------------------------------------------------------------------
if (remoteExists) {
local = remoteContent;
const syncTimeQueries = BaseItem.updateSyncTimeQueries(syncTargetId, local, time.unixMs());
await ItemClass.save(local, { autoTimestamp: false, changeSource: ItemChange.SOURCE_SYNC, nextQueries: syncTimeQueries });
if (local.encryption_applied) dispatch({ type: 'SYNC_GOT_ENCRYPTED_ITEM' });
} else {
// Remote no longer exists (note deleted) so delete local one too
await ItemClass.delete(local.id, { changeSource: ItemChange.SOURCE_SYNC, trackDeleted: false });
}
}
};