chore: allow setting custom icon to vault

This commit is contained in:
Aman Harwara
2023-08-08 19:00:04 +05:30
parent cd1b488769
commit f82974633b
14 changed files with 174 additions and 48 deletions

View File

@@ -8,6 +8,9 @@ import { VaultListingContent } from './VaultListingContent'
import { KeySystemRootKeyStorageMode } from '../KeySystemRootKey/KeySystemRootKeyStorageMode' import { KeySystemRootKeyStorageMode } from '../KeySystemRootKey/KeySystemRootKeyStorageMode'
import { VaultListingSharingInfo } from './VaultListingSharingInfo' import { VaultListingSharingInfo } from './VaultListingSharingInfo'
import { KeySystemIdentifier } from '../KeySystemRootKey/KeySystemIdentifier' import { KeySystemIdentifier } from '../KeySystemRootKey/KeySystemIdentifier'
import { EmojiString, IconType } from '../../Utilities/Icon/IconType'
export const DefaultVaultIconName: IconType = 'safe-square'
export class VaultListing extends DecryptedItem<VaultListingContent> implements VaultListingInterface { export class VaultListing extends DecryptedItem<VaultListingContent> implements VaultListingInterface {
systemIdentifier: KeySystemIdentifier systemIdentifier: KeySystemIdentifier
@@ -17,6 +20,7 @@ export class VaultListing extends DecryptedItem<VaultListingContent> implements
name: string name: string
description?: string description?: string
iconString: IconType | EmojiString
sharing?: VaultListingSharingInfo sharing?: VaultListingSharingInfo
@@ -30,6 +34,7 @@ export class VaultListing extends DecryptedItem<VaultListingContent> implements
this.name = payload.content.name this.name = payload.content.name
this.description = payload.content.description this.description = payload.content.description
this.iconString = payload.content.iconString || DefaultVaultIconName
this.sharing = payload.content.sharing this.sharing = payload.content.sharing
} }

View File

@@ -3,6 +3,7 @@ import { KeySystemIdentifier } from '../KeySystemRootKey/KeySystemIdentifier'
import { KeySystemRootKeyParamsInterface } from '../../Local/KeyParams/KeySystemRootKeyParamsInterface' import { KeySystemRootKeyParamsInterface } from '../../Local/KeyParams/KeySystemRootKeyParamsInterface'
import { KeySystemRootKeyStorageMode } from '../KeySystemRootKey/KeySystemRootKeyStorageMode' import { KeySystemRootKeyStorageMode } from '../KeySystemRootKey/KeySystemRootKeyStorageMode'
import { VaultListingSharingInfo } from './VaultListingSharingInfo' import { VaultListingSharingInfo } from './VaultListingSharingInfo'
import { EmojiString, IconType } from '../../Utilities/Icon/IconType'
export interface VaultListingContentSpecialized extends SpecializedContent { export interface VaultListingContentSpecialized extends SpecializedContent {
systemIdentifier: KeySystemIdentifier systemIdentifier: KeySystemIdentifier
@@ -12,6 +13,7 @@ export interface VaultListingContentSpecialized extends SpecializedContent {
name: string name: string
description?: string description?: string
iconString: IconType | EmojiString
sharing?: VaultListingSharingInfo sharing?: VaultListingSharingInfo
} }

View File

@@ -5,6 +5,7 @@ import { KeySystemRootKeyStorageMode } from '../KeySystemRootKey/KeySystemRootKe
import { VaultListingSharingInfo } from './VaultListingSharingInfo' import { VaultListingSharingInfo } from './VaultListingSharingInfo'
import { VaultListingContent } from './VaultListingContent' import { VaultListingContent } from './VaultListingContent'
import { DecryptedItemInterface } from '../../Abstract/Item' import { DecryptedItemInterface } from '../../Abstract/Item'
import { EmojiString, IconType } from '../../Utilities/Icon/IconType'
export interface VaultListingInterface extends DecryptedItemInterface<VaultListingContent> { export interface VaultListingInterface extends DecryptedItemInterface<VaultListingContent> {
systemIdentifier: KeySystemIdentifier systemIdentifier: KeySystemIdentifier
@@ -14,6 +15,7 @@ export interface VaultListingInterface extends DecryptedItemInterface<VaultListi
name: string name: string
description?: string description?: string
iconString: IconType | EmojiString
sharing?: VaultListingSharingInfo sharing?: VaultListingSharingInfo

View File

@@ -1,5 +1,6 @@
import { DecryptedItemMutator } from '../../Abstract/Item' import { DecryptedItemMutator } from '../../Abstract/Item'
import { KeySystemRootKeyParamsInterface } from '../../Local/KeyParams/KeySystemRootKeyParamsInterface' import { KeySystemRootKeyParamsInterface } from '../../Local/KeyParams/KeySystemRootKeyParamsInterface'
import { EmojiString, IconType } from '../../Utilities/Icon/IconType'
import { KeySystemRootKeyStorageMode } from '../KeySystemRootKey/KeySystemRootKeyStorageMode' import { KeySystemRootKeyStorageMode } from '../KeySystemRootKey/KeySystemRootKeyStorageMode'
import { VaultListingContent } from './VaultListingContent' import { VaultListingContent } from './VaultListingContent'
import { VaultListingSharingInfo } from './VaultListingSharingInfo' import { VaultListingSharingInfo } from './VaultListingSharingInfo'
@@ -13,6 +14,10 @@ export class VaultListingMutator extends DecryptedItemMutator<VaultListingConten
this.mutableContent.description = description this.mutableContent.description = description
} }
set iconString(iconString: IconType | EmojiString) {
this.mutableContent.iconString = iconString
}
set sharing(sharing: VaultListingSharingInfo | undefined) { set sharing(sharing: VaultListingSharingInfo | undefined) {
this.mutableContent.sharing = sharing this.mutableContent.sharing = sharing
} }

View File

@@ -8,6 +8,8 @@ import {
KeySystemRootKeyStorageMode, KeySystemRootKeyStorageMode,
FillItemContentSpecialized, FillItemContentSpecialized,
KeySystemRootKeyInterface, KeySystemRootKeyInterface,
EmojiString,
IconType,
} from '@standardnotes/models' } from '@standardnotes/models'
import { MutatorClientInterface } from '../../Mutator/MutatorClientInterface' import { MutatorClientInterface } from '../../Mutator/MutatorClientInterface'
import { ContentType } from '@standardnotes/domain-core' import { ContentType } from '@standardnotes/domain-core'
@@ -25,6 +27,7 @@ export class CreateVault {
async execute(dto: { async execute(dto: {
vaultName: string vaultName: string
vaultDescription?: string vaultDescription?: string
vaultIcon: IconType | EmojiString
userInputtedPassword: string | undefined userInputtedPassword: string | undefined
storagePreference: KeySystemRootKeyStorageMode storagePreference: KeySystemRootKeyStorageMode
}): Promise<VaultListingInterface> { }): Promise<VaultListingInterface> {
@@ -44,6 +47,7 @@ export class CreateVault {
keySystemIdentifier, keySystemIdentifier,
vaultName: dto.vaultName, vaultName: dto.vaultName,
vaultDescription: dto.vaultDescription, vaultDescription: dto.vaultDescription,
vaultIcon: dto.vaultIcon,
passwordType: dto.userInputtedPassword ? KeySystemPasswordType.UserInputted : KeySystemPasswordType.Randomized, passwordType: dto.userInputtedPassword ? KeySystemPasswordType.UserInputted : KeySystemPasswordType.Randomized,
rootKeyParams: rootKey.keyParams, rootKeyParams: rootKey.keyParams,
storage: dto.storagePreference, storage: dto.storagePreference,
@@ -58,6 +62,7 @@ export class CreateVault {
keySystemIdentifier: string keySystemIdentifier: string
vaultName: string vaultName: string
vaultDescription?: string vaultDescription?: string
vaultIcon: IconType | EmojiString
passwordType: KeySystemPasswordType passwordType: KeySystemPasswordType
rootKeyParams: KeySystemRootKeyParamsInterface rootKeyParams: KeySystemRootKeyParamsInterface
storage: KeySystemRootKeyStorageMode storage: KeySystemRootKeyStorageMode
@@ -68,6 +73,7 @@ export class CreateVault {
keyStorageMode: dto.storage, keyStorageMode: dto.storage,
name: dto.vaultName, name: dto.vaultName,
description: dto.vaultDescription, description: dto.vaultDescription,
iconString: dto.vaultIcon,
} }
return this.mutator.createItem(ContentType.TYPES.VaultListing, FillItemContentSpecialized(content), true) return this.mutator.createItem(ContentType.TYPES.VaultListing, FillItemContentSpecialized(content), true)

View File

@@ -4,7 +4,9 @@ import { SendVaultDataChangedMessage } from './../SharedVaults/UseCase/SendVault
import { isClientDisplayableError } from '@standardnotes/responses' import { isClientDisplayableError } from '@standardnotes/responses'
import { import {
DecryptedItemInterface, DecryptedItemInterface,
EmojiString,
FileItem, FileItem,
IconType,
KeySystemIdentifier, KeySystemIdentifier,
KeySystemRootKeyStorageMode, KeySystemRootKeyStorageMode,
SharedVaultListingInterface, SharedVaultListingInterface,
@@ -97,10 +99,15 @@ export class VaultService
return vault return vault
} }
async createRandomizedVault(dto: { name: string; description?: string }): Promise<VaultListingInterface> { async createRandomizedVault(dto: {
name: string
description?: string
iconString: IconType | EmojiString
}): Promise<VaultListingInterface> {
return this.createVaultWithParameters({ return this.createVaultWithParameters({
name: dto.name, name: dto.name,
description: dto.description, description: dto.description,
iconString: dto.iconString,
userInputtedPassword: undefined, userInputtedPassword: undefined,
storagePreference: KeySystemRootKeyStorageMode.Synced, storagePreference: KeySystemRootKeyStorageMode.Synced,
}) })
@@ -109,6 +116,7 @@ export class VaultService
async createUserInputtedPasswordVault(dto: { async createUserInputtedPasswordVault(dto: {
name: string name: string
description?: string description?: string
iconString: IconType | EmojiString
userInputtedPassword: string userInputtedPassword: string
storagePreference: KeySystemRootKeyStorageMode storagePreference: KeySystemRootKeyStorageMode
}): Promise<VaultListingInterface> { }): Promise<VaultListingInterface> {
@@ -118,12 +126,14 @@ export class VaultService
private async createVaultWithParameters(dto: { private async createVaultWithParameters(dto: {
name: string name: string
description?: string description?: string
iconString: IconType | EmojiString
userInputtedPassword: string | undefined userInputtedPassword: string | undefined
storagePreference: KeySystemRootKeyStorageMode storagePreference: KeySystemRootKeyStorageMode
}): Promise<VaultListingInterface> { }): Promise<VaultListingInterface> {
const result = await this._createVault.execute({ const result = await this._createVault.execute({
vaultName: dto.name, vaultName: dto.name,
vaultDescription: dto.description, vaultDescription: dto.description,
vaultIcon: dto.iconString,
userInputtedPassword: dto.userInputtedPassword, userInputtedPassword: dto.userInputtedPassword,
storagePreference: dto.storagePreference, storagePreference: dto.storagePreference,
}) })
@@ -188,13 +198,14 @@ export class VaultService
return true return true
} }
async changeVaultNameAndDescription( async changeVaultMetadata(
vault: VaultListingInterface, vault: VaultListingInterface,
params: { name: string; description?: string }, params: { name: string; description?: string; iconString: IconType | EmojiString },
): Promise<VaultListingInterface> { ): Promise<VaultListingInterface> {
const updatedVault = await this.mutator.changeItem<VaultListingMutator, VaultListingInterface>(vault, (mutator) => { const updatedVault = await this.mutator.changeItem<VaultListingMutator, VaultListingInterface>(vault, (mutator) => {
mutator.name = params.name mutator.name = params.name
mutator.description = params.description mutator.description = params.description
mutator.iconString = params.iconString
}) })
await this.sync.sync() await this.sync.sync()

View File

@@ -1,5 +1,7 @@
import { import {
DecryptedItemInterface, DecryptedItemInterface,
EmojiString,
IconType,
KeySystemIdentifier, KeySystemIdentifier,
KeySystemRootKeyStorageMode, KeySystemRootKeyStorageMode,
SharedVaultListingInterface, SharedVaultListingInterface,
@@ -12,10 +14,15 @@ import { Result } from '@standardnotes/domain-core'
export interface VaultServiceInterface export interface VaultServiceInterface
extends AbstractService<VaultServiceEvent, VaultServiceEventPayload[VaultServiceEvent]> { extends AbstractService<VaultServiceEvent, VaultServiceEventPayload[VaultServiceEvent]> {
createRandomizedVault(dto: { name: string; description?: string }): Promise<VaultListingInterface> createRandomizedVault(dto: {
name: string
description?: string
iconString: IconType | EmojiString
}): Promise<VaultListingInterface>
createUserInputtedPasswordVault(dto: { createUserInputtedPasswordVault(dto: {
name: string name: string
description?: string description?: string
iconString: IconType | EmojiString
userInputtedPassword: string userInputtedPassword: string
storagePreference: KeySystemRootKeyStorageMode storagePreference: KeySystemRootKeyStorageMode
}): Promise<VaultListingInterface> }): Promise<VaultListingInterface>
@@ -32,9 +39,9 @@ export interface VaultServiceInterface
isItemInVault(item: DecryptedItemInterface): boolean isItemInVault(item: DecryptedItemInterface): boolean
getItemVault(item: DecryptedItemInterface): VaultListingInterface | undefined getItemVault(item: DecryptedItemInterface): VaultListingInterface | undefined
changeVaultNameAndDescription( changeVaultMetadata(
vault: VaultListingInterface, vault: VaultListingInterface,
params: { name: string; description: string }, params: { name: string; description: string; iconString: IconType | EmojiString },
): Promise<VaultListingInterface> ): Promise<VaultListingInterface>
rotateVaultRootKey(vault: VaultListingInterface, vaultPassword?: string): Promise<void> rotateVaultRootKey(vault: VaultListingInterface, vaultPassword?: string): Promise<void>

View File

@@ -1,6 +1,6 @@
import { classNames } from '@standardnotes/utils' import { classNames } from '@standardnotes/utils'
import { EmojiString, Platform, VectorIconNameOrEmoji } from '@standardnotes/snjs' import { EmojiString, Platform, VectorIconNameOrEmoji } from '@standardnotes/snjs'
import { FunctionComponent, useMemo, useRef, useState } from 'react' import { ForwardedRef, forwardRef, useCallback, useMemo, useRef, useState } from 'react'
import Dropdown from '../Dropdown/Dropdown' import Dropdown from '../Dropdown/Dropdown'
import { DropdownItem } from '../Dropdown/DropdownItem' import { DropdownItem } from '../Dropdown/DropdownItem'
import { getEmojiLength } from './EmojiLength' import { getEmojiLength } from './EmojiLength'
@@ -17,6 +17,39 @@ type Props = {
className?: string className?: string
} }
const TabButton = forwardRef(
(
{
type,
label,
currentType,
selectTab,
}: {
label: string
type: IconPickerType | 'reset'
currentType: IconPickerType
selectTab: (type: IconPickerType | 'reset') => void
},
ref: ForwardedRef<HTMLButtonElement>,
) => {
const isSelected = currentType === type
return (
<button
className={`relative mr-2 cursor-pointer border-0 pb-1.5 text-mobile-menu-item focus:shadow-none md:text-tablet-menu-item lg:text-menu-item ${
isSelected ? 'font-medium text-info' : 'text-text'
}`}
onClick={() => {
selectTab(type)
}}
ref={ref}
>
{label}
</button>
)
},
)
const IconPicker = ({ selectedValue, onIconChange, platform, className, useIconGrid, iconGridClassName }: Props) => { const IconPicker = ({ selectedValue, onIconChange, platform, className, useIconGrid, iconGridClassName }: Props) => {
const iconKeys = useMemo(() => Object.keys(IconNameToSvgMapping), []) const iconKeys = useMemo(() => Object.keys(IconNameToSvgMapping), [])
@@ -51,26 +84,6 @@ const IconPicker = ({ selectedValue, onIconChange, platform, className, useIconG
} }
} }
const TabButton: FunctionComponent<{
label: string
type: IconPickerType | 'reset'
}> = ({ type, label }) => {
const isSelected = currentType === type
return (
<button
className={`relative mr-2 cursor-pointer border-0 pb-1.5 text-mobile-menu-item focus:shadow-none md:text-tablet-menu-item lg:text-menu-item ${
isSelected ? 'font-medium text-info' : 'text-text'
}`}
onClick={() => {
selectTab(type)
}}
>
{label}
</button>
)
}
const handleIconChange = (value: string) => { const handleIconChange = (value: string) => {
onIconChange(value) onIconChange(value)
} }
@@ -88,12 +101,20 @@ const IconPicker = ({ selectedValue, onIconChange, platform, className, useIconG
} }
} }
const focusOnMount = useCallback((element: HTMLButtonElement | null) => {
if (element) {
setTimeout(() => {
element.focus()
})
}
}, [])
return ( return (
<div className={`flex h-full flex-grow flex-col overflow-auto ${className}`}> <div className={`flex h-full flex-grow flex-col overflow-auto ${className}`}>
<div className="flex"> <div className="flex">
<TabButton label="Icon" type={'icon'} /> <TabButton label="Icon" type={'icon'} currentType={currentType} selectTab={selectTab} />
<TabButton label="Emoji" type={'emoji'} /> <TabButton label="Emoji" type={'emoji'} currentType={currentType} selectTab={selectTab} />
<TabButton label="Reset" type={'reset'} /> <TabButton label="Reset" type={'reset'} currentType={currentType} selectTab={selectTab} />
</div> </div>
<div className={'mt-2 h-full min-h-0 overflow-auto'}> <div className={'mt-2 h-full min-h-0 overflow-auto'}>
{currentType === 'icon' && {currentType === 'icon' &&
@@ -104,12 +125,13 @@ const IconPicker = ({ selectedValue, onIconChange, platform, className, useIconG
iconGridClassName, iconGridClassName,
)} )}
> >
{iconKeys.map((iconName) => ( {iconKeys.map((iconName, index) => (
<button <button
key={iconName} key={iconName}
onClick={() => { onClick={() => {
handleIconChange(iconName) handleIconChange(iconName)
}} }}
ref={index === 0 ? focusOnMount : undefined}
> >
<Icon type={iconName} /> <Icon type={iconName} />
</button> </button>

View File

@@ -48,6 +48,7 @@ const ModalOverlay = forwardRef(
modal={false} modal={false}
portal={true} portal={true}
preventBodyScroll={true} preventBodyScroll={true}
hideOnInteractOutside={false}
{...props} {...props}
> >
{children} {children}

View File

@@ -108,7 +108,7 @@ const VaultItem = ({ vault }: Props) => {
</ModalOverlay> </ModalOverlay>
<div className="flex flex-row gap-3.5 rounded-lg px-3.5 py-2.5 border border-border shadow"> <div className="flex flex-row gap-3.5 rounded-lg px-3.5 py-2.5 border border-border shadow">
<Icon type="safe-square" size="custom" className="mt-2.5 h-5.5 w-5.5 flex-shrink-0" /> <Icon type={vault.iconString} size="custom" className="mt-2.5 h-5.5 w-5.5 flex-shrink-0" />
<div className="flex flex-col gap-1.5 py-1.5"> <div className="flex flex-col gap-1.5 py-1.5">
<span className="mr-auto overflow-hidden text-ellipsis text-base font-bold">{vault.name}</span> <span className="mr-auto overflow-hidden text-ellipsis text-base font-bold">{vault.name}</span>
{vault.description && ( {vault.description && (

View File

@@ -9,6 +9,7 @@ import {
SharedVaultInviteServerHash, SharedVaultInviteServerHash,
SharedVaultUserServerHash, SharedVaultUserServerHash,
VaultListingInterface, VaultListingInterface,
VectorIconNameOrEmoji,
isClientDisplayableError, isClientDisplayableError,
} from '@standardnotes/snjs' } from '@standardnotes/snjs'
import { VaultModalMembers } from './VaultModalMembers' import { VaultModalMembers } from './VaultModalMembers'
@@ -16,6 +17,11 @@ import { VaultModalInvites } from './VaultModalInvites'
import { PasswordTypePreference } from './PasswordTypePreference' import { PasswordTypePreference } from './PasswordTypePreference'
import { KeyStoragePreference } from './KeyStoragePreference' import { KeyStoragePreference } from './KeyStoragePreference'
import useItem from '@/Hooks/useItem' import useItem from '@/Hooks/useItem'
import Button from '@/Components/Button/Button'
import Icon from '@/Components/Icon/Icon'
import StyledTooltip from '@/Components/StyledTooltip/StyledTooltip'
import Popover from '@/Components/Popover/Popover'
import IconPicker from '@/Components/Icon/IconPicker'
type Props = { type Props = {
existingVaultUuid?: string existingVaultUuid?: string
@@ -29,6 +35,7 @@ const EditVaultModal: FunctionComponent<Props> = ({ onCloseDialog, existingVault
const [name, setName] = useState('') const [name, setName] = useState('')
const [description, setDescription] = useState('') const [description, setDescription] = useState('')
const [iconString, setIconString] = useState<VectorIconNameOrEmoji>('safe-square')
const [members, setMembers] = useState<SharedVaultUserServerHash[]>([]) const [members, setMembers] = useState<SharedVaultUserServerHash[]>([])
const [invites, setInvites] = useState<SharedVaultInviteServerHash[]>([]) const [invites, setInvites] = useState<SharedVaultInviteServerHash[]>([])
const [isAdmin, setIsAdmin] = useState(true) const [isAdmin, setIsAdmin] = useState(true)
@@ -43,6 +50,7 @@ const EditVaultModal: FunctionComponent<Props> = ({ onCloseDialog, existingVault
if (existingVault) { if (existingVault) {
setName(existingVault.name ?? '') setName(existingVault.name ?? '')
setDescription(existingVault.description ?? '') setDescription(existingVault.description ?? '')
setIconString(existingVault.iconString)
setPasswordType(existingVault.rootKeyParams.passwordType) setPasswordType(existingVault.rootKeyParams.passwordType)
setKeyStorageMode(existingVault.keyStorageMode) setKeyStorageMode(existingVault.keyStorageMode)
} }
@@ -85,10 +93,11 @@ const EditVaultModal: FunctionComponent<Props> = ({ onCloseDialog, existingVault
return return
} }
if (vault.name !== name || vault.description !== description) { if (vault.name !== name || vault.description !== description || vault.iconString !== iconString) {
await application.vaults.changeVaultNameAndDescription(vault, { await application.vaults.changeVaultMetadata(vault, {
name: name, name: name,
description: description, description: description,
iconString: iconString,
}) })
} }
@@ -125,7 +134,16 @@ const EditVaultModal: FunctionComponent<Props> = ({ onCloseDialog, existingVault
handleDialogClose() handleDialogClose()
}, },
[application.vaults, customPassword, description, handleDialogClose, keyStorageMode, name, passwordType], [
application.vaults,
customPassword,
description,
handleDialogClose,
iconString,
keyStorageMode,
name,
passwordType,
],
) )
const createNewVault = useCallback(async () => { const createNewVault = useCallback(async () => {
@@ -141,6 +159,7 @@ const EditVaultModal: FunctionComponent<Props> = ({ onCloseDialog, existingVault
await application.vaults.createUserInputtedPasswordVault({ await application.vaults.createUserInputtedPasswordVault({
name, name,
description, description,
iconString: iconString,
storagePreference: keyStorageMode, storagePreference: keyStorageMode,
userInputtedPassword: customPassword, userInputtedPassword: customPassword,
}) })
@@ -148,11 +167,21 @@ const EditVaultModal: FunctionComponent<Props> = ({ onCloseDialog, existingVault
await application.vaults.createRandomizedVault({ await application.vaults.createRandomizedVault({
name, name,
description, description,
iconString: iconString,
}) })
} }
handleDialogClose() handleDialogClose()
}, [application.vaults, customPassword, description, handleDialogClose, keyStorageMode, name, passwordType]) }, [
application.vaults,
customPassword,
description,
handleDialogClose,
iconString,
keyStorageMode,
name,
passwordType,
])
const handleSubmit = useCallback(async () => { const handleSubmit = useCallback(async () => {
if (isSubmitting) { if (isSubmitting) {
@@ -186,6 +215,12 @@ const EditVaultModal: FunctionComponent<Props> = ({ onCloseDialog, existingVault
[existingVault, handleDialogClose, handleSubmit, isSubmitting], [existingVault, handleDialogClose, handleSubmit, isSubmitting],
) )
const [shouldShowIconPicker, setShouldShowIconPicker] = useState(false)
const iconPickerButtonRef = useRef<HTMLButtonElement>(null)
const toggleIconPicker = useCallback(() => {
setShouldShowIconPicker((shouldShow) => !shouldShow)
}, [])
if (existingVault && application.vaultLocks.isVaultLocked(existingVault)) { if (existingVault && application.vaultLocks.isVaultLocked(existingVault)) {
return <div>Vault is locked.</div> return <div>Vault is locked.</div>
} }
@@ -198,15 +233,45 @@ const EditVaultModal: FunctionComponent<Props> = ({ onCloseDialog, existingVault
<div className="text-lg">Vault Info</div> <div className="text-lg">Vault Info</div>
<div className="mt-1">The vault name and description are end-to-end encrypted.</div> <div className="mt-1">The vault name and description are end-to-end encrypted.</div>
<DecoratedInput <div className="flex items-center mt-4 gap-4">
className={{ container: 'mt-4' }} <StyledTooltip className="!z-modal" label="Choose icon">
ref={nameInputRef} <Button className="!px-1.5" ref={iconPickerButtonRef} onClick={toggleIconPicker}>
value={name} <Icon type={iconString} />
placeholder="Vault Name" </Button>
onChange={(value) => { </StyledTooltip>
setName(value) <Popover
}} title="Choose icon"
/> open={shouldShowIconPicker}
anchorElement={iconPickerButtonRef.current}
togglePopover={toggleIconPicker}
align="start"
overrideZIndex="z-modal"
hideOnClickInModal
>
<div className="p-2">
<IconPicker
selectedValue={iconString || 'safe-square'}
onIconChange={(value?: VectorIconNameOrEmoji) => {
setIconString(value ?? 'safe-square')
toggleIconPicker()
}}
platform={application.platform}
useIconGrid={true}
/>
</div>
</Popover>
<DecoratedInput
className={{
container: 'flex-grow',
}}
ref={nameInputRef}
value={name}
placeholder="Vault Name"
onChange={(value) => {
setName(value)
}}
/>
</div>
<DecoratedInput <DecoratedInput
className={{ container: 'mt-4' }} className={{ container: 'mt-4' }}

View File

@@ -39,7 +39,7 @@ const ManyVaultSelectionMenu: FunctionComponent = () => {
checked={isVaultVisible(vault)} checked={isVaultVisible(vault)}
key={vault.uuid} key={vault.uuid}
> >
<Icon type="safe-square" className="mr-2 text-neutral" /> <Icon type={vault.iconString} className="mr-2 text-neutral" />
<div className="flex w-full items-center gap-1"> <div className="flex w-full items-center gap-1">
{vault.name} {vault.name}
{application.vaultLocks.isVaultLocked(vault) && <Icon className="ml-1" type="lock" size={'small'} />} {application.vaultLocks.isVaultLocked(vault) && <Icon className="ml-1" type="lock" size={'small'} />}

View File

@@ -90,7 +90,7 @@ const VaultMenu = ({ items }: { items: DecryptedItemInterface[] }) => {
)} )}
> >
<Icon <Icon
type="safe-square" type={vault.iconString}
size="large" size="large"
className={classNames('mr-2 text-neutral', doesVaultContainItems(vault) ? 'text-info' : '')} className={classNames('mr-2 text-neutral', doesVaultContainItems(vault) ? 'text-info' : '')}
/> />

View File

@@ -9,7 +9,7 @@ type Props = {
const VaultNameBadge: FunctionComponent<Props> = ({ vault }) => { const VaultNameBadge: FunctionComponent<Props> = ({ vault }) => {
return ( return (
<div title="Vault name" className="flex rounded bg-success px-1.5 py-1 text-success-contrast select-none"> <div title="Vault name" className="flex rounded bg-success px-1.5 py-1 text-success-contrast select-none">
<Icon ariaLabel="Shared in vault" type="safe-square" className="mr-1 text-info-contrast" size="medium" /> <Icon ariaLabel="Shared in vault" type={vault.iconString} className="mr-1 text-info-contrast" size="medium" />
<span className="mr-auto overflow-hidden text-ellipsis text-xs">{vault.name}</span> <span className="mr-auto overflow-hidden text-ellipsis text-xs">{vault.name}</span>
</div> </div>
) )