Merge branch 'black' into 'main'

Redesign desktop layout

See merge request soapbox-pub/soapbox!3312
merge-requests/3313/head
Alex Gleason 2025-01-25 07:14:19 +00:00
commit 31c461b43f
66 zmienionych plików z 345 dodań i 518 usunięć

Wyświetl plik

@ -22,7 +22,7 @@ const AnnouncementsPanel = () => {
return (
<Widget title={<FormattedMessage id='announcements.title' defaultMessage='Announcements' />}>
<Card className='relative black:rounded-xl black:border black:border-gray-800' size='md' variant='rounded'>
<Card className='relative black:rounded-xl black:border black:border-gray-800' size='md'>
<ReactSwipeableViews animateHeight index={index} onChangeIndex={handleChangeIndex}>
{announcements!.map((announcement) => (
<Announcement

Wyświetl plik

@ -16,7 +16,7 @@ interface IBigCard {
const BigCard: React.FC<IBigCard> = ({ title, subtitle, children, onClose }) => {
return (
<Card variant='rounded' size='xl'>
<Card size='xl' rounded>
<CardBody className='relative'>
<div className='-mx-4 mb-4 border-b border-solid border-gray-200 pb-4 dark:border-gray-800 sm:-mx-10 sm:pb-10'>
<Stack space={2}>

Wyświetl plik

@ -1,6 +0,0 @@
/** Fullscreen gradient used as a backdrop to public pages. */
const LandingGradient: React.FC = () => (
<div className='fixed h-screen w-full bg-gradient-to-tr from-primary-50 via-white to-gradient-end/10 black:hidden dark:from-primary-900/50 dark:via-primary-900 dark:to-primary-800/50' />
);
export default LandingGradient;

Wyświetl plik

@ -1,12 +1,9 @@
import LandingGradient from 'soapbox/components/landing-gradient.tsx';
import Spinner from 'soapbox/components/ui/spinner.tsx';
/** Fullscreen loading indicator. */
const LoadingScreen: React.FC = () => {
return (
<div className='fixed h-screen w-screen'>
<LandingGradient />
<div className='d-screen fixed z-10 flex w-screen items-center justify-center'>
<div className='p-4'>
<Spinner size={40} withText={false} />

Wyświetl plik

@ -9,7 +9,7 @@ interface MissingIndicatorProps {
}
const MissingIndicator = ({ nested = false }: MissingIndicatorProps): JSX.Element => (
<Card variant={nested ? undefined : 'rounded'} size='lg'>
<Card rounded={!nested} size='lg'>
<CardBody>
<Stack space={2}>
<Text weight='medium' align='center' size='lg'>

Wyświetl plik

@ -3,8 +3,7 @@ import calendarIcon from '@tabler/icons/outline/calendar.svg';
import clsx from 'clsx';
import { useEffect, useState } from 'react';
import { useIntl, FormattedMessage } from 'react-intl';
import { useHistory } from 'react-router-dom';
import { Link, useHistory } from 'react-router-dom';
import { fetchRelationships } from 'soapbox/actions/accounts.ts';
import {
@ -14,15 +13,20 @@ import {
import { useAccount, usePatronUser } from 'soapbox/api/hooks/index.ts';
import Badge from 'soapbox/components/badge.tsx';
import Markup from 'soapbox/components/markup.tsx';
import StillImage from 'soapbox/components/still-image.tsx';
import Avatar from 'soapbox/components/ui/avatar.tsx';
import { Card, CardBody } from 'soapbox/components/ui/card.tsx';
import HStack from 'soapbox/components/ui/hstack.tsx';
import Icon from 'soapbox/components/ui/icon.tsx';
import Stack from 'soapbox/components/ui/stack.tsx';
import Text from 'soapbox/components/ui/text.tsx';
import VerificationBadge from 'soapbox/components/verification-badge.tsx';
import ActionButton from 'soapbox/features/ui/components/action-button.tsx';
import { UserPanel } from 'soapbox/features/ui/util/async-components.ts';
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
import { useSoapboxConfig } from 'soapbox/hooks/useSoapboxConfig.ts';
import { emojifyText } from 'soapbox/utils/emojify.tsx';
import { shortNumberFormat } from 'soapbox/utils/numbers.tsx';
import { showProfileHoverCard } from './hover-ref-wrapper.tsx';
import { dateFormatOptions } from './relative-timestamp.tsx';
@ -77,6 +81,7 @@ export const ProfileHoverCard: React.FC<IProfileHoverCard> = ({ visible = true }
const accountId: string | undefined = useAppSelector(state => state.profile_hover_card.accountId || undefined);
const { account } = useAccount(accountId, { withRelationship: true });
const { patronUser } = usePatronUser(account?.url);
const { displayFqn } = useSoapboxConfig();
const targetRef = useAppSelector(state => state.profile_hover_card.ref?.current);
const badges = getBadges(account, patronUser);
@ -118,14 +123,75 @@ export const ProfileHoverCard: React.FC<IProfileHoverCard> = ({ visible = true }
onMouseEnter={handleMouseEnter(dispatch)}
onMouseLeave={handleMouseLeave(dispatch)}
>
<Card variant='rounded' className='relative isolate overflow-hidden'>
<CardBody>
<Stack space={2}>
<UserPanel
accountId={account.id}
action={<ActionButton account={account} small />}
badges={badges}
/>
<Card className='relative isolate overflow-hidden' rounded slim>
<CardBody className='relative'>
<div className='relative h-24 overflow-hidden bg-gray-200'>
<StillImage src={account.header} />
</div>
<Stack space={2} className='-mt-12 px-3 pb-3'>
<HStack justifyContent='between'>
<Link to={`/@${account.acct}`} title={account.acct}>
<Avatar src={account.avatar} size={80} className='size-20 overflow-hidden bg-gray-50 ring-2 ring-white' />
</Link>
<div className='mt-2'>
<ActionButton account={account} small />
</div>
</HStack>
<Stack>
<Link to={`/@${account.acct}`}>
<HStack space={1} alignItems='center'>
<Text size='lg' weight='bold' truncate>
{emojifyText(account.display_name, account.emojis)}
</Text>
{account.verified && <VerificationBadge />}
{badges && badges.length > 0 && (
<HStack space={1} alignItems='center'>
{badges}
</HStack>
)}
</HStack>
</Link>
<HStack>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<Text size='sm' theme='muted' direction='ltr' truncate>
@{displayFqn ? account.fqn : account.acct}
</Text>
</HStack>
</Stack>
<HStack alignItems='center' space={3}>
{account.followers_count >= 0 && (
<Link to={`/@${account.acct}/followers`} title={intl.formatNumber(account.followers_count)}>
<HStack alignItems='center' space={1}>
<Text theme='primary' weight='bold' size='sm'>
{shortNumberFormat(account.followers_count)}
</Text>
<Text weight='bold' size='sm'>
<FormattedMessage id='account.followers' defaultMessage='Followers' />
</Text>
</HStack>
</Link>
)}
{account.following_count >= 0 && (
<Link to={`/@${account.acct}/following`} title={intl.formatNumber(account.following_count)}>
<HStack alignItems='center' space={1}>
<Text theme='primary' weight='bold' size='sm'>
{shortNumberFormat(account.following_count)}
</Text>
<Text weight='bold' size='sm'>
<FormattedMessage id='account.follows' defaultMessage='Following' />
</Text>
</HStack>
</Link>
)}
</HStack>
{account.local ? (
<HStack alignItems='center' space={0.5}>

Wyświetl plik

@ -36,8 +36,6 @@ interface IPureStatusList extends Omit<IScrollableList, 'onLoadMore' | 'children
emptyMessage: React.ReactNode;
/** ID of the timeline in Redux. */
timelineId?: string;
/** Whether to display a gap or border between statuses in the list. */
divideType?: 'space' | 'border';
/** Whether to display ads. */
showAds?: boolean;
/** Whether to show group information. */
@ -51,7 +49,6 @@ const PureStatusList: React.FC<IPureStatusList> = ({
statuses,
lastStatusId,
featuredStatuses,
divideType = 'border',
onLoadMore,
timelineId,
isLoading,
@ -133,7 +130,6 @@ const PureStatusList: React.FC<IPureStatusList> = ({
onMoveUp={handleMoveUp}
onMoveDown={handleMoveDown}
showGroup={showGroup}
variant={divideType === 'border' ? 'slim' : 'rounded'}
/>
);
};
@ -161,7 +157,6 @@ const PureStatusList: React.FC<IPureStatusList> = ({
onMoveUp={handleMoveUp}
onMoveDown={handleMoveDown}
showGroup={showGroup}
variant={divideType === 'border' ? 'slim' : 'default'} // shouldn't "default" be changed to "rounded" ?
/>
));
};
@ -236,15 +231,10 @@ const PureStatusList: React.FC<IPureStatusList> = ({
isLoading={isLoading}
showLoading={isLoading && statuses.length === 0}
onLoadMore={handleLoadOlder}
placeholderComponent={() => <PlaceholderStatus variant={divideType === 'border' ? 'slim' : 'rounded'} />}
placeholderComponent={() => <PlaceholderStatus />}
placeholderCount={20}
ref={node}
listClassName={clsx('divide-y divide-solid divide-gray-200 dark:divide-gray-800', {
'divide-none': divideType !== 'border',
}, className)}
itemClassName={clsx({
'pb-3': divideType !== 'border',
})}
listClassName={clsx('divide-y divide-solid divide-gray-200 dark:divide-gray-800', className)}
{...other}
>
{renderScrollableContent()}

Wyświetl plik

@ -57,7 +57,6 @@ export interface IPureStatus {
featured?: boolean;
hideActionBar?: boolean;
hoverable?: boolean;
variant?: 'default' | 'rounded' | 'slim';
showGroup?: boolean;
accountAction?: React.ReactElement;
}
@ -80,7 +79,6 @@ const PureStatus: React.FC<IPureStatus> = (props) => {
featured,
unread,
hideActionBar,
variant = 'rounded',
showGroup = true,
} = props;
@ -420,9 +418,9 @@ const PureStatus: React.FC<IPureStatus> = (props) => {
role='link'
>
<Card
variant={variant}
className={clsx('status--wrapper space-y-4', {
'py-6 sm:p-5': variant === 'rounded', muted, read: unread === false,
muted,
read: unread === false,
})}
data-id={status.id}
>

Wyświetl plik

@ -87,7 +87,7 @@ const ScrollTopButton: React.FC<IScrollTopButton> = ({
}
return (
<div className='fixed left-1/2 top-20 z-50 -translate-x-1/2'>
<div className='fixed left-1/2 top-28 z-50 -translate-x-1/2'>
<button
className='flex cursor-pointer items-center space-x-1.5 whitespace-nowrap rounded-full bg-primary-600 px-4 py-2 text-white transition-transform hover:scale-105 hover:bg-primary-700 active:scale-100'
onClick={handleClick}

Wyświetl plik

@ -169,7 +169,7 @@ const ScrollableList = forwardRef<VirtuosoHandle, IScrollableList>(({
) : (
<>
{emptyMessageCard ? (
<Card variant='rounded' size='lg'>
<Card size='lg'>
{emptyMessage}
</Card>
) : emptyMessage}

Wyświetl plik

@ -381,7 +381,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
</button>
{switcher && (
<div className='border-t-2 border-solid border-gray-100 black:border-t dark:border-gray-800'>
<div className='border-t border-solid border-gray-100 dark:border-gray-800'>
{otherAccounts.map(account => renderAccount(account))}
<NavLink className='flex items-center space-x-1 py-2' to='/login/add' onClick={handleClose}>

Wyświetl plik

@ -44,8 +44,9 @@ const SidebarNavigationLink = forwardRef((props: ISidebarNavigationLink, ref: Re
ref={ref}
onClick={handleClick}
className={clsx({
'flex items-center px-4 py-3.5 text-base font-semibold space-x-4 rtl:space-x-reverse rounded-full group text-gray-600 hover:text-gray-900 dark:text-gray-500 dark:hover:text-gray-100 hover:bg-primary-200 dark:hover:bg-primary-900': true,
'dark:text-gray-100 text-gray-900': isActive,
'flex items-center px-4 py-3.5 text-base font-semibold space-x-4 rtl:space-x-reverse rounded-full group hover:text-gray-900 dark:hover:text-gray-100 hover:bg-gray-50 dark:hover:bg-primary-800': true,
'text-gray-600 dark:text-gray-500': !isActive,
'text-gray-900 dark:text-gray-50': isActive,
})}
>
<span className='relative'>
@ -54,8 +55,8 @@ const SidebarNavigationLink = forwardRef((props: ISidebarNavigationLink, ref: Re
count={count}
countMax={countMax}
className={clsx('size-5', {
'text-gray-600 black:text-white dark:text-gray-500 group-hover:text-primary-500 dark:group-hover:text-primary-400': !isActive,
'text-primary-500 dark:text-primary-400': isActive,
'text-gray-600 black:text-white dark:text-gray-500 group-hover:text-gray-900 dark:group-hover:text-gray-50': !isActive,
'text-gray-900 dark:text-gray-50': isActive,
})}
/>
</span>

Wyświetl plik

@ -21,10 +21,15 @@ import userPlusIcon from '@tabler/icons/outline/user-plus.svg';
import userIcon from '@tabler/icons/outline/user.svg';
import worldIcon from '@tabler/icons/outline/world.svg';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { Link } from 'react-router-dom';
import Account from 'soapbox/components/account.tsx';
import SiteLogo from 'soapbox/components/site-logo.tsx';
import Stack from 'soapbox/components/ui/stack.tsx';
import { useStatContext } from 'soapbox/contexts/stat-context.tsx';
import Search from 'soapbox/features/compose/components/search.tsx';
import ComposeButton from 'soapbox/features/ui/components/compose-button.tsx';
import ProfileDropdown from 'soapbox/features/ui/components/profile-dropdown.tsx';
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
import { useFeatures } from 'soapbox/hooks/useFeatures.ts';
import { useInstance } from 'soapbox/hooks/useInstance.ts';
@ -139,7 +144,15 @@ const SidebarNavigation = () => {
};
return (
<Stack space={4}>
<Stack justifyContent='between' className='min-h-screen py-6'>
<Stack space={6}>
<Link key='logo' to='/' data-preview-title-id='column.home' className='ml-4 flex shrink-0 items-center'>
<SiteLogo alt='Logo' className='h-10 w-auto cursor-pointer' />
<span className='hidden'><FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></span>
</Link>
<Search openInRoute autosuggest />
<Stack space={2}>
<SidebarNavigationLink
to='/'
@ -235,6 +248,17 @@ const SidebarNavigation = () => {
<ComposeButton />
)}
</Stack>
{account && (
<div className='mt-12'>
<ProfileDropdown account={account} placement='top'>
<div className='w-full p-2'>
<Account account={account} showProfileHoverCard={false} withLinkToProfile={false} hideActions />
</div>
</ProfileDropdown>
</div>
)}
</Stack>
);
};

Wyświetl plik

@ -8,7 +8,6 @@ import {
updateStatusHoverCard,
} from 'soapbox/actions/status-hover-card.ts';
import { fetchStatus } from 'soapbox/actions/statuses.ts';
import { Card, CardBody } from 'soapbox/components/ui/card.tsx';
import StatusContainer from 'soapbox/containers/status-container.tsx';
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
@ -67,20 +66,7 @@ export const StatusHoverCard: React.FC<IStatusHoverCard> = ({ visible = true })
};
}, []);
if (!statusId) return null;
const renderStatus = (statusId: string) => {
return (
// @ts-ignore
<StatusContainer
key={statusId}
id={statusId}
hoverable={false}
hideActionBar
muted
/>
);
};
if (!status) return null;
return (
<div
@ -94,11 +80,9 @@ export const StatusHoverCard: React.FC<IStatusHoverCard> = ({ visible = true })
onMouseEnter={handleMouseEnter()}
onMouseLeave={handleMouseLeave()}
>
<Card className='relative'>
<CardBody>
{renderStatus(statusId)}
</CardBody>
</Card>
<div className='overflow-hidden rounded-xl bg-white p-4 black:bg-black dark:bg-primary-900'>
<StatusContainer id={status.id} hoverable={false} hideActionBar muted slim />
</div>
</div>
);
};

Wyświetl plik

@ -36,8 +36,6 @@ interface IStatusList extends Omit<IScrollableList, 'onLoadMore' | 'children'> {
emptyMessage: React.ReactNode;
/** ID of the timeline in Redux. */
timelineId?: string;
/** Whether to display a gap or border between statuses in the list. */
divideType?: 'space' | 'border';
/** Whether to display ads. */
showAds?: boolean;
/** Whether to show group information. */
@ -52,7 +50,6 @@ const StatusList: React.FC<IStatusList> = ({
statusIds,
lastStatusId,
featuredStatusIds,
divideType = 'border',
onLoadMore,
timelineId,
isLoading,
@ -131,7 +128,6 @@ const StatusList: React.FC<IStatusList> = ({
onMoveDown={handleMoveDown}
contextType={timelineId}
showGroup={showGroup}
variant={divideType === 'border' ? 'slim' : 'rounded'}
/>
);
};
@ -159,7 +155,6 @@ const StatusList: React.FC<IStatusList> = ({
onMoveDown={handleMoveDown}
contextType={timelineId}
showGroup={showGroup}
variant={divideType === 'border' ? 'slim' : 'default'}
/>
));
};
@ -234,15 +229,10 @@ const StatusList: React.FC<IStatusList> = ({
isLoading={isLoading}
showLoading={isLoading && statusIds.size === 0}
onLoadMore={handleLoadOlder}
placeholderComponent={() => <PlaceholderStatus variant={divideType === 'border' ? 'slim' : 'rounded'} />}
placeholderComponent={() => <PlaceholderStatus />}
placeholderCount={20}
ref={node}
listClassName={clsx('divide-y divide-solid divide-gray-200 dark:divide-gray-800', {
'divide-none': divideType !== 'border',
}, className)}
itemClassName={clsx({
'pb-3': divideType !== 'border',
})}
listClassName={clsx('divide-y divide-solid divide-gray-200 dark:divide-gray-800', className)}
{...other}
>
{renderScrollableContent()}

Wyświetl plik

@ -57,9 +57,9 @@ export interface IStatus {
featured?: boolean;
hideActionBar?: boolean;
hoverable?: boolean;
variant?: 'default' | 'rounded' | 'slim';
showGroup?: boolean;
accountAction?: React.ReactElement;
slim?: boolean;
}
/**
@ -81,8 +81,8 @@ const Status: React.FC<IStatus> = (props) => {
featured,
unread,
hideActionBar,
variant = 'rounded',
showGroup = true,
slim,
} = props;
const intl = useIntl();
@ -417,11 +417,12 @@ const Status: React.FC<IStatus> = (props) => {
role='link'
>
<Card
variant={variant}
className={clsx('status--wrapper space-y-4', {
'py-6 sm:p-5': variant === 'rounded', muted, read: unread === false,
muted,
read: unread === false,
})}
data-id={status.id}
slim={slim}
>
{renderStatusInfo()}

Wyświetl plik

@ -9,23 +9,16 @@ import SvgIcon from 'soapbox/components/ui/svg-icon.tsx';
import HStack from './hstack.tsx';
import Text from './text.tsx';
const sizes = {
md: 'p-4 sm:rounded-xl',
lg: 'p-4 sm:p-6 sm:rounded-xl',
xl: 'p-4 sm:p-10 sm:rounded-3xl',
};
const messages = defineMessages({
back: { id: 'card.back.label', defaultMessage: 'Back' },
});
export type CardSizes = keyof typeof sizes
interface ICard {
/** The type of card. */
variant?: 'default' | 'rounded' | 'slim';
rounded?: boolean;
transparent?: boolean;
slim?: boolean;
/** Card size preset. */
size?: CardSizes;
size?: 'md' | 'lg' | 'xl';
/** Extra classnames for the <div> element. */
className?: string;
/** Elements inside the card. */
@ -34,15 +27,16 @@ interface ICard {
}
/** An opaque backdrop to hold a collection of related elements. */
const Card = forwardRef<HTMLDivElement, ICard>(({ children, variant = 'default', size = 'md', className, ...filteredProps }, ref): JSX.Element => (
const Card = forwardRef<HTMLDivElement, ICard>(({ children, rounded, transparent, slim, size = 'md', className, ...filteredProps }, ref): JSX.Element => (
<div
ref={ref}
{...filteredProps}
className={clsx({
'bg-white dark:bg-primary-900 black:bg-black text-gray-900 dark:text-gray-100 shadow-lg dark:shadow-none': variant === 'rounded',
[sizes[size]]: variant === 'rounded',
'py-4': variant === 'slim',
'black:rounded-none': size !== 'xl',
'bg-white dark:bg-primary-900 black:bg-black': !transparent,
'overflow-hidden': rounded,
'rounded-xl': rounded && size !== 'xl',
'rounded-3xl': rounded && size === 'xl',
'py-4 px-5': !slim,
}, className)}
>
{children}

Wyświetl plik

@ -1,12 +1,12 @@
import clsx from 'clsx';
import { throttle } from 'es-toolkit';
import { forwardRef, useCallback, useEffect, useState } from 'react';
import { forwardRef } from 'react';
import { useHistory } from 'react-router-dom';
import Helmet from 'soapbox/components/helmet.tsx';
import Stack from 'soapbox/components/ui/stack.tsx';
import { useSoapboxConfig } from 'soapbox/hooks/useSoapboxConfig.ts';
import { Card, CardBody, CardHeader, CardTitle, type CardSizes } from './card.tsx';
import { Card, CardBody, CardHeader, CardTitle } from './card.tsx';
type IColumnHeader = Pick<IColumn, 'label' | 'backHref' | 'className' | 'action'>;
@ -47,6 +47,8 @@ export interface IColumn {
label?: string;
/** Whether this column should have a transparent background. */
transparent?: boolean;
/** Whether to display the column without padding. */
slim?: boolean;
/** Whether this column should have a title and back button. */
withHeader?: boolean;
/** Extra class name for top <div> element. */
@ -60,26 +62,13 @@ export interface IColumn {
/** Action for the ColumnHeader, displayed at the end. */
action?: React.ReactNode;
/** Column size, inherited from Card. */
size?: CardSizes;
size?: 'md' | 'lg' | 'xl';
}
/** A backdrop for the main section of the UI. */
const Column = forwardRef<HTMLDivElement, IColumn>((props, ref): JSX.Element => {
const { backHref, children, label, transparent = false, withHeader = true, className, bodyClassName, action, size } = props;
const { backHref, children, label, transparent = false, slim, withHeader = true, className, bodyClassName, action, size } = props;
const soapboxConfig = useSoapboxConfig();
const [isScrolled, setIsScrolled] = useState(false);
const handleScroll = useCallback(throttle(() => {
setIsScrolled(window.pageYOffset > 32);
}, 50), []);
useEffect(() => {
window.addEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, []);
return (
<div role='region' className='relative' ref={ref} aria-label={label} column-type={transparent ? 'transparent' : 'filled'}>
@ -95,25 +84,25 @@ const Column = forwardRef<HTMLDivElement, IColumn>((props, ref): JSX.Element =>
)}
</Helmet>
<Card size={size} variant={transparent ? undefined : 'rounded'} className={className}>
<Stack>
{withHeader && (
<ColumnHeader
label={label}
backHref={backHref}
className={clsx({
'rounded-t-3xl': !isScrolled && !transparent,
'sticky top-12 z-10 bg-white/90 dark:bg-primary-900/90 black:bg-black/90 backdrop-blur lg:top-16': !transparent,
'p-4 sm:p-0 sm:pb-4 black:p-4': transparent,
'-mt-4 p-4': size !== 'lg' && !transparent,
'-mt-4 p-4 sm:-mt-6 sm:-mx-6 sm:p-6': size === 'lg' && !transparent,
className={clsx('px-5 py-4', {
'sticky top-12 z-20 bg-white/90 dark:bg-primary-900/90 black:bg-black/90 backdrop-blur lg:top-0': !transparent,
'-mb-4': !slim,
})}
action={action}
/>
)}
<Card size={size} transparent={transparent} className={className} slim={slim}>
<CardBody className={bodyClassName}>
{children}
</CardBody>
</Card>
</Stack>
</div>
);
});

Wyświetl plik

@ -11,7 +11,7 @@ interface IDivider {
const Divider = ({ text, textSize = 'md' }: IDivider) => (
<div className='relative' data-testid='divider'>
<div className='absolute inset-0 flex items-center' aria-hidden='true'>
<div className='w-full border-t-2 border-solid border-gray-100 black:border-t dark:border-gray-800' />
<div className='w-full border-t border-solid border-gray-100 dark:border-gray-800' />
</div>
{text && (

Wyświetl plik

@ -95,7 +95,7 @@ const Input = forwardRef<HTMLInputElement, IInput>(
'text-gray-900 dark:text-gray-100': !props.disabled,
'text-gray-600': props.disabled,
'rounded-md bg-white dark:bg-gray-900 border-gray-400 dark:border-gray-800 black:bg-black': theme === 'normal',
'rounded-full bg-gray-200 border-gray-200 dark:bg-gray-800 dark:border-gray-800 focus:bg-white dark:focus:bg-gray-900': theme === 'search',
'rounded-full bg-white border-gray-200 dark:bg-gray-900 dark:border-gray-800 py-2.5': theme === 'search',
'pr-10 rtl:pl-10 rtl:pr-3': isPassword || append,
'pl-8': typeof icon !== 'undefined',
'pl-16': typeof prepend !== 'undefined',

Wyświetl plik

@ -21,7 +21,7 @@ interface LayoutComponent extends React.FC<ILayout> {
/** Layout container, to hold Sidebar, Main, and Aside. */
const Layout: LayoutComponent = ({ children }) => (
<div className='relative flex grow flex-col black:pt-0 sm:pt-4'>
<div className='relative flex grow flex-col'>
<div className='mx-auto w-full max-w-3xl grow sm:px-6 md:grid md:max-w-7xl md:grid-cols-12 md:gap-8 md:px-8'>
{children}
</div>
@ -31,7 +31,7 @@ const Layout: LayoutComponent = ({ children }) => (
/** Left sidebar container in the UI. */
const Sidebar: React.FC<ISidebar> = ({ children }) => (
<div className='hidden lg:col-span-3 lg:block'>
<StickyBox offsetTop={80} className='pb-4'>
<StickyBox>
{children}
</StickyBox>
</div>
@ -39,11 +39,7 @@ const Sidebar: React.FC<ISidebar> = ({ children }) => (
/** Center column container in the UI. */
const Main: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({ children, className }) => (
<main
className={clsx({
'md:col-span-12 lg:col-span-9 xl:col-span-6 pb-36 black:border-gray-800 lg:black:border-l xl:black:border-r': true,
}, className)}
>
<main className={clsx('border-gray-200 bg-white pb-6 black:border-gray-800 black:bg-black dark:border-gray-800 dark:bg-primary-900 md:col-span-12 lg:col-span-9 lg:border-l xl:col-span-6 xl:border-r', className)}>
{children}
</main>
);
@ -51,7 +47,7 @@ const Main: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({ children, classN
/** Right sidebar container in the UI. */
const Aside: React.FC<IAside> = ({ children }) => (
<aside className='hidden xl:col-span-3 xl:block'>
<StickyBox offsetTop={80} className='space-y-6 pb-12'>
<StickyBox className='space-y-6 py-6 pb-12'>
<Suspense>
{children}
</Suspense>

Wyświetl plik

@ -36,6 +36,6 @@ const MenuList: React.FC<IMenuList> = (props) => {
};
/** Divides menu items. */
const MenuDivider = () => <hr className='mx-2 my-1 border-t-2 border-gray-100 black:border-t dark:border-gray-800' />;
const MenuDivider = () => <hr className='mx-2 my-1 border-t border-gray-100 dark:border-gray-800' />;
export { Menu, MenuButton, MenuDivider, MenuItems, MenuItem, MenuList, MenuLink };

Wyświetl plik

@ -69,7 +69,7 @@ const AboutPage: React.FC = () => {
return (
<div>
<Card variant='rounded'>
<Card>
<div className='prose mx-auto py-4 dark:prose-invert sm:p-6'>
<div dangerouslySetInnerHTML={{ __html: pageHtml }} />
{alsoAvailable}

Wyświetl plik

@ -137,9 +137,9 @@ const Header: React.FC<IHeader> = ({ account }) => {
if (!account) {
return (
<div className='-mx-4 -mt-4 sm:-mx-6 sm:-mt-6'>
<div>
<div className='relative h-32 w-full bg-gray-200 black:rounded-t-none dark:bg-gray-900/50 md:rounded-t-xl lg:h-48' />
<div>
<div className='relative h-32 w-full bg-gray-200 dark:bg-gray-900/50 lg:h-48' />
</div>
<div className='px-4 sm:px-6'>
@ -679,13 +679,13 @@ const Header: React.FC<IHeader> = ({ account }) => {
const acceptsZaps = account.ditto.accepts_zaps === true;
return (
<div className='-mx-4 -mt-4 sm:-mx-6 sm:-mt-6'>
<div>
{(account.moved && typeof account.moved === 'object') && (
<MovedNote from={account} to={account.moved as Account} />
)}
<div>
<div className='relative isolate flex h-32 w-full flex-col justify-center overflow-hidden bg-gray-200 black:rounded-t-none dark:bg-gray-900/50 md:rounded-t-xl lg:h-48'>
<div className='relative isolate flex h-32 w-full flex-col justify-center overflow-hidden bg-gray-200 dark:bg-gray-900/50 lg:h-48'>
{renderHeader()}
<div className='absolute left-2 top-2'>

Wyświetl plik

@ -101,7 +101,7 @@ const AuthTokenList: React.FC = () => {
return (
<Column label={intl.formatMessage(messages.header)} transparent withHeader={false}>
<Card variant='rounded'>
<Card>
<CardHeader backHref='/settings'>
<CardTitle title={intl.formatMessage(messages.header)} />
</CardHeader>

Wyświetl plik

@ -84,7 +84,7 @@ const Backups = () => {
const showLoading = isLoading && backups.count() === 0;
const emptyMessage = (
<Card variant='rounded' size='lg'>
<Card size='lg'>
{intl.formatMessage(messages.emptyMessage, {
action: (
<Link to={'/'} className='inline-flex'>

Wyświetl plik

@ -5,8 +5,6 @@ import { useBookmarks } from 'soapbox/api/hooks/index.ts';
import PullToRefresh from 'soapbox/components/pull-to-refresh.tsx';
import PureStatusList from 'soapbox/components/pure-status-list.tsx';
import { Column } from 'soapbox/components/ui/column.tsx';
import { useIsMobile } from 'soapbox/hooks/useIsMobile.ts';
import { useTheme } from 'soapbox/hooks/useTheme.ts';
const messages = defineMessages({
heading: { id: 'column.bookmarks', defaultMessage: 'Bookmarks' },
@ -15,9 +13,6 @@ const messages = defineMessages({
const Bookmarks: React.FC = () => {
const intl = useIntl();
const theme = useTheme();
const isMobile = useIsMobile();
const handleLoadMore = debounce(() => {
fetchNextPage();
}, 300, { edges: ['leading'] });
@ -31,7 +26,7 @@ const Bookmarks: React.FC = () => {
const emptyMessage = <FormattedMessage id='empty_column.bookmarks' defaultMessage="You don't have any bookmarks yet. When you add one, it will show up here." />;
return (
<Column label={intl.formatMessage(messages.heading)} transparent>
<Column label={intl.formatMessage(messages.heading)}>
<PullToRefresh onRefresh={handleRefresh}>
<PureStatusList
className='black:p-4 black:sm:p-5'
@ -41,7 +36,6 @@ const Bookmarks: React.FC = () => {
isLoading={typeof isLoading === 'boolean' ? isLoading : true}
onLoadMore={() => handleLoadMore()}
emptyMessage={emptyMessage}
divideType={(theme === 'black' || isMobile) ? 'border' : 'space'}
/>
</PullToRefresh>
</Column>

Wyświetl plik

@ -8,15 +8,12 @@ import { Column } from 'soapbox/components/ui/column.tsx';
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
import { useInstance } from 'soapbox/hooks/useInstance.ts';
import { useIsMobile } from 'soapbox/hooks/useIsMobile.ts';
import { useSettings } from 'soapbox/hooks/useSettings.ts';
import { useTheme } from 'soapbox/hooks/useTheme.ts';
import Timeline from '../ui/components/timeline.tsx';
const CommunityTimeline = () => {
const dispatch = useAppDispatch();
const theme = useTheme();
const { instance } = useInstance();
const settings = useSettings();
@ -24,7 +21,6 @@ const CommunityTimeline = () => {
const next = useAppSelector(state => state.timelines.get('community')?.next);
const timelineId = 'community';
const isMobile = useIsMobile();
const handleLoadMore = (maxId: string) => {
dispatch(expandCommunityTimeline({ url: next, maxId, onlyMedia }));
@ -41,7 +37,7 @@ const CommunityTimeline = () => {
}, [onlyMedia]);
return (
<Column className='-mt-3 sm:mt-0' label={instance.domain} transparent={!isMobile}>
<Column label={instance.domain} slim withHeader={false}>
<PullToRefresh onRefresh={handleRefresh}>
<Timeline
className='black:p-4 black:sm:p-5'
@ -50,7 +46,6 @@ const CommunityTimeline = () => {
prefix='home'
onLoadMore={handleLoadMore}
emptyMessage={<FormattedMessage id='empty_column.community' defaultMessage='The local timeline is empty. Write something publicly to get the ball rolling!' />}
divideType={(theme === 'black' || isMobile) ? 'border' : 'space'}
/>
</PullToRefresh>
</Column>

Wyświetl plik

@ -154,13 +154,11 @@ const SearchResults = () => {
if (results.statuses && results.statuses.size > 0) {
searchResults = results.statuses.map((statusId: string) => (
// @ts-ignore
<StatusContainer
key={statusId}
id={statusId}
onMoveUp={handleMoveUp}
onMoveDown={handleMoveDown}
variant='slim'
/>
));
resultsIds = results.statuses;
@ -173,7 +171,6 @@ const SearchResults = () => {
id={statusId}
onMoveUp={handleMoveUp}
onMoveDown={handleMoveDown}
variant='slim'
/>
));
resultsIds = trendingStatuses;
@ -227,7 +224,9 @@ const SearchResults = () => {
/>
</Text>
</HStack>
) : renderFilterBar()}
) : (
<div className='px-4'>{renderFilterBar()}</div>
)}
{noResultsMessage || (
<ScrollableList
@ -244,7 +243,8 @@ const SearchResults = () => {
listClassName={clsx({
'divide-gray-200 dark:divide-gray-800 divide-solid divide-y': selectedFilter === 'statuses',
})}
itemClassName={clsx('px-4', {
itemClassName={clsx({
'px-4': selectedFilter !== 'statuses',
'pb-4': selectedFilter === 'accounts',
'pb-3': selectedFilter === 'hashtags',
})}

Wyświetl plik

@ -52,7 +52,7 @@ const DeleteAccount = () => {
}, [password, dispatch, intl]);
return (
<Card variant='rounded'>
<Card>
<CardHeader backHref='/settings'>
<CardTitle title={intl.formatMessage(messages.deleteHeader)} />
</CardHeader>

Wyświetl plik

@ -36,7 +36,7 @@ const DirectTimeline = () => {
};
return (
<Column label={intl.formatMessage(messages.heading)}>
<Column label={intl.formatMessage(messages.heading)} slim>
<AccountSearch
placeholder={intl.formatMessage(messages.searchPlaceholder)}
onSelected={handleSuggestion}
@ -47,7 +47,6 @@ const DirectTimeline = () => {
timelineId='direct'
onLoadMore={handleLoadMore}
emptyMessage={<FormattedMessage id='empty_column.direct' defaultMessage="You don't have any direct messages yet. When you send or receive one, it will show up here." />}
divideType='border'
/>
</Column>
);

Wyświetl plik

@ -55,7 +55,7 @@ const EmbeddedStatus: React.FC<IEmbeddedStatus> = ({ params }) => {
if (loading) {
return <Spinner />;
} else if (status) {
return <Status status={status} accountAction={logo} variant='default' />;
return <Status status={status} accountAction={logo} />;
} else {
return <MissingIndicator nested />;
}

Wyświetl plik

@ -185,7 +185,7 @@ const EventDiscussion: React.FC<IEventDiscussion> = (props) => {
ref={scroller}
hasMore={!!next}
onLoadMore={handleLoadMore}
placeholderComponent={() => <PlaceholderStatus variant='slim' />}
placeholderComponent={() => <PlaceholderStatus />}
initialTopMostItemIndex={0}
emptyMessage={<FormattedMessage id='event.discussion.empty' defaultMessage='No one has commented this event yet. When someone does, they will appear here.' />}
>

Wyświetl plik

@ -49,7 +49,7 @@ const EventCarousel: React.FC<IEventCarousel> = ({ statusIds, isLoading, emptyMe
}
return (
<Card variant='rounded' size='lg'>
<Card size='lg'>
{emptyMessage}
</Card>
);

Wyświetl plik

@ -98,7 +98,7 @@ const FeedSuggestions: React.FC<IFeedSuggesetions> = ({ statusId, onMoveUp, onMo
return (
<HotKeys handlers={handlers}>
<Card size='lg' variant='rounded' className='focusable space-y-6' tabIndex={0}>
<Card size='lg' className='focusable space-y-6' tabIndex={0}>
<HStack justifyContent='between' alignItems='center'>
<CardTitle title={intl.formatMessage(messages.heading)} />

Wyświetl plik

@ -38,9 +38,9 @@ const GroupHeader: React.FC<IGroupHeader> = ({ group }) => {
if (!group) {
return (
<div className='-mx-4 -mt-4 sm:-mx-6 sm:-mt-6' data-testid='group-header-missing'>
<div data-testid='group-header-missing'>
<div>
<div className='relative h-32 w-full bg-gray-200 black:rounded-t-none dark:bg-gray-900/50 md:rounded-t-xl lg:h-48' />
<div className='relative h-32 w-full bg-gray-200 dark:bg-gray-900/50 lg:h-48' />
</div>
<div className='px-4 sm:px-6'>
@ -123,7 +123,7 @@ const GroupHeader: React.FC<IGroupHeader> = ({ group }) => {
};
return (
<div className='-mx-4 -mt-4 sm:-mx-6 sm:-mt-6'>
<div>
<div className='relative'>
{renderHeader()}

Wyświetl plik

@ -47,7 +47,6 @@ const GroupTagTimeline: React.FC<IGroupTimeline> = (props) => {
scrollKey='group_timeline'
timelineId={`group:tags:${groupId}:${tag.name}`}
onLoadMore={handleLoadMore}
divideType='border'
showGroup={false}
emptyMessageCard={false}
emptyMessage={

Wyświetl plik

@ -130,7 +130,6 @@ const GroupTimeline: React.FC<IGroupTimeline> = (props) => {
</Stack>
}
emptyMessageCard={false}
divideType='border'
showGroup={false}
featuredStatusIds={featuredStatusIds}
/>

Wyświetl plik

@ -11,9 +11,7 @@ import Timeline from 'soapbox/features/ui/components/timeline.tsx';
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
import { useFeatures } from 'soapbox/hooks/useFeatures.ts';
import { useIsMobile } from 'soapbox/hooks/useIsMobile.ts';
import { useLoggedIn } from 'soapbox/hooks/useLoggedIn.ts';
import { useTheme } from 'soapbox/hooks/useTheme.ts';
interface IHashtagTimeline {
params?: {
@ -29,8 +27,6 @@ export const HashtagTimeline: React.FC<IHashtagTimeline> = ({ params }) => {
const tag = useAppSelector((state) => state.tags.get(id));
const next = useAppSelector(state => state.timelines.get(`hashtag:${id}`)?.next);
const { isLoggedIn } = useLoggedIn();
const theme = useTheme();
const isMobile = useIsMobile();
const handleLoadMore = (maxId: string) => {
dispatch(expandHashtagTimeline(id, { url: next, maxId }));
@ -57,7 +53,7 @@ export const HashtagTimeline: React.FC<IHashtagTimeline> = ({ params }) => {
}, [id]);
return (
<Column label={`#${id}`} transparent={!isMobile}>
<Column label={`#${id}`} slim>
{features.followHashtags && isLoggedIn && (
<List>
<ListItem
@ -76,7 +72,6 @@ export const HashtagTimeline: React.FC<IHashtagTimeline> = ({ params }) => {
timelineId={`hashtag:${id}`}
onLoadMore={handleLoadMore}
emptyMessage={<FormattedMessage id='empty_column.hashtag' defaultMessage='There is nothing in this hashtag yet.' />}
divideType={(theme === 'black' || isMobile) ? 'border' : 'space'}
/>
</Column>
);

Wyświetl plik

@ -12,8 +12,6 @@ import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
import { useFeatures } from 'soapbox/hooks/useFeatures.ts';
import { useInstance } from 'soapbox/hooks/useInstance.ts';
import { useIsMobile } from 'soapbox/hooks/useIsMobile.ts';
import { useTheme } from 'soapbox/hooks/useTheme.ts';
const messages = defineMessages({
title: { id: 'column.home', defaultMessage: 'Home' },
@ -24,10 +22,8 @@ const HomeTimeline: React.FC = () => {
const dispatch = useAppDispatch();
const features = useFeatures();
const { instance } = useInstance();
const theme = useTheme();
const polling = useRef<NodeJS.Timeout | null>(null);
const isMobile = useIsMobile();
const isPartial = useAppSelector(state => state.timelines.get('home')?.isPartial === true);
const next = useAppSelector(state => state.timelines.get('home')?.next);
@ -68,14 +64,12 @@ const HomeTimeline: React.FC = () => {
}, [isPartial]);
return (
<Column className='py-0' label={intl.formatMessage(messages.title)} transparent={!isMobile} withHeader={false}>
<Column label={intl.formatMessage(messages.title)} withHeader={false} slim>
<PullToRefresh onRefresh={handleRefresh}>
<Timeline
className='black:p-4 black:sm:p-5'
scrollKey='home_timeline'
onLoadMore={handleLoadMore}
timelineId='home'
divideType={(theme === 'black' || isMobile) ? 'border' : 'space'}
emptyMessage={
<Stack space={1}>
<Text size='xl' weight='medium' align='center'>

Wyświetl plik

@ -9,7 +9,6 @@ import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
import { useInstance } from 'soapbox/hooks/useInstance.ts';
import { useIsMobile } from 'soapbox/hooks/useIsMobile.ts';
import { useTheme } from 'soapbox/hooks/useTheme.ts';
import AboutPage from '../about/index.tsx';
import Timeline from '../ui/components/timeline.tsx';
@ -19,7 +18,6 @@ import { SiteBanner } from './components/site-banner.tsx';
const LandingTimeline = () => {
const dispatch = useAppDispatch();
const { instance } = useInstance();
const theme = useTheme();
const isMobile = useIsMobile();
const timelineEnabled = !instance.pleroma.metadata.restrict_unauthenticated.timelines.local;
@ -48,7 +46,7 @@ const LandingTimeline = () => {
}, []);
return (
<Column transparent={!isMobile} withHeader={false}>
<Column transparent={!isMobile} withHeader={false} slim>
<div className='my-12 mb-16 px-4 sm:mb-20'>
<SiteBanner />
</div>
@ -62,7 +60,6 @@ const LandingTimeline = () => {
prefix='home'
onLoadMore={handleLoadMore}
emptyMessage={<FormattedMessage id='empty_column.community' defaultMessage='The local timeline is empty. Write something publicly to get the ball rolling!' />}
divideType={(theme === 'black' || isMobile) ? 'border' : 'space'}
/>
</PullToRefresh>
) : (

Wyświetl plik

@ -12,16 +12,12 @@ import { Column } from 'soapbox/components/ui/column.tsx';
import Spinner from 'soapbox/components/ui/spinner.tsx';
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
import { useIsMobile } from 'soapbox/hooks/useIsMobile.ts';
import { useTheme } from 'soapbox/hooks/useTheme.ts';
import Timeline from '../ui/components/timeline.tsx';
const ListTimeline: React.FC = () => {
const dispatch = useAppDispatch();
const { id } = useParams<{ id: string }>();
const theme = useTheme();
const isMobile = useIsMobile();
const list = useAppSelector((state) => state.lists.get(id));
const next = useAppSelector(state => state.timelines.get(`list:${id}`)?.next);
@ -66,14 +62,13 @@ const ListTimeline: React.FC = () => {
);
return (
<Column label={title} transparent={!isMobile}>
<Column label={title}>
<Timeline
className='black:p-4 black:sm:p-5'
scrollKey='list_timeline'
timelineId={`list:${id}`}
onLoadMore={handleLoadMore}
emptyMessage={emptyMessage}
divideType={(theme === 'black' || isMobile) ? 'border' : 'space'}
/>
</Column>
);

Wyświetl plik

@ -19,9 +19,7 @@ import Portal from 'soapbox/components/ui/portal.tsx';
import PlaceholderNotification from 'soapbox/features/placeholder/components/placeholder-notification.tsx';
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
import { useIsMobile } from 'soapbox/hooks/useIsMobile.ts';
import { useSettings } from 'soapbox/hooks/useSettings.ts';
import { useTheme } from 'soapbox/hooks/useTheme.ts';
import FilterBar from './components/filter-bar.tsx';
import Notification from './components/notification.tsx';
@ -63,9 +61,6 @@ const Notifications = () => {
const hasMore = useAppSelector(state => state.notifications.hasMore);
const totalQueuedNotificationsCount = useAppSelector(state => state.notifications.totalQueuedNotificationsCount || 0);
const theme = useTheme();
const isMobile = useIsMobile();
const node = useRef<VirtuosoHandle>(null);
const column = useRef<HTMLDivElement>(null);
const scrollableContentRef = useRef<ImmutableList<JSX.Element> | null>(null);
@ -186,7 +181,7 @@ const Notifications = () => {
ref={column}
label={intl.formatMessage(messages.title)}
withHeader={false}
className={clsx({ '!p-0': isMobile || theme === 'black' })}
className='!p-0'
>
{filterBarContainer}

Wyświetl plik

@ -8,18 +8,12 @@ import PlaceholderDisplayName from './placeholder-display-name.tsx';
import PlaceholderStatusContent from './placeholder-status-content.tsx';
interface IPlaceholderStatus {
variant?: 'rounded' | 'slim' | 'default';
slim?: boolean;
}
/** Fake status to display while data is loading. */
const PlaceholderStatus: React.FC<IPlaceholderStatus> = ({ variant }) => (
<div
className={clsx({
'status-placeholder bg-white black:bg-black dark:bg-primary-900': true,
'shadow-xl dark:shadow-none sm:rounded-xl px-4 py-6 sm:p-5': variant === 'rounded',
'py-4': variant === 'slim',
})}
>
const PlaceholderStatus: React.FC<IPlaceholderStatus> = ({ slim }) => (
<div className={clsx('status-placeholder bg-white black:bg-black dark:bg-primary-900', { 'p-4': !slim })}>
<div className='w-full animate-pulse overflow-hidden'>
<div>
<HStack space={3} alignItems='center'>

Wyświetl plik

@ -14,9 +14,7 @@ import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
import { useFeatures } from 'soapbox/hooks/useFeatures.ts';
import { useInstance } from 'soapbox/hooks/useInstance.ts';
import { useIsMobile } from 'soapbox/hooks/useIsMobile.ts';
import { useSettings } from 'soapbox/hooks/useSettings.ts';
import { useTheme } from 'soapbox/hooks/useTheme.ts';
import PinnedHostsPicker from '../remote-timeline/components/pinned-hosts-picker.tsx';
import Timeline from '../ui/components/timeline.tsx';
@ -30,7 +28,6 @@ const PublicTimeline = () => {
const intl = useIntl();
const dispatch = useAppDispatch();
const features = useFeatures();
const theme = useTheme();
const [language, setLanguage] = useState<string>(localStorage.getItem('soapbox:global:language') || '');
@ -40,7 +37,6 @@ const PublicTimeline = () => {
const next = useAppSelector(state => state.timelines.get('public')?.next);
const timelineId = 'public';
const isMobile = useIsMobile();
const explanationBoxExpanded = settings.explanationBox;
const showExplanationBox = settings.showExplanationBox && !features.nostr;
@ -74,10 +70,9 @@ const PublicTimeline = () => {
return (
<Column
className='-mt-3 sm:mt-0'
label={intl.formatMessage(messages.title)}
transparent={!isMobile}
action={features.publicTimelineLanguage ? <LanguageDropdown language={language} setLanguage={setLanguage} /> : null}
slim
>
<PinnedHostsPicker />
@ -118,7 +113,6 @@ const PublicTimeline = () => {
prefix='home'
onLoadMore={handleLoadMore}
emptyMessage={<FormattedMessage id='empty_column.public' defaultMessage='There is nothing here! Write something publicly, or manually follow users from other servers to fill it up' />}
divideType={(theme === 'black' || isMobile) ? 'border' : 'space'}
/>
</PullToRefresh>
</Column>

Wyświetl plik

@ -10,7 +10,6 @@ import { Column } from 'soapbox/components/ui/column.tsx';
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
import { useIsMobile } from 'soapbox/hooks/useIsMobile.ts';
import { useTheme } from 'soapbox/hooks/useTheme.ts';
const messages = defineMessages({
heading: { id: 'column.quotes', defaultMessage: 'Post quotes' },
@ -23,7 +22,6 @@ const Quotes: React.FC = () => {
const dispatch = useAppDispatch();
const intl = useIntl();
const { statusId } = useParams<{ statusId: string }>();
const theme = useTheme();
const isMobile = useIsMobile();
const statusIds = useAppSelector((state) => state.status_lists.getIn([`quotes:${statusId}`, 'items'], ImmutableOrderedSet<string>()));
@ -51,7 +49,6 @@ const Quotes: React.FC = () => {
onLoadMore={() => handleLoadMore(statusId, dispatch)}
onRefresh={handleRefresh}
emptyMessage={emptyMessage}
divideType={(theme === 'black' || isMobile) ? 'border' : 'space'}
/>
</Column>
);

Wyświetl plik

@ -11,9 +11,7 @@ import HStack from 'soapbox/components/ui/hstack.tsx';
import Text from 'soapbox/components/ui/text.tsx';
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
import { useIsMobile } from 'soapbox/hooks/useIsMobile.ts';
import { useSettings } from 'soapbox/hooks/useSettings.ts';
import { useTheme } from 'soapbox/hooks/useTheme.ts';
import Timeline from '../ui/components/timeline.tsx';
@ -29,7 +27,6 @@ interface IRemoteTimeline {
const RemoteTimeline: React.FC<IRemoteTimeline> = ({ params }) => {
const history = useHistory();
const dispatch = useAppDispatch();
const theme = useTheme();
const instance = params?.instance as string;
const settings = useSettings();
@ -39,7 +36,6 @@ const RemoteTimeline: React.FC<IRemoteTimeline> = ({ params }) => {
const next = useAppSelector(state => state.timelines.get('remote')?.next);
const pinned = settings.remote_timeline.pinnedHosts.includes(instance);
const isMobile = useIsMobile();
const handleCloseClick: React.MouseEventHandler = () => {
history.push('/timeline/fediverse');
@ -56,7 +52,7 @@ const RemoteTimeline: React.FC<IRemoteTimeline> = ({ params }) => {
}, [onlyMedia]);
return (
<Column label={instance} transparent={!isMobile}>
<Column label={instance} slim>
{instance && <PinnedHostsPicker host={instance} />}
{!pinned && (
@ -84,7 +80,6 @@ const RemoteTimeline: React.FC<IRemoteTimeline> = ({ params }) => {
values={{ instance }}
/>
}
divideType={(theme === 'black' || isMobile) ? 'border' : 'space'}
/>
</Column>
);

Wyświetl plik

@ -1,11 +1,9 @@
import clsx from 'clsx';
import { defineMessages, useIntl } from 'react-intl';
import { Column } from 'soapbox/components/ui/column.tsx';
import Stack from 'soapbox/components/ui/stack.tsx';
import SearchResults from 'soapbox/features/compose/components/search-results.tsx';
import Search from 'soapbox/features/compose/components/search.tsx';
import { useIsMobile } from 'soapbox/hooks/useIsMobile.ts';
import { useTheme } from 'soapbox/hooks/useTheme.ts';
const messages = defineMessages({
heading: { id: 'column.search', defaultMessage: 'Discover' },
@ -14,20 +12,14 @@ const messages = defineMessages({
const SearchPage = () => {
const intl = useIntl();
const theme = useTheme();
const isMobile = useIsMobile();
return (
<Column
label={intl.formatMessage(messages.heading)}
className={clsx({ '!px-0': isMobile || theme === 'black' })}
>
<div className='space-y-4'>
<div className='px-4 sm:py-0'>
<Column label={intl.formatMessage(messages.heading)} slim>
<Stack space={4}>
<div className='px-4'>
<Search autoSubmit />
</div>
<SearchResults />
</div>
</Stack>
</Column>
);
};

Wyświetl plik

@ -68,8 +68,8 @@ const Settings = () => {
const displayName = account.display_name || account.username;
return (
<Column label={intl.formatMessage(messages.settings)} transparent withHeader={false}>
<Card className='space-y-4' variant='rounded'>
<Column label={intl.formatMessage(messages.settings)} transparent withHeader={false} slim>
<Card className='space-y-4'>
<CardHeader>
<CardTitle title={intl.formatMessage(messages.profile)} />
</CardHeader>

Wyświetl plik

@ -4,7 +4,6 @@ import { FormattedMessage } from 'react-intl';
import { defaultSettings } from 'soapbox/actions/settings.ts';
import SiteLogo from 'soapbox/components/site-logo.tsx';
import BackgroundShapes from 'soapbox/features/ui/components/background-shapes.tsx';
import { useSystemTheme } from 'soapbox/hooks/useSystemTheme.ts';
import { normalizeSoapboxConfig } from 'soapbox/normalizers/index.ts';
import { generateThemeCss } from 'soapbox/utils/theme.ts';
@ -39,7 +38,6 @@ const SitePreview: React.FC<ISitePreview> = ({ soapbox }) => {
<div className={bodyClass}>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<style>{`.site-preview {${generateThemeCss(soapboxConfig)}}`}</style>
<BackgroundShapes position='absolute' />
<div className='absolute z-[2] self-center overflow-hidden rounded-lg bg-accent-500 p-2 text-white'>
<FormattedMessage id='site_preview.preview' defaultMessage='Preview' />

Wyświetl plik

@ -15,7 +15,7 @@ const ThreadLoginCta: React.FC = () => {
if (!displayCta) return null;
return (
<Card className='space-y-6 px-6 py-12 text-center' variant='rounded'>
<Card className='space-y-6 px-6 py-12 text-center'>
<Stack>
<CardTitle title={<FormattedMessage id='thread_login.title' defaultMessage='Continue the conversation' />} />
<Text>

Wyświetl plik

@ -44,7 +44,7 @@ const ThreadStatus: React.FC<IThreadStatus> = (props): JSX.Element => {
// @ts-ignore FIXME
<StatusContainer {...props} showGroup={false} />
) : (
<PlaceholderStatus variant='default' />
<PlaceholderStatus slim />
)}
</div>
);

Wyświetl plik

@ -399,9 +399,7 @@ const Thread = (props: IThread) => {
</div>
</HotKeys>
{hasDescendants && (
<hr className='-mx-4 mt-2 max-w-[100vw] border-t-2 black:border-t dark:border-gray-800' />
)}
</div>
);
@ -445,7 +443,7 @@ const Thread = (props: IThread) => {
ref={scroller}
hasMore={!!next}
onLoadMore={handleLoadMore}
placeholderComponent={() => <PlaceholderStatus variant='slim' />}
placeholderComponent={() => <PlaceholderStatus />}
initialTopMostItemIndex={initialTopMostItemIndex}
useWindowScroll={useWindowScroll}
itemClassName={itemClassName}

Wyświetl plik

@ -12,8 +12,6 @@ import { expandTimelineSuccess } from 'soapbox/actions/timelines.ts';
import { Column } from 'soapbox/components/ui/column.tsx';
import Timeline from 'soapbox/features/ui/components/timeline.tsx';
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
import { useIsMobile } from 'soapbox/hooks/useIsMobile.ts';
import { useTheme } from 'soapbox/hooks/useTheme.ts';
const messages = defineMessages({
title: { id: 'column.test', defaultMessage: 'Test timeline' },
@ -31,8 +29,6 @@ const onlyMedia = false;
const TestTimeline: React.FC = () => {
const intl = useIntl();
const dispatch = useAppDispatch();
const theme = useTheme();
const isMobile = useIsMobile();
useEffect(() => {
dispatch(importFetchedStatuses(MOCK_STATUSES));
@ -40,12 +36,11 @@ const TestTimeline: React.FC = () => {
}, []);
return (
<Column label={intl.formatMessage(messages.title)} transparent={!isMobile}>
<Column label={intl.formatMessage(messages.title)} slim>
<Timeline
scrollKey={`${timelineId}_timeline`}
timelineId={`${timelineId}${onlyMedia ? ':media' : ''}`}
emptyMessage={<FormattedMessage id='empty_column.test' defaultMessage='The test timeline is empty.' />}
divideType={(theme === 'black' || isMobile) ? 'border' : 'space'}
/>
</Column>
);

Wyświetl plik

@ -1,15 +0,0 @@
import clsx from 'clsx';
interface IBackgroundShapes {
/** Whether the shapes should be absolute positioned or fixed. */
position?: 'fixed' | 'absolute';
}
/** Gradient that appears in the background of the UI. */
const BackgroundShapes: React.FC<IBackgroundShapes> = ({ position = 'fixed' }) => (
<div className={clsx(position, 'pointer-events-none inset-x-0 top-0 flex justify-center overflow-hidden black:hidden')}>
<div className='bg-gradient-sm lg:bg-gradient-light lg:dark:bg-gradient-dark h-screen w-screen' />
</div>
);
export default BackgroundShapes;

Wyświetl plik

@ -2,7 +2,7 @@ import { Card, CardBody } from 'soapbox/components/ui/card.tsx';
import Spinner from 'soapbox/components/ui/spinner.tsx';
const ColumnLoading = () => (
<Card variant='rounded'>
<Card>
<CardBody>
<Spinner />
</CardBody>

Wyświetl plik

@ -21,7 +21,6 @@ import Search from 'soapbox/features/compose/components/search.tsx';
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
import { useFeatures } from 'soapbox/hooks/useFeatures.ts';
import { useInstance } from 'soapbox/hooks/useInstance.ts';
import { useIsMobile } from 'soapbox/hooks/useIsMobile.ts';
import { useOwnAccount } from 'soapbox/hooks/useOwnAccount.ts';
import { useRegistrationStatus } from 'soapbox/hooks/useRegistrationStatus.ts';
import { useSettingsNotifications } from 'soapbox/hooks/useSettingsNotifications.ts';
@ -44,7 +43,6 @@ const Navbar = () => {
const { isOpen } = useRegistrationStatus();
const { account } = useOwnAccount();
const node = useRef(null);
const isMobile = useIsMobile();
const settingsNotifications = useSettingsNotifications();
const [isLoading, setLoading] = useState<boolean>(false);
@ -87,10 +85,7 @@ const Navbar = () => {
return (
<nav
className={clsx(
'sticky top-0 z-50 border-gray-200 bg-white shadow black:border-b black:border-b-gray-800 black:bg-black dark:border-gray-800 dark:bg-primary-900',
{ 'border-b': isMobile },
)}
className='border-b border-gray-200 bg-white shadow black:border-gray-800 black:bg-black dark:border-gray-800 dark:bg-primary-900'
ref={node}
data-testid='navbar'
>

Wyświetl plik

@ -65,7 +65,6 @@ const PendingStatus: React.FC<IPendingStatus> = ({ idempotencyKey, className, mu
'py-6 sm:p-5': !thread,
'status-reply': !!status.in_reply_to_id,
})}
variant={thread ? 'default' : 'rounded'}
>
<div className='mb-4'>
<HStack justifyContent='between' alignItems='start'>

Wyświetl plik

@ -1,4 +1,4 @@
import { useFloating } from '@floating-ui/react';
import { Placement, useFloating } from '@floating-ui/react';
import logoutIcon from '@tabler/icons/outline/logout.svg';
import plusIcon from '@tabler/icons/outline/plus.svg';
import clsx from 'clsx';
@ -29,6 +29,7 @@ const messages = defineMessages({
interface IProfileDropdown {
account: AccountEntity;
children: React.ReactNode;
placement?: Placement;
}
type IMenuItem = {
@ -39,13 +40,13 @@ type IMenuItem = {
action?: (event: React.MouseEvent) => void;
}
const ProfileDropdown: React.FC<IProfileDropdown> = ({ account, children }) => {
const ProfileDropdown: React.FC<IProfileDropdown> = ({ account, children, placement = 'bottom-end' }) => {
const dispatch = useAppDispatch();
const features = useFeatures();
const intl = useIntl();
const [visible, setVisible] = useState(false);
const { x, y, strategy, refs } = useFloating<HTMLButtonElement>({ placement: 'bottom-end' });
const { x, y, strategy, refs } = useFloating<HTMLButtonElement>({ placement });
const getOtherAccounts = useCallback(makeGetOtherAccounts(), []);
const otherAccounts = useAppSelector((state) => getOtherAccounts(state));
@ -117,7 +118,7 @@ const ProfileDropdown: React.FC<IProfileDropdown> = ({ account, children }) => {
return (
<>
<button
className='rounded-full focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 dark:ring-gray-800 dark:ring-offset-0 dark:focus:ring-primary-500'
className='w-full rounded-full focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 dark:ring-gray-800 dark:ring-offset-0 dark:focus:ring-primary-500'
type='button'
ref={refs.setReference}
onClick={toggleVisible}

Wyświetl plik

@ -127,7 +127,7 @@ const ProfileInfoPanel: React.FC<IProfileInfoPanel> = ({ account, username }) =>
if (!account) {
return (
<div className='mt-6 min-w-0 flex-1 sm:px-2'>
<div>
<Stack space={2}>
<Stack>
<HStack space={1} alignItems='center'>
@ -147,7 +147,7 @@ const ProfileInfoPanel: React.FC<IProfileInfoPanel> = ({ account, username }) =>
const badges = getBadges();
return (
<div className='mt-6 min-w-0 flex-1 sm:px-2'>
<div>
<Stack space={2}>
<Stack>
<HStack space={1} alignItems='center'>

Wyświetl plik

@ -1,116 +0,0 @@
import { FormattedMessage, useIntl } from 'react-intl';
import { Link } from 'react-router-dom';
import { useAccount } from 'soapbox/api/hooks/index.ts';
import StillImage from 'soapbox/components/still-image.tsx';
import Avatar from 'soapbox/components/ui/avatar.tsx';
import HStack from 'soapbox/components/ui/hstack.tsx';
import Stack from 'soapbox/components/ui/stack.tsx';
import Text from 'soapbox/components/ui/text.tsx';
import VerificationBadge from 'soapbox/components/verification-badge.tsx';
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
import { getAcct } from 'soapbox/utils/accounts.ts';
import { emojifyText } from 'soapbox/utils/emojify.tsx';
import { shortNumberFormat } from 'soapbox/utils/numbers.tsx';
import { displayFqn } from 'soapbox/utils/state.ts';
interface IUserPanel {
accountId: string;
action?: JSX.Element;
badges?: JSX.Element[];
domain?: string;
}
const UserPanel: React.FC<IUserPanel> = ({ accountId, action, badges, domain }) => {
const intl = useIntl();
const { account } = useAccount(accountId);
const fqn = useAppSelector((state) => displayFqn(state));
if (!account) return null;
const acct = !account.acct.includes('@') && domain ? `${account.acct}@${domain}` : account.acct;
const header = account.header;
const verified = account.verified;
return (
<div className='relative'>
<Stack space={2}>
<Stack>
<div className='relative -mx-4 -mt-4 h-24 overflow-hidden bg-gray-200'>
{header && (
<StillImage src={account.header} />
)}
</div>
<HStack justifyContent='between'>
<Link
to={`/@${account.acct}`}
title={acct}
className='-mt-12 block'
>
<Avatar src={account.avatar} size={80} className='size-20 overflow-hidden bg-gray-50 ring-2 ring-white' />
</Link>
{action && (
<div className='mt-2'>{action}</div>
)}
</HStack>
</Stack>
<Stack>
<Link to={`/@${account.acct}`}>
<HStack space={1} alignItems='center'>
<Text size='lg' weight='bold' truncate>
{emojifyText(account.display_name, account.emojis)}
</Text>
{verified && <VerificationBadge />}
{badges && badges.length > 0 && (
<HStack space={1} alignItems='center'>
{badges}
</HStack>
)}
</HStack>
</Link>
<HStack>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<Text size='sm' theme='muted' direction='ltr' truncate>
@{getAcct(account, fqn)}
</Text>
</HStack>
</Stack>
<HStack alignItems='center' space={3}>
{account.followers_count >= 0 && (
<Link to={`/@${account.acct}/followers`} title={intl.formatNumber(account.followers_count)}>
<HStack alignItems='center' space={1}>
<Text theme='primary' weight='bold' size='sm'>
{shortNumberFormat(account.followers_count)}
</Text>
<Text weight='bold' size='sm'>
<FormattedMessage id='account.followers' defaultMessage='Followers' />
</Text>
</HStack>
</Link>
)}
{account.following_count >= 0 && (
<Link to={`/@${account.acct}/following`} title={intl.formatNumber(account.following_count)}>
<HStack alignItems='center' space={1}>
<Text theme='primary' weight='bold' size='sm'>
{shortNumberFormat(account.following_count)}
</Text>
<Text weight='bold' size='sm'>
<FormattedMessage id='account.follows' defaultMessage='Following' />
</Text>
</HStack>
</Link>
)}
</HStack>
</Stack>
</div>
);
};
export default UserPanel;

Wyświetl plik

@ -43,7 +43,6 @@ import SearchPage from 'soapbox/pages/search-page.tsx';
import StatusPage from 'soapbox/pages/status-page.tsx';
import WidePage from 'soapbox/pages/wide-page.tsx';
import BackgroundShapes from './components/background-shapes.tsx';
import FloatingActionButton from './components/floating-action-button.tsx';
import Navbar from './components/navbar.tsx';
import {
@ -505,10 +504,10 @@ const UI: React.FC<IUI> = ({ children }) => {
})}
/>
<BackgroundShapes />
<div className='z-10 flex min-h-screen flex-col'>
<div className='sticky top-0 z-50 sm:hidden'>
<Navbar />
</div>
<Layout>
<Layout.Sidebar>

Wyświetl plik

@ -82,7 +82,6 @@ export const ServerInfo = lazy(() => import('soapbox/features/server-info/index.
export const Dashboard = lazy(() => import('soapbox/features/admin/index.tsx'));
export const ModerationLog = lazy(() => import('soapbox/features/admin/moderation-log.tsx'));
export const ThemeEditor = lazy(() => import('soapbox/features/theme-editor/index.tsx'));
export const UserPanel = lazy(() => import('soapbox/features/ui/components/user-panel.tsx'));
export const PromoPanel = lazy(() => import('soapbox/features/ui/components/promo-panel.tsx'));
export const SignUpPanel = lazy(() => import('soapbox/features/ui/components/panels/sign-up-panel.tsx'));
export const CtaBanner = lazy(() => import('soapbox/features/ui/components/cta-banner.tsx'));

Wyświetl plik

@ -25,7 +25,7 @@ const SoapboxHead: React.FC<ISoapboxHead> = ({ children }) => {
const themeCss = generateThemeCss(demo ? normalizeSoapboxConfig({ brandColor: '#0482d8' }) : soapboxConfig);
const dsn = soapboxConfig.sentryDsn;
const bodyClass = clsx('h-full bg-white text-base black:bg-black dark:bg-gray-800', {
const bodyClass = clsx('h-full bg-white text-base black:bg-black dark:bg-primary-900', {
'no-reduce-motion': !reduceMotion,
'underline-links': underlineLinks,
'demetricator': demetricator,

Wyświetl plik

@ -1,13 +1,14 @@
import clsx from 'clsx';
import { useRef } from 'react';
import { useIntl } from 'react-intl';
import { Link } from 'react-router-dom';
import { FormattedMessage, useIntl } from 'react-intl';
import { Link, useLocation } from 'react-router-dom';
import { uploadCompose } from 'soapbox/actions/compose.ts';
import Avatar from 'soapbox/components/ui/avatar.tsx';
import { Card, CardBody } from 'soapbox/components/ui/card.tsx';
import HStack from 'soapbox/components/ui/hstack.tsx';
import Layout from 'soapbox/components/ui/layout.tsx';
import Tabs from 'soapbox/components/ui/tabs.tsx';
import LinkFooter from 'soapbox/features/ui/components/link-footer.tsx';
import {
WhoToFollowPanel,
@ -24,11 +25,11 @@ import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
import { useDraggedFiles } from 'soapbox/hooks/useDraggedFiles.ts';
import { useFeatures } from 'soapbox/hooks/useFeatures.ts';
import { useInstance } from 'soapbox/hooks/useInstance.ts';
import { useIsMobile } from 'soapbox/hooks/useIsMobile.ts';
import { useOwnAccount } from 'soapbox/hooks/useOwnAccount.ts';
import { useSoapboxConfig } from 'soapbox/hooks/useSoapboxConfig.ts';
import ComposeForm from '../features/compose/components/compose-form.tsx';
interface IHomePage {
@ -38,11 +39,13 @@ interface IHomePage {
const HomePage: React.FC<IHomePage> = ({ children }) => {
const intl = useIntl();
const dispatch = useAppDispatch();
const { pathname } = useLocation();
const me = useAppSelector(state => state.me);
const { account } = useOwnAccount();
const features = useFeatures();
const soapboxConfig = useSoapboxConfig();
const { instance } = useInstance();
const composeId = 'home';
const composeBlock = useRef<HTMLDivElement>(null);
@ -61,15 +64,14 @@ const HomePage: React.FC<IHomePage> = ({ children }) => {
return (
<>
<Layout.Main className={clsx('black:space-y-0 dark:divide-gray-800', { 'pt-3 sm:pt-0 space-y-3': !isMobile })}>
<Layout.Main className={clsx('space-y-0 dark:divide-gray-800')}>
{me && (
<Card
className={clsx('relative z-[1] border-gray-200 transition black:border-b black:border-gray-800 dark:border-gray-800', {
className={clsx('relative z-[1] border-b border-gray-200 transition black:border-gray-800 dark:border-gray-800', {
'border-2 border-primary-600 border-dashed z-[99]': isDragging,
'ring-2 ring-offset-2 ring-primary-600': isDraggedOver,
'border-b': isMobile,
})}
variant='rounded'
ref={composeBlock}
>
<CardBody>
@ -91,6 +93,16 @@ const HomePage: React.FC<IHomePage> = ({ children }) => {
</Card>
)}
<div className='sticky top-12 z-20 bg-white/90 backdrop-blur black:bg-black/90 dark:bg-primary-900/90 lg:top-0'>
<Tabs
items={[
{ name: 'home', text: <FormattedMessage id='tabs_bar.home' defaultMessage='Home' />, to: '/' },
{ name: 'local', text: <div className='block max-w-xs truncate'>{instance.domain}</div>, to: '/timeline/local' },
]}
activeItem={pathname === '/timeline/local' ? 'local' : 'home'}
/>
</div>
{children}
{!me && (
@ -105,22 +117,22 @@ const HomePage: React.FC<IHomePage> = ({ children }) => {
{me && features.announcements && (
<AnnouncementsPanel />
)}
{(hasCrypto && cryptoLimit > 0 && me) && (
<CryptoDonatePanel limit={cryptoLimit} />
)}
{(hasPatron && me) && (
<FundingPanel />
)}
{features.birthdays && (
<BirthdayPanel limit={10} />
)}
{features.trends && (
<TrendsPanel limit={5} />
)}
{features.suggestions && (
<WhoToFollowPanel limit={3} />
)}
{features.birthdays && (
<BirthdayPanel limit={10} />
)}
<PromoPanel />
{(hasCrypto && cryptoLimit > 0 && me) && (
<CryptoDonatePanel limit={cryptoLimit} />
)}
{(hasPatron && me) && (
<FundingPanel />
)}
<LinkFooter />
</Layout.Aside>
</>

Wyświetl plik

@ -4,6 +4,7 @@ import { Redirect, useHistory } from 'react-router-dom';
import { useAccountLookup } from 'soapbox/api/hooks/index.ts';
import { Column } from 'soapbox/components/ui/column.tsx';
import Layout from 'soapbox/components/ui/layout.tsx';
import Stack from 'soapbox/components/ui/stack.tsx';
import Tabs from 'soapbox/components/ui/tabs.tsx';
import Header from 'soapbox/features/account/components/header.tsx';
import LinkFooter from 'soapbox/features/ui/components/link-footer.tsx';
@ -91,17 +92,20 @@ const ProfilePage: React.FC<IProfilePage> = ({ params, children }) => {
return (
<>
<Layout.Main>
<Column size='lg' label={account ? `@${getAcct(account, displayFqn)}` : ''} withHeader={false}>
<div className='space-y-4'>
<Column size='lg' label={account ? `@${getAcct(account, displayFqn)}` : ''} withHeader={false} slim>
<Stack space={4}>
<Header account={account} />
<Stack space={4} className='px-6'>
<ProfileInfoPanel username={username} account={account} />
{account && showTabs && (
<Tabs key={`profile-tabs-${account.id}`} items={tabItems} activeItem={activeItem} />
)}
</Stack>
{children}
</div>
</Stack>
</Column>
{!me && (