一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
68 行
2.1 KiB
TypeScript
68 行
2.1 KiB
TypeScript
import Note from '../../models/Note';
|
|
import ConflictNoteState from '../../models/ConflictNoteState';
|
|
import { autoMerge, MergedSection, twoWayDiff } from './diffNotes';
|
|
import { viewerDiffOptions } from './boundedDiff3';
|
|
import isConflictResolutionEnabled from './isConflictResolutionEnabled';
|
|
|
|
export enum ConflictDataStatus {
|
|
Ok = 'ok',
|
|
// No three-way data for this note (no state row, or it is still encrypted or
|
|
// locked), so show the read-only conflict view
|
|
Unavailable = 'unavailable',
|
|
}
|
|
|
|
export interface ConflictData {
|
|
status: ConflictDataStatus;
|
|
sections: MergedSection[];
|
|
localTitle: string;
|
|
remoteTitle: string;
|
|
titleConflict: boolean;
|
|
}
|
|
|
|
const unavailable = (): ConflictData => {
|
|
return {
|
|
status: ConflictDataStatus.Unavailable,
|
|
sections: [],
|
|
localTitle: '',
|
|
remoteTitle: '',
|
|
titleConflict: false,
|
|
};
|
|
};
|
|
|
|
// Sections are recomputed on each call because they were never stored.
|
|
export default async (noteId: string): Promise<ConflictData> => {
|
|
if (!isConflictResolutionEnabled()) return unavailable();
|
|
|
|
const note = await Note.load(noteId);
|
|
if (!note) return unavailable();
|
|
|
|
// No readable body to diff against yet - decryption re-saves the note, so the
|
|
// merge can be recomputed then.
|
|
if (note.encryption_applied || note.is_locked) return unavailable();
|
|
|
|
const state = await ConflictNoteState.byNoteId(noteId);
|
|
if (!state) return unavailable();
|
|
|
|
// The remote version stays as the original note.
|
|
const remoteNote = note.conflict_original_id ? await Note.load(note.conflict_original_id) : null;
|
|
if (!remoteNote) return unavailable();
|
|
if (remoteNote.encryption_applied || remoteNote.is_locked) return unavailable();
|
|
|
|
const localBody = note.body ?? '';
|
|
const remoteBody = remoteNote.body ?? '';
|
|
const baseBody = state.base_body ?? '';
|
|
|
|
const merged = baseBody ? autoMerge(baseBody, localBody, remoteBody, viewerDiffOptions) : twoWayDiff(localBody, remoteBody);
|
|
|
|
const localTitle = note.title ?? '';
|
|
const remoteTitle = remoteNote.title ?? '';
|
|
|
|
return {
|
|
status: ConflictDataStatus.Ok,
|
|
sections: merged.sections,
|
|
localTitle,
|
|
remoteTitle,
|
|
titleConflict: localTitle !== remoteTitle,
|
|
};
|
|
};
|