156 lines
5.0 KiB
TypeScript
156 lines
5.0 KiB
TypeScript
import { observer } from 'mobx-react-lite'
|
|
import { FunctionComponent, useCallback, useRef, useState } from 'react'
|
|
import Icon from '@/Components/Icon/Icon'
|
|
import { KeyboardKey } from '@standardnotes/ui-services'
|
|
import Popover from '../Popover/Popover'
|
|
import { classNames, DecryptedItemInterface, VaultListingInterface } from '@standardnotes/snjs'
|
|
import { useApplication } from '../ApplicationProvider'
|
|
import MenuItem from '../Menu/MenuItem'
|
|
import Menu from '../Menu/Menu'
|
|
import { featureTrunkVaultsEnabled } from '@/FeatureTrunk'
|
|
|
|
type Props = {
|
|
iconClassName: string
|
|
items: DecryptedItemInterface[]
|
|
}
|
|
|
|
const AddToVaultMenuOption: FunctionComponent<Props> = ({ iconClassName, items }) => {
|
|
const application = useApplication()
|
|
const menuContainerRef = useRef<HTMLDivElement>(null)
|
|
const buttonRef = useRef<HTMLButtonElement>(null)
|
|
|
|
const vaults = application.vaults.getVaults()
|
|
|
|
const [isSubMenuOpen, setIsSubMenuOpen] = useState(false)
|
|
|
|
const toggleSubMenu = useCallback(() => {
|
|
setIsSubMenuOpen((isOpen) => !isOpen)
|
|
}, [])
|
|
|
|
const addItemsToVault = useCallback(
|
|
async (vault: VaultListingInterface) => {
|
|
if (application.vaults.isVaultLocked(vault)) {
|
|
const unlocked = await application.vaultDisplayService.unlockVault(vault)
|
|
if (!unlocked) {
|
|
return
|
|
}
|
|
}
|
|
|
|
for (const item of items) {
|
|
await application.vaults.moveItemToVault(vault, item)
|
|
}
|
|
},
|
|
[application.vaultDisplayService, application.vaults, items],
|
|
)
|
|
|
|
const removeItemsFromVault = useCallback(async () => {
|
|
for (const item of items) {
|
|
const vault = application.vaults.getItemVault(item)
|
|
if (!vault) {
|
|
continue
|
|
}
|
|
|
|
if (application.vaults.isVaultLocked(vault)) {
|
|
const unlocked = await application.vaultDisplayService.unlockVault(vault)
|
|
if (!unlocked) {
|
|
return
|
|
}
|
|
}
|
|
await application.vaults.removeItemFromVault(item)
|
|
}
|
|
}, [application.vaultDisplayService, application.vaults, items])
|
|
|
|
const doesVaultContainItems = (vault: VaultListingInterface) => {
|
|
return items.every((item) => item.key_system_identifier === vault.systemIdentifier)
|
|
}
|
|
|
|
const doSomeItemsBelongToVault = items.some((item) => application.vaults.isItemInVault(item))
|
|
|
|
const singleItemVault = items.length === 1 ? application.vaults.getItemVault(items[0]) : undefined
|
|
|
|
if (!featureTrunkVaultsEnabled()) {
|
|
return null
|
|
}
|
|
|
|
return (
|
|
<div ref={menuContainerRef}>
|
|
<MenuItem
|
|
className="justify-between"
|
|
onClick={toggleSubMenu}
|
|
onKeyDown={(event) => {
|
|
if (event.key === KeyboardKey.Escape) {
|
|
setIsSubMenuOpen(false)
|
|
}
|
|
}}
|
|
ref={buttonRef}
|
|
>
|
|
<div className="flex items-center">
|
|
<Icon type="safe-square" className={iconClassName} />
|
|
Move to vault
|
|
</div>
|
|
<Icon type="chevron-right" className="text-neutral" />
|
|
</MenuItem>
|
|
<Popover
|
|
title="Move to vault"
|
|
togglePopover={toggleSubMenu}
|
|
anchorElement={buttonRef.current}
|
|
open={isSubMenuOpen}
|
|
side="right"
|
|
align="start"
|
|
className="py-2"
|
|
overrideZIndex="z-modal"
|
|
>
|
|
<Menu a11yLabel="Vault selection menu" isOpen={isSubMenuOpen}>
|
|
{doSomeItemsBelongToVault && (
|
|
<MenuItem
|
|
onClick={() => {
|
|
void removeItemsFromVault()
|
|
}}
|
|
>
|
|
<span className="flex overflow-hidden overflow-ellipsis whitespace-nowrap">
|
|
<Icon type="close" className="mr-2 text-neutral" />
|
|
<div className="flex w-full items-center gap-1">
|
|
Move out of {singleItemVault ? singleItemVault.name : 'vaults'}
|
|
</div>
|
|
</span>
|
|
</MenuItem>
|
|
)}
|
|
{vaults.map((vault) => {
|
|
if (singleItemVault) {
|
|
return null
|
|
}
|
|
|
|
return (
|
|
<MenuItem
|
|
key={vault.uuid}
|
|
onClick={() => {
|
|
doesVaultContainItems(vault) ? void removeItemsFromVault() : void addItemsToVault(vault)
|
|
}}
|
|
>
|
|
<span
|
|
className={classNames(
|
|
'flex overflow-ellipsis whitespace-nowrap',
|
|
doesVaultContainItems(vault) ? 'font-bold' : '',
|
|
)}
|
|
>
|
|
<Icon
|
|
type="safe-square"
|
|
size="large"
|
|
className={`mr-2 text-neutral ${doesVaultContainItems(vault) ? 'text-info' : ''}`}
|
|
/>
|
|
<div className="flex w-full items-center">
|
|
{vault.name}
|
|
{application.vaults.isVaultLocked(vault) && <Icon className="ml-1" type="lock" size={'small'} />}
|
|
</div>
|
|
</span>
|
|
</MenuItem>
|
|
)
|
|
})}
|
|
</Menu>
|
|
</Popover>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default observer(AddToVaultMenuOption)
|