mirror of
https://github.com/cinnyapp/cinny.git
synced 2025-11-04 14:30:29 +03:00
Merge branch 'cinnyapp:dev' into iOSChatFix
This commit is contained in:
commit
636ced5435
16 changed files with 120 additions and 30 deletions
2
.github/workflows/build-pull-request.yml
vendored
2
.github/workflows/build-pull-request.yml
vendored
|
|
@ -14,7 +14,7 @@ jobs:
|
|||
- name: Checkout repository
|
||||
uses: actions/checkout@v4.2.0
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4.3.0
|
||||
uses: actions/setup-node@v4.4.0
|
||||
with:
|
||||
node-version: 20.12.2
|
||||
cache: 'npm'
|
||||
|
|
|
|||
2
.github/workflows/docker-pr.yml
vendored
2
.github/workflows/docker-pr.yml
vendored
|
|
@ -13,7 +13,7 @@ jobs:
|
|||
- name: Checkout repository
|
||||
uses: actions/checkout@v4.2.0
|
||||
- name: Build Docker image
|
||||
uses: docker/build-push-action@v6.15.0
|
||||
uses: docker/build-push-action@v6.18.0
|
||||
with:
|
||||
context: .
|
||||
push: false
|
||||
|
|
|
|||
2
.github/workflows/netlify-dev.yml
vendored
2
.github/workflows/netlify-dev.yml
vendored
|
|
@ -13,7 +13,7 @@ jobs:
|
|||
- name: Checkout repository
|
||||
uses: actions/checkout@v4.2.0
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4.3.0
|
||||
uses: actions/setup-node@v4.4.0
|
||||
with:
|
||||
node-version: 20.12.2
|
||||
cache: 'npm'
|
||||
|
|
|
|||
4
.github/workflows/prod-deploy.yml
vendored
4
.github/workflows/prod-deploy.yml
vendored
|
|
@ -12,7 +12,7 @@ jobs:
|
|||
- name: Checkout repository
|
||||
uses: actions/checkout@v4.2.0
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4.3.0
|
||||
uses: actions/setup-node@v4.4.0
|
||||
with:
|
||||
node-version: 20.12.2
|
||||
cache: 'npm'
|
||||
|
|
@ -90,7 +90,7 @@ jobs:
|
|||
${{ secrets.DOCKER_USERNAME }}/cinny
|
||||
ghcr.io/${{ github.repository }}
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v6.15.0
|
||||
uses: docker/build-push-action@v6.18.0
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
|
|
|
|||
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "cinny",
|
||||
"version": "4.8.0",
|
||||
"version": "4.8.1",
|
||||
"description": "Yet another matrix client",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
)
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export function AuthFooter() {
|
|||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
v4.8.0
|
||||
v4.8.1
|
||||
</Text>
|
||||
<Text as="a" size="T300" href="https://twitter.com/cinnyapp" target="_blank" rel="noreferrer">
|
||||
Twitter
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export function WelcomePage() {
|
|||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
v4.8.0
|
||||
v4.8.1
|
||||
</a>
|
||||
</span>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<JoinBeforeNavigate
|
||||
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 (
|
||||
<JoinBeforeNavigate
|
||||
roomIdOrAlias={roomIdOrAlias!}
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ import {
|
|||
useRoomsNotificationPreferencesContext,
|
||||
} from '../../../hooks/useRoomsNotificationPreferences';
|
||||
import { useOpenSpaceSettings } from '../../../state/hooks/spaceSettings';
|
||||
import { useRoomNavigate } from '../../../hooks/useRoomNavigate';
|
||||
|
||||
type SpaceMenuProps = {
|
||||
room: Room;
|
||||
|
|
@ -83,11 +84,13 @@ type SpaceMenuProps = {
|
|||
const SpaceMenu = forwardRef<HTMLDivElement, SpaceMenuProps>(({ 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', getPowerLevel(mx.getUserId() ?? ''));
|
||||
const openSpaceSettings = useOpenSpaceSettings();
|
||||
const { navigateRoom } = useRoomNavigate();
|
||||
|
||||
const allChild = useSpaceChildren(
|
||||
allRoomsAtom,
|
||||
|
|
@ -118,6 +121,11 @@ const SpaceMenu = forwardRef<HTMLDivElement, SpaceMenuProps>(({ room, requestClo
|
|||
requestClose();
|
||||
};
|
||||
|
||||
const handleOpenTimeline = () => {
|
||||
navigateRoom(room.roomId);
|
||||
requestClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<Menu ref={ref} style={{ maxWidth: toRem(160), width: '100vw' }}>
|
||||
<Box direction="Column" gap="100" style={{ padding: config.space.S100 }}>
|
||||
|
|
@ -168,6 +176,18 @@ const SpaceMenu = forwardRef<HTMLDivElement, SpaceMenuProps>(({ room, requestClo
|
|||
Space Settings
|
||||
</Text>
|
||||
</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>
|
||||
<Line variant="Surface" size="300" />
|
||||
<Box direction="Column" gap="100" style={{ padding: config.space.S100 }}>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import {
|
|||
|
||||
const NAV_TO_ACTIVE_PATH = 'navToActivePath';
|
||||
|
||||
const getStoreKey = (userId: string): string => `${NAV_TO_ACTIVE_PATH}${userId}`;
|
||||
|
||||
type NavToActivePath = Map<string, Path>;
|
||||
|
||||
type NavToActivePathAction =
|
||||
|
|
@ -25,7 +27,7 @@ type NavToActivePathAction =
|
|||
export type NavToActivePathAtom = WritableAtom<NavToActivePath, [NavToActivePathAction], undefined>;
|
||||
|
||||
export const makeNavToActivePathAtom = (userId: string): NavToActivePathAtom => {
|
||||
const storeKey = `${NAV_TO_ACTIVE_PATH}${userId}`;
|
||||
const storeKey = getStoreKey(userId);
|
||||
|
||||
const baseNavToActivePathAtom = atomWithLocalStorage<NavToActivePath>(
|
||||
storeKey,
|
||||
|
|
@ -64,3 +66,7 @@ export const makeNavToActivePathAtom = (userId: string): NavToActivePathAtom =>
|
|||
|
||||
return navToActivePathAtom;
|
||||
};
|
||||
|
||||
export const clearNavToActivePathStore = (userId: string) => {
|
||||
localStorage.removeItem(getStoreKey(userId));
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue