mirror of https://github.com/vector-im/riot-web
Add reactions to html export (#28210)
* Absorb the matrix-react-sdk repository (#28192) Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com> Co-authored-by: github-merge-queue <github-merge-queue@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Florian Duros <florian.duros@ormaz.fr> Co-authored-by: Kim Brose <kim.brose@nordeck.net> Co-authored-by: Florian Duros <florianduros@element.io> Co-authored-by: R Midhun Suresh <hi@midhun.dev> Co-authored-by: dbkr <986903+dbkr@users.noreply.github.com> Co-authored-by: ElementRobot <releases@riot.im> Co-authored-by: dbkr <dbkr@users.noreply.github.com> Co-authored-by: David Baker <dbkr@users.noreply.github.com> Co-authored-by: Michael Telatynski <7t3chguy@gmail.com> Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Co-authored-by: David Langley <davidl@element.io> Co-authored-by: Michael Weimann <michaelw@matrix.org> Co-authored-by: Timshel <Timshel@users.noreply.github.com> Co-authored-by: Sahil Silare <32628578+sahil9001@users.noreply.github.com> Co-authored-by: Will Hunt <will@half-shot.uk> Co-authored-by: Hubert Chathi <hubert@uhoreg.ca> Co-authored-by: Andrew Ferrazzutti <andrewf@element.io> Co-authored-by: Robin <robin@robin.town> Co-authored-by: Tulir Asokan <tulir@maunium.net> * Update dependency @sentry/browser to v8.33.0 [SECURITY] (#28194) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update babel monorepo (#28196) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update dependency @types/react to v17.0.83 (#28138) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update dependency @matrix-org/spec to v1.12.0 (#28200) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update dependency @formatjs/intl-segmenter to v11.5.9 (#28197) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Remove references to `MatrixClient.crypto` (#28204) * Remove `VerificationExplorer` * Remove `remakeolm` slash command * Remove call to `crypto.cancelAndResendAllOutgoingKeyRequests` * Remove crypto mock in `LoginWithQR-test.tsx` * Remove `StopGadWidgetDriver.sendToDevice` * Remove remaining mock * Update dependency typescript to v5.6.3 (#28198) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update dependency eslint-plugin-unicorn to v56 (#28202) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update dependency stylelint to v16.10.0 (#28201) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update browserslist (#28199) * Update browserslist * Update tests Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Michael Telatynski <7t3chguy@gmail.com> * Add reactions to html export and add test * Add reaction to snapshot test * Update snapshot output * Remove logging * Add reaction to html export screenshot test. * lint * Update reference screenshot. --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> Co-authored-by: Michael Telatynski <7t3chguy@gmail.com> Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com> Co-authored-by: github-merge-queue <github-merge-queue@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Florian Duros <florian.duros@ormaz.fr> Co-authored-by: Kim Brose <kim.brose@nordeck.net> Co-authored-by: Florian Duros <florianduros@element.io> Co-authored-by: R Midhun Suresh <hi@midhun.dev> Co-authored-by: dbkr <986903+dbkr@users.noreply.github.com> Co-authored-by: ElementRobot <releases@riot.im> Co-authored-by: dbkr <dbkr@users.noreply.github.com> Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Co-authored-by: Michael Weimann <michaelw@matrix.org> Co-authored-by: Timshel <Timshel@users.noreply.github.com> Co-authored-by: Sahil Silare <32628578+sahil9001@users.noreply.github.com> Co-authored-by: Will Hunt <will@half-shot.uk> Co-authored-by: Hubert Chathi <hubert@uhoreg.ca> Co-authored-by: Andrew Ferrazzutti <andrewf@element.io> Co-authored-by: Robin <robin@robin.town> Co-authored-by: Tulir Asokan <tulir@maunium.net>pull/28007/head
parent
06d1239608
commit
59cd5180af
|
@ -96,7 +96,10 @@ test.describe("HTML Export", () => {
|
||||||
|
|
||||||
// Send a bunch of messages to populate the room
|
// Send a bunch of messages to populate the room
|
||||||
for (let i = 1; i < 10; i++) {
|
for (let i = 1; i < 10; i++) {
|
||||||
await app.client.sendMessage(room.roomId, { body: `Testing ${i}`, msgtype: "m.text" });
|
const respone = await app.client.sendMessage(room.roomId, { body: `Testing ${i}`, msgtype: "m.text" });
|
||||||
|
if (i == 1) {
|
||||||
|
await app.client.reactToMessage(room.roomId, null, respone.event_id, "🙃");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for all the messages to be displayed
|
// Wait for all the messages to be displayed
|
||||||
|
|
|
@ -222,14 +222,7 @@ export class MessageBuilder {
|
||||||
threadId: !ev.isThreadRoot ? ev.threadRootId : undefined,
|
threadId: !ev.isThreadRoot ? ev.threadRootId : undefined,
|
||||||
}));
|
}));
|
||||||
const roomId = await room.evaluate((room) => room.roomId);
|
const roomId = await room.evaluate((room) => room.roomId);
|
||||||
|
await bot.reactToMessage(roomId, threadId, id, reaction);
|
||||||
await bot.sendEvent(roomId, threadId ?? null, "m.reaction", {
|
|
||||||
"m.relates_to": {
|
|
||||||
rel_type: "m.annotation",
|
|
||||||
event_id: id,
|
|
||||||
key: reaction,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
})(this);
|
})(this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,6 +143,29 @@ export class Client {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a reaction to to a message
|
||||||
|
* @param roomId ID of the room to send the reaction into
|
||||||
|
* @param threadId ID of the thread to send into or null for main timeline
|
||||||
|
* @param eventId Event ID of the message you are reacting to
|
||||||
|
* @param reaction The reaction text to send
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public async reactToMessage(
|
||||||
|
roomId: string,
|
||||||
|
threadId: string | null,
|
||||||
|
eventId: string,
|
||||||
|
reaction: string,
|
||||||
|
): Promise<ISendEventResponse> {
|
||||||
|
return this.sendEvent(roomId, threadId ?? null, "m.reaction", {
|
||||||
|
"m.relates_to": {
|
||||||
|
rel_type: "m.annotation",
|
||||||
|
event_id: eventId,
|
||||||
|
key: reaction,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a room with given options.
|
* Create a room with given options.
|
||||||
* @param options the options to apply when creating the room
|
* @param options the options to apply when creating the room
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
|
@ -6,8 +6,8 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Direction, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
|
import { Direction, MatrixEvent, Relations, Room } from "matrix-js-sdk/src/matrix";
|
||||||
import { MediaEventContent } from "matrix-js-sdk/src/types";
|
import { EventType, MediaEventContent, RelationType } from "matrix-js-sdk/src/types";
|
||||||
import { saveAs } from "file-saver";
|
import { saveAs } from "file-saver";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
import sanitizeFilename from "sanitize-filename";
|
import sanitizeFilename from "sanitize-filename";
|
||||||
|
@ -284,5 +284,13 @@ export default abstract class Exporter {
|
||||||
return mxEv.getType() === attachmentTypes[0] || attachmentTypes.includes(mxEv.getContent().msgtype!);
|
return mxEv.getType() === attachmentTypes[0] || attachmentTypes.includes(mxEv.getContent().msgtype!);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected getRelationsForEvent = (
|
||||||
|
eventId: string,
|
||||||
|
relationType: RelationType | string,
|
||||||
|
eventType: EventType | string,
|
||||||
|
): Relations | undefined => {
|
||||||
|
return this.room.getUnfilteredTimelineSet().relations.getChildEventsForEvent(eventId, relationType, eventType);
|
||||||
|
};
|
||||||
|
|
||||||
public abstract export(): Promise<void>;
|
public abstract export(): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -288,9 +288,10 @@ export default class HTMLExporter extends Exporter {
|
||||||
permalinkCreator={this.permalinkCreator}
|
permalinkCreator={this.permalinkCreator}
|
||||||
lastSuccessful={false}
|
lastSuccessful={false}
|
||||||
isSelectedEvent={false}
|
isSelectedEvent={false}
|
||||||
showReactions={false}
|
showReactions={true}
|
||||||
layout={Layout.Group}
|
layout={Layout.Group}
|
||||||
showReadReceipts={false}
|
showReadReceipts={false}
|
||||||
|
getRelationsForEvent={this.getRelationsForEvent}
|
||||||
/>
|
/>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
</MatrixClientContext.Provider>
|
</MatrixClientContext.Provider>
|
||||||
|
|
|
@ -7,19 +7,24 @@ Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
EventTimeline,
|
||||||
|
EventTimelineSet,
|
||||||
EventType,
|
EventType,
|
||||||
IRoomEvent,
|
IRoomEvent,
|
||||||
MatrixClient,
|
MatrixClient,
|
||||||
MatrixEvent,
|
MatrixEvent,
|
||||||
MsgType,
|
MsgType,
|
||||||
|
Relations,
|
||||||
|
RelationType,
|
||||||
Room,
|
Room,
|
||||||
RoomMember,
|
RoomMember,
|
||||||
RoomState,
|
RoomState,
|
||||||
} from "matrix-js-sdk/src/matrix";
|
} from "matrix-js-sdk/src/matrix";
|
||||||
import fetchMock from "fetch-mock-jest";
|
import fetchMock from "fetch-mock-jest";
|
||||||
import escapeHtml from "escape-html";
|
import escapeHtml from "escape-html";
|
||||||
|
import { RelationsContainer } from "matrix-js-sdk/src/models/relations-container";
|
||||||
|
|
||||||
import { filterConsole, mkStubRoom, REPEATABLE_DATE, stubClient } from "../../../test-utils";
|
import { filterConsole, mkReaction, mkStubRoom, REPEATABLE_DATE, stubClient } from "../../../test-utils";
|
||||||
import { ExportType, IExportOptions } from "../../../../src/utils/exportUtils/exportUtils";
|
import { ExportType, IExportOptions } from "../../../../src/utils/exportUtils/exportUtils";
|
||||||
import SdkConfig from "../../../../src/SdkConfig";
|
import SdkConfig from "../../../../src/SdkConfig";
|
||||||
import HTMLExporter from "../../../../src/utils/exportUtils/HtmlExport";
|
import HTMLExporter from "../../../../src/utils/exportUtils/HtmlExport";
|
||||||
|
@ -123,6 +128,35 @@ describe("HTMLExport", () => {
|
||||||
fetchMock.get(media.srcHttp!, body);
|
fetchMock.get(media.srcHttp!, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mockReactionForMessage(message: IRoomEvent): MatrixEvent {
|
||||||
|
const firstMessage = new MatrixEvent(message);
|
||||||
|
const reaction = mkReaction(firstMessage);
|
||||||
|
|
||||||
|
const relationsContainer = {
|
||||||
|
getRelations: jest.fn(),
|
||||||
|
getChildEventsForEvent: jest.fn(),
|
||||||
|
} as unknown as RelationsContainer;
|
||||||
|
const relations = new Relations(RelationType.Annotation, EventType.Reaction, client);
|
||||||
|
relations.addEvent(reaction);
|
||||||
|
relationsContainer.getChildEventsForEvent = jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(
|
||||||
|
(eventId: string, relationType: RelationType | string, eventType: EventType | string) => {
|
||||||
|
if (eventId === firstMessage.getId()) {
|
||||||
|
return relations;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const timelineSet = {
|
||||||
|
relations: relationsContainer,
|
||||||
|
getLiveTimeline: () => timeline,
|
||||||
|
} as unknown as EventTimelineSet;
|
||||||
|
const timeline = new EventTimeline(timelineSet);
|
||||||
|
room.getUnfilteredTimelineSet = jest.fn().mockReturnValue(timelineSet);
|
||||||
|
return reaction;
|
||||||
|
}
|
||||||
|
|
||||||
it("should throw when created with invalid config for LastNMessages", async () => {
|
it("should throw when created with invalid config for LastNMessages", async () => {
|
||||||
expect(
|
expect(
|
||||||
() =>
|
() =>
|
||||||
|
@ -167,6 +201,7 @@ describe("HTMLExport", () => {
|
||||||
body: `Message #${i}`,
|
body: `Message #${i}`,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
mockReactionForMessage(events[0]);
|
||||||
mockMessages(...events);
|
mockMessages(...events);
|
||||||
|
|
||||||
const exporter = new HTMLExporter(
|
const exporter = new HTMLExporter(
|
||||||
|
@ -587,4 +622,24 @@ describe("HTMLExport", () => {
|
||||||
expect(await file.text()).toContain("testing testing");
|
expect(await file.text()).toContain("testing testing");
|
||||||
expect(client.createMessagesRequest).not.toHaveBeenCalled();
|
expect(client.createMessagesRequest).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should include reactions", async () => {
|
||||||
|
const reaction = mockReactionForMessage(EVENT_MESSAGE);
|
||||||
|
mockMessages(EVENT_MESSAGE);
|
||||||
|
const exporter = new HTMLExporter(
|
||||||
|
room,
|
||||||
|
ExportType.LastNMessages,
|
||||||
|
{
|
||||||
|
attachmentsIncluded: false,
|
||||||
|
maxSize: 1_024 * 1_024,
|
||||||
|
numberOfMessages: 40,
|
||||||
|
},
|
||||||
|
() => {},
|
||||||
|
);
|
||||||
|
|
||||||
|
await exporter.export();
|
||||||
|
|
||||||
|
const file = getMessageFile(exporter);
|
||||||
|
expect(await file.text()).toContain(reaction.getContent()["m.relates_to"]?.key);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue