diff --git a/package-lock.json b/package-lock.json
index f2303c02..a3d8536c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "cinny",
- "version": "4.8.0",
+ "version": "4.8.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "cinny",
- "version": "4.8.0",
+ "version": "4.8.1",
"license": "AGPL-3.0-only",
"dependencies": {
"@atlaskit/pragmatic-drag-and-drop": "1.1.6",
diff --git a/package.json b/package.json
index 8838c58b..e9b42b71 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "cinny",
- "version": "4.8.0",
+ "version": "4.8.1",
"description": "Yet another matrix client",
"main": "index.js",
"type": "module",
diff --git a/src/app/features/common-settings/general/RoomJoinRules.tsx b/src/app/features/common-settings/general/RoomJoinRules.tsx
index ebd4cad5..c0d62a6a 100644
--- a/src/app/features/common-settings/general/RoomJoinRules.tsx
+++ b/src/app/features/common-settings/general/RoomJoinRules.tsx
@@ -2,6 +2,7 @@ import React, { useCallback, useMemo } from 'react';
import { color, Text } from 'folds';
import { JoinRule, MatrixError, RestrictedAllowType } from 'matrix-js-sdk';
import { RoomJoinRulesEventContent } from 'matrix-js-sdk/lib/types';
+import { useAtomValue } from 'jotai';
import { IPowerLevels, powerLevelAPI } from '../../../hooks/usePowerLevels';
import {
ExtendedJoinRules,
@@ -20,6 +21,12 @@ import { useStateEvent } from '../../../hooks/useStateEvent';
import { useSpaceOptionally } from '../../../hooks/useSpace';
import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
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 = {
room_id: string;
@@ -36,7 +43,11 @@ export function RoomJoinRules({ powerLevels }: RoomJoinRulesProps) {
const allowKnockRestricted = roomVersion >= 10;
const allowRestricted = roomVersion >= 8;
const allowKnock = roomVersion >= 7;
+
+ const roomIdToParents = useAtomValue(roomToParentsAtom);
const space = useSpaceOptionally();
+ const subspacesScope = useRecursiveChildSpaceScopeFactory(mx, roomIdToParents);
+ const subspaces = useSpaceChildren(allRoomsAtom, space?.roomId ?? '', subspacesScope);
const userPowerLevel = powerLevelAPI.getPowerLevel(powerLevels, mx.getSafeUserId());
const canEdit = powerLevelAPI.canSendStateEvent(
@@ -74,9 +85,22 @@ export function RoomJoinRules({ powerLevels }: RoomJoinRulesProps) {
async (joinRule: ExtendedJoinRules) => {
const allow: RestrictedRoomAllowContent[] = [];
if (joinRule === JoinRule.Restricted || joinRule === 'knock_restricted') {
- const parents = getStateEvents(room, StateEvent.SpaceParent).map((event) =>
- event.getStateKey()
- );
+ const roomParents = roomIdToParents.get(room.roomId);
+
+ 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) => {
if (!parentRoomId) return;
allow.push({
@@ -92,7 +116,7 @@ export function RoomJoinRules({ powerLevels }: RoomJoinRulesProps) {
if (allow.length > 0) c.allow = allow;
await mx.sendStateEvent(room.roomId, StateEvent.RoomJoinRules as any, c);
},
- [mx, room]
+ [mx, room, space, subspaces, roomIdToParents]
)
);
diff --git a/src/app/hooks/useRoomNavigate.ts b/src/app/hooks/useRoomNavigate.ts
index 0f9f365c..e626c06b 100644
--- a/src/app/hooks/useRoomNavigate.ts
+++ b/src/app/hooks/useRoomNavigate.ts
@@ -13,6 +13,8 @@ import { getOrphanParents } from '../utils/room';
import { roomToParentsAtom } from '../state/room/roomToParents';
import { mDirectAtom } from '../state/mDirectList';
import { useSelectedSpace } from './router/useSelectedSpace';
+import { settingsAtom } from '../state/settings';
+import { useSetting } from '../state/hooks/settings';
export const useRoomNavigate = () => {
const navigate = useNavigate();
@@ -20,6 +22,7 @@ export const useRoomNavigate = () => {
const roomToParents = useAtomValue(roomToParentsAtom);
const mDirects = useAtomValue(mDirectAtom);
const spaceSelectedId = useSelectedSpace();
+ const [developerTools] = useSetting(settingsAtom, 'developerTools');
const navigateSpace = useCallback(
(roomId: string) => {
@@ -32,15 +35,22 @@ export const useRoomNavigate = () => {
const navigateRoom = useCallback(
(roomId: string, eventId?: string, opts?: NavigateOptions) => {
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) {
const pSpaceIdOrAlias = getCanonicalAliasOrRoomId(
mx,
spaceSelectedId && orphanParents.includes(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);
return;
}
@@ -52,7 +62,7 @@ export const useRoomNavigate = () => {
navigate(getHomeRoomPath(roomIdOrAlias, eventId), opts);
},
- [mx, navigate, spaceSelectedId, roomToParents, mDirects]
+ [mx, navigate, spaceSelectedId, roomToParents, mDirects, developerTools]
);
return {
diff --git a/src/app/pages/auth/AuthFooter.tsx b/src/app/pages/auth/AuthFooter.tsx
index 30f4b3ca..ff2fdb9b 100644
--- a/src/app/pages/auth/AuthFooter.tsx
+++ b/src/app/pages/auth/AuthFooter.tsx
@@ -15,7 +15,7 @@ export function AuthFooter() {
target="_blank"
rel="noreferrer"
>
- v4.8.0
+ v4.8.1
Twitter
diff --git a/src/app/pages/client/WelcomePage.tsx b/src/app/pages/client/WelcomePage.tsx
index 88d38981..645753ff 100644
--- a/src/app/pages/client/WelcomePage.tsx
+++ b/src/app/pages/client/WelcomePage.tsx
@@ -24,7 +24,7 @@ export function WelcomePage() {
target="_blank"
rel="noreferrer noopener"
>
- v4.8.0
+ v4.8.1
}
diff --git a/src/app/pages/client/sidebar/SpaceTabs.tsx b/src/app/pages/client/sidebar/SpaceTabs.tsx
index 5b47cb52..011741ee 100644
--- a/src/app/pages/client/sidebar/SpaceTabs.tsx
+++ b/src/app/pages/client/sidebar/SpaceTabs.tsx
@@ -744,13 +744,14 @@ export function SpaceTabs({ scrollRef }: SpaceTabsProps) {
const targetSpaceId = target.getAttribute('data-id');
if (!targetSpaceId) return;
+ const spacePath = getSpacePath(getCanonicalAliasOrRoomId(mx, targetSpaceId));
if (screenSize === ScreenSize.Mobile) {
- navigate(getSpacePath(getCanonicalAliasOrRoomId(mx, targetSpaceId)));
+ navigate(spacePath);
return;
}
const activePath = navToActivePath.get(targetSpaceId);
- if (activePath) {
+ if (activePath && activePath.pathname.startsWith(spacePath)) {
navigate(joinPathComponent(activePath));
return;
}
diff --git a/src/app/pages/client/space/RoomProvider.tsx b/src/app/pages/client/space/RoomProvider.tsx
index a9632137..0fd52ab6 100644
--- a/src/app/pages/client/space/RoomProvider.tsx
+++ b/src/app/pages/client/space/RoomProvider.tsx
@@ -1,21 +1,24 @@
import React, { ReactNode } from 'react';
import { useParams } from 'react-router-dom';
-import { useAtomValue } from 'jotai';
+import { useAtom, useAtomValue } from 'jotai';
import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom';
import { IsDirectRoomProvider, RoomProvider } from '../../../hooks/useRoom';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { JoinBeforeNavigate } from '../../../features/join-before-navigate';
import { useSpace } from '../../../hooks/useSpace';
-import { getAllParents } from '../../../utils/room';
+import { getAllParents, getSpaceChildren } from '../../../utils/room';
import { roomToParentsAtom } from '../../../state/room/roomToParents';
import { allRoomsAtom } from '../../../state/room-list/roomList';
import { useSearchParamsViaServers } from '../../../hooks/router/useSearchParamsViaServers';
import { mDirectAtom } from '../../../state/mDirectList';
+import { settingsAtom } from '../../../state/settings';
+import { useSetting } from '../../../state/hooks/settings';
export function SpaceRouteRoomProvider({ children }: { children: ReactNode }) {
const mx = useMatrixClient();
const space = useSpace();
- const roomToParents = useAtomValue(roomToParentsAtom);
+ const [developerTools] = useSetting(settingsAtom, 'developerTools');
+ const [roomToParents, setRoomToParents] = useAtom(roomToParentsAtom);
const mDirects = useAtomValue(mDirectAtom);
const allRooms = useAtomValue(allRoomsAtom);
@@ -24,12 +27,36 @@ export function SpaceRouteRoomProvider({ children }: { children: ReactNode }) {
const roomId = useSelectedRoom();
const room = mx.getRoom(roomId);
- if (
- !room ||
- room.isSpaceRoom() ||
- !allRooms.includes(room.roomId) ||
- !getAllParents(roomToParents, room.roomId).has(space.roomId)
- ) {
+ if (!room || !allRooms.includes(room.roomId)) {
+ // room is not joined
+ return (
+
+ );
+ }
+
+ if (developerTools && room.isSpaceRoom() && room.roomId === space.roomId) {
+ // allow to view space timeline
+ return (
+
+ {children}
+
+ );
+ }
+
+ 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 (
(({ room, requestClose }, ref) => {
const mx = useMatrixClient();
const [hideActivity] = useSetting(settingsAtom, 'hideActivity');
+ const [developerTools] = useSetting(settingsAtom, 'developerTools');
const roomToParents = useAtomValue(roomToParentsAtom);
const powerLevels = usePowerLevels(room);
const { getPowerLevel, canDoAction } = usePowerLevelsAPI(powerLevels);
const canInvite = canDoAction('invite', mx.getUserId() ?? '');
const openSpaceSettings = useOpenSpaceSettings();
+ const { navigateRoom } = useRoomNavigate();
const allChild = useSpaceChildren(
allRoomsAtom,
@@ -233,6 +236,11 @@ const SpaceMenu = forwardRef(({ room, requestClo
requestClose();
};
+ const handleOpenTimeline = () => {
+ navigateRoom(room.roomId);
+ requestClose();
+ };
+
return (
+ {developerTools && (
+ }
+ radii="300"
+ >
+
+ Event Timeline
+
+
+ )}
diff --git a/src/app/state/navToActivePath.ts b/src/app/state/navToActivePath.ts
index 80869146..af90c914 100644
--- a/src/app/state/navToActivePath.ts
+++ b/src/app/state/navToActivePath.ts
@@ -9,6 +9,8 @@ import {
const NAV_TO_ACTIVE_PATH = 'navToActivePath';
+const getStoreKey = (userId: string): string => `${NAV_TO_ACTIVE_PATH}${userId}`;
+
type NavToActivePath = Map;
type NavToActivePathAction =
@@ -25,7 +27,7 @@ type NavToActivePathAction =
export type NavToActivePathAtom = WritableAtom;
export const makeNavToActivePathAtom = (userId: string): NavToActivePathAtom => {
- const storeKey = `${NAV_TO_ACTIVE_PATH}${userId}`;
+ const storeKey = getStoreKey(userId);
const baseNavToActivePathAtom = atomWithLocalStorage(
storeKey,
@@ -64,3 +66,7 @@ export const makeNavToActivePathAtom = (userId: string): NavToActivePathAtom =>
return navToActivePathAtom;
};
+
+export const clearNavToActivePathStore = (userId: string) => {
+ localStorage.removeItem(getStoreKey(userId));
+};
diff --git a/src/client/initMatrix.ts b/src/client/initMatrix.ts
index b513e27c..b80a080f 100644
--- a/src/client/initMatrix.ts
+++ b/src/client/initMatrix.ts
@@ -1,6 +1,7 @@
import { createClient, MatrixClient, IndexedDBStore, IndexedDBCryptoStore } from 'matrix-js-sdk';
import { cryptoCallbacks } from './state/secretStorageKeys';
+import { clearNavToActivePathStore } from '../app/state/navToActivePath';
type Session = {
baseUrl: string;
@@ -46,6 +47,7 @@ export const startClient = async (mx: MatrixClient) => {
export const clearCacheAndReload = async (mx: MatrixClient) => {
mx.stopClient();
+ clearNavToActivePathStore(mx.getSafeUserId());
await mx.store.deleteAllData();
window.location.reload();
};
diff --git a/src/client/state/cons.js b/src/client/state/cons.js
index 3e9306b2..1cb8b102 100644
--- a/src/client/state/cons.js
+++ b/src/client/state/cons.js
@@ -1,5 +1,5 @@
const cons = {
- version: '4.8.0',
+ version: '4.8.1',
secretKey: {
ACCESS_TOKEN: 'cinny_access_token',
DEVICE_ID: 'cinny_device_id',