Skip to content

Commit ce9c66b

Browse files
Update algorithm for history visible banner. (#31577)
* feat: Update algorithm for history visible banner. - The banner now only shows for rooms with `shared` or `worldReadable` history visibility. - The banner does not show in rooms in which the current user cannot send messages. * tests: Add `getHistoryVisibility` to stub room. * docs: Add description to `visible` condition check. * docs: Fix spelling. Co-authored-by: Florian Duros <florian.duros@ormaz.fr> * chore: Remove `jest-sonar.xml`. --------- Co-authored-by: Florian Duros <florian.duros@ormaz.fr>
1 parent aa84b2e commit ce9c66b

File tree

5 files changed

+92
-18
lines changed

5 files changed

+92
-18
lines changed

src/components/views/composer/HistoryVisibleBanner.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ export const HistoryVisibleBanner: React.FC<{
1616
/** The room instance associated with this banner view model. */
1717
room: Room;
1818

19+
/** Whether the current user can send messages in the room. */
20+
canSendMessages: boolean;
21+
1922
/**
2023
* If not null, specifies the ID of the thread currently being viewed in the thread timeline side view,
2124
* where the banner view is displayed as a child of the message composer.

src/components/views/rooms/MessageComposer.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -675,7 +675,11 @@ export class MessageComposer extends React.Component<IProps, IState> {
675675

676676
return (
677677
<div className={classes} ref={this.ref} role="region" aria-label={_t("a11y|message_composer")}>
678-
<HistoryVisibleBanner room={this.props.room} threadId={threadId ?? null} />
678+
<HistoryVisibleBanner
679+
room={this.props.room}
680+
canSendMessages={canSendMessages}
681+
threadId={threadId ?? null}
682+
/>
679683
<div className="mx_MessageComposer_wrapper">
680684
<UserIdentityWarning room={this.props.room} key={this.props.room.roomId} />
681685
<ReplyPreview

src/viewmodels/composer/HistoryVisibleBannerViewModel.tsx

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,22 @@ import { HistoryVisibility, RoomStateEvent, type Room } from "matrix-js-sdk/src/
1515
import SettingsStore from "../../settings/SettingsStore";
1616
import { SettingLevel } from "../../settings/SettingLevel";
1717

18+
/**
19+
* A collection of {@link HistoryVisibility} levels that trigger the display of the history visible banner.
20+
*/
21+
const BANNER_VISIBLE_LEVELS = [HistoryVisibility.Shared, HistoryVisibility.WorldReadable];
22+
1823
interface Props {
1924
/**
2025
* The room instance associated with this banner view model.
2126
*/
2227
room: Room;
2328

29+
/**
30+
* Whether or not the current user is able to send messages in this room.
31+
*/
32+
canSendMessages: boolean;
33+
2434
/**
2535
* If not null, indicates the ID of the thread currently being viewed in the thread
2636
* timeline side view, where the banner view is displayed as a child of the message
@@ -66,23 +76,33 @@ export class HistoryVisibleBannerViewModel
6676

6777
/**
6878
* Computes the latest banner snapshot given the VM's props.
69-
* @param room - The room the banner will be shown in.
70-
* @param threadId - The thread ID passed in from the parent {@link MessageComposer}.
79+
* @param props - See {@link Props}.
7180
* @returns The latest snapshot. See {@link HistoryVisibleBannerViewSnapshot}.
7281
*/
73-
private static readonly computeSnapshot = (
74-
room: Room,
75-
threadId?: string | null,
76-
): HistoryVisibleBannerViewSnapshot => {
82+
private static readonly computeSnapshot = ({
83+
room,
84+
canSendMessages,
85+
threadId,
86+
}: Props): HistoryVisibleBannerViewSnapshot => {
7787
const featureEnabled = SettingsStore.getValue("feature_share_history_on_invite");
7888
const acknowledged = SettingsStore.getValue("acknowledgedHistoryVisibility", room.roomId);
79-
89+
const isHistoryVisible = BANNER_VISIBLE_LEVELS.includes(room.getHistoryVisibility());
90+
91+
// This implements point 1. of the algorithm described above. In the order below, all
92+
// of the following must be true for the banner to display:
93+
// - The room history sharing feature must be enabled.
94+
// - The room must be encrypted.
95+
// - The user must be able to send messages.
96+
// - The history must be visible.
97+
// - The view should not be part of a thread timeline.
98+
// - The user must not have acknowledged the banner.
8099
return {
81100
visible:
82101
featureEnabled &&
83-
!threadId &&
84102
room.hasEncryptionStateEvent() &&
85-
room.getHistoryVisibility() !== HistoryVisibility.Joined &&
103+
canSendMessages &&
104+
isHistoryVisible &&
105+
!threadId &&
86106
!acknowledged,
87107
};
88108
};
@@ -92,7 +112,7 @@ export class HistoryVisibleBannerViewModel
92112
* @param props - Properties for this view model. See {@link Props}.
93113
*/
94114
public constructor(props: Props) {
95-
super(props, HistoryVisibleBannerViewModel.computeSnapshot(props.room, props.threadId));
115+
super(props, HistoryVisibleBannerViewModel.computeSnapshot(props));
96116

97117
this.disposables.trackListener(props.room, RoomStateEvent.Update, () => this.setSnapshot());
98118

@@ -126,7 +146,7 @@ export class HistoryVisibleBannerViewModel
126146
);
127147
}
128148

129-
this.snapshot.set(HistoryVisibleBannerViewModel.computeSnapshot(this.props.room, this.props.threadId));
149+
this.snapshot.set(HistoryVisibleBannerViewModel.computeSnapshot(this.props));
130150
}
131151

132152
/**

test/test-utils/test-utils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,7 @@ export function mkStubRoom(
677677
getCanonicalAlias: jest.fn(),
678678
getDMInviter: jest.fn(),
679679
getEventReadUpTo: jest.fn(() => null),
680+
getHistoryVisibility: jest.fn().mockReturnValue(HistoryVisibility.Joined),
680681
getInvitedAndJoinedMemberCount: jest.fn().mockReturnValue(1),
681682
getJoinRule: jest.fn().mockReturnValue("invite"),
682683
getJoinedMemberCount: jest.fn().mockReturnValue(1),

test/unit-tests/components/viewmodels/composer/HistoryVisibleBannerViewModel-test.tsx

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ describe("HistoryVisibleBannerViewModel", () => {
5454
});
5555

5656
it("should not show the banner in unencrypted rooms", () => {
57-
const vm = new HistoryVisibleBannerViewModel({ room, threadId: null });
57+
const vm = new HistoryVisibleBannerViewModel({ room, canSendMessages: true, threadId: null });
5858
expect(vm.getSnapshot().visible).toBe(false);
5959
});
6060

@@ -76,7 +76,7 @@ describe("HistoryVisibleBannerViewModel", () => {
7676
}),
7777
]);
7878

79-
const vm = new HistoryVisibleBannerViewModel({ room, threadId: null });
79+
const vm = new HistoryVisibleBannerViewModel({ room, canSendMessages: true, threadId: null });
8080
expect(vm.getSnapshot().visible).toBe(false);
8181
});
8282

@@ -99,7 +99,7 @@ describe("HistoryVisibleBannerViewModel", () => {
9999
}),
100100
]);
101101

102-
const vm = new HistoryVisibleBannerViewModel({ room, threadId: null });
102+
const vm = new HistoryVisibleBannerViewModel({ room, canSendMessages: true, threadId: null });
103103
expect(vm.getSnapshot().visible).toBe(false);
104104
vm.dispose();
105105
});
@@ -122,12 +122,12 @@ describe("HistoryVisibleBannerViewModel", () => {
122122
}),
123123
]);
124124

125-
const vm = new HistoryVisibleBannerViewModel({ room, threadId: "some thread ID" });
125+
const vm = new HistoryVisibleBannerViewModel({ room, canSendMessages: true, threadId: "some thread ID" });
126126
expect(vm.getSnapshot().visible).toBe(false);
127127
vm.dispose();
128128
});
129129

130-
it("should show the banner in encrypted rooms with non-joined history visibility", async () => {
130+
it("should not show the banner if the user cannot send messages", () => {
131131
upsertRoomStateEvents(room, [
132132
mkEvent({
133133
event: true,
@@ -145,7 +145,53 @@ describe("HistoryVisibleBannerViewModel", () => {
145145
}),
146146
]);
147147

148-
const vm = new HistoryVisibleBannerViewModel({ room, threadId: null });
148+
const vm = new HistoryVisibleBannerViewModel({ room, canSendMessages: false, threadId: null });
149+
expect(vm.getSnapshot().visible).toBe(false);
150+
vm.dispose();
151+
});
152+
153+
it("should not show the banner if history visibility is `invited`", () => {
154+
upsertRoomStateEvents(room, [
155+
mkEvent({
156+
event: true,
157+
type: "m.room.encryption",
158+
user: "@user1:server",
159+
content: {},
160+
}),
161+
mkEvent({
162+
event: true,
163+
type: "m.room.history_visibility",
164+
user: "@user1:server",
165+
content: {
166+
history_visibility: "invited",
167+
},
168+
}),
169+
]);
170+
171+
const vm = new HistoryVisibleBannerViewModel({ room, canSendMessages: true, threadId: null });
172+
expect(vm.getSnapshot().visible).toBe(false);
173+
vm.dispose();
174+
});
175+
176+
it("should show the banner in encrypted rooms with shared history visibility", async () => {
177+
upsertRoomStateEvents(room, [
178+
mkEvent({
179+
event: true,
180+
type: "m.room.encryption",
181+
user: "@user1:server",
182+
content: {},
183+
}),
184+
mkEvent({
185+
event: true,
186+
type: "m.room.history_visibility",
187+
user: "@user1:server",
188+
content: {
189+
history_visibility: "shared",
190+
},
191+
}),
192+
]);
193+
194+
const vm = new HistoryVisibleBannerViewModel({ room, canSendMessages: true, threadId: null });
149195
expect(vm.getSnapshot().visible).toBe(true);
150196
await vm.onClose();
151197
expect(vm.getSnapshot().visible).toBe(false);

0 commit comments

Comments
 (0)