diff --git a/apps/web/src/viewmodels/message-body/DecryptionFailureBodyViewModel.ts b/apps/web/src/viewmodels/message-body/DecryptionFailureBodyViewModel.ts index 1cf41425d6..0f89f78eb2 100644 --- a/apps/web/src/viewmodels/message-body/DecryptionFailureBodyViewModel.ts +++ b/apps/web/src/viewmodels/message-body/DecryptionFailureBodyViewModel.ts @@ -86,8 +86,6 @@ export class DecryptionFailureBodyViewModel } public setDecryptionFailureCode(decryptionFailureCode: DecryptionFailureCode | null): void { - if (this.props.decryptionFailureCode === decryptionFailureCode) return; - this.props.decryptionFailureCode = decryptionFailureCode; this.snapshot.merge({ decryptionFailureReason: DecryptionFailureBodyViewModel.getDecryptionReasonFromCode(decryptionFailureCode), diff --git a/apps/web/src/viewmodels/message-body/ReactionsRowViewModel.ts b/apps/web/src/viewmodels/message-body/ReactionsRowViewModel.ts index 3842b25c36..240ba85231 100644 --- a/apps/web/src/viewmodels/message-body/ReactionsRowViewModel.ts +++ b/apps/web/src/viewmodels/message-body/ReactionsRowViewModel.ts @@ -99,17 +99,10 @@ export class ReactionsRowViewModel const nextIsVisible = this.props.isActionable && this.props.reactionGroupCount > 0; const nextShowAllButtonVisible = this.props.reactionGroupCount > MAX_ITEMS_WHEN_LIMITED + 1 && !this.props.showAll; - const updates: Partial = {}; - - if (this.snapshot.current.isVisible !== nextIsVisible) { - updates.isVisible = nextIsVisible; - } - if (this.snapshot.current.showAllButtonVisible !== nextShowAllButtonVisible) { - updates.showAllButtonVisible = nextShowAllButtonVisible; - } - if (Object.keys(updates).length > 0) { - this.snapshot.merge(updates); - } + this.snapshot.merge({ + isVisible: nextIsVisible, + showAllButtonVisible: nextShowAllButtonVisible, + }); } public setCanReact(canReact: boolean): void { diff --git a/apps/web/src/viewmodels/message-body/RedactedBodyViewModel.ts b/apps/web/src/viewmodels/message-body/RedactedBodyViewModel.ts index 22553fdffa..4086c1db14 100644 --- a/apps/web/src/viewmodels/message-body/RedactedBodyViewModel.ts +++ b/apps/web/src/viewmodels/message-body/RedactedBodyViewModel.ts @@ -83,30 +83,15 @@ export class RedactedBodyViewModel } public setEvent(mxEvent: MatrixEvent): void { - if (this.props.mxEvent === mxEvent) return; - this.props = { ...this.props, mxEvent }; const text = RedactedBodyViewModel.computeText(this.props); const tooltip = RedactedBodyViewModel.computeTooltip(this.props.mxEvent, this.showTwelveHour); - const updates: Partial = {}; - - if (this.snapshot.current.text !== text) { - updates.text = text; - } - if (this.snapshot.current.tooltip !== tooltip) { - updates.tooltip = tooltip; - } - - if (Object.keys(updates).length > 0) { - this.snapshot.merge(updates); - } + this.snapshot.merge({ text, tooltip }); } private updateTooltip(): void { const tooltip = RedactedBodyViewModel.computeTooltip(this.props.mxEvent, this.showTwelveHour); - if (this.snapshot.current.tooltip !== tooltip) { - this.snapshot.merge({ tooltip }); - } + this.snapshot.merge({ tooltip }); } } diff --git a/apps/web/src/viewmodels/room-list/RoomListItemViewModel.ts b/apps/web/src/viewmodels/room-list/RoomListItemViewModel.ts index d60c8e5a6a..b2f24da66d 100644 --- a/apps/web/src/viewmodels/room-list/RoomListItemViewModel.ts +++ b/apps/web/src/viewmodels/room-list/RoomListItemViewModel.ts @@ -210,9 +210,7 @@ export class RoomListItemViewModel */ private async loadAndSetMessagePreview(): Promise { const messagePreview = await this.loadMessagePreview(); - if (messagePreview !== this.snapshot.current.messagePreview) { - this.snapshot.merge({ messagePreview }); - } + this.snapshot.merge({ messagePreview }); } /** diff --git a/apps/web/src/viewmodels/structures/ResizerViewModel.ts b/apps/web/src/viewmodels/structures/ResizerViewModel.ts index 10067ff9ae..10fa0a4193 100644 --- a/apps/web/src/viewmodels/structures/ResizerViewModel.ts +++ b/apps/web/src/viewmodels/structures/ResizerViewModel.ts @@ -98,6 +98,6 @@ export class ResizerViewModel }; public onBlur = (): void => { - if (this.getSnapshot().isFocusedViaKeyboard) this.snapshot.merge({ isFocusedViaKeyboard: false }); + this.snapshot.merge({ isFocusedViaKeyboard: false }); }; } diff --git a/docs/MVVM.md b/docs/MVVM.md index 96c46cbcab..2c65848e66 100644 --- a/docs/MVVM.md +++ b/docs/MVVM.md @@ -92,7 +92,7 @@ export function FooView({ vm }: FooViewProps): JSX.Element { 1. A View model is a class extending [`BaseViewModel`](https://github.com/element-hq/element-web/blob/develop/src/viewmodels/base/BaseViewModel.ts). 2. Implements the interface defined in the view (e.g `FooViewModel` in the example above). 3. View models define a snapshot type that defines the data the view will consume. The snapshot is immutable and can only be changed by calling `this.snapshot.set(...)` or `this.snapshot.merge(...)` in the view model. This will trigger a re-render in the view. -4. Call [`this.snapshot.merge(...)`](https://github.com/element-hq/element-web/blob/develop/packages/shared-components/src/viewmodel/Snapshot.ts#L32) to only update part of the snapshot. +4. Call [`this.snapshot.merge(...)`](https://github.com/element-hq/element-web/blob/develop/packages/shared-components/src/viewmodel/Snapshot.ts#L32) to only update part of the snapshot. `merge(...)` already skips emitting when the merged fields are unchanged, so avoid extra equality guards that only duplicate that check. 5. Avoid recomputing the entire snapshot when you only need to update a single field. For performance reasons, only recompute the fields that have actually changed. For example, if only `title` has changed, call `this.snapshot.merge({ title: newTitle })` rather than rebuilding the full snapshot object with all fields recomputed. 6. View models can have props which are passed in the constructor. Props are usually used to pass in dependencies (eg: stores, sdk, etc) that the view model needs to do its work. They can also be used to pass in initial values for the snapshot.