feat: Added "Add tag" option to file context menu

This commit is contained in:
Aman Harwara
2023-01-05 01:16:35 +05:30
parent 30dda73e90
commit ba4b6a580b
11 changed files with 160 additions and 42 deletions

View File

@@ -216,6 +216,8 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
<FileContextMenuWrapper
filesController={viewControllerManager.filesController}
selectionController={viewControllerManager.selectionController}
navigationController={viewControllerManager.navigationController}
linkingController={viewControllerManager.linkingController}
/>
<PurchaseFlowWrapper application={application} viewControllerManager={viewControllerManager} />
<ConfirmSignoutContainer

View File

@@ -358,6 +358,7 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
filesController={filesController}
featuresController={featuresController}
linkingController={linkingController}
navigationController={navigationController}
/>
) : (
<ContentList

View File

@@ -1,4 +1,6 @@
import { FilesController } from '@/Controllers/FilesController'
import { LinkingController } from '@/Controllers/LinkingController'
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
import { SelectedItemsController } from '@/Controllers/SelectedItemsController'
import { observer } from 'mobx-react-lite'
import { FunctionComponent } from 'react'
@@ -9,36 +11,47 @@ import FileMenuOptions from './FileMenuOptions'
type Props = {
filesController: FilesController
selectionController: SelectedItemsController
linkingController: LinkingController
navigationController: NavigationController
}
const FileContextMenu: FunctionComponent<Props> = observer(({ filesController, selectionController }) => {
const { showFileContextMenu, setShowFileContextMenu, fileContextMenuLocation } = filesController
const { selectedFiles } = selectionController
const FileContextMenu: FunctionComponent<Props> = observer(
({ filesController, selectionController, linkingController, navigationController }) => {
const { showFileContextMenu, setShowFileContextMenu, fileContextMenuLocation } = filesController
const { selectedFiles } = selectionController
return (
<Popover
open={showFileContextMenu}
anchorPoint={fileContextMenuLocation}
togglePopover={() => setShowFileContextMenu(!showFileContextMenu)}
align="start"
className="py-2"
>
<Menu a11yLabel="File context menu" isOpen={showFileContextMenu}>
<FileMenuOptions
filesController={filesController}
selectedFiles={selectedFiles}
closeMenu={() => setShowFileContextMenu(false)}
shouldShowRenameOption={false}
shouldShowAttachOption={false}
/>
</Menu>
</Popover>
)
})
return (
<Popover
open={showFileContextMenu}
anchorPoint={fileContextMenuLocation}
togglePopover={() => setShowFileContextMenu(!showFileContextMenu)}
align="start"
className="py-2"
>
<Menu a11yLabel="File context menu" isOpen={showFileContextMenu}>
<FileMenuOptions
filesController={filesController}
linkingController={linkingController}
navigationController={navigationController}
selectedFiles={selectedFiles}
closeMenu={() => setShowFileContextMenu(false)}
shouldShowRenameOption={false}
shouldShowAttachOption={false}
/>
</Menu>
</Popover>
)
},
)
FileContextMenu.displayName = 'FileContextMenu'
const FileContextMenuWrapper: FunctionComponent<Props> = ({ filesController, selectionController }) => {
const FileContextMenuWrapper: FunctionComponent<Props> = ({
filesController,
linkingController,
navigationController,
selectionController,
}) => {
const { showFileContextMenu } = filesController
const { selectedFiles } = selectionController
@@ -48,7 +61,14 @@ const FileContextMenuWrapper: FunctionComponent<Props> = ({ filesController, sel
return null
}
return <FileContextMenu filesController={filesController} selectionController={selectionController} />
return (
<FileContextMenu
filesController={filesController}
linkingController={linkingController}
navigationController={navigationController}
selectionController={selectionController}
/>
)
}
export default observer(FileContextMenuWrapper)

View File

@@ -11,10 +11,16 @@ import MenuItem from '../Menu/MenuItem'
import { FileContextMenuBackupOption } from './FileContextMenuBackupOption'
import MenuSwitchButtonItem from '../Menu/MenuSwitchButtonItem'
import { FileItem } from '@standardnotes/snjs'
import AddTagOption from '../NotesOptions/AddTagOption'
import { MenuItemIconSize } from '@/Constants/TailwindClassNames'
import { LinkingController } from '@/Controllers/LinkingController'
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
type Props = {
closeMenu: () => void
filesController: FilesController
linkingController: LinkingController
navigationController: NavigationController
isFileAttachedToNote?: boolean
renameToggleCallback?: (isRenamingFile: boolean) => void
shouldShowRenameOption: boolean
@@ -25,6 +31,8 @@ type Props = {
const FileMenuOptions: FunctionComponent<Props> = ({
closeMenu,
filesController,
linkingController,
navigationController,
isFileAttachedToNote,
renameToggleCallback,
shouldShowRenameOption,
@@ -82,6 +90,12 @@ const FileMenuOptions: FunctionComponent<Props> = ({
) : null}
</>
)}
<AddTagOption
navigationController={navigationController}
linkingController={linkingController}
selectedItems={selectedFiles}
iconClassName={`text-neutral mr-2 ${MenuItemIconSize}`}
/>
<MenuSwitchButtonItem
checked={hasProtectedFiles}
onChange={(hasProtectedFiles) => {

View File

@@ -6,13 +6,22 @@ import { SelectedItemsController } from '@/Controllers/SelectedItemsController'
import Popover from '../Popover/Popover'
import RoundIconButton from '../Button/RoundIconButton'
import Menu from '../Menu/Menu'
import { LinkingController } from '@/Controllers/LinkingController'
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
type Props = {
filesController: FilesController
selectionController: SelectedItemsController
linkingController: LinkingController
navigationController: NavigationController
}
const FilesOptionsPanel = ({ filesController, selectionController }: Props) => {
const FilesOptionsPanel = ({
filesController,
linkingController,
navigationController,
selectionController,
}: Props) => {
const [isOpen, setIsOpen] = useState(false)
const buttonRef = useRef<HTMLButtonElement>(null)
@@ -25,6 +34,8 @@ const FilesOptionsPanel = ({ filesController, selectionController }: Props) => {
<Menu a11yLabel="File options panel" isOpen={isOpen}>
<FileMenuOptions
filesController={filesController}
linkingController={linkingController}
navigationController={navigationController}
selectedFiles={selectionController.selectedFiles}
closeMenu={() => {
setIsOpen(false)

View File

@@ -112,6 +112,8 @@ const FileViewWithoutProtection = ({ application, viewControllerManager, file }:
<FileOptionsPanel
filesController={viewControllerManager.filesController}
selectionController={viewControllerManager.selectionController}
linkingController={viewControllerManager.linkingController}
navigationController={viewControllerManager.navigationController}
/>
</div>
</div>

View File

@@ -31,8 +31,19 @@ import { LinkingController } from '@/Controllers/LinkingController'
import { FeaturesController } from '@/Controllers/FeaturesController'
import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
import { useApplication } from '../ApplicationProvider'
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
const ContextMenuCell = ({ files, filesController }: { files: FileItem[]; filesController: FilesController }) => {
const ContextMenuCell = ({
files,
filesController,
navigationController,
linkingController,
}: {
files: FileItem[]
filesController: FilesController
navigationController: NavigationController
linkingController: LinkingController
}) => {
const [contextMenuVisible, setContextMenuVisible] = useState(false)
const anchorElementRef = useRef<HTMLButtonElement>(null)
@@ -61,6 +72,8 @@ const ContextMenuCell = ({ files, filesController }: { files: FileItem[]; filesC
>
<Menu a11yLabel="File context menu" isOpen={contextMenuVisible}>
<FileMenuOptions
linkingController={linkingController}
navigationController={navigationController}
closeMenu={() => {
setContextMenuVisible(false)
}}
@@ -160,9 +173,16 @@ type Props = {
filesController: FilesController
featuresController: FeaturesController
linkingController: LinkingController
navigationController: NavigationController
}
const FilesTableView = ({ application, filesController, featuresController, linkingController }: Props) => {
const FilesTableView = ({
application,
filesController,
featuresController,
linkingController,
navigationController,
}: Props) => {
const files = application.items
.getDisplayableNotesAndFiles()
.filter((item) => item.content_type === ContentType.File) as FileItem[]
@@ -312,12 +332,22 @@ const FilesTableView = ({ application, filesController, featuresController, link
featuresController={featuresController}
linkingController={linkingController}
/>
<ContextMenuCell files={[file]} filesController={filesController} />
<ContextMenuCell
files={[file]}
filesController={filesController}
linkingController={linkingController}
navigationController={navigationController}
/>
</div>
)
},
selectionActions: (fileIds) => (
<ContextMenuCell files={files.filter((file) => fileIds.includes(file.uuid))} filesController={filesController} />
<ContextMenuCell
files={files.filter((file) => fileIds.includes(file.uuid))}
filesController={filesController}
linkingController={linkingController}
navigationController={navigationController}
/>
),
showSelectionActions: true,
})
@@ -347,6 +377,8 @@ const FilesTableView = ({ application, filesController, featuresController, link
shouldShowRenameOption={false}
shouldShowAttachOption={false}
selectedFiles={[contextMenuFile]}
linkingController={linkingController}
navigationController={navigationController}
/>
</Menu>
</Popover>

View File

@@ -5,13 +5,22 @@ import { useCallback } from 'react'
import FileOptionsPanel from '../FileContextMenu/FileOptionsPanel'
import { FilesController } from '@/Controllers/FilesController'
import { SelectedItemsController } from '@/Controllers/SelectedItemsController'
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
import { LinkingController } from '@/Controllers/LinkingController'
type Props = {
filesController: FilesController
selectionController: SelectedItemsController
linkingController: LinkingController
navigationController: NavigationController
}
const MultipleSelectedFiles = ({ filesController, selectionController }: Props) => {
const MultipleSelectedFiles = ({
filesController,
selectionController,
linkingController,
navigationController,
}: Props) => {
const count = selectionController.selectedFilesCount
const cancelMultipleSelection = useCallback(() => {
@@ -23,7 +32,12 @@ const MultipleSelectedFiles = ({ filesController, selectionController }: Props)
<div className="flex w-full items-center justify-between p-4">
<h1 className="m-0 text-lg font-bold">{count} selected files</h1>
<div>
<FileOptionsPanel filesController={filesController} selectionController={selectionController} />
<FileOptionsPanel
filesController={filesController}
selectionController={selectionController}
linkingController={linkingController}
navigationController={navigationController}
/>
</div>
</div>
<div className="flex min-h-full w-full max-w-md flex-grow flex-col items-center justify-center">

View File

@@ -118,6 +118,8 @@ class NoteGroupView extends AbstractComponent<Props, State> {
<MultipleSelectedFiles
filesController={this.viewControllerManager.filesController}
selectionController={this.viewControllerManager.selectionController}
navigationController={this.viewControllerManager.navigationController}
linkingController={this.viewControllerManager.linkingController}
/>
)}
{shouldNotShowMultipleSelectedItems && hasControllers && (

View File

@@ -2,22 +2,28 @@ import { observer } from 'mobx-react-lite'
import { FunctionComponent, useCallback, useRef, useState } from 'react'
import Icon from '@/Components/Icon/Icon'
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
import { NotesController } from '@/Controllers/NotesController/NotesController'
import { KeyboardKey } from '@standardnotes/ui-services'
import Popover from '../Popover/Popover'
import { IconType } from '@standardnotes/snjs'
import { classNames, DecryptedItemInterface, IconType, SNTag } from '@standardnotes/snjs'
import { getTitleForLinkedTag } from '@/Utils/Items/Display/getTitleForLinkedTag'
import { useApplication } from '../ApplicationProvider'
import MenuItem from '../Menu/MenuItem'
import Menu from '../Menu/Menu'
import { LinkingController } from '@/Controllers/LinkingController'
type Props = {
navigationController: NavigationController
notesController: NotesController
linkingController: LinkingController
selectedItems: DecryptedItemInterface[]
iconClassName: string
}
const AddTagOption: FunctionComponent<Props> = ({ navigationController, notesController, iconClassName }) => {
const AddTagOption: FunctionComponent<Props> = ({
navigationController,
linkingController,
selectedItems,
iconClassName,
}) => {
const application = useApplication()
const menuContainerRef = useRef<HTMLDivElement>(null)
const buttonRef = useRef<HTMLButtonElement>(null)
@@ -28,6 +34,18 @@ const AddTagOption: FunctionComponent<Props> = ({ navigationController, notesCon
setIsOpen((isOpen) => !isOpen)
}, [])
const isTagLinkedToSelectedItems = (tag: SNTag) => {
return selectedItems.every((item) => application.getItemTags(item).find((itemTag) => itemTag.uuid === tag.uuid))
}
const linkTagToSelectedItems = (tag: SNTag) => {
selectedItems.forEach((item) => linkingController.linkItems(item, tag))
}
const unlinkTagFromSelectedItems = (tag: SNTag) => {
selectedItems.forEach((item) => linkingController.unlinkItems(item, tag))
}
return (
<div ref={menuContainerRef}>
<MenuItem
@@ -59,9 +77,7 @@ const AddTagOption: FunctionComponent<Props> = ({ navigationController, notesCon
<MenuItem
key={tag.uuid}
onClick={() => {
notesController.isTagInSelectedNotes(tag)
? notesController.removeTagFromSelectedNotes(tag).catch(console.error)
: notesController.addTagToSelectedNotes(tag).catch(console.error)
isTagLinkedToSelectedItems(tag) ? unlinkTagFromSelectedItems(tag) : linkTagToSelectedItems(tag)
}}
>
{tag.iconString && (
@@ -72,8 +88,10 @@ const AddTagOption: FunctionComponent<Props> = ({ navigationController, notesCon
/>
)}
<span
className={`overflow-hidden overflow-ellipsis whitespace-nowrap
${notesController.isTagInSelectedNotes(tag) ? 'font-bold' : ''}`}
className={classNames(
'overflow-hidden overflow-ellipsis whitespace-nowrap',
isTagLinkedToSelectedItems(tag) ? 'font-bold' : '',
)}
>
{getTitleForLinkedTag(tag, application)?.longTitle}
</span>

View File

@@ -42,6 +42,7 @@ const NotesOptions = ({
application,
navigationController,
notesController,
linkingController,
historyModalController,
closeMenu,
}: NotesOptionsProps) => {
@@ -214,7 +215,8 @@ const NotesOptions = ({
<AddTagOption
iconClassName={iconClass}
navigationController={navigationController}
notesController={notesController}
selectedItems={notes}
linkingController={linkingController}
/>
)}
<MenuItem