Skip to content

Commit

Permalink
feat: add tag id to tag context menu (#1185)
Browse files Browse the repository at this point in the history
  • Loading branch information
amanharwara committed Jun 30, 2022
1 parent 604520f commit 8b845ea
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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']
Expand Down Expand Up @@ -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
Expand All @@ -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'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down
103 changes: 49 additions & 54 deletions packages/web/src/javascripts/Components/Tags/TagContextMenu.tsx
Original file line number Diff line number Diff line change
@@ -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'

type WrapperProps = {
viewControllerManager: ViewControllerManager
}

type ContextMenuProps = WrapperProps & {
import { useCloseOnClickOutside } from '@/Hooks/useCloseOnClickOutside'
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
import HorizontalSeparator from '../Shared/HorizontalSeparator'
import { formatDateForContextMenu } from '@/Utils/DateUtils'

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)
Expand All @@ -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,
Expand All @@ -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)
Original file line number Diff line number Diff line change
@@ -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)
Original file line number Diff line number Diff line change
Expand Up @@ -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()}`
}

0 comments on commit 8b845ea

Please sign in to comment.