pl-fe: cleanup, mostly
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
@ -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;
|
||||
|
||||
@ -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>
|
||||
);
|
||||
|
||||
@ -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>
|
||||
);
|
||||
|
||||
@ -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', {
|
||||
|
||||
@ -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}>
|
||||
|
||||
@ -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'
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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. */
|
||||
|
||||
@ -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. */
|
||||
|
||||
@ -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. */
|
||||
|
||||
@ -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>
|
||||
);
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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> }} />} /> */}
|
||||
|
||||
@ -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 />}
|
||||
|
||||
@ -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>
|
||||
);
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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} />
|
||||
|
||||
@ -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} />
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>;
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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 && (
|
||||
|
||||
@ -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 ? (
|
||||
|
||||
Reference in New Issue
Block a user