feat: Added a conflict resolution dialog and a Conflicts view for easier management of conflicts (#2337)
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -5,4 +5,5 @@ export enum SystemViewId {
|
||||
TrashedNotes = 'trashed-notes',
|
||||
UntaggedNotes = 'untagged-notes',
|
||||
StarredNotes = 'starred-notes',
|
||||
Conflicts = 'conflicts',
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user