Add a page and config option for a room directory server

This commit is contained in:
Ginger 2025-09-06 13:24:50 -04:00
parent 3de1ce8f2f
commit 401d8ed930
No known key found for this signature in database
5 changed files with 113 additions and 37 deletions

View file

@ -9,7 +9,6 @@
"xmr.se" "xmr.se"
], ],
"allowCustomHomeservers": true, "allowCustomHomeservers": true,
"featuredCommunities": { "featuredCommunities": {
"openAsDefault": false, "openAsDefault": false,
"spaces": [ "spaces": [
@ -28,9 +27,14 @@
"#PrivSec.dev:arcticfoxes.net", "#PrivSec.dev:arcticfoxes.net",
"#disroot:aria-net.org" "#disroot:aria-net.org"
], ],
"servers": ["envs.net", "matrix.org", "monero.social", "mozilla.org"] "servers": [
"envs.net",
"matrix.org",
"monero.social",
"mozilla.org"
],
"directoryServer": "matrixrooms.info"
}, },
"hashRouter": { "hashRouter": {
"enabled": false, "enabled": false,
"basename": "/" "basename": "/"

View file

@ -1,5 +1,6 @@
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({
@ -26,3 +27,8 @@ export const useExploreServer = (): string | undefined => {
return server; return server;
}; };
export const useDirectoryServer = (): string | undefined => {
const { featuredCommunities } = useClientConfig();
return featuredCommunities?.directoryServer;
};

View file

@ -15,6 +15,7 @@ export type ClientConfig = {
spaces?: string[]; spaces?: string[];
rooms?: string[]; rooms?: string[];
servers?: string[]; servers?: string[];
directoryServer?: string;
}; };
hashRouter?: HashRouterConfig; hashRouter?: HashRouterConfig;

View file

@ -32,6 +32,7 @@ import {
} from '../../../components/nav'; } from '../../../components/nav';
import { getExploreFeaturedPath, getExploreServerPath } from '../../pathUtils'; import { getExploreFeaturedPath, getExploreServerPath } from '../../pathUtils';
import { import {
useDirectoryServer,
useExploreFeaturedRooms, useExploreFeaturedRooms,
useExploreServer, useExploreServer,
} from '../../../hooks/router/useExploreSelected'; } from '../../../hooks/router/useExploreSelected';
@ -250,28 +251,31 @@ export function Explore() {
useNavToActivePathMapper('explore'); useNavToActivePathMapper('explore');
const userId = mx.getUserId(); const userId = mx.getUserId();
const userServer = userId ? getMxIdServer(userId) : undefined; const userServer = userId ? getMxIdServer(userId) : undefined;
const directoryServer = useDirectoryServer();
const [bookmarkedServers, addServerBookmark, removeServerBookmark] = useBookmarkedServers(); const [bookmarkedServers, addServerBookmark, removeServerBookmark] = useBookmarkedServers();
const selectedServer = useExploreServer(); 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 ||
bookmarkedServers.includes(selectedServer) bookmarkedServers.includes(selectedServer)
), ),
[bookmarkedServers, selectedServer, userServer] [bookmarkedServers, selectedServer, userServer, exploringDirectory]
); );
const viewServerCallback = useCallback( const viewServerCallback = useCallback(
async (server: string, saveBookmark: boolean) => { async (server: string, saveBookmark: boolean) => {
if (saveBookmark && server !== userServer && selectedServer) { if (saveBookmark && server !== userServer && server !== directoryServer && selectedServer) {
await addServerBookmark(server); await addServerBookmark(server);
} }
navigate(getExploreServerPath(server)); navigate(getExploreServerPath(server));
}, },
[addServerBookmark, navigate, userServer, selectedServer] [addServerBookmark, navigate, userServer, directoryServer, selectedServer]
); );
const removeServerBookmarkCallback = useCallback( const removeServerBookmarkCallback = useCallback(
@ -312,6 +316,24 @@ export function Explore() {
</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}

View file

@ -9,11 +9,13 @@ 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,
@ -49,6 +51,7 @@ 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 { useBookmarkedServers } from '../../../hooks/useBookmarkedServers';
import { useDirectoryServer } from '../../../hooks/router/useExploreSelected';
const useServerSearchParams = (searchParams: URLSearchParams): ExploreServerPathSearchParams => const useServerSearchParams = (searchParams: URLSearchParams): ExploreServerPathSearchParams =>
useMemo( useMemo(
@ -349,6 +352,7 @@ 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();
@ -363,6 +367,15 @@ export function PublicRooms() {
const [bookmarkedServers, addServerBookmark, removeServerBookmark] = useBookmarkedServers(); const [bookmarkedServers, addServerBookmark, removeServerBookmark] = useBookmarkedServers();
const isUserHomeserver = server !== undefined && server === userServer; const isUserHomeserver = server !== undefined && server === userServer;
const isBookmarkedServer = server !== undefined && bookmarkedServers.includes(server); const isBookmarkedServer = server !== undefined && bookmarkedServers.includes(server);
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;
@ -526,14 +539,43 @@ 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 && ( {screenSize !== ScreenSize.Mobile && <Icon size="400" src={headerIcon} />}
<Icon size="400" src={isUserHomeserver ? Icons.Home : Icons.Server} />
)}
<Text size="H3" truncate> <Text size="H3" truncate>
{server} {isDirectoryServer ? 'Public Room Directory' : 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
position="Bottom"
align="End"
offset={4}
tooltip={
<Tooltip>
<Text>provided by {directoryServer}</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 <TooltipProvider
position="Bottom" position="Bottom"
align="End" align="End"
@ -560,6 +602,7 @@ export function PublicRooms() {
) )
} }
</TooltipProvider> </TooltipProvider>
)}
</Box> </Box>
</> </>
)} )}