feat: Added a conflict resolution dialog and a Conflicts view for easier management of conflicts (#2337)

This commit is contained in:
Aman Harwara
2023-06-25 14:27:51 +05:30
committed by GitHub
parent 49d43fd14b
commit e0e9249334
48 changed files with 1201 additions and 94 deletions

View File

@@ -187,6 +187,8 @@ export abstract class Collection<
const conflictOf = element.content.conflict_of
if (conflictOf) {
this.conflictMap.establishRelationship(conflictOf, element.uuid)
} else if (this.conflictMap.getInverseRelationships(element.uuid).length > 0) {
this.conflictMap.removeFromMap(element.uuid)
}
this.referenceMap.setAllRelationships(
@@ -203,6 +205,9 @@ export abstract class Collection<
if (element.deleted) {
this.nondeletedIndex.delete(element.uuid)
if (this.conflictMap.getInverseRelationships(element.uuid).length > 0) {
this.conflictMap.removeFromMap(element.uuid)
}
} else {
this.nondeletedIndex.add(element.uuid)
}
@@ -260,4 +265,8 @@ export abstract class Collection<
remove(array, { uuid: element.uuid as never })
this.typedMap[element.content_type] = array
}
public numberOfItemsWithConflicts(): number {
return this.conflictMap.directMapSize
}
}

View File

@@ -18,7 +18,7 @@ export class TagItemsIndex implements SNIndex {
private isItemCountable = (item: ItemInterface) => {
if (isDecryptedItem(item)) {
return !item.archived && !item.trashed
return !item.archived && !item.trashed && !item.conflictOf
}
return false
}

View File

@@ -10,8 +10,9 @@ import { FilterDisplayOptions } from './DisplayOptions'
export function computeUnifiedFilterForDisplayOptions(
options: FilterDisplayOptions,
collection: ReferenceLookupCollection,
additionalFilters: ItemFilter[] = [],
): ItemFilter {
const filters = computeFiltersForDisplayOptions(options, collection)
const filters = computeFiltersForDisplayOptions(options, collection).concat(additionalFilters)
return (item: SearchableDecryptedItem) => {
return itemPassesFilters(item, filters)
@@ -74,5 +75,9 @@ export function computeFiltersForDisplayOptions(
filters.push((item) => itemMatchesQuery(item, query, collection))
}
if (!viewsPredicate?.keypathIncludesString('conflict_of')) {
filters.push((item) => !item.conflictOf)
}
return filters
}

View File

@@ -85,7 +85,19 @@ export function BuildSmartViews(options: FilterDisplayOptions): SmartView[] {
}),
)
return [notes, files, starred, archived, trash, untagged]
const conflicts = new SmartView(
new DecryptedPayload({
uuid: SystemViewId.Conflicts,
content_type: ContentType.SmartView,
...PayloadTimestampDefaults(),
content: FillItemContent<SmartViewContent>({
title: 'Conflicts',
predicate: conflictsPredicate(options).toJson(),
}),
}),
)
return [notes, files, starred, archived, trash, untagged, conflicts]
}
function allNotesPredicate(options: FilterDisplayOptions) {
@@ -203,3 +215,26 @@ function starredNotesPredicate(options: FilterDisplayOptions) {
return predicate
}
function conflictsPredicate(options: FilterDisplayOptions) {
const subPredicates: Predicate<SNNote>[] = [new Predicate('content_type', '=', ContentType.Note)]
if (options.includeTrashed === false) {
subPredicates.push(new Predicate('trashed', '=', false))
}
if (options.includeArchived === false) {
subPredicates.push(new Predicate('archived', '=', false))
}
if (options.includeProtected === false) {
subPredicates.push(new Predicate('protected', '=', false))
}
if (options.includePinned === false) {
subPredicates.push(new Predicate('pinned', '=', false))
}
const predicate = new CompoundPredicate('and', subPredicates)
return predicate
}

View File

@@ -8,6 +8,7 @@ export const SmartViewIcons: Record<SystemViewId, IconType> = {
[SystemViewId.TrashedNotes]: 'trash',
[SystemViewId.UntaggedNotes]: 'hashtag-off',
[SystemViewId.StarredNotes]: 'star-filled',
[SystemViewId.Conflicts]: 'merge',
}
export function systemViewIcon(id: SystemViewId): IconType {

View File

@@ -5,4 +5,5 @@ export enum SystemViewId {
TrashedNotes = 'trashed-notes',
UntaggedNotes = 'untagged-notes',
StarredNotes = 'starred-notes',
Conflicts = 'conflicts',
}