mirror of
https://github.com/cinnyapp/cinny.git
synced 2025-11-17 12:40:28 +03:00
Support room version 12 (#2399)
Some checks are pending
Deploy to Netlify (dev) / Deploy to Netlify (push) Waiting to run
Some checks are pending
Deploy to Netlify (dev) / Deploy to Netlify (push) Waiting to run
* WIP - support room version 12 * add room creators hook * revert changes from powerlevels * improve use room creators hook * add hook to get dm users * add options to add creators in create room/space * add member item component in member drawer * remove unused import * extract member drawer header component * get room creators as set only if room version support them * add room permissions hook * support room v12 creators power * make predecessor event id optional * add info about founders in permissions * allow to create infinite powers to room creators * allow everyone with permission to create infinite power * handle additional creators in room upgrade * add option to follow space tombstone
This commit is contained in:
parent
4d1ae4eafd
commit
f82cfead46
58 changed files with 1717 additions and 783 deletions
|
|
@ -27,6 +27,9 @@ import { stopPropagation } from '../../utils/keyboard';
|
|||
import { useOpenRoomSettings } from '../../state/hooks/roomSettings';
|
||||
import { useSpaceOptionally } from '../../hooks/useSpace';
|
||||
import { useOpenSpaceSettings } from '../../state/hooks/spaceSettings';
|
||||
import { IPowerLevels } from '../../hooks/usePowerLevels';
|
||||
import { getRoomCreatorsForRoomId } from '../../hooks/useRoomCreators';
|
||||
import { getRoomPermissionsAPI } from '../../hooks/useRoomPermissions';
|
||||
|
||||
type HierarchyItemWithParent = HierarchyItem & {
|
||||
parentId: string;
|
||||
|
|
@ -45,7 +48,7 @@ function SuggestMenuItem({
|
|||
const [toggleState, handleToggleSuggested] = useAsyncCallback(
|
||||
useCallback(() => {
|
||||
const newContent: MSpaceChildContent = { ...content, suggested: !content.suggested };
|
||||
return mx.sendStateEvent(parentId, StateEvent.SpaceChild, newContent, roomId);
|
||||
return mx.sendStateEvent(parentId, StateEvent.SpaceChild as any, newContent, roomId);
|
||||
}, [mx, parentId, roomId, content])
|
||||
);
|
||||
|
||||
|
|
@ -82,7 +85,7 @@ function RemoveMenuItem({
|
|||
|
||||
const [removeState, handleRemove] = useAsyncCallback(
|
||||
useCallback(
|
||||
() => mx.sendStateEvent(parentId, StateEvent.SpaceChild, {}, roomId),
|
||||
() => mx.sendStateEvent(parentId, StateEvent.SpaceChild as any, {}, roomId),
|
||||
[mx, parentId, roomId]
|
||||
)
|
||||
);
|
||||
|
|
@ -180,7 +183,7 @@ type HierarchyItemMenuProps = {
|
|||
parentId: string;
|
||||
};
|
||||
joined: boolean;
|
||||
canInvite: boolean;
|
||||
powerLevels?: IPowerLevels;
|
||||
canEditChild: boolean;
|
||||
pinned?: boolean;
|
||||
onTogglePin?: (roomId: string) => void;
|
||||
|
|
@ -188,13 +191,22 @@ type HierarchyItemMenuProps = {
|
|||
export function HierarchyItemMenu({
|
||||
item,
|
||||
joined,
|
||||
canInvite,
|
||||
powerLevels,
|
||||
canEditChild,
|
||||
pinned,
|
||||
onTogglePin,
|
||||
}: HierarchyItemMenuProps) {
|
||||
const mx = useMatrixClient();
|
||||
const [menuAnchor, setMenuAnchor] = useState<RectCords>();
|
||||
|
||||
const canInvite = (): boolean => {
|
||||
if (!powerLevels) return false;
|
||||
const creators = getRoomCreatorsForRoomId(mx, item.roomId);
|
||||
const permissions = getRoomPermissionsAPI(creators, powerLevels);
|
||||
|
||||
return permissions.action('invite', mx.getSafeUserId());
|
||||
};
|
||||
|
||||
const handleOpenMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
|
||||
setMenuAnchor(evt.currentTarget.getBoundingClientRect());
|
||||
};
|
||||
|
|
@ -254,7 +266,7 @@ export function HierarchyItemMenu({
|
|||
<InviteMenuItem
|
||||
item={item}
|
||||
requestClose={handleRequestClose}
|
||||
disabled={!canInvite}
|
||||
disabled={!canInvite()}
|
||||
/>
|
||||
<SettingsMenuItem item={item} requestClose={handleRequestClose} />
|
||||
<UseStateProvider initial={false}>
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ import { useElementSizeObserver } from '../../hooks/useElementSizeObserver';
|
|||
import {
|
||||
IPowerLevels,
|
||||
PowerLevelsContextProvider,
|
||||
powerLevelAPI,
|
||||
usePowerLevels,
|
||||
useRoomsPowerLevels,
|
||||
} from '../../hooks/usePowerLevels';
|
||||
|
|
@ -55,12 +54,13 @@ import { useRoomMembers } from '../../hooks/useRoomMembers';
|
|||
import { SpaceHierarchy } from './SpaceHierarchy';
|
||||
import { useGetRoom } from '../../hooks/useGetRoom';
|
||||
import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
|
||||
import { getRoomPermissionsAPI } from '../../hooks/useRoomPermissions';
|
||||
import { getRoomCreatorsForRoomId } from '../../hooks/useRoomCreators';
|
||||
|
||||
const useCanDropLobbyItem = (
|
||||
space: Room,
|
||||
roomsPowerLevels: Map<string, IPowerLevels>,
|
||||
getRoom: (roomId: string) => Room | undefined,
|
||||
canEditSpaceChild: (powerLevels: IPowerLevels) => boolean
|
||||
getRoom: (roomId: string) => Room | undefined
|
||||
): CanDropCallback => {
|
||||
const mx = useMatrixClient();
|
||||
|
||||
|
|
@ -74,16 +74,20 @@ const useCanDropLobbyItem = (
|
|||
|
||||
const containerSpaceId = space.roomId;
|
||||
|
||||
const powerLevels = roomsPowerLevels.get(containerSpaceId) ?? {};
|
||||
const creators = getRoomCreatorsForRoomId(mx, containerSpaceId);
|
||||
const permissions = getRoomPermissionsAPI(creators, powerLevels);
|
||||
|
||||
if (
|
||||
getRoom(containerSpaceId) === undefined ||
|
||||
!canEditSpaceChild(roomsPowerLevels.get(containerSpaceId) ?? {})
|
||||
!permissions.stateEvent(StateEvent.SpaceChild, mx.getSafeUserId())
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
[space, roomsPowerLevels, getRoom, canEditSpaceChild]
|
||||
[space, roomsPowerLevels, getRoom, mx]
|
||||
);
|
||||
|
||||
const canDropRoom: CanDropCallback = useCallback(
|
||||
|
|
@ -97,30 +101,31 @@ const useCanDropLobbyItem = (
|
|||
// check and do not allow restricted room to be dragged outside
|
||||
// current space if can't change `m.room.join_rules` `content.allow`
|
||||
if (draggingOutsideSpace && restrictedItem) {
|
||||
const itemPowerLevel = roomsPowerLevels.get(item.roomId) ?? {};
|
||||
const userPLInItem = powerLevelAPI.getPowerLevel(
|
||||
itemPowerLevel,
|
||||
mx.getUserId() ?? undefined
|
||||
);
|
||||
const canChangeJoinRuleAllow = powerLevelAPI.canSendStateEvent(
|
||||
itemPowerLevel,
|
||||
const itemPowerLevels = roomsPowerLevels.get(item.roomId) ?? {};
|
||||
const itemCreators = getRoomCreatorsForRoomId(mx, item.roomId);
|
||||
const itemPermissions = getRoomPermissionsAPI(itemCreators, itemPowerLevels);
|
||||
|
||||
const canChangeJoinRuleAllow = itemPermissions.stateEvent(
|
||||
StateEvent.RoomJoinRules,
|
||||
userPLInItem
|
||||
mx.getSafeUserId()
|
||||
);
|
||||
if (!canChangeJoinRuleAllow) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const powerLevels = roomsPowerLevels.get(containerSpaceId) ?? {};
|
||||
const creators = getRoomCreatorsForRoomId(mx, containerSpaceId);
|
||||
const permissions = getRoomPermissionsAPI(creators, powerLevels);
|
||||
if (
|
||||
getRoom(containerSpaceId) === undefined ||
|
||||
!canEditSpaceChild(roomsPowerLevels.get(containerSpaceId) ?? {})
|
||||
!permissions.stateEvent(StateEvent.SpaceChild, mx.getSafeUserId())
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
[mx, getRoom, canEditSpaceChild, roomsPowerLevels]
|
||||
[mx, getRoom, roomsPowerLevels]
|
||||
);
|
||||
|
||||
const canDrop: CanDropCallback = useCallback(
|
||||
|
|
@ -183,16 +188,6 @@ export function Lobby() {
|
|||
|
||||
const getRoom = useGetRoom(allJoinedRooms);
|
||||
|
||||
const canEditSpaceChild = useCallback(
|
||||
(powerLevels: IPowerLevels) =>
|
||||
powerLevelAPI.canSendStateEvent(
|
||||
powerLevels,
|
||||
StateEvent.SpaceChild,
|
||||
powerLevelAPI.getPowerLevel(powerLevels, mx.getUserId() ?? undefined)
|
||||
),
|
||||
[mx]
|
||||
);
|
||||
|
||||
const [draggingItem, setDraggingItem] = useState<HierarchyItem>();
|
||||
const hierarchy = useSpaceHierarchy(
|
||||
space.roomId,
|
||||
|
|
@ -229,12 +224,7 @@ export function Lobby() {
|
|||
)
|
||||
);
|
||||
|
||||
const canDrop: CanDropCallback = useCanDropLobbyItem(
|
||||
space,
|
||||
roomsPowerLevels,
|
||||
getRoom,
|
||||
canEditSpaceChild
|
||||
);
|
||||
const canDrop: CanDropCallback = useCanDropLobbyItem(space, roomsPowerLevels, getRoom);
|
||||
|
||||
const [reorderSpaceState, reorderSpace] = useAsyncCallback(
|
||||
useCallback(
|
||||
|
|
@ -270,7 +260,11 @@ export function Lobby() {
|
|||
.filter((reorder, index) => {
|
||||
if (!reorder.item.parentId) return false;
|
||||
const parentPL = roomsPowerLevels.get(reorder.item.parentId);
|
||||
const canEdit = parentPL && canEditSpaceChild(parentPL);
|
||||
if (!parentPL) return false;
|
||||
|
||||
const creators = getRoomCreatorsForRoomId(mx, reorder.item.parentId);
|
||||
const permissions = getRoomPermissionsAPI(creators, parentPL);
|
||||
const canEdit = permissions.stateEvent(StateEvent.SpaceChild, mx.getSafeUserId());
|
||||
return canEdit && reorder.orderKey !== currentOrders[index];
|
||||
});
|
||||
|
||||
|
|
@ -286,7 +280,7 @@ export function Lobby() {
|
|||
});
|
||||
}
|
||||
},
|
||||
[mx, hierarchy, lex, roomsPowerLevels, canEditSpaceChild]
|
||||
[mx, hierarchy, lex, roomsPowerLevels]
|
||||
)
|
||||
);
|
||||
const reorderingSpace = reorderSpaceState.status === AsyncStatus.Loading;
|
||||
|
|
@ -428,7 +422,7 @@ export function Lobby() {
|
|||
newItems.push(rId);
|
||||
}
|
||||
const newSpacesContent = makeCinnySpacesContent(mx, newItems);
|
||||
mx.setAccountData(AccountDataEvent.CinnySpaces, newSpacesContent);
|
||||
mx.setAccountData(AccountDataEvent.CinnySpaces as any, newSpacesContent as any);
|
||||
},
|
||||
[mx, sidebarItems, sidebarSpaces]
|
||||
);
|
||||
|
|
@ -493,7 +487,6 @@ export function Lobby() {
|
|||
allJoinedRooms={allJoinedRooms}
|
||||
mDirects={mDirects}
|
||||
roomsPowerLevels={roomsPowerLevels}
|
||||
canEditSpaceChild={canEditSpaceChild}
|
||||
categoryId={categoryId}
|
||||
closed={
|
||||
closedCategories.has(categoryId) ||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import { RoomAvatar } from '../../components/room-avatar';
|
|||
import { nameInitials } from '../../utils/common';
|
||||
import * as css from './LobbyHeader.css';
|
||||
import { openInviteUser } from '../../../client/action/navigation';
|
||||
import { IPowerLevels, usePowerLevelsAPI } from '../../hooks/usePowerLevels';
|
||||
import { IPowerLevels } from '../../hooks/usePowerLevels';
|
||||
import { UseStateProvider } from '../../components/UseStateProvider';
|
||||
import { LeaveSpacePrompt } from '../../components/leave-space-prompt';
|
||||
import { stopPropagation } from '../../utils/keyboard';
|
||||
|
|
@ -36,26 +36,30 @@ import { BackRouteHandler } from '../../components/BackRouteHandler';
|
|||
import { mxcUrlToHttp } from '../../utils/matrix';
|
||||
import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
|
||||
import { useOpenSpaceSettings } from '../../state/hooks/spaceSettings';
|
||||
import { useRoomCreators } from '../../hooks/useRoomCreators';
|
||||
import { useRoomPermissions } from '../../hooks/useRoomPermissions';
|
||||
|
||||
type LobbyMenuProps = {
|
||||
roomId: string;
|
||||
powerLevels: IPowerLevels;
|
||||
requestClose: () => void;
|
||||
};
|
||||
const LobbyMenu = forwardRef<HTMLDivElement, LobbyMenuProps>(
|
||||
({ roomId, powerLevels, requestClose }, ref) => {
|
||||
({ powerLevels, requestClose }, ref) => {
|
||||
const mx = useMatrixClient();
|
||||
const { getPowerLevel, canDoAction } = usePowerLevelsAPI(powerLevels);
|
||||
const canInvite = canDoAction('invite', getPowerLevel(mx.getUserId() ?? ''));
|
||||
const space = useSpace();
|
||||
const creators = useRoomCreators(space);
|
||||
|
||||
const permissions = useRoomPermissions(creators, powerLevels);
|
||||
const canInvite = permissions.action('invite', mx.getSafeUserId());
|
||||
const openSpaceSettings = useOpenSpaceSettings();
|
||||
|
||||
const handleInvite = () => {
|
||||
openInviteUser(roomId);
|
||||
openInviteUser(space.roomId);
|
||||
requestClose();
|
||||
};
|
||||
|
||||
const handleRoomSettings = () => {
|
||||
openSpaceSettings(roomId);
|
||||
openSpaceSettings(space.roomId);
|
||||
requestClose();
|
||||
};
|
||||
|
||||
|
|
@ -106,7 +110,7 @@ const LobbyMenu = forwardRef<HTMLDivElement, LobbyMenuProps>(
|
|||
</MenuItem>
|
||||
{promptLeave && (
|
||||
<LeaveSpacePrompt
|
||||
roomId={roomId}
|
||||
roomId={space.roomId}
|
||||
onDone={requestClose}
|
||||
onCancel={() => setPromptLeave(false)}
|
||||
/>
|
||||
|
|
@ -242,7 +246,6 @@ export function LobbyHeader({ showProfile, powerLevels }: LobbyHeaderProps) {
|
|||
}}
|
||||
>
|
||||
<LobbyMenu
|
||||
roomId={space.roomId}
|
||||
powerLevels={powerLevels}
|
||||
requestClose={() => setMenuAnchor(undefined)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -8,14 +8,16 @@ import {
|
|||
HierarchyItemSpace,
|
||||
useFetchSpaceHierarchyLevel,
|
||||
} from '../../hooks/useSpaceHierarchy';
|
||||
import { IPowerLevels, powerLevelAPI } from '../../hooks/usePowerLevels';
|
||||
import { IPowerLevels } from '../../hooks/usePowerLevels';
|
||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||
import { SpaceItemCard } from './SpaceItem';
|
||||
import { AfterItemDropTarget, CanDropCallback } from './DnD';
|
||||
import { HierarchyItemMenu } from './HierarchyItemMenu';
|
||||
import { RoomItemCard } from './RoomItem';
|
||||
import { RoomType } from '../../../types/matrix/room';
|
||||
import { RoomType, StateEvent } from '../../../types/matrix/room';
|
||||
import { SequenceCard } from '../../components/sequence-card';
|
||||
import { getRoomCreatorsForRoomId } from '../../hooks/useRoomCreators';
|
||||
import { getRoomPermissionsAPI } from '../../hooks/useRoomPermissions';
|
||||
|
||||
type SpaceHierarchyProps = {
|
||||
summary: IHierarchyRoom | undefined;
|
||||
|
|
@ -24,7 +26,6 @@ type SpaceHierarchyProps = {
|
|||
allJoinedRooms: Set<string>;
|
||||
mDirects: Set<string>;
|
||||
roomsPowerLevels: Map<string, IPowerLevels>;
|
||||
canEditSpaceChild: (powerLevels: IPowerLevels) => boolean;
|
||||
categoryId: string;
|
||||
closed: boolean;
|
||||
handleClose: MouseEventHandler<HTMLButtonElement>;
|
||||
|
|
@ -48,7 +49,6 @@ export const SpaceHierarchy = forwardRef<HTMLDivElement, SpaceHierarchyProps>(
|
|||
allJoinedRooms,
|
||||
mDirects,
|
||||
roomsPowerLevels,
|
||||
canEditSpaceChild,
|
||||
categoryId,
|
||||
closed,
|
||||
handleClose,
|
||||
|
|
@ -79,25 +79,28 @@ export const SpaceHierarchy = forwardRef<HTMLDivElement, SpaceHierarchyProps>(
|
|||
return s;
|
||||
}, [rooms]);
|
||||
|
||||
const spacePowerLevels = roomsPowerLevels.get(spaceItem.roomId) ?? {};
|
||||
const userPLInSpace = powerLevelAPI.getPowerLevel(
|
||||
spacePowerLevels,
|
||||
mx.getUserId() ?? undefined
|
||||
);
|
||||
const canInviteInSpace = powerLevelAPI.canDoAction(spacePowerLevels, 'invite', userPLInSpace);
|
||||
const spacePowerLevels = roomsPowerLevels.get(spaceItem.roomId);
|
||||
const spaceCreators = getRoomCreatorsForRoomId(mx, spaceItem.roomId);
|
||||
const spacePermissions =
|
||||
spacePowerLevels && getRoomPermissionsAPI(spaceCreators, spacePowerLevels);
|
||||
|
||||
const draggingSpace =
|
||||
draggingItem?.roomId === spaceItem.roomId && draggingItem.parentId === spaceItem.parentId;
|
||||
|
||||
const { parentId } = spaceItem;
|
||||
const parentPowerLevels = parentId ? roomsPowerLevels.get(parentId) ?? {} : undefined;
|
||||
const parentPowerLevels = parentId ? roomsPowerLevels.get(parentId) : undefined;
|
||||
const parentCreators = parentId ? getRoomCreatorsForRoomId(mx, parentId) : undefined;
|
||||
const parentPermissions =
|
||||
parentCreators &&
|
||||
parentPowerLevels &&
|
||||
getRoomPermissionsAPI(parentCreators, parentPowerLevels);
|
||||
|
||||
useEffect(() => {
|
||||
onSpacesFound(Array.from(subspaces.values()));
|
||||
}, [subspaces, onSpacesFound]);
|
||||
|
||||
let childItems = roomItems?.filter((i) => !subspaces.has(i.roomId));
|
||||
if (!canEditSpaceChild(spacePowerLevels)) {
|
||||
if (!spacePermissions?.stateEvent(StateEvent.SpaceChild, mx.getSafeUserId())) {
|
||||
// hide unknown rooms for normal user
|
||||
childItems = childItems?.filter((i) => {
|
||||
const forbidden = error instanceof MatrixError ? error.errcode === 'M_FORBIDDEN' : false;
|
||||
|
|
@ -117,18 +120,22 @@ export const SpaceHierarchy = forwardRef<HTMLDivElement, SpaceHierarchyProps>(
|
|||
closed={closed}
|
||||
handleClose={handleClose}
|
||||
getRoom={getRoom}
|
||||
canEditChild={canEditSpaceChild(spacePowerLevels)}
|
||||
canEditChild={!!spacePermissions?.stateEvent(StateEvent.SpaceChild, mx.getSafeUserId())}
|
||||
canReorder={
|
||||
parentPowerLevels && !disabledReorder ? canEditSpaceChild(parentPowerLevels) : false
|
||||
parentPowerLevels && !disabledReorder && parentPermissions
|
||||
? parentPermissions.stateEvent(StateEvent.SpaceChild, mx.getSafeUserId())
|
||||
: false
|
||||
}
|
||||
options={
|
||||
parentId &&
|
||||
parentPowerLevels && (
|
||||
<HierarchyItemMenu
|
||||
item={{ ...spaceItem, parentId }}
|
||||
canInvite={canInviteInSpace}
|
||||
powerLevels={spacePowerLevels}
|
||||
joined={allJoinedRooms.has(spaceItem.roomId)}
|
||||
canEditChild={canEditSpaceChild(parentPowerLevels)}
|
||||
canEditChild={
|
||||
!!parentPermissions?.stateEvent(StateEvent.SpaceChild, mx.getSafeUserId())
|
||||
}
|
||||
pinned={pinned}
|
||||
onTogglePin={togglePinToSidebar}
|
||||
/>
|
||||
|
|
@ -151,15 +158,6 @@ export const SpaceHierarchy = forwardRef<HTMLDivElement, SpaceHierarchyProps>(
|
|||
const roomSummary = rooms.get(roomItem.roomId);
|
||||
|
||||
const roomPowerLevels = roomsPowerLevels.get(roomItem.roomId) ?? {};
|
||||
const userPLInRoom = powerLevelAPI.getPowerLevel(
|
||||
roomPowerLevels,
|
||||
mx.getUserId() ?? undefined
|
||||
);
|
||||
const canInviteInRoom = powerLevelAPI.canDoAction(
|
||||
roomPowerLevels,
|
||||
'invite',
|
||||
userPLInRoom
|
||||
);
|
||||
|
||||
const lastItem = index === childItems.length;
|
||||
const nextRoomId = lastItem ? nextSpaceId : childItems[index + 1]?.roomId;
|
||||
|
|
@ -178,13 +176,18 @@ export const SpaceHierarchy = forwardRef<HTMLDivElement, SpaceHierarchyProps>(
|
|||
dm={mDirects.has(roomItem.roomId)}
|
||||
onOpen={onOpenRoom}
|
||||
getRoom={getRoom}
|
||||
canReorder={canEditSpaceChild(spacePowerLevels) && !disabledReorder}
|
||||
canReorder={
|
||||
!!spacePermissions?.stateEvent(StateEvent.SpaceChild, mx.getSafeUserId()) &&
|
||||
!disabledReorder
|
||||
}
|
||||
options={
|
||||
<HierarchyItemMenu
|
||||
item={roomItem}
|
||||
canInvite={canInviteInRoom}
|
||||
powerLevels={roomPowerLevels}
|
||||
joined={allJoinedRooms.has(roomItem.roomId)}
|
||||
canEditChild={canEditSpaceChild(spacePowerLevels)}
|
||||
canEditChild={
|
||||
!!spacePermissions?.stateEvent(StateEvent.SpaceChild, mx.getSafeUserId())
|
||||
}
|
||||
/>
|
||||
}
|
||||
after={
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue