feat: add tag id to tag context menu (#1185)

This commit is contained in:
Aman Harwara
2022-06-30 21:57:33 +05:30
committed by GitHub
parent 604520f1ae
commit 8b845ea424
8 changed files with 95 additions and 67 deletions

View File

@@ -12,7 +12,7 @@ import MenuItemSeparator from '@/Components/Menu/MenuItemSeparator'
import { MenuItemType } from '@/Components/Menu/MenuItemType' import { MenuItemType } from '@/Components/Menu/MenuItemType'
import WorkspaceSwitcherOption from './WorkspaceSwitcher/WorkspaceSwitcherOption' import WorkspaceSwitcherOption from './WorkspaceSwitcher/WorkspaceSwitcherOption'
import { ApplicationGroup } from '@/Application/ApplicationGroup' import { ApplicationGroup } from '@/Application/ApplicationGroup'
import { formatLastSyncDate } from '@/Utils/FormatLastSyncDate' import { formatLastSyncDate } from '@/Utils/DateUtils'
import Spinner from '@/Components/Spinner/Spinner' import Spinner from '@/Components/Spinner/Spinner'
type Props = { type Props = {

View File

@@ -17,13 +17,13 @@ import { FunctionComponent, useCallback, useEffect, useMemo, useState } from 're
import RevisionHistoryModal from '@/Components/RevisionHistoryModal/RevisionHistoryModal' import RevisionHistoryModal from '@/Components/RevisionHistoryModal/RevisionHistoryModal'
import PremiumModalProvider from '@/Hooks/usePremiumModal' import PremiumModalProvider from '@/Hooks/usePremiumModal'
import ConfirmSignoutContainer from '@/Components/ConfirmSignoutModal/ConfirmSignoutModal' import ConfirmSignoutContainer from '@/Components/ConfirmSignoutModal/ConfirmSignoutModal'
import TagsContextMenuWrapper from '@/Components/Tags/TagContextMenu'
import { ToastContainer } from '@standardnotes/toast' import { ToastContainer } from '@standardnotes/toast'
import FilePreviewModalWrapper from '@/Components/FilePreview/FilePreviewModal' import FilePreviewModalWrapper from '@/Components/FilePreview/FilePreviewModal'
import ContentListView from '@/Components/ContentListView/ContentListView' import ContentListView from '@/Components/ContentListView/ContentListView'
import FileContextMenuWrapper from '@/Components/FileContextMenu/FileContextMenu' import FileContextMenuWrapper from '@/Components/FileContextMenu/FileContextMenu'
import PermissionsModalWrapper from '@/Components/PermissionsModal/PermissionsModalWrapper' import PermissionsModalWrapper from '@/Components/PermissionsModal/PermissionsModalWrapper'
import { PanelResizedData } from '@/Types/PanelResizedData' import { PanelResizedData } from '@/Types/PanelResizedData'
import TagContextMenuWrapper from '@/Components/Tags/TagContextMenuWrapper'
type Props = { type Props = {
application: WebApplication application: WebApplication
@@ -214,7 +214,10 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
noteTagsController={viewControllerManager.noteTagsController} noteTagsController={viewControllerManager.noteTagsController}
historyModalController={viewControllerManager.historyModalController} historyModalController={viewControllerManager.historyModalController}
/> />
<TagsContextMenuWrapper viewControllerManager={viewControllerManager} /> <TagContextMenuWrapper
navigationController={viewControllerManager.navigationController}
featuresController={viewControllerManager.featuresController}
/>
<FileContextMenuWrapper <FileContextMenuWrapper
filesController={viewControllerManager.filesController} filesController={viewControllerManager.filesController}
selectionController={viewControllerManager.selectionController} selectionController={viewControllerManager.selectionController}

View File

@@ -86,7 +86,7 @@ const FileContextMenu: FunctionComponent<Props> = observer(({ filesController, s
return ( return (
<div <div
ref={contextMenuRef} 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={{ style={{
...contextMenuStyle, ...contextMenuStyle,
maxHeight: contextMenuMaxHeight, maxHeight: contextMenuMaxHeight,

View File

@@ -12,6 +12,7 @@ import { addToast, dismissToast, ToastType } from '@standardnotes/toast'
import { NotesOptionsProps } from './NotesOptionsProps' import { NotesOptionsProps } from './NotesOptionsProps'
import { NotesController } from '@/Controllers/NotesController' import { NotesController } from '@/Controllers/NotesController'
import HorizontalSeparator from '../Shared/HorizontalSeparator' import HorizontalSeparator from '../Shared/HorizontalSeparator'
import { formatDateForContextMenu } from '@/Utils/DateUtils'
type DeletePermanentlyButtonProps = { type DeletePermanentlyButtonProps = {
closeOnBlur: NotesOptionsProps['closeOnBlur'] 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<{ const NoteAttributes: FunctionComponent<{
application: SNApplication application: SNApplication
note: SNNote note: SNNote
@@ -93,9 +87,9 @@ const NoteAttributes: FunctionComponent<{
const readTime = useMemo(() => (typeof words === 'number' ? calculateReadTime(words) : 'N/A'), [words]) 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 editor = application.componentManager.editorForNote(note)
const format = editor?.package_info?.file_type || 'txt' const format = editor?.package_info?.file_type || 'txt'

View File

@@ -5,7 +5,7 @@ import { STRING_GENERIC_SYNC_ERROR } from '@/Constants/Strings'
import { observer } from 'mobx-react-lite' import { observer } from 'mobx-react-lite'
import { WebApplication } from '@/Application/Application' import { WebApplication } from '@/Application/Application'
import { FunctionComponent, useState } from 'react' import { FunctionComponent, useState } from 'react'
import { formatLastSyncDate } from '@/Utils/FormatLastSyncDate' import { formatLastSyncDate } from '@/Utils/DateUtils'
import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup' import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup'
import PreferencesSegment from '../../PreferencesComponents/PreferencesSegment' import PreferencesSegment from '../../PreferencesComponents/PreferencesSegment'

View File

@@ -1,36 +1,33 @@
import { ViewControllerManager } from '@/Services/ViewControllerManager'
import { observer } from 'mobx-react-lite' 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 Icon from '@/Components/Icon/Icon'
import Menu from '@/Components/Menu/Menu' import Menu from '@/Components/Menu/Menu'
import MenuItem from '@/Components/Menu/MenuItem' import MenuItem from '@/Components/Menu/MenuItem'
import { MenuItemType } from '@/Components/Menu/MenuItemType' import { MenuItemType } from '@/Components/Menu/MenuItemType'
import { usePremiumModal } from '@/Hooks/usePremiumModal' import { usePremiumModal } from '@/Hooks/usePremiumModal'
import { useCloseOnBlur } from '@/Hooks/useCloseOnBlur'
import { SNTag } from '@standardnotes/snjs' 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 = { type ContextMenuProps = {
viewControllerManager: ViewControllerManager navigationController: NavigationController
} isEntitledToFolders: boolean
type ContextMenuProps = WrapperProps & {
selectedTag: SNTag selectedTag: SNTag
} }
const TagsContextMenu = observer(({ viewControllerManager, selectedTag }: ContextMenuProps) => { const TagContextMenu = ({ navigationController, isEntitledToFolders, selectedTag }: ContextMenuProps) => {
const premiumModal = usePremiumModal() const premiumModal = usePremiumModal()
const { contextMenuOpen, contextMenuPosition, contextMenuMaxHeight } = viewControllerManager.navigationController const { contextMenuOpen, contextMenuPosition, contextMenuMaxHeight } = navigationController
const contextMenuRef = useRef<HTMLDivElement>(null) const contextMenuRef = useRef<HTMLDivElement>(null)
const [closeOnBlur] = useCloseOnBlur(contextMenuRef, (open: boolean) => useCloseOnClickOutside(contextMenuRef, () => navigationController.setContextMenuOpen(false))
viewControllerManager.navigationController.setContextMenuOpen(open),
)
const reloadContextMenuLayout = useCallback(() => { const reloadContextMenuLayout = useCallback(() => {
viewControllerManager.navigationController.reloadContextMenuLayout() navigationController.reloadContextMenuLayout()
}, [viewControllerManager]) }, [navigationController])
useEffect(() => { useEffect(() => {
window.addEventListener('resize', reloadContextMenuLayout) window.addEventListener('resize', reloadContextMenuLayout)
@@ -40,28 +37,35 @@ const TagsContextMenu = observer(({ viewControllerManager, selectedTag }: Contex
}, [reloadContextMenuLayout]) }, [reloadContextMenuLayout])
const onClickAddSubtag = useCallback(() => { const onClickAddSubtag = useCallback(() => {
if (!viewControllerManager.featuresController.hasFolders) { if (!isEntitledToFolders) {
premiumModal.activate('Folders') premiumModal.activate('Folders')
return return
} }
viewControllerManager.navigationController.setContextMenuOpen(false) navigationController.setContextMenuOpen(false)
viewControllerManager.navigationController.setAddingSubtagTo(selectedTag) navigationController.setAddingSubtagTo(selectedTag)
}, [viewControllerManager, selectedTag, premiumModal]) }, [isEntitledToFolders, navigationController, selectedTag, premiumModal])
const onClickRename = useCallback(() => { const onClickRename = useCallback(() => {
viewControllerManager.navigationController.setContextMenuOpen(false) navigationController.setContextMenuOpen(false)
viewControllerManager.navigationController.editingTag = selectedTag navigationController.editingTag = selectedTag
}, [viewControllerManager, selectedTag]) }, [navigationController, selectedTag])
const onClickDelete = useCallback(() => { const onClickDelete = useCallback(() => {
viewControllerManager.navigationController.remove(selectedTag, true).catch(console.error) navigationController.remove(selectedTag, true).catch(console.error)
}, [viewControllerManager, selectedTag]) }, [navigationController, selectedTag])
const tagLastModified = useMemo(
() => formatDateForContextMenu(selectedTag.userModifiedDate),
[selectedTag.userModifiedDate],
)
const tagCreatedAt = useMemo(() => formatDateForContextMenu(selectedTag.created_at), [selectedTag.created_at])
return contextMenuOpen ? ( return contextMenuOpen ? (
<div <div
ref={contextMenuRef} 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={{ style={{
...contextMenuPosition, ...contextMenuPosition,
maxHeight: contextMenuMaxHeight, maxHeight: contextMenuMaxHeight,
@@ -71,48 +75,39 @@ const TagsContextMenu = observer(({ viewControllerManager, selectedTag }: Contex
a11yLabel="Tag context menu" a11yLabel="Tag context menu"
isOpen={contextMenuOpen} isOpen={contextMenuOpen}
closeMenu={() => { closeMenu={() => {
viewControllerManager.navigationController.setContextMenuOpen(false) navigationController.setContextMenuOpen(false)
}} }}
> >
<MenuItem <MenuItem type={MenuItemType.IconButton} className={'justify-between py-1.5'} onClick={onClickAddSubtag}>
type={MenuItemType.IconButton}
onBlur={closeOnBlur}
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" />
Add subtag Add subtag
</div> </div>
{!viewControllerManager.featuresController.hasFolders && <Icon type="premium-feature" />} {!isEntitledToFolders && <Icon type="premium-feature" />}
</MenuItem> </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" /> <Icon type="pencil-filled" className="mr-2 text-neutral" />
Rename Rename
</MenuItem> </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" /> <Icon type="trash" className="mr-2 text-danger" />
<span className="text-danger">Delete</span> <span className="text-danger">Delete</span>
</MenuItem> </MenuItem>
</Menu> </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> </div>
) : null ) : 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)

View File

@@ -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)

View File

@@ -3,3 +3,11 @@ import { dateToLocalizedString } from '@standardnotes/snjs/'
export const formatLastSyncDate = (lastUpdatedDate: Date) => { export const formatLastSyncDate = (lastUpdatedDate: Date) => {
return dateToLocalizedString(lastUpdatedDate) return dateToLocalizedString(lastUpdatedDate)
} }
export const formatDateForContextMenu = (date: Date | undefined) => {
if (!date) {
return
}
return `${date.toDateString()} ${date.toLocaleTimeString()}`
}