From cb4e444a60686d2dcd00eabb5c8743f1bc7b5f25 Mon Sep 17 00:00:00 2001 From: Michael Weimann Date: Tue, 3 Jan 2023 10:02:28 +0100 Subject: [PATCH 1/7] Mute broadcast chunk notifications --- src/Notifier.ts | 8 ++++++++ test/Notifier-test.ts | 41 +++++++++++++++++++++++++++++++++++++++ test/test-utils/client.ts | 1 + 3 files changed, 50 insertions(+) diff --git a/src/Notifier.ts b/src/Notifier.ts index dd624e2dc7..7e1f9eb0a4 100644 --- a/src/Notifier.ts +++ b/src/Notifier.ts @@ -50,6 +50,7 @@ import { localNotificationsAreSilenced, createLocalNotificationSettingsIfNeeded import { getIncomingCallToastKey, IncomingCallToast } from "./toasts/IncomingCallToast"; import ToastStore from "./stores/ToastStore"; import { ElementCall } from "./models/Call"; +import { VoiceBroadcastChunkEventType } from "./voice-broadcast"; /* * Dispatches: @@ -77,6 +78,13 @@ const msgTypeHandlers = { [M_LOCATION.altName]: (event: MatrixEvent) => { return TextForEvent.textForLocationEvent(event)(); }, + [MsgType.Audio]: (event: MatrixEvent): string | null => { + if (event.getContent()?.[VoiceBroadcastChunkEventType]) { + // mute broadcast chunks + return null; + } + return TextForEvent.textForEvent(event); + }, }; export const Notifier = { diff --git a/test/Notifier-test.ts b/test/Notifier-test.ts index bfa1e1d1f3..36021e6f6f 100644 --- a/test/Notifier-test.ts +++ b/test/Notifier-test.ts @@ -20,6 +20,7 @@ import { Room, RoomEvent } from "matrix-js-sdk/src/models/room"; import { IContent, MatrixEvent } from "matrix-js-sdk/src/models/event"; import { SyncState } from "matrix-js-sdk/src/sync"; import { waitFor } from "@testing-library/react"; +import { EventType, MsgType } from "matrix-js-sdk/src/matrix"; import BasePlatform from "../src/BasePlatform"; import { ElementCall } from "../src/models/Call"; @@ -39,6 +40,7 @@ import { mkThread } from "./test-utils/threads"; import dis from "../src/dispatcher/dispatcher"; import { ThreadPayload } from "../src/dispatcher/payloads/ThreadPayload"; import { Action } from "../src/dispatcher/actions"; +import { VoiceBroadcastChunkEventType } from "../src/voice-broadcast"; jest.mock("../src/utils/notifications", () => ({ // @ts-ignore @@ -73,6 +75,22 @@ describe("Notifier", () => { }); }; + const mkAudioEvent = (broadcastChunk = false): MatrixEvent => { + const chunkContent = broadcastChunk ? { [VoiceBroadcastChunkEventType]: {} } : {}; + + return mkEvent({ + event: true, + type: EventType.RoomMessage, + user: "@user:example.com", + room: "!room:example.com", + content: { + ...chunkContent, + msgtype: MsgType.Audio, + body: "test audio message", + }, + }); + }; + beforeEach(() => { accountDataStore = {}; mockClient = getMockClientWithEventEmitter({ @@ -256,6 +274,24 @@ describe("Notifier", () => { Notifier._displayPopupNotification(testEvent, testRoom); expect(MockPlatform.displayNotification).toHaveBeenCalledTimes(count); }); + + it("should display a notification for a voice message", () => { + const audioEvent = mkAudioEvent(); + Notifier._displayPopupNotification(audioEvent, testRoom); + expect(MockPlatform.displayNotification).toHaveBeenCalledWith( + "@user:example.com (!room1:server)", + "@user:example.com: test audio message", + "data:image/png;base64,00", + testRoom, + audioEvent, + ); + }); + + it("should not display a notification for a broadcast chunk", () => { + const audioEvent = mkAudioEvent(true); + Notifier._displayPopupNotification(audioEvent, testRoom); + expect(MockPlatform.displayNotification).not.toHaveBeenCalled(); + }); }); describe("getSoundForRoom", () => { @@ -456,6 +492,11 @@ describe("Notifier", () => { Notifier._evaluateEvent(events[1]); expect(Notifier._displayPopupNotification).toHaveBeenCalledTimes(1); }); + + it("should show a pop-up for an audio message", () => { + Notifier._evaluateEvent(mkAudioEvent()); + expect(Notifier._displayPopupNotification).toHaveBeenCalledTimes(1); + }); }); describe("setPromptHidden", () => { diff --git a/test/test-utils/client.ts b/test/test-utils/client.ts index 47e037b8fd..47040dcb4b 100644 --- a/test/test-utils/client.ts +++ b/test/test-utils/client.ts @@ -92,6 +92,7 @@ export const unmockClientPeg = () => jest.spyOn(MatrixClientPeg, "get").mockRest */ export const mockClientMethodsUser = (userId = "@alice:domain") => ({ getUserId: jest.fn().mockReturnValue(userId), + getSafeUserId: jest.fn().mockReturnValue(userId), getUser: jest.fn().mockReturnValue(new User(userId)), isGuest: jest.fn().mockReturnValue(false), mxcUrlToHttp: jest.fn().mockReturnValue("mock-mxcUrlToHttp"), From b79912e390a6fb0b70e46317228b7c0db8c22bef Mon Sep 17 00:00:00 2001 From: Michael Weimann Date: Tue, 3 Jan 2023 10:16:46 +0100 Subject: [PATCH 2/7] Improve strict typing --- src/Modal.tsx | 4 ++-- test/Notifier-test.ts | 14 ++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Modal.tsx b/src/Modal.tsx index 290c082344..c9f737f9ae 100644 --- a/src/Modal.tsx +++ b/src/Modal.tsx @@ -107,8 +107,8 @@ export class ModalManager extends TypedEventEmitter 0; + public hasDialogs(): boolean { + return !!(this.priorityModal || this.staticModal || this.modals.length > 0); } public createDialog( diff --git a/test/Notifier-test.ts b/test/Notifier-test.ts index 36021e6f6f..a3b1f9b2db 100644 --- a/test/Notifier-test.ts +++ b/test/Notifier-test.ts @@ -112,11 +112,11 @@ describe("Notifier", () => { }); mockClient.pushRules = { - global: undefined, + global: {}, }; - accountDataEventKey = getLocalNotificationAccountDataEventType(mockClient.deviceId); + accountDataEventKey = getLocalNotificationAccountDataEventType(mockClient.deviceId!); - testRoom = new Room(roomId, mockClient, mockClient.getUserId()); + testRoom = new Room(roomId, mockClient, mockClient.getSafeUserId()); MockPlatform = mockPlatformPeg({ supportsNotifications: jest.fn().mockReturnValue(true), @@ -127,8 +127,10 @@ describe("Notifier", () => { Notifier.isBodyEnabled = jest.fn().mockReturnValue(true); - mockClient.getRoom.mockImplementation((id) => { - return id === roomId ? testRoom : new Room(id, mockClient, mockClient.getUserId()); + mockClient.getRoom.mockImplementation((id: string | undefined): Room | null => { + if (id === roomId) return testRoom; + if (id) return new Room(id, mockClient, mockClient.getSafeUserId()); + return null; }); }); @@ -484,7 +486,7 @@ describe("Notifier", () => { dis.dispatch({ action: Action.ViewThread, - thread_id: rootEvent.getId(), + thread_id: rootEvent.getId()!, }); await waitFor(() => expect(SdkContextClass.instance.roomViewStore.getThreadId()).toBe(rootEvent.getId())); From f2178bd164f2d585d56ba82e2729774e64f37c6b Mon Sep 17 00:00:00 2001 From: Michael Weimann Date: Tue, 3 Jan 2023 15:59:08 +0100 Subject: [PATCH 3/7] Roll back strict type improvements --- src/Modal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Modal.tsx b/src/Modal.tsx index c9f737f9ae..290c082344 100644 --- a/src/Modal.tsx +++ b/src/Modal.tsx @@ -107,8 +107,8 @@ export class ModalManager extends TypedEventEmitter 0); + public hasDialogs() { + return this.priorityModal || this.staticModal || this.modals.length > 0; } public createDialog( From ab9152044c03469cc583680d7b63a26e64dd4fc6 Mon Sep 17 00:00:00 2001 From: Michael Weimann Date: Thu, 5 Jan 2023 17:05:21 +0100 Subject: [PATCH 4/7] Use the same avatar colour when creating 1:1 DM rooms (#9850) --- src/components/views/avatars/RoomAvatar.tsx | 24 ++++-- .../structures/auth/ForgotPassword-test.tsx | 16 ++-- .../views/avatars/RoomAvatar-test.tsx | 78 +++++++++++++++++++ .../__snapshots__/RoomAvatar-test.tsx.snap | 76 ++++++++++++++++++ test/components/views/rooms/RoomTile-test.tsx | 12 ++- .../UserOnboardingPage-test.tsx | 16 +--- test/test-utils/console.ts | 45 ++++++----- .../VoiceBroadcastRecordingPip-test.tsx | 8 +- 8 files changed, 215 insertions(+), 60 deletions(-) create mode 100644 test/components/views/avatars/RoomAvatar-test.tsx create mode 100644 test/components/views/avatars/__snapshots__/RoomAvatar-test.tsx.snap diff --git a/src/components/views/avatars/RoomAvatar.tsx b/src/components/views/avatars/RoomAvatar.tsx index 383edd0792..770ea01fce 100644 --- a/src/components/views/avatars/RoomAvatar.tsx +++ b/src/components/views/avatars/RoomAvatar.tsx @@ -29,6 +29,7 @@ import * as Avatar from "../../../Avatar"; import DMRoomMap from "../../../utils/DMRoomMap"; import { mediaFromMxc } from "../../../customisations/Media"; import { IOOBData } from "../../../stores/ThreepidInviteStore"; +import { LocalRoom } from "../../../models/LocalRoom"; interface IProps extends Omit, "name" | "idName" | "url" | "onClick"> { // Room may be left unset here, but if it is, @@ -117,13 +118,26 @@ export default class RoomAvatar extends React.Component { Modal.createDialog(ImageView, params, "mx_Dialog_lightbox", null, true); }; + private get roomIdName(): string | undefined { + const room = this.props.room; + + if (room) { + const dmMapUserId = DMRoomMap.shared().getUserIdForRoomId(room.roomId); + // If the room is a DM, we use the other user's ID for the color hash + // in order to match the room avatar with their avatar + if (dmMapUserId) return dmMapUserId; + + if (room instanceof LocalRoom && room.targets.length === 1) { + return room.targets[0].userId; + } + } + + return this.props.room?.roomId || this.props.oobData?.roomId; + } + public render() { const { room, oobData, viewAvatarOnClick, onClick, className, ...otherProps } = this.props; - const roomName = room?.name ?? oobData.name; - // If the room is a DM, we use the other user's ID for the color hash - // in order to match the room avatar with their avatar - const idName = room ? DMRoomMap.shared().getUserIdForRoomId(room.roomId) ?? room.roomId : oobData.roomId; return ( { mx_RoomAvatar_isSpaceRoom: (room?.getType() ?? this.props.oobData?.roomType) === RoomType.Space, })} name={roomName} - idName={idName} + idName={this.roomIdName} urls={this.state.urls} onClick={viewAvatarOnClick && this.state.urls[0] ? this.onRoomAvatarClick : onClick} /> diff --git a/test/components/structures/auth/ForgotPassword-test.tsx b/test/components/structures/auth/ForgotPassword-test.tsx index 2c348f7efd..57eccec014 100644 --- a/test/components/structures/auth/ForgotPassword-test.tsx +++ b/test/components/structures/auth/ForgotPassword-test.tsx @@ -40,7 +40,6 @@ describe("", () => { let onComplete: () => void; let onLoginClick: () => void; let renderResult: RenderResult; - let restoreConsole: () => void; const typeIntoField = async (label: string, value: string): Promise => { await act(async () => { @@ -63,14 +62,14 @@ describe("", () => { }); }; - beforeEach(() => { - restoreConsole = filterConsole( - // not implemented by js-dom https://github.com/jsdom/jsdom/issues/1937 - "Not implemented: HTMLFormElement.prototype.requestSubmit", - // not of interested for this test - "Starting load of AsyncWrapper for modal", - ); + filterConsole( + // not implemented by js-dom https://github.com/jsdom/jsdom/issues/1937 + "Not implemented: HTMLFormElement.prototype.requestSubmit", + // not of interested for this test + "Starting load of AsyncWrapper for modal", + ); + beforeEach(() => { client = stubClient(); mocked(createClient).mockReturnValue(client); @@ -87,7 +86,6 @@ describe("", () => { afterEach(() => { // clean up modals Modal.closeCurrentModal("force"); - restoreConsole?.(); }); beforeAll(() => { diff --git a/test/components/views/avatars/RoomAvatar-test.tsx b/test/components/views/avatars/RoomAvatar-test.tsx new file mode 100644 index 0000000000..e23cd96f02 --- /dev/null +++ b/test/components/views/avatars/RoomAvatar-test.tsx @@ -0,0 +1,78 @@ +/* +Copyright 2022 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 React from "react"; +import { render } from "@testing-library/react"; +import { MatrixClient, Room } from "matrix-js-sdk/src/matrix"; +import { mocked } from "jest-mock"; + +import RoomAvatar from "../../../../src/components/views/avatars/RoomAvatar"; +import { filterConsole, stubClient } from "../../../test-utils"; +import DMRoomMap from "../../../../src/utils/DMRoomMap"; +import { LocalRoom } from "../../../../src/models/LocalRoom"; +import * as AvatarModule from "../../../../src/Avatar"; +import { DirectoryMember } from "../../../../src/utils/direct-messages"; + +describe("RoomAvatar", () => { + let client: MatrixClient; + + filterConsole( + // unrelated for this test + "Room !room:example.com does not have an m.room.create event", + ); + + beforeAll(() => { + client = stubClient(); + const dmRoomMap = new DMRoomMap(client); + jest.spyOn(dmRoomMap, "getUserIdForRoomId"); + jest.spyOn(DMRoomMap, "shared").mockReturnValue(dmRoomMap); + jest.spyOn(AvatarModule, "defaultAvatarUrlForString"); + }); + + afterAll(() => { + jest.restoreAllMocks(); + }); + + afterEach(() => { + mocked(DMRoomMap.shared().getUserIdForRoomId).mockReset(); + mocked(AvatarModule.defaultAvatarUrlForString).mockClear(); + }); + + it("should render as expected for a Room", () => { + const room = new Room("!room:example.com", client, client.getSafeUserId()); + room.name = "test room"; + expect(render().container).toMatchSnapshot(); + expect(AvatarModule.defaultAvatarUrlForString).toHaveBeenCalledWith(room.roomId); + }); + + it("should render as expected for a DM room", () => { + const userId = "@dm_user@example.com"; + const room = new Room("!room:example.com", client, client.getSafeUserId()); + room.name = "DM room"; + mocked(DMRoomMap.shared().getUserIdForRoomId).mockReturnValue(userId); + expect(render().container).toMatchSnapshot(); + expect(AvatarModule.defaultAvatarUrlForString).toHaveBeenCalledWith(userId); + }); + + it("should render as expected for a LocalRoom", () => { + const userId = "@local_room_user@example.com"; + const localRoom = new LocalRoom("!room:example.com", client, client.getSafeUserId()); + localRoom.name = "local test room"; + localRoom.targets.push(new DirectoryMember({ user_id: userId })); + expect(render().container).toMatchSnapshot(); + expect(AvatarModule.defaultAvatarUrlForString).toHaveBeenCalledWith(userId); + }); +}); diff --git a/test/components/views/avatars/__snapshots__/RoomAvatar-test.tsx.snap b/test/components/views/avatars/__snapshots__/RoomAvatar-test.tsx.snap new file mode 100644 index 0000000000..6bffa157b6 --- /dev/null +++ b/test/components/views/avatars/__snapshots__/RoomAvatar-test.tsx.snap @@ -0,0 +1,76 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RoomAvatar should render as expected for a DM room 1`] = ` +
+ + + + +
+`; + +exports[`RoomAvatar should render as expected for a LocalRoom 1`] = ` +
+ + + + +
+`; + +exports[`RoomAvatar should render as expected for a Room 1`] = ` +
+ + + + +
+`; diff --git a/test/components/views/rooms/RoomTile-test.tsx b/test/components/views/rooms/RoomTile-test.tsx index e02a72b716..b4bfde243c 100644 --- a/test/components/views/rooms/RoomTile-test.tsx +++ b/test/components/views/rooms/RoomTile-test.tsx @@ -75,20 +75,19 @@ describe("RoomTile", () => { }; let client: Mocked; - let restoreConsole: () => void; let voiceBroadcastInfoEvent: MatrixEvent; let room: Room; let renderResult: RenderResult; let sdkContext: TestSdkContext; + filterConsole( + // irrelevant for this test + "Room !1:example.org does not have an m.room.create event", + ); + beforeEach(() => { sdkContext = new TestSdkContext(); - restoreConsole = filterConsole( - // irrelevant for this test - "Room !1:example.org does not have an m.room.create event", - ); - client = mocked(stubClient()); sdkContext.client = client; DMRoomMap.makeShared(); @@ -105,7 +104,6 @@ describe("RoomTile", () => { }); afterEach(() => { - restoreConsole(); jest.clearAllMocks(); }); diff --git a/test/components/views/user-onboarding/UserOnboardingPage-test.tsx b/test/components/views/user-onboarding/UserOnboardingPage-test.tsx index 0ff637e2c9..af94db6461 100644 --- a/test/components/views/user-onboarding/UserOnboardingPage-test.tsx +++ b/test/components/views/user-onboarding/UserOnboardingPage-test.tsx @@ -33,8 +33,6 @@ jest.mock("../../../../src/components/structures/HomePage", () => ({ })); describe("UserOnboardingPage", () => { - let restoreConsole: () => void; - const renderComponent = async (): Promise => { const renderResult = render(); await act(async () => { @@ -43,12 +41,10 @@ describe("UserOnboardingPage", () => { return renderResult; }; - beforeAll(() => { - restoreConsole = filterConsole( - // unrelated for this test - "could not update user onboarding context", - ); - }); + filterConsole( + // unrelated for this test + "could not update user onboarding context", + ); beforeEach(() => { stubClient(); @@ -60,10 +56,6 @@ describe("UserOnboardingPage", () => { jest.restoreAllMocks(); }); - afterAll(() => { - restoreConsole(); - }); - describe("when the user registered before the cutoff date", () => { beforeEach(() => { jest.spyOn(MatrixClientPeg, "userRegisteredAfter").mockReturnValue(false); diff --git a/test/test-utils/console.ts b/test/test-utils/console.ts index b4c4b98f91..468d2e3b8f 100644 --- a/test/test-utils/console.ts +++ b/test/test-utils/console.ts @@ -16,36 +16,39 @@ limitations under the License. type FilteredConsole = Pick; -const originalFunctions: FilteredConsole = { - log: console.log, - error: console.error, - info: console.info, - debug: console.debug, - warn: console.warn, -}; - /** * Allows to filter out specific messages in console.*. + * Call this from any describe block. + * Automagically restores the original function by implementing an afterAll hook. * * @param ignoreList Messages to be filtered - * @returns function to restore the console */ -export const filterConsole = (...ignoreList: string[]): (() => void) => { - for (const [key, originalFunction] of Object.entries(originalFunctions)) { - window.console[key as keyof FilteredConsole] = (...data: any[]) => { - const message = data?.[0]?.message || data?.[0]; +export const filterConsole = (...ignoreList: string[]): void => { + const originalFunctions: FilteredConsole = { + log: console.log, + error: console.error, + info: console.info, + debug: console.debug, + warn: console.warn, + }; - if (typeof message === "string" && ignoreList.some((i) => message.includes(i))) { - return; - } + beforeAll(() => { + for (const [key, originalFunction] of Object.entries(originalFunctions)) { + window.console[key as keyof FilteredConsole] = (...data: any[]) => { + const message = data?.[0]?.message || data?.[0]; - originalFunction(...data); - }; - } + if (typeof message === "string" && ignoreList.some((i) => message.includes(i))) { + return; + } - return () => { + originalFunction(...data); + }; + } + }); + + afterAll(() => { for (const [key, originalFunction] of Object.entries(originalFunctions)) { window.console[key as keyof FilteredConsole] = originalFunction; } - }; + }); }; diff --git a/test/voice-broadcast/components/molecules/VoiceBroadcastRecordingPip-test.tsx b/test/voice-broadcast/components/molecules/VoiceBroadcastRecordingPip-test.tsx index 15b3348465..662d7b2619 100644 --- a/test/voice-broadcast/components/molecules/VoiceBroadcastRecordingPip-test.tsx +++ b/test/voice-broadcast/components/molecules/VoiceBroadcastRecordingPip-test.tsx @@ -62,7 +62,6 @@ describe("VoiceBroadcastRecordingPip", () => { let infoEvent: MatrixEvent; let recording: VoiceBroadcastRecording; let renderResult: RenderResult; - let restoreConsole: () => void; const renderPip = async (state: VoiceBroadcastInfoState) => { infoEvent = mkVoiceBroadcastInfoStateEvent(roomId, state, client.getUserId() || "", client.getDeviceId() || ""); @@ -85,6 +84,8 @@ describe("VoiceBroadcastRecordingPip", () => { }); }; + filterConsole("Starting load of AsyncWrapper for modal"); + beforeAll(() => { client = stubClient(); mocked(requestMediaPermissions).mockResolvedValue({ @@ -105,11 +106,6 @@ describe("VoiceBroadcastRecordingPip", () => { [MediaDeviceKindEnum.VideoInput]: [], }); jest.spyOn(MediaDeviceHandler.instance, "setDevice").mockImplementation(); - restoreConsole = filterConsole("Starting load of AsyncWrapper for modal"); - }); - - afterAll(() => { - restoreConsole(); }); describe("when rendering a started recording", () => { From fb761c70d5eed26f549fb90db4b1096a43cc8574 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 5 Jan 2023 09:11:30 -0700 Subject: [PATCH 5/7] Remove unused function/file (#9863) --- src/events/getReferenceRelationsForEvent.ts | 31 ------------------- src/events/index.ts | 1 - .../models/VoiceBroadcastPlayback-test.tsx | 4 --- 3 files changed, 36 deletions(-) delete mode 100644 src/events/getReferenceRelationsForEvent.ts diff --git a/src/events/getReferenceRelationsForEvent.ts b/src/events/getReferenceRelationsForEvent.ts deleted file mode 100644 index 3c258f8fe1..0000000000 --- a/src/events/getReferenceRelationsForEvent.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* -Copyright 2022 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 { EventType, MatrixClient, MatrixEvent, RelationType } from "matrix-js-sdk/src/matrix"; -import { Relations } from "matrix-js-sdk/src/models/relations"; - -import { VoiceBroadcastInfoEventType } from "../voice-broadcast"; - -export const getReferenceRelationsForEvent = ( - event: MatrixEvent, - messageType: EventType | typeof VoiceBroadcastInfoEventType, - client: MatrixClient, -): Relations | undefined => { - const room = client.getRoom(event.getRoomId()); - return room - ?.getUnfilteredTimelineSet() - ?.relations?.getChildEventsForEvent(event.getId(), RelationType.Reference, messageType); -}; diff --git a/src/events/index.ts b/src/events/index.ts index 16349b0512..5b5be07ca8 100644 --- a/src/events/index.ts +++ b/src/events/index.ts @@ -16,4 +16,3 @@ limitations under the License. export { getForwardableEvent } from "./forward/getForwardableEvent"; export { getShareableLocationEvent } from "./location/getShareableLocationEvent"; -export * from "./getReferenceRelationsForEvent"; diff --git a/test/voice-broadcast/models/VoiceBroadcastPlayback-test.tsx b/test/voice-broadcast/models/VoiceBroadcastPlayback-test.tsx index e99605c7d3..2f3455dd0a 100644 --- a/test/voice-broadcast/models/VoiceBroadcastPlayback-test.tsx +++ b/test/voice-broadcast/models/VoiceBroadcastPlayback-test.tsx @@ -35,10 +35,6 @@ import { flushPromises, stubClient } from "../../test-utils"; import { createTestPlayback } from "../../test-utils/audio"; import { mkVoiceBroadcastChunkEvent, mkVoiceBroadcastInfoStateEvent } from "../utils/test-utils"; -jest.mock("../../../src/events/getReferenceRelationsForEvent", () => ({ - getReferenceRelationsForEvent: jest.fn(), -})); - jest.mock("../../../src/utils/MediaEventHelper", () => ({ MediaEventHelper: jest.fn(), })); From 8628aae4107b0b64354d88121670d64b9b1e6d55 Mon Sep 17 00:00:00 2001 From: Robin Date: Thu, 5 Jan 2023 23:11:41 -0500 Subject: [PATCH 6/7] Don't prefer STIXGeneral over the default font (#9711) * Don't prefer STIXGeneral over the default font STIXGeneral contains some glyphs for non-LGC scripts, but often doesn't implement these scripts fully. We should always try the browser's default fonts, which are likely to look nicer and have broader script support, before falling back to STIXGeneral. * Explain why STIXGeneral shouldn't have precedence * Add a regression test --- .github/workflows/cypress.yaml | 6 ++++++ cypress/e2e/polls/polls.spec.ts | 5 ++++- res/themes/legacy-light/css/_legacy-light.pcss | 13 ++++++++----- res/themes/light/css/_light.pcss | 11 +++++++---- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/.github/workflows/cypress.yaml b/.github/workflows/cypress.yaml index 59e904ee0e..5f71523fa1 100644 --- a/.github/workflows/cypress.yaml +++ b/.github/workflows/cypress.yaml @@ -92,6 +92,12 @@ jobs: # # Run 4 instances in Parallel # runner: [1, 2, 3, 4] steps: + - uses: tecolicom/actions-use-apt-tools@v1 + with: + # Our test suite includes some screenshot tests with unusual diacritics, which are + # supposed to be covered by STIXGeneral. + tools: fonts-stix + - uses: actions/checkout@v3 with: # XXX: We're checking out untrusted code in a secure context diff --git a/cypress/e2e/polls/polls.spec.ts b/cypress/e2e/polls/polls.spec.ts index ce0962410c..c092d4f647 100644 --- a/cypress/e2e/polls/polls.spec.ts +++ b/cypress/e2e/polls/polls.spec.ts @@ -115,7 +115,10 @@ describe("Polls", () => { const pollParams = { title: "Does the polls feature work?", - options: ["Yes", "No", "Maybe"], + // Since we're going to take a screenshot anyways, we include some + // non-ASCII characters here to stress test the app's font config + // while we're at it. + options: ["Yes", "Noo⃐o⃑o⃩o⃪o⃫o⃬o⃭o⃮o⃯", "のらねこ Maybe?"], }; createPoll(pollParams); diff --git a/res/themes/legacy-light/css/_legacy-light.pcss b/res/themes/legacy-light/css/_legacy-light.pcss index 41a4118576..ae52b078a7 100644 --- a/res/themes/legacy-light/css/_legacy-light.pcss +++ b/res/themes/legacy-light/css/_legacy-light.pcss @@ -4,17 +4,20 @@ Arial empirically gets it right, hence prioritising Arial here. We also include STIXGeneral explicitly to support a wider range of combining diacritics (Chrome fails without it, as per - https://bugs.chromium.org/p/chromium/issues/detail?id=1328898) */ + https://bugs.chromium.org/p/chromium/issues/detail?id=1328898). + We should never actively *prefer* STIXGeneral over the default font though, + since it looks pretty rough and implements some non-LGC scripts only + partially, making, for example, Japanese text look patchy and sad. */ /* We fall through to Twemoji for emoji rather than falling through to native Emoji fonts (if any) to ensure cross-browser consistency */ /* Noto Color Emoji contains digits, in fixed-width, therefore causing digits in flowed text to stand out. TODO: Consider putting all emoji fonts to the end rather than the front. */ -$font-family: "Nunito", "Twemoji", "Apple Color Emoji", "Segoe UI Emoji", "STIXGeneral", "Arial", "Helvetica", - sans-serif, "Noto Color Emoji"; +$font-family: "Nunito", "Twemoji", "Apple Color Emoji", "Segoe UI Emoji", "Arial", "Helvetica", sans-serif, + "STIXGeneral", "Noto Color Emoji"; -$monospace-font-family: "Inconsolata", "Twemoji", "Apple Color Emoji", "Segoe UI Emoji", "STIXGeneral", "Courier", - monospace, "Noto Color Emoji"; +$monospace-font-family: "Inconsolata", "Twemoji", "Apple Color Emoji", "Segoe UI Emoji", "Courier", monospace, + "STIXGeneral", "Noto Color Emoji"; /* unified palette */ /* try to use these colors when possible */ diff --git a/res/themes/light/css/_light.pcss b/res/themes/light/css/_light.pcss index 0b016e9c8d..4a1ae7e53a 100644 --- a/res/themes/light/css/_light.pcss +++ b/res/themes/light/css/_light.pcss @@ -4,17 +4,20 @@ Arial empirically gets it right, hence prioritising Arial here. We also include STIXGeneral explicitly to support a wider range of combining diacritics (Chrome fails without it, as per - https://bugs.chromium.org/p/chromium/issues/detail?id=1328898) */ + https://bugs.chromium.org/p/chromium/issues/detail?id=1328898). + We should never actively *prefer* STIXGeneral over the default font though, + since it looks pretty rough and implements some non-LGC scripts only + partially, making, for example, Japanese text look patchy and sad. */ /* We fall through to Twemoji for emoji rather than falling through to native Emoji fonts (if any) to ensure cross-browser consistency */ /* Noto Color Emoji contains digits, in fixed-width, therefore causing digits in flowed text to stand out. TODO: Consider putting all emoji fonts to the end rather than the front. */ -$font-family: "Inter", "Twemoji", "Apple Color Emoji", "Segoe UI Emoji", "STIXGeneral", "Arial", "Helvetica", sans-serif, +$font-family: "Inter", "Twemoji", "Apple Color Emoji", "Segoe UI Emoji", "Arial", "Helvetica", sans-serif, "STIXGeneral", "Noto Color Emoji"; -$monospace-font-family: "Inconsolata", "Twemoji", "Apple Color Emoji", "Segoe UI Emoji", "STIXGeneral", "Courier", - monospace, "Noto Color Emoji"; +$monospace-font-family: "Inconsolata", "Twemoji", "Apple Color Emoji", "Segoe UI Emoji", "Courier", monospace, + "STIXGeneral", "Noto Color Emoji"; /* Colors from Figma Compound https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=559%3A120 */ /* ******************** */ From d0aca6e5eb810c558cd0132e099ed96d9be8ec89 Mon Sep 17 00:00:00 2001 From: Germain Date: Fri, 6 Jan 2023 08:12:00 +0000 Subject: [PATCH 7/7] Change clear notifications to have more readable copy (#9867) --- src/components/views/settings/Notifications.tsx | 2 +- src/i18n/strings/en_EN.json | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/views/settings/Notifications.tsx b/src/components/views/settings/Notifications.tsx index 96269ae179..ca4a9480c0 100644 --- a/src/components/views/settings/Notifications.tsx +++ b/src/components/views/settings/Notifications.tsx @@ -642,7 +642,7 @@ export default class Notifications extends React.PureComponent { className="mx_UserNotifSettings_clearNotifsButton" data-testid="clear-notifications" > - {_t("Clear notifications")} + {_t("Mark all as read")} ); } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index e481dfca6f..952dba4590 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1419,7 +1419,7 @@ "Enable desktop notifications for this session": "Enable desktop notifications for this session", "Show message in desktop notification": "Show message in desktop notification", "Enable audible notifications for this session": "Enable audible notifications for this session", - "Clear notifications": "Clear notifications", + "Mark all as read": "Mark all as read", "Keyword": "Keyword", "New keyword": "New keyword", "On": "On", @@ -2126,7 +2126,6 @@ "%(count)s reply|one": "%(count)s reply", "Open thread": "Open thread", "Jump to first unread message.": "Jump to first unread message.", - "Mark all as read": "Mark all as read", "Unable to access your microphone": "Unable to access your microphone", "We were unable to access your microphone. Please check your browser settings and try again.": "We were unable to access your microphone. Please check your browser settings and try again.", "No microphone found": "No microphone found",