mirror of
https://github.com/cinnyapp/cinny.git
synced 2025-02-13 16:13:24 +01:00
make power level api more reusable
This commit is contained in:
parent
da026b4e12
commit
e5330c5e13
6 changed files with 132 additions and 61 deletions
|
@ -25,7 +25,7 @@ import { nameInitials } from '../../utils/common';
|
|||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||
import { useRoomUnread } from '../../state/hooks/unread';
|
||||
import { roomToUnreadAtom } from '../../state/room/roomToUnread';
|
||||
import { usePowerLevels } from '../../hooks/usePowerLevels';
|
||||
import { usePowerLevels, usePowerLevelsAPI } from '../../hooks/usePowerLevels';
|
||||
import { copyToClipboard } from '../../utils/dom';
|
||||
import { getOriginBaseUrl, withOriginBaseUrl } from '../../pages/pathUtils';
|
||||
import { markAsRead } from '../../../client/action/notifications';
|
||||
|
@ -42,7 +42,8 @@ const RoomNavItemMenu = forwardRef<HTMLDivElement, RoomNavItemMenuProps>(
|
|||
({ room, linkPath, requestClose }, ref) => {
|
||||
const mx = useMatrixClient();
|
||||
const unread = useRoomUnread(room.roomId, roomToUnreadAtom);
|
||||
const { getPowerLevel, canDoAction } = usePowerLevels(room);
|
||||
const powerLevels = usePowerLevels(room);
|
||||
const { getPowerLevel, canDoAction } = usePowerLevelsAPI(powerLevels);
|
||||
const canInvite = canDoAction('invite', getPowerLevel(mx.getUserId() ?? ''));
|
||||
|
||||
const handleMarkAsRead = () => {
|
||||
|
|
|
@ -22,10 +22,10 @@ export function Room() {
|
|||
|
||||
const [isDrawer] = useSetting(settingsAtom, 'isPeopleDrawer');
|
||||
const screenSize = useScreenSize();
|
||||
const powerLevelAPI = usePowerLevels(room);
|
||||
const powerLevels = usePowerLevels(room);
|
||||
|
||||
return (
|
||||
<PowerLevelsContextProvider value={powerLevelAPI}>
|
||||
<PowerLevelsContextProvider value={powerLevels}>
|
||||
<Box grow="Yes">
|
||||
<RoomView room={room} eventId={eventId} />
|
||||
{screenSize === ScreenSize.Desktop && isDrawer && (
|
||||
|
|
|
@ -101,7 +101,7 @@ import * as css from './RoomTimeline.css';
|
|||
import { inSameDay, minuteDifference, timeDayMonthYear, today, yesterday } from '../../utils/time';
|
||||
import { createMentionElement, isEmptyEditor, moveCursor } from '../../components/editor';
|
||||
import { roomIdToReplyDraftAtomFamily } from '../../state/room/roomInputDrafts';
|
||||
import { usePowerLevelsAPI } from '../../hooks/usePowerLevels';
|
||||
import { usePowerLevelsAPI, usePowerLevelsContext } from '../../hooks/usePowerLevels';
|
||||
import { GetContentCallback, MessageEvent, StateEvent } from '../../../types/matrix/room';
|
||||
import initMatrix from '../../../client/initMatrix';
|
||||
import { useKeyDown } from '../../hooks/useKeyDown';
|
||||
|
@ -437,7 +437,8 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli
|
|||
const showUrlPreview = encryptedRoom ? encUrlPreview : urlPreview;
|
||||
const [showHiddenEvents] = useSetting(settingsAtom, 'showHiddenEvents');
|
||||
const setReplyDraft = useSetAtom(roomIdToReplyDraftAtomFamily(room.roomId));
|
||||
const { canDoAction, canSendEvent, getPowerLevel } = usePowerLevelsAPI();
|
||||
const powerLevels = usePowerLevelsContext();
|
||||
const { canDoAction, canSendEvent, getPowerLevel } = usePowerLevelsAPI(powerLevels);
|
||||
const myPowerLevel = getPowerLevel(mx.getUserId() ?? '');
|
||||
const canRedact = canDoAction('redact', myPowerLevel);
|
||||
const canSendReaction = canSendEvent(MessageEvent.Reaction, myPowerLevel);
|
||||
|
|
|
@ -4,7 +4,7 @@ import { EventType, Room } from 'matrix-js-sdk';
|
|||
|
||||
import { useStateEvent } from '../../hooks/useStateEvent';
|
||||
import { StateEvent } from '../../../types/matrix/room';
|
||||
import { usePowerLevelsAPI } from '../../hooks/usePowerLevels';
|
||||
import { usePowerLevelsAPI, usePowerLevelsContext } from '../../hooks/usePowerLevels';
|
||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||
import { useEditor } from '../../components/editor';
|
||||
import { RoomInputPlaceholder } from './RoomInputPlaceholder';
|
||||
|
@ -26,7 +26,8 @@ export function RoomView({ room, eventId }: { room: Room; eventId?: string }) {
|
|||
const mx = useMatrixClient();
|
||||
|
||||
const tombstoneEvent = useStateEvent(room, StateEvent.RoomTombstone);
|
||||
const { getPowerLevel, canSendEvent } = usePowerLevelsAPI();
|
||||
const powerLevels = usePowerLevelsContext();
|
||||
const { getPowerLevel, canSendEvent } = usePowerLevelsAPI(powerLevels);
|
||||
const myUserId = mx.getUserId();
|
||||
const canMessage = myUserId
|
||||
? canSendEvent(EventType.RoomMessage, getPowerLevel(myUserId))
|
||||
|
|
|
@ -47,7 +47,7 @@ import { getCanonicalAliasOrRoomId } from '../../utils/matrix';
|
|||
import { _SearchPathSearchParams } from '../../pages/paths';
|
||||
import * as css from './RoomViewHeader.css';
|
||||
import { useRoomUnread } from '../../state/hooks/unread';
|
||||
import { usePowerLevelsAPI } from '../../hooks/usePowerLevels';
|
||||
import { usePowerLevelsAPI, usePowerLevelsContext } from '../../hooks/usePowerLevels';
|
||||
import { markAsRead } from '../../../client/action/notifications';
|
||||
import { roomToUnreadAtom } from '../../state/room/roomToUnread';
|
||||
import { openInviteUser, toggleRoomSettings } from '../../../client/action/navigation';
|
||||
|
@ -65,7 +65,8 @@ const RoomMenu = forwardRef<HTMLDivElement, RoomMenuProps>(
|
|||
({ room, linkPath, requestClose }, ref) => {
|
||||
const mx = useMatrixClient();
|
||||
const unread = useRoomUnread(room.roomId, roomToUnreadAtom);
|
||||
const { getPowerLevel, canDoAction } = usePowerLevelsAPI();
|
||||
const powerLevels = usePowerLevelsContext();
|
||||
const { getPowerLevel, canDoAction } = usePowerLevelsAPI(powerLevels);
|
||||
const canInvite = canDoAction('invite', getPowerLevel(mx.getUserId() ?? ''));
|
||||
|
||||
const handleMarkAsRead = () => {
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
import { Room } from 'matrix-js-sdk';
|
||||
import { createContext, useCallback, useContext } from 'react';
|
||||
import { createContext, useCallback, useContext, useMemo } from 'react';
|
||||
import { useStateEvent } from './useStateEvent';
|
||||
import { StateEvent } from '../../types/matrix/room';
|
||||
import { useForceUpdate } from './useForceUpdate';
|
||||
import { useStateEventCallback } from './useStateEventCallback';
|
||||
import { useMatrixClient } from './useMatrixClient';
|
||||
import { getStateEvent } from '../utils/room';
|
||||
|
||||
export type PowerLevelActions = 'invite' | 'redact' | 'kick' | 'ban' | 'historical';
|
||||
|
||||
enum DefaultPowerLevels {
|
||||
export enum DefaultPowerLevels {
|
||||
usersDefault = 0,
|
||||
stateDefault = 50,
|
||||
eventsDefault = 0,
|
||||
|
@ -16,7 +20,7 @@ enum DefaultPowerLevels {
|
|||
historical = 0,
|
||||
}
|
||||
|
||||
interface IPowerLevels {
|
||||
export interface IPowerLevels {
|
||||
users_default?: number;
|
||||
state_default?: number;
|
||||
events_default?: number;
|
||||
|
@ -31,9 +35,75 @@ interface IPowerLevels {
|
|||
notifications?: Record<string, number>;
|
||||
}
|
||||
|
||||
export type GetPowerLevel = (userId: string) => number;
|
||||
export type CanSend = (eventType: string | undefined, powerLevel: number) => boolean;
|
||||
export type CanDoAction = (action: PowerLevelActions, powerLevel: number) => boolean;
|
||||
export function usePowerLevels(room: Room): IPowerLevels {
|
||||
const powerLevelsEvent = useStateEvent(room, StateEvent.RoomPowerLevels);
|
||||
const powerLevels: IPowerLevels =
|
||||
powerLevelsEvent?.getContent<IPowerLevels>() ?? DefaultPowerLevels;
|
||||
|
||||
return powerLevels;
|
||||
}
|
||||
|
||||
export const PowerLevelsContext = createContext<IPowerLevels | null>(null);
|
||||
|
||||
export const PowerLevelsContextProvider = PowerLevelsContext.Provider;
|
||||
|
||||
export const usePowerLevelsContext = (): IPowerLevels => {
|
||||
const pl = useContext(PowerLevelsContext);
|
||||
if (!pl) throw new Error('PowerLevelContext is not initialized!');
|
||||
return pl;
|
||||
};
|
||||
|
||||
export const useRoomsPowerLevels = (rooms: Room[]): Map<string, IPowerLevels> => {
|
||||
const mx = useMatrixClient();
|
||||
const [updateCount, forceUpdate] = useForceUpdate();
|
||||
|
||||
useStateEventCallback(
|
||||
mx,
|
||||
useCallback(
|
||||
(event) => {
|
||||
const roomId = event.getRoomId();
|
||||
if (
|
||||
roomId &&
|
||||
event.getType() === StateEvent.RoomPowerLevels &&
|
||||
event.getStateKey() === '' &&
|
||||
rooms.find((r) => r.roomId === roomId)
|
||||
) {
|
||||
forceUpdate();
|
||||
}
|
||||
},
|
||||
[rooms, forceUpdate]
|
||||
)
|
||||
);
|
||||
|
||||
const roomToPowerLevels = useMemo(
|
||||
() => {
|
||||
const rToPl = new Map<string, IPowerLevels>();
|
||||
|
||||
rooms.forEach((room) => {
|
||||
const pl = getStateEvent(room, StateEvent.RoomPowerLevels, '')?.getContent<IPowerLevels>();
|
||||
if (pl) rToPl.set(room.roomId, pl);
|
||||
});
|
||||
|
||||
return rToPl;
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[rooms, updateCount]
|
||||
);
|
||||
|
||||
return roomToPowerLevels;
|
||||
};
|
||||
|
||||
export type GetPowerLevel = (powerLevels: IPowerLevels, userId: string | undefined) => number;
|
||||
export type CanSend = (
|
||||
powerLevels: IPowerLevels,
|
||||
eventType: string | undefined,
|
||||
powerLevel: number
|
||||
) => boolean;
|
||||
export type CanDoAction = (
|
||||
powerLevels: IPowerLevels,
|
||||
action: PowerLevelActions,
|
||||
powerLevel: number
|
||||
) => boolean;
|
||||
|
||||
export type PowerLevelsAPI = {
|
||||
getPowerLevel: GetPowerLevel;
|
||||
|
@ -42,51 +112,58 @@ export type PowerLevelsAPI = {
|
|||
canDoAction: CanDoAction;
|
||||
};
|
||||
|
||||
export function usePowerLevels(room: Room): PowerLevelsAPI {
|
||||
const powerLevelsEvent = useStateEvent(room, StateEvent.RoomPowerLevels);
|
||||
const powerLevels: IPowerLevels = powerLevelsEvent?.getContent() ?? DefaultPowerLevels;
|
||||
export const powerLevelAPI: PowerLevelsAPI = {
|
||||
getPowerLevel: (powerLevels, userId) => {
|
||||
const { users_default: usersDefault, users } = powerLevels;
|
||||
if (userId && users && typeof users[userId] === 'number') {
|
||||
return users[userId];
|
||||
}
|
||||
return usersDefault ?? DefaultPowerLevels.usersDefault;
|
||||
},
|
||||
canSendEvent: (powerLevels, eventType, powerLevel) => {
|
||||
const { events, events_default: eventsDefault } = powerLevels;
|
||||
if (events && eventType && typeof events[eventType] === 'number') {
|
||||
return powerLevel >= events[eventType];
|
||||
}
|
||||
return powerLevel >= (eventsDefault ?? DefaultPowerLevels.eventsDefault);
|
||||
},
|
||||
canSendStateEvent: (powerLevels, eventType, powerLevel) => {
|
||||
const { events, state_default: stateDefault } = powerLevels;
|
||||
if (events && eventType && typeof events[eventType] === 'number') {
|
||||
return powerLevel >= events[eventType];
|
||||
}
|
||||
return powerLevel >= (stateDefault ?? DefaultPowerLevels.stateDefault);
|
||||
},
|
||||
canDoAction: (powerLevels, action, powerLevel) => {
|
||||
const requiredPL = powerLevels[action];
|
||||
if (typeof requiredPL === 'number') {
|
||||
return powerLevel >= requiredPL;
|
||||
}
|
||||
return powerLevel >= DefaultPowerLevels[action];
|
||||
},
|
||||
};
|
||||
|
||||
const getPowerLevel: GetPowerLevel = useCallback(
|
||||
(userId) => {
|
||||
const { users_default: usersDefault, users } = powerLevels;
|
||||
if (users && typeof users[userId] === 'number') {
|
||||
return users[userId];
|
||||
}
|
||||
return usersDefault ?? DefaultPowerLevels.usersDefault;
|
||||
},
|
||||
export const usePowerLevelsAPI = (powerLevels: IPowerLevels) => {
|
||||
const getPowerLevel = useCallback(
|
||||
(userId: string | undefined) => powerLevelAPI.getPowerLevel(powerLevels, userId),
|
||||
[powerLevels]
|
||||
);
|
||||
|
||||
const canSendEvent: CanSend = useCallback(
|
||||
(eventType, powerLevel) => {
|
||||
const { events, events_default: eventsDefault } = powerLevels;
|
||||
if (events && eventType && typeof events[eventType] === 'number') {
|
||||
return powerLevel >= events[eventType];
|
||||
}
|
||||
return powerLevel >= (eventsDefault ?? DefaultPowerLevels.eventsDefault);
|
||||
},
|
||||
const canSendEvent = useCallback(
|
||||
(eventType: string | undefined, powerLevel: number) =>
|
||||
powerLevelAPI.canSendEvent(powerLevels, eventType, powerLevel),
|
||||
[powerLevels]
|
||||
);
|
||||
|
||||
const canSendStateEvent: CanSend = useCallback(
|
||||
(eventType, powerLevel) => {
|
||||
const { events, state_default: stateDefault } = powerLevels;
|
||||
if (events && eventType && typeof events[eventType] === 'number') {
|
||||
return powerLevel >= events[eventType];
|
||||
}
|
||||
return powerLevel >= (stateDefault ?? DefaultPowerLevels.stateDefault);
|
||||
},
|
||||
const canSendStateEvent = useCallback(
|
||||
(eventType: string | undefined, powerLevel: number) =>
|
||||
powerLevelAPI.canSendStateEvent(powerLevels, eventType, powerLevel),
|
||||
[powerLevels]
|
||||
);
|
||||
|
||||
const canDoAction: CanDoAction = useCallback(
|
||||
(action, powerLevel) => {
|
||||
const requiredPL = powerLevels[action];
|
||||
if (typeof requiredPL === 'number') {
|
||||
return powerLevel >= requiredPL;
|
||||
}
|
||||
return powerLevel >= DefaultPowerLevels[action];
|
||||
},
|
||||
const canDoAction = useCallback(
|
||||
(action: PowerLevelActions, powerLevel: number) =>
|
||||
powerLevelAPI.canDoAction(powerLevels, action, powerLevel),
|
||||
[powerLevels]
|
||||
);
|
||||
|
||||
|
@ -96,14 +173,4 @@ export function usePowerLevels(room: Room): PowerLevelsAPI {
|
|||
canSendStateEvent,
|
||||
canDoAction,
|
||||
};
|
||||
}
|
||||
|
||||
export const PowerLevelsContext = createContext<PowerLevelsAPI | null>(null);
|
||||
|
||||
export const PowerLevelsContextProvider = PowerLevelsContext.Provider;
|
||||
|
||||
export const usePowerLevelsAPI = (): PowerLevelsAPI => {
|
||||
const api = useContext(PowerLevelsContext);
|
||||
if (!api) throw new Error('PowerLevelContext is not initialized!');
|
||||
return api;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue