Updates logic to match new spec

This commit is contained in:
David Langley 2025-12-01 19:27:13 +00:00
parent f46869e114
commit fa6de481ce
3 changed files with 128 additions and 64 deletions

View File

@ -414,36 +414,58 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
const state = this.props.room.currentState;
const canChangeHistory = state?.mayClientSendStateEvent(EventType.RoomHistoryVisibility, client);
const options = [
{
value: HistoryVisibility.Shared,
label: _t("room_settings|security|history_visibility_shared"),
},
{
// Map 'joined' to 'invited' for display purposes
const displayHistory = history === HistoryVisibility.Joined ? HistoryVisibility.Invited : history;
const isPublicRoom = this.props.room.getJoinRule() === JoinRule.Public;
const isEncrypted = this.state.encrypted;
const options: Array<{ value: HistoryVisibility; label: string }> = [];
// Show "invited" when room's join rule is NOT public OR E2EE is turned on, or if currently selected
if (
!isPublicRoom ||
isEncrypted ||
history === HistoryVisibility.Invited ||
history === HistoryVisibility.Joined
) {
options.push({
value: HistoryVisibility.Invited,
label: _t("room_settings|security|history_visibility_invited"),
},
{
value: HistoryVisibility.Joined,
label: _t("room_settings|security|history_visibility_joined"),
},
];
});
}
// World readable doesn't make sense for encrypted rooms
if (!this.state.encrypted || history === HistoryVisibility.WorldReadable) {
options.unshift({
// Always show "shared" option
options.push({
value: HistoryVisibility.Shared,
label: _t("room_settings|security|history_visibility_shared"),
});
// Show "world_readable" when (is public AND not encrypted) OR currently selected
if ((isPublicRoom && !isEncrypted) || history === HistoryVisibility.WorldReadable) {
options.push({
value: HistoryVisibility.WorldReadable,
label: _t("room_settings|security|history_visibility_world_readable"),
});
}
const description = _t("room_settings|security|history_visibility_warning");
const description = (
<>
{_t(
"room_settings|security|history_visibility_warning",
{},
{
a: (sub) => <ExternalLink href="https://element.io/help#encryption20">{sub}</ExternalLink>,
},
)}
</>
);
return (
<SettingsFieldset legend={_t("room_settings|security|history_visibility_legend")} description={description}>
<StyledRadioGroup
name="historyVis"
value={history}
value={displayHistory}
onChange={this.onHistoryRadioToggle}
disabled={!canChangeHistory}
definitions={options}

View File

@ -377,6 +377,77 @@ describe("<SecurityRoomSettingsTab />", () => {
expect(screen.getByDisplayValue(HistoryVisibility.Shared)).toBeChecked();
expect(logger.error).toHaveBeenCalledWith("oups");
});
it("maps 'joined' history visibility to 'invited' for display", () => {
const room = new Room(roomId, client, userId);
setRoomStateEvents(room, undefined, undefined, HistoryVisibility.Joined);
getComponent(room);
// Should display as 'invited' even though underlying value is 'joined'
expect(screen.getByDisplayValue(HistoryVisibility.Invited)).toBeChecked();
// Should not have a 'joined' option visible
expect(screen.queryByDisplayValue(HistoryVisibility.Joined)).not.toBeInTheDocument();
});
it("shows 'invited' option for non-public rooms", () => {
const room = new Room(roomId, client, userId);
setRoomStateEvents(room, JoinRule.Invite);
getComponent(room);
expect(screen.getByDisplayValue(HistoryVisibility.Invited)).toBeInTheDocument();
});
it("shows 'invited' option for encrypted rooms even if public", async () => {
const room = new Room(roomId, client, userId);
jest.spyOn(client.getCrypto()!, "isEncryptionEnabledInRoom").mockResolvedValue(true);
setRoomStateEvents(room, JoinRule.Public);
getComponent(room);
await waitFor(() => expect(screen.getByDisplayValue(HistoryVisibility.Invited)).toBeInTheDocument());
});
it("does not show 'invited' option for public unencrypted rooms unless selected", async () => {
const room = new Room(roomId, client, userId);
setRoomStateEvents(room, JoinRule.Public, undefined, HistoryVisibility.Shared);
getComponent(room);
await waitFor(() =>
expect(screen.queryByDisplayValue(HistoryVisibility.Invited)).not.toBeInTheDocument(),
);
});
it("shows 'world_readable' option for public unencrypted rooms", async () => {
const room = new Room(roomId, client, userId);
setRoomStateEvents(room, JoinRule.Public);
getComponent(room);
await waitFor(() => expect(screen.getByDisplayValue(HistoryVisibility.WorldReadable)).toBeInTheDocument());
});
it("does not show 'world_readable' option for private encrypted rooms unless selected", async () => {
const room = new Room(roomId, client, userId);
jest.spyOn(client.getCrypto()!, "isEncryptionEnabledInRoom").mockResolvedValue(true);
setRoomStateEvents(room, JoinRule.Invite);
getComponent(room);
await waitFor(() =>
expect(screen.queryByDisplayValue(HistoryVisibility.WorldReadable)).not.toBeInTheDocument(),
);
});
it("always shows 'shared' option", () => {
const room = new Room(roomId, client, userId);
setRoomStateEvents(room);
getComponent(room);
expect(screen.getByDisplayValue(HistoryVisibility.Shared)).toBeInTheDocument();
});
});
describe("encryption", () => {

View File

@ -15,7 +15,20 @@ exports[`<SecurityRoomSettingsTab /> history visibility uses shared as default h
<div
class="mx_SettingsSubsection_text"
>
The visibility of existing history will not be changed.
<span>
Changes won't affect past messages, only new ones.
<a
class="mx_ExternalLink"
href="https://element.io/help#encryption20"
rel="noreferrer noopener"
target="_blank"
>
Learn more
<i
class="mx_ExternalLink_icon"
/>
</a>
</span>
</div>
</div>
<div
@ -25,10 +38,10 @@ exports[`<SecurityRoomSettingsTab /> history visibility uses shared as default h
class="mx_StyledRadioButton mx_StyledRadioButton_enabled"
>
<input
id="historyVis-world_readable"
id="historyVis-invited"
name="historyVis"
type="radio"
value="world_readable"
value="invited"
/>
<div>
<div />
@ -36,7 +49,7 @@ exports[`<SecurityRoomSettingsTab /> history visibility uses shared as default h
<div
class="mx_StyledRadioButton_content"
>
Anyone
Members since invited
</div>
<div
class="mx_StyledRadioButton_spacer"
@ -58,49 +71,7 @@ exports[`<SecurityRoomSettingsTab /> history visibility uses shared as default h
<div
class="mx_StyledRadioButton_content"
>
Members only (since the point in time of selecting this option)
</div>
<div
class="mx_StyledRadioButton_spacer"
/>
</label>
<label
class="mx_StyledRadioButton mx_StyledRadioButton_enabled"
>
<input
id="historyVis-invited"
name="historyVis"
type="radio"
value="invited"
/>
<div>
<div />
</div>
<div
class="mx_StyledRadioButton_content"
>
Members only (since they were invited)
</div>
<div
class="mx_StyledRadioButton_spacer"
/>
</label>
<label
class="mx_StyledRadioButton mx_StyledRadioButton_enabled"
>
<input
id="historyVis-joined"
name="historyVis"
type="radio"
value="joined"
/>
<div>
<div />
</div>
<div
class="mx_StyledRadioButton_content"
>
Members only (since they joined)
Members (full history)
</div>
<div
class="mx_StyledRadioButton_spacer"