feat: add tag id to tag context menu (#1185)
This commit is contained in:
@@ -12,7 +12,7 @@ import MenuItemSeparator from '@/Components/Menu/MenuItemSeparator'
|
||||
import { MenuItemType } from '@/Components/Menu/MenuItemType'
|
||||
import WorkspaceSwitcherOption from './WorkspaceSwitcher/WorkspaceSwitcherOption'
|
||||
import { ApplicationGroup } from '@/Application/ApplicationGroup'
|
||||
import { formatLastSyncDate } from '@/Utils/FormatLastSyncDate'
|
||||
import { formatLastSyncDate } from '@/Utils/DateUtils'
|
||||
import Spinner from '@/Components/Spinner/Spinner'
|
||||
|
||||
type Props = {
|
||||
|
||||
@@ -17,13 +17,13 @@ import { FunctionComponent, useCallback, useEffect, useMemo, useState } from 're
|
||||
import RevisionHistoryModal from '@/Components/RevisionHistoryModal/RevisionHistoryModal'
|
||||
import PremiumModalProvider from '@/Hooks/usePremiumModal'
|
||||
import ConfirmSignoutContainer from '@/Components/ConfirmSignoutModal/ConfirmSignoutModal'
|
||||
import TagsContextMenuWrapper from '@/Components/Tags/TagContextMenu'
|
||||
import { ToastContainer } from '@standardnotes/toast'
|
||||
import FilePreviewModalWrapper from '@/Components/FilePreview/FilePreviewModal'
|
||||
import ContentListView from '@/Components/ContentListView/ContentListView'
|
||||
import FileContextMenuWrapper from '@/Components/FileContextMenu/FileContextMenu'
|
||||
import PermissionsModalWrapper from '@/Components/PermissionsModal/PermissionsModalWrapper'
|
||||
import { PanelResizedData } from '@/Types/PanelResizedData'
|
||||
import TagContextMenuWrapper from '@/Components/Tags/TagContextMenuWrapper'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
@@ -214,7 +214,10 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
|
||||
noteTagsController={viewControllerManager.noteTagsController}
|
||||
historyModalController={viewControllerManager.historyModalController}
|
||||
/>
|
||||
<TagsContextMenuWrapper viewControllerManager={viewControllerManager} />
|
||||
<TagContextMenuWrapper
|
||||
navigationController={viewControllerManager.navigationController}
|
||||
featuresController={viewControllerManager.featuresController}
|
||||
/>
|
||||
<FileContextMenuWrapper
|
||||
filesController={viewControllerManager.filesController}
|
||||
selectionController={viewControllerManager.selectionController}
|
||||
|
||||
@@ -86,7 +86,7 @@ const FileContextMenu: FunctionComponent<Props> = observer(({ filesController, s
|
||||
return (
|
||||
<div
|
||||
ref={contextMenuRef}
|
||||
className="max-h-120 fixed flex min-w-60 max-w-xs flex-col overflow-y-auto rounded bg-default py-2 shadow-main"
|
||||
className="max-h-120 fixed z-dropdown-menu flex min-w-60 max-w-xs flex-col overflow-y-auto rounded bg-default py-2 shadow-main"
|
||||
style={{
|
||||
...contextMenuStyle,
|
||||
maxHeight: contextMenuMaxHeight,
|
||||
|
||||
@@ -12,6 +12,7 @@ import { addToast, dismissToast, ToastType } from '@standardnotes/toast'
|
||||
import { NotesOptionsProps } from './NotesOptionsProps'
|
||||
import { NotesController } from '@/Controllers/NotesController'
|
||||
import HorizontalSeparator from '../Shared/HorizontalSeparator'
|
||||
import { formatDateForContextMenu } from '@/Utils/DateUtils'
|
||||
|
||||
type DeletePermanentlyButtonProps = {
|
||||
closeOnBlur: NotesOptionsProps['closeOnBlur']
|
||||
@@ -78,13 +79,6 @@ const calculateReadTime = (words: number) => {
|
||||
}
|
||||
}
|
||||
|
||||
const formatDate = (date: Date | undefined) => {
|
||||
if (!date) {
|
||||
return
|
||||
}
|
||||
return `${date.toDateString()} ${date.toLocaleTimeString()}`
|
||||
}
|
||||
|
||||
const NoteAttributes: FunctionComponent<{
|
||||
application: SNApplication
|
||||
note: SNNote
|
||||
@@ -93,9 +87,9 @@ const NoteAttributes: FunctionComponent<{
|
||||
|
||||
const readTime = useMemo(() => (typeof words === 'number' ? calculateReadTime(words) : 'N/A'), [words])
|
||||
|
||||
const dateLastModified = useMemo(() => formatDate(note.userModifiedDate), [note.userModifiedDate])
|
||||
const dateLastModified = useMemo(() => formatDateForContextMenu(note.userModifiedDate), [note.userModifiedDate])
|
||||
|
||||
const dateCreated = useMemo(() => formatDate(note.created_at), [note.created_at])
|
||||
const dateCreated = useMemo(() => formatDateForContextMenu(note.created_at), [note.created_at])
|
||||
|
||||
const editor = application.componentManager.editorForNote(note)
|
||||
const format = editor?.package_info?.file_type || 'txt'
|
||||
|
||||
@@ -5,7 +5,7 @@ import { STRING_GENERIC_SYNC_ERROR } from '@/Constants/Strings'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { FunctionComponent, useState } from 'react'
|
||||
import { formatLastSyncDate } from '@/Utils/FormatLastSyncDate'
|
||||
import { formatLastSyncDate } from '@/Utils/DateUtils'
|
||||
import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup'
|
||||
import PreferencesSegment from '../../PreferencesComponents/PreferencesSegment'
|
||||
|
||||
|
||||
@@ -1,36 +1,33 @@
|
||||
import { ViewControllerManager } from '@/Services/ViewControllerManager'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { useCallback, useEffect, useRef } from 'react'
|
||||
import { useCallback, useEffect, useRef, useMemo } from 'react'
|
||||
import Icon from '@/Components/Icon/Icon'
|
||||
import Menu from '@/Components/Menu/Menu'
|
||||
import MenuItem from '@/Components/Menu/MenuItem'
|
||||
import { MenuItemType } from '@/Components/Menu/MenuItemType'
|
||||
import { usePremiumModal } from '@/Hooks/usePremiumModal'
|
||||
import { useCloseOnBlur } from '@/Hooks/useCloseOnBlur'
|
||||
import { SNTag } from '@standardnotes/snjs'
|
||||
import { isControllerDealloced } from '@/Controllers/Abstract/IsControllerDealloced'
|
||||
import { useCloseOnClickOutside } from '@/Hooks/useCloseOnClickOutside'
|
||||
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
||||
import HorizontalSeparator from '../Shared/HorizontalSeparator'
|
||||
import { formatDateForContextMenu } from '@/Utils/DateUtils'
|
||||
|
||||
type WrapperProps = {
|
||||
viewControllerManager: ViewControllerManager
|
||||
}
|
||||
|
||||
type ContextMenuProps = WrapperProps & {
|
||||
type ContextMenuProps = {
|
||||
navigationController: NavigationController
|
||||
isEntitledToFolders: boolean
|
||||
selectedTag: SNTag
|
||||
}
|
||||
|
||||
const TagsContextMenu = observer(({ viewControllerManager, selectedTag }: ContextMenuProps) => {
|
||||
const TagContextMenu = ({ navigationController, isEntitledToFolders, selectedTag }: ContextMenuProps) => {
|
||||
const premiumModal = usePremiumModal()
|
||||
|
||||
const { contextMenuOpen, contextMenuPosition, contextMenuMaxHeight } = viewControllerManager.navigationController
|
||||
const { contextMenuOpen, contextMenuPosition, contextMenuMaxHeight } = navigationController
|
||||
|
||||
const contextMenuRef = useRef<HTMLDivElement>(null)
|
||||
const [closeOnBlur] = useCloseOnBlur(contextMenuRef, (open: boolean) =>
|
||||
viewControllerManager.navigationController.setContextMenuOpen(open),
|
||||
)
|
||||
useCloseOnClickOutside(contextMenuRef, () => navigationController.setContextMenuOpen(false))
|
||||
|
||||
const reloadContextMenuLayout = useCallback(() => {
|
||||
viewControllerManager.navigationController.reloadContextMenuLayout()
|
||||
}, [viewControllerManager])
|
||||
navigationController.reloadContextMenuLayout()
|
||||
}, [navigationController])
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('resize', reloadContextMenuLayout)
|
||||
@@ -40,28 +37,35 @@ const TagsContextMenu = observer(({ viewControllerManager, selectedTag }: Contex
|
||||
}, [reloadContextMenuLayout])
|
||||
|
||||
const onClickAddSubtag = useCallback(() => {
|
||||
if (!viewControllerManager.featuresController.hasFolders) {
|
||||
if (!isEntitledToFolders) {
|
||||
premiumModal.activate('Folders')
|
||||
return
|
||||
}
|
||||
|
||||
viewControllerManager.navigationController.setContextMenuOpen(false)
|
||||
viewControllerManager.navigationController.setAddingSubtagTo(selectedTag)
|
||||
}, [viewControllerManager, selectedTag, premiumModal])
|
||||
navigationController.setContextMenuOpen(false)
|
||||
navigationController.setAddingSubtagTo(selectedTag)
|
||||
}, [isEntitledToFolders, navigationController, selectedTag, premiumModal])
|
||||
|
||||
const onClickRename = useCallback(() => {
|
||||
viewControllerManager.navigationController.setContextMenuOpen(false)
|
||||
viewControllerManager.navigationController.editingTag = selectedTag
|
||||
}, [viewControllerManager, selectedTag])
|
||||
navigationController.setContextMenuOpen(false)
|
||||
navigationController.editingTag = selectedTag
|
||||
}, [navigationController, selectedTag])
|
||||
|
||||
const onClickDelete = useCallback(() => {
|
||||
viewControllerManager.navigationController.remove(selectedTag, true).catch(console.error)
|
||||
}, [viewControllerManager, selectedTag])
|
||||
navigationController.remove(selectedTag, true).catch(console.error)
|
||||
}, [navigationController, selectedTag])
|
||||
|
||||
const tagLastModified = useMemo(
|
||||
() => formatDateForContextMenu(selectedTag.userModifiedDate),
|
||||
[selectedTag.userModifiedDate],
|
||||
)
|
||||
|
||||
const tagCreatedAt = useMemo(() => formatDateForContextMenu(selectedTag.created_at), [selectedTag.created_at])
|
||||
|
||||
return contextMenuOpen ? (
|
||||
<div
|
||||
ref={contextMenuRef}
|
||||
className="max-h-120 fixed flex min-w-60 max-w-xs flex-col overflow-y-auto rounded bg-default py-2 shadow-main"
|
||||
className="max-h-120 fixed z-dropdown-menu flex min-w-60 flex-col overflow-y-auto rounded bg-default py-2 shadow-main"
|
||||
style={{
|
||||
...contextMenuPosition,
|
||||
maxHeight: contextMenuMaxHeight,
|
||||
@@ -71,48 +75,39 @@ const TagsContextMenu = observer(({ viewControllerManager, selectedTag }: Contex
|
||||
a11yLabel="Tag context menu"
|
||||
isOpen={contextMenuOpen}
|
||||
closeMenu={() => {
|
||||
viewControllerManager.navigationController.setContextMenuOpen(false)
|
||||
navigationController.setContextMenuOpen(false)
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
type={MenuItemType.IconButton}
|
||||
onBlur={closeOnBlur}
|
||||
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">
|
||||
<Icon type="add" className="mr-2 text-neutral" />
|
||||
Add subtag
|
||||
</div>
|
||||
{!viewControllerManager.featuresController.hasFolders && <Icon type="premium-feature" />}
|
||||
{!isEntitledToFolders && <Icon type="premium-feature" />}
|
||||
</MenuItem>
|
||||
<MenuItem type={MenuItemType.IconButton} onBlur={closeOnBlur} className={'py-1.5'} onClick={onClickRename}>
|
||||
<MenuItem type={MenuItemType.IconButton} className={'py-1.5'} onClick={onClickRename}>
|
||||
<Icon type="pencil-filled" className="mr-2 text-neutral" />
|
||||
Rename
|
||||
</MenuItem>
|
||||
<MenuItem type={MenuItemType.IconButton} onBlur={closeOnBlur} className={'py-1.5'} onClick={onClickDelete}>
|
||||
<MenuItem type={MenuItemType.IconButton} className={'py-1.5'} onClick={onClickDelete}>
|
||||
<Icon type="trash" className="mr-2 text-danger" />
|
||||
<span className="text-danger">Delete</span>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
<HorizontalSeparator classes="my-2" />
|
||||
<div className="px-3 pt-1 pb-1.5 text-xs font-medium text-neutral">
|
||||
<div className="mb-1">
|
||||
<span className="font-semibold">Last modified:</span> {tagLastModified}
|
||||
</div>
|
||||
<div className="mb-1">
|
||||
<span className="font-semibold">Created:</span> {tagCreatedAt}
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-semibold">Tag ID:</span> {selectedTag.uuid}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null
|
||||
})
|
||||
|
||||
TagsContextMenu.displayName = 'TagsContextMenu'
|
||||
|
||||
const TagsContextMenuWrapper = ({ viewControllerManager }: WrapperProps) => {
|
||||
if (isControllerDealloced(viewControllerManager)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const selectedTag = viewControllerManager.navigationController.selected
|
||||
|
||||
if (!selectedTag || !(selectedTag instanceof SNTag)) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <TagsContextMenu viewControllerManager={viewControllerManager} selectedTag={selectedTag} />
|
||||
}
|
||||
|
||||
export default observer(TagsContextMenuWrapper)
|
||||
export default observer(TagContextMenu)
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { SNTag } from '@standardnotes/snjs'
|
||||
import TagContextMenu from './TagContextMenu'
|
||||
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
||||
import { FeaturesController } from '@/Controllers/FeaturesController'
|
||||
|
||||
type Props = {
|
||||
navigationController: NavigationController
|
||||
featuresController: FeaturesController
|
||||
}
|
||||
|
||||
const TagContextMenuWrapper = ({ navigationController, featuresController }: Props) => {
|
||||
const selectedTag = navigationController.selected
|
||||
|
||||
if (!selectedTag || !(selectedTag instanceof SNTag)) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<TagContextMenu
|
||||
navigationController={navigationController}
|
||||
isEntitledToFolders={featuresController.hasFolders}
|
||||
selectedTag={selectedTag}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default observer(TagContextMenuWrapper)
|
||||
@@ -3,3 +3,11 @@ import { dateToLocalizedString } from '@standardnotes/snjs/'
|
||||
export const formatLastSyncDate = (lastUpdatedDate: Date) => {
|
||||
return dateToLocalizedString(lastUpdatedDate)
|
||||
}
|
||||
|
||||
export const formatDateForContextMenu = (date: Date | undefined) => {
|
||||
if (!date) {
|
||||
return
|
||||
}
|
||||
|
||||
return `${date.toDateString()} ${date.toLocaleTimeString()}`
|
||||
}
|
||||
Reference in New Issue
Block a user