You've already forked matrix-react-sdk
							
							
				mirror of
				https://github.com/matrix-org/matrix-react-sdk.git
				synced 2025-11-03 00:33:22 +03:00 
			
		
		
		
	Allow reactions for broadcasts (#9856)
This commit is contained in:
		@@ -59,6 +59,7 @@ import { Action } from "../../../dispatcher/actions";
 | 
				
			|||||||
import { ShowThreadPayload } from "../../../dispatcher/payloads/ShowThreadPayload";
 | 
					import { ShowThreadPayload } from "../../../dispatcher/payloads/ShowThreadPayload";
 | 
				
			||||||
import useFavouriteMessages from "../../../hooks/useFavouriteMessages";
 | 
					import useFavouriteMessages from "../../../hooks/useFavouriteMessages";
 | 
				
			||||||
import { GetRelationsForEvent } from "../rooms/EventTile";
 | 
					import { GetRelationsForEvent } from "../rooms/EventTile";
 | 
				
			||||||
 | 
					import { VoiceBroadcastInfoEventType } from "../../../voice-broadcast/types";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface IOptionsButtonProps {
 | 
					interface IOptionsButtonProps {
 | 
				
			||||||
    mxEvent: MatrixEvent;
 | 
					    mxEvent: MatrixEvent;
 | 
				
			||||||
@@ -394,7 +395,8 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
 | 
				
			|||||||
             * until cross-platform support
 | 
					             * until cross-platform support
 | 
				
			||||||
             * (PSF-1041)
 | 
					             * (PSF-1041)
 | 
				
			||||||
             */
 | 
					             */
 | 
				
			||||||
            !M_BEACON_INFO.matches(this.props.mxEvent.getType());
 | 
					            !M_BEACON_INFO.matches(this.props.mxEvent.getType()) &&
 | 
				
			||||||
 | 
					            !(this.props.mxEvent.getType() === VoiceBroadcastInfoEventType);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return inNotThreadTimeline && isAllowedMessageType;
 | 
					        return inNotThreadTimeline && isAllowedMessageType;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,6 +19,7 @@ import { M_BEACON_INFO } from "matrix-js-sdk/src/@types/beacon";
 | 
				
			|||||||
import { MatrixEvent, MatrixClient } from "matrix-js-sdk/src/matrix";
 | 
					import { MatrixEvent, MatrixClient } from "matrix-js-sdk/src/matrix";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { getShareableLocationEventForBeacon } from "../../utils/beacon/getShareableLocation";
 | 
					import { getShareableLocationEventForBeacon } from "../../utils/beacon/getShareableLocation";
 | 
				
			||||||
 | 
					import { VoiceBroadcastInfoEventType } from "../../voice-broadcast/types";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Get forwardable event for a given event
 | 
					 * Get forwardable event for a given event
 | 
				
			||||||
@@ -29,6 +30,8 @@ export const getForwardableEvent = (event: MatrixEvent, cli: MatrixClient): Matr
 | 
				
			|||||||
        return null;
 | 
					        return null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (event.getType() === VoiceBroadcastInfoEventType) return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Live location beacons should forward their latest location as a static pin location
 | 
					    // Live location beacons should forward their latest location as a static pin location
 | 
				
			||||||
    // If the beacon is not live, or doesn't have a location forwarding is not allowed
 | 
					    // If the beacon is not live, or doesn't have a location forwarding is not allowed
 | 
				
			||||||
    if (M_BEACON_INFO.matches(event.getType())) {
 | 
					    if (M_BEACON_INFO.matches(event.getType())) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,6 +32,7 @@ import { TimelineRenderingType } from "../contexts/RoomContext";
 | 
				
			|||||||
import { launchPollEditor } from "../components/views/messages/MPollBody";
 | 
					import { launchPollEditor } from "../components/views/messages/MPollBody";
 | 
				
			||||||
import { Action } from "../dispatcher/actions";
 | 
					import { Action } from "../dispatcher/actions";
 | 
				
			||||||
import { ViewRoomPayload } from "../dispatcher/payloads/ViewRoomPayload";
 | 
					import { ViewRoomPayload } from "../dispatcher/payloads/ViewRoomPayload";
 | 
				
			||||||
 | 
					import { VoiceBroadcastInfoEventType, VoiceBroadcastInfoState } from "../voice-broadcast/types";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Returns whether an event should allow actions like reply, reactions, edit, etc.
 | 
					 * Returns whether an event should allow actions like reply, reactions, edit, etc.
 | 
				
			||||||
@@ -56,7 +57,9 @@ export function isContentActionable(mxEvent: MatrixEvent): boolean {
 | 
				
			|||||||
        } else if (
 | 
					        } else if (
 | 
				
			||||||
            mxEvent.getType() === "m.sticker" ||
 | 
					            mxEvent.getType() === "m.sticker" ||
 | 
				
			||||||
            M_POLL_START.matches(mxEvent.getType()) ||
 | 
					            M_POLL_START.matches(mxEvent.getType()) ||
 | 
				
			||||||
            M_BEACON_INFO.matches(mxEvent.getType())
 | 
					            M_BEACON_INFO.matches(mxEvent.getType()) ||
 | 
				
			||||||
 | 
					            (mxEvent.getType() === VoiceBroadcastInfoEventType &&
 | 
				
			||||||
 | 
					                mxEvent.getContent()?.state === VoiceBroadcastInfoState.Started)
 | 
				
			||||||
        ) {
 | 
					        ) {
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -42,14 +42,15 @@ import dispatcher from "../../../../src/dispatcher/dispatcher";
 | 
				
			|||||||
import SettingsStore from "../../../../src/settings/SettingsStore";
 | 
					import SettingsStore from "../../../../src/settings/SettingsStore";
 | 
				
			||||||
import { ReadPinsEventId } from "../../../../src/components/views/right_panel/types";
 | 
					import { ReadPinsEventId } from "../../../../src/components/views/right_panel/types";
 | 
				
			||||||
import { Action } from "../../../../src/dispatcher/actions";
 | 
					import { Action } from "../../../../src/dispatcher/actions";
 | 
				
			||||||
 | 
					import { mkVoiceBroadcastInfoStateEvent } from "../../../voice-broadcast/utils/test-utils";
 | 
				
			||||||
 | 
					import { VoiceBroadcastInfoState } from "../../../../src/voice-broadcast";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
jest.mock("../../../../src/utils/strings", () => ({
 | 
					jest.mock("../../../../src/utils/strings", () => ({
 | 
				
			||||||
    copyPlaintext: jest.fn(),
 | 
					    copyPlaintext: jest.fn(),
 | 
				
			||||||
    getSelectedText: jest.fn(),
 | 
					    getSelectedText: jest.fn(),
 | 
				
			||||||
}));
 | 
					}));
 | 
				
			||||||
jest.mock("../../../../src/utils/EventUtils", () => ({
 | 
					jest.mock("../../../../src/utils/EventUtils", () => ({
 | 
				
			||||||
    // @ts-ignore don't mock everything
 | 
					    ...(jest.requireActual("../../../../src/utils/EventUtils") as object),
 | 
				
			||||||
    ...jest.requireActual("../../../../src/utils/EventUtils"),
 | 
					 | 
				
			||||||
    canEditContent: jest.fn(),
 | 
					    canEditContent: jest.fn(),
 | 
				
			||||||
}));
 | 
					}));
 | 
				
			||||||
jest.mock("../../../../src/dispatcher/dispatcher");
 | 
					jest.mock("../../../../src/dispatcher/dispatcher");
 | 
				
			||||||
@@ -241,6 +242,17 @@ describe("MessageContextMenu", () => {
 | 
				
			|||||||
            expect(menu.find('div[aria-label="Forward"]')).toHaveLength(0);
 | 
					            expect(menu.find('div[aria-label="Forward"]')).toHaveLength(0);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it("should not allow forwarding a voice broadcast", () => {
 | 
				
			||||||
 | 
					            const broadcastStartEvent = mkVoiceBroadcastInfoStateEvent(
 | 
				
			||||||
 | 
					                roomId,
 | 
				
			||||||
 | 
					                VoiceBroadcastInfoState.Started,
 | 
				
			||||||
 | 
					                "@user:example.com",
 | 
				
			||||||
 | 
					                "ABC123",
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            const menu = createMenu(broadcastStartEvent);
 | 
				
			||||||
 | 
					            expect(menu.find('div[aria-label="Forward"]')).toHaveLength(0);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        describe("forwarding beacons", () => {
 | 
					        describe("forwarding beacons", () => {
 | 
				
			||||||
            const aliceId = "@alice:server.org";
 | 
					            const aliceId = "@alice:server.org";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,8 +15,7 @@ limitations under the License.
 | 
				
			|||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import React from "react";
 | 
					import React from "react";
 | 
				
			||||||
import { render, fireEvent } from "@testing-library/react";
 | 
					import { act, render, fireEvent } from "@testing-library/react";
 | 
				
			||||||
import { act } from "react-test-renderer";
 | 
					 | 
				
			||||||
import { EventType, EventStatus, MatrixEvent, MatrixEventEvent, MsgType, Room } from "matrix-js-sdk/src/matrix";
 | 
					import { EventType, EventStatus, MatrixEvent, MatrixEventEvent, MsgType, Room } from "matrix-js-sdk/src/matrix";
 | 
				
			||||||
import { FeatureSupport, Thread } from "matrix-js-sdk/src/models/thread";
 | 
					import { FeatureSupport, Thread } from "matrix-js-sdk/src/models/thread";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -34,6 +33,8 @@ import dispatcher from "../../../../src/dispatcher/dispatcher";
 | 
				
			|||||||
import SettingsStore from "../../../../src/settings/SettingsStore";
 | 
					import SettingsStore from "../../../../src/settings/SettingsStore";
 | 
				
			||||||
import { Action } from "../../../../src/dispatcher/actions";
 | 
					import { Action } from "../../../../src/dispatcher/actions";
 | 
				
			||||||
import { UserTab } from "../../../../src/components/views/dialogs/UserTab";
 | 
					import { UserTab } from "../../../../src/components/views/dialogs/UserTab";
 | 
				
			||||||
 | 
					import { mkVoiceBroadcastInfoStateEvent } from "../../../voice-broadcast/utils/test-utils";
 | 
				
			||||||
 | 
					import { VoiceBroadcastInfoState } from "../../../../src/voice-broadcast";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
jest.mock("../../../../src/dispatcher/dispatcher");
 | 
					jest.mock("../../../../src/dispatcher/dispatcher");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -405,6 +406,17 @@ describe("<MessageActionBar />", () => {
 | 
				
			|||||||
                expect(queryByLabelText("Reply in thread")).toBeTruthy();
 | 
					                expect(queryByLabelText("Reply in thread")).toBeTruthy();
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            it("does not render thread button for a voice broadcast", () => {
 | 
				
			||||||
 | 
					                const broadcastEvent = mkVoiceBroadcastInfoStateEvent(
 | 
				
			||||||
 | 
					                    roomId,
 | 
				
			||||||
 | 
					                    VoiceBroadcastInfoState.Started,
 | 
				
			||||||
 | 
					                    userId,
 | 
				
			||||||
 | 
					                    "ABC123",
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					                const { queryByLabelText } = getComponent({ mxEvent: broadcastEvent });
 | 
				
			||||||
 | 
					                expect(queryByLabelText("Reply in thread")).not.toBeInTheDocument();
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            it("opens user settings on click", () => {
 | 
					            it("opens user settings on click", () => {
 | 
				
			||||||
                jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
 | 
					                jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
 | 
				
			||||||
                const { getByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
 | 
					                const { getByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,6 +43,8 @@ import {
 | 
				
			|||||||
import { getMockClientWithEventEmitter, makeBeaconInfoEvent, makePollStartEvent, stubClient } from "../test-utils";
 | 
					import { getMockClientWithEventEmitter, makeBeaconInfoEvent, makePollStartEvent, stubClient } from "../test-utils";
 | 
				
			||||||
import dis from "../../src/dispatcher/dispatcher";
 | 
					import dis from "../../src/dispatcher/dispatcher";
 | 
				
			||||||
import { Action } from "../../src/dispatcher/actions";
 | 
					import { Action } from "../../src/dispatcher/actions";
 | 
				
			||||||
 | 
					import { mkVoiceBroadcastInfoStateEvent } from "../voice-broadcast/utils/test-utils";
 | 
				
			||||||
 | 
					import { VoiceBroadcastInfoState } from "../../src/voice-broadcast/types";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
jest.mock("../../src/dispatcher/dispatcher");
 | 
					jest.mock("../../src/dispatcher/dispatcher");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -151,6 +153,20 @@ describe("EventUtils", () => {
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const voiceBroadcastStart = mkVoiceBroadcastInfoStateEvent(
 | 
				
			||||||
 | 
					        "!room:example.com",
 | 
				
			||||||
 | 
					        VoiceBroadcastInfoState.Started,
 | 
				
			||||||
 | 
					        "@user:example.com",
 | 
				
			||||||
 | 
					        "ABC123",
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const voiceBroadcastStop = mkVoiceBroadcastInfoStateEvent(
 | 
				
			||||||
 | 
					        "!room:example.com",
 | 
				
			||||||
 | 
					        VoiceBroadcastInfoState.Stopped,
 | 
				
			||||||
 | 
					        "@user:example.com",
 | 
				
			||||||
 | 
					        "ABC123",
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    describe("isContentActionable()", () => {
 | 
					    describe("isContentActionable()", () => {
 | 
				
			||||||
        type TestCase = [string, MatrixEvent];
 | 
					        type TestCase = [string, MatrixEvent];
 | 
				
			||||||
        it.each<TestCase>([
 | 
					        it.each<TestCase>([
 | 
				
			||||||
@@ -161,6 +177,7 @@ describe("EventUtils", () => {
 | 
				
			|||||||
            ["room member event", roomMemberEvent],
 | 
					            ["room member event", roomMemberEvent],
 | 
				
			||||||
            ["event without msgtype", noMsgType],
 | 
					            ["event without msgtype", noMsgType],
 | 
				
			||||||
            ["event without content body property", noContentBody],
 | 
					            ["event without content body property", noContentBody],
 | 
				
			||||||
 | 
					            ["broadcast stop event", voiceBroadcastStop],
 | 
				
			||||||
        ])("returns false for %s", (_description, event) => {
 | 
					        ])("returns false for %s", (_description, event) => {
 | 
				
			||||||
            expect(isContentActionable(event)).toBe(false);
 | 
					            expect(isContentActionable(event)).toBe(false);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
@@ -171,6 +188,7 @@ describe("EventUtils", () => {
 | 
				
			|||||||
            ["event with empty content body", emptyContentBody],
 | 
					            ["event with empty content body", emptyContentBody],
 | 
				
			||||||
            ["event with a content body", niceTextMessage],
 | 
					            ["event with a content body", niceTextMessage],
 | 
				
			||||||
            ["beacon_info event", beaconInfoEvent],
 | 
					            ["beacon_info event", beaconInfoEvent],
 | 
				
			||||||
 | 
					            ["broadcast start event", voiceBroadcastStart],
 | 
				
			||||||
        ])("returns true for %s", (_description, event) => {
 | 
					        ])("returns true for %s", (_description, event) => {
 | 
				
			||||||
            expect(isContentActionable(event)).toBe(true);
 | 
					            expect(isContentActionable(event)).toBe(true);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,7 @@ import {
 | 
				
			|||||||
    VoiceBroadcastChunkEventType,
 | 
					    VoiceBroadcastChunkEventType,
 | 
				
			||||||
    VoiceBroadcastInfoEventType,
 | 
					    VoiceBroadcastInfoEventType,
 | 
				
			||||||
    VoiceBroadcastInfoState,
 | 
					    VoiceBroadcastInfoState,
 | 
				
			||||||
} from "../../../src/voice-broadcast";
 | 
					} from "../../../src/voice-broadcast/types";
 | 
				
			||||||
import { mkEvent } from "../../test-utils";
 | 
					import { mkEvent } from "../../test-utils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// timestamp incremented on each call to prevent duplicate timestamp
 | 
					// timestamp incremented on each call to prevent duplicate timestamp
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user