chore: only invalidate vault user cache when required (#2519)
This commit is contained in:
@@ -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()
|
||||||
|
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
export enum VaultUserServiceEvent {
|
export enum VaultUserServiceEvent {
|
||||||
UsersChanged = 'VaultUserServiceEvent.UsersChanged',
|
UsersChanged = 'VaultUserServiceEvent.UsersChanged',
|
||||||
|
InvalidatedAllUserCache = 'VaultUserServiceEvent.InvalidatedUserCache',
|
||||||
|
InvalidatedUserCacheForVault = 'VaultUserServiceEvent.InvalidatedUserCacheForVault',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user