mirror of
https://github.com/vector-im/element-web.git
synced 2024-11-15 20:54:59 +08:00
Move reaction message previews out of labs (#10601)
* Update reaction message previews to match designs * Delabs reaction message previews * tsc strict * Iterate * Add test * Iterate
This commit is contained in:
parent
1f4d857283
commit
4dd214506b
@ -114,7 +114,7 @@ interface IProps extends MenuProps {
|
||||
// True if the menu is being used as a right click menu
|
||||
rightClick?: boolean;
|
||||
// The Relations model from the JS SDK for reactions to `mxEvent`
|
||||
reactions?: Relations | null | undefined;
|
||||
reactions?: Relations | null;
|
||||
// A permalink to this event or an href of an anchor element the user has clicked
|
||||
link?: string;
|
||||
|
||||
@ -556,7 +556,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
||||
}
|
||||
|
||||
let jumpToRelatedEventButton: JSX.Element | undefined;
|
||||
const relatedEventId = mxEvent.getWireContent()?.["m.relates_to"]?.event_id;
|
||||
const relatedEventId = mxEvent.relationEventId;
|
||||
if (relatedEventId && SettingsStore.getValue("developerMode")) {
|
||||
jumpToRelatedEventButton = (
|
||||
<IconizedContextMenuOption
|
||||
|
@ -915,7 +915,8 @@
|
||||
"%(senderName)s is calling": "%(senderName)s is calling",
|
||||
"* %(senderName)s %(emote)s": "* %(senderName)s %(emote)s",
|
||||
"%(senderName)s: %(message)s": "%(senderName)s: %(message)s",
|
||||
"%(senderName)s: %(reaction)s": "%(senderName)s: %(reaction)s",
|
||||
"You reacted %(reaction)s to %(message)s": "You reacted %(reaction)s to %(message)s",
|
||||
"%(sender)s reacted %(reaction)s to %(message)s": "%(sender)s reacted %(reaction)s to %(message)s",
|
||||
"%(senderName)s: %(stickerName)s": "%(senderName)s: %(stickerName)s",
|
||||
"Threads": "Threads",
|
||||
"Back to chat": "Back to chat",
|
||||
@ -937,7 +938,6 @@
|
||||
"Voice & Video": "Voice & Video",
|
||||
"Moderation": "Moderation",
|
||||
"Analytics": "Analytics",
|
||||
"Message Previews": "Message Previews",
|
||||
"Themes": "Themes",
|
||||
"Encryption": "Encryption",
|
||||
"Experimental": "Experimental",
|
||||
@ -963,8 +963,6 @@
|
||||
"New ways to ignore people": "New ways to ignore people",
|
||||
"Currently experimental.": "Currently experimental.",
|
||||
"Support adding custom themes": "Support adding custom themes",
|
||||
"Show message previews for reactions in DMs": "Show message previews for reactions in DMs",
|
||||
"Show message previews for reactions in all rooms": "Show message previews for reactions in all rooms",
|
||||
"Offline encrypted messaging using dehydrated devices": "Offline encrypted messaging using dehydrated devices",
|
||||
"Show current avatar and name for users in message history": "Show current avatar and name for users in message history",
|
||||
"Show HTML representation of room topics": "Show HTML representation of room topics",
|
||||
|
@ -84,7 +84,6 @@ export enum LabGroup {
|
||||
VoiceAndVideo,
|
||||
Moderation,
|
||||
Analytics,
|
||||
MessagePreviews,
|
||||
Themes,
|
||||
Encryption,
|
||||
Experimental,
|
||||
@ -105,7 +104,6 @@ export const labGroupNames: Record<LabGroup, string> = {
|
||||
[LabGroup.VoiceAndVideo]: _td("Voice & Video"),
|
||||
[LabGroup.Moderation]: _td("Moderation"),
|
||||
[LabGroup.Analytics]: _td("Analytics"),
|
||||
[LabGroup.MessagePreviews]: _td("Message Previews"),
|
||||
[LabGroup.Themes]: _td("Themes"),
|
||||
[LabGroup.Encryption]: _td("Encryption"),
|
||||
[LabGroup.Experimental]: _td("Experimental"),
|
||||
@ -298,22 +296,6 @@ export const SETTINGS: { [setting: string]: ISetting } = {
|
||||
supportedLevels: LEVELS_FEATURE,
|
||||
default: false,
|
||||
},
|
||||
"feature_roomlist_preview_reactions_dms": {
|
||||
isFeature: true,
|
||||
labsGroup: LabGroup.MessagePreviews,
|
||||
displayName: _td("Show message previews for reactions in DMs"),
|
||||
supportedLevels: LEVELS_FEATURE,
|
||||
default: false,
|
||||
// this option is a subset of `feature_roomlist_preview_reactions_all` so disable it when that one is enabled
|
||||
controller: new IncompatibleController("feature_roomlist_preview_reactions_all"),
|
||||
},
|
||||
"feature_roomlist_preview_reactions_all": {
|
||||
isFeature: true,
|
||||
labsGroup: LabGroup.MessagePreviews,
|
||||
displayName: _td("Show message previews for reactions in all rooms"),
|
||||
supportedLevels: LEVELS_FEATURE,
|
||||
default: false,
|
||||
},
|
||||
"feature_dehydration": {
|
||||
isFeature: true,
|
||||
labsGroup: LabGroup.Encryption,
|
||||
|
@ -18,37 +18,39 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
|
||||
import { IPreview } from "./IPreview";
|
||||
import { TagID } from "../models";
|
||||
import { getSenderName, isSelf, shouldPrefixMessagesIn } from "./utils";
|
||||
import { getSenderName, isSelf } from "./utils";
|
||||
import { _t } from "../../../languageHandler";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import DMRoomMap from "../../../utils/DMRoomMap";
|
||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
import { MessagePreviewStore } from "../MessagePreviewStore";
|
||||
|
||||
export class ReactionEventPreview implements IPreview {
|
||||
public getTextFor(event: MatrixEvent, tagId?: TagID, isThread?: boolean): string | null {
|
||||
const showDms = SettingsStore.getValue("feature_roomlist_preview_reactions_dms");
|
||||
const showAll = SettingsStore.getValue("feature_roomlist_preview_reactions_all");
|
||||
|
||||
const roomId = event.getRoomId();
|
||||
if (!roomId) return null; // not a room event
|
||||
|
||||
// If we're not showing all reactions, see if we're showing DMs instead
|
||||
if (!showAll) {
|
||||
// If we're not showing reactions on DMs, or we are and the room isn't a DM, skip
|
||||
if (!(showDms && DMRoomMap.shared().getUserIdForRoomId(roomId))) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const relation = event.getRelation();
|
||||
if (!relation) return null; // invalid reaction (probably redacted)
|
||||
|
||||
const reaction = relation.key;
|
||||
if (!reaction) return null; // invalid reaction (unknown format)
|
||||
|
||||
if (isThread || isSelf(event) || !shouldPrefixMessagesIn(roomId, tagId)) {
|
||||
return reaction;
|
||||
} else {
|
||||
return _t("%(senderName)s: %(reaction)s", { senderName: getSenderName(event), reaction });
|
||||
const cli = MatrixClientPeg.get();
|
||||
const room = cli?.getRoom(roomId);
|
||||
const relatedEvent = relation.event_id ? room?.findEventById(relation.event_id) : null;
|
||||
if (!relatedEvent) return null;
|
||||
|
||||
const message = MessagePreviewStore.instance.generatePreviewForEvent(relatedEvent);
|
||||
if (isSelf(event)) {
|
||||
return _t("You reacted %(reaction)s to %(message)s", {
|
||||
reaction,
|
||||
message,
|
||||
});
|
||||
}
|
||||
|
||||
return _t("%(sender)s reacted %(reaction)s to %(message)s", {
|
||||
sender: getSenderName(event),
|
||||
reaction,
|
||||
message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
import { DefaultTagID, TagID } from "../models";
|
||||
|
||||
export function isSelf(event: MatrixEvent): boolean {
|
||||
const selfUserId = MatrixClientPeg.get().getUserId();
|
||||
const selfUserId = MatrixClientPeg.get().getSafeUserId();
|
||||
if (event.getType() === "m.room.member") {
|
||||
return event.getStateKey() === selfUserId;
|
||||
}
|
||||
@ -37,5 +37,5 @@ export function shouldPrefixMessagesIn(roomId: string, tagId?: TagID): boolean {
|
||||
}
|
||||
|
||||
export function getSenderName(event: MatrixEvent): string {
|
||||
return event.sender ? event.sender.name : event.getSender() || "";
|
||||
return event.sender?.name ?? event.getSender() ?? "";
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ describe("<LabsUserSettingsTab />", () => {
|
||||
const { container } = render(getComponent());
|
||||
|
||||
const labsSections = container.getElementsByClassName("mx_SettingsTab_section");
|
||||
expect(labsSections).toHaveLength(12);
|
||||
expect(labsSections).toHaveLength(11);
|
||||
});
|
||||
|
||||
it("allow setting a labs flag which requires unstable support once support is confirmed", async () => {
|
||||
|
@ -22,6 +22,7 @@ import { makePollStartEvent } from "../../../test-utils";
|
||||
|
||||
jest.spyOn(MatrixClientPeg, "get").mockReturnValue({
|
||||
getUserId: () => "@me:example.com",
|
||||
getSafeUserId: () => "@me:example.com",
|
||||
} as unknown as MatrixClient);
|
||||
|
||||
describe("PollStartEventPreview", () => {
|
||||
|
139
test/stores/room-list/previews/ReactionEventPreview-test.ts
Normal file
139
test/stores/room-list/previews/ReactionEventPreview-test.ts
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { RelationType, Room, RoomMember } from "matrix-js-sdk/src/matrix";
|
||||
import { mocked } from "jest-mock";
|
||||
|
||||
import { mkEvent, stubClient } from "../../../test-utils";
|
||||
import { ReactionEventPreview } from "../../../../src/stores/room-list/previews/ReactionEventPreview";
|
||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||
|
||||
describe("ReactionEventPreview", () => {
|
||||
const preview = new ReactionEventPreview();
|
||||
const userId = "@user:example.com";
|
||||
const roomId = "!room:example.com";
|
||||
|
||||
beforeAll(() => {
|
||||
stubClient();
|
||||
});
|
||||
|
||||
describe("getTextFor", () => {
|
||||
it("should return null for non-relations", () => {
|
||||
const event = mkEvent({
|
||||
event: true,
|
||||
content: {},
|
||||
user: userId,
|
||||
type: "m.room.message",
|
||||
room: roomId,
|
||||
});
|
||||
expect(preview.getTextFor(event)).toBeNull();
|
||||
});
|
||||
|
||||
it("should return null for non-reactions", () => {
|
||||
const event = mkEvent({
|
||||
event: true,
|
||||
content: {
|
||||
"body": "",
|
||||
"m.relates_to": {
|
||||
rel_type: RelationType.Thread,
|
||||
event_id: "$foo:bar",
|
||||
},
|
||||
},
|
||||
user: userId,
|
||||
type: "m.room.message",
|
||||
room: roomId,
|
||||
});
|
||||
expect(preview.getTextFor(event)).toBeNull();
|
||||
});
|
||||
|
||||
it("should use 'You' for your own reactions", () => {
|
||||
const cli = MatrixClientPeg.get();
|
||||
const room = new Room(roomId, cli, userId);
|
||||
mocked(cli.getRoom).mockReturnValue(room);
|
||||
|
||||
const message = mkEvent({
|
||||
event: true,
|
||||
content: {
|
||||
"body": "duck duck goose",
|
||||
"m.relates_to": {
|
||||
rel_type: RelationType.Thread,
|
||||
event_id: "$foo:bar",
|
||||
},
|
||||
},
|
||||
user: userId,
|
||||
type: "m.room.message",
|
||||
room: roomId,
|
||||
});
|
||||
|
||||
room.getUnfilteredTimelineSet().addLiveEvent(message, {});
|
||||
|
||||
const event = mkEvent({
|
||||
event: true,
|
||||
content: {
|
||||
"m.relates_to": {
|
||||
rel_type: RelationType.Annotation,
|
||||
key: "🪿",
|
||||
event_id: message.getId(),
|
||||
},
|
||||
},
|
||||
user: cli.getSafeUserId(),
|
||||
type: "m.reaction",
|
||||
room: roomId,
|
||||
});
|
||||
expect(preview.getTextFor(event)).toMatchInlineSnapshot(`"You reacted 🪿 to duck duck goose"`);
|
||||
});
|
||||
|
||||
it("should use display name for your others' reactions", () => {
|
||||
const cli = MatrixClientPeg.get();
|
||||
const room = new Room(roomId, cli, userId);
|
||||
mocked(cli.getRoom).mockReturnValue(room);
|
||||
|
||||
const message = mkEvent({
|
||||
event: true,
|
||||
content: {
|
||||
"body": "duck duck goose",
|
||||
"m.relates_to": {
|
||||
rel_type: RelationType.Thread,
|
||||
event_id: "$foo:bar",
|
||||
},
|
||||
},
|
||||
user: userId,
|
||||
type: "m.room.message",
|
||||
room: roomId,
|
||||
});
|
||||
|
||||
room.getUnfilteredTimelineSet().addLiveEvent(message, {});
|
||||
|
||||
const event = mkEvent({
|
||||
event: true,
|
||||
content: {
|
||||
"m.relates_to": {
|
||||
rel_type: RelationType.Annotation,
|
||||
key: "🪿",
|
||||
event_id: message.getId(),
|
||||
},
|
||||
},
|
||||
user: userId,
|
||||
type: "m.reaction",
|
||||
room: roomId,
|
||||
});
|
||||
event.sender = new RoomMember(roomId, userId);
|
||||
event.sender.name = "Bob";
|
||||
|
||||
expect(preview.getTextFor(event)).toMatchInlineSnapshot(`"Bob reacted 🪿 to duck duck goose"`);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user