From d52fdae0b359b4768855ae030ac13d1c16f2312a Mon Sep 17 00:00:00 2001 From: Mo Date: Mon, 17 Oct 2022 10:22:58 -0500 Subject: [PATCH] feat: starred notes (#1813) --- .../Domain/Abstract/Content/ItemContent.ts | 1 + .../Item/Implementations/DecryptedItem.ts | 2 ++ .../Abstract/Item/Interfaces/DecryptedItem.ts | 1 + .../Item/Mutator/DecryptedItemMutator.ts | 4 +++ .../Domain/Syncable/SmartView/SmartView.ts | 1 + .../Syncable/SmartView/SmartViewBuilder.ts | 35 +++++++++++++++++-- .../ContentListView/ListItemFlagIcons.tsx | 6 ++++ .../Components/NotesOptions/NotesOptions.tsx | 12 +++++++ .../Components/Tags/SmartViewsListItem.tsx | 34 +++++++++--------- .../Controllers/NotesController.ts | 6 ++++ 10 files changed, 84 insertions(+), 18 deletions(-) diff --git a/packages/models/src/Domain/Abstract/Content/ItemContent.ts b/packages/models/src/Domain/Abstract/Content/ItemContent.ts index c8d35f700..06a45bda7 100644 --- a/packages/models/src/Domain/Abstract/Content/ItemContent.ts +++ b/packages/models/src/Domain/Abstract/Content/ItemContent.ts @@ -13,6 +13,7 @@ export interface ItemContent { trashed?: boolean pinned?: boolean archived?: boolean + starred?: boolean locked?: boolean appData?: AppData } diff --git a/packages/models/src/Domain/Abstract/Item/Implementations/DecryptedItem.ts b/packages/models/src/Domain/Abstract/Item/Implementations/DecryptedItem.ts index d45cab07d..3462d4334 100644 --- a/packages/models/src/Domain/Abstract/Item/Implementations/DecryptedItem.ts +++ b/packages/models/src/Domain/Abstract/Item/Implementations/DecryptedItem.ts @@ -21,6 +21,7 @@ export class DecryptedItem public readonly pinned: boolean = false public readonly archived: boolean = false public readonly locked: boolean = false + public readonly starred: boolean = false constructor(payload: DecryptedPayloadInterface) { super(payload) @@ -32,6 +33,7 @@ export class DecryptedItem this.updatedAtString = dateToLocalizedString(this.userModifiedDate) this.protected = useBoolean(this.payload.content.protected, false) this.trashed = useBoolean(this.payload.content.trashed, false) + this.starred = useBoolean(this.payload.content.starred, false) this.pinned = this.getAppDomainValueWithDefault(AppDataField.Pinned, false) this.archived = this.getAppDomainValueWithDefault(AppDataField.Archived, false) this.locked = this.getAppDomainValueWithDefault(AppDataField.Locked, false) diff --git a/packages/models/src/Domain/Abstract/Item/Interfaces/DecryptedItem.ts b/packages/models/src/Domain/Abstract/Item/Interfaces/DecryptedItem.ts index 7bd838f6b..79e13c136 100644 --- a/packages/models/src/Domain/Abstract/Item/Interfaces/DecryptedItem.ts +++ b/packages/models/src/Domain/Abstract/Item/Interfaces/DecryptedItem.ts @@ -22,6 +22,7 @@ export interface DecryptedItemInterface readonly pinned: boolean readonly archived: boolean readonly locked: boolean + readonly starred: boolean readonly userModifiedDate: Date readonly references: ContentReference[] diff --git a/packages/models/src/Domain/Abstract/Item/Mutator/DecryptedItemMutator.ts b/packages/models/src/Domain/Abstract/Item/Mutator/DecryptedItemMutator.ts index f6b3e7610..ce136a686 100644 --- a/packages/models/src/Domain/Abstract/Item/Mutator/DecryptedItemMutator.ts +++ b/packages/models/src/Domain/Abstract/Item/Mutator/DecryptedItemMutator.ts @@ -78,6 +78,10 @@ export class DecryptedItemMutator extends I this.mutableContent.trashed = trashed } + set starred(starred: boolean) { + this.mutableContent.starred = starred + } + public set pinned(pinned: boolean) { this.setAppDataItem(AppDataField.Pinned, pinned) } diff --git a/packages/models/src/Domain/Syncable/SmartView/SmartView.ts b/packages/models/src/Domain/Syncable/SmartView/SmartView.ts index 21af071a6..984349660 100644 --- a/packages/models/src/Domain/Syncable/SmartView/SmartView.ts +++ b/packages/models/src/Domain/Syncable/SmartView/SmartView.ts @@ -12,6 +12,7 @@ export enum SystemViewId { ArchivedNotes = 'archived-notes', TrashedNotes = 'trashed-notes', UntaggedNotes = 'untagged-notes', + StarredNotes = 'starred-notes', } export interface SmartViewContent extends ItemContent { diff --git a/packages/models/src/Domain/Syncable/SmartView/SmartViewBuilder.ts b/packages/models/src/Domain/Syncable/SmartView/SmartViewBuilder.ts index aa3b00af6..3052db51d 100644 --- a/packages/models/src/Domain/Syncable/SmartView/SmartViewBuilder.ts +++ b/packages/models/src/Domain/Syncable/SmartView/SmartViewBuilder.ts @@ -74,10 +74,22 @@ export function BuildSmartViews( }), ) + const starred = new SmartView( + new DecryptedPayload({ + uuid: SystemViewId.StarredNotes, + content_type: ContentType.SmartView, + ...PayloadTimestampDefaults(), + content: FillItemContent({ + title: 'Starred', + predicate: starredNotesPredicate(options).toJson(), + }), + }), + ) + if (supportsFileNavigation) { - return [notes, files, archived, trash, untagged] + return [notes, starred, files, archived, trash, untagged] } else { - return [notes, archived, trash, untagged] + return [notes, starred, archived, trash, untagged] } } @@ -177,3 +189,22 @@ function untaggedNotesPredicate(options: FilterDisplayOptions) { return predicate } + +function starredNotesPredicate(options: FilterDisplayOptions) { + const subPredicates: Predicate[] = [ + new Predicate('starred', '=', true), + new Predicate('content_type', '=', ContentType.Note), + ] + if (options.includeTrashed === false) { + subPredicates.push(new Predicate('trashed', '=', 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 +} diff --git a/packages/web/src/javascripts/Components/ContentListView/ListItemFlagIcons.tsx b/packages/web/src/javascripts/Components/ContentListView/ListItemFlagIcons.tsx index 1af42e9a5..aa877c249 100644 --- a/packages/web/src/javascripts/Components/ContentListView/ListItemFlagIcons.tsx +++ b/packages/web/src/javascripts/Components/ContentListView/ListItemFlagIcons.tsx @@ -8,6 +8,7 @@ type Props = { trashed: ListableContentItem['trashed'] archived: ListableContentItem['archived'] pinned: ListableContentItem['pinned'] + starred: ListableContentItem['starred'] } hasFiles?: boolean } @@ -40,6 +41,11 @@ const ListItemFlagIcons: FunctionComponent = ({ item, hasFiles = false }) )} + {item.starred && ( + + + + )} ) } diff --git a/packages/web/src/javascripts/Components/NotesOptions/NotesOptions.tsx b/packages/web/src/javascripts/Components/NotesOptions/NotesOptions.tsx index 6fa287cc1..48db88890 100644 --- a/packages/web/src/javascripts/Components/NotesOptions/NotesOptions.tsx +++ b/packages/web/src/javascripts/Components/NotesOptions/NotesOptions.tsx @@ -201,6 +201,7 @@ const NotesOptions = ({ const notTrashed = notes.some((note) => !note.trashed) const pinned = notes.some((note) => note.pinned) const unpinned = notes.some((note) => !note.pinned) + const starred = notes.some((note) => note.starred) const editorForNote = useMemo( () => (notes[0] ? application.componentManager.editorForNote(notes[0]) : undefined), @@ -330,6 +331,17 @@ const NotesOptions = ({ linkingController={linkingController} /> )} + + + {unpinned && (