pl-fe: cleanup, mostly

Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
nicole mikołajczyk
2025-12-24 23:39:01 +01:00
parent ea0aa7c506
commit f126ef7ff4
24 changed files with 109 additions and 102 deletions

View File

@ -1,4 +1,4 @@
import { useNavigate, type LinkProps } from '@tanstack/react-router';
import { useNavigate, type LinkOptions } from '@tanstack/react-router';
import clsx from 'clsx';
import React, { useEffect, useRef } from 'react';
@ -26,11 +26,7 @@ type MenuItem = {
items?: Menu;
onSelectFile?: (files: FileList) => void;
accept?: string;
} & ({
to: LinkProps['to'];
params?: LinkProps['params'];
search?: LinkProps['search'];
} | { to?: undefined });
} & (LinkOptions | { to?: undefined });
interface IDropdownMenuItem {
index: number;

View File

@ -1,6 +1,6 @@
/* eslint-disable jsx-a11y/interactive-supports-focus */
import { useInfiniteQuery } from '@tanstack/react-query';
import { Link, type LinkProps } from '@tanstack/react-router';
import { Link, type LinkOptions } from '@tanstack/react-router';
import clsx from 'clsx';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { FormattedMessage } from 'react-intl';
@ -29,14 +29,14 @@ import sourceCode from 'pl-fe/utils/code';
import type { Account as AccountEntity } from 'pl-api';
interface IDropdownNavigationLink extends Partial<Pick<LinkProps, 'to' | 'params'>> {
interface IDropdownNavigationLink extends Partial<LinkOptions> {
href?: string;
icon: string;
text: string | JSX.Element;
onClick: React.EventHandler<React.MouseEvent>;
}
const DropdownNavigationLink: React.FC<IDropdownNavigationLink> = React.memo(({ href, to, params, icon, text, onClick }) => {
const DropdownNavigationLink: React.FC<IDropdownNavigationLink> = React.memo(({ href, to, icon, text, onClick, ...rest }) => {
const body = (
<>
<div className='⁂-dropdown-navigation__link__icon'>
@ -49,7 +49,7 @@ const DropdownNavigationLink: React.FC<IDropdownNavigationLink> = React.memo(({
if (to) {
return (
<Link className='⁂-dropdown-navigation__link' to={to} params={params} onClick={onClick}>
<Link className='⁂-dropdown-navigation__link' to={to} {...rest} onClick={onClick}>
{body}
</Link>
);

View File

@ -1,4 +1,4 @@
import { Link } from '@tanstack/react-router';
import { Link, type LinkOptions } from '@tanstack/react-router';
import clsx from 'clsx';
import React, { useState } from 'react';
@ -15,19 +15,18 @@ const List: React.FC<IList> = ({ children }) => (
<div className='⁂-list'>{children}</div>
);
interface IListItem {
type IListItem = {
className?: string;
label: React.ReactNode;
hint?: React.ReactNode;
to?: string;
href?: string;
onClick?(): void;
isSelected?: boolean;
children?: React.ReactNode;
size?: 'sm' | 'md';
}
} & (LinkOptions | {});
const ListItem: React.FC<IListItem> = ({ className, label, hint, children, to, href, onClick, isSelected, size = 'md' }) => {
const ListItem: React.FC<IListItem> = ({ className, label, hint, children, href, onClick, isSelected, size = 'md', ...rest }) => {
const [domId] = useState(`list-group-${crypto.randomUUID()}`);
const onKeyDown = (e: React.KeyboardEvent) => {
@ -36,7 +35,7 @@ const ListItem: React.FC<IListItem> = ({ className, label, hint, children, to, h
}
};
const LabelComp = to || href || onClick ? 'span' : 'label';
const LabelComp = 'to' in rest || href || onClick ? 'span' : 'label';
const renderChildren = React.useCallback(() => React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
@ -72,7 +71,7 @@ const ListItem: React.FC<IListItem> = ({ className, label, hint, children, to, h
) : null}
</div>
{(to || href || onClick) ? (
{('to' in rest || href || onClick) ? (
<HStack space={1} alignItems='center' className='⁂-list-item__body'>
{children}
@ -80,12 +79,12 @@ const ListItem: React.FC<IListItem> = ({ className, label, hint, children, to, h
</HStack>
) : null}
{typeof to === 'undefined' && typeof onClick === 'undefined' ? renderChildren() : null}
{!('to' in rest) && typeof onClick === 'undefined' ? renderChildren() : null}
</>
);
if (to) return (
<Link className={classNames} to={to}>
if ('to' in rest) return (
<Link className={classNames} {...rest}>
{body}
</Link>
);

View File

@ -1,4 +1,4 @@
import { Link } from '@tanstack/react-router';
import { Link, type LinkOptions } from '@tanstack/react-router';
import clsx from 'clsx';
import React from 'react';
import { FormattedMessage } from 'react-intl';
@ -7,17 +7,15 @@ import HStack from 'pl-fe/components/ui/hstack';
import Icon from 'pl-fe/components/ui/icon';
import Text from 'pl-fe/components/ui/text';
interface IPendingItemsRow {
/** Path to navigate the user when clicked. */
to: string;
interface IPendingItemsRow extends LinkOptions {
/** Number of pending items. */
count: number;
/** Size of the icon. */
size?: 'md' | 'lg';
}
const PendingItemsRow: React.FC<IPendingItemsRow> = ({ to, count, size = 'md' }) => (
<Link to={to} className='group' data-testid='pending-items-row'>
const PendingItemsRow: React.FC<IPendingItemsRow> = ({ count, size = 'md', ...props }) => (
<Link {...props} className='group' data-testid='pending-items-row'>
<HStack alignItems='center' justifyContent='between'>
<HStack alignItems='center' space={2}>
<div className={clsx('rounded-full bg-primary-200 text-primary-500 dark:bg-primary-800 dark:text-primary-200', {

View File

@ -15,13 +15,13 @@ const RadioGroup = ({ onChange, children }: IRadioGroup) => {
return <List>{childrenWithProps}</List>;
};
interface IRadioItem extends IListItem {
type IRadioItem = IListItem & {
label: React.ReactNode;
hint?: React.ReactNode;
value: string;
checked: boolean;
onChange?: React.ChangeEventHandler;
}
};
const RadioItem: React.FC<IRadioItem> = ({ label, hint, checked = false, onChange, value, ...props }) => (
<ListItem label={label} hint={hint} {...props}>

View File

@ -1,11 +1,11 @@
import { Link, type LinkProps } from '@tanstack/react-router';
import { Link, type LinkOptions } from '@tanstack/react-router';
import React from 'react';
import { useSettings } from 'pl-fe/stores/settings';
import Icon from './ui/icon';
interface ISidebarNavigationLink extends Partial<Pick<LinkProps, 'to' | 'params'>> {
interface ISidebarNavigationLink extends Partial<LinkOptions> {
/** Notification count, if any. */
count?: number;
/** Optional max to cap count (ie: N+) */
@ -22,7 +22,7 @@ interface ISidebarNavigationLink extends Partial<Pick<LinkProps, 'to' | 'params'
/** Desktop sidebar navigation link. */
const SidebarNavigationLink = React.memo(React.forwardRef((props: ISidebarNavigationLink, ref: React.ForwardedRef<HTMLAnchorElement>): JSX.Element => {
const { icon, activeIcon, text, to, params, count, countMax, onClick } = props;
const { icon, activeIcon, text, to, count, countMax, onClick, ...rest } = props;
const isActive = location.pathname === to;
const { demetricator } = useSettings();
@ -40,10 +40,10 @@ const SidebarNavigationLink = React.memo(React.forwardRef((props: ISidebarNaviga
activeOptions={{ exact: true }}
activeProps={{ className: '⁂-sidebar-navigation-link--active' }}
to={to}
params={params}
ref={ref}
onClick={handleClick}
className='⁂-sidebar-navigation-link'
{...rest}
>
<span
className='⁂-sidebar-navigation-link__icon'

View File

@ -1,29 +1,28 @@
import { Link, useMatchRoute } from '@tanstack/react-router';
import { Link, useMatchRoute, type LinkOptions } from '@tanstack/react-router';
import React from 'react';
import IconWithCounter from 'pl-fe/components/icon-with-counter';
import Icon from 'pl-fe/components/ui/icon';
import { useSettings } from 'pl-fe/stores/settings';
interface IThumbNavigationLink {
interface IThumbNavigationLink extends LinkOptions {
count?: number;
countMax?: number;
src: string;
activeSrc?: string;
text: string;
to: string;
exact?: boolean;
}
const ThumbNavigationLink: React.FC<IThumbNavigationLink> = ({ count, countMax, src, activeSrc, text, to, exact }): JSX.Element => {
const ThumbNavigationLink: React.FC<IThumbNavigationLink> = ({ count, countMax, src, activeSrc, text, exact, ...props }): JSX.Element => {
const { demetricator } = useSettings();
const matchRoute = useMatchRoute();
const icon = (activeSrc && matchRoute({ to }) !== null && activeSrc) || src;
const icon = (activeSrc && matchRoute({ to: props.to, params: props.params, search: props.search }) !== null && activeSrc) || src;
return (
<Link to={to} activeOptions={{ exact }} className='⁂-thumb-navigation__item' activeProps={{ className: '⁂-thumb-navigation__item--active' }} title={text}>
<Link {...props} activeOptions={{ exact }} className='⁂-thumb-navigation__item' activeProps={{ className: '⁂-thumb-navigation__item--active' }} title={text}>
{!demetricator && count !== undefined ? (
<IconWithCounter
src={icon}

View File

@ -1,4 +1,4 @@
import { Link, type LinkProps } from '@tanstack/react-router';
import { Link, type LinkOptions } from '@tanstack/react-router';
import clsx from 'clsx';
import React from 'react';
@ -11,7 +11,7 @@ import type { ButtonSizes, ButtonThemes } from './useButtonStyles';
type IButton = Pick<
React.ComponentProps<'button'>,
'children' | 'className' | 'disabled' | 'onClick' | 'onMouseDown' | 'onKeyDown' | 'onKeyPress' | 'title' | 'type'
> & (Pick<LinkProps, 'to' | 'params' | 'search'> | { to?: undefined }) & {
> & (LinkOptions | { to?: undefined }) & {
/** Whether this button expands the width of its container. */
block?: boolean;
/** URL to an SVG icon to render inside the button. */

View File

@ -1,4 +1,4 @@
import { LinkProps, useNavigate, useRouter } from '@tanstack/react-router';
import { type LinkOptions, useNavigate, useRouter } from '@tanstack/react-router';
import clsx from 'clsx';
import throttle from 'lodash/throttle';
import React, { useCallback, useEffect, useState } from 'react';
@ -43,8 +43,9 @@ const ColumnHeader: React.FC<IColumnHeader> = ({ label, backHref, backParams, cl
interface IColumn {
/** Route the back button goes to. */
backHref?: LinkProps['to'];
backParams?: LinkProps['params'];
backHref?: LinkOptions['to'];
backParams?: LinkOptions['params'];
backSearch?: LinkOptions['search'];
/** Column title text. */
label?: string;
/** Whether this column should have a transparent background. */

View File

@ -11,7 +11,7 @@ import React from 'react';
import Counter from './counter';
import type { LinkProps } from '@tanstack/react-router';
import type { LinkOptions } from '@tanstack/react-router';
import './tabs.css';
@ -117,12 +117,7 @@ type Item = {
count?: number;
/** Unique name for this tab. */
name: string;
} & ({
/** Route to visit when the tab is selected. */
to: LinkProps['to'];
params?: LinkProps['params'];
search?: LinkProps['search'];
} | { to?: undefined });
} & (LinkOptions | { to?: undefined });
interface ITabs {
/** Array of structured tab items. */

View File

@ -1,4 +1,4 @@
import { Link } from '@tanstack/react-router';
import { Link, type LinkOptions } from '@tanstack/react-router';
import clsx from 'clsx';
import React from 'react';
import { FormattedNumber } from 'react-intl';
@ -27,24 +27,23 @@ const percIncrease = (a: number, b: number) => {
return percent;
};
interface ICounter {
type ICounter = {
measure: AdminMeasureKey;
startAt: string;
endAt: string;
label: JSX.Element | string;
to?: string;
params?: AdminGetMeasuresParams;
target?: string;
}
} & (LinkOptions | {});
const Counter: React.FC<ICounter> = ({
measure,
startAt,
endAt,
label,
to,
params,
target,
...rest
}) => {
const { data } = useMeasures([measure], startAt, endAt, params);
@ -89,9 +88,9 @@ const Counter: React.FC<ICounter> = ({
const className = 'relative flex flex-col rounded bg-gray-200 font-medium dark:bg-gray-800';
if (to) {
if ('to' in rest) {
return (
<Link to={to} className={clsx(className, 'transition-transform hover:-translate-y-1')} target={target}>
<Link {...rest} className={clsx(className, 'transition-transform hover:-translate-y-1')} target={target}>
{inner}
</Link>
);

View File

@ -1,29 +1,27 @@
import { Link } from '@tanstack/react-router';
import { Link, type LinkOptions } from '@tanstack/react-router';
import React from 'react';
import { FormattedNumber } from 'react-intl';
import Text from 'pl-fe/components/ui/text';
import { isNumber } from 'pl-fe/utils/numbers';
interface IDashCounter {
type IDashCounter = {
count: number | undefined;
label: React.ReactNode;
to?: string;
percent?: boolean;
}
} & (LinkOptions | {});
/** Displays a (potentially clickable) dashboard statistic. */
const DashCounter: React.FC<IDashCounter> = ({ count, label, to = '#', percent = false }) => {
const DashCounter: React.FC<IDashCounter> = ({ count, label, percent = false, ...rest }) => {
if (!isNumber(count)) {
return null;
}
return (
<Link
className='flex cursor-pointer flex-col items-center space-y-2 rounded bg-gray-200 p-4 transition-transform hover:-translate-y-1 dark:bg-gray-800'
to={to}
>
const className = 'flex cursor-pointer flex-col items-center space-y-2 rounded bg-gray-200 p-4 transition-transform hover:-translate-y-1 dark:bg-gray-800';
const body = (
<>
<Text align='center' size='2xl' weight='medium'>
<FormattedNumber
value={count}
@ -35,6 +33,16 @@ const DashCounter: React.FC<IDashCounter> = ({ count, label, to = '#', percent =
<Text align='center'>
{label}
</Text>
</>
);
if (!('to' in rest)) {
return <span className={className}>{body}</span>;
}
return (
<Link className={className} {...rest}>
{body}
</Link>
);
};

View File

@ -100,7 +100,8 @@ const Dashboard: React.FC = () => {
measure='resolved_reports'
startAt={monthAgo}
endAt={today}
to='/pl-fe/admin/reports?resolved=true'
to='/pl-fe/admin/reports'
search={{ resolved: true }}
label={<FormattedMessage id='admin.counters.resolved_reports' defaultMessage='reports resolved' />}
/>
</>
@ -115,7 +116,7 @@ const Dashboard: React.FC = () => {
label={<FormattedMessage id='admin.dashcounters.domain_count_label' defaultMessage='peers' />}
/>
<List>
<ListItem size='sm' to='/pl-fe/admin/reports?resolved=false' label={<FormattedMessage id='admin.links.pending_reports' defaultMessage='{count, plural, one {{formattedCount} pending report} other {{formattedCount} pending reports}}' values={{ count: pendingReportsCount, formattedCount: <strong><FormattedNumber value={pendingReportsCount} /></strong> }} />} />
<ListItem size='sm' to='/pl-fe/admin/reports' search={{ resolved: false }} label={<FormattedMessage id='admin.links.pending_reports' defaultMessage='{count, plural, one {{formattedCount} pending report} other {{formattedCount} pending reports}}' values={{ count: pendingReportsCount, formattedCount: <strong><FormattedNumber value={pendingReportsCount} /></strong> }} />} />
<ListItem size='sm' to='/pl-fe/admin/users' label={<FormattedMessage id='admin.links.pending_users' defaultMessage='{count, plural, one {{formattedCount} pending user} other {{formattedCount} pending users}}' values={{ count: awaitingApprovalCount, formattedCount: <strong><FormattedNumber value={awaitingApprovalCount} /></strong> }} />} />
{/* <ListItem size='sm' to='/pl-fe/admin' label={<FormattedMessage id='admin.links.pending_tags' defaultMessage='{count} pending tags' values={{ count: <strong>0</strong> }} />} />
<ListItem size='sm' to='/pl-fe/admin' label={<FormattedMessage id='admin.links.pending_appeals' defaultMessage='{count} pending appeals' values={{ count: <strong>0</strong> }} />} /> */}

View File

@ -1,4 +1,4 @@
import { Link } from '@tanstack/react-router';
import { Link, type LinkProps } from '@tanstack/react-router';
import React, { useRef } from 'react';
import Avatar from 'pl-fe/components/ui/avatar';
@ -14,13 +14,13 @@ import Chat from '../chat';
import ChatPaneHeader from './chat-pane-header';
import ChatSettings from './chat-settings';
const LinkWrapper = ({ enabled, to, children }: { enabled: boolean; to: string; children: React.ReactNode }): JSX.Element => {
const LinkWrapper = ({ enabled, children, ...rest }: LinkProps & { enabled: boolean; children: React.ReactNode }): JSX.Element => {
if (!enabled) {
return <>{children}</>;
}
return (
<Link to={to}>
<Link {...rest}>
{children}
</Link>
);
@ -75,7 +75,7 @@ const ChatWindow = () => {
)}
<Stack alignItems='start'>
<LinkWrapper enabled={isOpen} to={`/@${chat.account.acct}`}>
<LinkWrapper enabled={isOpen} to='/@{$username}' params={{ username: chat.account.acct }}>
<div className='flex grow items-center space-x-1'>
<Text size='sm' weight='bold' truncate>{chat.account.display_name || `@${chat.account.acct}`}</Text>
{chat.account.verified && <VerificationBadge />}

View File

@ -1,4 +1,4 @@
import { Link } from '@tanstack/react-router';
import { Link, LinkOptions } from '@tanstack/react-router';
import clsx from 'clsx';
import React from 'react';
import { FormattedMessage } from 'react-intl';
@ -60,7 +60,7 @@ const StatusInteractionBar: React.FC<IStatusInteractionBar> = ({ status }): JSX.
const getQuotes = () => {
if (status.quotes_count) {
return (
<InteractionCounter count={status.quotes_count} to={`/@${status.account.acct}/posts/${status.id}/quotes`}>
<InteractionCounter count={status.quotes_count} to='/@{$username}/posts/$statusId/quotes' params={{ username: status.account.acct, id: status.id }}>
<FormattedMessage
id='status.interactions.quotes'
defaultMessage='{count, plural, one {Quote} other {Quotes}}'
@ -131,14 +131,13 @@ const StatusInteractionBar: React.FC<IStatusInteractionBar> = ({ status }): JSX.
);
};
interface IInteractionCounter {
type IInteractionCounter = {
count: number;
children: React.ReactNode;
onClick?: React.MouseEventHandler<HTMLButtonElement>;
to?: string;
}
} & (LinkOptions | {});
const InteractionCounter: React.FC<IInteractionCounter> = ({ count, children, onClick, to }) => {
const InteractionCounter: React.FC<IInteractionCounter> = ({ count, children, onClick, ...rest }) => {
const features = useFeatures();
const className = clsx({
@ -159,9 +158,9 @@ const InteractionCounter: React.FC<IInteractionCounter> = ({ count, children, on
</HStack>
);
if (to) {
if ('to' in rest) {
return (
<Link to={to} className={className}>
<Link className={className} {...rest}>
{body}
</Link>
);

View File

@ -1,4 +1,4 @@
import { Link } from '@tanstack/react-router';
import { Link, type LinkOptions } from '@tanstack/react-router';
import clsx from 'clsx';
import React, { useMemo } from 'react';
import { defineMessages, useIntl } from 'react-intl';
@ -30,7 +30,7 @@ interface IProfileDropdown {
type IMenuItem = {
text: string | React.ReactElement | null;
to?: string;
linkOptions?: LinkOptions;
toggle?: JSX.Element;
icon?: string;
action?: (event: React.MouseEvent) => void;
@ -63,7 +63,7 @@ const ProfileDropdown: React.FC<IProfileDropdown> = ({ account, children }) => {
const ProfileDropdownMenu = useMemo(() => {
const menu: IMenuItem[] = [];
menu.push({ text: renderAccount(account), to: `/@${account.acct}` });
menu.push({ text: renderAccount(account), linkOptions: { to: '/@{$username}', params: { username: account.acct } } });
otherAccounts.forEach((otherAccount?: AccountEntity) => {
if (otherAccount && otherAccount.id !== account.id) {
@ -80,13 +80,13 @@ const ProfileDropdown: React.FC<IProfileDropdown> = ({ account, children }) => {
menu.push({
text: intl.formatMessage(messages.add),
to: '/login/add',
linkOptions: { to: '/login/add' },
icon: require('@phosphor-icons/core/regular/plus.svg'),
});
menu.push({
text: intl.formatMessage(messages.logout, { acct: account.acct }),
to: '/logout',
linkOptions: { to: '/logout' },
action: handleLogOut,
icon: require('@phosphor-icons/core/regular/sign-out.svg'),
});
@ -142,10 +142,10 @@ const MenuItem: React.FC<MenuItemProps> = ({ className, menuItem }) => {
{menuItem.text}
</button>
);
} else if (menuItem.to) {
} else if (menuItem.linkOptions) {
return (
<Link
to={menuItem.to}
{...menuItem.linkOptions}
className={baseClassName}
>
{menuItem.text}

View File

@ -1181,6 +1181,12 @@ export const errorRoute = createRoute({
component: IntentionalError,
});
export const networkErrorRoute = createRoute({
getParentRoute: () => layouts.empty,
path: '/error/network',
component: React.lazy(() => Promise.reject(new TypeError('Failed to fetch dynamically imported module: TEST'))),
});
// Crypto donate
export const cryptoDonateRoute = createRoute({
getParentRoute: () => layouts.default,
@ -1285,6 +1291,7 @@ const routeTree = rootRoute.addChildren([
signupRoute,
serverInfoRoute,
errorRoute,
networkErrorRoute,
]),
layouts.event.addChildren([
eventInformationRoute,

View File

@ -97,7 +97,8 @@ const CirclesPage: React.FC = () => {
{circles.map((circle) => (
<ListItem
key={circle.id}
to={`/circles/${circle.id}`}
to='/circles/$circleId'
params={{ circleId: circle.id }}
label={
<HStack alignItems='center' space={2}>
<Icon src={require('@phosphor-icons/core/regular/list-bullets.svg')} size={20} />

View File

@ -106,7 +106,8 @@ const ListsPage: React.FC = () => {
{lists.map((list: any) => (
<ListItem
key={list.id}
to={`/list/${list.id}`}
to='/list/$listId'
params={{ listId: list.id }}
label={
<HStack alignItems='center' space={2}>
<Icon src={require('@phosphor-icons/core/regular/list-bullets.svg')} size={20} />

View File

@ -267,7 +267,8 @@ const ReportPage: React.FC = () => {
)}
<ListItem
label={<FormattedMessage id='admin.report.moderate' defaultMessage='Moderate account' />}
to={`/pl-fe/admin/accounts/${report.target_account_id}`}
to='/pl-fe/admin/accounts/$accountId'
params={{ accountId: report.target_account_id }}
/>
</List>
</Column>

View File

@ -1,4 +1,4 @@
import { Link } from '@tanstack/react-router';
import { Link, type LinkOptions } from '@tanstack/react-router';
import React from 'react';
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
@ -12,17 +12,16 @@ const messages = defineMessages({
heading: { id: 'column.developers', defaultMessage: 'Developers' },
});
interface IDashWidget {
to?: string;
interface IDashWidget extends Partial<LinkOptions> {
onClick?: React.MouseEventHandler<HTMLButtonElement>;
children: React.ReactNode;
}
const DashWidget: React.FC<IDashWidget> = ({ to, onClick, children }) => {
const DashWidget: React.FC<IDashWidget> = ({ to, onClick, children, ...rest }) => {
const className = 'bg-gray-200 dark:bg-gray-800 hover:bg-gray-300 dark:hover:bg-gray-800/75 p-4 rounded flex flex-col items-center justify-center space-y-2';
if (to) {
return <Link className={className} to={to}>{children}</Link>;
return <Link className={className} to={to} {...rest}>{children}</Link>;
} else {
return <button className={className} onClick={onClick}>{children}</button>;
}

View File

@ -43,7 +43,8 @@ const GroupMembers: React.FC = () => {
prepend={(pendingCount > 0) && (
<div className={clsx('py-3', { 'border-b border-gray-200 dark:border-gray-800': members.length })}>
<PendingItemsRow
to={`/groups/${group?.id}/manage/requests`}
to='/groups/$groupId/manage/requests'
params={{ groupId: group?.id! }}
count={pendingCount}
/>
</div>

View File

@ -80,7 +80,7 @@ const ManageGroup: React.FC = () => {
</CardHeader>
<List>
<ListItem label={intl.formatMessage(messages.editGroup)} to={`/groups/${group.id}/manage/edit`}>
<ListItem label={intl.formatMessage(messages.editGroup)} to='/groups/$groupId/manage/edit' params={{ groupId: group.id }}>
<span><Emojify text={group.display_name} emojis={group.emojis} /></span>
</ListItem>
</List>
@ -92,9 +92,9 @@ const ManageGroup: React.FC = () => {
</CardHeader>
<List>
<ListItem label={intl.formatMessage(messages.pendingRequests)} to={`/groups/${group.id}/manage/requests`} />
<ListItem label={intl.formatMessage(messages.pendingRequests)} to='/groups/$groupId/manage/requests' params={{ groupId: group.id }} />
<ListItem label={intl.formatMessage(messages.blockedMembers)} to={`/groups/${group.id}/manage/blocks`} />
<ListItem label={intl.formatMessage(messages.blockedMembers)} to='/groups/$groupId/manage/blocks' params={{ groupId: group.id }} />
</List>
{isOwner && (

View File

@ -96,7 +96,8 @@ const BookmarkFoldersPage: React.FC = () => {
<List>
<ListItem
to='/bookmarks/all'
to='/bookmarks/$folderId'
params={{ folderId: 'all' }}
label={
<HStack alignItems='center' space={2}>
<Icon src={require('@phosphor-icons/core/regular/bookmarks.svg')} size={20} />
@ -107,7 +108,8 @@ const BookmarkFoldersPage: React.FC = () => {
{bookmarkFolders?.map((folder) => (
<ListItem
key={folder.id}
to={`/bookmarks/${folder.id}`}
to='/bookmarks/$folderId'
params={{ folderId: folder.id }}
label={
<HStack alignItems='center' space={2}>
{folder.emoji ? (