chore: minor vaults ui improvements
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import Button from '@/Components/Button/Button'
|
import Button from '@/Components/Button/Button'
|
||||||
import Icon from '@/Components/Icon/Icon'
|
import Icon from '@/Components/Icon/Icon'
|
||||||
import ModalOverlay from '@/Components/Modal/ModalOverlay'
|
import ModalOverlay from '@/Components/Modal/ModalOverlay'
|
||||||
import { TrustedContactInterface } from '@standardnotes/snjs'
|
import { TrustedContactInterface, classNames } from '@standardnotes/snjs'
|
||||||
import EditContactModal from './EditContactModal'
|
import EditContactModal from './EditContactModal'
|
||||||
import { useCallback, useState } from 'react'
|
import { useCallback, useState } from 'react'
|
||||||
import { useApplication } from '@/Components/ApplicationProvider'
|
import { useApplication } from '@/Components/ApplicationProvider'
|
||||||
@@ -28,15 +28,19 @@ const ContactItem = ({ contact }: Props) => {
|
|||||||
<EditContactModal editContactUuid={contact.contactUuid} onCloseDialog={closeContactModal} />
|
<EditContactModal editContactUuid={contact.contactUuid} onCloseDialog={closeContactModal} />
|
||||||
</ModalOverlay>
|
</ModalOverlay>
|
||||||
|
|
||||||
<div className="bg-gray-100 flex flex-row gap-3.5 rounded-lg px-3.5 py-2.5 shadow-md">
|
<div className="bg-gray-100 flex flex-row gap-3.5 rounded-lg px-3.5 py-2.5 border border-border shadow">
|
||||||
<Icon type={'user'} size="custom" className="mt-2.5 h-5.5 w-5.5 flex-shrink-0" />
|
<Icon type="user" size="custom" className="mt-2 h-5 w-5 flex-shrink-0" />
|
||||||
<div className="flex flex-col gap-2 py-1.5">
|
<div className="flex flex-col gap-1 py-1.5 overflow-hidden">
|
||||||
<span
|
<span
|
||||||
className={`mr-auto overflow-hidden text-ellipsis text-base font-bold ${contact.isMe ? 'text-info' : ''}`}
|
className={classNames(
|
||||||
|
'mr-auto overflow-hidden text-ellipsis text-base font-bold w-full',
|
||||||
|
contact.isMe ? 'text-info' : '',
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{contact.name}
|
{contact.name}
|
||||||
</span>
|
</span>
|
||||||
<span className="mr-auto overflow-hidden text-ellipsis text-sm">{collaborationID}</span>
|
|
||||||
|
<span className="mr-auto overflow-hidden text-ellipsis text-sm w-full">{collaborationID}</span>
|
||||||
|
|
||||||
<div className="mt-2.5 flex flex-row">
|
<div className="mt-2.5 flex flex-row">
|
||||||
<Button label="Edit" className={'mr-3 text-xs'} onClick={() => setIsContactModalOpen(true)} />
|
<Button label="Edit" className={'mr-3 text-xs'} onClick={() => setIsContactModalOpen(true)} />
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import VaultItem from './Vaults/VaultItem'
|
|||||||
import Button from '@/Components/Button/Button'
|
import Button from '@/Components/Button/Button'
|
||||||
import InviteItem from './Invites/InviteItem'
|
import InviteItem from './Invites/InviteItem'
|
||||||
import EditVaultModal from './Vaults/VaultModal/EditVaultModal'
|
import EditVaultModal from './Vaults/VaultModal/EditVaultModal'
|
||||||
|
import PreferencesPane from '../../PreferencesComponents/PreferencesPane'
|
||||||
|
import { ToastType, addToast } from '@standardnotes/toast'
|
||||||
|
|
||||||
const Vaults = () => {
|
const Vaults = () => {
|
||||||
const application = useApplication()
|
const application = useApplication()
|
||||||
@@ -94,7 +96,7 @@ const Vaults = () => {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<PreferencesPane>
|
||||||
<ModalOverlay isOpen={isAddContactModalOpen} close={closeAddContactModal}>
|
<ModalOverlay isOpen={isAddContactModalOpen} close={closeAddContactModal}>
|
||||||
<EditContactModal onCloseDialog={closeAddContactModal} />
|
<EditContactModal onCloseDialog={closeAddContactModal} />
|
||||||
</ModalOverlay>
|
</ModalOverlay>
|
||||||
@@ -147,23 +149,42 @@ const Vaults = () => {
|
|||||||
<Title>CollaborationID</Title>
|
<Title>CollaborationID</Title>
|
||||||
<Subtitle>Share your CollaborationID with collaborators to join their vaults.</Subtitle>
|
<Subtitle>Share your CollaborationID with collaborators to join their vaults.</Subtitle>
|
||||||
{contactService.isCollaborationEnabled() ? (
|
{contactService.isCollaborationEnabled() ? (
|
||||||
<div className="mt-2.5 flex flex-row">
|
<>
|
||||||
<code>
|
<code className="mt-2.5 overflow-hidden whitespace-pre-wrap break-words p-3 border border-border rounded bg-contrast">
|
||||||
<pre>{contactService.getCollaborationID()}</pre>
|
{contactService.getCollaborationID()}
|
||||||
</code>
|
</code>
|
||||||
</div>
|
<Button
|
||||||
|
label="Copy ID"
|
||||||
|
className="mt-2"
|
||||||
|
onClick={async () => {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(contactService.getCollaborationID())
|
||||||
|
addToast({
|
||||||
|
type: ToastType.Success,
|
||||||
|
message: 'Copied to clipboard',
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
addToast({
|
||||||
|
type: ToastType.Error,
|
||||||
|
message: 'Failed to copy to clipboard',
|
||||||
|
})
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div className="mt-2.5 flex flex-row">
|
<div className="mt-2.5 flex flex-row">
|
||||||
<Button
|
<Button
|
||||||
label="Enable Vault Sharing"
|
label="Enable Vault Sharing"
|
||||||
className={'mr-3 text-xs'}
|
className="mr-3 text-xs"
|
||||||
onClick={() => contactService.enableCollaboration()}
|
onClick={() => contactService.enableCollaboration()}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</PreferencesSegment>
|
</PreferencesSegment>
|
||||||
</PreferencesGroup>
|
</PreferencesGroup>
|
||||||
</>
|
</PreferencesPane>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,12 @@ const RadioButtonGroup = ({ value, items, onChange, className }: Props) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RadioGroup store={radio} className={`flex divide-x divide-border rounded border border-border ${className ?? ''}`}>
|
<RadioGroup
|
||||||
|
store={radio}
|
||||||
|
className={`flex divide-x divide-border rounded border border-border md:translucent-ui:border-[--popover-border-color] ${
|
||||||
|
className ?? ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
{items.map(({ label, value: itemValue }) => (
|
{items.map(({ label, value: itemValue }) => (
|
||||||
<label
|
<label
|
||||||
className={classNames(
|
className={classNames(
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ const ManyVaultSelectionMenu: FunctionComponent = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu a11yLabel="Vault selection menu" isOpen>
|
<Menu a11yLabel="Vault selection menu" isOpen>
|
||||||
|
{!vaults.length && <div className="text-center py-1">No vaults found</div>}
|
||||||
{vaults.map((vault) => (
|
{vaults.map((vault) => (
|
||||||
<MenuSwitchButtonItem
|
<MenuSwitchButtonItem
|
||||||
onChange={() => {
|
onChange={() => {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ const SingleVaultSelectionMenu: FunctionComponent = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu a11yLabel="Vault selection menu" isOpen>
|
<Menu a11yLabel="Vault selection menu" isOpen>
|
||||||
|
{!vaults.length && <div className="text-center py-1">No vaults found</div>}
|
||||||
{vaults.map((vault) => (
|
{vaults.map((vault) => (
|
||||||
<MenuRadioButtonItem key={vault.uuid} checked={isVaultVisible(vault)} onClick={() => selectVault(vault)}>
|
<MenuRadioButtonItem key={vault.uuid} checked={isVaultVisible(vault)} onClick={() => selectVault(vault)}>
|
||||||
<div className="flex w-full items-center gap-1">
|
<div className="flex w-full items-center gap-1">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { observer } from 'mobx-react-lite'
|
import { observer } from 'mobx-react-lite'
|
||||||
import { FunctionComponent, useCallback, useRef, useState } from 'react'
|
import { useCallback, useRef, useState } from 'react'
|
||||||
import Icon from '@/Components/Icon/Icon'
|
import Icon from '@/Components/Icon/Icon'
|
||||||
import { KeyboardKey } from '@standardnotes/ui-services'
|
import { KeyboardKey } from '@standardnotes/ui-services'
|
||||||
import Popover from '../Popover/Popover'
|
import Popover from '../Popover/Popover'
|
||||||
@@ -9,24 +9,10 @@ import MenuItem from '../Menu/MenuItem'
|
|||||||
import Menu from '../Menu/Menu'
|
import Menu from '../Menu/Menu'
|
||||||
import { featureTrunkVaultsEnabled } from '@/FeatureTrunk'
|
import { featureTrunkVaultsEnabled } from '@/FeatureTrunk'
|
||||||
|
|
||||||
type Props = {
|
const VaultMenu = ({ items }: { items: DecryptedItemInterface[] }) => {
|
||||||
iconClassName: string
|
|
||||||
items: DecryptedItemInterface[]
|
|
||||||
}
|
|
||||||
|
|
||||||
const AddToVaultMenuOption: FunctionComponent<Props> = ({ iconClassName, items }) => {
|
|
||||||
const application = useApplication()
|
const application = useApplication()
|
||||||
const menuContainerRef = useRef<HTMLDivElement>(null)
|
|
||||||
const buttonRef = useRef<HTMLButtonElement>(null)
|
|
||||||
|
|
||||||
const vaults = application.vaults.getVaults()
|
const vaults = application.vaults.getVaults()
|
||||||
|
|
||||||
const [isSubMenuOpen, setIsSubMenuOpen] = useState(false)
|
|
||||||
|
|
||||||
const toggleSubMenu = useCallback(() => {
|
|
||||||
setIsSubMenuOpen((isOpen) => !isOpen)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const addItemsToVault = useCallback(
|
const addItemsToVault = useCallback(
|
||||||
async (vault: VaultListingInterface) => {
|
async (vault: VaultListingInterface) => {
|
||||||
if (application.vaultLocks.isVaultLocked(vault)) {
|
if (application.vaultLocks.isVaultLocked(vault)) {
|
||||||
@@ -68,6 +54,68 @@ const AddToVaultMenuOption: FunctionComponent<Props> = ({ iconClassName, items }
|
|||||||
|
|
||||||
const singleItemVault = items.length === 1 ? application.vaults.getItemVault(items[0]) : undefined
|
const singleItemVault = items.length === 1 ? application.vaults.getItemVault(items[0]) : undefined
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Menu a11yLabel="Vault selection menu" isOpen={true}>
|
||||||
|
{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.length && <div className="flex flex-col items-center justify-center py-1">No vaults found</div>}
|
||||||
|
{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={classNames('mr-2 text-neutral', doesVaultContainItems(vault) ? 'text-info' : '')}
|
||||||
|
/>
|
||||||
|
<div className="flex w-full items-center">
|
||||||
|
{vault.name}
|
||||||
|
{application.vaultLocks.isVaultLocked(vault) && <Icon className="ml-1" type="lock" size={'small'} />}
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</MenuItem>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Menu>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const AddToVaultMenuOption = ({ iconClassName, items }: { iconClassName: string; items: DecryptedItemInterface[] }) => {
|
||||||
|
const menuContainerRef = useRef<HTMLDivElement>(null)
|
||||||
|
const buttonRef = useRef<HTMLButtonElement>(null)
|
||||||
|
|
||||||
|
const [isSubMenuOpen, setIsSubMenuOpen] = useState(false)
|
||||||
|
|
||||||
|
const toggleSubMenu = useCallback(() => {
|
||||||
|
setIsSubMenuOpen((isOpen) => !isOpen)
|
||||||
|
}, [])
|
||||||
|
|
||||||
if (!featureTrunkVaultsEnabled()) {
|
if (!featureTrunkVaultsEnabled()) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -100,55 +148,7 @@ const AddToVaultMenuOption: FunctionComponent<Props> = ({ iconClassName, items }
|
|||||||
className="py-2"
|
className="py-2"
|
||||||
overrideZIndex="z-modal"
|
overrideZIndex="z-modal"
|
||||||
>
|
>
|
||||||
<Menu a11yLabel="Vault selection menu" isOpen={isSubMenuOpen}>
|
<VaultMenu items={items} />
|
||||||
{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.vaultLocks.isVaultLocked(vault) && (
|
|
||||||
<Icon className="ml-1" type="lock" size={'small'} />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
</MenuItem>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</Menu>
|
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user