Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ed8260877 | ||
|
|
44347db6e4 | ||
|
|
91632aa193 | ||
|
|
e6f4eeca8e | ||
|
|
a23279e633 | ||
|
|
83057ebbd4 |
23
package-lock.json
generated
23
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "cinny",
|
"name": "cinny",
|
||||||
"version": "4.8.0",
|
"version": "4.8.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "cinny",
|
"name": "cinny",
|
||||||
"version": "4.8.0",
|
"version": "4.8.1",
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@atlaskit/pragmatic-drag-and-drop": "1.1.6",
|
"@atlaskit/pragmatic-drag-and-drop": "1.1.6",
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
"file-saver": "2.0.5",
|
"file-saver": "2.0.5",
|
||||||
"flux": "4.0.3",
|
"flux": "4.0.3",
|
||||||
"focus-trap-react": "10.0.2",
|
"focus-trap-react": "10.0.2",
|
||||||
"folds": "2.1.0",
|
"folds": "2.2.0",
|
||||||
"formik": "2.4.6",
|
"formik": "2.4.6",
|
||||||
"html-dom-parser": "4.0.0",
|
"html-dom-parser": "4.0.0",
|
||||||
"html-react-parser": "4.2.0",
|
"html-react-parser": "4.2.0",
|
||||||
@@ -7265,15 +7265,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/folds": {
|
"node_modules/folds": {
|
||||||
"version": "2.1.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/folds/-/folds-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/folds/-/folds-2.2.0.tgz",
|
||||||
"integrity": "sha512-KwAG8bH3jsyZ9FKPMg+6ABV2YOcpp4nL0cCelsalnaPeRThkc5fgG1Xj5mhmdffYKjEXpEbERi5qmGbepgJryg==",
|
"integrity": "sha512-uOfck5eWEIK11rhOAEdSoPIvMXwv+D1Go03pxSAKezWVb+uRoBdmE6LEqiLOF+ac4DGmZRMPvpdDsXCg7EVNIg==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@vanilla-extract/css": "^1.9.2",
|
"@vanilla-extract/css": "1.9.2",
|
||||||
"@vanilla-extract/recipes": "^0.3.0",
|
"@vanilla-extract/recipes": "0.3.0",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "2.3.2",
|
||||||
"react": "^17.0.0",
|
"react": "17.0.0",
|
||||||
"react-dom": "^17.0.0"
|
"react-dom": "17.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/for-each": {
|
"node_modules/for-each": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cinny",
|
"name": "cinny",
|
||||||
"version": "4.8.0",
|
"version": "4.8.1",
|
||||||
"description": "Yet another matrix client",
|
"description": "Yet another matrix client",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
"file-saver": "2.0.5",
|
"file-saver": "2.0.5",
|
||||||
"flux": "4.0.3",
|
"flux": "4.0.3",
|
||||||
"focus-trap-react": "10.0.2",
|
"focus-trap-react": "10.0.2",
|
||||||
"folds": "2.1.0",
|
"folds": "2.2.0",
|
||||||
"formik": "2.4.6",
|
"formik": "2.4.6",
|
||||||
"html-dom-parser": "4.0.0",
|
"html-dom-parser": "4.0.0",
|
||||||
"html-react-parser": "4.2.0",
|
"html-react-parser": "4.2.0",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React, { useCallback, useMemo } from 'react';
|
|||||||
import { color, Text } from 'folds';
|
import { color, Text } from 'folds';
|
||||||
import { JoinRule, MatrixError, RestrictedAllowType } from 'matrix-js-sdk';
|
import { JoinRule, MatrixError, RestrictedAllowType } from 'matrix-js-sdk';
|
||||||
import { RoomJoinRulesEventContent } from 'matrix-js-sdk/lib/types';
|
import { RoomJoinRulesEventContent } from 'matrix-js-sdk/lib/types';
|
||||||
|
import { useAtomValue } from 'jotai';
|
||||||
import { IPowerLevels, powerLevelAPI } from '../../../hooks/usePowerLevels';
|
import { IPowerLevels, powerLevelAPI } from '../../../hooks/usePowerLevels';
|
||||||
import {
|
import {
|
||||||
ExtendedJoinRules,
|
ExtendedJoinRules,
|
||||||
@@ -20,6 +21,12 @@ import { useStateEvent } from '../../../hooks/useStateEvent';
|
|||||||
import { useSpaceOptionally } from '../../../hooks/useSpace';
|
import { useSpaceOptionally } from '../../../hooks/useSpace';
|
||||||
import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
|
import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
|
||||||
import { getStateEvents } from '../../../utils/room';
|
import { getStateEvents } from '../../../utils/room';
|
||||||
|
import {
|
||||||
|
useRecursiveChildSpaceScopeFactory,
|
||||||
|
useSpaceChildren,
|
||||||
|
} from '../../../state/hooks/roomList';
|
||||||
|
import { allRoomsAtom } from '../../../state/room-list/roomList';
|
||||||
|
import { roomToParentsAtom } from '../../../state/room/roomToParents';
|
||||||
|
|
||||||
type RestrictedRoomAllowContent = {
|
type RestrictedRoomAllowContent = {
|
||||||
room_id: string;
|
room_id: string;
|
||||||
@@ -36,7 +43,11 @@ export function RoomJoinRules({ powerLevels }: RoomJoinRulesProps) {
|
|||||||
const allowKnockRestricted = roomVersion >= 10;
|
const allowKnockRestricted = roomVersion >= 10;
|
||||||
const allowRestricted = roomVersion >= 8;
|
const allowRestricted = roomVersion >= 8;
|
||||||
const allowKnock = roomVersion >= 7;
|
const allowKnock = roomVersion >= 7;
|
||||||
|
|
||||||
|
const roomIdToParents = useAtomValue(roomToParentsAtom);
|
||||||
const space = useSpaceOptionally();
|
const space = useSpaceOptionally();
|
||||||
|
const subspacesScope = useRecursiveChildSpaceScopeFactory(mx, roomIdToParents);
|
||||||
|
const subspaces = useSpaceChildren(allRoomsAtom, space?.roomId ?? '', subspacesScope);
|
||||||
|
|
||||||
const userPowerLevel = powerLevelAPI.getPowerLevel(powerLevels, mx.getSafeUserId());
|
const userPowerLevel = powerLevelAPI.getPowerLevel(powerLevels, mx.getSafeUserId());
|
||||||
const canEdit = powerLevelAPI.canSendStateEvent(
|
const canEdit = powerLevelAPI.canSendStateEvent(
|
||||||
@@ -74,9 +85,22 @@ export function RoomJoinRules({ powerLevels }: RoomJoinRulesProps) {
|
|||||||
async (joinRule: ExtendedJoinRules) => {
|
async (joinRule: ExtendedJoinRules) => {
|
||||||
const allow: RestrictedRoomAllowContent[] = [];
|
const allow: RestrictedRoomAllowContent[] = [];
|
||||||
if (joinRule === JoinRule.Restricted || joinRule === 'knock_restricted') {
|
if (joinRule === JoinRule.Restricted || joinRule === 'knock_restricted') {
|
||||||
const parents = getStateEvents(room, StateEvent.SpaceParent).map((event) =>
|
const roomParents = roomIdToParents.get(room.roomId);
|
||||||
event.getStateKey()
|
|
||||||
);
|
const parents = getStateEvents(room, StateEvent.SpaceParent)
|
||||||
|
.map((event) => event.getStateKey())
|
||||||
|
.filter((parentId) => typeof parentId === 'string')
|
||||||
|
.filter((parentId) => roomParents?.has(parentId));
|
||||||
|
|
||||||
|
if (parents.length === 0 && space && roomParents) {
|
||||||
|
// if no m.space.parent found
|
||||||
|
// find parent in current space
|
||||||
|
const selectedParents = subspaces.filter((rId) => roomParents.has(rId));
|
||||||
|
if (roomParents.has(space.roomId)) {
|
||||||
|
selectedParents.push(space.roomId);
|
||||||
|
}
|
||||||
|
selectedParents.forEach((pId) => parents.push(pId));
|
||||||
|
}
|
||||||
parents.forEach((parentRoomId) => {
|
parents.forEach((parentRoomId) => {
|
||||||
if (!parentRoomId) return;
|
if (!parentRoomId) return;
|
||||||
allow.push({
|
allow.push({
|
||||||
@@ -92,7 +116,7 @@ export function RoomJoinRules({ powerLevels }: RoomJoinRulesProps) {
|
|||||||
if (allow.length > 0) c.allow = allow;
|
if (allow.length > 0) c.allow = allow;
|
||||||
await mx.sendStateEvent(room.roomId, StateEvent.RoomJoinRules as any, c);
|
await mx.sendStateEvent(room.roomId, StateEvent.RoomJoinRules as any, c);
|
||||||
},
|
},
|
||||||
[mx, room]
|
[mx, room, space, subspaces, roomIdToParents]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { MouseEventHandler, useCallback, useMemo, useRef, useState } from 'react';
|
import React, { MouseEventHandler, useCallback, useMemo, useRef, useState } from 'react';
|
||||||
import { Box, Icon, IconButton, Icons, Line, Scroll, config } from 'folds';
|
import { Box, Chip, Icon, IconButton, Icons, Line, Scroll, Spinner, Text, config } from 'folds';
|
||||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||||
import { useAtom, useAtomValue } from 'jotai';
|
import { useAtom, useAtomValue } from 'jotai';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
@@ -36,7 +36,7 @@ import { makeLobbyCategoryId } from '../../state/closedLobbyCategories';
|
|||||||
import { useCategoryHandler } from '../../hooks/useCategoryHandler';
|
import { useCategoryHandler } from '../../hooks/useCategoryHandler';
|
||||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||||
import { allRoomsAtom } from '../../state/room-list/roomList';
|
import { allRoomsAtom } from '../../state/room-list/roomList';
|
||||||
import { getCanonicalAliasOrRoomId } from '../../utils/matrix';
|
import { getCanonicalAliasOrRoomId, rateLimitedActions } from '../../utils/matrix';
|
||||||
import { getSpaceRoomPath } from '../../pages/pathUtils';
|
import { getSpaceRoomPath } from '../../pages/pathUtils';
|
||||||
import { StateEvent } from '../../../types/matrix/room';
|
import { StateEvent } from '../../../types/matrix/room';
|
||||||
import { CanDropCallback, useDnDMonitor } from './DnD';
|
import { CanDropCallback, useDnDMonitor } from './DnD';
|
||||||
@@ -53,6 +53,95 @@ import { roomToParentsAtom } from '../../state/room/roomToParents';
|
|||||||
import { AccountDataEvent } from '../../../types/matrix/accountData';
|
import { AccountDataEvent } from '../../../types/matrix/accountData';
|
||||||
import { useRoomMembers } from '../../hooks/useRoomMembers';
|
import { useRoomMembers } from '../../hooks/useRoomMembers';
|
||||||
import { SpaceHierarchy } from './SpaceHierarchy';
|
import { SpaceHierarchy } from './SpaceHierarchy';
|
||||||
|
import { useGetRoom } from '../../hooks/useGetRoom';
|
||||||
|
import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
|
||||||
|
|
||||||
|
const useCanDropLobbyItem = (
|
||||||
|
space: Room,
|
||||||
|
roomsPowerLevels: Map<string, IPowerLevels>,
|
||||||
|
getRoom: (roomId: string) => Room | undefined,
|
||||||
|
canEditSpaceChild: (powerLevels: IPowerLevels) => boolean
|
||||||
|
): CanDropCallback => {
|
||||||
|
const mx = useMatrixClient();
|
||||||
|
|
||||||
|
const canDropSpace: CanDropCallback = useCallback(
|
||||||
|
(item, container) => {
|
||||||
|
if (!('space' in container.item)) {
|
||||||
|
// can not drop around rooms.
|
||||||
|
// space can only be drop around other spaces
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const containerSpaceId = space.roomId;
|
||||||
|
|
||||||
|
if (
|
||||||
|
getRoom(containerSpaceId) === undefined ||
|
||||||
|
!canEditSpaceChild(roomsPowerLevels.get(containerSpaceId) ?? {})
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
[space, roomsPowerLevels, getRoom, canEditSpaceChild]
|
||||||
|
);
|
||||||
|
|
||||||
|
const canDropRoom: CanDropCallback = useCallback(
|
||||||
|
(item, container) => {
|
||||||
|
const containerSpaceId =
|
||||||
|
'space' in container.item ? container.item.roomId : container.item.parentId;
|
||||||
|
|
||||||
|
const draggingOutsideSpace = item.parentId !== containerSpaceId;
|
||||||
|
const restrictedItem = mx.getRoom(item.roomId)?.getJoinRule() === JoinRule.Restricted;
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
StateEvent.RoomJoinRules,
|
||||||
|
userPLInItem
|
||||||
|
);
|
||||||
|
if (!canChangeJoinRuleAllow) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
getRoom(containerSpaceId) === undefined ||
|
||||||
|
!canEditSpaceChild(roomsPowerLevels.get(containerSpaceId) ?? {})
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
[mx, getRoom, canEditSpaceChild, roomsPowerLevels]
|
||||||
|
);
|
||||||
|
|
||||||
|
const canDrop: CanDropCallback = useCallback(
|
||||||
|
(item, container): boolean => {
|
||||||
|
if (item.roomId === container.item.roomId || item.roomId === container.nextRoomId) {
|
||||||
|
// can not drop before or after itself
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we are dragging a space
|
||||||
|
if ('space' in item) {
|
||||||
|
return canDropSpace(item, container);
|
||||||
|
}
|
||||||
|
|
||||||
|
return canDropRoom(item, container);
|
||||||
|
},
|
||||||
|
[canDropSpace, canDropRoom]
|
||||||
|
);
|
||||||
|
|
||||||
|
return canDrop;
|
||||||
|
};
|
||||||
|
|
||||||
export function Lobby() {
|
export function Lobby() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -92,15 +181,7 @@ export function Lobby() {
|
|||||||
useCallback((w, height) => setHeroSectionHeight(height), [])
|
useCallback((w, height) => setHeroSectionHeight(height), [])
|
||||||
);
|
);
|
||||||
|
|
||||||
const getRoom = useCallback(
|
const getRoom = useGetRoom(allJoinedRooms);
|
||||||
(rId: string) => {
|
|
||||||
if (allJoinedRooms.has(rId)) {
|
|
||||||
return mx.getRoom(rId) ?? undefined;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
},
|
|
||||||
[mx, allJoinedRooms]
|
|
||||||
);
|
|
||||||
|
|
||||||
const canEditSpaceChild = useCallback(
|
const canEditSpaceChild = useCallback(
|
||||||
(powerLevels: IPowerLevels) =>
|
(powerLevels: IPowerLevels) =>
|
||||||
@@ -150,64 +231,16 @@ export function Lobby() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const canDrop: CanDropCallback = useCallback(
|
const canDrop: CanDropCallback = useCanDropLobbyItem(
|
||||||
(item, container): boolean => {
|
space,
|
||||||
const restrictedItem = mx.getRoom(item.roomId)?.getJoinRule() === JoinRule.Restricted;
|
roomsPowerLevels,
|
||||||
if (item.roomId === container.item.roomId || item.roomId === container.nextRoomId) {
|
getRoom,
|
||||||
// can not drop before or after itself
|
canEditSpaceChild
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('space' in item) {
|
|
||||||
if (!('space' in container.item)) return false;
|
|
||||||
const containerSpaceId = space.roomId;
|
|
||||||
|
|
||||||
if (
|
|
||||||
getRoom(containerSpaceId) === undefined ||
|
|
||||||
!canEditSpaceChild(roomsPowerLevels.get(containerSpaceId) ?? {})
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const containerSpaceId =
|
|
||||||
'space' in container.item ? container.item.roomId : container.item.parentId;
|
|
||||||
|
|
||||||
const dropOutsideSpace = item.parentId !== containerSpaceId;
|
|
||||||
|
|
||||||
if (dropOutsideSpace && restrictedItem) {
|
|
||||||
// do not allow restricted room to drop outside
|
|
||||||
// current space if can't change join rule allow
|
|
||||||
const itemPowerLevel = roomsPowerLevels.get(item.roomId) ?? {};
|
|
||||||
const userPLInItem = powerLevelAPI.getPowerLevel(
|
|
||||||
itemPowerLevel,
|
|
||||||
mx.getUserId() ?? undefined
|
|
||||||
);
|
|
||||||
const canChangeJoinRuleAllow = powerLevelAPI.canSendStateEvent(
|
|
||||||
itemPowerLevel,
|
|
||||||
StateEvent.RoomJoinRules,
|
|
||||||
userPLInItem
|
|
||||||
);
|
|
||||||
if (!canChangeJoinRuleAllow) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
getRoom(containerSpaceId) === undefined ||
|
|
||||||
!canEditSpaceChild(roomsPowerLevels.get(containerSpaceId) ?? {})
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
[getRoom, space.roomId, roomsPowerLevels, canEditSpaceChild, mx]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const reorderSpace = useCallback(
|
const [reorderSpaceState, reorderSpace] = useAsyncCallback(
|
||||||
(item: HierarchyItemSpace, containerItem: HierarchyItem) => {
|
useCallback(
|
||||||
|
async (item: HierarchyItemSpace, containerItem: HierarchyItem) => {
|
||||||
if (!item.parentId) return;
|
if (!item.parentId) return;
|
||||||
|
|
||||||
const itemSpaces: HierarchyItemSpace[] = hierarchy
|
const itemSpaces: HierarchyItemSpace[] = hierarchy
|
||||||
@@ -231,26 +264,38 @@ export function Lobby() {
|
|||||||
|
|
||||||
const newOrders = orderKeys(lex, currentOrders);
|
const newOrders = orderKeys(lex, currentOrders);
|
||||||
|
|
||||||
newOrders?.forEach((orderKey, index) => {
|
const reorders = newOrders
|
||||||
const itm = itemSpaces[index];
|
?.map((orderKey, index) => ({
|
||||||
if (!itm || !itm.parentId) return;
|
item: itemSpaces[index],
|
||||||
const parentPL = roomsPowerLevels.get(itm.parentId);
|
orderKey,
|
||||||
|
}))
|
||||||
|
.filter((reorder, index) => {
|
||||||
|
if (!reorder.item.parentId) return false;
|
||||||
|
const parentPL = roomsPowerLevels.get(reorder.item.parentId);
|
||||||
const canEdit = parentPL && canEditSpaceChild(parentPL);
|
const canEdit = parentPL && canEditSpaceChild(parentPL);
|
||||||
if (canEdit && orderKey !== currentOrders[index]) {
|
return canEdit && reorder.orderKey !== currentOrders[index];
|
||||||
mx.sendStateEvent(
|
|
||||||
itm.parentId,
|
|
||||||
StateEvent.SpaceChild as any,
|
|
||||||
{ ...itm.content, order: orderKey },
|
|
||||||
itm.roomId
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (reorders) {
|
||||||
|
await rateLimitedActions(reorders, async (reorder) => {
|
||||||
|
if (!reorder.item.parentId) return;
|
||||||
|
await mx.sendStateEvent(
|
||||||
|
reorder.item.parentId,
|
||||||
|
StateEvent.SpaceChild as any,
|
||||||
|
{ ...reorder.item.content, order: reorder.orderKey },
|
||||||
|
reorder.item.roomId
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[mx, hierarchy, lex, roomsPowerLevels, canEditSpaceChild]
|
[mx, hierarchy, lex, roomsPowerLevels, canEditSpaceChild]
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
const reorderingSpace = reorderSpaceState.status === AsyncStatus.Loading;
|
||||||
|
|
||||||
const reorderRoom = useCallback(
|
const [reorderRoomState, reorderRoom] = useAsyncCallback(
|
||||||
(item: HierarchyItem, containerItem: HierarchyItem): void => {
|
useCallback(
|
||||||
|
async (item: HierarchyItem, containerItem: HierarchyItem) => {
|
||||||
const itemRoom = mx.getRoom(item.roomId);
|
const itemRoom = mx.getRoom(item.roomId);
|
||||||
if (!item.parentId) {
|
if (!item.parentId) {
|
||||||
return;
|
return;
|
||||||
@@ -259,6 +304,7 @@ export function Lobby() {
|
|||||||
'space' in containerItem ? containerItem.roomId : containerItem.parentId;
|
'space' in containerItem ? containerItem.roomId : containerItem.parentId;
|
||||||
const itemContent = item.content;
|
const itemContent = item.content;
|
||||||
|
|
||||||
|
// remove from current space
|
||||||
if (item.parentId !== containerParentId) {
|
if (item.parentId !== containerParentId) {
|
||||||
mx.sendStateEvent(item.parentId, StateEvent.SpaceChild as any, {}, item.roomId);
|
mx.sendStateEvent(item.parentId, StateEvent.SpaceChild as any, {}, item.roomId);
|
||||||
}
|
}
|
||||||
@@ -277,7 +323,8 @@ export function Lobby() {
|
|||||||
|
|
||||||
if (joinRuleContent) {
|
if (joinRuleContent) {
|
||||||
const allow =
|
const allow =
|
||||||
joinRuleContent.allow?.filter((allowRule) => allowRule.room_id !== item.parentId) ?? [];
|
joinRuleContent.allow?.filter((allowRule) => allowRule.room_id !== item.parentId) ??
|
||||||
|
[];
|
||||||
allow.push({ type: RestrictedAllowType.RoomMembership, room_id: containerParentId });
|
allow.push({ type: RestrictedAllowType.RoomMembership, room_id: containerParentId });
|
||||||
mx.sendStateEvent(itemRoom.roomId, StateEvent.RoomJoinRules as any, {
|
mx.sendStateEvent(itemRoom.roomId, StateEvent.RoomJoinRules as any, {
|
||||||
...joinRuleContent,
|
...joinRuleContent,
|
||||||
@@ -310,20 +357,29 @@ export function Lobby() {
|
|||||||
|
|
||||||
const newOrders = orderKeys(lex, currentOrders);
|
const newOrders = orderKeys(lex, currentOrders);
|
||||||
|
|
||||||
newOrders?.forEach((orderKey, index) => {
|
const reorders = newOrders
|
||||||
const itm = itemSpaces[index];
|
?.map((orderKey, index) => ({
|
||||||
if (itm && orderKey !== currentOrders[index]) {
|
item: itemSpaces[index],
|
||||||
mx.sendStateEvent(
|
orderKey,
|
||||||
|
}))
|
||||||
|
.filter((reorder, index) => reorder.item && reorder.orderKey !== currentOrders[index]);
|
||||||
|
|
||||||
|
if (reorders) {
|
||||||
|
await rateLimitedActions(reorders, async (reorder) => {
|
||||||
|
await mx.sendStateEvent(
|
||||||
containerParentId,
|
containerParentId,
|
||||||
StateEvent.SpaceChild as any,
|
StateEvent.SpaceChild as any,
|
||||||
{ ...itm.content, order: orderKey },
|
{ ...reorder.item.content, order: reorder.orderKey },
|
||||||
itm.roomId
|
reorder.item.roomId
|
||||||
);
|
);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[mx, hierarchy, lex]
|
[mx, hierarchy, lex]
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
const reorderingRoom = reorderRoomState.status === AsyncStatus.Loading;
|
||||||
|
const reordering = reorderingRoom || reorderingSpace;
|
||||||
|
|
||||||
useDnDMonitor(
|
useDnDMonitor(
|
||||||
scrollRef,
|
scrollRef,
|
||||||
@@ -449,6 +505,7 @@ export function Lobby() {
|
|||||||
draggingItem={draggingItem}
|
draggingItem={draggingItem}
|
||||||
onDragging={setDraggingItem}
|
onDragging={setDraggingItem}
|
||||||
canDrop={canDrop}
|
canDrop={canDrop}
|
||||||
|
disabledReorder={reordering}
|
||||||
nextSpaceId={nextSpaceId}
|
nextSpaceId={nextSpaceId}
|
||||||
getRoom={getRoom}
|
getRoom={getRoom}
|
||||||
pinned={sidebarSpaces.has(item.space.roomId)}
|
pinned={sidebarSpaces.has(item.space.roomId)}
|
||||||
@@ -460,6 +517,28 @@ export function Lobby() {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
{reordering && (
|
||||||
|
<Box
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: config.space.S400,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
zIndex: 2,
|
||||||
|
pointerEvents: 'none',
|
||||||
|
}}
|
||||||
|
justifyContent="Center"
|
||||||
|
>
|
||||||
|
<Chip
|
||||||
|
variant="Secondary"
|
||||||
|
outlined
|
||||||
|
radii="Pill"
|
||||||
|
before={<Spinner variant="Secondary" fill="Soft" size="100" />}
|
||||||
|
>
|
||||||
|
<Text size="L400">Reordering</Text>
|
||||||
|
</Chip>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
</PageContentCenter>
|
</PageContentCenter>
|
||||||
</PageContent>
|
</PageContent>
|
||||||
</Scroll>
|
</Scroll>
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ type SpaceHierarchyProps = {
|
|||||||
draggingItem?: HierarchyItem;
|
draggingItem?: HierarchyItem;
|
||||||
onDragging: (item?: HierarchyItem) => void;
|
onDragging: (item?: HierarchyItem) => void;
|
||||||
canDrop: CanDropCallback;
|
canDrop: CanDropCallback;
|
||||||
|
disabledReorder?: boolean;
|
||||||
nextSpaceId?: string;
|
nextSpaceId?: string;
|
||||||
getRoom: (roomId: string) => Room | undefined;
|
getRoom: (roomId: string) => Room | undefined;
|
||||||
pinned: boolean;
|
pinned: boolean;
|
||||||
@@ -54,6 +55,7 @@ export const SpaceHierarchy = forwardRef<HTMLDivElement, SpaceHierarchyProps>(
|
|||||||
draggingItem,
|
draggingItem,
|
||||||
onDragging,
|
onDragging,
|
||||||
canDrop,
|
canDrop,
|
||||||
|
disabledReorder,
|
||||||
nextSpaceId,
|
nextSpaceId,
|
||||||
getRoom,
|
getRoom,
|
||||||
pinned,
|
pinned,
|
||||||
@@ -116,7 +118,9 @@ export const SpaceHierarchy = forwardRef<HTMLDivElement, SpaceHierarchyProps>(
|
|||||||
handleClose={handleClose}
|
handleClose={handleClose}
|
||||||
getRoom={getRoom}
|
getRoom={getRoom}
|
||||||
canEditChild={canEditSpaceChild(spacePowerLevels)}
|
canEditChild={canEditSpaceChild(spacePowerLevels)}
|
||||||
canReorder={parentPowerLevels ? canEditSpaceChild(parentPowerLevels) : false}
|
canReorder={
|
||||||
|
parentPowerLevels && !disabledReorder ? canEditSpaceChild(parentPowerLevels) : false
|
||||||
|
}
|
||||||
options={
|
options={
|
||||||
parentId &&
|
parentId &&
|
||||||
parentPowerLevels && (
|
parentPowerLevels && (
|
||||||
@@ -174,7 +178,7 @@ export const SpaceHierarchy = forwardRef<HTMLDivElement, SpaceHierarchyProps>(
|
|||||||
dm={mDirects.has(roomItem.roomId)}
|
dm={mDirects.has(roomItem.roomId)}
|
||||||
onOpen={onOpenRoom}
|
onOpen={onOpenRoom}
|
||||||
getRoom={getRoom}
|
getRoom={getRoom}
|
||||||
canReorder={canEditSpaceChild(spacePowerLevels)}
|
canReorder={canEditSpaceChild(spacePowerLevels) && !disabledReorder}
|
||||||
options={
|
options={
|
||||||
<HierarchyItemMenu
|
<HierarchyItemMenu
|
||||||
item={roomItem}
|
item={roomItem}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ const useSettingsMenuItems = (): SettingsMenuItem[] =>
|
|||||||
{
|
{
|
||||||
page: SettingsPages.DevicesPage,
|
page: SettingsPages.DevicesPage,
|
||||||
name: 'Devices',
|
name: 'Devices',
|
||||||
icon: Icons.Category,
|
icon: Icons.Monitor,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
page: SettingsPages.EmojisStickersPage,
|
page: SettingsPages.EmojisStickersPage,
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import { getOrphanParents } from '../utils/room';
|
|||||||
import { roomToParentsAtom } from '../state/room/roomToParents';
|
import { roomToParentsAtom } from '../state/room/roomToParents';
|
||||||
import { mDirectAtom } from '../state/mDirectList';
|
import { mDirectAtom } from '../state/mDirectList';
|
||||||
import { useSelectedSpace } from './router/useSelectedSpace';
|
import { useSelectedSpace } from './router/useSelectedSpace';
|
||||||
|
import { settingsAtom } from '../state/settings';
|
||||||
|
import { useSetting } from '../state/hooks/settings';
|
||||||
|
|
||||||
export const useRoomNavigate = () => {
|
export const useRoomNavigate = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -20,6 +22,7 @@ export const useRoomNavigate = () => {
|
|||||||
const roomToParents = useAtomValue(roomToParentsAtom);
|
const roomToParents = useAtomValue(roomToParentsAtom);
|
||||||
const mDirects = useAtomValue(mDirectAtom);
|
const mDirects = useAtomValue(mDirectAtom);
|
||||||
const spaceSelectedId = useSelectedSpace();
|
const spaceSelectedId = useSelectedSpace();
|
||||||
|
const [developerTools] = useSetting(settingsAtom, 'developerTools');
|
||||||
|
|
||||||
const navigateSpace = useCallback(
|
const navigateSpace = useCallback(
|
||||||
(roomId: string) => {
|
(roomId: string) => {
|
||||||
@@ -32,15 +35,22 @@ export const useRoomNavigate = () => {
|
|||||||
const navigateRoom = useCallback(
|
const navigateRoom = useCallback(
|
||||||
(roomId: string, eventId?: string, opts?: NavigateOptions) => {
|
(roomId: string, eventId?: string, opts?: NavigateOptions) => {
|
||||||
const roomIdOrAlias = getCanonicalAliasOrRoomId(mx, roomId);
|
const roomIdOrAlias = getCanonicalAliasOrRoomId(mx, roomId);
|
||||||
|
const openSpaceTimeline = developerTools && spaceSelectedId === roomId;
|
||||||
|
|
||||||
const orphanParents = getOrphanParents(roomToParents, roomId);
|
const orphanParents = openSpaceTimeline ? [roomId] : getOrphanParents(roomToParents, roomId);
|
||||||
if (orphanParents.length > 0) {
|
if (orphanParents.length > 0) {
|
||||||
const pSpaceIdOrAlias = getCanonicalAliasOrRoomId(
|
const pSpaceIdOrAlias = getCanonicalAliasOrRoomId(
|
||||||
mx,
|
mx,
|
||||||
spaceSelectedId && orphanParents.includes(spaceSelectedId)
|
spaceSelectedId && orphanParents.includes(spaceSelectedId)
|
||||||
? spaceSelectedId
|
? spaceSelectedId
|
||||||
: orphanParents[0]
|
: orphanParents[0] // TODO: better orphan parent selection.
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (openSpaceTimeline) {
|
||||||
|
navigate(getSpaceRoomPath(pSpaceIdOrAlias, roomId, eventId), opts);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
navigate(getSpaceRoomPath(pSpaceIdOrAlias, roomIdOrAlias, eventId), opts);
|
navigate(getSpaceRoomPath(pSpaceIdOrAlias, roomIdOrAlias, eventId), opts);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -52,7 +62,7 @@ export const useRoomNavigate = () => {
|
|||||||
|
|
||||||
navigate(getHomeRoomPath(roomIdOrAlias, eventId), opts);
|
navigate(getHomeRoomPath(roomIdOrAlias, eventId), opts);
|
||||||
},
|
},
|
||||||
[mx, navigate, spaceSelectedId, roomToParents, mDirects]
|
[mx, navigate, spaceSelectedId, roomToParents, mDirects, developerTools]
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -28,9 +28,11 @@ import { allRoomsAtom } from '../../state/room-list/roomList';
|
|||||||
import { mDirectAtom } from '../../state/mDirectList';
|
import { mDirectAtom } from '../../state/mDirectList';
|
||||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||||
import { getViaServers } from '../../plugins/via-servers';
|
import { getViaServers } from '../../plugins/via-servers';
|
||||||
|
import { rateLimitedActions } from '../../utils/matrix';
|
||||||
|
import { useAlive } from '../../hooks/useAlive';
|
||||||
|
|
||||||
function SpaceAddExistingContent({ roomId, spaces: onlySpaces }) {
|
function SpaceAddExistingContent({ roomId, spaces: onlySpaces }) {
|
||||||
const mountStore = useStore(roomId);
|
const alive = useAlive();
|
||||||
const [debounce] = useState(new Debounce());
|
const [debounce] = useState(new Debounce());
|
||||||
const [process, setProcess] = useState(null);
|
const [process, setProcess] = useState(null);
|
||||||
const [allRoomIds, setAllRoomIds] = useState([]);
|
const [allRoomIds, setAllRoomIds] = useState([]);
|
||||||
@@ -68,14 +70,14 @@ function SpaceAddExistingContent({ roomId, spaces: onlySpaces }) {
|
|||||||
const handleAdd = async () => {
|
const handleAdd = async () => {
|
||||||
setProcess(`Adding ${selected.length} items...`);
|
setProcess(`Adding ${selected.length} items...`);
|
||||||
|
|
||||||
const promises = selected.map((rId) => {
|
await rateLimitedActions(selected, async (rId) => {
|
||||||
const room = mx.getRoom(rId);
|
const room = mx.getRoom(rId);
|
||||||
const via = getViaServers(room);
|
const via = getViaServers(room);
|
||||||
if (via.length === 0) {
|
if (via.length === 0) {
|
||||||
via.push(getIdServer(rId));
|
via.push(getIdServer(rId));
|
||||||
}
|
}
|
||||||
|
|
||||||
return mx.sendStateEvent(
|
await mx.sendStateEvent(
|
||||||
roomId,
|
roomId,
|
||||||
'm.space.child',
|
'm.space.child',
|
||||||
{
|
{
|
||||||
@@ -87,9 +89,7 @@ function SpaceAddExistingContent({ roomId, spaces: onlySpaces }) {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
mountStore.setItem(true);
|
if (!alive()) return;
|
||||||
await Promise.allSettled(promises);
|
|
||||||
if (mountStore.getItem() !== true) return;
|
|
||||||
|
|
||||||
const roomIds = onlySpaces ? [...spaces] : [...rooms, ...directs];
|
const roomIds = onlySpaces ? [...spaces] : [...rooms, ...directs];
|
||||||
const allIds = roomIds.filter(
|
const allIds = roomIds.filter(
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export function AuthFooter() {
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
v4.8.0
|
v4.8.1
|
||||||
</Text>
|
</Text>
|
||||||
<Text as="a" size="T300" href="https://twitter.com/cinnyapp" target="_blank" rel="noreferrer">
|
<Text as="a" size="T300" href="https://twitter.com/cinnyapp" target="_blank" rel="noreferrer">
|
||||||
Twitter
|
Twitter
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export function WelcomePage() {
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer noopener"
|
rel="noreferrer noopener"
|
||||||
>
|
>
|
||||||
v4.8.0
|
v4.8.1
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ export function Explore() {
|
|||||||
<Box as="span" grow="Yes" alignItems="Center" gap="200">
|
<Box as="span" grow="Yes" alignItems="Center" gap="200">
|
||||||
<Avatar size="200" radii="400">
|
<Avatar size="200" radii="400">
|
||||||
<Icon
|
<Icon
|
||||||
src={Icons.Category}
|
src={Icons.Server}
|
||||||
size="100"
|
size="100"
|
||||||
filled={selectedServer === userServer}
|
filled={selectedServer === userServer}
|
||||||
/>
|
/>
|
||||||
@@ -243,11 +243,7 @@ export function Explore() {
|
|||||||
<NavItemContent>
|
<NavItemContent>
|
||||||
<Box as="span" grow="Yes" alignItems="Center" gap="200">
|
<Box as="span" grow="Yes" alignItems="Center" gap="200">
|
||||||
<Avatar size="200" radii="400">
|
<Avatar size="200" radii="400">
|
||||||
<Icon
|
<Icon src={Icons.Server} size="100" filled={server === selectedServer} />
|
||||||
src={Icons.Category}
|
|
||||||
size="100"
|
|
||||||
filled={server === selectedServer}
|
|
||||||
/>
|
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<Box as="span" grow="Yes">
|
<Box as="span" grow="Yes">
|
||||||
<Text as="span" size="Inherit" truncate>
|
<Text as="span" size="Inherit" truncate>
|
||||||
|
|||||||
@@ -507,7 +507,7 @@ export function PublicRooms() {
|
|||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<Box grow="Yes" justifyContent="Center" alignItems="Center" gap="200">
|
<Box grow="Yes" justifyContent="Center" alignItems="Center" gap="200">
|
||||||
{screenSize !== ScreenSize.Mobile && <Icon size="400" src={Icons.Category} />}
|
{screenSize !== ScreenSize.Mobile && <Icon size="400" src={Icons.Server} />}
|
||||||
<Text size="H3" truncate>
|
<Text size="H3" truncate>
|
||||||
{server}
|
{server}
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
@@ -744,13 +744,14 @@ export function SpaceTabs({ scrollRef }: SpaceTabsProps) {
|
|||||||
const targetSpaceId = target.getAttribute('data-id');
|
const targetSpaceId = target.getAttribute('data-id');
|
||||||
if (!targetSpaceId) return;
|
if (!targetSpaceId) return;
|
||||||
|
|
||||||
|
const spacePath = getSpacePath(getCanonicalAliasOrRoomId(mx, targetSpaceId));
|
||||||
if (screenSize === ScreenSize.Mobile) {
|
if (screenSize === ScreenSize.Mobile) {
|
||||||
navigate(getSpacePath(getCanonicalAliasOrRoomId(mx, targetSpaceId)));
|
navigate(spacePath);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const activePath = navToActivePath.get(targetSpaceId);
|
const activePath = navToActivePath.get(targetSpaceId);
|
||||||
if (activePath) {
|
if (activePath && activePath.pathname.startsWith(spacePath)) {
|
||||||
navigate(joinPathComponent(activePath));
|
navigate(joinPathComponent(activePath));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,24 @@
|
|||||||
import React, { ReactNode } from 'react';
|
import React, { ReactNode } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { useAtomValue } from 'jotai';
|
import { useAtom, useAtomValue } from 'jotai';
|
||||||
import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom';
|
import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom';
|
||||||
import { IsDirectRoomProvider, RoomProvider } from '../../../hooks/useRoom';
|
import { IsDirectRoomProvider, RoomProvider } from '../../../hooks/useRoom';
|
||||||
import { useMatrixClient } from '../../../hooks/useMatrixClient';
|
import { useMatrixClient } from '../../../hooks/useMatrixClient';
|
||||||
import { JoinBeforeNavigate } from '../../../features/join-before-navigate';
|
import { JoinBeforeNavigate } from '../../../features/join-before-navigate';
|
||||||
import { useSpace } from '../../../hooks/useSpace';
|
import { useSpace } from '../../../hooks/useSpace';
|
||||||
import { getAllParents } from '../../../utils/room';
|
import { getAllParents, getSpaceChildren } from '../../../utils/room';
|
||||||
import { roomToParentsAtom } from '../../../state/room/roomToParents';
|
import { roomToParentsAtom } from '../../../state/room/roomToParents';
|
||||||
import { allRoomsAtom } from '../../../state/room-list/roomList';
|
import { allRoomsAtom } from '../../../state/room-list/roomList';
|
||||||
import { useSearchParamsViaServers } from '../../../hooks/router/useSearchParamsViaServers';
|
import { useSearchParamsViaServers } from '../../../hooks/router/useSearchParamsViaServers';
|
||||||
import { mDirectAtom } from '../../../state/mDirectList';
|
import { mDirectAtom } from '../../../state/mDirectList';
|
||||||
|
import { settingsAtom } from '../../../state/settings';
|
||||||
|
import { useSetting } from '../../../state/hooks/settings';
|
||||||
|
|
||||||
export function SpaceRouteRoomProvider({ children }: { children: ReactNode }) {
|
export function SpaceRouteRoomProvider({ children }: { children: ReactNode }) {
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
const space = useSpace();
|
const space = useSpace();
|
||||||
const roomToParents = useAtomValue(roomToParentsAtom);
|
const [developerTools] = useSetting(settingsAtom, 'developerTools');
|
||||||
|
const [roomToParents, setRoomToParents] = useAtom(roomToParentsAtom);
|
||||||
const mDirects = useAtomValue(mDirectAtom);
|
const mDirects = useAtomValue(mDirectAtom);
|
||||||
const allRooms = useAtomValue(allRoomsAtom);
|
const allRooms = useAtomValue(allRoomsAtom);
|
||||||
|
|
||||||
@@ -24,12 +27,36 @@ export function SpaceRouteRoomProvider({ children }: { children: ReactNode }) {
|
|||||||
const roomId = useSelectedRoom();
|
const roomId = useSelectedRoom();
|
||||||
const room = mx.getRoom(roomId);
|
const room = mx.getRoom(roomId);
|
||||||
|
|
||||||
if (
|
if (!room || !allRooms.includes(room.roomId)) {
|
||||||
!room ||
|
// room is not joined
|
||||||
room.isSpaceRoom() ||
|
return (
|
||||||
!allRooms.includes(room.roomId) ||
|
<JoinBeforeNavigate
|
||||||
!getAllParents(roomToParents, room.roomId).has(space.roomId)
|
roomIdOrAlias={roomIdOrAlias!}
|
||||||
) {
|
eventId={eventId}
|
||||||
|
viaServers={viaServers}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (developerTools && room.isSpaceRoom() && room.roomId === space.roomId) {
|
||||||
|
// allow to view space timeline
|
||||||
|
return (
|
||||||
|
<RoomProvider key={room.roomId} value={room}>
|
||||||
|
<IsDirectRoomProvider value={mDirects.has(room.roomId)}>{children}</IsDirectRoomProvider>
|
||||||
|
</RoomProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!getAllParents(roomToParents, room.roomId).has(space.roomId)) {
|
||||||
|
if (getSpaceChildren(space).includes(room.roomId)) {
|
||||||
|
// fill missing roomToParent mapping
|
||||||
|
setRoomToParents({
|
||||||
|
type: 'PUT',
|
||||||
|
parent: space.roomId,
|
||||||
|
children: [room.roomId],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<JoinBeforeNavigate
|
<JoinBeforeNavigate
|
||||||
roomIdOrAlias={roomIdOrAlias!}
|
roomIdOrAlias={roomIdOrAlias!}
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ import {
|
|||||||
useRoomsNotificationPreferencesContext,
|
useRoomsNotificationPreferencesContext,
|
||||||
} from '../../../hooks/useRoomsNotificationPreferences';
|
} from '../../../hooks/useRoomsNotificationPreferences';
|
||||||
import { useOpenSpaceSettings } from '../../../state/hooks/spaceSettings';
|
import { useOpenSpaceSettings } from '../../../state/hooks/spaceSettings';
|
||||||
|
import { useRoomNavigate } from '../../../hooks/useRoomNavigate';
|
||||||
|
|
||||||
type SpaceMenuProps = {
|
type SpaceMenuProps = {
|
||||||
room: Room;
|
room: Room;
|
||||||
@@ -83,11 +84,13 @@ type SpaceMenuProps = {
|
|||||||
const SpaceMenu = forwardRef<HTMLDivElement, SpaceMenuProps>(({ room, requestClose }, ref) => {
|
const SpaceMenu = forwardRef<HTMLDivElement, SpaceMenuProps>(({ room, requestClose }, ref) => {
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
const [hideActivity] = useSetting(settingsAtom, 'hideActivity');
|
const [hideActivity] = useSetting(settingsAtom, 'hideActivity');
|
||||||
|
const [developerTools] = useSetting(settingsAtom, 'developerTools');
|
||||||
const roomToParents = useAtomValue(roomToParentsAtom);
|
const roomToParents = useAtomValue(roomToParentsAtom);
|
||||||
const powerLevels = usePowerLevels(room);
|
const powerLevels = usePowerLevels(room);
|
||||||
const { getPowerLevel, canDoAction } = usePowerLevelsAPI(powerLevels);
|
const { getPowerLevel, canDoAction } = usePowerLevelsAPI(powerLevels);
|
||||||
const canInvite = canDoAction('invite', getPowerLevel(mx.getUserId() ?? ''));
|
const canInvite = canDoAction('invite', getPowerLevel(mx.getUserId() ?? ''));
|
||||||
const openSpaceSettings = useOpenSpaceSettings();
|
const openSpaceSettings = useOpenSpaceSettings();
|
||||||
|
const { navigateRoom } = useRoomNavigate();
|
||||||
|
|
||||||
const allChild = useSpaceChildren(
|
const allChild = useSpaceChildren(
|
||||||
allRoomsAtom,
|
allRoomsAtom,
|
||||||
@@ -118,6 +121,11 @@ const SpaceMenu = forwardRef<HTMLDivElement, SpaceMenuProps>(({ room, requestClo
|
|||||||
requestClose();
|
requestClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleOpenTimeline = () => {
|
||||||
|
navigateRoom(room.roomId);
|
||||||
|
requestClose();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu ref={ref} style={{ maxWidth: toRem(160), width: '100vw' }}>
|
<Menu ref={ref} style={{ maxWidth: toRem(160), width: '100vw' }}>
|
||||||
<Box direction="Column" gap="100" style={{ padding: config.space.S100 }}>
|
<Box direction="Column" gap="100" style={{ padding: config.space.S100 }}>
|
||||||
@@ -168,6 +176,18 @@ const SpaceMenu = forwardRef<HTMLDivElement, SpaceMenuProps>(({ room, requestClo
|
|||||||
Space Settings
|
Space Settings
|
||||||
</Text>
|
</Text>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
{developerTools && (
|
||||||
|
<MenuItem
|
||||||
|
onClick={handleOpenTimeline}
|
||||||
|
size="300"
|
||||||
|
after={<Icon size="100" src={Icons.Terminal} />}
|
||||||
|
radii="300"
|
||||||
|
>
|
||||||
|
<Text style={{ flexGrow: 1 }} as="span" size="T300" truncate>
|
||||||
|
Event Timeline
|
||||||
|
</Text>
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<Line variant="Surface" size="300" />
|
<Line variant="Surface" size="300" />
|
||||||
<Box direction="Column" gap="100" style={{ padding: config.space.S100 }}>
|
<Box direction="Column" gap="100" style={{ padding: config.space.S100 }}>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as badWords from 'badwords-list';
|
import * as badWords from 'badwords-list';
|
||||||
import { sanitizeForRegex } from '../utils/regex';
|
import { sanitizeForRegex } from '../utils/regex';
|
||||||
|
|
||||||
const additionalBadWords: string[] = ['Torture', 'T0rture'];
|
const additionalBadWords: string[] = ['torture', 't0rture'];
|
||||||
|
|
||||||
const fullBadWordList = additionalBadWords.concat(
|
const fullBadWordList = additionalBadWords.concat(
|
||||||
badWords.array.filter((word) => !additionalBadWords.includes(word))
|
badWords.array.filter((word) => !additionalBadWords.includes(word))
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import {
|
|||||||
|
|
||||||
const NAV_TO_ACTIVE_PATH = 'navToActivePath';
|
const NAV_TO_ACTIVE_PATH = 'navToActivePath';
|
||||||
|
|
||||||
|
const getStoreKey = (userId: string): string => `${NAV_TO_ACTIVE_PATH}${userId}`;
|
||||||
|
|
||||||
type NavToActivePath = Map<string, Path>;
|
type NavToActivePath = Map<string, Path>;
|
||||||
|
|
||||||
type NavToActivePathAction =
|
type NavToActivePathAction =
|
||||||
@@ -25,7 +27,7 @@ type NavToActivePathAction =
|
|||||||
export type NavToActivePathAtom = WritableAtom<NavToActivePath, [NavToActivePathAction], undefined>;
|
export type NavToActivePathAtom = WritableAtom<NavToActivePath, [NavToActivePathAction], undefined>;
|
||||||
|
|
||||||
export const makeNavToActivePathAtom = (userId: string): NavToActivePathAtom => {
|
export const makeNavToActivePathAtom = (userId: string): NavToActivePathAtom => {
|
||||||
const storeKey = `${NAV_TO_ACTIVE_PATH}${userId}`;
|
const storeKey = getStoreKey(userId);
|
||||||
|
|
||||||
const baseNavToActivePathAtom = atomWithLocalStorage<NavToActivePath>(
|
const baseNavToActivePathAtom = atomWithLocalStorage<NavToActivePath>(
|
||||||
storeKey,
|
storeKey,
|
||||||
@@ -64,3 +66,7 @@ export const makeNavToActivePathAtom = (userId: string): NavToActivePathAtom =>
|
|||||||
|
|
||||||
return navToActivePathAtom;
|
return navToActivePathAtom;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const clearNavToActivePathStore = (userId: string) => {
|
||||||
|
localStorage.removeItem(getStoreKey(userId));
|
||||||
|
};
|
||||||
|
|||||||
@@ -300,7 +300,7 @@ export const downloadEncryptedMedia = async (
|
|||||||
|
|
||||||
export const rateLimitedActions = async <T, R = void>(
|
export const rateLimitedActions = async <T, R = void>(
|
||||||
data: T[],
|
data: T[],
|
||||||
callback: (item: T) => Promise<R>,
|
callback: (item: T, index: number) => Promise<R>,
|
||||||
maxRetryCount?: number
|
maxRetryCount?: number
|
||||||
) => {
|
) => {
|
||||||
let retryCount = 0;
|
let retryCount = 0;
|
||||||
@@ -312,8 +312,8 @@ export const rateLimitedActions = async <T, R = void>(
|
|||||||
setTimeout(resolve, ms);
|
setTimeout(resolve, ms);
|
||||||
});
|
});
|
||||||
|
|
||||||
const performAction = async (dataItem: T) => {
|
const performAction = async (dataItem: T, index: number) => {
|
||||||
const [err] = await to<R, MatrixError>(callback(dataItem));
|
const [err] = await to<R, MatrixError>(callback(dataItem, index));
|
||||||
|
|
||||||
if (err?.httpStatus === 429) {
|
if (err?.httpStatus === 429) {
|
||||||
if (retryCount === maxRetryCount) {
|
if (retryCount === maxRetryCount) {
|
||||||
@@ -321,11 +321,11 @@ export const rateLimitedActions = async <T, R = void>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const waitMS = err.getRetryAfterMs() ?? 3000;
|
const waitMS = err.getRetryAfterMs() ?? 3000;
|
||||||
actionInterval = waitMS + 500;
|
actionInterval = waitMS * 1.5;
|
||||||
await sleepForMs(waitMS);
|
await sleepForMs(waitMS);
|
||||||
retryCount += 1;
|
retryCount += 1;
|
||||||
|
|
||||||
await performAction(dataItem);
|
await performAction(dataItem, index);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -333,7 +333,7 @@ export const rateLimitedActions = async <T, R = void>(
|
|||||||
const dataItem = data[i];
|
const dataItem = data[i];
|
||||||
retryCount = 0;
|
retryCount = 0;
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
await performAction(dataItem);
|
await performAction(dataItem, i);
|
||||||
if (actionInterval > 0) {
|
if (actionInterval > 0) {
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
await sleepForMs(actionInterval);
|
await sleepForMs(actionInterval);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { createClient, MatrixClient, IndexedDBStore, IndexedDBCryptoStore } from 'matrix-js-sdk';
|
import { createClient, MatrixClient, IndexedDBStore, IndexedDBCryptoStore } from 'matrix-js-sdk';
|
||||||
|
|
||||||
import { cryptoCallbacks } from './state/secretStorageKeys';
|
import { cryptoCallbacks } from './state/secretStorageKeys';
|
||||||
|
import { clearNavToActivePathStore } from '../app/state/navToActivePath';
|
||||||
|
|
||||||
type Session = {
|
type Session = {
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
@@ -46,6 +47,7 @@ export const startClient = async (mx: MatrixClient) => {
|
|||||||
|
|
||||||
export const clearCacheAndReload = async (mx: MatrixClient) => {
|
export const clearCacheAndReload = async (mx: MatrixClient) => {
|
||||||
mx.stopClient();
|
mx.stopClient();
|
||||||
|
clearNavToActivePathStore(mx.getSafeUserId());
|
||||||
await mx.store.deleteAllData();
|
await mx.store.deleteAllData();
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
const cons = {
|
const cons = {
|
||||||
version: '4.8.0',
|
version: '4.8.1',
|
||||||
secretKey: {
|
secretKey: {
|
||||||
ACCESS_TOKEN: 'cinny_access_token',
|
ACCESS_TOKEN: 'cinny_access_token',
|
||||||
DEVICE_ID: 'cinny_device_id',
|
DEVICE_ID: 'cinny_device_id',
|
||||||
|
|||||||
Reference in New Issue
Block a user