From 7487e5322b7a6ec9a21b6cae05b09ada250f7105 Mon Sep 17 00:00:00 2001 From: Ajay Bura <32841439+ajbura@users.noreply.github.com> Date: Sun, 20 Jul 2025 15:10:20 +0530 Subject: [PATCH] WIP - support room version 12 --- .../common-settings/general/RoomUpgrade.tsx | 8 ++--- .../permissions/PermissionGroups.tsx | 4 +++ src/app/hooks/usePowerLevelTags.ts | 21 +++++++++--- src/app/hooks/usePowerLevels.ts | 30 +++++++++++----- src/app/utils/room.ts | 34 +++++++++++++++++++ 5 files changed, 79 insertions(+), 18 deletions(-) diff --git a/src/app/features/common-settings/general/RoomUpgrade.tsx b/src/app/features/common-settings/general/RoomUpgrade.tsx index 5d6bc5e2..f5a2a4f7 100644 --- a/src/app/features/common-settings/general/RoomUpgrade.tsx +++ b/src/app/features/common-settings/general/RoomUpgrade.tsx @@ -18,14 +18,14 @@ import { } from 'folds'; import FocusTrap from 'focus-trap-react'; import { MatrixError } from 'matrix-js-sdk'; -import { RoomCreateEventContent, RoomTombstoneEventContent } from 'matrix-js-sdk/lib/types'; +import { RoomTombstoneEventContent } from 'matrix-js-sdk/lib/types'; import { SequenceCard } from '../../../components/sequence-card'; import { SequenceCardStyle } from '../../room-settings/styles.css'; import { SettingTile } from '../../../components/setting-tile'; import { useRoom } from '../../../hooks/useRoom'; import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback'; import { IPowerLevels, powerLevelAPI } from '../../../hooks/usePowerLevels'; -import { StateEvent } from '../../../../types/matrix/room'; +import { IRoomCreateContent, StateEvent } from '../../../../types/matrix/room'; import { useMatrixClient } from '../../../hooks/useMatrixClient'; import { useStateEvent } from '../../../hooks/useStateEvent'; import { useRoomNavigate } from '../../../hooks/useRoomNavigate'; @@ -43,8 +43,8 @@ export function RoomUpgrade({ powerLevels, requestClose }: RoomUpgradeProps) { const createContent = useStateEvent( room, StateEvent.RoomCreate - )?.getContent(); - const roomVersion = createContent?.room_version ?? 1; + )?.getContent(); + const roomVersion = createContent?.room_version ?? '1'; const predecessorRoomId = createContent?.predecessor?.room_id; const capabilities = useCapabilities(); diff --git a/src/app/features/common-settings/permissions/PermissionGroups.tsx b/src/app/features/common-settings/permissions/PermissionGroups.tsx index 54c9c5d2..bddd8847 100644 --- a/src/app/features/common-settings/permissions/PermissionGroups.tsx +++ b/src/app/features/common-settings/permissions/PermissionGroups.tsx @@ -82,6 +82,10 @@ export function PermissionGroups({ powerLevels, permissionGroups }: PermissionGr permissionUpdate.forEach((power, location) => applyPermissionPower(draftPowerLevels, location, power) ); + + // eslint-disable-next-line no-param-reassign + delete draftPowerLevels['in.cinny.creators']; + return draftPowerLevels; }); await mx.sendStateEvent(room.roomId, StateEvent.RoomPowerLevels as any, editedPowerLevels); diff --git a/src/app/hooks/usePowerLevelTags.ts b/src/app/hooks/usePowerLevelTags.ts index bdcb9bcc..03ab3706 100644 --- a/src/app/hooks/usePowerLevelTags.ts +++ b/src/app/hooks/usePowerLevelTags.ts @@ -19,17 +19,24 @@ export type PowerLevelTag = { export type PowerLevelTags = Record; -export const powerSortFn = (a: number, b: number) => b - a; -export const sortPowers = (powers: number[]): number[] => powers.sort(powerSortFn); +const powerSortFn = (a: number, b: number) => b - a; +const sortPowers = (powers: number[]): number[] => powers.sort(powerSortFn); export const getPowers = (tags: PowerLevelTags): number[] => { - const powers: number[] = Object.keys(tags).map((p) => parseInt(p, 10)); + const powers: number[] = Object.keys(tags).map((p) => { + const power = parseInt(p, 10); + if (Number.isNaN(power)) { + return Infinity; + } + return power; + }); return sortPowers(powers); }; export const getUsedPowers = (powerLevels: IPowerLevels): Set => { const powers: Set = new Set(); + powers.add(Infinity); const findAndAddPower = (data: Record) => { Object.keys(data).forEach((key) => { @@ -51,12 +58,16 @@ export const getUsedPowers = (powerLevels: IPowerLevels): Set => { }; const DEFAULT_TAGS: PowerLevelTags = { + Infinity: { + name: 'Creator', + color: '#ff6a00', + }, 9001: { name: 'Goku', color: '#ff6a00', }, - 102: { - name: 'Goku Reborn', + 150: { + name: 'Super Admin', color: '#ff6a7f', }, 101: { diff --git a/src/app/hooks/usePowerLevels.ts b/src/app/hooks/usePowerLevels.ts index 8bf8b747..ea007c8c 100644 --- a/src/app/hooks/usePowerLevels.ts +++ b/src/app/hooks/usePowerLevels.ts @@ -5,7 +5,7 @@ import { useStateEvent } from './useStateEvent'; import { StateEvent } from '../../types/matrix/room'; import { useStateEventCallback } from './useStateEventCallback'; import { useMatrixClient } from './useMatrixClient'; -import { getStateEvent } from '../utils/room'; +import { getRoomCreators, getStateEvent } from '../utils/room'; export type PowerLevelActions = 'invite' | 'redact' | 'kick' | 'ban' | 'historical'; export type PowerLevelNotificationsAction = 'room'; @@ -23,6 +23,7 @@ export type IPowerLevels = { events?: Record; users?: Record; notifications?: Record; + 'in.cinny.creators'?: string[]; }; const DEFAULT_POWER_LEVELS: Required = { @@ -39,6 +40,7 @@ const DEFAULT_POWER_LEVELS: Required = { notifications: { room: 50, }, + 'in.cinny.creators': [], }; const fillMissingPowers = (powerLevels: IPowerLevels): IPowerLevels => @@ -57,18 +59,23 @@ const fillMissingPowers = (powerLevels: IPowerLevels): IPowerLevels => return draftPl; }); -const getPowersLevelFromMatrixEvent = (mEvent?: MatrixEvent): IPowerLevels => { - const pl = mEvent?.getContent(); - if (!pl) return DEFAULT_POWER_LEVELS; +const getPowersLevelFromMatrixEvent = (mEvent?: MatrixEvent, creators?: string[]): IPowerLevels => { + const plContent = mEvent?.getContent(); - return fillMissingPowers(pl); + const powerLevels = !plContent ? DEFAULT_POWER_LEVELS : fillMissingPowers(plContent); + + return produce(powerLevels, (draftPl) => { + // eslint-disable-next-line no-param-reassign + draftPl['in.cinny.creators'] = creators; + return draftPl; + }); }; export function usePowerLevels(room: Room): IPowerLevels { const powerLevelsEvent = useStateEvent(room, StateEvent.RoomPowerLevels); const powerLevels: IPowerLevels = useMemo( - () => getPowersLevelFromMatrixEvent(powerLevelsEvent), - [powerLevelsEvent] + () => getPowersLevelFromMatrixEvent(powerLevelsEvent, getRoomCreators(room)), + [room, powerLevelsEvent] ); return powerLevels; @@ -91,7 +98,7 @@ export const useRoomsPowerLevels = (rooms: Room[]): Map => rooms.forEach((room) => { const mEvent = getStateEvent(room, StateEvent.RoomPowerLevels, ''); - rToPl.set(room.roomId, getPowersLevelFromMatrixEvent(mEvent)); + rToPl.set(room.roomId, getPowersLevelFromMatrixEvent(mEvent, getRoomCreators(room))); }); return rToPl; @@ -155,7 +162,12 @@ export type ReadPowerLevelAPI = { export const readPowerLevel: ReadPowerLevelAPI = { user: (powerLevels, userId) => { - const { users_default: usersDefault, users } = powerLevels; + const { users_default: usersDefault, users, 'in.cinny.creators': creators } = powerLevels; + + if (userId && Array.isArray(creators) && creators.includes(userId)) { + return Infinity; + } + if (userId && users && typeof users[userId] === 'number') { return users[userId]; } diff --git a/src/app/utils/room.ts b/src/app/utils/room.ts index cae23514..8aff98fa 100644 --- a/src/app/utils/room.ts +++ b/src/app/utils/room.ts @@ -20,6 +20,7 @@ import { import { CryptoBackend } from 'matrix-js-sdk/lib/common-crypto/CryptoBackend'; import { AccountDataEvent } from '../../types/matrix/accountData'; import { + IRoomCreateContent, Membership, MessageEvent, NotificationType, @@ -514,3 +515,36 @@ export const guessPerfectParent = ( return perfectParent; }; + +export const getRoomCreators = (room: Room): string[] | undefined => { + const createEvent = getStateEvent(room, StateEvent.RoomCreate); + if (!createEvent) return undefined; + + const createContent = createEvent.getContent(); + const roomVersion = createContent.room_version; + + if (['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'].includes(roomVersion)) { + // room version doesn't support creators. + return undefined; + } + + const creators: Set = new Set(); + + if (createEvent?.event.sender) { + creators.add(createEvent?.event.sender); + } + + if ( + createContent && + 'additional_creators' in createContent && + Array.isArray(createContent.additional_creators) + ) { + createContent.additional_creators.forEach((creator) => { + if (typeof creator === 'string') { + creators.add(creator); + } + }); + } + + return Array.from(creators); +};