Merge branch 'redesign-repost' into 'develop'

Move status info to above Account

See merge request soapbox-pub/soapbox!2120
environments/review-develop-3zknud/deployments/2196
Chewbacca 2023-01-09 17:42:52 +00:00
commit 63201c4acf
4 zmienionych plików z 117 dodań i 87 usunięć

Wyświetl plik

@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Posts: letterbox images to 19:6 again.
- Status Info: moved context (repost, pinned) to improve UX.
### Fixed
- Layout: use accent color for "floating action button" (mobile compose button).

Wyświetl plik

@ -2,7 +2,7 @@ import classNames from 'clsx';
import React, { useEffect, useRef, useState } from 'react';
import { HotKeys } from 'react-hotkeys';
import { useIntl, FormattedMessage, defineMessages } from 'react-intl';
import { NavLink, useHistory } from 'react-router-dom';
import { useHistory } from 'react-router-dom';
import { mentionCompose, replyCompose } from 'soapbox/actions/compose';
import { toggleFavourite, toggleReblog } from 'soapbox/actions/interactions';
@ -21,7 +21,8 @@ import StatusContent from './status-content';
import StatusMedia from './status-media';
import StatusReplyMentions from './status-reply-mentions';
import SensitiveContentOverlay from './statuses/sensitive-content-overlay';
import { Card, HStack, Stack, Text } from './ui';
import StatusInfo from './statuses/status-info';
import { Card, Stack, Text } from './ui';
import type {
Account as AccountEntity,
@ -37,6 +38,7 @@ const messages = defineMessages({
export interface IStatus {
id?: string,
avatarSize?: number,
status: StatusEntity,
onClick?: () => void,
muted?: boolean,
@ -56,6 +58,8 @@ export interface IStatus {
const Status: React.FC<IStatus> = (props) => {
const {
status,
accountAction,
avatarSize = 42,
focusable = true,
hoverable = true,
onClick,
@ -84,7 +88,7 @@ const Status: React.FC<IStatus> = (props) => {
const [minHeight, setMinHeight] = useState(208);
const actualStatus = getActualStatus(status);
const isReblog = status.reblog && typeof status.reblog === 'object';
const statusUrl = `/@${actualStatus.getIn(['account', 'acct'])}/posts/${actualStatus.id}`;
// Track height changes we know about to compensate scrolling.
@ -201,8 +205,49 @@ const Status: React.FC<IStatus> = (props) => {
firstEmoji?.focus();
};
const renderStatusInfo = () => {
if (isReblog) {
return (
<StatusInfo
avatarSize={avatarSize}
to={`/@${status.getIn(['account', 'acct'])}`}
icon={<Icon src={require('@tabler/icons/repeat.svg')} className='text-green-600' />}
text={
<FormattedMessage
id='status.reblogged_by'
defaultMessage='{name} reposted'
values={{
name: (
<bdi className='truncate pr-1 rtl:pl-1'>
<strong
className='text-gray-800 dark:text-gray-200'
dangerouslySetInnerHTML={{
__html: String(status.getIn(['account', 'display_name_html'])),
}}
/>
</bdi>
),
}}
/>
}
/>
);
} else if (featured) {
return (
<StatusInfo
avatarSize={avatarSize}
icon={<Icon src={require('@tabler/icons/pinned.svg')} className='text-gray-600 dark:text-gray-400' />}
text={
<Text size='xs' theme='muted' weight='medium'>
<FormattedMessage id='status.pinned' defaultMessage='Pinned post' />
</Text>
}
/>
);
}
};
if (!status) return null;
let rebloggedByText, reblogElement, reblogElementMobile;
if (hidden) {
return (
@ -228,55 +273,8 @@ const Status: React.FC<IStatus> = (props) => {
);
}
let rebloggedByText;
if (status.reblog && typeof status.reblog === 'object') {
const displayNameHtml = { __html: String(status.getIn(['account', 'display_name_html'])) };
reblogElement = (
<NavLink
to={`/@${status.getIn(['account', 'acct'])}`}
onClick={(event) => event.stopPropagation()}
className='hidden sm:flex items-center text-gray-700 dark:text-gray-600 text-xs font-medium space-x-1 rtl:space-x-reverse hover:underline'
>
<Icon src={require('@tabler/icons/repeat.svg')} className='text-green-600' />
<HStack alignItems='center'>
<FormattedMessage
id='status.reblogged_by'
defaultMessage='{name} reposted'
values={{
name: <bdi className='max-w-[100px] truncate pr-1 rtl:px-1'>
<strong className='text-gray-800 dark:text-gray-200' dangerouslySetInnerHTML={displayNameHtml} />
</bdi>,
}}
/>
</HStack>
</NavLink>
);
reblogElementMobile = (
<div className='pb-5 -mt-2 sm:hidden truncate'>
<NavLink
to={`/@${status.getIn(['account', 'acct'])}`}
onClick={(event) => event.stopPropagation()}
className='flex items-center text-gray-700 dark:text-gray-600 text-xs font-medium space-x-1 hover:underline'
>
<Icon src={require('@tabler/icons/repeat.svg')} className='text-green-600' />
<span>
<FormattedMessage
id='status.reblogged_by'
defaultMessage='{name} reposted'
values={{
name: <bdi>
<strong className='text-gray-800 dark:text-gray-200' dangerouslySetInnerHTML={displayNameHtml} />
</bdi>,
}}
/>
</span>
</NavLink>
</div>
);
rebloggedByText = intl.formatMessage(
messages.reblogged_by,
{ name: String(status.getIn(['account', 'acct'])) },
@ -312,8 +310,6 @@ const Status: React.FC<IStatus> = (props) => {
react: handleHotkeyReact,
};
const accountAction = props.accountAction || reblogElement;
const isUnderReview = actualStatus.visibility === 'self';
const isSensitive = actualStatus.hidden;
@ -328,21 +324,9 @@ const Status: React.FC<IStatus> = (props) => {
onClick={handleClick}
role='link'
>
{featured && (
<div className='pt-4 px-4'>
<HStack alignItems='center' space={1}>
<Icon src={require('@tabler/icons/pinned.svg')} className='text-gray-600 dark:text-gray-400' />
<Text size='sm' theme='muted' weight='medium'>
<FormattedMessage id='status.pinned' defaultMessage='Pinned post' />
</Text>
</HStack>
</div>
)}
<Card
variant={variant}
className={classNames('status__wrapper', `status-${actualStatus.visibility}`, {
className={classNames('status__wrapper space-y-4', `status-${actualStatus.visibility}`, {
'py-6 sm:p-5': variant === 'rounded',
'status-reply': !!status.in_reply_to_id,
muted,
@ -350,21 +334,20 @@ const Status: React.FC<IStatus> = (props) => {
})}
data-id={status.id}
>
{reblogElementMobile}
{renderStatusInfo()}
<div className='mb-4'>
<AccountContainer
key={String(actualStatus.getIn(['account', 'id']))}
id={String(actualStatus.getIn(['account', 'id']))}
timestamp={actualStatus.created_at}
timestampUrl={statusUrl}
action={accountAction}
hideActions={!accountAction}
showEdit={!!actualStatus.edited_at}
showProfileHoverCard={hoverable}
withLinkToProfile={hoverable}
/>
</div>
<AccountContainer
key={String(actualStatus.getIn(['account', 'id']))}
id={String(actualStatus.getIn(['account', 'id']))}
timestamp={actualStatus.created_at}
timestampUrl={statusUrl}
action={accountAction}
hideActions={!accountAction}
showEdit={!!actualStatus.edited_at}
showProfileHoverCard={hoverable}
withLinkToProfile={hoverable}
avatarSize={avatarSize}
/>
<div className='status__content-wrapper'>
<StatusReplyMentions status={actualStatus} hoverable={hoverable} />

Wyświetl plik

@ -0,0 +1,38 @@
import React from 'react';
import { Link } from 'react-router-dom';
interface IStatusInfo {
avatarSize: number
to?: string
icon: React.ReactNode
text: React.ReactNode
}
const StatusInfo = (props: IStatusInfo) => {
const { avatarSize, to, icon, text } = props;
const onClick = (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
event.stopPropagation();
};
const Container = to ? Link : 'div';
const containerProps: any = to ? { onClick, to } : {};
return (
<Container
{...containerProps}
className='flex items-center text-gray-700 dark:text-gray-600 text-xs font-medium space-x-3 rtl:space-x-reverse hover:underline'
>
<div
className='flex justify-end'
style={{ width: avatarSize }}
>
{icon}
</div>
{text}
</Container>
);
};
export default StatusInfo;

Wyświetl plik

@ -151,6 +151,8 @@ const buildMessage = (
});
};
const avatarSize = 48;
interface INotificaton {
hidden?: boolean,
notification: NotificationEntity,
@ -290,7 +292,7 @@ const Notification: React.FC<INotificaton> = (props) => {
<AccountContainer
id={account.id}
hidden={hidden}
avatarSize={48}
avatarSize={avatarSize}
/>
) : null;
case 'follow_request':
@ -298,7 +300,7 @@ const Notification: React.FC<INotificaton> = (props) => {
<AccountContainer
id={account.id}
hidden={hidden}
avatarSize={48}
avatarSize={avatarSize}
actionType='follow_request'
/>
) : null;
@ -307,7 +309,7 @@ const Notification: React.FC<INotificaton> = (props) => {
<AccountContainer
id={notification.target.id}
hidden={hidden}
avatarSize={48}
avatarSize={avatarSize}
/>
) : null;
case 'favourite':
@ -327,6 +329,7 @@ const Notification: React.FC<INotificaton> = (props) => {
hidden={hidden}
onMoveDown={handleMoveDown}
onMoveUp={handleMoveUp}
avatarSize={avatarSize}
/>
) : null;
default:
@ -358,13 +361,18 @@ const Notification: React.FC<INotificaton> = (props) => {
>
<div className='p-4 focusable'>
<div className='mb-2'>
<HStack alignItems='center' space={1.5}>
{renderIcon()}
<HStack alignItems='center' space={3}>
<div
className='flex justify-end'
style={{ flexBasis: avatarSize }}
>
{renderIcon()}
</div>
<div className='truncate'>
<Text
theme='muted'
size='sm'
size='xs'
truncate
data-testid='message'
>