feat: ability to favorite tags + customize icon (#1858)

This commit is contained in:
Mo
2022-10-21 11:11:31 -05:00
committed by GitHub
parent 3b048a31aa
commit cbd0063926
38 changed files with 568 additions and 262 deletions

View File

@@ -1,41 +1,33 @@
import { DecryptedItem } from '../../Abstract/Item/Implementations/DecryptedItem'
import { PredicateInterface, PredicateJsonForm } from '../../Runtime/Predicate/Interface'
import { PredicateInterface } from '../../Runtime/Predicate/Interface'
import { predicateFromJson } from '../../Runtime/Predicate/Generators'
import { ItemContent } from '../../Abstract/Content/ItemContent'
import { DecryptedPayloadInterface } from '../../Abstract/Payload/Interfaces/DecryptedPayload'
import { SystemViewId } from './SystemViewId'
import { EmojiString, IconType } from '../../Utilities/Icon/IconType'
import { SmartViewDefaultIconName, systemViewIcon } from './SmartViewIcons'
import { SmartViewContent } from './SmartViewContent'
export const SMART_TAG_DSL_PREFIX = '!['
export enum SystemViewId {
AllNotes = 'all-notes',
Files = 'files',
ArchivedNotes = 'archived-notes',
TrashedNotes = 'trashed-notes',
UntaggedNotes = 'untagged-notes',
StarredNotes = 'starred-notes',
}
export interface SmartViewContent extends ItemContent {
title: string
predicate: PredicateJsonForm
}
export function isSystemView(view: SmartView): boolean {
return Object.values(SystemViewId).includes(view.uuid as SystemViewId)
}
/**
* A tag that defines a predicate that consumers can use
* to retrieve a dynamic list of items.
*/
export class SmartView extends DecryptedItem<SmartViewContent> {
public readonly predicate!: PredicateInterface<DecryptedItem>
public readonly title: string
public readonly iconString: IconType | EmojiString
constructor(payload: DecryptedPayloadInterface<SmartViewContent>) {
super(payload)
this.title = String(this.content.title || '')
if (isSystemView(this)) {
this.iconString = systemViewIcon(this.uuid as SystemViewId)
} else {
this.iconString = this.payload.content.iconString || SmartViewDefaultIconName
}
try {
this.predicate = this.content.predicate && predicateFromJson(this.content.predicate)
} catch (error) {

View File

@@ -1,6 +1,8 @@
import { DecryptedPayload } from './../../Abstract/Payload/Implementations/DecryptedPayload'
import { SNNote } from '../Note/Note'
import { SmartViewContent, SmartView, SystemViewId } from './SmartView'
import { SmartView } from './SmartView'
import { SmartViewContent } from './SmartViewContent'
import { SystemViewId } from './SystemViewId'
import { ItemWithTags } from '../../Runtime/Display/Search/ItemWithTags'
import { ContentType } from '@standardnotes/common'
import { FillItemContent } from '../../Abstract/Content/ItemContent'

View File

@@ -0,0 +1,6 @@
import { TagContent } from './../Tag/TagContent'
import { PredicateJsonForm } from '../../Runtime/Predicate/Interface'
export interface SmartViewContent extends TagContent {
predicate: PredicateJsonForm
}

View File

@@ -0,0 +1,17 @@
import { SystemViewId } from './SystemViewId'
import { IconType } from '../../Utilities/Icon/IconType'
export const SmartViewIcons: Record<SystemViewId, IconType> = {
[SystemViewId.AllNotes]: 'notes',
[SystemViewId.Files]: 'folder',
[SystemViewId.ArchivedNotes]: 'archive',
[SystemViewId.TrashedNotes]: 'trash',
[SystemViewId.UntaggedNotes]: 'hashtag-off',
[SystemViewId.StarredNotes]: 'star-filled',
}
export function systemViewIcon(id: SystemViewId): IconType {
return SmartViewIcons[id]
}
export const SmartViewDefaultIconName: IconType = 'restore'

View File

@@ -0,0 +1,8 @@
export enum SystemViewId {
AllNotes = 'all-notes',
Files = 'files',
ArchivedNotes = 'archived-notes',
TrashedNotes = 'trashed-notes',
UntaggedNotes = 'untagged-notes',
StarredNotes = 'starred-notes',
}

View File

@@ -1,2 +1,4 @@
export * from './SmartView'
export * from './SmartViewBuilder'
export * from './SystemViewId'
export * from './SmartViewContent'

View File

@@ -1,10 +1,11 @@
import { PayloadSource } from './../../Abstract/Payload/Types/PayloadSource'
import { DecryptedPayload } from './../../Abstract/Payload/Implementations/DecryptedPayload'
import { SNTag, TagContent } from './Tag'
import { SNTag } from './Tag'
import { ContentType } from '@standardnotes/common'
import { FillItemContent } from '../../Abstract/Content/ItemContent'
import { ContentReference } from '../../Abstract/Reference/ContentReference'
import { PayloadTimestampDefaults } from '../../Abstract/Payload'
import { TagContent } from './TagContent'
const randUuid = () => String(Math.random())

View File

@@ -1,32 +1,28 @@
import { VectorIconNameOrEmoji, IconType } from './../../Utilities/Icon/IconType'
import { ContentType, Uuid } from '@standardnotes/common'
import { DecryptedItem } from '../../Abstract/Item/Implementations/DecryptedItem'
import { ItemInterface } from '../../Abstract/Item/Interfaces/ItemInterface'
import { ItemContent } from '../../Abstract/Content/ItemContent'
import { ContentReference } from '../../Abstract/Reference/ContentReference'
import { isTagToParentTagReference } from '../../Abstract/Reference/Functions'
import { DecryptedPayloadInterface } from '../../Abstract/Payload/Interfaces/DecryptedPayload'
import { TagContent, TagContentSpecialized } from './TagContent'
export const TagFolderDelimitter = '.'
interface TagInterface {
title: string
expanded: boolean
}
export type TagContent = TagInterface & ItemContent
export const DefaultTagIconName: IconType = 'hashtag'
export const isTag = (x: ItemInterface): x is SNTag => x.content_type === ContentType.Tag
export class SNTag extends DecryptedItem<TagContent> implements TagInterface {
export class SNTag extends DecryptedItem<TagContent> implements TagContentSpecialized {
public readonly title: string
/** Whether to render child tags in view hierarchy. Opposite of collapsed. */
public readonly iconString: VectorIconNameOrEmoji
public readonly expanded: boolean
constructor(payload: DecryptedPayloadInterface<TagContent>) {
super(payload)
this.title = this.payload.content.title || ''
this.expanded = this.payload.content.expanded != undefined ? this.payload.content.expanded : true
this.iconString = this.payload.content.iconString || DefaultTagIconName
}
get noteReferences(): ContentReference[] {

View File

@@ -0,0 +1,11 @@
import { IconType } from './../../Utilities/Icon/IconType'
import { ItemContent } from '../../Abstract/Content/ItemContent'
import { EmojiString } from '../../Utilities/Icon/IconType'
export interface TagContentSpecialized {
title: string
expanded: boolean
iconString: IconType | EmojiString
}
export type TagContent = TagContentSpecialized & ItemContent

View File

@@ -1,5 +1,6 @@
import { ContentType } from '@standardnotes/common'
import { TagContent, SNTag } from './Tag'
import { SNTag } from './Tag'
import { TagContent } from './TagContent'
import { FileItem } from '../File'
import { SNNote } from '../Note'
import { isTagToParentTagReference } from '../../Abstract/Reference/Functions'
@@ -17,6 +18,10 @@ export class TagMutator extends DecryptedItemMutator<TagContent> {
this.mutableContent.expanded = expanded
}
set iconString(iconString: string) {
this.mutableContent.iconString = iconString
}
public makeChildOf(tag: SNTag): void {
const references = this.immutableItem.references.filter((ref) => !isTagToParentTagReference(ref))

View File

@@ -1,2 +1,3 @@
export * from './Tag'
export * from './TagMutator'
export * from './TagContent'

View File

@@ -0,0 +1,190 @@
export type VectorIconNameOrEmoji = EmojiString | IconType
export type EmojiString = string
export type IconType =
| 'accessibility'
| 'account-card-details-outline'
| 'account-circle'
| 'add-bold'
| 'add-text'
| 'add'
| 'archive'
| 'arrow-down'
| 'arrow-left'
| 'arrow-right'
| 'arrow-up'
| 'arrows-horizontal'
| 'arrows-sort-down'
| 'arrows-sort-up'
| 'asterisk'
| 'attachment-file'
| 'authenticator-variant'
| 'authenticator'
| 'back-ios'
| 'bold'
| 'box-filled'
| 'box'
| 'camera'
| 'check-all'
| 'check-bold'
| 'check-circle-filled'
| 'check-circle'
| 'check'
| 'chevron-down'
| 'chevron-left'
| 'chevron-right'
| 'chevron-up'
| 'clear-circle-filled'
| 'close'
| 'cloud-off'
| 'code-2'
| 'code-tags'
| 'code'
| 'color-fill'
| 'copy'
| 'dashboard'
| 'diamond-filled'
| 'diamond'
| 'download'
| 'drag'
| 'draw'
| 'editor-filled'
| 'editor'
| 'email-filled'
| 'email'
| 'enter'
| 'eye-filled'
| 'eye-off-filled'
| 'eye-off'
| 'eye'
| 'feedback'
| 'file-filled'
| 'file'
| 'file-other'
| 'file-pdf'
| 'file-doc'
| 'file-ppt'
| 'file-xls'
| 'file-image'
| 'file-mov'
| 'file-music'
| 'file-zip'
| 'files-illustration'
| 'folder-filled'
| 'folder-key-filled'
| 'folder'
| 'format-align-center'
| 'format-align-justify'
| 'format-align-left'
| 'format-align-right'
| 'forward-ios'
| 'fullscreen-exit'
| 'fullscreen'
| 'gift-outline'
| 'hashtag-filled'
| 'hashtag-off'
| 'hashtag'
| 'heart-filled'
| 'help-filled'
| 'help'
| 'history'
| 'image'
| 'info'
| 'italic'
| 'keyboard-close'
| 'keyboard-command'
| 'keyboard-filled'
| 'keyboard-option'
| 'keyboard-shift'
| 'keyboard-show'
| 'keyboard'
| 'lifebuoy'
| 'line-width'
| 'link-off'
| 'link'
| 'list-bulleted'
| 'listed-filled'
| 'listed'
| 'lock-filled'
| 'lock'
| 'markdown'
| 'menu-arrow-down-alt'
| 'menu-arrow-down'
| 'menu-arrow-right'
| 'menu-close'
| 'menu-open'
| 'menu-variant'
| 'merge'
| 'more-vert'
| 'more'
| 'notes-filled'
| 'notes-remove'
| 'notes'
| 'open-in'
| 'password'
| 'pencil-filled'
| 'pencil-off'
| 'pencil'
| 'pin-filled'
| 'pin-off'
| 'pin'
| 'plain-text'
| 'plus-circle-filled'
| 'plus-circle'
| 'premium-feature'
| 'print'
| 'redo'
| 'restore'
| 'rich-text'
| 'safe-square-filled'
| 'safe-square'
| 'safe'
| 'save'
| 'search-ios'
| 'search'
| 'security'
| 'select-all'
| 'send'
| 'server'
| 'settings-filled'
| 'settings'
| 'share'
| 'signIn'
| 'signOut'
| 'sort-descending'
| 'spellcheck'
| 'spreadsheets'
| 'standard-notes-2'
| 'standard-notes'
| 'star-circle-filled'
| 'star-filled'
| 'star-variant-filled'
| 'star'
| 'strikethrough'
| 'sync'
| 'tasks'
| 'text-circle'
| 'text-paragraph-long'
| 'text'
| 'textbox-password'
| 'themes-filled'
| 'themes'
| 'timer'
| 'trash-filled'
| 'trash-sweep-filled'
| 'trash-sweep'
| 'trash'
| 'tune'
| 'unarchive'
| 'underline'
| 'undo'
| 'unpin'
| 'upload'
| 'user-add'
| 'user-filled'
| 'user-switch'
| 'user'
| 'view'
| 'warning'
| 'window'

View File

@@ -1,4 +1,4 @@
import { TagContent } from './../../Syncable/Tag/Tag'
import { TagContent } from './../../Syncable/Tag/TagContent'
import { ContentType } from '@standardnotes/common'
import { FillItemContent, ItemContent } from '../../Abstract/Content/ItemContent'
import { DecryptedPayload, PayloadSource, PayloadTimestampDefaults } from '../../Abstract/Payload'

View File

@@ -1,7 +1,7 @@
export * from './Abstract/Component/ActionObserver'
export * from './Abstract/Component/ComponentViewerEvent'
export * from './Abstract/Component/ComponentMessage'
export * from './Abstract/Component/ComponentEventObserver'
export * from './Abstract/Component/ComponentMessage'
export * from './Abstract/Component/ComponentViewerEvent'
export * from './Abstract/Component/IncomingComponentItemPayload'
export * from './Abstract/Component/KeyboardModifier'
export * from './Abstract/Component/MessageData'
@@ -43,9 +43,9 @@ export * from './Runtime/Collection/Payload/ImmutablePayloadCollection'
export * from './Runtime/Collection/Payload/PayloadCollection'
export * from './Runtime/Deltas'
export * from './Runtime/DirtyCounter/DirtyCounter'
export * from './Runtime/Display'
export * from './Runtime/Display/ItemDisplayController'
export * from './Runtime/Display/Types'
export * from './Runtime/Display'
export * from './Runtime/History'
export * from './Runtime/Index/ItemDelta'
export * from './Runtime/Index/SNIndex'
@@ -70,6 +70,7 @@ export * from './Syncable/SmartView'
export * from './Syncable/Tag'
export * from './Syncable/Theme'
export * from './Syncable/UserPrefs'
export * from './Utilities/Icon/IconType'
export * from './Utilities/Item/FindItem'
export * from './Utilities/Item/ItemContentsDiffer'
export * from './Utilities/Item/ItemContentsEqual'