feat(room-settings): one-line "who can see this" under the room name (#133)
Room Settings → General now reads e.g. "Encrypted · Invite only · History visible to members since they joined" under the name — derived from m.room.encryption, m.room.join_rules and m.room.history_visibility with the same words the preview-card chips and the history setting use. One Text line, no card, no icons, no controls; spec defaults (invite-only, shared history) when a state event is absent. Unit-tested. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
@@ -40,6 +40,8 @@ import { useMatrixClient } from '../../../hooks/useMatrixClient';
|
|||||||
import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
|
import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
|
||||||
import { StateEvent } from '../../../../types/matrix/room';
|
import { StateEvent } from '../../../../types/matrix/room';
|
||||||
import { sendStateEvent } from '../../../utils/room';
|
import { sendStateEvent } from '../../../utils/room';
|
||||||
|
import { describeRoomVisibility } from '../../../utils/roomVisibilityLine';
|
||||||
|
import { useStateEvent } from '../../../hooks/useStateEvent';
|
||||||
import { CompactUploadCardRenderer } from '../../../components/upload-card';
|
import { CompactUploadCardRenderer } from '../../../components/upload-card';
|
||||||
import { useObjectURL } from '../../../hooks/useObjectURL';
|
import { useObjectURL } from '../../../hooks/useObjectURL';
|
||||||
import { createUploadAtom, UploadSuccess } from '../../../state/upload';
|
import { createUploadAtom, UploadSuccess } from '../../../state/upload';
|
||||||
@@ -434,6 +436,14 @@ export function RoomProfile({ permissions }: RoomProfileProps) {
|
|||||||
const name = useRoomName(room);
|
const name = useRoomName(room);
|
||||||
const topic = useRoomTopic(room);
|
const topic = useRoomTopic(room);
|
||||||
const joinRule = useRoomJoinRule(room);
|
const joinRule = useRoomJoinRule(room);
|
||||||
|
// [Gitea #133] One line, no controls: encryption · join rule · history.
|
||||||
|
const historyVisibilityEvent = useStateEvent(room, StateEvent.RoomHistoryVisibility);
|
||||||
|
const visibilityLine = describeRoomVisibility({
|
||||||
|
encrypted: room.hasEncryptionStateEvent(),
|
||||||
|
joinRule: joinRule?.join_rule,
|
||||||
|
historyVisibility: historyVisibilityEvent?.getContent<{ history_visibility?: string }>()
|
||||||
|
.history_visibility,
|
||||||
|
});
|
||||||
|
|
||||||
const canEditAvatar = permissions.stateEvent(StateEvent.RoomAvatar, mx.getSafeUserId());
|
const canEditAvatar = permissions.stateEvent(StateEvent.RoomAvatar, mx.getSafeUserId());
|
||||||
const canEditName = permissions.stateEvent(StateEvent.RoomName, mx.getSafeUserId());
|
const canEditName = permissions.stateEvent(StateEvent.RoomName, mx.getSafeUserId());
|
||||||
@@ -474,6 +484,9 @@ export function RoomProfile({ permissions }: RoomProfileProps) {
|
|||||||
<Text className={BreakWord} size="H5">
|
<Text className={BreakWord} size="H5">
|
||||||
{name ?? 'Unknown'}
|
{name ?? 'Unknown'}
|
||||||
</Text>
|
</Text>
|
||||||
|
<Text size="T200" priority="300" className={BreakWord}>
|
||||||
|
{visibilityLine}
|
||||||
|
</Text>
|
||||||
{topic && (
|
{topic && (
|
||||||
<Text className={classNames(BreakWord, LineClamp3)} size="T200">
|
<Text className={classNames(BreakWord, LineClamp3)} size="T200">
|
||||||
{topic.format === 'org.matrix.custom.html' &&
|
{topic.format === 'org.matrix.custom.html' &&
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { test } from 'node:test';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import { describeRoomVisibility } from './roomVisibilityLine';
|
||||||
|
|
||||||
|
test('describeRoomVisibility composes the three facts', () => {
|
||||||
|
assert.equal(
|
||||||
|
describeRoomVisibility({ encrypted: true, joinRule: 'invite', historyVisibility: 'joined' }),
|
||||||
|
'Encrypted · Invite only · History visible to members since they joined',
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
describeRoomVisibility({ encrypted: false, joinRule: 'public', historyVisibility: 'shared' }),
|
||||||
|
'Not encrypted · Anyone can join · History visible to members, including before they joined',
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
describeRoomVisibility({
|
||||||
|
encrypted: false,
|
||||||
|
joinRule: 'knock',
|
||||||
|
historyVisibility: 'world_readable',
|
||||||
|
}),
|
||||||
|
'Not encrypted · Ask to join · History visible to anyone, even guests',
|
||||||
|
);
|
||||||
|
// Missing state: spec defaults are invite-only and "shared".
|
||||||
|
assert.equal(
|
||||||
|
describeRoomVisibility({ encrypted: true }),
|
||||||
|
'Encrypted · Invite only · History visible to members, including before they joined',
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import { HistoryVisibility, JoinRule } from 'matrix-js-sdk';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [Gitea #133] One sentence for Room Settings → General, under the name:
|
||||||
|
* "Encrypted · Invite only · History visible to members since they joined".
|
||||||
|
* Wording mirrors the room preview card chips and the history-visibility
|
||||||
|
* setting so the same words mean the same thing everywhere.
|
||||||
|
*/
|
||||||
|
export function describeRoomVisibility(input: {
|
||||||
|
encrypted: boolean;
|
||||||
|
joinRule?: string;
|
||||||
|
historyVisibility?: string;
|
||||||
|
}): string {
|
||||||
|
const parts: string[] = [input.encrypted ? 'Encrypted' : 'Not encrypted'];
|
||||||
|
switch (input.joinRule) {
|
||||||
|
case JoinRule.Public:
|
||||||
|
parts.push('Anyone can join');
|
||||||
|
break;
|
||||||
|
case JoinRule.Knock:
|
||||||
|
case 'knock_restricted':
|
||||||
|
parts.push('Ask to join');
|
||||||
|
break;
|
||||||
|
case JoinRule.Restricted:
|
||||||
|
parts.push('Restricted');
|
||||||
|
break;
|
||||||
|
case JoinRule.Private:
|
||||||
|
parts.push('Private');
|
||||||
|
break;
|
||||||
|
case JoinRule.Invite:
|
||||||
|
default:
|
||||||
|
parts.push('Invite only');
|
||||||
|
}
|
||||||
|
switch (input.historyVisibility) {
|
||||||
|
case HistoryVisibility.WorldReadable:
|
||||||
|
parts.push('History visible to anyone, even guests');
|
||||||
|
break;
|
||||||
|
case HistoryVisibility.Invited:
|
||||||
|
parts.push('History visible to members since they were invited');
|
||||||
|
break;
|
||||||
|
case HistoryVisibility.Joined:
|
||||||
|
parts.push('History visible to members since they joined');
|
||||||
|
break;
|
||||||
|
case HistoryVisibility.Shared:
|
||||||
|
default:
|
||||||
|
// "shared" is also the spec default when the state event is absent.
|
||||||
|
parts.push('History visible to members, including before they joined');
|
||||||
|
}
|
||||||
|
return parts.join(' · ');
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user