Refactor MessageActionBar using MVVM and move to shared-components (#32784)
* Refactor MessageActionBar into MVVM ActionBarView * Adding tooltips for menu items and correct i18n strings * Layout changes * Renaming some properties * Rename property * Create a first version of the view model and refactor media visibility logic * Refactor view to take options and rections menu as optional properties * Cleaner interface between view and view model * Refactor view properties and replace Menu and MenuItem * Bugfixes and switching to ActionBarView instead of MessageActionBar in element-web * Avoid creating view models and render toolbar until it is actually shown * Added unit and playwright tests and documented the view * Added view model unit tests and updated snapshots of dependant tests * Remove unused components and unnecessary css * Remove unused language tags * Fix for handling join-rules correctly * Prettier * Add handling of stale view model in async calls * Prettier * Split the element-web css into two different. One for legacy components and one for the ActionBarView * Missing variables used for linting * Fix for showing ActionBarView when using keyboard for navigation * Handle visibility on context menu closing * ThreadPanel uses the ActionBarView so restore css rule * Fix for visibility of the ActionBarView in Thread panel * Fix for ActionBarVuew visibility when closing right-click context menu and not still hovering * Add roving index to function as a toolbar * Adjust the RoomView test to send hover to the EventTile instead of the message text * Fix SonarCloud issues * Fix for SonarCloud issue * Merge fix * Rename mx_LegacyActionBar to mx_ThreadActionBar * Added documentation and simplified join rules * Generalize the ActionBarView and move logic to view model * Add the four new buttons to the ActionBarView * Update view model and tests to use the updated ActionBarView * Refactor element-web to use ActionBarView * Clean up styling in element-web * Clean up and updating snaps and screenshots * Added unit-tests for better coverage * Moving ActionBarView to the correct folder in shared components * Update snaps in element-web * Better documentation in stories * Merge fixes * Updates after review comments * Review comment fixes * Added documentation to view models and updated snaps * Hide button had the wrong label * Replace createRef with useRef
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
Copyright 2026 Element Creations Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { JoinRule, type MatrixClient, type MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { type MediaPreviewConfig, MediaPreviewValue } from "../../@types/media_preview";
|
||||
import { SettingLevel } from "../../settings/SettingLevel";
|
||||
import SettingsStore from "../../settings/SettingsStore";
|
||||
|
||||
/**
|
||||
* Determine whether a room should be treated as private when applying media preview defaults.
|
||||
*
|
||||
* @param client - Matrix client used to resolve the room and its current join rule.
|
||||
* @param roomId - Room to inspect. If omitted or unknown, the room is treated as non-private.
|
||||
* @returns `true` when the room's join rule restricts membership, otherwise `false`.
|
||||
*/
|
||||
function isRoomPrivate(client: MatrixClient, roomId?: string): boolean {
|
||||
const room = roomId ? client.getRoom(roomId) : undefined;
|
||||
const joinRule = room?.currentState.getJoinRule();
|
||||
|
||||
switch (joinRule) {
|
||||
case JoinRule.Invite:
|
||||
case JoinRule.Knock:
|
||||
case JoinRule.Restricted:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve whether media for a single event should be shown.
|
||||
*
|
||||
* Precedence is:
|
||||
* 1. An explicit per-event override stored in `showMediaEventIds`
|
||||
* 2. Always show media in events sent by the current user
|
||||
* 3. Fall back to the room-level `mediaPreviewConfig` policy
|
||||
*
|
||||
* @param mediaPreviewSetting - Effective room-level media preview configuration.
|
||||
* @param eventVisibility - Per-event visibility overrides keyed by event ID.
|
||||
* @param userId - Current user ID, used to always show media sent by the local user.
|
||||
* @param eventId - Event being evaluated. Used to look up any explicit override.
|
||||
* @param sender - Sender of the event being evaluated.
|
||||
* @param roomIsPrivate - Whether the event's room should use the private-room preview behavior.
|
||||
* @returns `true` when media should be displayed for the event, otherwise `false`.
|
||||
*/
|
||||
export function computeMediaVisibility(
|
||||
mediaPreviewSetting: MediaPreviewConfig,
|
||||
eventVisibility: Record<string, boolean>,
|
||||
userId: string | undefined,
|
||||
eventId: string | undefined,
|
||||
sender: string | undefined,
|
||||
roomIsPrivate: boolean,
|
||||
): boolean {
|
||||
const explicitEventVisibility = eventId ? eventVisibility[eventId] : undefined;
|
||||
|
||||
if (explicitEventVisibility !== undefined) {
|
||||
return explicitEventVisibility;
|
||||
}
|
||||
|
||||
if (sender === userId) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (mediaPreviewSetting.media_previews) {
|
||||
case MediaPreviewValue.Off:
|
||||
return false;
|
||||
case MediaPreviewValue.On:
|
||||
return true;
|
||||
case MediaPreviewValue.Private:
|
||||
return roomIsPrivate;
|
||||
default:
|
||||
console.warn("Invalid media visibility setting", mediaPreviewSetting.media_previews);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the effective media visibility for a Matrix event using the current settings state.
|
||||
*
|
||||
* @param mxEvent - Event whose media visibility should be evaluated.
|
||||
* @param client - Matrix client used to resolve the current user and room metadata.
|
||||
* @returns `true` when media should be shown for the event, otherwise `false`.
|
||||
*/
|
||||
export function getMediaVisibility(mxEvent: MatrixEvent, client: MatrixClient): boolean {
|
||||
const eventId = mxEvent.getId();
|
||||
const roomId = mxEvent.getRoomId();
|
||||
const mediaPreviewSetting = SettingsStore.getValue("mediaPreviewConfig", roomId);
|
||||
const eventVisibility = SettingsStore.getValue("showMediaEventIds");
|
||||
|
||||
return computeMediaVisibility(
|
||||
mediaPreviewSetting,
|
||||
eventVisibility,
|
||||
client.getUserId() ?? undefined,
|
||||
eventId,
|
||||
mxEvent.getSender(),
|
||||
isRoomPrivate(client, roomId),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist a per-event override for whether media should be displayed on this device.
|
||||
*
|
||||
* @param mxEvent - Event whose media visibility override should be updated.
|
||||
* @param visible - Whether media for the event should be shown.
|
||||
* @returns A promise that resolves once the device-scoped setting has been updated.
|
||||
*/
|
||||
export async function setMediaVisibility(mxEvent: MatrixEvent, visible: boolean): Promise<void> {
|
||||
const eventId = mxEvent.getId();
|
||||
if (!eventId) return;
|
||||
|
||||
const eventVisibility = SettingsStore.getValue("showMediaEventIds");
|
||||
|
||||
await SettingsStore.setValue("showMediaEventIds", null, SettingLevel.DEVICE, {
|
||||
...eventVisibility,
|
||||
[eventId]: visible,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user