mirror of
https://github.com/mastodon/mastodon.git
synced 2025-09-07 18:31:07 +00:00
Change design of quote posts in web UI (#35584)
This commit is contained in:
parent
39250ab961
commit
92bf55afd0
63
app/javascript/mastodon/components/learn_more_link.tsx
Normal file
63
app/javascript/mastodon/components/learn_more_link.tsx
Normal file
|
@ -0,0 +1,63 @@
|
|||
import { useState, useRef, useCallback, useId } from 'react';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import Overlay from 'react-overlays/Overlay';
|
||||
|
||||
export const LearnMoreLink: React.FC<{ children: React.ReactNode }> = ({
|
||||
children,
|
||||
}) => {
|
||||
const accessibilityId = useId();
|
||||
const [open, setOpen] = useState(false);
|
||||
const triggerRef = useRef(null);
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
setOpen(!open);
|
||||
}, [open, setOpen]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
className='link-button'
|
||||
ref={triggerRef}
|
||||
onClick={handleClick}
|
||||
aria-expanded={open}
|
||||
aria-controls={accessibilityId}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='learn_more_link.learn_more'
|
||||
defaultMessage='Learn more'
|
||||
/>
|
||||
</button>
|
||||
|
||||
<Overlay
|
||||
show={open}
|
||||
rootClose
|
||||
onHide={handleClick}
|
||||
offset={[5, 5]}
|
||||
placement='bottom-end'
|
||||
target={triggerRef}
|
||||
>
|
||||
{({ props }) => (
|
||||
<div
|
||||
{...props}
|
||||
role='region'
|
||||
id={accessibilityId}
|
||||
className='account__domain-pill__popout learn-more__popout dropdown-animation'
|
||||
>
|
||||
<div className='learn-more__popout__content'>{children}</div>
|
||||
|
||||
<div>
|
||||
<button className='link-button' onClick={handleClick}>
|
||||
<FormattedMessage
|
||||
id='learn_more_link.got_it'
|
||||
defaultMessage='Got it'
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Overlay>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -138,6 +138,16 @@ class StatusContent extends PureComponent {
|
|||
|
||||
onCollapsedToggle(collapsed);
|
||||
}
|
||||
|
||||
// Remove quote fallback link from the DOM so it doesn't
|
||||
// mess with paragraph margins
|
||||
if (!!status.get('quote')) {
|
||||
const inlineQuote = node.querySelector('.quote-inline');
|
||||
|
||||
if (inlineQuote) {
|
||||
inlineQuote.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleMouseEnter = ({ currentTarget }) => {
|
||||
|
|
|
@ -3,19 +3,15 @@ import { useEffect, useMemo } from 'react';
|
|||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import type { Map as ImmutableMap } from 'immutable';
|
||||
|
||||
import ArticleIcon from '@/material-icons/400-24px/article.svg?react';
|
||||
import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { LearnMoreLink } from 'mastodon/components/learn_more_link';
|
||||
import StatusContainer from 'mastodon/containers/status_container';
|
||||
import type { Status } from 'mastodon/models/status';
|
||||
import type { RootState } from 'mastodon/store';
|
||||
import { useAppDispatch, useAppSelector } from 'mastodon/store';
|
||||
|
||||
import QuoteIcon from '../../images/quote.svg?react';
|
||||
import { fetchStatus } from '../actions/statuses';
|
||||
import { makeGetStatus } from '../selectors';
|
||||
|
||||
|
@ -31,7 +27,6 @@ const QuoteWrapper: React.FC<{
|
|||
'status__quote--error': isError,
|
||||
})}
|
||||
>
|
||||
<Icon id='quote' icon={QuoteIcon} className='status__quote-icon' />
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
@ -45,27 +40,20 @@ const NestedQuoteLink: React.FC<{
|
|||
accountId ? state.accounts.get(accountId) : undefined,
|
||||
);
|
||||
|
||||
const quoteAuthorName = account?.display_name_html;
|
||||
const quoteAuthorName = account?.acct;
|
||||
|
||||
if (!quoteAuthorName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const quoteAuthorElement = (
|
||||
<span dangerouslySetInnerHTML={{ __html: quoteAuthorName }} />
|
||||
);
|
||||
const quoteUrl = `/@${account.get('acct')}/${status.get('id') as string}`;
|
||||
|
||||
return (
|
||||
<Link to={quoteUrl} className='status__quote-author-button'>
|
||||
<div className='status__quote-author-button'>
|
||||
<FormattedMessage
|
||||
id='status.quote_post_author'
|
||||
defaultMessage='Post by {name}'
|
||||
values={{ name: quoteAuthorElement }}
|
||||
defaultMessage='Quoted a post by @{name}'
|
||||
values={{ name: quoteAuthorName }}
|
||||
/>
|
||||
<Icon id='chevron_right' icon={ChevronRightIcon} />
|
||||
<Icon id='article' icon={ArticleIcon} />
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -112,39 +100,42 @@ export const QuotedStatus: React.FC<{
|
|||
defaultMessage='Hidden due to one of your filters'
|
||||
/>
|
||||
);
|
||||
} else if (quoteState === 'deleted') {
|
||||
quoteError = (
|
||||
<FormattedMessage
|
||||
id='status.quote_error.removed'
|
||||
defaultMessage='This post was removed by its author.'
|
||||
/>
|
||||
);
|
||||
} else if (quoteState === 'unauthorized') {
|
||||
quoteError = (
|
||||
<FormattedMessage
|
||||
id='status.quote_error.unauthorized'
|
||||
defaultMessage='This post cannot be displayed as you are not authorized to view it.'
|
||||
/>
|
||||
);
|
||||
} else if (quoteState === 'pending') {
|
||||
quoteError = (
|
||||
<>
|
||||
<FormattedMessage
|
||||
id='status.quote_error.pending_approval'
|
||||
defaultMessage='This post is pending approval from the original author.'
|
||||
defaultMessage='Post pending'
|
||||
/>
|
||||
|
||||
<LearnMoreLink>
|
||||
<h6>
|
||||
<FormattedMessage
|
||||
id='status.quote_error.pending_approval_popout.title'
|
||||
defaultMessage='Pending quote? Remain calm'
|
||||
/>
|
||||
</h6>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id='status.quote_error.pending_approval_popout.body'
|
||||
defaultMessage='Quotes shared across the Fediverse may take time to display, as different servers have different protocols.'
|
||||
/>
|
||||
</p>
|
||||
</LearnMoreLink>
|
||||
</>
|
||||
);
|
||||
} else if (quoteState === 'rejected' || quoteState === 'revoked') {
|
||||
} else if (
|
||||
!status ||
|
||||
!quotedStatusId ||
|
||||
quoteState === 'deleted' ||
|
||||
quoteState === 'rejected' ||
|
||||
quoteState === 'revoked' ||
|
||||
quoteState === 'unauthorized'
|
||||
) {
|
||||
quoteError = (
|
||||
<FormattedMessage
|
||||
id='status.quote_error.rejected'
|
||||
defaultMessage='This post cannot be displayed as the original author does not allow it to be quoted.'
|
||||
/>
|
||||
);
|
||||
} else if (!status || !quotedStatusId) {
|
||||
quoteError = (
|
||||
<FormattedMessage
|
||||
id='status.quote_error.not_found'
|
||||
defaultMessage='This post cannot be displayed.'
|
||||
id='status.quote_error.not_available'
|
||||
defaultMessage='Post unavailable'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -168,7 +159,7 @@ export const QuotedStatus: React.FC<{
|
|||
isQuotedPost
|
||||
id={quotedStatusId}
|
||||
contextType={contextType}
|
||||
avatarSize={40}
|
||||
avatarSize={32}
|
||||
>
|
||||
{canRenderChildQuote && (
|
||||
<QuotedStatus
|
||||
|
|
|
@ -498,6 +498,8 @@
|
|||
"keyboard_shortcuts.translate": "to translate a post",
|
||||
"keyboard_shortcuts.unfocus": "Unfocus compose textarea/search",
|
||||
"keyboard_shortcuts.up": "Move up in the list",
|
||||
"learn_more_link.got_it": "Got it",
|
||||
"learn_more_link.learn_more": "Learn more",
|
||||
"lightbox.close": "Close",
|
||||
"lightbox.next": "Next",
|
||||
"lightbox.previous": "Previous",
|
||||
|
@ -873,12 +875,11 @@
|
|||
"status.open": "Expand this post",
|
||||
"status.pin": "Pin on profile",
|
||||
"status.quote_error.filtered": "Hidden due to one of your filters",
|
||||
"status.quote_error.not_found": "This post cannot be displayed.",
|
||||
"status.quote_error.pending_approval": "This post is pending approval from the original author.",
|
||||
"status.quote_error.rejected": "This post cannot be displayed as the original author does not allow it to be quoted.",
|
||||
"status.quote_error.removed": "This post was removed by its author.",
|
||||
"status.quote_error.unauthorized": "This post cannot be displayed as you are not authorized to view it.",
|
||||
"status.quote_post_author": "Post by {name}",
|
||||
"status.quote_error.not_available": "Post unavailable",
|
||||
"status.quote_error.pending_approval": "Post pending",
|
||||
"status.quote_error.pending_approval_popout.body": "Quotes shared across the Fediverse may take time to display, as different servers have different protocols.",
|
||||
"status.quote_error.pending_approval_popout.title": "Pending quote? Remain calm",
|
||||
"status.quote_post_author": "Quoted a post by @{name}",
|
||||
"status.read_more": "Read more",
|
||||
"status.reblog": "Boost",
|
||||
"status.reblog_private": "Boost with original visibility",
|
||||
|
|
|
@ -12,6 +12,8 @@ body {
|
|||
--background-color: #fff;
|
||||
--background-color-tint: rgba(255, 255, 255, 80%);
|
||||
--background-filter: blur(10px);
|
||||
--surface-variant-background-color: #f1ebfb;
|
||||
--surface-border-color: #cac4d0;
|
||||
--on-surface-color: #{color.adjust($ui-base-color, $alpha: -0.65)};
|
||||
--rich-text-container-color: rgba(255, 216, 231, 100%);
|
||||
--rich-text-text-color: rgba(114, 47, 83, 100%);
|
||||
|
|
|
@ -1433,10 +1433,6 @@ body > [data-popper-placement] {
|
|||
}
|
||||
}
|
||||
|
||||
.status--has-quote .quote-inline {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.status {
|
||||
padding: 16px;
|
||||
min-height: 54px;
|
||||
|
@ -1470,10 +1466,6 @@ body > [data-popper-placement] {
|
|||
margin-top: 16px;
|
||||
}
|
||||
|
||||
&--is-quote {
|
||||
border: none;
|
||||
}
|
||||
|
||||
&--in-thread {
|
||||
--thread-margin: calc(46px + 8px);
|
||||
|
||||
|
@ -1860,79 +1852,99 @@ body > [data-popper-placement] {
|
|||
// --status-gutter-width is currently only set inside of
|
||||
// .notification-ungrouped, so everywhere else this will fall back
|
||||
// to the pixel values
|
||||
--quote-margin: var(--status-gutter-width, 36px);
|
||||
--quote-margin: var(--status-gutter-width);
|
||||
|
||||
position: relative;
|
||||
margin-block-start: 16px;
|
||||
margin-inline-start: calc(var(--quote-margin) + var(--thread-margin, 0px));
|
||||
border-radius: 8px;
|
||||
border-radius: 12px;
|
||||
color: var(--nested-card-text);
|
||||
background: var(--nested-card-background);
|
||||
border: var(--nested-card-border);
|
||||
|
||||
@container (width > 460px) {
|
||||
--quote-margin: var(--status-gutter-width, 56px);
|
||||
}
|
||||
border: 1px solid var(--surface-border-color);
|
||||
}
|
||||
|
||||
.status__quote--error {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
padding: 12px;
|
||||
font-size: 15px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
letter-spacing: 0.25px;
|
||||
min-height: 56px;
|
||||
|
||||
.link-button {
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
letter-spacing: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.status__quote-author-button {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: inline-flex;
|
||||
width: auto;
|
||||
margin-block-start: 10px;
|
||||
padding: 5px 12px;
|
||||
display: flex;
|
||||
margin-top: 8px;
|
||||
padding: 8px 12px;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-family: inherit;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
line-height: normal;
|
||||
letter-spacing: 0;
|
||||
text-decoration: none;
|
||||
color: $highlight-text-color;
|
||||
background: var(--nested-card-background);
|
||||
border: var(--nested-card-border);
|
||||
border-radius: 4px;
|
||||
|
||||
&:active,
|
||||
&:focus,
|
||||
&:hover {
|
||||
border-color: lighten($highlight-text-color, 4%);
|
||||
color: lighten($highlight-text-color, 4%);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: $ui-button-icon-focus-outline;
|
||||
}
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
letter-spacing: 0.25px;
|
||||
color: $darker-text-color;
|
||||
background: var(--surface-variant-background-color);
|
||||
border-radius: 8px;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.status__quote-icon {
|
||||
position: absolute;
|
||||
inset-block-start: 18px;
|
||||
inset-inline-start: -40px;
|
||||
display: block;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
padding: 5px;
|
||||
color: #6a49ba;
|
||||
z-index: 10;
|
||||
.status--is-quote {
|
||||
border: none;
|
||||
padding: 12px;
|
||||
|
||||
.status__quote--error & {
|
||||
inset-block-start: 50%;
|
||||
transform: translateY(-50%);
|
||||
.status__info {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
@container (width > 460px) {
|
||||
inset-inline-start: -50px;
|
||||
.display-name,
|
||||
.status__relative-time {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
letter-spacing: 0.1px;
|
||||
}
|
||||
|
||||
.display-name__account {
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.status__content {
|
||||
display: -webkit-box;
|
||||
font-size: 14px;
|
||||
letter-spacing: 0.25px;
|
||||
line-height: 20px;
|
||||
-webkit-line-clamp: 4;
|
||||
line-clamp: 4;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
|
||||
p {
|
||||
margin-bottom: 20px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.media-gallery,
|
||||
.video-player,
|
||||
.audio-player,
|
||||
.attachment-list,
|
||||
.poll {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2152,6 +2164,27 @@ body > [data-popper-placement] {
|
|||
}
|
||||
}
|
||||
|
||||
.learn-more__popout {
|
||||
gap: 8px;
|
||||
|
||||
&__content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: inherit;
|
||||
font-weight: 500;
|
||||
line-height: inherit;
|
||||
letter-spacing: 0.1px;
|
||||
}
|
||||
|
||||
.link-button {
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.account__wrapper {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
--surface-background-color: #{darken($ui-base-color, 4%)};
|
||||
--surface-variant-background-color: #{$ui-base-color};
|
||||
--surface-variant-active-background-color: #{lighten($ui-base-color, 4%)};
|
||||
--surface-border-color: #{lighten($ui-base-color, 8%)};
|
||||
--on-surface-color: #{color.adjust($ui-base-color, $alpha: -0.5)};
|
||||
--avatar-border-radius: 8px;
|
||||
--media-outline-color: #{rgba(#fcf8ff, 0.15)};
|
||||
|
|
Loading…
Reference in New Issue
Block a user