chore: only invalidate vault user cache when required (#2519)

This commit is contained in:
Aman Harwara
2023-09-20 17:20:30 +05:30
committed by GitHub
parent 3a42925ca2
commit e2901d9535
8 changed files with 80 additions and 22 deletions

View File

@@ -179,7 +179,8 @@ export class VaultInviteService
this.removePendingInvite(pendingInvite.invite.uuid) this.removePendingInvite(pendingInvite.invite.uuid)
void this.sync.sync() this.sync.sync().catch(console.error)
this.vaultUsers.invalidateVaultUsersCache(pendingInvite.invite.shared_vault_uuid).catch(console.error)
await this._decryptErroredPayloads.execute() await this._decryptErroredPayloads.execute()

View File

@@ -12,16 +12,10 @@ import { AbstractService } from './../Service/AbstractService'
import { VaultUserServiceEvent } from './VaultUserServiceEvent' import { VaultUserServiceEvent } from './VaultUserServiceEvent'
import { Result } from '@standardnotes/domain-core' import { Result } from '@standardnotes/domain-core'
import { IsVaultOwner } from './UseCase/IsVaultOwner' import { IsVaultOwner } from './UseCase/IsVaultOwner'
import { InternalEventInterface } from '../Internal/InternalEventInterface'
import { InternalEventHandlerInterface } from '../Internal/InternalEventHandlerInterface'
import { ApplicationEvent } from '../Event/ApplicationEvent'
import { IsReadonlyVaultMember } from './UseCase/IsReadonlyVaultMember' import { IsReadonlyVaultMember } from './UseCase/IsReadonlyVaultMember'
import { IsVaultAdmin } from './UseCase/IsVaultAdmin' import { IsVaultAdmin } from './UseCase/IsVaultAdmin'
export class VaultUserService export class VaultUserService extends AbstractService<VaultUserServiceEvent> implements VaultUserServiceInterface {
extends AbstractService<VaultUserServiceEvent>
implements VaultUserServiceInterface, InternalEventHandlerInterface
{
constructor( constructor(
private vaults: VaultServiceInterface, private vaults: VaultServiceInterface,
private vaultLocks: VaultLockServiceInterface, private vaultLocks: VaultLockServiceInterface,
@@ -47,20 +41,28 @@ export class VaultUserService
;(this._leaveVault as unknown) = undefined ;(this._leaveVault as unknown) = undefined
} }
async handleEvent(event: InternalEventInterface): Promise<void> { async invalidateVaultUsersCache(sharedVaultUuid?: string) {
if (event.type === ApplicationEvent.CompletedFullSync) { if (sharedVaultUuid) {
this.vaults.getVaults().forEach((vault) => { await this._getVaultUsers.execute({
sharedVaultUuid: sharedVaultUuid,
readFromCache: false,
})
void this.notifyEvent(VaultUserServiceEvent.InvalidatedUserCacheForVault, sharedVaultUuid)
return
}
await Promise.all(
this.vaults.getVaults().map(async (vault) => {
if (!vault.isSharedVaultListing()) { if (!vault.isSharedVaultListing()) {
return return
} }
this._getVaultUsers await this._getVaultUsers.execute({
.execute({ sharedVaultUuid: vault.sharing.sharedVaultUuid,
sharedVaultUuid: vault.sharing.sharedVaultUuid, readFromCache: false,
readFromCache: false, })
}) void this.notifyEvent(VaultUserServiceEvent.InvalidatedUserCacheForVault, vault.sharing.sharedVaultUuid)
.catch(console.error) }),
}) )
} void this.notifyEvent(VaultUserServiceEvent.InvalidatedAllUserCache)
} }
public async getSharedVaultUsersFromServer( public async getSharedVaultUsersFromServer(

View File

@@ -1,3 +1,5 @@
export enum VaultUserServiceEvent { export enum VaultUserServiceEvent {
UsersChanged = 'VaultUserServiceEvent.UsersChanged', UsersChanged = 'VaultUserServiceEvent.UsersChanged',
InvalidatedAllUserCache = 'VaultUserServiceEvent.InvalidatedUserCache',
InvalidatedUserCacheForVault = 'VaultUserServiceEvent.InvalidatedUserCacheForVault',
} }

View File

@@ -15,4 +15,5 @@ export interface VaultUserServiceInterface extends ApplicationServiceInterface<V
leaveSharedVault(sharedVault: SharedVaultListingInterface): Promise<ClientDisplayableError | void> leaveSharedVault(sharedVault: SharedVaultListingInterface): Promise<ClientDisplayableError | void>
isVaultUserOwner(user: SharedVaultUserServerHash): boolean isVaultUserOwner(user: SharedVaultUserServerHash): boolean
getFormattedMemberPermission(permission: string): string getFormattedMemberPermission(permission: string): string
invalidateVaultUsersCache(sharedVaultUuid?: string): Promise<void>
} }

View File

@@ -469,6 +469,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
source: SyncSource.External, source: SyncSource.External,
sourceDescription: 'Application Launch', sourceDescription: 'Application Launch',
}) })
this.vaultUsers.invalidateVaultUsersCache().catch(console.error)
}) })
.catch((error) => { .catch((error) => {
void this.notifyEvent(ApplicationEvent.LocalDatabaseReadError, error) void this.notifyEvent(ApplicationEvent.LocalDatabaseReadError, error)

View File

@@ -43,7 +43,6 @@ export function RegisterApplicationServicesEvents(container: Dependencies, event
events.addEventHandler(container.get(TYPES.VaultInviteService), ApplicationEvent.Launched) events.addEventHandler(container.get(TYPES.VaultInviteService), ApplicationEvent.Launched)
events.addEventHandler(container.get(TYPES.VaultInviteService), SyncEvent.ReceivedSharedVaultInvites) events.addEventHandler(container.get(TYPES.VaultInviteService), SyncEvent.ReceivedSharedVaultInvites)
events.addEventHandler(container.get(TYPES.VaultInviteService), WebSocketsServiceEvent.UserInvitedToSharedVault) events.addEventHandler(container.get(TYPES.VaultInviteService), WebSocketsServiceEvent.UserInvitedToSharedVault)
events.addEventHandler(container.get(TYPES.VaultUserService), ApplicationEvent.CompletedFullSync)
if (container.get(TYPES.FilesBackupService)) { if (container.get(TYPES.FilesBackupService)) {
events.addEventHandler(container.get(TYPES.FilesBackupService), ApplicationEvent.ApplicationStageChanged) events.addEventHandler(container.get(TYPES.FilesBackupService), ApplicationEvent.ApplicationStageChanged)

View File

@@ -11,11 +11,35 @@ import Popover from '../Popover/Popover'
import FilePreviewInfoPanel from '../FilePreview/FilePreviewInfoPanel' import FilePreviewInfoPanel from '../FilePreview/FilePreviewInfoPanel'
import { useFileDragNDrop } from '../FileDragNDropProvider' import { useFileDragNDrop } from '../FileDragNDropProvider'
import RoundIconButton from '../Button/RoundIconButton' import RoundIconButton from '../Button/RoundIconButton'
import { useItemVaultInfo } from '@/Hooks/useItemVaultInfo'
import Icon from '../Icon/Icon'
import { VaultUserServiceEvent } from '@standardnotes/snjs'
const SyncTimeoutNoDebounceMs = 100 const SyncTimeoutNoDebounceMs = 100
const SyncTimeoutDebounceMs = 350 const SyncTimeoutDebounceMs = 350
const FileViewWithoutProtection = ({ application, file }: FileViewProps) => { const FileViewWithoutProtection = ({ application, file }: FileViewProps) => {
const { vault } = useItemVaultInfo(file)
const [isReadonly, setIsReadonly] = useState(false)
useEffect(() => {
if (!vault) {
return
}
setIsReadonly(application.vaultUsers.isCurrentUserReadonlyVaultMember(vault))
}, [application.vaultUsers, vault])
useEffect(() => {
return application.vaultUsers.addEventObserver((event, data) => {
if (event === VaultUserServiceEvent.InvalidatedUserCacheForVault) {
if ((data as string) !== vault?.sharing?.sharedVaultUuid) {
return
}
setIsReadonly(vault ? application.vaultUsers.isCurrentUserReadonlyVaultMember(vault) : false)
}
})
}, [application.vaultUsers, vault])
const syncTimeoutRef = useRef<number>() const syncTimeoutRef = useRef<number>()
const fileInfoButtonRef = useRef<HTMLButtonElement>(null) const fileInfoButtonRef = useRef<HTMLButtonElement>(null)
@@ -67,6 +91,12 @@ const FileViewWithoutProtection = ({ application, file }: FileViewProps) => {
return ( return (
<div className="sn-component section editor" aria-label="File" ref={fileDragTargetRef}> <div className="sn-component section editor" aria-label="File" ref={fileDragTargetRef}>
<div className="flex flex-col"> <div className="flex flex-col">
{isReadonly && (
<div className="bg-warning-faded relative flex items-center px-3.5 py-2 text-sm text-accessory-tint-3">
<Icon type="pencil-off" className="mr-3" />
This file is readonly
</div>
)}
<div <div
className="content-title-bar section-title-bar section-title-bar z-editor-title-bar w-full" className="content-title-bar section-title-bar section-title-bar z-editor-title-bar w-full"
id="file-title-bar" id="file-title-bar"
@@ -85,11 +115,12 @@ const FileViewWithoutProtection = ({ application, file }: FileViewProps) => {
spellCheck={false} spellCheck={false}
defaultValue={file.name} defaultValue={file.name}
autoComplete="off" autoComplete="off"
disabled={isReadonly}
/> />
</div> </div>
</div> </div>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<LinkedItemsButton linkingController={application.linkingController} /> {!isReadonly && <LinkedItemsButton linkingController={application.linkingController} />}
<RoundIconButton <RoundIconButton
label="File information panel" label="File information panel"
onClick={toggleFileInfoPanel} onClick={toggleFileInfoPanel}
@@ -110,7 +141,11 @@ const FileViewWithoutProtection = ({ application, file }: FileViewProps) => {
</div> </div>
</div> </div>
<div className="hidden md:flex"> <div className="hidden md:flex">
<LinkedItemBubblesContainer item={file} linkingController={application.linkingController} /> <LinkedItemBubblesContainer
item={file}
linkingController={application.linkingController}
readonly={isReadonly}
/>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -26,6 +26,7 @@ import {
PrefKey, PrefKey,
ProposedSecondsToDeferUILevelSessionExpirationDuringActiveInteraction, ProposedSecondsToDeferUILevelSessionExpirationDuringActiveInteraction,
SNNote, SNNote,
VaultUserServiceEvent,
} from '@standardnotes/snjs' } from '@standardnotes/snjs'
import { confirmDialog, DELETE_NOTE_KEYBOARD_COMMAND, KeyboardKey } from '@standardnotes/ui-services' import { confirmDialog, DELETE_NOTE_KEYBOARD_COMMAND, KeyboardKey } from '@standardnotes/ui-services'
import { ChangeEventHandler, createRef, CSSProperties, KeyboardEventHandler, RefObject } from 'react' import { ChangeEventHandler, createRef, CSSProperties, KeyboardEventHandler, RefObject } from 'react'
@@ -97,6 +98,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
private removeNoteStreamObserver?: () => void private removeNoteStreamObserver?: () => void
private removeComponentManagerObserver?: () => void private removeComponentManagerObserver?: () => void
private removeInnerNoteObserver?: () => void private removeInnerNoteObserver?: () => void
private removeVaultUsersEventHandler?: () => void
private protectionTimeoutId: ReturnType<typeof setTimeout> | null = null private protectionTimeoutId: ReturnType<typeof setTimeout> | null = null
private noteViewElementRef: RefObject<HTMLDivElement> private noteViewElementRef: RefObject<HTMLDivElement>
@@ -158,6 +160,9 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
this.removeTrashKeyObserver?.() this.removeTrashKeyObserver?.()
this.removeTrashKeyObserver = undefined this.removeTrashKeyObserver = undefined
this.removeVaultUsersEventHandler?.()
this.removeVaultUsersEventHandler = undefined
this.clearNoteProtectionInactivityTimer() this.clearNoteProtectionInactivityTimer()
;(this.ensureNoteIsInsertedBeforeUIAction as unknown) = undefined ;(this.ensureNoteIsInsertedBeforeUIAction as unknown) = undefined
@@ -211,6 +216,18 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
override componentDidMount(): void { override componentDidMount(): void {
super.componentDidMount() super.componentDidMount()
this.removeVaultUsersEventHandler = this.application.vaultUsers.addEventObserver((event, data) => {
if (event === VaultUserServiceEvent.InvalidatedUserCacheForVault) {
const vault = this.application.vaults.getItemVault(this.note)
if ((data as string) !== vault?.sharing?.sharedVaultUuid) {
return
}
this.setState({
readonly: vault ? this.application.vaultUsers.isCurrentUserReadonlyVaultMember(vault) : false,
})
}
})
this.registerKeyboardShortcuts() this.registerKeyboardShortcuts()
this.removeInnerNoteObserver = this.controller.addNoteInnerValueChangeObserver((note, source) => { this.removeInnerNoteObserver = this.controller.addNoteInnerValueChangeObserver((note, source) => {