mirror of
https://github.com/cinnyapp/cinny.git
synced 2025-11-05 06:50:28 +03:00
Revert "Add a page and config option for a room directory server"
This reverts commit401d8ed930. Revert "Minor styling changes" This reverts commit3de1ce8f2f. Revert "Use consistent "bookmark" wording in code and UI" This reverts commitc45cb62e2d. Revert "Move featured servers into the Featured section" This reverts commit53612f4641.
This commit is contained in:
parent
6a462d2801
commit
be51dc5f12
13 changed files with 279 additions and 377 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
|
@ -4,8 +4,5 @@
|
||||||
"typescript.tsdk": "node_modules/typescript/lib",
|
"typescript.tsdk": "node_modules/typescript/lib",
|
||||||
"[typescript]": {
|
"[typescript]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
},
|
|
||||||
"[typescriptreact]": {
|
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
10
config.json
10
config.json
|
|
@ -9,6 +9,7 @@
|
||||||
"xmr.se"
|
"xmr.se"
|
||||||
],
|
],
|
||||||
"allowCustomHomeservers": true,
|
"allowCustomHomeservers": true,
|
||||||
|
|
||||||
"featuredCommunities": {
|
"featuredCommunities": {
|
||||||
"openAsDefault": false,
|
"openAsDefault": false,
|
||||||
"spaces": [
|
"spaces": [
|
||||||
|
|
@ -27,14 +28,9 @@
|
||||||
"#PrivSec.dev:arcticfoxes.net",
|
"#PrivSec.dev:arcticfoxes.net",
|
||||||
"#disroot:aria-net.org"
|
"#disroot:aria-net.org"
|
||||||
],
|
],
|
||||||
"servers": [
|
"servers": ["envs.net", "matrix.org", "monero.social", "mozilla.org"]
|
||||||
"envs.net",
|
|
||||||
"matrix.org",
|
|
||||||
"monero.social",
|
|
||||||
"mozilla.org"
|
|
||||||
],
|
|
||||||
"directoryServer": "matrixrooms.info"
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"hashRouter": {
|
"hashRouter": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"basename": "/"
|
"basename": "/"
|
||||||
|
|
|
||||||
|
|
@ -5,5 +5,6 @@ export const InfoCard = style([
|
||||||
{
|
{
|
||||||
padding: config.space.S200,
|
padding: config.space.S200,
|
||||||
borderRadius: config.radii.R300,
|
borderRadius: config.radii.R300,
|
||||||
|
borderWidth: config.borderWidth.B300,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ const setGridColumnCount = (grid: HTMLElement, count: GridColumnCount): void =>
|
||||||
grid.style.setProperty('grid-template-columns', `repeat(${count}, 1fr)`);
|
grid.style.setProperty('grid-template-columns', `repeat(${count}, 1fr)`);
|
||||||
};
|
};
|
||||||
|
|
||||||
export function CardGrid({ children }: { children: ReactNode }) {
|
export function RoomCardGrid({ children }: { children: ReactNode }) {
|
||||||
const gridRef = useRef<HTMLDivElement>(null);
|
const gridRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
useElementSizeObserver(
|
useElementSizeObserver(
|
||||||
|
|
@ -60,17 +60,17 @@ export function CardGrid({ children }: { children: ReactNode }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CardBase = as<'div'>(({ className, ...props }, ref) => (
|
export const RoomCardBase = as<'div'>(({ className, ...props }, ref) => (
|
||||||
<Box
|
<Box
|
||||||
direction="Column"
|
direction="Column"
|
||||||
gap="300"
|
gap="300"
|
||||||
className={classNames(css.CardBase, className)}
|
className={classNames(css.RoomCardBase, className)}
|
||||||
{...props}
|
{...props}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
||||||
export const CardName = as<'h6'>(({ ...props }, ref) => (
|
export const RoomCardName = as<'h6'>(({ ...props }, ref) => (
|
||||||
<Text as="h6" size="H6" truncate {...props} ref={ref} />
|
<Text as="h6" size="H6" truncate {...props} ref={ref} />
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
@ -208,7 +208,7 @@ export const RoomCard = as<'div', RoomCardProps>(
|
||||||
const openTopic = () => setViewTopic(true);
|
const openTopic = () => setViewTopic(true);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CardBase {...props} ref={ref}>
|
<RoomCardBase {...props} ref={ref}>
|
||||||
<Box gap="200" justifyContent="SpaceBetween">
|
<Box gap="200" justifyContent="SpaceBetween">
|
||||||
<Avatar size="500">
|
<Avatar size="500">
|
||||||
<RoomAvatar
|
<RoomAvatar
|
||||||
|
|
@ -229,7 +229,7 @@ export const RoomCard = as<'div', RoomCardProps>(
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<Box grow="Yes" direction="Column" gap="100">
|
<Box grow="Yes" direction="Column" gap="100">
|
||||||
<CardName>{roomName}</CardName>
|
<RoomCardName>{roomName}</RoomCardName>
|
||||||
<RoomCardTopic onClick={openTopic} onKeyDown={onEnterOrSpace(openTopic)} tabIndex={0}>
|
<RoomCardTopic onClick={openTopic} onKeyDown={onEnterOrSpace(openTopic)} tabIndex={0}>
|
||||||
{roomTopic}
|
{roomTopic}
|
||||||
</RoomCardTopic>
|
</RoomCardTopic>
|
||||||
|
|
@ -314,7 +314,7 @@ export const RoomCard = as<'div', RoomCardProps>(
|
||||||
</ErrorDialog>
|
</ErrorDialog>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</CardBase>
|
</RoomCardBase>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ export const CardGrid = style({
|
||||||
gap: config.space.S400,
|
gap: config.space.S400,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const CardBase = style([
|
export const RoomCardBase = style([
|
||||||
DefaultReset,
|
DefaultReset,
|
||||||
ContainerColor({ variant: 'SurfaceVariant' }),
|
ContainerColor({ variant: 'SurfaceVariant' }),
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { useMatch, useParams } from 'react-router-dom';
|
import { useMatch, useParams } from 'react-router-dom';
|
||||||
import { getExploreFeaturedPath, getExplorePath } from '../../pages/pathUtils';
|
import { getExploreFeaturedPath, getExplorePath } from '../../pages/pathUtils';
|
||||||
import { useClientConfig } from '../useClientConfig';
|
|
||||||
|
|
||||||
export const useExploreSelected = (): boolean => {
|
export const useExploreSelected = (): boolean => {
|
||||||
const match = useMatch({
|
const match = useMatch({
|
||||||
|
|
@ -27,8 +26,3 @@ export const useExploreServer = (): string | undefined => {
|
||||||
|
|
||||||
return server;
|
return server;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useDirectoryServer = (): string | undefined => {
|
|
||||||
const { featuredCommunities } = useClientConfig();
|
|
||||||
return featuredCommunities?.directoryServer;
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
import { useCallback, useMemo } from 'react';
|
|
||||||
import { AccountDataEvent } from '../../types/matrix/accountData';
|
|
||||||
import { useAccountData } from './useAccountData';
|
|
||||||
import { useMatrixClient } from './useMatrixClient';
|
|
||||||
|
|
||||||
export type InCinnyBookmarkedServersContent = {
|
|
||||||
servers?: string[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useBookmarkedServers = (): [
|
|
||||||
string[],
|
|
||||||
(server: string) => Promise<void>,
|
|
||||||
(server: string) => Promise<void>
|
|
||||||
] => {
|
|
||||||
const mx = useMatrixClient();
|
|
||||||
const accountData = useAccountData(AccountDataEvent.CinnyBookmarkedServers);
|
|
||||||
const bookmarkedServers = useMemo(
|
|
||||||
() => accountData?.getContent<InCinnyBookmarkedServersContent>()?.servers ?? [],
|
|
||||||
[accountData]
|
|
||||||
);
|
|
||||||
|
|
||||||
const addServerBookmark = useCallback(
|
|
||||||
async (server: string) => {
|
|
||||||
if (bookmarkedServers.indexOf(server) === -1) {
|
|
||||||
await mx.setAccountData(AccountDataEvent.CinnyBookmarkedServers, {
|
|
||||||
servers: [...bookmarkedServers, server],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[mx, bookmarkedServers]
|
|
||||||
);
|
|
||||||
|
|
||||||
const removeServerBookmark = useCallback(
|
|
||||||
async (server: string) => {
|
|
||||||
await mx.setAccountData(AccountDataEvent.CinnyBookmarkedServers, {
|
|
||||||
servers: bookmarkedServers.filter((addedServer) => server !== addedServer),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[mx, bookmarkedServers]
|
|
||||||
);
|
|
||||||
|
|
||||||
return [bookmarkedServers, addServerBookmark, removeServerBookmark];
|
|
||||||
};
|
|
||||||
|
|
@ -15,7 +15,6 @@ export type ClientConfig = {
|
||||||
spaces?: string[];
|
spaces?: string[];
|
||||||
rooms?: string[];
|
rooms?: string[];
|
||||||
servers?: string[];
|
servers?: string[];
|
||||||
directoryServer?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
hashRouter?: HashRouterConfig;
|
hashRouter?: HashRouterConfig;
|
||||||
|
|
|
||||||
43
src/app/hooks/useExploreServers.ts
Normal file
43
src/app/hooks/useExploreServers.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
import { useCallback, useMemo } from 'react';
|
||||||
|
import { AccountDataEvent } from '../../types/matrix/accountData';
|
||||||
|
import { useAccountData } from './useAccountData';
|
||||||
|
import { useMatrixClient } from './useMatrixClient';
|
||||||
|
|
||||||
|
export type InCinnyExploreServersContent = {
|
||||||
|
servers?: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useExploreServers = (): [
|
||||||
|
string[],
|
||||||
|
(server: string) => Promise<void>,
|
||||||
|
(server: string) => Promise<void>
|
||||||
|
] => {
|
||||||
|
const mx = useMatrixClient();
|
||||||
|
const accountData = useAccountData(AccountDataEvent.CinnyExplore);
|
||||||
|
const userAddedServers = useMemo(
|
||||||
|
() => accountData?.getContent<InCinnyExploreServersContent>()?.servers ?? [],
|
||||||
|
[accountData]
|
||||||
|
);
|
||||||
|
|
||||||
|
const addServer = useCallback(
|
||||||
|
async (server: string) => {
|
||||||
|
if (userAddedServers.indexOf(server) === -1) {
|
||||||
|
await mx.setAccountData(AccountDataEvent.CinnyExplore, {
|
||||||
|
servers: [...userAddedServers, server],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[mx, userAddedServers]
|
||||||
|
);
|
||||||
|
|
||||||
|
const removeServer = useCallback(
|
||||||
|
async (server: string) => {
|
||||||
|
await mx.setAccountData(AccountDataEvent.CinnyExplore, {
|
||||||
|
servers: userAddedServers.filter((addedServer) => server !== addedServer),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[mx, userAddedServers]
|
||||||
|
);
|
||||||
|
|
||||||
|
return [userAddedServers, addServer, removeServer];
|
||||||
|
};
|
||||||
|
|
@ -31,8 +31,8 @@ import {
|
||||||
NavLink,
|
NavLink,
|
||||||
} from '../../../components/nav';
|
} from '../../../components/nav';
|
||||||
import { getExploreFeaturedPath, getExploreServerPath } from '../../pathUtils';
|
import { getExploreFeaturedPath, getExploreServerPath } from '../../pathUtils';
|
||||||
|
import { useClientConfig } from '../../../hooks/useClientConfig';
|
||||||
import {
|
import {
|
||||||
useDirectoryServer,
|
|
||||||
useExploreFeaturedRooms,
|
useExploreFeaturedRooms,
|
||||||
useExploreServer,
|
useExploreServer,
|
||||||
} from '../../../hooks/router/useExploreSelected';
|
} from '../../../hooks/router/useExploreSelected';
|
||||||
|
|
@ -42,21 +42,21 @@ import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
|
||||||
import { useNavToActivePathMapper } from '../../../hooks/useNavToActivePathMapper';
|
import { useNavToActivePathMapper } from '../../../hooks/useNavToActivePathMapper';
|
||||||
import { PageNav, PageNavContent, PageNavHeader } from '../../../components/page';
|
import { PageNav, PageNavContent, PageNavHeader } from '../../../components/page';
|
||||||
import { stopPropagation } from '../../../utils/keyboard';
|
import { stopPropagation } from '../../../utils/keyboard';
|
||||||
import { useBookmarkedServers } from '../../../hooks/useBookmarkedServers';
|
import { useExploreServers } from '../../../hooks/useExploreServers';
|
||||||
import { useAlive } from '../../../hooks/useAlive';
|
import { useAlive } from '../../../hooks/useAlive';
|
||||||
|
|
||||||
type ExploreServerPromptProps = {
|
type AddExploreServerPromptProps = {
|
||||||
onSubmit: (server: string, save: boolean) => Promise<void>;
|
onSubmit: (server: string) => Promise<void>;
|
||||||
header: ReactNode;
|
header: ReactNode;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
selected?: boolean;
|
selected?: boolean;
|
||||||
};
|
};
|
||||||
export function ExploreServerPrompt({
|
export function AddExploreServerPrompt({
|
||||||
onSubmit,
|
onSubmit,
|
||||||
header,
|
header,
|
||||||
children,
|
children,
|
||||||
selected = false,
|
selected = false,
|
||||||
}: ExploreServerPromptProps) {
|
}: AddExploreServerPromptProps) {
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
const [dialog, setDialog] = useState(false);
|
const [dialog, setDialog] = useState(false);
|
||||||
const alive = useAlive();
|
const alive = useAlive();
|
||||||
|
|
@ -69,27 +69,19 @@ export function ExploreServerPrompt({
|
||||||
return server || undefined;
|
return server || undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = useCallback(
|
const [submitState, handleSubmit] = useAsyncCallback(
|
||||||
async (saveBookmark: boolean) => {
|
useCallback(async () => {
|
||||||
const server = getInputServer();
|
const server = getInputServer();
|
||||||
if (!server) return;
|
if (!server) return;
|
||||||
|
|
||||||
await mx.publicRooms({ server, limit: 1 });
|
await mx.publicRooms({ server, limit: 1 });
|
||||||
await onSubmit(server, saveBookmark);
|
await onSubmit(server);
|
||||||
if (alive()) {
|
if (alive()) {
|
||||||
setDialog(false);
|
setDialog(false);
|
||||||
}
|
}
|
||||||
},
|
}, [alive, onSubmit, mx])
|
||||||
[alive, onSubmit, mx]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const [viewState, handleView] = useAsyncCallback(() => handleSubmit(false));
|
|
||||||
const [saveViewState, handleSaveView] = useAsyncCallback(() => handleSubmit(true));
|
|
||||||
const busy =
|
|
||||||
viewState.status === AsyncStatus.Loading || saveViewState.status === AsyncStatus.Loading;
|
|
||||||
const failed =
|
|
||||||
viewState.status === AsyncStatus.Error || saveViewState.status === AsyncStatus.Error;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Overlay open={dialog} backdrop={<OverlayBackdrop />}>
|
<Overlay open={dialog} backdrop={<OverlayBackdrop />}>
|
||||||
|
|
@ -116,46 +108,41 @@ export function ExploreServerPrompt({
|
||||||
<Icon src={Icons.Cross} />
|
<Icon src={Icons.Cross} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Header>
|
</Header>
|
||||||
<Box as="form" style={{ padding: config.space.S400 }} direction="Column" gap="400">
|
<Box
|
||||||
|
as="form"
|
||||||
|
onSubmit={(evt) => {
|
||||||
|
evt.preventDefault();
|
||||||
|
handleSubmit();
|
||||||
|
}}
|
||||||
|
style={{ padding: config.space.S400 }}
|
||||||
|
direction="Column"
|
||||||
|
gap="400"
|
||||||
|
>
|
||||||
<Text priority="400">Add server name to explore public communities.</Text>
|
<Text priority="400">Add server name to explore public communities.</Text>
|
||||||
<Box direction="Column" gap="100">
|
<Box direction="Column" gap="100">
|
||||||
<Text size="L400">Server Name</Text>
|
<Text size="L400">Server Name</Text>
|
||||||
<Input ref={serverInputRef} name="serverInput" variant="Background" required />
|
<Input ref={serverInputRef} name="serverInput" variant="Background" required />
|
||||||
{failed && (
|
{submitState.status === AsyncStatus.Error && (
|
||||||
<Text style={{ color: color.Critical.Main }} size="T300">
|
<Text style={{ color: color.Critical.Main }} size="T300">
|
||||||
Failed to load public rooms. Please try again.
|
Failed to load public rooms. Please try again.
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
|
</Box>
|
||||||
<Box direction="Column" gap="200">
|
<Box direction="Column" gap="200">
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="submit"
|
||||||
onClick={handleView}
|
onClick={handleSubmit}
|
||||||
variant="Secondary"
|
variant="Secondary"
|
||||||
fill="Soft"
|
fill="Soft"
|
||||||
before={
|
before={
|
||||||
viewState.status === AsyncStatus.Loading && (
|
submitState.status === AsyncStatus.Loading && (
|
||||||
<Spinner fill="Solid" variant="Secondary" size="200" />
|
<Spinner fill="Solid" variant="Secondary" size="200" />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
disabled={busy}
|
disabled={submitState.status === AsyncStatus.Loading}
|
||||||
>
|
>
|
||||||
<Text size="B400">View</Text>
|
<Text size="B400">View</Text>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
onClick={handleSaveView}
|
|
||||||
variant="Primary"
|
|
||||||
fill="Soft"
|
|
||||||
before={
|
|
||||||
saveViewState.status === AsyncStatus.Loading && (
|
|
||||||
<Spinner fill="Solid" variant="Secondary" size="200" />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
disabled={busy}
|
|
||||||
>
|
|
||||||
<Text size="B400">Bookmark & View</Text>
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
@ -171,34 +158,30 @@ export function ExploreServerPrompt({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExploreServerNavItemAction = {
|
|
||||||
onClick: () => Promise<void>;
|
|
||||||
icon: IconSrc;
|
|
||||||
filled?: boolean;
|
|
||||||
alwaysVisible: boolean;
|
|
||||||
};
|
|
||||||
type ExploreServerNavItemProps = {
|
type ExploreServerNavItemProps = {
|
||||||
server: string;
|
server: string;
|
||||||
selected: boolean;
|
selected: boolean;
|
||||||
icon: IconSrc;
|
icon: IconSrc;
|
||||||
action?: ExploreServerNavItemAction;
|
onRemove?: (() => Promise<void>) | null;
|
||||||
};
|
};
|
||||||
export function ExploreServerNavItem({
|
export function ExploreServerNavItem({
|
||||||
server,
|
server,
|
||||||
selected,
|
selected,
|
||||||
icon,
|
icon,
|
||||||
action,
|
onRemove = null,
|
||||||
}: ExploreServerNavItemProps) {
|
}: ExploreServerNavItemProps) {
|
||||||
const [hover, setHover] = useState(false);
|
const [hover, setHover] = useState(false);
|
||||||
const { hoverProps } = useHover({ onHoverChange: setHover });
|
const { hoverProps } = useHover({ onHoverChange: setHover });
|
||||||
const { focusWithinProps } = useFocusWithin({ onFocusWithinChange: setHover });
|
const { focusWithinProps } = useFocusWithin({ onFocusWithinChange: setHover });
|
||||||
const [actionState, actionCallback] = useAsyncCallback(
|
const [removeState, removeCallback] = useAsyncCallback(
|
||||||
useCallback(async () => {
|
useCallback(async () => {
|
||||||
await action?.onClick();
|
if (onRemove !== null) {
|
||||||
}, [action])
|
await onRemove();
|
||||||
|
}
|
||||||
|
}, [onRemove])
|
||||||
);
|
);
|
||||||
const actionInProgress =
|
const removeInProgress =
|
||||||
actionState.status === AsyncStatus.Loading || actionState.status === AsyncStatus.Success;
|
removeState.status === AsyncStatus.Loading || removeState.status === AsyncStatus.Success;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NavItem
|
<NavItem
|
||||||
|
|
@ -222,21 +205,20 @@ export function ExploreServerNavItem({
|
||||||
</Box>
|
</Box>
|
||||||
</NavItemContent>
|
</NavItemContent>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
{action !== undefined && (hover || actionInProgress || action.alwaysVisible) && (
|
{onRemove !== null && (hover || removeInProgress) && (
|
||||||
<NavItemOptions>
|
<NavItemOptions>
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={actionCallback}
|
onClick={removeCallback}
|
||||||
variant={selected ? 'Background' : 'Surface'}
|
variant="Background"
|
||||||
fill="None"
|
fill="None"
|
||||||
outlined={action.alwaysVisible}
|
|
||||||
size="300"
|
size="300"
|
||||||
radii="300"
|
radii="300"
|
||||||
disabled={actionInProgress}
|
disabled={removeInProgress}
|
||||||
>
|
>
|
||||||
{actionInProgress ? (
|
{removeInProgress ? (
|
||||||
<Spinner variant="Secondary" fill="Solid" size="50" />
|
<Spinner variant="Secondary" fill="Solid" size="200" />
|
||||||
) : (
|
) : (
|
||||||
<Icon size="50" src={action.icon} filled={action.filled} />
|
<Icon size="50" src={Icons.Minus} />
|
||||||
)}
|
)}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</NavItemOptions>
|
</NavItemOptions>
|
||||||
|
|
@ -250,39 +232,50 @@ export function Explore() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
useNavToActivePathMapper('explore');
|
useNavToActivePathMapper('explore');
|
||||||
const userId = mx.getUserId();
|
const userId = mx.getUserId();
|
||||||
|
const clientConfig = useClientConfig();
|
||||||
const userServer = userId ? getMxIdServer(userId) : undefined;
|
const userServer = userId ? getMxIdServer(userId) : undefined;
|
||||||
const directoryServer = useDirectoryServer();
|
const featuredServers = useMemo(
|
||||||
const [bookmarkedServers, addServerBookmark, removeServerBookmark] = useBookmarkedServers();
|
() =>
|
||||||
const selectedServer = useExploreServer();
|
clientConfig.featuredCommunities?.servers?.filter((server) => server !== userServer) ?? [],
|
||||||
|
[clientConfig, userServer]
|
||||||
|
);
|
||||||
|
const [exploreServers, addServer, removeServer] = useExploreServers();
|
||||||
|
|
||||||
|
const selectedServer = useExploreServer();
|
||||||
const exploringFeaturedRooms = useExploreFeaturedRooms();
|
const exploringFeaturedRooms = useExploreFeaturedRooms();
|
||||||
const exploringDirectory = directoryServer !== undefined && selectedServer === directoryServer;
|
|
||||||
const exploringUnlistedServer = useMemo(
|
const exploringUnlistedServer = useMemo(
|
||||||
() =>
|
() =>
|
||||||
!(
|
!(
|
||||||
selectedServer === undefined ||
|
selectedServer === undefined ||
|
||||||
selectedServer === userServer ||
|
selectedServer === userServer ||
|
||||||
exploringDirectory ||
|
featuredServers.includes(selectedServer) ||
|
||||||
bookmarkedServers.includes(selectedServer)
|
exploreServers.includes(selectedServer)
|
||||||
),
|
),
|
||||||
[bookmarkedServers, selectedServer, userServer, exploringDirectory]
|
[exploreServers, featuredServers, selectedServer, userServer]
|
||||||
);
|
);
|
||||||
|
|
||||||
const viewServerCallback = useCallback(
|
const addServerCallback = useCallback(
|
||||||
async (server: string, saveBookmark: boolean) => {
|
async (server: string) => {
|
||||||
if (saveBookmark && server !== userServer && server !== directoryServer && selectedServer) {
|
if (server !== userServer && selectedServer && !featuredServers.includes(selectedServer)) {
|
||||||
await addServerBookmark(server);
|
await addServer(server);
|
||||||
}
|
}
|
||||||
navigate(getExploreServerPath(server));
|
navigate(getExploreServerPath(server));
|
||||||
},
|
},
|
||||||
[addServerBookmark, navigate, userServer, directoryServer, selectedServer]
|
[addServer, navigate, userServer, featuredServers, selectedServer]
|
||||||
);
|
);
|
||||||
|
|
||||||
const removeServerBookmarkCallback = useCallback(
|
const removeServerCallback = useCallback(
|
||||||
async (server: string) => {
|
async (server: string) => {
|
||||||
await removeServerBookmark(server);
|
await removeServer(server);
|
||||||
},
|
},
|
||||||
[removeServerBookmark]
|
[removeServer]
|
||||||
|
);
|
||||||
|
|
||||||
|
const exploreUnlistedServerCallback = useCallback(
|
||||||
|
async (server: string) => {
|
||||||
|
navigate(getExploreServerPath(server));
|
||||||
|
},
|
||||||
|
[navigate]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -300,6 +293,29 @@ export function Explore() {
|
||||||
<PageNavContent>
|
<PageNavContent>
|
||||||
<Box direction="Column" gap="300">
|
<Box direction="Column" gap="300">
|
||||||
<NavCategory>
|
<NavCategory>
|
||||||
|
<AddExploreServerPrompt
|
||||||
|
onSubmit={exploreUnlistedServerCallback}
|
||||||
|
header={<Text size="H4">View Server</Text>}
|
||||||
|
selected={exploringUnlistedServer}
|
||||||
|
>
|
||||||
|
<Box as="span" grow="Yes" alignItems="Center" gap="200">
|
||||||
|
<Avatar size="200" radii="400">
|
||||||
|
<Icon src={Icons.Link} size="100" />
|
||||||
|
</Avatar>
|
||||||
|
<Box as="span" grow="Yes">
|
||||||
|
<Text as="span" size="Inherit" truncate>
|
||||||
|
Explore with Address
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</AddExploreServerPrompt>
|
||||||
|
</NavCategory>
|
||||||
|
<NavCategory>
|
||||||
|
<NavCategoryHeader>
|
||||||
|
<Text size="O400" style={{ paddingLeft: config.space.S200 }}>
|
||||||
|
Featured
|
||||||
|
</Text>
|
||||||
|
</NavCategoryHeader>
|
||||||
<NavItem variant="Background" radii="400" aria-selected={exploringFeaturedRooms}>
|
<NavItem variant="Background" radii="400" aria-selected={exploringFeaturedRooms}>
|
||||||
<NavLink to={getExploreFeaturedPath()}>
|
<NavLink to={getExploreFeaturedPath()}>
|
||||||
<NavItemContent>
|
<NavItemContent>
|
||||||
|
|
@ -309,31 +325,13 @@ export function Explore() {
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<Box as="span" grow="Yes">
|
<Box as="span" grow="Yes">
|
||||||
<Text as="span" size="Inherit" truncate>
|
<Text as="span" size="Inherit" truncate>
|
||||||
Featured
|
Featured Rooms
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</NavItemContent>
|
</NavItemContent>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</NavItem>
|
</NavItem>
|
||||||
{directoryServer && (
|
|
||||||
<NavItem variant="Background" radii="400" aria-selected={exploringDirectory}>
|
|
||||||
<NavLink to={getExploreServerPath(directoryServer)}>
|
|
||||||
<NavItemContent>
|
|
||||||
<Box as="span" grow="Yes" alignItems="Center" gap="200">
|
|
||||||
<Avatar size="200" radii="400">
|
|
||||||
<Icon src={Icons.Search} size="100" filled={exploringDirectory} />
|
|
||||||
</Avatar>
|
|
||||||
<Box as="span" grow="Yes">
|
|
||||||
<Text as="span" size="Inherit" truncate>
|
|
||||||
Public Room Directory
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</NavItemContent>
|
|
||||||
</NavLink>
|
|
||||||
</NavItem>
|
|
||||||
)}
|
|
||||||
{userServer && (
|
{userServer && (
|
||||||
<ExploreServerNavItem
|
<ExploreServerNavItem
|
||||||
server={userServer}
|
server={userServer}
|
||||||
|
|
@ -341,40 +339,32 @@ export function Explore() {
|
||||||
icon={Icons.Home}
|
icon={Icons.Home}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{exploringUnlistedServer && selectedServer !== undefined && (
|
{featuredServers.map((server) => (
|
||||||
<ExploreServerNavItem
|
|
||||||
server={selectedServer}
|
|
||||||
selected
|
|
||||||
icon={Icons.Eye}
|
|
||||||
action={{
|
|
||||||
alwaysVisible: true,
|
|
||||||
icon: Icons.Bookmark,
|
|
||||||
onClick: () => viewServerCallback(selectedServer, true),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</NavCategory>
|
|
||||||
<NavCategory>
|
|
||||||
<NavCategoryHeader>
|
|
||||||
<Text size="O400" style={{ paddingLeft: config.space.S200 }}>
|
|
||||||
Bookmarks
|
|
||||||
</Text>
|
|
||||||
</NavCategoryHeader>
|
|
||||||
{bookmarkedServers.map((server) => (
|
|
||||||
<ExploreServerNavItem
|
<ExploreServerNavItem
|
||||||
key={server}
|
key={server}
|
||||||
server={server}
|
server={server}
|
||||||
selected={server === selectedServer}
|
selected={server === selectedServer}
|
||||||
icon={Icons.Server}
|
icon={Icons.Server}
|
||||||
action={{
|
|
||||||
alwaysVisible: false,
|
|
||||||
icon: Icons.Minus,
|
|
||||||
onClick: () => removeServerBookmarkCallback(server),
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<ExploreServerPrompt
|
</NavCategory>
|
||||||
onSubmit={viewServerCallback}
|
<NavCategory>
|
||||||
|
<NavCategoryHeader>
|
||||||
|
<Text size="O400" style={{ paddingLeft: config.space.S200 }}>
|
||||||
|
Servers
|
||||||
|
</Text>
|
||||||
|
</NavCategoryHeader>
|
||||||
|
{exploreServers.map((server) => (
|
||||||
|
<ExploreServerNavItem
|
||||||
|
key={server}
|
||||||
|
server={server}
|
||||||
|
selected={server === selectedServer}
|
||||||
|
onRemove={() => removeServerCallback(server)}
|
||||||
|
icon={Icons.Server}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
<AddExploreServerPrompt
|
||||||
|
onSubmit={addServerCallback}
|
||||||
header={<Text size="H4">Add Server</Text>}
|
header={<Text size="H4">Add Server</Text>}
|
||||||
>
|
>
|
||||||
<Box as="span" grow="Yes" alignItems="Center" gap="200">
|
<Box as="span" grow="Yes" alignItems="Center" gap="200">
|
||||||
|
|
@ -387,7 +377,7 @@ export function Explore() {
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</ExploreServerPrompt>
|
</AddExploreServerPrompt>
|
||||||
</NavCategory>
|
</NavCategory>
|
||||||
</Box>
|
</Box>
|
||||||
</PageNavContent>
|
</PageNavContent>
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
import React, { useCallback } from 'react';
|
import React from 'react';
|
||||||
import { Box, Button, color, Icon, IconButton, Icons, Scroll, Spinner, Text } from 'folds';
|
import { Box, Icon, IconButton, Icons, Scroll, Text } from 'folds';
|
||||||
import { useAtomValue } from 'jotai';
|
import { useAtomValue } from 'jotai';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import { useClientConfig } from '../../../hooks/useClientConfig';
|
import { useClientConfig } from '../../../hooks/useClientConfig';
|
||||||
import { RoomCard, CardGrid, CardName, CardBase } from '../../../components/room-card';
|
import { RoomCard, RoomCardGrid } from '../../../components/room-card';
|
||||||
import { allRoomsAtom } from '../../../state/room-list/roomList';
|
import { allRoomsAtom } from '../../../state/room-list/roomList';
|
||||||
import { RoomSummaryLoader } from '../../../components/RoomSummaryLoader';
|
import { RoomSummaryLoader } from '../../../components/RoomSummaryLoader';
|
||||||
import {
|
import {
|
||||||
|
|
@ -20,69 +18,13 @@ import * as css from './style.css';
|
||||||
import { useRoomNavigate } from '../../../hooks/useRoomNavigate';
|
import { useRoomNavigate } from '../../../hooks/useRoomNavigate';
|
||||||
import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize';
|
import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize';
|
||||||
import { BackRouteHandler } from '../../../components/BackRouteHandler';
|
import { BackRouteHandler } from '../../../components/BackRouteHandler';
|
||||||
import { useMatrixClient } from '../../../hooks/useMatrixClient';
|
|
||||||
import { millify } from '../../../plugins/millify';
|
|
||||||
import { getExploreServerPath } from '../../pathUtils';
|
|
||||||
|
|
||||||
type ServerCardProps = {
|
|
||||||
serverName: string;
|
|
||||||
onExplore: () => unknown;
|
|
||||||
};
|
|
||||||
|
|
||||||
function ServerCard({ serverName, onExplore }: ServerCardProps) {
|
|
||||||
const mx = useMatrixClient();
|
|
||||||
|
|
||||||
const fetchPublicRooms = useCallback(
|
|
||||||
() => mx.publicRooms({ server: serverName }),
|
|
||||||
[mx, serverName]
|
|
||||||
);
|
|
||||||
|
|
||||||
const { data, isLoading, isError } = useQuery({
|
|
||||||
queryKey: [serverName, `publicRooms`],
|
|
||||||
queryFn: fetchPublicRooms,
|
|
||||||
});
|
|
||||||
const publicRoomCount = data?.total_room_count_estimate;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<CardBase>
|
|
||||||
<CardName>{serverName}</CardName>
|
|
||||||
<Box gap="100" grow="Yes" style={isError ? { color: color.Critical.Main } : undefined}>
|
|
||||||
{isLoading ? (
|
|
||||||
<Spinner size="50" />
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<Icon size="50" src={isError ? Icons.Warning : Icons.Category} />
|
|
||||||
<Text size="T200">
|
|
||||||
{publicRoomCount === undefined
|
|
||||||
? 'Error loading rooms'
|
|
||||||
: `${millify(publicRoomCount)} Public Rooms`}
|
|
||||||
</Text>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
<Button onClick={onExplore} variant="Secondary" size="300">
|
|
||||||
<Text size="B300" truncate>
|
|
||||||
Explore Rooms
|
|
||||||
</Text>
|
|
||||||
</Button>
|
|
||||||
</CardBase>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function FeaturedRooms() {
|
export function FeaturedRooms() {
|
||||||
const { featuredCommunities } = useClientConfig();
|
const { featuredCommunities } = useClientConfig();
|
||||||
const { rooms, spaces, servers } = featuredCommunities ?? {};
|
const { rooms, spaces } = featuredCommunities ?? {};
|
||||||
const allRooms = useAtomValue(allRoomsAtom);
|
const allRooms = useAtomValue(allRoomsAtom);
|
||||||
const screenSize = useScreenSizeContext();
|
const screenSize = useScreenSizeContext();
|
||||||
const { navigateSpace, navigateRoom } = useRoomNavigate();
|
const { navigateSpace, navigateRoom } = useRoomNavigate();
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
const exploreServer = useCallback(
|
|
||||||
async (server: string) => {
|
|
||||||
navigate(getExploreServerPath(server));
|
|
||||||
},
|
|
||||||
[navigate]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
|
|
@ -107,28 +49,15 @@ export function FeaturedRooms() {
|
||||||
<PageHeroSection>
|
<PageHeroSection>
|
||||||
<PageHero
|
<PageHero
|
||||||
icon={<Icon size="600" src={Icons.Bulb} />}
|
icon={<Icon size="600" src={Icons.Bulb} />}
|
||||||
title="Featured"
|
title="Featured by Client"
|
||||||
subTitle="Find and explore public communities featured by your client provider."
|
subTitle="Find and explore public rooms and spaces featured by client provider."
|
||||||
/>
|
/>
|
||||||
</PageHeroSection>
|
</PageHeroSection>
|
||||||
<Box direction="Column" gap="700">
|
<Box direction="Column" gap="700">
|
||||||
{servers && servers.length > 0 && (
|
|
||||||
<Box direction="Column" gap="400">
|
|
||||||
<Text size="H4">Featured Servers</Text>
|
|
||||||
<CardGrid>
|
|
||||||
{servers.map((serverName) => (
|
|
||||||
<ServerCard
|
|
||||||
serverName={serverName}
|
|
||||||
onExplore={() => exploreServer(serverName)}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</CardGrid>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
{spaces && spaces.length > 0 && (
|
{spaces && spaces.length > 0 && (
|
||||||
<Box direction="Column" gap="400">
|
<Box direction="Column" gap="400">
|
||||||
<Text size="H4">Featured Spaces</Text>
|
<Text size="H4">Featured Spaces</Text>
|
||||||
<CardGrid>
|
<RoomCardGrid>
|
||||||
{spaces.map((roomIdOrAlias) => (
|
{spaces.map((roomIdOrAlias) => (
|
||||||
<RoomSummaryLoader key={roomIdOrAlias} roomIdOrAlias={roomIdOrAlias}>
|
<RoomSummaryLoader key={roomIdOrAlias} roomIdOrAlias={roomIdOrAlias}>
|
||||||
{(roomSummary) => (
|
{(roomSummary) => (
|
||||||
|
|
@ -151,13 +80,13 @@ export function FeaturedRooms() {
|
||||||
)}
|
)}
|
||||||
</RoomSummaryLoader>
|
</RoomSummaryLoader>
|
||||||
))}
|
))}
|
||||||
</CardGrid>
|
</RoomCardGrid>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
{rooms && rooms.length > 0 && (
|
{rooms && rooms.length > 0 && (
|
||||||
<Box direction="Column" gap="400">
|
<Box direction="Column" gap="400">
|
||||||
<Text size="H4">Featured Rooms</Text>
|
<Text size="H4">Featured Rooms</Text>
|
||||||
<CardGrid>
|
<RoomCardGrid>
|
||||||
{rooms.map((roomIdOrAlias) => (
|
{rooms.map((roomIdOrAlias) => (
|
||||||
<RoomSummaryLoader key={roomIdOrAlias} roomIdOrAlias={roomIdOrAlias}>
|
<RoomSummaryLoader key={roomIdOrAlias} roomIdOrAlias={roomIdOrAlias}>
|
||||||
{(roomSummary) => (
|
{(roomSummary) => (
|
||||||
|
|
@ -180,7 +109,7 @@ export function FeaturedRooms() {
|
||||||
)}
|
)}
|
||||||
</RoomSummaryLoader>
|
</RoomSummaryLoader>
|
||||||
))}
|
))}
|
||||||
</CardGrid>
|
</RoomCardGrid>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
{((spaces && spaces.length === 0 && rooms && rooms.length === 0) ||
|
{((spaces && spaces.length === 0 && rooms && rooms.length === 0) ||
|
||||||
|
|
@ -194,7 +123,7 @@ export function FeaturedRooms() {
|
||||||
>
|
>
|
||||||
<Icon size="400" src={Icons.Info} />
|
<Icon size="400" src={Icons.Info} />
|
||||||
<Text size="T300" align="Center">
|
<Text size="T300" align="Center">
|
||||||
No rooms or spaces are featured.
|
No rooms or spaces featured by client provider.
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,11 @@ import React, {
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import {
|
import {
|
||||||
Badge,
|
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Chip,
|
Chip,
|
||||||
Icon,
|
Icon,
|
||||||
IconButton,
|
IconButton,
|
||||||
IconSrc,
|
|
||||||
Icons,
|
Icons,
|
||||||
Input,
|
Input,
|
||||||
Line,
|
Line,
|
||||||
|
|
@ -39,7 +37,7 @@ import { MatrixClient, Method, RoomType } from 'matrix-js-sdk';
|
||||||
import { Page, PageContent, PageContentCenter, PageHeader } from '../../../components/page';
|
import { Page, PageContent, PageContentCenter, PageHeader } from '../../../components/page';
|
||||||
import { useMatrixClient } from '../../../hooks/useMatrixClient';
|
import { useMatrixClient } from '../../../hooks/useMatrixClient';
|
||||||
import { RoomTopicViewer } from '../../../components/room-topic-viewer';
|
import { RoomTopicViewer } from '../../../components/room-topic-viewer';
|
||||||
import { RoomCard, CardBase, CardGrid } from '../../../components/room-card';
|
import { RoomCard, RoomCardBase, RoomCardGrid } from '../../../components/room-card';
|
||||||
import { ExploreServerPathSearchParams } from '../../paths';
|
import { ExploreServerPathSearchParams } from '../../paths';
|
||||||
import { getExploreServerPath, withSearchParam } from '../../pathUtils';
|
import { getExploreServerPath, withSearchParam } from '../../pathUtils';
|
||||||
import * as css from './style.css';
|
import * as css from './style.css';
|
||||||
|
|
@ -50,8 +48,7 @@ import { stopPropagation } from '../../../utils/keyboard';
|
||||||
import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize';
|
import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize';
|
||||||
import { BackRouteHandler } from '../../../components/BackRouteHandler';
|
import { BackRouteHandler } from '../../../components/BackRouteHandler';
|
||||||
import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
|
import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
|
||||||
import { useBookmarkedServers } from '../../../hooks/useBookmarkedServers';
|
import { useExploreServers } from '../../../hooks/useExploreServers';
|
||||||
import { useDirectoryServer } from '../../../hooks/router/useExploreSelected';
|
|
||||||
|
|
||||||
const useServerSearchParams = (searchParams: URLSearchParams): ExploreServerPathSearchParams =>
|
const useServerSearchParams = (searchParams: URLSearchParams): ExploreServerPathSearchParams =>
|
||||||
useMemo(
|
useMemo(
|
||||||
|
|
@ -352,10 +349,10 @@ export function PublicRooms() {
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
const userId = mx.getUserId();
|
const userId = mx.getUserId();
|
||||||
const userServer = userId && getMxIdServer(userId);
|
const userServer = userId && getMxIdServer(userId);
|
||||||
const directoryServer = useDirectoryServer();
|
|
||||||
const allRooms = useAtomValue(allRoomsAtom);
|
const allRooms = useAtomValue(allRoomsAtom);
|
||||||
const { navigateSpace, navigateRoom } = useRoomNavigate();
|
const { navigateSpace, navigateRoom } = useRoomNavigate();
|
||||||
const screenSize = useScreenSizeContext();
|
const screenSize = useScreenSizeContext();
|
||||||
|
const [menuAnchor, setMenuAnchor] = useState<RectCords>();
|
||||||
|
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const serverSearchParams = useServerSearchParams(searchParams);
|
const serverSearchParams = useServerSearchParams(searchParams);
|
||||||
|
|
@ -364,18 +361,9 @@ export function PublicRooms() {
|
||||||
const searchInputRef = useRef<HTMLInputElement>(null);
|
const searchInputRef = useRef<HTMLInputElement>(null);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const roomTypeFilters = useRoomTypeFilters();
|
const roomTypeFilters = useRoomTypeFilters();
|
||||||
const [bookmarkedServers, addServerBookmark, removeServerBookmark] = useBookmarkedServers();
|
const [exploreServers, , removeServer] = useExploreServers();
|
||||||
const isUserHomeserver = server !== undefined && server === userServer;
|
const isUserAddedServer = server && exploreServers.includes(server);
|
||||||
const isBookmarkedServer = server !== undefined && bookmarkedServers.includes(server);
|
const isUserHomeServer = server && server === userServer;
|
||||||
const isDirectoryServer = server !== undefined && server === directoryServer;
|
|
||||||
let headerIcon: IconSrc;
|
|
||||||
if (isUserHomeserver) {
|
|
||||||
headerIcon = Icons.Home;
|
|
||||||
} else if (isDirectoryServer) {
|
|
||||||
headerIcon = Icons.Search;
|
|
||||||
} else {
|
|
||||||
headerIcon = Icons.Server;
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentLimit: number = useMemo(() => {
|
const currentLimit: number = useMemo(() => {
|
||||||
const limitParam = serverSearchParams.limit;
|
const limitParam = serverSearchParams.limit;
|
||||||
|
|
@ -488,17 +476,19 @@ export function PublicRooms() {
|
||||||
explore({ instance: instanceId, since: undefined });
|
explore({ instance: instanceId, since: undefined });
|
||||||
};
|
};
|
||||||
|
|
||||||
const [bookmarkActionState, handleBookmarkAction] = useAsyncCallback(
|
const handleOpenMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
|
||||||
useCallback(
|
setMenuAnchor(evt.currentTarget.getBoundingClientRect());
|
||||||
async (action: (server: string) => Promise<unknown>) => {
|
};
|
||||||
|
|
||||||
|
const [removeServerState, handleRemoveServer] = useAsyncCallback(
|
||||||
|
useCallback(async () => {
|
||||||
if (!server) return;
|
if (!server) return;
|
||||||
|
|
||||||
await action(server);
|
setMenuAnchor(undefined);
|
||||||
},
|
await removeServer(server);
|
||||||
[server]
|
}, [server, removeServer])
|
||||||
)
|
|
||||||
);
|
);
|
||||||
const bookmarkActionLoading = bookmarkActionState.status === AsyncStatus.Loading;
|
const isRemoving = removeServerState.status === AsyncStatus.Loading;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
|
|
@ -539,70 +529,76 @@ export function PublicRooms() {
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<Box grow="Yes" basis="Yes" justifyContent="Center" alignItems="Center" gap="200">
|
<Box grow="Yes" basis="Yes" justifyContent="Center" alignItems="Center" gap="200">
|
||||||
{screenSize !== ScreenSize.Mobile && <Icon size="400" src={headerIcon} />}
|
{screenSize !== ScreenSize.Mobile && <Icon size="400" src={isUserHomeServer ? Icons.Home : Icons.Server} />}
|
||||||
<Text size="H3" truncate>
|
<Text size="H3" truncate>
|
||||||
{isDirectoryServer ? 'Public Room Directory' : server}
|
{server}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box shrink="No" grow="Yes" basis="No" justifyContent="End">
|
<Box shrink="No" grow="Yes" basis="No" justifyContent="End">
|
||||||
{isDirectoryServer ? (
|
|
||||||
<TooltipProvider
|
<TooltipProvider
|
||||||
position="Bottom"
|
position="Bottom"
|
||||||
align="End"
|
align="End"
|
||||||
offset={4}
|
offset={4}
|
||||||
tooltip={
|
tooltip={
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<Text>provided by {directoryServer}</Text>
|
<Text>More Options</Text>
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{(triggerRef) => (
|
|
||||||
<Chip
|
|
||||||
as="a"
|
|
||||||
href={`https://${directoryServer}`}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer noopener"
|
|
||||||
variant="Secondary"
|
|
||||||
radii="Pill"
|
|
||||||
fill="Soft"
|
|
||||||
ref={screenSize === ScreenSize.Mobile ? triggerRef : undefined}
|
|
||||||
style={{ maxWidth: '100%' }}
|
|
||||||
before={<Icon src={Icons.External} size="50" />}
|
|
||||||
>
|
|
||||||
<Text size="T200" truncate>
|
|
||||||
provided by {directoryServer}
|
|
||||||
</Text>
|
|
||||||
</Chip>
|
|
||||||
)}
|
|
||||||
</TooltipProvider>
|
|
||||||
) : (
|
|
||||||
<TooltipProvider
|
|
||||||
position="Bottom"
|
|
||||||
align="End"
|
|
||||||
offset={4}
|
|
||||||
tooltip={
|
|
||||||
<Tooltip>
|
|
||||||
<Text>{isBookmarkedServer ? 'Remove Bookmark' : 'Add Bookmark'}</Text>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{(triggerRef) =>
|
{(triggerRef) =>
|
||||||
!isUserHomeserver && (
|
isUserAddedServer && (
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={() =>
|
onClick={handleOpenMenu}
|
||||||
handleBookmarkAction(
|
|
||||||
isBookmarkedServer ? removeServerBookmark : addServerBookmark
|
|
||||||
)
|
|
||||||
}
|
|
||||||
ref={triggerRef}
|
ref={triggerRef}
|
||||||
disabled={bookmarkActionLoading}
|
aria-pressed={!!menuAnchor}
|
||||||
>
|
>
|
||||||
<Icon size="400" src={Icons.Bookmark} filled={isBookmarkedServer} />
|
<Icon size="400" src={Icons.VerticalDots} filled={!!menuAnchor} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
)}
|
<PopOut
|
||||||
|
anchor={menuAnchor}
|
||||||
|
position="Bottom"
|
||||||
|
align="End"
|
||||||
|
content={
|
||||||
|
<FocusTrap
|
||||||
|
focusTrapOptions={{
|
||||||
|
initialFocus: false,
|
||||||
|
returnFocusOnDeactivate: false,
|
||||||
|
onDeactivate: () => setMenuAnchor(undefined),
|
||||||
|
clickOutsideDeactivates: true,
|
||||||
|
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
||||||
|
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Menu style={{ maxWidth: toRem(160), width: '100vw' }}>
|
||||||
|
<Box direction="Column" gap="100" style={{ padding: config.space.S100 }}>
|
||||||
|
<MenuItem
|
||||||
|
onClick={handleRemoveServer}
|
||||||
|
variant="Critical"
|
||||||
|
fill="None"
|
||||||
|
size="300"
|
||||||
|
after={
|
||||||
|
isRemoving ? (
|
||||||
|
<Spinner fill="Solid" variant="Secondary" size="200" />
|
||||||
|
) : (
|
||||||
|
<Icon size="100" src={Icons.Delete} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
radii="300"
|
||||||
|
disabled={isRemoving}
|
||||||
|
>
|
||||||
|
<Text style={{ flexGrow: 1 }} as="span" size="T300" truncate>
|
||||||
|
Remove Server
|
||||||
|
</Text>
|
||||||
|
</MenuItem>
|
||||||
|
</Box>
|
||||||
|
</Menu>
|
||||||
|
</FocusTrap>
|
||||||
|
}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
@ -664,11 +660,11 @@ export function PublicRooms() {
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
<CardGrid>
|
<RoomCardGrid>
|
||||||
{[...Array(currentLimit).keys()].map((item) => (
|
{[...Array(currentLimit).keys()].map((item) => (
|
||||||
<CardBase key={item} style={{ minHeight: toRem(260) }} />
|
<RoomCardBase key={item} style={{ minHeight: toRem(260) }} />
|
||||||
))}
|
))}
|
||||||
</CardGrid>
|
</RoomCardGrid>
|
||||||
)}
|
)}
|
||||||
{error && (
|
{error && (
|
||||||
<Box direction="Column" className={css.PublicRoomsError} gap="200">
|
<Box direction="Column" className={css.PublicRoomsError} gap="200">
|
||||||
|
|
@ -679,7 +675,7 @@ export function PublicRooms() {
|
||||||
{data &&
|
{data &&
|
||||||
(data.chunk.length > 0 ? (
|
(data.chunk.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
<CardGrid>
|
<RoomCardGrid>
|
||||||
{data?.chunk.map((chunkRoom) => (
|
{data?.chunk.map((chunkRoom) => (
|
||||||
<RoomCard
|
<RoomCard
|
||||||
key={chunkRoom.room_id}
|
key={chunkRoom.room_id}
|
||||||
|
|
@ -704,7 +700,7 @@ export function PublicRooms() {
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</CardGrid>
|
</RoomCardGrid>
|
||||||
|
|
||||||
{(data.prev_batch || data.next_batch) && (
|
{(data.prev_batch || data.next_batch) && (
|
||||||
<Box justifyContent="Center" gap="200">
|
<Box justifyContent="Center" gap="200">
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ export enum AccountDataEvent {
|
||||||
IgnoredUserList = 'm.ignored_user_list',
|
IgnoredUserList = 'm.ignored_user_list',
|
||||||
|
|
||||||
CinnySpaces = 'in.cinny.spaces',
|
CinnySpaces = 'in.cinny.spaces',
|
||||||
CinnyBookmarkedServers = 'in.cinny.bookmarked_servers',
|
CinnyExplore = 'in.cinny.explore',
|
||||||
|
|
||||||
ElementRecentEmoji = 'io.element.recent_emoji',
|
ElementRecentEmoji = 'io.element.recent_emoji',
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue