Merge branch 'long-name-fixes' into 'develop'

Fix overflow problems in various places

See merge request soapbox-pub/soapbox!2487
This commit is contained in:
Alex Gleason 2023-05-03 00:35:06 +00:00
commit c0c0dc1cc2
14 changed files with 33 additions and 23 deletions

View file

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Posts: Support posts filtering on recent Mastodon versions - Posts: Support posts filtering on recent Mastodon versions
- Reactions: Support custom emoji reactions - Reactions: Support custom emoji reactions
- Compatbility: Support Mastodon v2 timeline filters. - Compatbility: Support Mastodon v2 timeline filters.
- Compatbility: Preliminary support for Ditto backend.
- Posts: Support dislikes on Friendica. - Posts: Support dislikes on Friendica.
- UI: added a character counter to some textareas. - UI: added a character counter to some textareas.
@ -30,6 +31,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- 18n: fixed Chinese language being detected from the browser. - 18n: fixed Chinese language being detected from the browser.
- Conversations: fixed pagination (Mastodon). - Conversations: fixed pagination (Mastodon).
- Compatibility: fix version parsing for Friendica. - Compatibility: fix version parsing for Friendica.
- UI: fixed various overflow issues related to long usernames.
- UI: fixed display of Markdown code blocks in the reply indicator.
## [3.2.0] - 2023-02-15 ## [3.2.0] - 2023-02-15

View file

@ -142,7 +142,7 @@ const deleteStatusModal = (intl: IntlShape, statusId: string, afterConfirm = ()
dispatch(openModal('CONFIRM', { dispatch(openModal('CONFIRM', {
icon: require('@tabler/icons/trash.svg'), icon: require('@tabler/icons/trash.svg'),
heading: intl.formatMessage(messages.deleteStatusHeading), heading: intl.formatMessage(messages.deleteStatusHeading),
message: intl.formatMessage(messages.deleteStatusPrompt, { acct }), message: intl.formatMessage(messages.deleteStatusPrompt, { acct: <strong className='break-words'>{acct}</strong> }),
confirm: intl.formatMessage(messages.deleteStatusConfirm), confirm: intl.formatMessage(messages.deleteStatusConfirm),
onConfirm: () => { onConfirm: () => {
dispatch(deleteStatus(statusId)).then(() => { dispatch(deleteStatus(statusId)).then(() => {

View file

@ -56,8 +56,7 @@ const ListItem: React.FC<IListItem> = ({ label, hint, children, onClick, onSelec
return ( return (
<Comp <Comp
className={clsx({ className={clsx('flex items-center justify-between overflow-hidden bg-gradient-to-r from-gradient-start/20 to-gradient-end/20 px-4 py-2 first:rounded-t-lg last:rounded-b-lg dark:from-gradient-start/10 dark:to-gradient-end/10', {
'flex items-center justify-between px-4 py-2 first:rounded-t-lg last:rounded-b-lg bg-gradient-to-r from-gradient-start/20 to-gradient-end/20 dark:from-gradient-start/10 dark:to-gradient-end/10': true,
'cursor-pointer hover:from-gradient-start/30 hover:to-gradient-end/30 dark:hover:from-gradient-start/5 dark:hover:to-gradient-end/5': typeof onClick !== 'undefined' || typeof onSelect !== 'undefined', 'cursor-pointer hover:from-gradient-start/30 hover:to-gradient-end/30 dark:hover:from-gradient-start/5 dark:hover:to-gradient-end/5': typeof onClick !== 'undefined' || typeof onSelect !== 'undefined',
})} })}
{...linkProps} {...linkProps}
@ -71,7 +70,7 @@ const ListItem: React.FC<IListItem> = ({ label, hint, children, onClick, onSelec
</div> </div>
{onClick ? ( {onClick ? (
<HStack space={1} alignItems='center' className='text-gray-700 dark:text-gray-600'> <HStack space={1} alignItems='center' className='overflow-hidden text-gray-700 dark:text-gray-600'>
{children} {children}
<Icon src={require('@tabler/icons/chevron-right.svg')} className='ml-1 rtl:rotate-180' /> <Icon src={require('@tabler/icons/chevron-right.svg')} className='ml-1 rtl:rotate-180' />

View file

@ -258,8 +258,8 @@ const StatusActionBar: React.FC<IStatusActionBar> = ({
dispatch(openModal('CONFIRM', { dispatch(openModal('CONFIRM', {
icon: require('@tabler/icons/ban.svg'), icon: require('@tabler/icons/ban.svg'),
heading: <FormattedMessage id='confirmations.block.heading' defaultMessage='Block @{name}' values={{ name: account.get('acct') }} />, heading: <FormattedMessage id='confirmations.block.heading' defaultMessage='Block @{name}' values={{ name: account.acct }} />,
message: <FormattedMessage id='confirmations.block.message' defaultMessage='Are you sure you want to block {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />, message: <FormattedMessage id='confirmations.block.message' defaultMessage='Are you sure you want to block {name}?' values={{ name: <strong className='break-words'>@{account.acct}</strong> }} />,
confirm: intl.formatMessage(messages.blockConfirm), confirm: intl.formatMessage(messages.blockConfirm),
onConfirm: () => dispatch(blockAccount(account.id)), onConfirm: () => dispatch(blockAccount(account.id)),
secondary: intl.formatMessage(messages.blockAndReport), secondary: intl.formatMessage(messages.blockAndReport),
@ -313,7 +313,7 @@ const StatusActionBar: React.FC<IStatusActionBar> = ({
dispatch(openModal('CONFIRM', { dispatch(openModal('CONFIRM', {
heading: intl.formatMessage(messages.deleteHeading), heading: intl.formatMessage(messages.deleteHeading),
message: intl.formatMessage(messages.deleteFromGroupMessage, { name: account.username }), message: intl.formatMessage(messages.deleteFromGroupMessage, { name: <strong className='break-words'>{account.username}</strong> }),
confirm: intl.formatMessage(messages.deleteConfirm), confirm: intl.formatMessage(messages.deleteConfirm),
onConfirm: () => dispatch(groupDeleteStatus((status.group as Group).id, status.id)), onConfirm: () => dispatch(groupDeleteStatus((status.group as Group).id, status.id)),
})); }));
@ -324,7 +324,7 @@ const StatusActionBar: React.FC<IStatusActionBar> = ({
dispatch(openModal('CONFIRM', { dispatch(openModal('CONFIRM', {
heading: intl.formatMessage(messages.kickFromGroupHeading), heading: intl.formatMessage(messages.kickFromGroupHeading),
message: intl.formatMessage(messages.kickFromGroupMessage, { name: account.username }), message: intl.formatMessage(messages.kickFromGroupMessage, { name: <strong className='break-words'>{account.username}</strong> }),
confirm: intl.formatMessage(messages.kickFromGroupConfirm), confirm: intl.formatMessage(messages.kickFromGroupConfirm),
onConfirm: () => dispatch(groupKick((status.group as Group).id, account.id)), onConfirm: () => dispatch(groupKick((status.group as Group).id, account.id)),
})); }));
@ -335,7 +335,7 @@ const StatusActionBar: React.FC<IStatusActionBar> = ({
dispatch(openModal('CONFIRM', { dispatch(openModal('CONFIRM', {
heading: intl.formatMessage(messages.blockFromGroupHeading), heading: intl.formatMessage(messages.blockFromGroupHeading),
message: intl.formatMessage(messages.blockFromGroupMessage, { name: account.username }), message: intl.formatMessage(messages.blockFromGroupMessage, { name: <strong className='break-words'>{account.username}</strong> }),
confirm: intl.formatMessage(messages.blockFromGroupConfirm), confirm: intl.formatMessage(messages.blockFromGroupConfirm),
onConfirm: () => dispatch(groupBlock((status.group as Group).id, account.id)), onConfirm: () => dispatch(groupBlock((status.group as Group).id, account.id)),
})); }));

View file

@ -102,7 +102,7 @@ const Modal = React.forwardRef<HTMLDivElement, IModal>(({
'flex-row-reverse': closePosition === 'left', 'flex-row-reverse': closePosition === 'left',
})} })}
> >
<h3 className='grow text-lg font-bold leading-6 text-gray-900 dark:text-white'> <h3 className='grow truncate text-lg font-bold leading-6 text-gray-900 dark:text-white'>
{title} {title}
</h3> </h3>

View file

@ -130,7 +130,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
dispatch(openModal('CONFIRM', { dispatch(openModal('CONFIRM', {
icon: require('@tabler/icons/ban.svg'), icon: require('@tabler/icons/ban.svg'),
heading: <FormattedMessage id='confirmations.block.heading' defaultMessage='Block @{name}' values={{ name: account.acct }} />, heading: <FormattedMessage id='confirmations.block.heading' defaultMessage='Block @{name}' values={{ name: account.acct }} />,
message: <FormattedMessage id='confirmations.block.message' defaultMessage='Are you sure you want to block {name}?' values={{ name: <strong>@{account.acct}</strong> }} />, message: <FormattedMessage id='confirmations.block.message' defaultMessage='Are you sure you want to block {name}?' values={{ name: <strong className='break-words'>@{account.acct}</strong> }} />,
confirm: intl.formatMessage(messages.blockConfirm), confirm: intl.formatMessage(messages.blockConfirm),
onConfirm: () => dispatch(blockAccount(account.id)), onConfirm: () => dispatch(blockAccount(account.id)),
secondary: intl.formatMessage(messages.blockAndReport), secondary: intl.formatMessage(messages.blockAndReport),
@ -215,7 +215,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
const unfollowModal = getSettings(getState()).get('unfollowModal'); const unfollowModal = getSettings(getState()).get('unfollowModal');
if (unfollowModal) { if (unfollowModal) {
dispatch(openModal('CONFIRM', { dispatch(openModal('CONFIRM', {
message: <FormattedMessage id='confirmations.remove_from_followers.message' defaultMessage='Are you sure you want to remove {name} from your followers?' values={{ name: <strong>@{account.acct}</strong> }} />, message: <FormattedMessage id='confirmations.remove_from_followers.message' defaultMessage='Are you sure you want to remove {name} from your followers?' values={{ name: <strong className='break-words'>@{account.acct}</strong> }} />,
confirm: intl.formatMessage(messages.removeFromFollowersConfirm), confirm: intl.formatMessage(messages.removeFromFollowersConfirm),
onConfirm: () => dispatch(removeFromFollowers(account.id)), onConfirm: () => dispatch(removeFromFollowers(account.id)),
})); }));

View file

@ -2,7 +2,8 @@ import clsx from 'clsx';
import React from 'react'; import React from 'react';
import AttachmentThumbs from 'soapbox/components/attachment-thumbs'; import AttachmentThumbs from 'soapbox/components/attachment-thumbs';
import { Stack, Text } from 'soapbox/components/ui'; import Markup from 'soapbox/components/markup';
import { Stack } from 'soapbox/components/ui';
import AccountContainer from 'soapbox/containers/account-container'; import AccountContainer from 'soapbox/containers/account-container';
import { isRtl } from 'soapbox/rtl'; import { isRtl } from 'soapbox/rtl';
@ -45,8 +46,8 @@ const ReplyIndicator: React.FC<IReplyIndicator> = ({ className, status, hideActi
hideActions={hideActions} hideActions={hideActions}
/> />
<Text <Markup
className='status__content break-words' className='break-words'
size='sm' size='sm'
dangerouslySetInnerHTML={{ __html: status.contentHtml }} dangerouslySetInnerHTML={{ __html: status.contentHtml }}
direction={isRtl(status.search_index) ? 'rtl' : 'ltr'} direction={isRtl(status.search_index) ? 'rtl' : 'ltr'}

View file

@ -238,11 +238,11 @@ const SearchResults = () => {
{filterByAccount ? ( {filterByAccount ? (
<HStack className='mb-4 border-b border-solid border-gray-200 px-2 pb-4 dark:border-gray-800' space={2}> <HStack className='mb-4 border-b border-solid border-gray-200 px-2 pb-4 dark:border-gray-800' space={2}>
<IconButton iconClassName='h-5 w-5' src={require('@tabler/icons/x.svg')} onClick={handleUnsetAccount} /> <IconButton iconClassName='h-5 w-5' src={require('@tabler/icons/x.svg')} onClick={handleUnsetAccount} />
<Text> <Text truncate>
<FormattedMessage <FormattedMessage
id='search_results.filter_message' id='search_results.filter_message'
defaultMessage='You are searching for posts from @{acct}.' defaultMessage='You are searching for posts from @{acct}.'
values={{ acct: account }} values={{ acct: <strong className='break-words'>{account}</strong> }}
/> />
</Text> </Text>
</HStack> </HStack>

View file

@ -124,7 +124,7 @@ const accountToCredentials = (account: Account): AccountCredentials => {
discoverable: account.discoverable, discoverable: account.discoverable,
bot: account.bot, bot: account.bot,
display_name: account.display_name, display_name: account.display_name,
note: account.source.get('note'), note: account.source.get('note', ''),
locked: account.locked, locked: account.locked,
fields_attributes: [...account.source.get<Iterable<AccountCredentialsField>>('fields', ImmutableList()).toJS()], fields_attributes: [...account.source.get<Iterable<AccountCredentialsField>>('fields', ImmutableList()).toJS()],
stranger_notifications: account.getIn(['pleroma', 'notification_settings', 'block_from_strangers']) === true, stranger_notifications: account.getIn(['pleroma', 'notification_settings', 'block_from_strangers']) === true,

View file

@ -74,7 +74,7 @@ const Settings = () => {
<CardBody> <CardBody>
<List> <List>
<ListItem label={intl.formatMessage(messages.editProfile)} onClick={navigateToEditProfile}> <ListItem label={intl.formatMessage(messages.editProfile)} onClick={navigateToEditProfile}>
<span>{displayName}</span> <span className='max-w-full truncate'>{displayName}</span>
</ListItem> </ListItem>
</List> </List>
</CardBody> </CardBody>

View file

@ -60,7 +60,7 @@ const MuteModal = () => {
<FormattedMessage <FormattedMessage
id='confirmations.mute.message' id='confirmations.mute.message'
defaultMessage='Are you sure you want to mute {name}?' defaultMessage='Are you sure you want to mute {name}?'
values={{ name: <strong>@{account.acct}</strong> }} values={{ name: <strong className='break-words'>@{account.acct}</strong> }}
/> />
</Text> </Text>

View file

@ -120,7 +120,7 @@ const ProfileInfoPanel: React.FC<IProfileInfoPanel> = ({ account, username }) =>
<Stack space={2}> <Stack space={2}>
<Stack> <Stack>
<HStack space={1} alignItems='center'> <HStack space={1} alignItems='center'>
<Text size='sm' theme='muted' direction='ltr'> <Text size='sm' theme='muted' direction='ltr' truncate>
@{username} @{username}
</Text> </Text>
</HStack> </HStack>

View file

@ -59,7 +59,7 @@ const UserPanel: React.FC<IUserPanel> = ({ accountId, action, badges, domain })
<Stack> <Stack>
<Link to={`/@${account.acct}`}> <Link to={`/@${account.acct}`}>
<HStack space={1} alignItems='center'> <HStack space={1} alignItems='center'>
<Text size='lg' weight='bold' dangerouslySetInnerHTML={displayNameHtml} /> <Text size='lg' weight='bold' dangerouslySetInnerHTML={displayNameHtml} truncate />
{verified && <VerificationBadge />} {verified && <VerificationBadge />}
@ -71,7 +71,7 @@ const UserPanel: React.FC<IUserPanel> = ({ accountId, action, badges, domain })
</HStack> </HStack>
</Link> </Link>
<Text size='sm' theme='muted'> <Text size='sm' theme='muted' truncate>
@{getAcct(account, fqn)} @{getAcct(account, fqn)}
</Text> </Text>
</Stack> </Stack>

View file

@ -16,6 +16,12 @@ const overrides = custom('features');
/** Truthy array convenience function */ /** Truthy array convenience function */
const any = (arr: Array<any>): boolean => arr.some(Boolean); const any = (arr: Array<any>): boolean => arr.some(Boolean);
/**
* Ditto, a Nostr server with Mastodon API.
* @see {@link https://gitlab.com/soapbox-pub/ditto}
*/
export const DITTO = 'Ditto';
/** /**
* Friendica, decentralized social platform implementing multiple federation protocols. * Friendica, decentralized social platform implementing multiple federation protocols.
* @see {@link https://friendi.ca/} * @see {@link https://friendi.ca/}
@ -137,6 +143,7 @@ const getInstanceFeatures = (instance: Instance) => {
v.software === PLEROMA && gte(v.version, '2.4.50'), v.software === PLEROMA && gte(v.version, '2.4.50'),
v.software === TAKAHE && gte(v.version, '0.6.1'), v.software === TAKAHE && gte(v.version, '0.6.1'),
v.software === TRUTHSOCIAL, v.software === TRUTHSOCIAL,
v.software === DITTO,
]), ]),
/** /**