mirror of
https://github.com/mastodon/mastodon.git
synced 2026-09-12 23:26:23 -05:00
Composer redesign: Replies (#40186)
Co-authored-by: diondiondion <mail@diondiondion.com>
This commit is contained in:
@@ -17,7 +17,7 @@ import { EmojiHTML } from '../emoji/html';
|
||||
import { Icon } from '../icon';
|
||||
import { Poll } from '../poll';
|
||||
|
||||
import { useElementHandledLink } from './handled_link';
|
||||
import { useHandlersForStatus } from './hooks';
|
||||
|
||||
const MAX_HEIGHT = 706; // 22px * 32 (+ 2px padding at the top)
|
||||
|
||||
@@ -72,20 +72,7 @@ export const StatusContent: React.FC<{
|
||||
[onClick],
|
||||
);
|
||||
|
||||
const hrefToMention = useCallback(
|
||||
(href: string) => status?.mentions.find((item) => item.url === href),
|
||||
[status?.mentions],
|
||||
);
|
||||
const hrefToCollectionId = useCallback(
|
||||
(href: string) =>
|
||||
status?.tagged_collections.find((item) => item.url === href)?.id,
|
||||
[status?.tagged_collections],
|
||||
);
|
||||
const htmlHandlers = useElementHandledLink({
|
||||
hashtagAccountId: status?.account,
|
||||
hrefToCollectionId,
|
||||
hrefToMention,
|
||||
});
|
||||
const htmlHandlers = useHandlersForStatus(status);
|
||||
|
||||
if (!status) {
|
||||
return null;
|
||||
|
||||
@@ -11,12 +11,16 @@ import { openModal } from '@/mastodon/actions/modal';
|
||||
import { toggleStatusSpoilers } from '@/mastodon/actions/statuses';
|
||||
import { useExpandedStatus } from '@/mastodon/hooks/useStatus';
|
||||
import { useToggle } from '@/mastodon/hooks/useToggle';
|
||||
import type { ExpandedStatusShape } from '@/mastodon/models/status';
|
||||
import type {
|
||||
ExpandedStatusShape,
|
||||
StatusShape,
|
||||
} from '@/mastodon/models/status';
|
||||
import { selectStatusFilters } from '@/mastodon/selectors/filters';
|
||||
import { useAppSelector, useAppDispatch } from '@/mastodon/store';
|
||||
|
||||
import { FOCUS_TARGET } from '../navigation_focus_target';
|
||||
|
||||
import { useElementHandledLink } from './handled_link';
|
||||
import type { StatusContextType } from './types';
|
||||
|
||||
const messages = defineMessages({
|
||||
@@ -260,3 +264,28 @@ export function useTextForScreenReader({
|
||||
return values.join(', ');
|
||||
}, [intl, isQuote, reblogAcct, status]);
|
||||
}
|
||||
|
||||
export function useHandlersForStatus(
|
||||
status?: Pick<
|
||||
StatusShape | ExpandedStatusShape,
|
||||
'account' | 'mentions' | 'tagged_collections'
|
||||
> | null,
|
||||
) {
|
||||
const hrefToMention = useCallback(
|
||||
(href: string) => status?.mentions.find((item) => item.url === href),
|
||||
[status?.mentions],
|
||||
);
|
||||
const hrefToCollectionId = useCallback(
|
||||
(href: string) =>
|
||||
status?.tagged_collections.find((item) => item.url === href)?.id,
|
||||
[status?.tagged_collections],
|
||||
);
|
||||
return useElementHandledLink({
|
||||
hashtagAccountId:
|
||||
typeof status?.account === 'string'
|
||||
? status.account
|
||||
: status?.account.acct,
|
||||
hrefToCollectionId,
|
||||
hrefToMention,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import type { OnEmojiPick } from './emoji';
|
||||
import { ComposeFooter } from './footer';
|
||||
import { ComposeFormHeader } from './header';
|
||||
import { LanguageButton } from './language';
|
||||
import { ComposeReply } from './reply';
|
||||
import {
|
||||
selectComposeCanSubmit,
|
||||
selectComposeSensitive,
|
||||
@@ -87,6 +88,8 @@ export const RedesignComposeForm: React.FC<RedesignComposeFormProps> = ({
|
||||
>
|
||||
<ComposeFormHeader id={titleId} noMinimize={noMinimize} />
|
||||
|
||||
<ComposeReply />
|
||||
|
||||
<div className={classes.toolbar}>
|
||||
<div className={classes.flexGrowWrap}>
|
||||
{type !== 'message' && <ComposeVisibility />}
|
||||
|
||||
55
app/javascript/mastodon/features/compose/redesign/reply.tsx
Normal file
55
app/javascript/mastodon/features/compose/redesign/reply.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { Avatar } from '@/mastodon/components/avatar';
|
||||
import { LinkedDisplayName } from '@/mastodon/components/display_name';
|
||||
import { EmojiHTML } from '@/mastodon/components/emoji/html';
|
||||
import { RelativeTimestamp } from '@/mastodon/components/relative_timestamp';
|
||||
import { useHandlersForStatus } from '@/mastodon/components/status/hooks';
|
||||
import { selectAccountStatus } from '@/mastodon/selectors/statuses';
|
||||
import { useAppSelector } from '@/mastodon/store';
|
||||
|
||||
import classes from './styles.module.scss';
|
||||
|
||||
export const ComposeReply: React.FC = () => {
|
||||
const replyId = useAppSelector(
|
||||
(state) => state.compose.get('in_reply_to') as null | string,
|
||||
);
|
||||
const status = useAppSelector((state) => selectAccountStatus(state, replyId));
|
||||
|
||||
const htmlHandlers = useHandlersForStatus(status);
|
||||
|
||||
if (!status) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<figure className={classes.reply}>
|
||||
<figcaption className={classes.replyAccount}>
|
||||
<Avatar
|
||||
account={status.account}
|
||||
className={classes.replyAvatar}
|
||||
withLink
|
||||
/>
|
||||
<LinkedDisplayName
|
||||
displayProps={{ account: status.account, variant: 'simple' }}
|
||||
/>
|
||||
<span className={classes.replyTime}>
|
||||
·
|
||||
<Link to={`/@${status.account.acct}/${status.id}`}>
|
||||
<RelativeTimestamp timestamp={status.created_at} />
|
||||
</Link>
|
||||
</span>
|
||||
</figcaption>
|
||||
|
||||
<EmojiHTML
|
||||
as='blockquote'
|
||||
cite={status.uri}
|
||||
htmlString={status.translation?.contentHtml ?? status.contentHtml}
|
||||
extraEmojis={status.emojis}
|
||||
className={classes.replyText}
|
||||
lang={status.translation?.language ?? status.language}
|
||||
{...htmlHandlers}
|
||||
/>
|
||||
</figure>
|
||||
);
|
||||
};
|
||||
@@ -187,6 +187,51 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Reply
|
||||
|
||||
.reply {
|
||||
border-inline-start: 0.5px solid var(--color-border-primary);
|
||||
padding: 0 var(--space-md);
|
||||
}
|
||||
|
||||
.replyAccount {
|
||||
display: flex;
|
||||
margin-bottom: var(--space-2xs);
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.replyAvatar {
|
||||
margin-inline-end: var(--space-xs);
|
||||
border-radius: var(--radius-round);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.replyTime {
|
||||
color: var(--color-text-tertiary);
|
||||
}
|
||||
|
||||
.replyText {
|
||||
@include mixins.line-clamp(2);
|
||||
@include mixins.type-body-compact;
|
||||
|
||||
// Round two lines to the nearest 5px.
|
||||
max-height: round(2lh, 5px);
|
||||
|
||||
a {
|
||||
color: var(--color-text-brand);
|
||||
text-decoration: none;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Attachments
|
||||
|
||||
.mediaSingle {
|
||||
|
||||
@@ -156,3 +156,15 @@
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utilities
|
||||
*/
|
||||
|
||||
@mixin line-clamp($lines) {
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: $lines;
|
||||
line-clamp: $lines;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user