feat: ability to favorite tags (#1852)
This commit is contained in:
@@ -68,6 +68,7 @@ export const ICONS = {
|
|||||||
eye: icons.EyeIcon,
|
eye: icons.EyeIcon,
|
||||||
file: icons.FileIcon,
|
file: icons.FileIcon,
|
||||||
folder: icons.FolderIcon,
|
folder: icons.FolderIcon,
|
||||||
|
fullscreen: icons.FullscreenIcon,
|
||||||
hashtag: icons.HashtagIcon,
|
hashtag: icons.HashtagIcon,
|
||||||
help: icons.HelpIcon,
|
help: icons.HelpIcon,
|
||||||
history: icons.HistoryIcon,
|
history: icons.HistoryIcon,
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ const smartViewIconType = (view: SmartView, isSelected: boolean): IconType => {
|
|||||||
[SystemViewId.StarredNotes]: 'star-filled',
|
[SystemViewId.StarredNotes]: 'star-filled',
|
||||||
}
|
}
|
||||||
|
|
||||||
return mapping[view.uuid as SystemViewId] || 'hashtag'
|
return mapping[view.uuid as SystemViewId] || 'window'
|
||||||
}
|
}
|
||||||
|
|
||||||
const getIconClass = (view: SmartView, isSelected: boolean): string => {
|
const getIconClass = (view: SmartView, isSelected: boolean): string => {
|
||||||
|
|||||||
@@ -46,6 +46,11 @@ const TagContextMenu = ({ navigationController, isEntitledToFolders, selectedTag
|
|||||||
navigationController.remove(selectedTag, true).catch(console.error)
|
navigationController.remove(selectedTag, true).catch(console.error)
|
||||||
}, [navigationController, selectedTag])
|
}, [navigationController, selectedTag])
|
||||||
|
|
||||||
|
const onClickStar = useCallback(() => {
|
||||||
|
navigationController.setFavorite(selectedTag, !selectedTag.starred).catch(console.error)
|
||||||
|
navigationController.setContextMenuOpen(false)
|
||||||
|
}, [navigationController, selectedTag])
|
||||||
|
|
||||||
const tagLastModified = useMemo(
|
const tagLastModified = useMemo(
|
||||||
() => formatDateForContextMenu(selectedTag.userModifiedDate),
|
() => formatDateForContextMenu(selectedTag.userModifiedDate),
|
||||||
[selectedTag.userModifiedDate],
|
[selectedTag.userModifiedDate],
|
||||||
@@ -62,6 +67,12 @@ const TagContextMenu = ({ navigationController, isEntitledToFolders, selectedTag
|
|||||||
>
|
>
|
||||||
<div ref={contextMenuRef}>
|
<div ref={contextMenuRef}>
|
||||||
<Menu a11yLabel="Tag context menu" isOpen={contextMenuOpen}>
|
<Menu a11yLabel="Tag context menu" isOpen={contextMenuOpen}>
|
||||||
|
<MenuItem type={MenuItemType.IconButton} className={'justify-between py-1.5'} onClick={onClickStar}>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<Icon type="star" className="mr-2 text-neutral" />
|
||||||
|
{selectedTag.starred ? 'Unfavorite' : 'Favorite'}
|
||||||
|
</div>
|
||||||
|
</MenuItem>
|
||||||
<MenuItem type={MenuItemType.IconButton} className={'justify-between py-1.5'} onClick={onClickAddSubtag}>
|
<MenuItem type={MenuItemType.IconButton} className={'justify-between py-1.5'} onClick={onClickAddSubtag}>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Icon type="add" className="mr-2 text-neutral" />
|
<Icon type="add" className="mr-2 text-neutral" />
|
||||||
|
|||||||
@@ -9,11 +9,12 @@ import { TagsListItem } from './TagsListItem'
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
viewControllerManager: ViewControllerManager
|
viewControllerManager: ViewControllerManager
|
||||||
|
type: 'all' | 'favorites'
|
||||||
}
|
}
|
||||||
|
|
||||||
const TagsList: FunctionComponent<Props> = ({ viewControllerManager }: Props) => {
|
const TagsList: FunctionComponent<Props> = ({ viewControllerManager, type }: Props) => {
|
||||||
const tagsState = viewControllerManager.navigationController
|
const navigationController = viewControllerManager.navigationController
|
||||||
const allTags = tagsState.allLocalRootTags
|
const allTags = type === 'all' ? navigationController.allLocalRootTags : navigationController.starredTags
|
||||||
|
|
||||||
const backend = HTML5Backend
|
const backend = HTML5Backend
|
||||||
|
|
||||||
@@ -49,17 +50,20 @@ const TagsList: FunctionComponent<Props> = ({ viewControllerManager }: Props) =>
|
|||||||
level={0}
|
level={0}
|
||||||
key={tag.uuid}
|
key={tag.uuid}
|
||||||
tag={tag}
|
tag={tag}
|
||||||
tagsState={tagsState}
|
type={type}
|
||||||
|
tagsState={navigationController}
|
||||||
features={viewControllerManager.featuresController}
|
features={viewControllerManager.featuresController}
|
||||||
linkingController={viewControllerManager.linkingController}
|
linkingController={viewControllerManager.linkingController}
|
||||||
onContextMenu={onContextMenu}
|
onContextMenu={onContextMenu}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
<RootTagDropZone
|
{type === 'all' && (
|
||||||
tagsState={viewControllerManager.navigationController}
|
<RootTagDropZone
|
||||||
featuresState={viewControllerManager.featuresController}
|
tagsState={viewControllerManager.navigationController}
|
||||||
/>
|
featuresState={viewControllerManager.featuresController}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</DndProvider>
|
</DndProvider>
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import { LinkingController } from '@/Controllers/LinkingController'
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
tag: SNTag
|
tag: SNTag
|
||||||
|
type: 'all' | 'favorites'
|
||||||
tagsState: NavigationController
|
tagsState: NavigationController
|
||||||
features: FeaturesController
|
features: FeaturesController
|
||||||
linkingController: LinkingController
|
linkingController: LinkingController
|
||||||
@@ -40,7 +41,7 @@ const PADDING_BASE_PX = 14
|
|||||||
const PADDING_PER_LEVEL_PX = 21
|
const PADDING_PER_LEVEL_PX = 21
|
||||||
|
|
||||||
export const TagsListItem: FunctionComponent<Props> = observer(
|
export const TagsListItem: FunctionComponent<Props> = observer(
|
||||||
({ tag, features, tagsState, level, onContextMenu, linkingController }) => {
|
({ tag, type, features, tagsState, level, onContextMenu, linkingController }) => {
|
||||||
const { toggleAppPane } = useResponsiveAppPane()
|
const { toggleAppPane } = useResponsiveAppPane()
|
||||||
|
|
||||||
const [title, setTitle] = useState(tag.title || '')
|
const [title, setTitle] = useState(tag.title || '')
|
||||||
@@ -263,7 +264,18 @@ export const TagsListItem: FunctionComponent<Props> = observer(
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className={'tag-icon draggable mr-2'} ref={dragRef}>
|
<div className={'tag-icon draggable mr-2'} ref={dragRef}>
|
||||||
<Icon type="hashtag" className={`${isSelected ? 'text-info' : 'text-neutral'}`} />
|
<Icon
|
||||||
|
type="hashtag"
|
||||||
|
className={`${
|
||||||
|
type === 'favorites'
|
||||||
|
? isSelected
|
||||||
|
? 'text-warning'
|
||||||
|
: 'text-info'
|
||||||
|
: isSelected
|
||||||
|
? 'text-info'
|
||||||
|
: 'text-neutral'
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{isEditing ? (
|
{isEditing ? (
|
||||||
<input
|
<input
|
||||||
@@ -335,6 +347,7 @@ export const TagsListItem: FunctionComponent<Props> = observer(
|
|||||||
level={level + 1}
|
level={level + 1}
|
||||||
key={tag.uuid}
|
key={tag.uuid}
|
||||||
tag={tag}
|
tag={tag}
|
||||||
|
type={type}
|
||||||
tagsState={tagsState}
|
tagsState={tagsState}
|
||||||
features={features}
|
features={features}
|
||||||
linkingController={linkingController}
|
linkingController={linkingController}
|
||||||
|
|||||||
@@ -52,22 +52,37 @@ const TagsSection: FunctionComponent<Props> = ({ viewControllerManager }) => {
|
|||||||
}, [viewControllerManager, checkIfMigrationNeeded])
|
}, [viewControllerManager, checkIfMigrationNeeded])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section>
|
<>
|
||||||
<div className={'section-title-bar'}>
|
{viewControllerManager.navigationController.starredTags.length > 0 && (
|
||||||
<div className="section-title-bar-header">
|
<section>
|
||||||
<TagsSectionTitle
|
<div className={'section-title-bar'}>
|
||||||
features={viewControllerManager.featuresController}
|
<div className="section-title-bar-header">
|
||||||
hasMigration={hasMigration}
|
<div className="title text-sm">
|
||||||
onClickMigration={runMigration}
|
<span className="font-bold">Favorites</span>
|
||||||
/>
|
</div>
|
||||||
<TagsSectionAddButton
|
</div>
|
||||||
tags={viewControllerManager.navigationController}
|
</div>
|
||||||
features={viewControllerManager.featuresController}
|
<TagsList type="favorites" viewControllerManager={viewControllerManager} />
|
||||||
/>
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<div className={'section-title-bar'}>
|
||||||
|
<div className="section-title-bar-header">
|
||||||
|
<TagsSectionTitle
|
||||||
|
features={viewControllerManager.featuresController}
|
||||||
|
hasMigration={hasMigration}
|
||||||
|
onClickMigration={runMigration}
|
||||||
|
/>
|
||||||
|
<TagsSectionAddButton
|
||||||
|
tags={viewControllerManager.navigationController}
|
||||||
|
features={viewControllerManager.featuresController}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<TagsList type="all" viewControllerManager={viewControllerManager} />
|
||||||
<TagsList viewControllerManager={viewControllerManager} />
|
</section>
|
||||||
</section>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ export class NavigationController
|
|||||||
{
|
{
|
||||||
tags: SNTag[] = []
|
tags: SNTag[] = []
|
||||||
smartViews: SmartView[] = []
|
smartViews: SmartView[] = []
|
||||||
|
starredTags: SNTag[] = []
|
||||||
allNotesCount_ = 0
|
allNotesCount_ = 0
|
||||||
selectedUuid: AnyTag['uuid'] | undefined = undefined
|
selectedUuid: AnyTag['uuid'] | undefined = undefined
|
||||||
selected_: AnyTag | undefined
|
selected_: AnyTag | undefined
|
||||||
@@ -66,6 +67,7 @@ export class NavigationController
|
|||||||
|
|
||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
tags: observable,
|
tags: observable,
|
||||||
|
starredTags: observable,
|
||||||
smartViews: observable.ref,
|
smartViews: observable.ref,
|
||||||
hasAtLeastOneFolder: computed,
|
hasAtLeastOneFolder: computed,
|
||||||
allNotesCount_: observable,
|
allNotesCount_: observable,
|
||||||
@@ -111,7 +113,7 @@ export class NavigationController
|
|||||||
this.application.streamItems([ContentType.Tag, ContentType.SmartView], ({ changed, removed }) => {
|
this.application.streamItems([ContentType.Tag, ContentType.SmartView], ({ changed, removed }) => {
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.tags = this.application.items.getDisplayableTags()
|
this.tags = this.application.items.getDisplayableTags()
|
||||||
|
this.starredTags = this.tags.filter((tag) => tag.starred)
|
||||||
this.smartViews = this.application.items.getSmartViews()
|
this.smartViews = this.application.items.getSmartViews()
|
||||||
|
|
||||||
const currentSelectedTag = this.selected_
|
const currentSelectedTag = this.selected_
|
||||||
@@ -476,6 +478,14 @@ export class NavigationController
|
|||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async setFavorite(tag: SNTag, favorite: boolean) {
|
||||||
|
return this.application.mutator
|
||||||
|
.changeAndSaveItem<TagMutator>(tag, (mutator) => {
|
||||||
|
mutator.starred = favorite
|
||||||
|
})
|
||||||
|
.catch(console.error)
|
||||||
|
}
|
||||||
|
|
||||||
public get editingTag(): SNTag | SmartView | undefined {
|
public get editingTag(): SNTag | SmartView | undefined {
|
||||||
return this.editing_
|
return this.editing_
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user