chore: minor vaults ui improvements

This commit is contained in:
Aman Harwara
2023-08-07 15:36:26 +05:30
parent ee473fe34d
commit e2215f9a1f
6 changed files with 111 additions and 79 deletions

View File

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

View File

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

View File

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

View File

@@ -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={() => {

View File

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

View File

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