Merge remote-tracking branch 'origin/develop' into stillimage-tw

modal-video-fix
Alex Gleason 2022-11-21 10:59:38 -06:00
commit 1e155c4efd
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 7211D1F99744FBB7
30 zmienionych plików z 299 dodań i 413 usunięć

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -1,12 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Generated by IcoMoon</metadata>
<defs>
<font id="icomoon" horiz-adv-x="1024">
<font-face units-per-em="1024" ascent="960" descent="-64" />
<missing-glyph horiz-adv-x="1024" />
<glyph unicode="&#x20;" horiz-adv-x="512" d="" />
<glyph unicode="&#xe900;" glyph-name="spinster" d="M512 857.6c-226.216 0-409.6-183.384-409.6-409.6v0c0-226.216 183.384-409.6 409.6-409.6v0c226.216 0 409.6 183.384 409.6 409.6v0c0 226.216-183.384 409.6-409.6 409.6v0zM525.84 706.224c32.633 0 64.793-3.777 96.48-11.344 31.687-7.095 59.576-17.748 83.696-31.936l-43.264-104.272c-47.294 25.539-93.176 38.304-137.632 38.304-27.903 0-48.239-4.255-61.008-12.768-12.769-8.040-19.152-18.677-19.152-31.92s7.57-23.171 22.704-29.792c15.134-6.621 39.493-13.482 73.072-20.576 37.835-8.040 69.039-16.797 93.632-26.256 25.066-8.986 46.588-23.648 64.56-43.984 18.445-19.863 27.664-47.059 27.664-81.584 0-29.795-8.279-56.744-24.832-80.864s-41.374-43.515-74.48-58.176c-33.106-14.188-73.314-21.28-120.608-21.28-40.2 0-79.205 4.964-117.040 14.896s-68.577 23.175-92.224 39.728l46.112 103.568c22.228-14.661 48.006-26.486 77.328-35.472s58.168-13.472 86.544-13.472c53.915 0 80.864 13.475 80.864 40.432 0 14.188-7.801 24.595-23.408 31.216-15.134 7.094-39.724 14.432-73.776 22-37.362 8.040-68.582 16.551-93.648 25.536-25.066 9.459-46.572 24.352-64.544 44.688s-26.96 47.763-26.96 82.288c0 30.268 8.279 57.464 24.832 81.584 16.553 24.592 41.143 43.988 73.776 58.176 33.106 14.188 73.545 21.28 121.312 21.28z" />
<glyph unicode="&#xe901;" glyph-name="fediverse" d="M553.99 908.789c-46.369-0.785-83.969-37.261-86.545-83.096l-0.010-0.231c-0.083-1.432-0.13-3.108-0.13-4.794 0-46.987 36.77-85.385 83.105-87.99l0.231-0.010c1.432-0.083 3.107-0.13 4.794-0.13 46.988 0 85.387 36.772 87.99 83.108l0.010 0.231c0.083 1.431 0.13 3.106 0.13 4.791 0 46.988-36.771 85.387-83.108 87.99l-0.231 0.010c-1.441 0.084-3.127 0.132-4.823 0.132-0.497 0-0.993-0.004-1.487-0.012l0.075 0.001zM459.882 805.031l-251.29-127.347c13.547-13.809 23-31.679 26.366-51.617l0.080-0.57 251.287 127.353c-13.545 13.808-22.997 31.675-26.363 51.611l-0.080 0.57zM641.318 775.903c-9.415-17.78-23.636-31.938-40.939-41.021l-0.532-0.254 198.787-199.554c9.415 17.78 23.634 31.938 40.936 41.021l0.532 0.254zM487.306 751.83l-147.023-287.024 43.408-43.576 155.667 303.891c-20.483 3.55-38.302 13.087-52.060 26.716l0.007-0.007zM599.388 734.397c-12.846-6.718-28.060-10.66-44.195-10.66-1.77 0-3.529 0.047-5.276 0.141l0.244-0.010c-3.232 0.199-6.15 0.516-9.026 0.959l0.542-0.069 22.259-142.535 60.737-9.746zM138.038 697.983c-46.37-0.783-83.972-37.26-86.548-83.095l-0.010-0.231c-0.083-1.432-0.13-3.107-0.13-4.794 0-46.988 36.772-85.387 83.108-87.99l0.231-0.010c1.432-0.083 3.107-0.13 4.794-0.13 46.988 0 85.387 36.772 87.99 83.108l0.010 0.231c0.083 1.432 0.13 3.107 0.13 4.794 0 46.988-36.772 85.387-83.108 87.99l-0.231 0.010c-1.43 0.083-3.103 0.13-4.787 0.13-0.51 0-1.018-0.004-1.526-0.013l0.076 0.001zM235.216 624.428c0.752-4.537 1.182-9.766 1.182-15.095 0-1.667-0.042-3.325-0.125-4.972l0.009 0.231c-0.796-13.969-4.43-26.918-10.33-38.52l0.254 0.551 142.645-22.911 28.036 54.751zM479.695 585.167l-28.039-54.757 337.040-54.13c-0.697 4.368-1.096 9.405-1.096 14.535 0 1.678 0.043 3.346 0.127 5.002l-0.009-0.232c0.815 14.158 4.546 27.272 10.597 38.992l-0.254-0.542zM883.076 578.43c-46.37-0.783-83.972-37.26-86.548-83.095l-0.010-0.231c-0.083-1.432-0.13-3.107-0.13-4.794 0-46.988 36.772-85.387 83.108-87.99l0.231-0.010c1.432-0.083 3.107-0.13 4.794-0.13 46.988 0 85.387 36.772 87.99 83.108l0.010 0.231c0.083 1.432 0.13 3.107 0.13 4.794 0 46.988-36.772 85.387-83.108 87.99l-0.231 0.010c-1.438 0.084-3.119 0.131-4.812 0.131-0.501 0-1-0.004-1.499-0.012l0.075 0.001zM225.366 565.098c-9.414-17.777-23.632-31.933-40.931-41.016l-0.532-0.254 227.623-228.511 54.877 27.811zM182.639 523.19c-12.642-6.466-27.577-10.256-43.397-10.256-1.77 0-3.529 0.047-5.276 0.141l0.244-0.010c-3.521 0.199-6.741 0.548-9.909 1.050l0.551-0.072 43.485-278.147c12.642 6.466 27.577 10.256 43.397 10.256 1.77 0 3.529-0.047 5.276-0.141l-0.244 0.010c3.519-0.2 6.737-0.548 9.903-1.050l-0.55 0.072zM576.873 499.359l52.629-336.996c12.457 6.245 27.143 9.902 42.682 9.902 1.773 0 3.535-0.048 5.285-0.142l-0.244 0.010c3.8-0.219 7.286-0.616 10.711-1.192l-0.569 0.079-49.754 318.595zM788.965 474.681l-128.865-65.308 9.501-60.776 145.806 73.896c-13.546 13.809-22.998 31.679-26.363 51.617l-0.080 0.57zM816.386 421.477l-128.362-250.594c20.486-3.55 38.307-13.087 52.065-26.719l-0.007 0.007 128.359 250.591c-20.485 3.551-38.305 13.090-52.062 26.722l0.007-0.007zM302.044 390.153l-74.471-145.382c20.481-3.55 38.298-13.086 52.054-26.714l-0.007 0.007 65.83 128.515zM585.292 371.462l-304.691-154.416c13.549-13.81 23.003-31.682 26.368-51.622l0.080-0.57 287.744 145.83zM525.607 263.696l-54.877-27.811 115.337-115.788c9.415 17.78 23.636 31.938 40.939 41.021l0.532 0.254zM210.049 237.339c-46.369-0.785-83.969-37.261-86.545-83.096l-0.010-0.231c-0.083-1.432-0.13-3.107-0.13-4.794 0-46.988 36.772-85.387 83.108-87.99l0.231-0.010c1.432-0.083 3.107-0.13 4.794-0.13 46.988 0 85.387 36.772 87.99 83.108l0.010 0.231c0.083 1.432 0.13 3.107 0.13 4.794 0 46.988-36.772 85.387-83.108 87.99l-0.231 0.010c-1.438 0.084-3.12 0.132-4.813 0.132-0.501 0-1.002-0.004-1.501-0.013l0.075 0.001zM307.279 163.476c0.72-4.438 1.131-9.554 1.131-14.766 0-1.675-0.042-3.34-0.126-4.993l0.009 0.232c-0.806-14.078-4.495-27.122-10.481-38.793l0.254 0.546 278.1-44.626c-0.721 4.442-1.133 9.563-1.133 14.779 0 1.671 0.042 3.332 0.126 4.983l-0.009-0.232c0.807 14.078 4.497 27.12 10.484 38.79l-0.254-0.546zM670.509 163.451c-46.37-0.783-83.972-37.26-86.548-83.095l-0.010-0.231c-0.083-1.432-0.13-3.107-0.13-4.794 0-46.988 36.772-85.387 83.108-87.99l0.231-0.010c1.432-0.083 3.107-0.13 4.794-0.13 46.988 0 85.387 36.772 87.99 83.108l0.010 0.231c0.083 1.432 0.13 3.107 0.13 4.794 0 46.988-36.772 85.387-83.108 87.99l-0.231 0.010c-1.438 0.084-3.119 0.131-4.812 0.131-0.501 0-1-0.004-1.499-0.012l0.075 0.001z" />
</font></defs></svg>

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 6.0 KiB

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -9,7 +9,6 @@ import { getAcct } from 'soapbox/utils/accounts';
import { displayFqn } from 'soapbox/utils/state'; import { displayFqn } from 'soapbox/utils/state';
import RelativeTimestamp from './relative-timestamp'; import RelativeTimestamp from './relative-timestamp';
import StopPropagation from './stop-propagation';
import { Avatar, Emoji, HStack, Icon, IconButton, Stack, Text } from './ui'; import { Avatar, Emoji, HStack, Icon, IconButton, Stack, Text } from './ui';
import type { Account as AccountEntity } from 'soapbox/types/entities'; import type { Account as AccountEntity } from 'soapbox/types/entities';
@ -22,6 +21,8 @@ const InstanceFavicon: React.FC<IInstanceFavicon> = ({ account }) => {
const history = useHistory(); const history = useHistory();
const handleClick: React.MouseEventHandler = (e) => { const handleClick: React.MouseEventHandler = (e) => {
e.stopPropagation();
const timelineUrl = `/timeline/${account.domain}`; const timelineUrl = `/timeline/${account.domain}`;
if (!(e.ctrlKey || e.metaKey)) { if (!(e.ctrlKey || e.metaKey)) {
history.push(timelineUrl); history.push(timelineUrl);
@ -166,100 +167,106 @@ const Account = ({
const LinkEl: any = withLinkToProfile ? Link : 'div'; const LinkEl: any = withLinkToProfile ? Link : 'div';
return ( return (
<StopPropagation> <div data-testid='account' className='flex-shrink-0 group block w-full' ref={overflowRef}>
<div data-testid='account' className='flex-shrink-0 group block w-full' ref={overflowRef}> <HStack alignItems={actionAlignment} justifyContent='between'>
<HStack alignItems={actionAlignment} justifyContent='between'> <HStack alignItems={withAccountNote ? 'top' : 'center'} space={3}>
<HStack alignItems={withAccountNote ? 'top' : 'center'} space={3}> <ProfilePopper
condition={showProfileHoverCard}
wrapper={(children) => <HoverRefWrapper className='relative' accountId={account.id} inline>{children}</HoverRefWrapper>}
>
<LinkEl
to={`/@${account.acct}`}
title={account.acct}
onClick={(event: React.MouseEvent) => event.stopPropagation()}
>
<Avatar src={account.avatar} size={avatarSize} />
{emoji && (
<Emoji
className='w-5 h-5 absolute -bottom-1.5 -right-1.5'
emoji={emoji}
/>
)}
</LinkEl>
</ProfilePopper>
<div className='flex-grow'>
<ProfilePopper <ProfilePopper
condition={showProfileHoverCard} condition={showProfileHoverCard}
wrapper={(children) => <HoverRefWrapper className='relative' accountId={account.id} inline>{children}</HoverRefWrapper>} wrapper={(children) => <HoverRefWrapper accountId={account.id} inline>{children}</HoverRefWrapper>}
> >
<LinkEl to={`/@${account.acct}`} title={account.acct}> <LinkEl
<Avatar src={account.avatar} size={avatarSize} /> to={`/@${account.acct}`}
{emoji && ( title={account.acct}
<Emoji onClick={(event: React.MouseEvent) => event.stopPropagation()}
className='w-5 h-5 absolute -bottom-1.5 -right-1.5' >
emoji={emoji} <div className='flex items-center space-x-1 flex-grow' style={style}>
<Text
size='sm'
weight='semibold'
truncate
dangerouslySetInnerHTML={{ __html: account.display_name_html }}
/> />
)}
{account.verified && <VerificationBadge />}
</div>
</LinkEl> </LinkEl>
</ProfilePopper> </ProfilePopper>
<div className='flex-grow'> <Stack space={withAccountNote ? 1 : 0}>
<ProfilePopper <HStack alignItems='center' space={1} style={style}>
condition={showProfileHoverCard} <Text theme='muted' size='sm' truncate>@{username}</Text>
wrapper={(children) => <HoverRefWrapper accountId={account.id} inline>{children}</HoverRefWrapper>}
>
<LinkEl to={`/@${account.acct}`} title={account.acct}>
<div className='flex items-center space-x-1 flex-grow' style={style}>
<Text
size='sm'
weight='semibold'
truncate
dangerouslySetInnerHTML={{ __html: account.display_name_html }}
/>
{account.verified && <VerificationBadge />} {account.favicon && (
</div> <InstanceFavicon account={account} />
</LinkEl>
</ProfilePopper>
<Stack space={withAccountNote ? 1 : 0}>
<HStack alignItems='center' space={1} style={style}>
<Text theme='muted' size='sm' truncate>@{username}</Text>
{account.favicon && (
<InstanceFavicon account={account} />
)}
{(timestamp) ? (
<>
<Text tag='span' theme='muted' size='sm'>&middot;</Text>
{timestampUrl ? (
<Link to={timestampUrl} className='hover:underline'>
<RelativeTimestamp timestamp={timestamp} theme='muted' size='sm' className='whitespace-nowrap' futureDate={futureTimestamp} />
</Link>
) : (
<RelativeTimestamp timestamp={timestamp} theme='muted' size='sm' className='whitespace-nowrap' futureDate={futureTimestamp} />
)}
</>
) : null}
{showEdit ? (
<>
<Text tag='span' theme='muted' size='sm'>&middot;</Text>
<Icon className='h-5 w-5 text-gray-700 dark:text-gray-600' src={require('@tabler/icons/pencil.svg')} />
</>
) : null}
{actionType === 'muting' && account.mute_expires_at ? (
<>
<Text tag='span' theme='muted' size='sm'>&middot;</Text>
<Text theme='muted' size='sm'><RelativeTimestamp timestamp={account.mute_expires_at} futureDate /></Text>
</>
) : null}
</HStack>
{withAccountNote && (
<Text
size='sm'
dangerouslySetInnerHTML={{ __html: account.note_emojified }}
className='mr-2'
/>
)} )}
</Stack>
</div>
</HStack>
<div ref={actionRef}> {(timestamp) ? (
{withRelationship ? renderAction() : null} <>
<Text tag='span' theme='muted' size='sm'>&middot;</Text>
{timestampUrl ? (
<Link to={timestampUrl} className='hover:underline' onClick={(event) => event.stopPropagation()}>
<RelativeTimestamp timestamp={timestamp} theme='muted' size='sm' className='whitespace-nowrap' futureDate={futureTimestamp} />
</Link>
) : (
<RelativeTimestamp timestamp={timestamp} theme='muted' size='sm' className='whitespace-nowrap' futureDate={futureTimestamp} />
)}
</>
) : null}
{showEdit ? (
<>
<Text tag='span' theme='muted' size='sm'>&middot;</Text>
<Icon className='h-5 w-5 text-gray-700 dark:text-gray-600' src={require('@tabler/icons/pencil.svg')} />
</>
) : null}
{actionType === 'muting' && account.mute_expires_at ? (
<>
<Text tag='span' theme='muted' size='sm'>&middot;</Text>
<Text theme='muted' size='sm'><RelativeTimestamp timestamp={account.mute_expires_at} futureDate /></Text>
</>
) : null}
</HStack>
{withAccountNote && (
<Text
size='sm'
dangerouslySetInnerHTML={{ __html: account.note_emojified }}
className='mr-2'
/>
)}
</Stack>
</div> </div>
</HStack> </HStack>
</div>
</StopPropagation> <div ref={actionRef}>
{withRelationship ? renderAction() : null}
</div>
</HStack>
</div>
); );
}; };

Wyświetl plik

@ -1,77 +1,77 @@
.status-content p { [data-markup] p {
@apply mb-4 whitespace-pre-wrap; @apply mb-4 whitespace-pre-wrap;
} }
.status-content p:last-child { [data-markup] p:last-child {
@apply mb-0; @apply mb-0;
} }
.status-content a { [data-markup] a {
@apply text-primary-600 dark:text-accent-blue hover:underline; @apply text-primary-600 dark:text-accent-blue hover:underline;
} }
.status-content strong { [data-markup] strong {
@apply font-bold; @apply font-bold;
} }
.status-content em { [data-markup] em {
@apply italic; @apply italic;
} }
.status-content ul, [data-markup] ul,
.status-content ol { [data-markup] ol {
@apply pl-10 mb-4; @apply pl-10 mb-4;
} }
.status-content ul { [data-markup] ul {
@apply list-disc list-outside; @apply list-disc list-outside;
} }
.status-content ol { [data-markup] ol {
@apply list-decimal list-outside; @apply list-decimal list-outside;
} }
.status-content blockquote { [data-markup] blockquote {
@apply py-1 pl-4 mb-4 border-l-4 border-solid border-gray-400 text-gray-500 dark:text-gray-400; @apply py-1 pl-4 mb-4 border-l-4 border-solid border-gray-400 text-gray-500 dark:text-gray-400;
} }
.status-content code { [data-markup] code {
@apply cursor-text font-mono; @apply cursor-text font-mono;
} }
.status-content p > code, [data-markup] p > code,
.status-content pre { [data-markup] pre {
@apply bg-gray-100 dark:bg-primary-800; @apply bg-gray-100 dark:bg-primary-800;
} }
/* Inline code */ /* Inline code */
.status-content p > code { [data-markup] p > code {
@apply py-0.5 px-1 rounded-sm; @apply py-0.5 px-1 rounded-sm;
} }
/* Code block */ /* Code block */
.status-content pre { [data-markup] pre {
@apply py-2 px-3 mb-4 leading-6 overflow-x-auto rounded-md break-all; @apply py-2 px-3 mb-4 leading-6 overflow-x-auto rounded-md break-all;
} }
.status-content pre:last-child { [data-markup] pre:last-child {
@apply mb-0; @apply mb-0;
} }
/* Markdown images */ /* Markdown images */
.status-content img:not(.emojione):not([width][height]) { [data-markup] img:not(.emojione):not([width][height]) {
@apply w-full h-72 object-contain rounded-lg overflow-hidden my-4 block; @apply w-full h-72 object-contain rounded-lg overflow-hidden my-4 block;
} }
/* User setting to underline links */ /* User setting to underline links */
body.underline-links .status-content a { body.underline-links [data-markup] a {
@apply underline; @apply underline;
} }
.status-content .big-emoji img.emojione { [data-markup] .big-emoji img.emojione {
@apply inline w-9 h-9 p-1; @apply inline w-9 h-9 p-1;
} }
.status-content .status-link { [data-markup] .status-link {
@apply hover:underline text-primary-600 dark:text-accent-blue hover:text-primary-800 dark:hover:text-accent-blue; @apply hover:underline text-primary-600 dark:text-accent-blue hover:text-primary-800 dark:hover:text-accent-blue;
} }

Wyświetl plik

@ -0,0 +1,16 @@
import React from 'react';
import Text, { IText } from './ui/text/text';
import './markup.css';
interface IMarkup extends IText {
}
/** Styles HTML markup returned by the API, such as in account bios and statuses. */
const Markup = React.forwardRef<any, IMarkup>((props, ref) => {
return (
<Text ref={ref} {...props} data-markup />
);
});
export default Markup;

Wyświetl plik

@ -5,7 +5,6 @@ import { openModal } from 'soapbox/actions/modals';
import { vote } from 'soapbox/actions/polls'; import { vote } from 'soapbox/actions/polls';
import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
import StopPropagation from '../stop-propagation';
import { Stack, Text } from '../ui'; import { Stack, Text } from '../ui';
import PollFooter from './poll-footer'; import PollFooter from './poll-footer';
@ -65,7 +64,8 @@ const Poll: React.FC<IPoll> = ({ id, status }): JSX.Element | null => {
const showResults = poll.voted || poll.expired; const showResults = poll.voted || poll.expired;
return ( return (
<StopPropagation> // eslint-disable-next-line jsx-a11y/no-static-element-interactions
<div onClick={e => e.stopPropagation()}>
{!showResults && poll.multiple && ( {!showResults && poll.multiple && (
<Text theme='muted' size='sm'> <Text theme='muted' size='sm'>
{intl.formatMessage(messages.multiple)} {intl.formatMessage(messages.multiple)}
@ -93,7 +93,7 @@ const Poll: React.FC<IPoll> = ({ id, status }): JSX.Element | null => {
selected={selected} selected={selected}
/> />
</Stack> </Stack>
</StopPropagation> </div>
); );
}; };

Wyświetl plik

@ -13,7 +13,6 @@ import OutlineBox from './outline-box';
import StatusContent from './status-content'; import StatusContent from './status-content';
import StatusReplyMentions from './status-reply-mentions'; import StatusReplyMentions from './status-reply-mentions';
import SensitiveContentOverlay from './statuses/sensitive-content-overlay'; import SensitiveContentOverlay from './statuses/sensitive-content-overlay';
import StopPropagation from './stop-propagation';
import type { Account as AccountEntity, Status as StatusEntity } from 'soapbox/types/entities'; import type { Account as AccountEntity, Status as StatusEntity } from 'soapbox/types/entities';
@ -92,60 +91,58 @@ const QuotedStatus: React.FC<IQuotedStatus> = ({ status, onCancel, compose }) =>
} }
return ( return (
<StopPropagation> <OutlineBox
<OutlineBox data-testid='quoted-status'
data-testid='quoted-status' className={classNames('cursor-pointer', {
className={classNames('cursor-pointer', { 'hover:bg-gray-100 dark:hover:bg-gray-800': !compose,
'hover:bg-gray-100 dark:hover:bg-gray-800': !compose, })}
})} >
<Stack
space={2}
onClick={handleExpandClick}
> >
<AccountContainer
{...actions}
id={account.id}
timestamp={status.created_at}
withRelationship={false}
showProfileHoverCard={!compose}
withLinkToProfile={!compose}
/>
<StatusReplyMentions status={status} hoverable={false} />
<Stack <Stack
space={2} className='relative z-0'
onClick={handleExpandClick} style={{ minHeight: status.hidden ? Math.max(minHeight, 208) + 12 : undefined }}
> >
<AccountContainer {(status.hidden) && (
{...actions} <SensitiveContentOverlay
id={account.id} status={status}
timestamp={status.created_at} visible={showMedia}
withRelationship={false} onToggleVisibility={handleToggleMediaVisibility}
showProfileHoverCard={!compose} ref={overlay}
withLinkToProfile={!compose} />
/> )}
<StatusReplyMentions status={status} hoverable={false} /> <Stack space={4}>
<StatusContent
status={status}
collapsable
/>
<Stack {(status.card || status.media_attachments.size > 0) && (
className='relative z-0' <StatusMedia
style={{ minHeight: status.hidden ? Math.max(minHeight, 208) + 12 : undefined }}
>
{(status.hidden) && (
<SensitiveContentOverlay
status={status} status={status}
visible={showMedia} muted={compose}
showMedia={showMedia}
onToggleVisibility={handleToggleMediaVisibility} onToggleVisibility={handleToggleMediaVisibility}
ref={overlay}
/> />
)} )}
<Stack space={4}>
<StatusContent
status={status}
collapsable
/>
{(status.card || status.media_attachments.size > 0) && (
<StatusMedia
status={status}
muted={compose}
showMedia={showMedia}
onToggleVisibility={handleToggleMediaVisibility}
/>
)}
</Stack>
</Stack> </Stack>
</Stack> </Stack>
</OutlineBox> </Stack>
</StopPropagation> </OutlineBox>
); );
}; };

Wyświetl plik

@ -530,8 +530,6 @@ const StatusActionBar: React.FC<IStatusActionBar> = ({
justifyContent={space === 'expand' ? 'between' : undefined} justifyContent={space === 'expand' ? 'between' : undefined}
space={space === 'compact' ? 2 : undefined} space={space === 'compact' ? 2 : undefined}
grow={space === 'expand'} grow={space === 'expand'}
onMouseUp={e => e.stopPropagation()}
onMouseDown={e => e.stopPropagation()}
onClick={e => e.stopPropagation()} onClick={e => e.stopPropagation()}
> >
<StatusActionButton <StatusActionButton

Wyświetl plik

@ -10,32 +10,24 @@ import { onlyEmoji as isOnlyEmoji } from 'soapbox/utils/rich-content';
import { isRtl } from '../rtl'; import { isRtl } from '../rtl';
import Markup from './markup';
import Poll from './polls/poll'; import Poll from './polls/poll';
import './status-content.css';
import StopPropagation from './stop-propagation';
import type { Status, Mention } from 'soapbox/types/entities'; import type { Status, Mention } from 'soapbox/types/entities';
const MAX_HEIGHT = 642; // 20px * 32 (+ 2px padding at the top) const MAX_HEIGHT = 642; // 20px * 32 (+ 2px padding at the top)
const BIG_EMOJI_LIMIT = 10; const BIG_EMOJI_LIMIT = 10;
type Point = [
x: number,
y: number,
]
interface IReadMoreButton { interface IReadMoreButton {
onClick: React.MouseEventHandler, onClick: React.MouseEventHandler,
} }
/** Button to expand a truncated status (due to too much content) */ /** Button to expand a truncated status (due to too much content) */
const ReadMoreButton: React.FC<IReadMoreButton> = ({ onClick }) => ( const ReadMoreButton: React.FC<IReadMoreButton> = ({ onClick }) => (
<StopPropagation> <button className='flex items-center text-gray-900 dark:text-gray-300 border-0 bg-transparent p-0 pt-2 hover:underline active:underline' onClick={onClick}>
<button className='flex items-center text-gray-900 dark:text-gray-300 border-0 bg-transparent p-0 pt-2 hover:underline active:underline' onClick={onClick}> <FormattedMessage id='status.read_more' defaultMessage='Read more' />
<FormattedMessage id='status.read_more' defaultMessage='Read more' /> <Icon className='inline-block h-5 w-5' src={require('@tabler/icons/chevron-right.svg')} fixedWidth />
<Icon className='inline-block h-5 w-5' src={require('@tabler/icons/chevron-right.svg')} fixedWidth /> </button>
</button>
</StopPropagation>
); );
interface IStatusContent { interface IStatusContent {
@ -52,7 +44,6 @@ const StatusContent: React.FC<IStatusContent> = ({ status, onClick, collapsable
const [collapsed, setCollapsed] = useState(false); const [collapsed, setCollapsed] = useState(false);
const [onlyEmoji, setOnlyEmoji] = useState(false); const [onlyEmoji, setOnlyEmoji] = useState(false);
const startXY = useRef<Point>();
const node = useRef<HTMLDivElement>(null); const node = useRef<HTMLDivElement>(null);
const { greentext } = useSoapboxConfig(); const { greentext } = useSoapboxConfig();
@ -106,10 +97,6 @@ const StatusContent: React.FC<IStatusContent> = ({ status, onClick, collapsable
link.setAttribute('title', link.href); link.setAttribute('title', link.href);
link.addEventListener('click', onLinkClick.bind(link), false); link.addEventListener('click', onLinkClick.bind(link), false);
} }
// Prevent bubbling
link.addEventListener('mouseup', e => e.stopPropagation());
link.addEventListener('mousedown', e => e.stopPropagation());
}); });
}; };
@ -138,29 +125,6 @@ const StatusContent: React.FC<IStatusContent> = ({ status, onClick, collapsable
updateStatusLinks(); updateStatusLinks();
}); });
const handleMouseDown: React.EventHandler<React.MouseEvent> = (e) => {
startXY.current = [e.clientX, e.clientY];
};
const handleMouseUp: React.EventHandler<React.MouseEvent> = (e) => {
if (!startXY.current) return;
const target = e.target as HTMLElement;
const parentNode = target.parentNode as HTMLElement;
const [startX, startY] = startXY.current;
const [deltaX, deltaY] = [Math.abs(e.clientX - startX), Math.abs(e.clientY - startY)];
if (target.localName === 'button' || target.localName === 'a' || (parentNode && (parentNode.localName === 'button' || parentNode.localName === 'a'))) {
return;
}
if (deltaX + deltaY < 5 && e.button === 0 && !(e.ctrlKey || e.metaKey) && onClick) {
onClick();
}
startXY.current = undefined;
};
const parsedHtml = useMemo((): string => { const parsedHtml = useMemo((): string => {
const html = translatable && status.translation ? status.translation.get('content')! : status.contentHtml; const html = translatable && status.translation ? status.translation.get('content')! : status.contentHtml;
@ -180,30 +144,24 @@ const StatusContent: React.FC<IStatusContent> = ({ status, onClick, collapsable
const baseClassName = 'text-gray-900 dark:text-gray-100 break-words text-ellipsis overflow-hidden relative focus:outline-none'; const baseClassName = 'text-gray-900 dark:text-gray-100 break-words text-ellipsis overflow-hidden relative focus:outline-none';
const content = { __html: parsedHtml }; const content = { __html: parsedHtml };
const directionStyle: React.CSSProperties = { direction: 'ltr' }; const direction = isRtl(status.search_index) ? 'rtl' : 'ltr';
const className = classNames(baseClassName, 'status-content', { const className = classNames(baseClassName, {
'cursor-pointer': onClick, 'cursor-pointer': onClick,
'whitespace-normal': withSpoiler, 'whitespace-normal': withSpoiler,
'max-h-[300px]': collapsed, 'max-h-[300px]': collapsed,
'leading-normal big-emoji': onlyEmoji, 'leading-normal big-emoji': onlyEmoji,
}); });
if (isRtl(status.search_index)) {
directionStyle.direction = 'rtl';
}
if (onClick) { if (onClick) {
const output = [ const output = [
<div <Markup
ref={node} ref={node}
tabIndex={0} tabIndex={0}
key='content' key='content'
className={className} className={className}
style={directionStyle} direction={direction}
dangerouslySetInnerHTML={content} dangerouslySetInnerHTML={content}
lang={status.language || undefined} lang={status.language || undefined}
onMouseDown={handleMouseDown}
onMouseUp={handleMouseUp}
/>, />,
]; ];
@ -219,14 +177,14 @@ const StatusContent: React.FC<IStatusContent> = ({ status, onClick, collapsable
return <div className={classNames({ 'bg-gray-100 dark:bg-primary-800 rounded-md p-4': hasPoll })}>{output}</div>; return <div className={classNames({ 'bg-gray-100 dark:bg-primary-800 rounded-md p-4': hasPoll })}>{output}</div>;
} else { } else {
const output = [ const output = [
<div <Markup
ref={node} ref={node}
tabIndex={0} tabIndex={0}
key='content' key='content'
className={classNames(baseClassName, 'status-content', { className={classNames(baseClassName, {
'leading-normal big-emoji': onlyEmoji, 'leading-normal big-emoji': onlyEmoji,
})} })}
style={directionStyle} direction={direction}
dangerouslySetInnerHTML={content} dangerouslySetInnerHTML={content}
lang={status.language || undefined} lang={status.language || undefined}
/>, />,

Wyświetl plik

@ -2,7 +2,6 @@ import React, { useState } from 'react';
import { openModal } from 'soapbox/actions/modals'; import { openModal } from 'soapbox/actions/modals';
import AttachmentThumbs from 'soapbox/components/attachment-thumbs'; import AttachmentThumbs from 'soapbox/components/attachment-thumbs';
import StopPropagation from 'soapbox/components/stop-propagation';
import PlaceholderCard from 'soapbox/features/placeholder/components/placeholder-card'; import PlaceholderCard from 'soapbox/features/placeholder/components/placeholder-card';
import Card from 'soapbox/features/status/components/card'; import Card from 'soapbox/features/status/components/card';
import Bundle from 'soapbox/features/ui/components/bundle'; import Bundle from 'soapbox/features/ui/components/bundle';
@ -176,9 +175,10 @@ const StatusMedia: React.FC<IStatusMedia> = ({
if (media) { if (media) {
return ( return (
<StopPropagation> // eslint-disable-next-line jsx-a11y/no-static-element-interactions
<div onClick={e => e.stopPropagation()}>
{media} {media}
</StopPropagation> </div>
); );
} else { } else {
return null; return null;

Wyświetl plik

@ -5,7 +5,6 @@ import { Link } from 'react-router-dom';
import { openModal } from 'soapbox/actions/modals'; import { openModal } from 'soapbox/actions/modals';
import HoverRefWrapper from 'soapbox/components/hover-ref-wrapper'; import HoverRefWrapper from 'soapbox/components/hover-ref-wrapper';
import HoverStatusWrapper from 'soapbox/components/hover-status-wrapper'; import HoverStatusWrapper from 'soapbox/components/hover-status-wrapper';
import StopPropagation from 'soapbox/components/stop-propagation';
import { useAppDispatch } from 'soapbox/hooks'; import { useAppDispatch } from 'soapbox/hooks';
import type { Account, Status } from 'soapbox/types/entities'; import type { Account, Status } from 'soapbox/types/entities';
@ -19,6 +18,8 @@ const StatusReplyMentions: React.FC<IStatusReplyMentions> = ({ status, hoverable
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const handleOpenMentionsModal: React.MouseEventHandler<HTMLSpanElement> = (e) => { const handleOpenMentionsModal: React.MouseEventHandler<HTMLSpanElement> = (e) => {
e.stopPropagation();
const account = status.account as Account; const account = status.account as Account;
dispatch(openModal('MENTIONS', { dispatch(openModal('MENTIONS', {
@ -49,7 +50,7 @@ const StatusReplyMentions: React.FC<IStatusReplyMentions> = ({ status, hoverable
// The typical case with a reply-to and a list of mentions. // The typical case with a reply-to and a list of mentions.
const accounts = to.slice(0, 2).map(account => { const accounts = to.slice(0, 2).map(account => {
const link = ( const link = (
<Link to={`/@${account.acct}`} className='reply-mentions__account'>@{account.username}</Link> <Link to={`/@${account.acct}`} className='reply-mentions__account' onClick={(e) => e.stopPropagation()}>@{account.username}</Link>
); );
if (hoverable) { if (hoverable) {
@ -72,34 +73,32 @@ const StatusReplyMentions: React.FC<IStatusReplyMentions> = ({ status, hoverable
} }
return ( return (
<StopPropagation> <div className='reply-mentions'>
<div className='reply-mentions'> <FormattedMessage
<FormattedMessage id='reply_mentions.reply.hoverable'
id='reply_mentions.reply.hoverable' defaultMessage='<hover>Replying to</hover> {accounts}'
defaultMessage='<hover>Replying to</hover> {accounts}' values={{
values={{ accounts: <FormattedList type='conjunction' value={accounts} />,
accounts: <FormattedList type='conjunction' value={accounts} />, hover: (children: React.ReactNode) => {
hover: (children: React.ReactNode) => { if (hoverable) {
if (hoverable) { return (
return ( <HoverStatusWrapper statusId={status.in_reply_to_id} inline>
<HoverStatusWrapper statusId={status.in_reply_to_id} inline> <span
<span key='hoverstatus'
key='hoverstatus' className='hover:underline cursor-pointer'
className='hover:underline cursor-pointer' role='presentation'
role='presentation' >
> {children}
{children} </span>
</span> </HoverStatusWrapper>
</HoverStatusWrapper> );
); } else {
} else { return children;
return children; }
} },
}, }}
}} />
/> </div>
</div>
</StopPropagation>
); );
}; };

Wyświetl plik

@ -235,8 +235,7 @@ const Status: React.FC<IStatus> = (props) => {
reblogElement = ( reblogElement = (
<NavLink <NavLink
to={`/@${status.getIn(['account', 'acct'])}`} to={`/@${status.getIn(['account', 'acct'])}`}
onClick={e => e.stopPropagation()} onClick={(event) => event.stopPropagation()}
onMouseUp={e => e.stopPropagation()}
className='hidden sm:flex items-center text-gray-700 dark:text-gray-600 text-xs font-medium space-x-1 hover:underline' className='hidden sm: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' /> <Icon src={require('@tabler/icons/repeat.svg')} className='text-green-600' />
@ -259,8 +258,7 @@ const Status: React.FC<IStatus> = (props) => {
<div className='pb-5 -mt-2 sm:hidden truncate'> <div className='pb-5 -mt-2 sm:hidden truncate'>
<NavLink <NavLink
to={`/@${status.getIn(['account', 'acct'])}`} to={`/@${status.getIn(['account', 'acct'])}`}
onClick={e => e.stopPropagation()} onClick={(event) => event.stopPropagation()}
onMouseUp={e => e.stopPropagation()}
className='flex items-center text-gray-700 dark:text-gray-600 text-xs font-medium space-x-1 hover:underline' 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' /> <Icon src={require('@tabler/icons/repeat.svg')} className='text-green-600' />
@ -328,7 +326,7 @@ const Status: React.FC<IStatus> = (props) => {
data-featured={featured ? 'true' : null} data-featured={featured ? 'true' : null}
aria-label={textForScreenReader(intl, actualStatus, rebloggedByText)} aria-label={textForScreenReader(intl, actualStatus, rebloggedByText)}
ref={node} ref={node}
onMouseUp={handleClick} onClick={handleClick}
role='link' role='link'
> >
{featured && ( {featured && (

Wyświetl plik

@ -5,7 +5,6 @@ import { defineMessages, useIntl } from 'react-intl';
import { useSettings, useSoapboxConfig } from 'soapbox/hooks'; import { useSettings, useSoapboxConfig } from 'soapbox/hooks';
import { defaultMediaVisibility } from 'soapbox/utils/status'; import { defaultMediaVisibility } from 'soapbox/utils/status';
import StopPropagation from '../stop-propagation';
import { Button, HStack, Text } from '../ui'; import { Button, HStack, Text } from '../ui';
import type { Status as StatusEntity } from 'soapbox/types/entities'; import type { Status as StatusEntity } from 'soapbox/types/entities';
@ -39,7 +38,9 @@ const SensitiveContentOverlay = React.forwardRef<HTMLDivElement, ISensitiveConte
const [visible, setVisible] = useState<boolean>(defaultMediaVisibility(status, displayMedia)); const [visible, setVisible] = useState<boolean>(defaultMediaVisibility(status, displayMedia));
const toggleVisibility = () => { const toggleVisibility = (event: React.MouseEvent<HTMLButtonElement>) => {
event.stopPropagation();
if (onToggleVisibility) { if (onToggleVisibility) {
onToggleVisibility(); onToggleVisibility();
} else { } else {
@ -63,15 +64,13 @@ const SensitiveContentOverlay = React.forwardRef<HTMLDivElement, ISensitiveConte
data-testid='sensitive-overlay' data-testid='sensitive-overlay'
> >
{visible ? ( {visible ? (
<StopPropagation> <Button
<Button text={intl.formatMessage(messages.hide)}
text={intl.formatMessage(messages.hide)} icon={require('@tabler/icons/eye-off.svg')}
icon={require('@tabler/icons/eye-off.svg')} onClick={toggleVisibility}
onClick={toggleVisibility} theme='primary'
theme='primary' size='sm'
size='sm' />
/>
</StopPropagation>
) : ( ) : (
<div className='text-center w-3/4 mx-auto space-y-4' ref={ref}> <div className='text-center w-3/4 mx-auto space-y-4' ref={ref}>
<div className='space-y-1'> <div className='space-y-1'>
@ -93,34 +92,36 @@ const SensitiveContentOverlay = React.forwardRef<HTMLDivElement, ISensitiveConte
</div> </div>
<HStack alignItems='center' justifyContent='center' space={2}> <HStack alignItems='center' justifyContent='center' space={2}>
<StopPropagation> {isUnderReview ? (
{isUnderReview ? ( <>
<> {links.get('support') && (
{links.get('support') && ( <a
<a href={links.get('support')} target='_blank'> href={links.get('support')}
<Button target='_blank'
type='button' onClick={(event) => event.stopPropagation()}
theme='outline' >
size='sm' <Button
icon={require('@tabler/icons/headset.svg')} type='button'
> theme='outline'
{intl.formatMessage(messages.contact)} size='sm'
</Button> icon={require('@tabler/icons/headset.svg')}
</a> >
)} {intl.formatMessage(messages.contact)}
</> </Button>
) : null} </a>
)}
</>
) : null}
<Button <Button
type='button' type='button'
theme='outline' theme='outline'
size='sm' size='sm'
icon={require('@tabler/icons/eye.svg')} icon={require('@tabler/icons/eye.svg')}
onClick={toggleVisibility} onClick={toggleVisibility}
> >
{intl.formatMessage(messages.show)} {intl.formatMessage(messages.show)}
</Button> </Button>
</StopPropagation>
</HStack> </HStack>
</div> </div>
)} )}

Wyświetl plik

@ -1,33 +0,0 @@
import React from 'react';
interface IStopPropagation {
/** Children to render within the bubble. */
children: React.ReactNode,
/** Whether to prevent mouse events from bubbling. (default: `true`) */
enabled?: boolean,
}
/**
* Prevent mouse events from bubbling up.
*
* Why is this needed? Because `onClick`, `onMouseDown`, and `onMouseUp` are 3 separate events.
* To prevent a lot of code duplication, this component can stop all mouse events.
* Plus, placing it in the component tree makes it more readable.
*/
const StopPropagation: React.FC<IStopPropagation> = ({ children, enabled = true }) => {
const handler: React.MouseEventHandler<HTMLDivElement> = (e) => {
if (enabled) {
e.stopPropagation();
}
};
return (
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
<div onClick={handler} onMouseDown={handler} onMouseUp={handler}>
{children}
</div>
);
};
export default StopPropagation;

Wyświetl plik

@ -4,7 +4,6 @@ import { FormattedMessage, useIntl } from 'react-intl';
import { translateStatus, undoStatusTranslation } from 'soapbox/actions/statuses'; import { translateStatus, undoStatusTranslation } from 'soapbox/actions/statuses';
import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks'; import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks';
import StopPropagation from './stop-propagation';
import { Stack } from './ui'; import { Stack } from './ui';
import type { Status } from 'soapbox/types/entities'; import type { Status } from 'soapbox/types/entities';
@ -43,21 +42,17 @@ const TranslateButton: React.FC<ITranslateButton> = ({ status }) => {
<Stack className='text-gray-700 dark:text-gray-600 text-sm' alignItems='start'> <Stack className='text-gray-700 dark:text-gray-600 text-sm' alignItems='start'>
<FormattedMessage id='status.translated_from_with' defaultMessage='Translated from {lang} using {provider}' values={{ lang: languageName, provider }} /> <FormattedMessage id='status.translated_from_with' defaultMessage='Translated from {lang} using {provider}' values={{ lang: languageName, provider }} />
<StopPropagation> <button className='text-primary-600 dark:text-accent-blue hover:text-primary-700 dark:hover:text-accent-blue hover:underline' onClick={handleTranslate}>
<button className='text-primary-600 dark:text-accent-blue hover:text-primary-700 dark:hover:text-accent-blue hover:underline' onClick={handleTranslate}> <FormattedMessage id='status.show_original' defaultMessage='Show original' />
<FormattedMessage id='status.show_original' defaultMessage='Show original' /> </button>
</button>
</StopPropagation>
</Stack> </Stack>
); );
} }
return ( return (
<StopPropagation> <button className='text-primary-600 dark:text-accent-blue hover:text-primary-700 dark:hover:text-accent-blue text-left text-sm hover:underline' onClick={handleTranslate}>
<button className='text-primary-600 dark:text-accent-blue hover:text-primary-700 dark:hover:text-accent-blue text-left text-sm hover:underline' onClick={handleTranslate}> <FormattedMessage id='status.translate' defaultMessage='Translate' />
<FormattedMessage id='status.translate' defaultMessage='Translate' /> </button>
</button>
</StopPropagation>
); );
}; };

Wyświetl plik

@ -8,7 +8,7 @@ import { useButtonStyles } from './useButtonStyles';
import type { ButtonSizes, ButtonThemes } from './useButtonStyles'; import type { ButtonSizes, ButtonThemes } from './useButtonStyles';
interface IButton extends Pick<React.HTMLAttributes<HTMLButtonElement>, 'onClick' | 'onMouseUp'> { interface IButton {
/** Whether this button expands the width of its container. */ /** Whether this button expands the width of its container. */
block?: boolean, block?: boolean,
/** Elements inside the <button> */ /** Elements inside the <button> */
@ -19,6 +19,8 @@ interface IButton extends Pick<React.HTMLAttributes<HTMLButtonElement>, 'onClick
disabled?: boolean, disabled?: boolean,
/** URL to an SVG icon to render inside the button. */ /** URL to an SVG icon to render inside the button. */
icon?: string, icon?: string,
/** Action when the button is clicked. */
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void,
/** A predefined button size. */ /** A predefined button size. */
size?: ButtonSizes, size?: ButtonSizes,
/** Text inside the button. Takes precedence over `children`. */ /** Text inside the button. Takes precedence over `children`. */

Wyświetl plik

@ -27,7 +27,7 @@ const spaces = {
8: 'space-x-8', 8: 'space-x-8',
}; };
interface IHStack extends Pick<React.HTMLAttributes<HTMLDivElement>, 'onClick' | 'onMouseUp' | 'onMouseDown'> { interface IHStack extends Pick<React.HTMLAttributes<HTMLDivElement>, 'onClick'> {
/** Vertical alignment of children. */ /** Vertical alignment of children. */
alignItems?: keyof typeof alignItemsOptions alignItems?: keyof typeof alignItemsOptions
/** Extra class names on the <div> element. */ /** Extra class names on the <div> element. */

Wyświetl plik

@ -54,7 +54,9 @@ export type Sizes = keyof typeof sizes
type Tags = 'abbr' | 'p' | 'span' | 'pre' | 'time' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'label' type Tags = 'abbr' | 'p' | 'span' | 'pre' | 'time' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'label'
type Directions = 'ltr' | 'rtl' type Directions = 'ltr' | 'rtl'
interface IText extends Pick<React.HTMLAttributes<HTMLParagraphElement>, 'dangerouslySetInnerHTML'> { interface IText extends Pick<React.HTMLAttributes<HTMLParagraphElement>, 'dangerouslySetInnerHTML' | 'tabIndex' | 'lang'> {
/** Text content. */
children?: React.ReactNode,
/** How to align the text. */ /** How to align the text. */
align?: keyof typeof alignments, align?: keyof typeof alignments,
/** Extra class names for the outer element. */ /** Extra class names for the outer element. */
@ -84,8 +86,8 @@ interface IText extends Pick<React.HTMLAttributes<HTMLParagraphElement>, 'danger
} }
/** UI-friendly text container with dark mode support. */ /** UI-friendly text container with dark mode support. */
const Text: React.FC<IText> = React.forwardRef( const Text = React.forwardRef<any, IText>(
(props: IText, ref: React.LegacyRef<any>) => { (props, ref) => {
const { const {
align, align,
className, className,

Wyświetl plik

@ -449,6 +449,7 @@ const Audio: React.FC<IAudio> = (props) => {
onMouseLeave={handleMouseLeave} onMouseLeave={handleMouseLeave}
tabIndex={0} tabIndex={0}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
onClick={e => e.stopPropagation()}
> >
<audio <audio
src={src} src={src}

Wyświetl plik

@ -14,14 +14,7 @@ const AutosuggestAccount: React.FC<IAutosuggestAccount> = ({ id }) => {
if (!account) return null; if (!account) return null;
return ( return <Account account={account} hideActions showProfileHoverCard={false} />;
<div className='relative'>
{/* HACK: The <Account> component stops click events, so insert this div as something to click. */}
<div className='absolute inset-0' />
<Account account={account} showProfileHoverCard={false} withLinkToProfile={false} hideActions />
</div>
);
}; };

Wyświetl plik

@ -2,14 +2,13 @@ import * as React from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { prepareRequest } from 'soapbox/actions/consumer-auth'; import { prepareRequest } from 'soapbox/actions/consumer-auth';
import Markup from 'soapbox/components/markup';
import { Button, Card, CardBody, Stack, Text } from 'soapbox/components/ui'; import { Button, Card, CardBody, Stack, Text } from 'soapbox/components/ui';
import VerificationBadge from 'soapbox/components/verification-badge'; import VerificationBadge from 'soapbox/components/verification-badge';
import RegistrationForm from 'soapbox/features/auth-login/components/registration-form'; import RegistrationForm from 'soapbox/features/auth-login/components/registration-form';
import { useAppDispatch, useAppSelector, useFeatures, useSoapboxConfig } from 'soapbox/hooks'; import { useAppDispatch, useAppSelector, useFeatures, useSoapboxConfig } from 'soapbox/hooks';
import { capitalize } from 'soapbox/utils/strings'; import { capitalize } from 'soapbox/utils/strings';
import './instance-description.css';
const LandingPage = () => { const LandingPage = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const features = useFeatures(); const features = useFeatures();
@ -114,12 +113,10 @@ const LandingPage = () => {
{instance.title} {instance.title}
</h1> </h1>
<Text size='lg'> <Markup
<span size='lg'
className='instance-description' dangerouslySetInnerHTML={{ __html: instance.short_description || instance.description }}
dangerouslySetInnerHTML={{ __html: instance.short_description || instance.description }} />
/>
</Text>
</Stack> </Stack>
</div> </div>
</div> </div>

Wyświetl plik

@ -1,14 +0,0 @@
/* Instance HTML from the API. */
.instance-description a {
@apply underline;
}
.instance-description b,
.instance-description strong {
@apply font-bold;
}
.instance-description i,
.instance-description em {
@apply italic;
}

Wyświetl plik

@ -58,12 +58,7 @@ const ProfileDropdown: React.FC<IProfileDropdown> = ({ account, children }) => {
const renderAccount = (account: AccountEntity) => { const renderAccount = (account: AccountEntity) => {
return ( return (
<div className='relative'> <Account account={account} showProfileHoverCard={false} withLinkToProfile={false} hideActions />
{/* HACK: The <Account> component stops click events, so insert this div as something to click. */}
<div className='absolute inset-0' />
<Account account={account} showProfileHoverCard={false} withLinkToProfile={false} hideActions />
</div>
); );
}; };

Wyświetl plik

@ -2,7 +2,8 @@ import classNames from 'clsx';
import React from 'react'; import React from 'react';
import { defineMessages, useIntl, FormattedMessage, FormatDateOptions } from 'react-intl'; import { defineMessages, useIntl, FormattedMessage, FormatDateOptions } from 'react-intl';
import { Widget, Stack, HStack, Icon, Text } from 'soapbox/components/ui'; import Markup from 'soapbox/components/markup';
import { Widget, Stack, HStack, Icon } from 'soapbox/components/ui';
import BundleContainer from 'soapbox/features/ui/containers/bundle-container'; import BundleContainer from 'soapbox/features/ui/containers/bundle-container';
import { CryptoAddress } from 'soapbox/features/ui/util/async-components'; import { CryptoAddress } from 'soapbox/features/ui/util/async-components';
@ -51,7 +52,7 @@ const ProfileField: React.FC<IProfileField> = ({ field }) => {
return ( return (
<dl> <dl>
<dt title={field.name}> <dt title={field.name}>
<Text weight='bold' tag='span' dangerouslySetInnerHTML={{ __html: field.name_emojified }} /> <Markup weight='bold' tag='span' dangerouslySetInnerHTML={{ __html: field.name_emojified }} />
</dt> </dt>
<dd <dd
@ -65,7 +66,7 @@ const ProfileField: React.FC<IProfileField> = ({ field }) => {
</span> </span>
)} )}
<Text className='break-words overflow-hidden' tag='span' dangerouslySetInnerHTML={{ __html: field.value_emojified }} /> <Markup className='break-words overflow-hidden' tag='span' dangerouslySetInnerHTML={{ __html: field.value_emojified }} />
</HStack> </HStack>
</dd> </dd>
</dl> </dl>

Wyświetl plik

@ -4,6 +4,7 @@ import React from 'react';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import Badge from 'soapbox/components/badge'; import Badge from 'soapbox/components/badge';
import Markup from 'soapbox/components/markup';
import { Icon, HStack, Stack, Text } from 'soapbox/components/ui'; import { Icon, HStack, Stack, Text } from 'soapbox/components/ui';
import VerificationBadge from 'soapbox/components/verification-badge'; import VerificationBadge from 'soapbox/components/verification-badge';
import { useSoapboxConfig } from 'soapbox/hooks'; import { useSoapboxConfig } from 'soapbox/hooks';
@ -139,13 +140,6 @@ const ProfileInfoPanel: React.FC<IProfileInfoPanel> = ({ account, username }) =>
return ( return (
<div className='mt-6 min-w-0 flex-1 sm:px-2'> <div className='mt-6 min-w-0 flex-1 sm:px-2'>
<Stack space={2}> <Stack space={2}>
{/* Not sure if this is actual used. */}
{/* <div className='profile-info-panel-content__deactivated'>
<FormattedMessage
id='account.deactivated_description' defaultMessage='This account has been deactivated.'
/>
</div> */}
<Stack> <Stack>
<HStack space={1} alignItems='center'> <HStack space={1} alignItems='center'>
<Text size='lg' weight='bold' dangerouslySetInnerHTML={displayNameHtml} /> <Text size='lg' weight='bold' dangerouslySetInnerHTML={displayNameHtml} />
@ -178,8 +172,8 @@ const ProfileInfoPanel: React.FC<IProfileInfoPanel> = ({ account, username }) =>
<ProfileStats account={account} /> <ProfileStats account={account} />
{account.note.length > 0 && account.note !== '<p></p>' && ( {account.note.length > 0 && (
<Text size='sm' dangerouslySetInnerHTML={content} /> <Markup size='sm' dangerouslySetInnerHTML={content} />
)} )}
<div className='flex flex-col md:flex-row items-start md:flex-wrap md:items-center gap-2'> <div className='flex flex-col md:flex-row items-start md:flex-wrap md:items-center gap-2'>

Wyświetl plik

@ -183,6 +183,8 @@ const Video: React.FC<IVideo> = ({
} }
}, [video.current]); }, [video.current]);
const handleClickRoot: React.MouseEventHandler = e => e.stopPropagation();
const handlePlay = () => { const handlePlay = () => {
setPaused(false); setPaused(false);
}; };
@ -505,6 +507,7 @@ const Video: React.FC<IVideo> = ({
ref={player} ref={player}
onMouseEnter={handleMouseEnter} onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave} onMouseLeave={handleMouseLeave}
onClick={handleClickRoot}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
tabIndex={0} tabIndex={0}
> >

Wyświetl plik

@ -269,6 +269,15 @@ const fixBirthday = (account: ImmutableMap<string, any>) => {
return account.set('birthday', birthday || ''); return account.set('birthday', birthday || '');
}; };
/** Rewrite `<p></p>` to empty string. */
const fixNote = (account: ImmutableMap<string, any>) => {
if (account.get('note') === '<p></p>') {
return account.set('note', '');
} else {
return account;
}
};
export const normalizeAccount = (account: Record<string, any>) => { export const normalizeAccount = (account: Record<string, any>) => {
return AccountRecord( return AccountRecord(
ImmutableMap(fromJS(account)).withMutations(account => { ImmutableMap(fromJS(account)).withMutations(account => {
@ -289,6 +298,7 @@ export const normalizeAccount = (account: Record<string, any>) => {
fixUsername(account); fixUsername(account);
fixDisplayName(account); fixDisplayName(account);
fixBirthday(account); fixBirthday(account);
fixNote(account);
addInternalFields(account); addInternalFields(account);
}), }),
); );

Wyświetl plik

@ -58,25 +58,3 @@
line-height: #{$px + "px"}; line-height: #{$px + "px"};
line-height: #{$rem + "rem"}; line-height: #{$rem + "rem"};
} }
// Soapbox icon font
@font-face {
font-family: 'soapbox';
src: url('../assets/fonts/soapbox/soapbox.eot?pryg6i');
src: url('../assets/fonts/soapbox/soapbox.eot?pryg6i#iefix') format('embedded-opentype'),
url('../assets/fonts/soapbox/soapbox.ttf?pryg6i') format('truetype'),
url('../assets/fonts/soapbox/soapbox.woff?pryg6i') format('woff'),
url('../assets/fonts/soapbox/soapbox.svg?pryg6i#soapbox') format('svg');
font-weight: normal;
font-style: normal;
}
.fa-fediverse::before {
font-family: 'soapbox';
content: "\e901";
}
.fa-spinster::before {
font-family: 'soapbox';
content: "\e900";
}