Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
abd713d693 | ||
|
|
802357b7a0 | ||
|
|
c5d4530947 | ||
|
|
367397fdd4 | ||
|
|
63fa60e7f4 | ||
|
|
544a06964d | ||
|
|
50583f9474 | ||
|
|
1ad7fe8deb | ||
|
|
752a19a4e7 |
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "cinny",
|
"name": "cinny",
|
||||||
"version": "4.9.0",
|
"version": "4.9.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "cinny",
|
"name": "cinny",
|
||||||
"version": "4.9.0",
|
"version": "4.9.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",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cinny",
|
"name": "cinny",
|
||||||
"version": "4.9.0",
|
"version": "4.9.1",
|
||||||
"description": "Yet another matrix client",
|
"description": "Yet another matrix client",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import { UserAvatar } from '../user-avatar';
|
|||||||
import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
|
import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
|
||||||
import { useOpenUserRoomProfile } from '../../state/hooks/userRoomProfile';
|
import { useOpenUserRoomProfile } from '../../state/hooks/userRoomProfile';
|
||||||
import { useSpaceOptionally } from '../../hooks/useSpace';
|
import { useSpaceOptionally } from '../../hooks/useSpace';
|
||||||
|
import { getMouseEventCords } from '../../utils/dom';
|
||||||
|
|
||||||
export type EventReadersProps = {
|
export type EventReadersProps = {
|
||||||
room: Room;
|
room: Room;
|
||||||
@@ -83,7 +84,7 @@ export const EventReaders = as<'div', EventReadersProps>(
|
|||||||
room.roomId,
|
room.roomId,
|
||||||
space?.roomId,
|
space?.roomId,
|
||||||
readerId,
|
readerId,
|
||||||
event.currentTarget.getBoundingClientRect(),
|
getMouseEventCords(event.nativeEvent),
|
||||||
'Bottom'
|
'Bottom'
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
|||||||
131
src/app/components/join-address-prompt/JoinAddressPrompt.tsx
Normal file
131
src/app/components/join-address-prompt/JoinAddressPrompt.tsx
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
import React, { FormEventHandler, useState } from 'react';
|
||||||
|
import FocusTrap from 'focus-trap-react';
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
Overlay,
|
||||||
|
OverlayCenter,
|
||||||
|
OverlayBackdrop,
|
||||||
|
Header,
|
||||||
|
config,
|
||||||
|
Box,
|
||||||
|
Text,
|
||||||
|
IconButton,
|
||||||
|
Icon,
|
||||||
|
Icons,
|
||||||
|
Button,
|
||||||
|
Input,
|
||||||
|
color,
|
||||||
|
} from 'folds';
|
||||||
|
import { stopPropagation } from '../../utils/keyboard';
|
||||||
|
import { isRoomAlias, isRoomId } from '../../utils/matrix';
|
||||||
|
import { parseMatrixToRoom, parseMatrixToRoomEvent, testMatrixTo } from '../../plugins/matrix-to';
|
||||||
|
import { tryDecodeURIComponent } from '../../utils/dom';
|
||||||
|
|
||||||
|
type JoinAddressProps = {
|
||||||
|
onOpen: (roomIdOrAlias: string, via?: string[], eventId?: string) => void;
|
||||||
|
onCancel: () => void;
|
||||||
|
};
|
||||||
|
export function JoinAddressPrompt({ onOpen, onCancel }: JoinAddressProps) {
|
||||||
|
const [invalid, setInvalid] = useState(false);
|
||||||
|
|
||||||
|
const handleSubmit: FormEventHandler<HTMLFormElement> = (evt) => {
|
||||||
|
evt.preventDefault();
|
||||||
|
setInvalid(false);
|
||||||
|
|
||||||
|
const target = evt.target as HTMLFormElement | undefined;
|
||||||
|
const addressInput = target?.addressInput as HTMLInputElement | undefined;
|
||||||
|
const address = addressInput?.value.trim();
|
||||||
|
if (!address) return;
|
||||||
|
|
||||||
|
if (isRoomId(address) || isRoomAlias(address)) {
|
||||||
|
onOpen(address);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (testMatrixTo(address)) {
|
||||||
|
const decodedAddress = tryDecodeURIComponent(address);
|
||||||
|
const toRoom = parseMatrixToRoom(decodedAddress);
|
||||||
|
if (toRoom) {
|
||||||
|
onOpen(toRoom.roomIdOrAlias, toRoom.viaServers);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const toEvent = parseMatrixToRoomEvent(decodedAddress);
|
||||||
|
if (toEvent) {
|
||||||
|
onOpen(toEvent.roomIdOrAlias, toEvent.viaServers, toEvent.eventId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setInvalid(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Overlay open backdrop={<OverlayBackdrop />}>
|
||||||
|
<OverlayCenter>
|
||||||
|
<FocusTrap
|
||||||
|
focusTrapOptions={{
|
||||||
|
initialFocus: false,
|
||||||
|
onDeactivate: onCancel,
|
||||||
|
clickOutsideDeactivates: true,
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Dialog variant="Surface">
|
||||||
|
<Header
|
||||||
|
style={{
|
||||||
|
padding: `0 ${config.space.S200} 0 ${config.space.S400}`,
|
||||||
|
}}
|
||||||
|
variant="Surface"
|
||||||
|
size="500"
|
||||||
|
>
|
||||||
|
<Box grow="Yes">
|
||||||
|
<Text size="H4">Join with Address</Text>
|
||||||
|
</Box>
|
||||||
|
<IconButton size="300" onClick={onCancel} radii="300">
|
||||||
|
<Icon src={Icons.Cross} />
|
||||||
|
</IconButton>
|
||||||
|
</Header>
|
||||||
|
<Box
|
||||||
|
as="form"
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
style={{ padding: config.space.S400, paddingTop: 0 }}
|
||||||
|
direction="Column"
|
||||||
|
gap="400"
|
||||||
|
>
|
||||||
|
<Box direction="Column" gap="200">
|
||||||
|
<Text priority="400" size="T300">
|
||||||
|
Enter public address to join the community. Addresses looks like:
|
||||||
|
</Text>
|
||||||
|
<Text as="ul" size="T200" priority="300" style={{ paddingLeft: config.space.S400 }}>
|
||||||
|
<li>#community:server</li>
|
||||||
|
<li>https://matrix.to/#/#community:server</li>
|
||||||
|
<li>https://matrix.to/#/!xYzAj?via=server</li>
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box direction="Column" gap="100">
|
||||||
|
<Text size="L400">Address</Text>
|
||||||
|
<Input
|
||||||
|
size="500"
|
||||||
|
autoFocus
|
||||||
|
name="addressInput"
|
||||||
|
variant="Background"
|
||||||
|
placeholder="#community:server"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
{invalid && (
|
||||||
|
<Text size="T200" style={{ color: color.Critical.Main }}>
|
||||||
|
<b>Invalid Address</b>
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
<Button type="submit" variant="Primary">
|
||||||
|
<Text size="B400">Open</Text>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Dialog>
|
||||||
|
</FocusTrap>
|
||||||
|
</OverlayCenter>
|
||||||
|
</Overlay>
|
||||||
|
);
|
||||||
|
}
|
||||||
1
src/app/components/join-address-prompt/index.ts
Normal file
1
src/app/components/join-address-prompt/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './JoinAddressPrompt';
|
||||||
@@ -124,8 +124,8 @@ export function UserRoomProfile({ userId }: UserRoomProfileProps) {
|
|||||||
{server && <ServerChip server={server} />}
|
{server && <ServerChip server={server} />}
|
||||||
<ShareChip userId={userId} />
|
<ShareChip userId={userId} />
|
||||||
{creator ? <CreatorChip /> : <PowerChip userId={userId} />}
|
{creator ? <CreatorChip /> : <PowerChip userId={userId} />}
|
||||||
<MutualRoomsChip userId={userId} />
|
{userId !== myUserId && <MutualRoomsChip userId={userId} />}
|
||||||
<OptionsChip userId={userId} />
|
{userId !== myUserId && <OptionsChip userId={userId} />}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
{ignored && <IgnoredUserAlert />}
|
{ignored && <IgnoredUserAlert />}
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ import {
|
|||||||
import { useSpaceOptionally } from '../../../hooks/useSpace';
|
import { useSpaceOptionally } from '../../../hooks/useSpace';
|
||||||
import { useFlattenPowerTagMembers, useGetMemberPowerTag } from '../../../hooks/useMemberPowerTag';
|
import { useFlattenPowerTagMembers, useGetMemberPowerTag } from '../../../hooks/useMemberPowerTag';
|
||||||
import { useRoomCreators } from '../../../hooks/useRoomCreators';
|
import { useRoomCreators } from '../../../hooks/useRoomCreators';
|
||||||
|
import { getMouseEventCords } from '../../../utils/dom';
|
||||||
|
|
||||||
const SEARCH_OPTIONS: UseAsyncSearchOptions = {
|
const SEARCH_OPTIONS: UseAsyncSearchOptions = {
|
||||||
limit: 1000,
|
limit: 1000,
|
||||||
@@ -145,7 +146,7 @@ export function Members({ requestClose }: MembersProps) {
|
|||||||
const btn = evt.currentTarget as HTMLButtonElement;
|
const btn = evt.currentTarget as HTMLButtonElement;
|
||||||
const userId = btn.getAttribute('data-user-id');
|
const userId = btn.getAttribute('data-user-id');
|
||||||
if (userId) {
|
if (userId) {
|
||||||
openProfile(room.roomId, space?.roomId, userId, btn.getBoundingClientRect());
|
openProfile(room.roomId, space?.roomId, userId, getMouseEventCords(evt.nativeEvent));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import { UserAvatar } from '../../../components/user-avatar';
|
|||||||
import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
|
import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
|
||||||
import { useOpenUserRoomProfile } from '../../../state/hooks/userRoomProfile';
|
import { useOpenUserRoomProfile } from '../../../state/hooks/userRoomProfile';
|
||||||
import { useSpaceOptionally } from '../../../hooks/useSpace';
|
import { useSpaceOptionally } from '../../../hooks/useSpace';
|
||||||
|
import { getMouseEventCords } from '../../../utils/dom';
|
||||||
|
|
||||||
export type ReactionViewerProps = {
|
export type ReactionViewerProps = {
|
||||||
room: Room;
|
room: Room;
|
||||||
@@ -136,7 +137,7 @@ export const ReactionViewer = as<'div', ReactionViewerProps>(
|
|||||||
room.roomId,
|
room.roomId,
|
||||||
space?.roomId,
|
space?.roomId,
|
||||||
senderId,
|
senderId,
|
||||||
event.currentTarget.getBoundingClientRect(),
|
getMouseEventCords(event.nativeEvent),
|
||||||
'Bottom'
|
'Bottom'
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ const DEFAULT_TAGS: PowerLevelTags = {
|
|||||||
color: '#ff6a00',
|
color: '#ff6a00',
|
||||||
},
|
},
|
||||||
150: {
|
150: {
|
||||||
name: 'Co-Founder',
|
name: 'Manager',
|
||||||
color: '#ff6a7f',
|
color: '#ff6a7f',
|
||||||
},
|
},
|
||||||
101: {
|
101: {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export function AuthFooter() {
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
v4.9.0
|
v4.9.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.9.0
|
v4.9.1
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,10 +30,12 @@ import {
|
|||||||
NavLink,
|
NavLink,
|
||||||
} from '../../../components/nav';
|
} from '../../../components/nav';
|
||||||
import {
|
import {
|
||||||
|
encodeSearchParamValueArray,
|
||||||
getExplorePath,
|
getExplorePath,
|
||||||
getHomeCreatePath,
|
getHomeCreatePath,
|
||||||
getHomeRoomPath,
|
getHomeRoomPath,
|
||||||
getHomeSearchPath,
|
getHomeSearchPath,
|
||||||
|
withSearchParam,
|
||||||
} from '../../pathUtils';
|
} from '../../pathUtils';
|
||||||
import { getCanonicalAliasOrRoomId } from '../../../utils/matrix';
|
import { getCanonicalAliasOrRoomId } from '../../../utils/matrix';
|
||||||
import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom';
|
import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom';
|
||||||
@@ -49,7 +51,6 @@ import { makeNavCategoryId } from '../../../state/closedNavCategories';
|
|||||||
import { roomToUnreadAtom } from '../../../state/room/roomToUnread';
|
import { roomToUnreadAtom } from '../../../state/room/roomToUnread';
|
||||||
import { useCategoryHandler } from '../../../hooks/useCategoryHandler';
|
import { useCategoryHandler } from '../../../hooks/useCategoryHandler';
|
||||||
import { useNavToActivePathMapper } from '../../../hooks/useNavToActivePathMapper';
|
import { useNavToActivePathMapper } from '../../../hooks/useNavToActivePathMapper';
|
||||||
import { openJoinAlias } from '../../../../client/action/navigation';
|
|
||||||
import { PageNav, PageNavHeader, PageNavContent } from '../../../components/page';
|
import { PageNav, PageNavHeader, PageNavContent } from '../../../components/page';
|
||||||
import { useRoomsUnread } from '../../../state/hooks/unread';
|
import { useRoomsUnread } from '../../../state/hooks/unread';
|
||||||
import { markAsRead } from '../../../../client/action/notifications';
|
import { markAsRead } from '../../../../client/action/notifications';
|
||||||
@@ -61,6 +62,9 @@ import {
|
|||||||
getRoomNotificationMode,
|
getRoomNotificationMode,
|
||||||
useRoomsNotificationPreferencesContext,
|
useRoomsNotificationPreferencesContext,
|
||||||
} from '../../../hooks/useRoomsNotificationPreferences';
|
} from '../../../hooks/useRoomsNotificationPreferences';
|
||||||
|
import { UseStateProvider } from '../../../components/UseStateProvider';
|
||||||
|
import { JoinAddressPrompt } from '../../../components/join-address-prompt';
|
||||||
|
import { _RoomSearchParams } from '../../paths';
|
||||||
|
|
||||||
type HomeMenuProps = {
|
type HomeMenuProps = {
|
||||||
requestClose: () => void;
|
requestClose: () => void;
|
||||||
@@ -77,11 +81,6 @@ const HomeMenu = forwardRef<HTMLDivElement, HomeMenuProps>(({ requestClose }, re
|
|||||||
requestClose();
|
requestClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleJoinAddress = () => {
|
|
||||||
openJoinAlias();
|
|
||||||
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 }}>
|
||||||
@@ -96,16 +95,6 @@ const HomeMenu = forwardRef<HTMLDivElement, HomeMenuProps>(({ requestClose }, re
|
|||||||
Mark as Read
|
Mark as Read
|
||||||
</Text>
|
</Text>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
|
||||||
onClick={handleJoinAddress}
|
|
||||||
size="300"
|
|
||||||
radii="300"
|
|
||||||
after={<Icon size="100" src={Icons.Link} />}
|
|
||||||
>
|
|
||||||
<Text style={{ flexGrow: 1 }} as="span" size="T300" truncate>
|
|
||||||
Join with Address
|
|
||||||
</Text>
|
|
||||||
</MenuItem>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
@@ -268,22 +257,44 @@ export function Home() {
|
|||||||
</NavItemContent>
|
</NavItemContent>
|
||||||
</NavButton>
|
</NavButton>
|
||||||
</NavItem>
|
</NavItem>
|
||||||
<NavItem variant="Background" radii="400">
|
<UseStateProvider initial={false}>
|
||||||
<NavButton onClick={() => openJoinAlias()}>
|
{(open, setOpen) => (
|
||||||
<NavItemContent>
|
<>
|
||||||
<Box as="span" grow="Yes" alignItems="Center" gap="200">
|
<NavItem variant="Background" radii="400">
|
||||||
<Avatar size="200" radii="400">
|
<NavButton onClick={() => setOpen(true)}>
|
||||||
<Icon src={Icons.Link} size="100" />
|
<NavItemContent>
|
||||||
</Avatar>
|
<Box as="span" grow="Yes" alignItems="Center" gap="200">
|
||||||
<Box as="span" grow="Yes">
|
<Avatar size="200" radii="400">
|
||||||
<Text as="span" size="Inherit" truncate>
|
<Icon src={Icons.Link} size="100" />
|
||||||
Join with Address
|
</Avatar>
|
||||||
</Text>
|
<Box as="span" grow="Yes">
|
||||||
</Box>
|
<Text as="span" size="Inherit" truncate>
|
||||||
</Box>
|
Join with Address
|
||||||
</NavItemContent>
|
</Text>
|
||||||
</NavButton>
|
</Box>
|
||||||
</NavItem>
|
</Box>
|
||||||
|
</NavItemContent>
|
||||||
|
</NavButton>
|
||||||
|
</NavItem>
|
||||||
|
{open && (
|
||||||
|
<JoinAddressPrompt
|
||||||
|
onCancel={() => setOpen(false)}
|
||||||
|
onOpen={(roomIdOrAlias, viaServers, eventId) => {
|
||||||
|
setOpen(false);
|
||||||
|
const path = getHomeRoomPath(roomIdOrAlias, eventId);
|
||||||
|
navigate(
|
||||||
|
viaServers
|
||||||
|
? withSearchParam<_RoomSearchParams>(path, {
|
||||||
|
viaServers: encodeSearchParamValueArray(viaServers),
|
||||||
|
})
|
||||||
|
: path
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</UseStateProvider>
|
||||||
<NavItem variant="Background" radii="400" aria-selected={searchSelected}>
|
<NavItem variant="Background" radii="400" aria-selected={searchSelected}>
|
||||||
<NavLink to={getHomeSearchPath()}>
|
<NavLink to={getHomeSearchPath()}>
|
||||||
<NavItemContent>
|
<NavItemContent>
|
||||||
|
|||||||
@@ -7,15 +7,22 @@ import { stopPropagation } from '../../../utils/keyboard';
|
|||||||
import { SequenceCard } from '../../../components/sequence-card';
|
import { SequenceCard } from '../../../components/sequence-card';
|
||||||
import { SettingTile } from '../../../components/setting-tile';
|
import { SettingTile } from '../../../components/setting-tile';
|
||||||
import { ContainerColor } from '../../../styles/ContainerColor.css';
|
import { ContainerColor } from '../../../styles/ContainerColor.css';
|
||||||
import { openJoinAlias } from '../../../../client/action/navigation';
|
import {
|
||||||
import { getCreatePath } from '../../pathUtils';
|
encodeSearchParamValueArray,
|
||||||
|
getCreatePath,
|
||||||
|
getSpacePath,
|
||||||
|
withSearchParam,
|
||||||
|
} from '../../pathUtils';
|
||||||
import { useCreateSelected } from '../../../hooks/router/useCreateSelected';
|
import { useCreateSelected } from '../../../hooks/router/useCreateSelected';
|
||||||
|
import { JoinAddressPrompt } from '../../../components/join-address-prompt';
|
||||||
|
import { _RoomSearchParams } from '../../paths';
|
||||||
|
|
||||||
export function CreateTab() {
|
export function CreateTab() {
|
||||||
const createSelected = useCreateSelected();
|
const createSelected = useCreateSelected();
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [menuCords, setMenuCords] = useState<RectCords>();
|
const [menuCords, setMenuCords] = useState<RectCords>();
|
||||||
|
const [joinAddress, setJoinAddress] = useState(false);
|
||||||
|
|
||||||
const handleMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
|
const handleMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
|
||||||
setMenuCords(menuCords ? undefined : evt.currentTarget.getBoundingClientRect());
|
setMenuCords(menuCords ? undefined : evt.currentTarget.getBoundingClientRect());
|
||||||
@@ -27,7 +34,7 @@ export function CreateTab() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleJoinWithAddress = () => {
|
const handleJoinWithAddress = () => {
|
||||||
openJoinAlias();
|
setJoinAddress(true);
|
||||||
setMenuCords(undefined);
|
setMenuCords(undefined);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -103,6 +110,22 @@ export function CreateTab() {
|
|||||||
>
|
>
|
||||||
<Icon src={Icons.Plus} />
|
<Icon src={Icons.Plus} />
|
||||||
</SidebarAvatar>
|
</SidebarAvatar>
|
||||||
|
{joinAddress && (
|
||||||
|
<JoinAddressPrompt
|
||||||
|
onCancel={() => setJoinAddress(false)}
|
||||||
|
onOpen={(roomIdOrAlias, viaServers) => {
|
||||||
|
setJoinAddress(false);
|
||||||
|
const path = getSpacePath(roomIdOrAlias);
|
||||||
|
navigate(
|
||||||
|
viaServers
|
||||||
|
? withSearchParam<_RoomSearchParams>(path, {
|
||||||
|
viaServers: encodeSearchParamValueArray(viaServers),
|
||||||
|
})
|
||||||
|
: path
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</PopOut>
|
</PopOut>
|
||||||
)}
|
)}
|
||||||
</SidebarItemTooltip>
|
</SidebarItemTooltip>
|
||||||
|
|||||||
@@ -297,7 +297,7 @@ function SpaceHeader() {
|
|||||||
type SpaceTombstoneProps = { roomId: string; replacementRoomId: string };
|
type SpaceTombstoneProps = { roomId: string; replacementRoomId: string };
|
||||||
export function SpaceTombstone({ roomId, replacementRoomId }: SpaceTombstoneProps) {
|
export function SpaceTombstone({ roomId, replacementRoomId }: SpaceTombstoneProps) {
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
const { navigateRoom } = useRoomNavigate();
|
const { navigateSpace } = useRoomNavigate();
|
||||||
|
|
||||||
const [joinState, handleJoin] = useAsyncCallback(
|
const [joinState, handleJoin] = useAsyncCallback(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
@@ -311,8 +311,8 @@ export function SpaceTombstone({ roomId, replacementRoomId }: SpaceTombstoneProp
|
|||||||
const replacementRoom = mx.getRoom(replacementRoomId);
|
const replacementRoom = mx.getRoom(replacementRoomId);
|
||||||
|
|
||||||
const handleOpen = () => {
|
const handleOpen = () => {
|
||||||
if (replacementRoom) navigateRoom(replacementRoom.roomId);
|
if (replacementRoom) navigateSpace(replacementRoom.roomId);
|
||||||
if (joinState.status === AsyncStatus.Success) navigateRoom(joinState.data.roomId);
|
if (joinState.status === AsyncStatus.Success) navigateSpace(joinState.data.roomId);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -42,9 +42,9 @@ const MATRIX_TO = /^https?:\/\/matrix\.to\S*$/;
|
|||||||
export const testMatrixTo = (href: string): boolean => MATRIX_TO.test(href);
|
export const testMatrixTo = (href: string): boolean => MATRIX_TO.test(href);
|
||||||
|
|
||||||
const MATRIX_TO_USER = /^https?:\/\/matrix\.to\/#\/(@[^:\s]+:[^?/\s]+)\/?$/;
|
const MATRIX_TO_USER = /^https?:\/\/matrix\.to\/#\/(@[^:\s]+:[^?/\s]+)\/?$/;
|
||||||
const MATRIX_TO_ROOM = /^https?:\/\/matrix\.to\/#\/([#!][^:\s]+:[^?/\s]+)\/?(\?[\S]*)?$/;
|
const MATRIX_TO_ROOM = /^https?:\/\/matrix\.to\/#\/([#!][^?/\s]+)\/?(\?[\S]*)?$/;
|
||||||
const MATRIX_TO_ROOM_EVENT =
|
const MATRIX_TO_ROOM_EVENT =
|
||||||
/^https?:\/\/matrix\.to\/#\/([#!][^:\s]+:[^?/\s]+)\/(\$[^?/\s]+)\/?(\?[\S]*)?$/;
|
/^https?:\/\/matrix\.to\/#\/([#!][^?/\s]+)\/(\$[^?/\s]+)\/?(\?[\S]*)?$/;
|
||||||
|
|
||||||
export const parseMatrixToUser = (href: string): string | undefined => {
|
export const parseMatrixToUser = (href: string): string | undefined => {
|
||||||
const match = href.match(MATRIX_TO_USER);
|
const match = href.match(MATRIX_TO_USER);
|
||||||
|
|||||||
@@ -1,11 +1,19 @@
|
|||||||
import { Room } from 'matrix-js-sdk';
|
import { Room } from 'matrix-js-sdk';
|
||||||
import { IPowerLevels } from '../hooks/usePowerLevels';
|
import { IPowerLevels } from '../hooks/usePowerLevels';
|
||||||
import { getMxIdServer } from '../utils/matrix';
|
import { creatorsSupported, getMxIdServer } from '../utils/matrix';
|
||||||
import { StateEvent } from '../../types/matrix/room';
|
import { IRoomCreateContent, StateEvent } from '../../types/matrix/room';
|
||||||
import { getStateEvent } from '../utils/room';
|
import { getStateEvent } from '../utils/room';
|
||||||
|
|
||||||
export const getViaServers = (room: Room): string[] => {
|
export const getViaServers = (room: Room): string[] => {
|
||||||
const getHighestPowerUserId = (): string | undefined => {
|
const getHighestPowerUserId = (): string | undefined => {
|
||||||
|
const creatorEvent = getStateEvent(room, StateEvent.RoomCreate);
|
||||||
|
if (
|
||||||
|
creatorEvent &&
|
||||||
|
creatorsSupported(creatorEvent.getContent<IRoomCreateContent>().room_version)
|
||||||
|
) {
|
||||||
|
return creatorEvent.getSender();
|
||||||
|
}
|
||||||
|
|
||||||
const powerLevels = getStateEvent(room, StateEvent.RoomPowerLevels)?.getContent<IPowerLevels>();
|
const powerLevels = getStateEvent(room, StateEvent.RoomPowerLevels)?.getContent<IPowerLevels>();
|
||||||
|
|
||||||
if (!powerLevels) return undefined;
|
if (!powerLevels) return undefined;
|
||||||
|
|||||||
@@ -43,6 +43,17 @@ export const canFitInScrollView = (
|
|||||||
|
|
||||||
export type FilesOrFile<T extends boolean | undefined = undefined> = T extends true ? File[] : File;
|
export type FilesOrFile<T extends boolean | undefined = undefined> = T extends true ? File[] : File;
|
||||||
|
|
||||||
|
export const getFilesFromFileList = (fileList: FileList): File[] => {
|
||||||
|
const files: File[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < fileList.length; i += 1) {
|
||||||
|
const file: File | undefined = fileList[i];
|
||||||
|
if (file instanceof File) files.push(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
|
};
|
||||||
|
|
||||||
export const selectFile = <M extends boolean | undefined = undefined>(
|
export const selectFile = <M extends boolean | undefined = undefined>(
|
||||||
accept: string,
|
accept: string,
|
||||||
multiple?: M
|
multiple?: M
|
||||||
@@ -58,7 +69,7 @@ export const selectFile = <M extends boolean | undefined = undefined>(
|
|||||||
if (!fileList) {
|
if (!fileList) {
|
||||||
resolve(undefined);
|
resolve(undefined);
|
||||||
} else {
|
} else {
|
||||||
const files: File[] = [...fileList].filter((file) => file);
|
const files: File[] = getFilesFromFileList(fileList);
|
||||||
resolve((multiple ? files : files[0]) as FilesOrFile<M>);
|
resolve((multiple ? files : files[0]) as FilesOrFile<M>);
|
||||||
}
|
}
|
||||||
input.removeEventListener('change', changeHandler);
|
input.removeEventListener('change', changeHandler);
|
||||||
@@ -70,7 +81,7 @@ export const selectFile = <M extends boolean | undefined = undefined>(
|
|||||||
|
|
||||||
export const getDataTransferFiles = (dataTransfer: DataTransfer): File[] | undefined => {
|
export const getDataTransferFiles = (dataTransfer: DataTransfer): File[] | undefined => {
|
||||||
const fileList = dataTransfer.files;
|
const fileList = dataTransfer.files;
|
||||||
const files = [...fileList].filter((file) => file);
|
const files: File[] = getFilesFromFileList(fileList);
|
||||||
if (files.length === 0) return undefined;
|
if (files.length === 0) return undefined;
|
||||||
return files;
|
return files;
|
||||||
};
|
};
|
||||||
@@ -224,3 +235,10 @@ export const notificationPermission = (permission: NotificationPermission) => {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getMouseEventCords = (event: MouseEvent) => ({
|
||||||
|
x: event.clientX,
|
||||||
|
y: event.clientY,
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
});
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import {
|
|||||||
import { CryptoBackend } from 'matrix-js-sdk/lib/common-crypto/CryptoBackend';
|
import { CryptoBackend } from 'matrix-js-sdk/lib/common-crypto/CryptoBackend';
|
||||||
import { AccountDataEvent } from '../../types/matrix/accountData';
|
import { AccountDataEvent } from '../../types/matrix/accountData';
|
||||||
import {
|
import {
|
||||||
|
IRoomCreateContent,
|
||||||
Membership,
|
Membership,
|
||||||
MessageEvent,
|
MessageEvent,
|
||||||
NotificationType,
|
NotificationType,
|
||||||
@@ -43,7 +44,7 @@ export const getStateEvents = (room: Room, eventType: StateEvent): MatrixEvent[]
|
|||||||
export const getAccountData = (
|
export const getAccountData = (
|
||||||
mx: MatrixClient,
|
mx: MatrixClient,
|
||||||
eventType: AccountDataEvent
|
eventType: AccountDataEvent
|
||||||
): MatrixEvent | undefined => mx.getAccountData(eventType);
|
): MatrixEvent | undefined => mx.getAccountData(eventType as any);
|
||||||
|
|
||||||
export const getMDirects = (mDirectEvent: MatrixEvent): Set<string> => {
|
export const getMDirects = (mDirectEvent: MatrixEvent): Set<string> => {
|
||||||
const roomIds = new Set<string>();
|
const roomIds = new Set<string>();
|
||||||
@@ -480,6 +481,23 @@ export const bannedInRooms = (mx: MatrixClient, rooms: string[], otherUserId: st
|
|||||||
return banned;
|
return banned;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const getAllVersionsRoomCreator = (room: Room): Set<string> => {
|
||||||
|
const creators = new Set<string>();
|
||||||
|
|
||||||
|
const createEvent = getStateEvent(room, StateEvent.RoomCreate);
|
||||||
|
const createContent = createEvent?.getContent<IRoomCreateContent>();
|
||||||
|
const creator = createEvent?.getSender();
|
||||||
|
if (typeof creator === 'string') creators.add(creator);
|
||||||
|
|
||||||
|
if (createContent && Array.isArray(createContent.additional_creators)) {
|
||||||
|
createContent.additional_creators.forEach((c) => {
|
||||||
|
if (typeof c === 'string') creators.add(c);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return creators;
|
||||||
|
};
|
||||||
|
|
||||||
export const guessPerfectParent = (
|
export const guessPerfectParent = (
|
||||||
mx: MatrixClient,
|
mx: MatrixClient,
|
||||||
roomId: string,
|
roomId: string,
|
||||||
@@ -490,15 +508,29 @@ export const guessPerfectParent = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getSpecialUsers = (rId: string): string[] => {
|
const getSpecialUsers = (rId: string): string[] => {
|
||||||
|
const specialUsers: Set<string> = new Set();
|
||||||
|
|
||||||
const r = mx.getRoom(rId);
|
const r = mx.getRoom(rId);
|
||||||
const powerLevels =
|
if (!r) return [];
|
||||||
r && getStateEvent(r, StateEvent.RoomPowerLevels)?.getContent<IPowerLevelsContent>();
|
|
||||||
|
getAllVersionsRoomCreator(r).forEach((c) => specialUsers.add(c));
|
||||||
|
|
||||||
|
const powerLevels = getStateEvent(
|
||||||
|
r,
|
||||||
|
StateEvent.RoomPowerLevels
|
||||||
|
)?.getContent<IPowerLevelsContent>();
|
||||||
|
|
||||||
const { users_default: usersDefault, users } = powerLevels ?? {};
|
const { users_default: usersDefault, users } = powerLevels ?? {};
|
||||||
if (typeof users !== 'object') return [];
|
|
||||||
|
|
||||||
const defaultPower = typeof usersDefault === 'number' ? usersDefault : 0;
|
const defaultPower = typeof usersDefault === 'number' ? usersDefault : 0;
|
||||||
return Object.keys(users).filter((userId) => users[userId] > defaultPower);
|
|
||||||
|
if (typeof users === 'object')
|
||||||
|
Object.keys(users).forEach((userId) => {
|
||||||
|
if (users[userId] > defaultPower) {
|
||||||
|
specialUsers.add(userId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Array.from(specialUsers);
|
||||||
};
|
};
|
||||||
|
|
||||||
let perfectParent: string | undefined;
|
let perfectParent: string | undefined;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
const cons = {
|
const cons = {
|
||||||
version: '4.9.0',
|
version: '4.9.1',
|
||||||
secretKey: {
|
secretKey: {
|
||||||
ACCESS_TOKEN: 'cinny_access_token',
|
ACCESS_TOKEN: 'cinny_access_token',
|
||||||
DEVICE_ID: 'cinny_device_id',
|
DEVICE_ID: 'cinny_device_id',
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ export type IRoomCreateContent = {
|
|||||||
['m.federate']?: boolean;
|
['m.federate']?: boolean;
|
||||||
room_version: string;
|
room_version: string;
|
||||||
type?: string;
|
type?: string;
|
||||||
|
additional_creators?: string[];
|
||||||
predecessor?: {
|
predecessor?: {
|
||||||
event_id?: string;
|
event_id?: string;
|
||||||
room_id: string;
|
room_id: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user