Merge commit '7f16397f3c37a8e378239974b73afbfe2b6e6844' into glitch-soc/merge-upstream

Conflicts:
- `lib/mastodon/version.rb`:
  Upstream bumped the mastodon API version, glitch-soc has a change on an adjacent
  line adding a glitch API version.
This commit is contained in:
Claire
2026-02-27 21:25:37 +01:00
86 changed files with 1440 additions and 273 deletions

View File

@@ -26,6 +26,7 @@ exports[`<AvatarOverlay > renders a overlay avatar 1`] = `
>
<img
alt="alice"
onError={[Function]}
src="/static/alice.jpg"
/>
</div>
@@ -44,6 +45,7 @@ exports[`<AvatarOverlay > renders a overlay avatar 1`] = `
>
<img
alt="eve@blackhat.lair"
onError={[Function]}
src="/static/eve.jpg"
/>
</div>

View File

@@ -7,6 +7,8 @@ import { useHovering } from 'mastodon/hooks/useHovering';
import { autoPlayGif } from 'mastodon/initial_state';
import type { Account } from 'mastodon/models/account';
import { useAccount } from '../hooks/useAccount';
interface Props {
account:
| Pick<Account, 'id' | 'acct' | 'avatar' | 'avatar_static'>
@@ -91,3 +93,10 @@ export const Avatar: React.FC<Props> = ({
return avatar;
};
export const AvatarById: React.FC<
{ accountId: string } & Omit<Props, 'account'>
> = ({ accountId, ...otherProps }) => {
const account = useAccount(accountId);
return <Avatar account={account} {...otherProps} />;
};

View File

@@ -10,6 +10,14 @@ interface Props {
overlaySize?: number;
}
const handleImgLoadError = (error: { currentTarget: HTMLElement }) => {
//
// When the img tag fails to load the image, set the img tag to display: none. This prevents the
// alt-text from overrunning the containing div.
//
error.currentTarget.style.display = 'none';
};
export const AvatarOverlay: React.FC<Props> = ({
account,
friend,
@@ -38,7 +46,13 @@ export const AvatarOverlay: React.FC<Props> = ({
className='account__avatar'
style={{ width: `${baseSize}px`, height: `${baseSize}px` }}
>
{accountSrc && <img src={accountSrc} alt={account?.get('acct')} />}
{accountSrc && (
<img
src={accountSrc}
alt={account?.get('acct')}
onError={handleImgLoadError}
/>
)}
</div>
</div>
<div className='account__avatar-overlay-overlay'>
@@ -46,7 +60,13 @@ export const AvatarOverlay: React.FC<Props> = ({
className='account__avatar'
style={{ width: `${overlaySize}px`, height: `${overlaySize}px` }}
>
{friendSrc && <img src={friendSrc} alt={friend?.get('acct')} />}
{friendSrc && (
<img
src={friendSrc}
alt={friend?.get('acct')}
onError={handleImgLoadError}
/>
)}
</div>
</div>
</div>

View File

@@ -19,8 +19,9 @@ const messages = defineMessages({
export const CopyIconButton: React.FC<{
title: string;
value: string;
className: string;
}> = ({ title, value, className }) => {
className?: string;
'aria-describedby'?: string;
}> = ({ title, value, className, 'aria-describedby': ariaDescribedBy }) => {
const [copied, setCopied] = useState(false);
const dispatch = useAppDispatch();
@@ -38,8 +39,9 @@ export const CopyIconButton: React.FC<{
className={classNames(className, copied ? 'copied' : 'copyable')}
title={title}
onClick={handleClick}
icon=''
icon='copy-icon'
iconComponent={ContentCopyIcon}
aria-describedby={ariaDescribedBy}
/>
);
};

View File

@@ -0,0 +1,14 @@
.wrapper {
position: relative;
}
.input {
padding-inline-end: 45px;
}
.copyButton {
position: absolute;
inset-inline-end: 0;
top: 0;
padding: 9px;
}

View File

@@ -0,0 +1,81 @@
import { forwardRef, useCallback, useRef } from 'react';
import { useIntl } from 'react-intl';
import classNames from 'classnames';
import { CopyIconButton } from 'mastodon/components/copy_icon_button';
import classes from './copy_link_field.module.scss';
import { FormFieldWrapper } from './form_field_wrapper';
import type { CommonFieldWrapperProps } from './form_field_wrapper';
import { TextInput } from './text_input_field';
import type { TextInputProps } from './text_input_field';
interface CopyLinkFieldProps extends CommonFieldWrapperProps, TextInputProps {
value: string;
}
/**
* A read-only text field with a button for copying the field value
*/
export const CopyLinkField = forwardRef<HTMLInputElement, CopyLinkFieldProps>(
(
{ id, label, hint, hasError, value, required, className, ...otherProps },
ref,
) => {
const intl = useIntl();
const inputRef = useRef<HTMLInputElement | null>();
const handleFocus = useCallback(() => {
inputRef.current?.select();
}, []);
const mergeRefs = useCallback(
(element: HTMLInputElement | null) => {
inputRef.current = element;
if (typeof ref === 'function') {
ref(element);
} else if (ref) {
ref.current = element;
}
},
[ref],
);
return (
<FormFieldWrapper
label={label}
hint={hint}
required={required}
hasError={hasError}
inputId={id}
>
{(inputProps) => (
<div className={classes.wrapper}>
<TextInput
readOnly
{...otherProps}
{...inputProps}
ref={mergeRefs}
value={value}
onFocus={handleFocus}
className={classNames(className, classes.input)}
/>
<CopyIconButton
value={value}
title={intl.formatMessage({
id: 'copy_icon_button.copy_this_text',
defaultMessage: 'Copy link to clipboard',
})}
className={classes.copyButton}
aria-describedby={inputProps.id}
/>
</div>
)}
</FormFieldWrapper>
);
},
);
CopyLinkField.displayName = 'CopyLinkField';

View File

@@ -1,3 +1,4 @@
export { FormFieldWrapper } from './form_field_wrapper';
export { FormStack } from './form_stack';
export { Fieldset } from './fieldset';
export { TextInputField, TextInput } from './text_input_field';
@@ -8,6 +9,7 @@ export {
Combobox,
type ComboboxItemState,
} from './combobox_field';
export { CopyLinkField } from './copy_link_field';
export { RadioButtonField, RadioButton } from './radio_button_field';
export { ToggleField, Toggle } from './toggle_field';
export { SelectField, Select } from './select_field';

View File

@@ -0,0 +1,56 @@
import classNames from 'classnames';
interface SimpleComponentProps {
className?: string;
children?: React.ReactNode;
}
interface ModalShellComponent extends React.FC<SimpleComponentProps> {
Body: React.FC<SimpleComponentProps>;
Actions: React.FC<SimpleComponentProps>;
}
export const ModalShell: ModalShellComponent = ({ children, className }) => {
return (
<div
className={classNames(
'modal-root__modal',
'safety-action-modal',
className,
)}
>
{children}
</div>
);
};
const ModalShellBody: ModalShellComponent['Body'] = ({
children,
className,
}) => {
return (
<div className='safety-action-modal__top'>
<div
className={classNames('safety-action-modal__confirmation', className)}
>
{children}
</div>
</div>
);
};
const ModalShellActions: ModalShellComponent['Actions'] = ({
children,
className,
}) => {
return (
<div className='safety-action-modal__bottom'>
<div className={classNames('safety-action-modal__actions', className)}>
{children}
</div>
</div>
);
};
ModalShell.Body = ModalShellBody;
ModalShell.Actions = ModalShellActions;

View File

@@ -0,0 +1,122 @@
import type { ChangeEventHandler, FC } from 'react';
import { useCallback } from 'react';
import { FormattedMessage, useIntl } from 'react-intl';
import { Callout } from '@/mastodon/components/callout';
import { ToggleField } from '@/mastodon/components/form_fields';
import { LoadingIndicator } from '@/mastodon/components/loading_indicator';
import { patchProfile } from '@/mastodon/reducers/slices/profile_edit';
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
import type { DialogModalProps } from '../../ui/components/dialog_modal';
import { DialogModal } from '../../ui/components/dialog_modal';
import { messages } from '../index';
import classes from '../styles.module.scss';
export const ProfileDisplayModal: FC<DialogModalProps> = ({ onClose }) => {
const intl = useIntl();
const { profile, isPending } = useAppSelector((state) => state.profileEdit);
const serverName = useAppSelector(
(state) => state.meta.get('domain') as string,
);
const dispatch = useAppDispatch();
const handleToggleChange: ChangeEventHandler<HTMLInputElement> = useCallback(
(event) => {
const { name, checked } = event.target;
void dispatch(patchProfile({ [name]: checked }));
},
[dispatch],
);
if (!profile) {
return <LoadingIndicator />;
}
return (
<DialogModal
onClose={onClose}
title={intl.formatMessage(messages.profileTabTitle)}
noCancelButton
>
<div className={classes.toggleInputWrapper}>
<ToggleField
checked={profile.showMedia}
onChange={handleToggleChange}
disabled={isPending}
name='show_media'
label={
<FormattedMessage
id='account_edit.profile_tab.show_media.title'
defaultMessage='Show Media tab'
/>
}
hint={
<FormattedMessage
id='account_edit.profile_tab.show_media.description'
defaultMessage='Media is an optional tab that shows your posts containing images or videos.'
/>
}
/>
<ToggleField
checked={profile.showMediaReplies}
onChange={handleToggleChange}
disabled={!profile.showMedia || isPending}
name='show_media_replies'
label={
<FormattedMessage
id='account_edit.profile_tab.show_media_replies.title'
defaultMessage='Include replies on Media tab'
/>
}
hint={
<FormattedMessage
id='account_edit.profile_tab.show_media_replies.description'
defaultMessage='When enabled, Media tab shows both your posts and replies to other peoples posts.'
/>
}
/>
<ToggleField
checked={profile.showFeatured}
onChange={handleToggleChange}
disabled={isPending}
name='show_featured'
label={
<FormattedMessage
id='account_edit.profile_tab.show_featured.title'
defaultMessage='Show Featured tab'
/>
}
hint={
<FormattedMessage
id='account_edit.profile_tab.show_featured.description'
defaultMessage='Featured is an optional tab where you can showcase other accounts.'
/>
}
/>
</div>
<Callout
title={
<FormattedMessage
id='account_edit.profile_tab.hint.title'
defaultMessage='Displays still vary'
/>
}
icon={false}
>
<FormattedMessage
id='account_edit.profile_tab.hint.description'
defaultMessage='These settings customize what users see on {server} in the official apps, but they may not apply to users on other servers and 3rd party apps.'
values={{
server: serverName,
}}
/>
</Callout>
</DialogModal>
);
};

View File

@@ -1,13 +1,14 @@
import { useCallback, useEffect } from 'react';
import type { FC } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { useHistory } from 'react-router-dom';
import type { ModalType } from '@/mastodon/actions/modal';
import { openModal } from '@/mastodon/actions/modal';
import { Avatar } from '@/mastodon/components/avatar';
import { Button } from '@/mastodon/components/button';
import { CustomEmojiProvider } from '@/mastodon/components/emoji/context';
import { EmojiHTML } from '@/mastodon/components/emoji/html';
import { useElementHandledLink } from '@/mastodon/components/status/handled_link';
@@ -25,7 +26,7 @@ import { EditButton } from './components/edit_button';
import { AccountEditSection } from './components/section';
import classes from './styles.module.scss';
const messages = defineMessages({
export const messages = defineMessages({
columnTitle: {
id: 'account_edit.column_title',
defaultMessage: 'Edit Profile',
@@ -104,6 +105,9 @@ export const AccountEdit: FC = () => {
const handleBioEdit = useCallback(() => {
handleOpenModal('ACCOUNT_EDIT_BIO');
}, [handleOpenModal]);
const handleProfileDisplayEdit = useCallback(() => {
handleOpenModal('ACCOUNT_EDIT_PROFILE_DISPLAY');
}, [handleOpenModal]);
const history = useHistory();
const handleFeaturedTagsEdit = useCallback(() => {
@@ -193,6 +197,17 @@ export const AccountEdit: FC = () => {
title={messages.profileTabTitle}
description={messages.profileTabSubtitle}
showDescription
buttons={
<Button
className={classes.editButton}
onClick={handleProfileDisplayEdit}
>
<FormattedMessage
id='account_edit.profile_tab.button_label'
defaultMessage='Customize'
/>
</Button>
}
/>
</CustomEmojiProvider>
</AccountEditColumn>

View File

@@ -90,6 +90,16 @@ textarea.inputText {
}
}
.toggleInputWrapper {
> div {
padding: 12px 0;
&:not(:first-child) {
border-top: 1px solid var(--color-border-primary);
}
}
}
// Column component
.column {

View File

@@ -118,14 +118,14 @@ const RedesignAccountHeaderFields: FC<{ account: Account }> = ({ account }) => {
<CustomEmojiProvider emojis={emojis}>
<dl className={classes.fieldList} ref={wrapperRef}>
{fields.map((field, key) => (
<FieldRow key={key} field={field} htmlHandlers={htmlHandlers} />
<FieldCard key={key} field={field} htmlHandlers={htmlHandlers} />
))}
</dl>
</CustomEmojiProvider>
);
};
const FieldRow: FC<{
const FieldCard: FC<{
htmlHandlers: ReturnType<typeof useElementHandledLink>;
field: AccountField;
}> = ({ htmlHandlers, field }) => {
@@ -183,15 +183,14 @@ const FieldRow: FC<{
ref={wrapperRef}
>
{verified_at && (
<Icon
id='verified'
icon={IconVerified}
<span
className={classes.fieldVerifiedIcon}
aria-label={intl.formatMessage(verifyMessage, {
title={intl.formatMessage(verifyMessage, {
date: intl.formatDate(verified_at, dateFormatOptions),
})}
noFill
/>
>
<Icon id='verified' icon={IconVerified} noFill />
</span>
)}
</MiniCard>
);

View File

@@ -278,11 +278,17 @@ svg.badgeIcon {
}
.fieldVerifiedIcon {
display: block;
position: absolute;
width: 16px;
height: 16px;
position: absolute;
top: 8px;
right: 8px;
> svg {
width: 100%;
height: 100%;
}
}
.fieldOverflowButton {

View File

@@ -2,7 +2,9 @@ import type { FC } from 'react';
import { FormattedMessage } from 'react-intl';
import { Button } from '@/mastodon/components/button';
import { EmojiHTML } from '@/mastodon/components/emoji/html';
import { ModalShell } from '@/mastodon/components/modal_shell';
import type { AccountField } from '../common';
import { useFieldHtml } from '../hooks/useFieldHtml';
@@ -16,29 +18,25 @@ export const AccountFieldModal: FC<{
const handleLabelElement = useFieldHtml(field.nameHasEmojis);
const handleValueElement = useFieldHtml(field.valueHasEmojis);
return (
<div className='modal-root__modal safety-action-modal'>
<div className='safety-action-modal__top'>
<div className='safety-action-modal__confirmation'>
<EmojiHTML
as='p'
htmlString={field.name_emojified}
onElement={handleLabelElement}
/>
<EmojiHTML
as='p'
htmlString={field.value_emojified}
onElement={handleValueElement}
className={classes.fieldValue}
/>
</div>
</div>
<div className='safety-action-modal__bottom'>
<div className='safety-action-modal__actions'>
<button onClick={onClose} className='link-button' type='button'>
<FormattedMessage id='lightbox.close' defaultMessage='Close' />
</button>
</div>
</div>
</div>
<ModalShell>
<ModalShell.Body>
<EmojiHTML
as='h2'
htmlString={field.name_emojified}
onElement={handleLabelElement}
/>
<EmojiHTML
as='p'
htmlString={field.value_emojified}
onElement={handleValueElement}
className={classes.fieldValue}
/>
</ModalShell.Body>
<ModalShell.Actions>
<Button onClick={onClose} plain>
<FormattedMessage id='lightbox.close' defaultMessage='Close' />
</Button>
</ModalShell.Actions>
</ModalShell>
);
};

View File

@@ -7,6 +7,7 @@
border: none;
background: none;
padding: 8px 0;
font-size: 15px;
font-weight: 500;
display: flex;
align-items: center;
@@ -41,6 +42,10 @@
align-items: center;
font-size: 15px;
}
[data-color-scheme='dark'] & {
border: 1px solid var(--color-border-primary);
}
}
.tagsWrapper {

View File

@@ -44,6 +44,7 @@
--gap: 0.75ch;
display: flex;
flex-wrap: wrap;
gap: var(--gap);
& > li:not(:last-child)::after {

View File

@@ -3,18 +3,21 @@ import { useCallback, useEffect } from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { Helmet } from 'react-helmet';
import { useParams } from 'react-router';
import { useLocation, useParams } from 'react-router';
import { openModal } from '@/mastodon/actions/modal';
import { useRelationship } from '@/mastodon/hooks/useRelationship';
import ListAltIcon from '@/material-icons/400-24px/list_alt.svg?react';
import ShareIcon from '@/material-icons/400-24px/share.svg?react';
import { showAlert } from 'mastodon/actions/alerts';
import type { ApiCollectionJSON } from 'mastodon/api_types/collections';
import { Account } from 'mastodon/components/account';
import { Avatar } from 'mastodon/components/avatar';
import { Column } from 'mastodon/components/column';
import { ColumnHeader } from 'mastodon/components/column_header';
import { LinkedDisplayName } from 'mastodon/components/display_name';
import {
DisplayName,
LinkedDisplayName,
} from 'mastodon/components/display_name';
import { IconButton } from 'mastodon/components/icon_button';
import ScrollableList from 'mastodon/components/scrollable_list';
import { Tag } from 'mastodon/components/tags/tag';
@@ -46,32 +49,40 @@ const messages = defineMessages({
},
});
const AuthorNote: React.FC<{ id: string }> = ({ id }) => {
export const AuthorNote: React.FC<{ id: string; previewMode?: boolean }> = ({
id,
// When previewMode is enabled, your own display name
// will not be replaced with "you"
previewMode = false,
}) => {
const account = useAccount(id);
const author = (
<span className={classes.displayNameWithAvatar}>
<Avatar size={18} account={account} />
<LinkedDisplayName displayProps={{ account, variant: 'simple' }} />
{previewMode ? (
<DisplayName account={account} variant='simple' />
) : (
<LinkedDisplayName displayProps={{ account, variant: 'simple' }} />
)}
</span>
);
if (id === me) {
return (
<p className={classes.authorNote}>
const displayAsYou = id === me && !previewMode;
return (
<p className={previewMode ? classes.previewAuthorNote : classes.authorNote}>
{displayAsYou ? (
<FormattedMessage
id='collections.detail.curated_by_you'
defaultMessage='Curated by you'
/>
</p>
);
}
return (
<p className={classes.authorNote}>
<FormattedMessage
id='collections.detail.curated_by_author'
defaultMessage='Curated by {author}'
values={{ author }}
/>
) : (
<FormattedMessage
id='collections.detail.curated_by_author'
defaultMessage='Curated by {author}'
values={{ author }}
/>
)}
</p>
);
};
@@ -84,8 +95,23 @@ const CollectionHeader: React.FC<{ collection: ApiCollectionJSON }> = ({
const dispatch = useAppDispatch();
const handleShare = useCallback(() => {
dispatch(showAlert({ message: 'Collection sharing not yet implemented' }));
}, [dispatch]);
dispatch(
openModal({
modalType: 'SHARE_COLLECTION',
modalProps: {
collection,
},
}),
);
}, [collection, dispatch]);
const location = useLocation<{ newCollection?: boolean }>();
const wasJustCreated = location.state.newCollection;
useEffect(() => {
if (wasJustCreated) {
handleShare();
}
}, [handleShare, wasJustCreated]);
return (
<div className={classes.header}>

View File

@@ -0,0 +1,75 @@
.heading {
font-size: 28px;
line-height: 1.3;
margin-bottom: 16px;
}
.preview {
display: flex;
flex-wrap: wrap-reverse;
align-items: start;
justify-content: space-between;
gap: 8px;
padding: 16px;
margin-bottom: 16px;
border-radius: 8px;
color: var(--color-text-primary);
background: linear-gradient(
145deg,
var(--color-bg-brand-soft),
var(--color-bg-primary)
);
border: 1px solid var(--color-bg-brand-base);
}
.previewHeading {
font-size: 22px;
line-height: 1.3;
margin-bottom: 4px;
}
.actions {
display: flex;
flex-direction: column;
justify-content: center;
}
$bottomsheet-breakpoint: 630px;
.shareButtonWrapper {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 8px;
width: 100%;
& > button {
flex: 1;
min-width: 220px;
white-space: normal;
@media (width > $bottomsheet-breakpoint) {
max-width: 50%;
}
}
}
.closeButtonDesktop {
position: absolute;
top: 4px;
inset-inline-end: 4px;
padding: 8px;
@media (width <= $bottomsheet-breakpoint) {
display: none;
}
}
.closeButtonMobile {
margin-top: 16px;
margin-bottom: -18px;
@media (width > $bottomsheet-breakpoint) {
display: none;
}
}

View File

@@ -0,0 +1,141 @@
import { useCallback } from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { useLocation } from 'react-router';
import { me } from '@/mastodon/initial_state';
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
import { changeCompose, focusCompose } from 'mastodon/actions/compose';
import type { ApiCollectionJSON } from 'mastodon/api_types/collections';
import { AvatarById } from 'mastodon/components/avatar';
import { AvatarGroup } from 'mastodon/components/avatar_group';
import { Button } from 'mastodon/components/button';
import { CopyLinkField } from 'mastodon/components/form_fields';
import { IconButton } from 'mastodon/components/icon_button';
import { ModalShell } from 'mastodon/components/modal_shell';
import { useAppDispatch } from 'mastodon/store';
import { AuthorNote } from '.';
import classes from './share_modal.module.scss';
const messages = defineMessages({
shareTextOwn: {
id: 'collection.share_template_own',
defaultMessage: 'Check out my new collection: {link}',
},
shareTextOther: {
id: 'collection.share_template_other',
defaultMessage: 'Check out this cool collection: {link}',
},
});
export const CollectionShareModal: React.FC<{
collection: ApiCollectionJSON;
onClose: () => void;
}> = ({ collection, onClose }) => {
const intl = useIntl();
const dispatch = useAppDispatch();
const location = useLocation<{ newCollection?: boolean }>();
const isNew = !!location.state.newCollection;
const isOwnCollection = collection.account_id === me;
const collectionLink = `${window.location.origin}/collections/${collection.id}`;
const handleShareOnDevice = useCallback(() => {
void navigator.share({
url: collectionLink,
});
}, [collectionLink]);
const handleShareViaPost = useCallback(() => {
const shareMessage = isOwnCollection
? intl.formatMessage(messages.shareTextOwn, {
link: collectionLink,
})
: intl.formatMessage(messages.shareTextOther, {
link: collectionLink,
});
onClose();
dispatch(changeCompose(shareMessage));
dispatch(focusCompose());
}, [collectionLink, dispatch, intl, isOwnCollection, onClose]);
return (
<ModalShell>
<ModalShell.Body>
<h1 className={classes.heading}>
{isNew ? (
<FormattedMessage
id='collection.share_modal.title_new'
defaultMessage='Share your new collection!'
/>
) : (
<FormattedMessage
id='collection.share_modal.title'
defaultMessage='Share collection'
/>
)}
</h1>
<IconButton
title={intl.formatMessage({
id: 'lightbox.close',
defaultMessage: 'Close',
})}
iconComponent={CloseIcon}
icon='close'
className={classes.closeButtonDesktop}
onClick={onClose}
/>
<div className={classes.preview}>
<div>
<h2 className={classes.previewHeading}>{collection.name}</h2>
<AuthorNote previewMode id={collection.account_id} />
</div>
<AvatarGroup>
{collection.items.slice(0, 5).map(({ account_id }) => {
if (!account_id) return;
return (
<AvatarById key={account_id} accountId={account_id} size={28} />
);
})}
</AvatarGroup>
</div>
<CopyLinkField
label={intl.formatMessage({
id: 'collection.share_modal.share_link_label',
defaultMessage: 'Invite share link',
})}
value={collectionLink}
/>
</ModalShell.Body>
<ModalShell.Actions className={classes.actions}>
<div className={classes.shareButtonWrapper}>
<Button secondary onClick={handleShareViaPost}>
<FormattedMessage
id='collection.share_modal.share_via_post'
defaultMessage='Post on Mastodon'
/>
</Button>
{'share' in navigator && (
<Button secondary onClick={handleShareOnDevice}>
<FormattedMessage
id='collection.share_modal.share_via_system'
defaultMessage='Share to…'
/>
</Button>
)}
</div>
<Button plain onClick={onClose} className={classes.closeButtonMobile}>
<FormattedMessage id='lightbox.close' defaultMessage='Close' />
</Button>
</ModalShell.Actions>
</ModalShell>
);
};

View File

@@ -48,6 +48,10 @@
color: var(--color-text-secondary);
}
.previewAuthorNote {
font-size: 13px;
}
.metaData {
margin-top: 16px;
font-size: 15px;

View File

@@ -127,7 +127,9 @@ export const CollectionDetails: React.FC<{
history.replace(
`/collections/${result.payload.collection.id}/edit/details`,
);
history.push(`/collections/${result.payload.collection.id}`);
history.push(`/collections/${result.payload.collection.id}`, {
newCollection: true,
});
}
});
}

View File

@@ -3,6 +3,7 @@ import { useCallback } from 'react';
import { FormattedMessage } from 'react-intl';
import { Button } from 'mastodon/components/button';
import { ModalShell } from 'mastodon/components/modal_shell';
export interface BaseConfirmationModalProps {
onClose: () => void;
@@ -56,53 +57,49 @@ export const ConfirmationModal: React.FC<
}, [onClose, onSecondary]);
return (
<div className='modal-root__modal safety-action-modal'>
<div className='safety-action-modal__top'>
<div className='safety-action-modal__confirmation'>
<h1 id={titleId}>{title}</h1>
{message && <p>{message}</p>}
<ModalShell>
<ModalShell.Body>
<h1 id={titleId}>{title}</h1>
{message && <p>{message}</p>}
{extraContent ?? children}
</div>
</div>
{extraContent ?? children}
</ModalShell.Body>
<div className='safety-action-modal__bottom'>
<div className='safety-action-modal__actions'>
<button onClick={onClose} className='link-button' type='button'>
{cancel ?? (
<FormattedMessage
id='confirmation_modal.cancel'
defaultMessage='Cancel'
/>
)}
</button>
{secondary && (
<>
<div className='spacer' />
<button
onClick={handleSecondary}
className='link-button'
type='button'
disabled={disabled}
>
{secondary}
</button>
</>
<ModalShell.Actions>
<button onClick={onClose} className='link-button' type='button'>
{cancel ?? (
<FormattedMessage
id='confirmation_modal.cancel'
defaultMessage='Cancel'
/>
)}
</button>
{/* eslint-disable jsx-a11y/no-autofocus -- we are in a modal and thus autofocusing is justified */}
<Button
onClick={handleClick}
loading={updating}
disabled={disabled}
autoFocus={!noFocusButton}
>
{confirm}
</Button>
{/* eslint-enable */}
</div>
</div>
</div>
{secondary && (
<>
<div className='spacer' />
<button
onClick={handleSecondary}
className='link-button'
type='button'
disabled={disabled}
>
{secondary}
</button>
</>
)}
{/* eslint-disable jsx-a11y/no-autofocus -- we are in a modal and thus autofocusing is justified */}
<Button
onClick={handleClick}
loading={updating}
disabled={disabled}
autoFocus={!noFocusButton}
>
{confirm}
</Button>
{/* eslint-enable */}
</ModalShell.Actions>
</ModalShell>
);
};

View File

@@ -0,0 +1,94 @@
import type { FC, ReactNode } from 'react';
import { FormattedMessage, useIntl } from 'react-intl';
import classNames from 'classnames';
import { Button } from '@/mastodon/components/button';
import { IconButton } from '@/mastodon/components/icon_button';
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
export type { BaseConfirmationModalProps as DialogModalProps } from './confirmation_modals/confirmation_modal';
interface DialogModalProps {
className?: string;
title: ReactNode;
onClose: () => void;
description?: ReactNode;
formClassName?: string;
children?: ReactNode;
noCancelButton?: boolean;
onSave?: () => void;
saveLabel?: ReactNode;
}
export const DialogModal: FC<DialogModalProps> = ({
className,
title,
onClose,
description,
formClassName,
children,
noCancelButton = false,
onSave,
saveLabel,
}) => {
const intl = useIntl();
const showButtons = !noCancelButton || onSave;
return (
<div className={classNames('modal-root__modal dialog-modal', className)}>
<div className='dialog-modal__header'>
<IconButton
className='dialog-modal__header__close'
title={intl.formatMessage({
id: 'lightbox.close',
defaultMessage: 'Close',
})}
icon='close'
iconComponent={CloseIcon}
onClick={onClose}
/>
<h1 className='dialog-modal__header__title'>{title}</h1>
</div>
<div className='dialog-modal__content'>
{description && (
<div className='dialog-modal__content__description'>
{description}
</div>
)}
<div
className={classNames('dialog-modal__content__form', formClassName)}
>
{children}
</div>
</div>
{showButtons && (
<div className='dialog-modal__content__actions'>
{!noCancelButton && (
<Button onClick={onClose} secondary>
<FormattedMessage
id='confirmation_modal.cancel'
defaultMessage='Cancel'
/>
</Button>
)}
{onSave && (
<Button onClick={onClose}>
{saveLabel ?? (
<FormattedMessage
id='confirmation_modal.cancel'
defaultMessage='Cancel'
/>
)}
</Button>
)}
</div>
)}
</div>
);
};

View File

@@ -11,6 +11,7 @@ import {
DomainBlockModal,
ReportModal,
ReportCollectionModal,
ShareCollectionModal,
EmbedModal,
ListAdder,
CompareHistoryModal,
@@ -79,6 +80,7 @@ export const MODAL_COMPONENTS = {
'DOMAIN_BLOCK': DomainBlockModal,
'REPORT': ReportModal,
'REPORT_COLLECTION': ReportCollectionModal,
'SHARE_COLLECTION': ShareCollectionModal,
'ACTIONS': () => Promise.resolve({ default: ActionsModal }),
'EMBED': EmbedModal,
'FOCAL_POINT': () => Promise.resolve({ default: AltTextModal }),
@@ -95,6 +97,7 @@ export const MODAL_COMPONENTS = {
'ACCOUNT_FIELD_OVERFLOW': () => import('@/mastodon/features/account_timeline/modals/field_modal').then(module => ({ default: module.AccountFieldModal })),
'ACCOUNT_EDIT_NAME': () => import('@/mastodon/features/account_edit/components/name_modal').then(module => ({ default: module.NameModal })),
'ACCOUNT_EDIT_BIO': () => import('@/mastodon/features/account_edit/components/bio_modal').then(module => ({ default: module.BioModal })),
'ACCOUNT_EDIT_PROFILE_DISPLAY': () => import('@/mastodon/features/account_edit/components/profile_display_modal').then(module => ({ default: module.ProfileDisplayModal })),
};
export default class ModalRoot extends PureComponent {

View File

@@ -62,6 +62,12 @@ export function CollectionsEditor() {
);
}
export function ShareCollectionModal() {
return import('../../collections/detail/share_modal').then(
module => ({default: module.CollectionShareModal})
);
}
export function Status () {
return import('../../status');
}

View File

@@ -271,6 +271,13 @@
"closed_registrations_modal.find_another_server": "Знайсці іншы сервер",
"closed_registrations_modal.preamble": "Mastodon дэцэнтралізаваны, так што дзе б вы ні стварылі ўліковы запіс, вы зможаце падпісвацца і камунікаваць з кім хочаце на гэтым серверы. Вы нават можаце стварыць свой!",
"closed_registrations_modal.title": "Рэгістрацыя ў Mastodon",
"collection.share_modal.share_link_label": "Падзяліцца спасылкай",
"collection.share_modal.share_via_post": "Апублікаваць у Mastodon",
"collection.share_modal.share_via_system": "Падзяліцца ў…",
"collection.share_modal.title": "Падзяліцца калекцыяй",
"collection.share_modal.title_new": "Падзяліцеся сваёй калекцыяй!",
"collection.share_template_other": "Глядзі, якая класная калекцыя: {link}",
"collection.share_template_own": "Глядзі, у мяне новая калекцыя: {link}",
"collections.account_count": "{count, plural,one {# уліковы запіс} few {# уліковыя запісы} other {# уліковых запісаў}}",
"collections.accounts.empty_description": "Дадайце да {count} уліковых запісаў, на якія Вы падпісаныя",
"collections.accounts.empty_title": "Гэтая калекцыя пустая",
@@ -448,6 +455,7 @@
"conversation.open": "Прагледзець размову",
"conversation.with": "З {names}",
"copy_icon_button.copied": "Скапіявана ў буфер абмену",
"copy_icon_button.copy_this_text": "Скапіяваць спасылку ў буфер абмену",
"copypaste.copied": "Скапіравана",
"copypaste.copy_to_clipboard": "Скапіяваць у буфер абмену",
"directory.federated": "З вядомага федэральнага сусвету",

View File

@@ -271,6 +271,12 @@
"closed_registrations_modal.find_another_server": "Find en anden server",
"closed_registrations_modal.preamble": "Mastodon er decentraliseret, så uanset hvor du opretter din konto, vil du være i stand til at følge og interagere med hvem som helst på denne server. Du kan endda selv være vært for den!",
"closed_registrations_modal.title": "Oprettelse på Mastodon",
"collection.share_modal.share_link_label": "Invitationlink til deling",
"collection.share_modal.share_via_system": "Del med…",
"collection.share_modal.title": "Del samling",
"collection.share_modal.title_new": "Del din nye samling!",
"collection.share_template_other": "Tjek denne seje samling: {link}",
"collection.share_template_own": "Tjek min nye samling: {link}",
"collections.account_count": "{count, plural, one {# konto} other {# konti}}",
"collections.accounts.empty_description": "Tilføj op til {count} konti, du følger",
"collections.accounts.empty_title": "Denne samling er tom",
@@ -448,6 +454,7 @@
"conversation.open": "Vis samtale",
"conversation.with": "Med {names}",
"copy_icon_button.copied": "Kopieret til udklipsholderen",
"copy_icon_button.copy_this_text": "Kopiér link til udklipsholderen",
"copypaste.copied": "Kopieret",
"copypaste.copy_to_clipboard": "Kopiér til udklipsholder",
"directory.federated": "Fra kendt fediverse",

View File

@@ -271,6 +271,13 @@
"closed_registrations_modal.find_another_server": "Anderen Server suchen",
"closed_registrations_modal.preamble": "Mastodon ist dezentralisiert, das heißt, unabhängig davon, wo du dein Konto erstellst, kannst du jedem Profil auf diesem Server folgen und mit ihm interagieren. Du kannst sogar deinen eigenen Mastodon-Server hosten!",
"closed_registrations_modal.title": "Bei Mastodon registrieren",
"collection.share_modal.share_link_label": "Link zum Teilen",
"collection.share_modal.share_via_post": "Auf Mastodon veröffentlichen",
"collection.share_modal.share_via_system": "Teilen …",
"collection.share_modal.title": "Sammlung teilen",
"collection.share_modal.title_new": "Teile deine neue Sammlung!",
"collection.share_template_other": "Seht euch diese coole Sammlung an: {link}",
"collection.share_template_own": "Seht euch meine neue Sammlung an: {link}",
"collections.account_count": "{count, plural, one {# Konto} other {# Konten}}",
"collections.accounts.empty_description": "Füge bis zu {count} Konten, denen du folgst, hinzu",
"collections.accounts.empty_title": "Diese Sammlung ist leer",
@@ -448,6 +455,7 @@
"conversation.open": "Unterhaltung anzeigen",
"conversation.with": "Mit {names}",
"copy_icon_button.copied": "In die Zwischenablage kopiert",
"copy_icon_button.copy_this_text": "Link in die Zwischenablage kopieren",
"copypaste.copied": "Kopiert",
"copypaste.copy_to_clipboard": "In die Zwischenablage kopieren",
"directory.federated": "Aus bekanntem Fediverse",

View File

@@ -271,6 +271,13 @@
"closed_registrations_modal.find_another_server": "Βρες άλλον διακομιστή",
"closed_registrations_modal.preamble": "Το Mastodon είναι αποκεντρωμένο, οπότε ανεξάρτητα από το πού θα δημιουργήσεις τον λογαριασμό σου, μπορείς να ακολουθήσεις και να αλληλεπιδράσεις με οποιονδήποτε σε αυτόν τον διακομιστή. Μπορείς ακόμη και να κάνεις τον δικό σου!",
"closed_registrations_modal.title": "Εγγραφή στο Mastodon",
"collection.share_modal.share_link_label": "Σύνδεσμος κοινοποίησης",
"collection.share_modal.share_via_post": "Ανάρτηση στο Mastodon",
"collection.share_modal.share_via_system": "Κοινοποίηση σε…",
"collection.share_modal.title": "Κοινοποίηση συλλογής",
"collection.share_modal.title_new": "Μοιραστείτε τη νέα σας συλλογή!",
"collection.share_template_other": "Δείτε αυτή την ωραία συλλογή: {link}",
"collection.share_template_own": "Δείτε τη νέα μου συλλογή: {link}",
"collections.account_count": "{count, plural, one {# λογαριασμός} other {# λογαριασμοί}}",
"collections.accounts.empty_description": "Προσθέστε μέχρι και {count} λογαριασμούς που ακολουθείτε",
"collections.accounts.empty_title": "Αυτή η συλλογή είναι κενή",
@@ -448,6 +455,7 @@
"conversation.open": "Προβολή συνομιλίας",
"conversation.with": "Με {names}",
"copy_icon_button.copied": "Αντιγράφηκε στο πρόχειρο",
"copy_icon_button.copy_this_text": "Αντιγραφή συνδέσμου στο πρόχειρο",
"copypaste.copied": "Αντιγράφηκε",
"copypaste.copy_to_clipboard": "Αντιγραφή στο πρόχειρο",
"directory.federated": "Από το γνωστό fediverse",

View File

@@ -271,6 +271,13 @@
"closed_registrations_modal.find_another_server": "Find another server",
"closed_registrations_modal.preamble": "Mastodon is decentralised, so no matter where you create your account, you will be able to follow and interact with anyone on this server. You can even self-host it!",
"closed_registrations_modal.title": "Signing up on Mastodon",
"collection.share_modal.share_link_label": "Invite share link",
"collection.share_modal.share_via_post": "Post on Mastodon",
"collection.share_modal.share_via_system": "Share to…",
"collection.share_modal.title": "Share collection",
"collection.share_modal.title_new": "Share your new collection!",
"collection.share_template_other": "Check out this cool collection: {link}",
"collection.share_template_own": "Check out my new collection: {link}",
"collections.account_count": "{count, plural, one {# account} other {# accounts}}",
"collections.accounts.empty_description": "Add up to {count} accounts you follow",
"collections.accounts.empty_title": "This collection is empty",
@@ -448,6 +455,7 @@
"conversation.open": "View conversation",
"conversation.with": "With {names}",
"copy_icon_button.copied": "Copied to clipboard",
"copy_icon_button.copy_this_text": "Copy link to clipboard",
"copypaste.copied": "Copied",
"copypaste.copy_to_clipboard": "Copy to clipboard",
"directory.federated": "From known fediverse",

View File

@@ -161,6 +161,15 @@
"account_edit.featured_hashtags.title": "Featured hashtags",
"account_edit.name_modal.add_title": "Add display name",
"account_edit.name_modal.edit_title": "Edit display name",
"account_edit.profile_tab.button_label": "Customize",
"account_edit.profile_tab.hint.description": "These settings customize what users see on {server} in the official apps, but they may not apply to users on other servers and 3rd party apps.",
"account_edit.profile_tab.hint.title": "Displays still vary",
"account_edit.profile_tab.show_featured.description": "Featured is an optional tab where you can showcase other accounts.",
"account_edit.profile_tab.show_featured.title": "Show Featured tab",
"account_edit.profile_tab.show_media.description": "Media is an optional tab that shows your posts containing images or videos.",
"account_edit.profile_tab.show_media.title": "Show Media tab",
"account_edit.profile_tab.show_media_replies.description": "When enabled, Media tab shows both your posts and replies to other peoples posts.",
"account_edit.profile_tab.show_media_replies.title": "Include replies on Media tab",
"account_edit.profile_tab.subtitle": "Customize the tabs on your profile and what they display.",
"account_edit.profile_tab.title": "Profile tab settings",
"account_edit.save": "Save",
@@ -271,6 +280,13 @@
"closed_registrations_modal.find_another_server": "Find another server",
"closed_registrations_modal.preamble": "Mastodon is decentralized, so no matter where you create your account, you will be able to follow and interact with anyone on this server. You can even self-host it!",
"closed_registrations_modal.title": "Signing up on Mastodon",
"collection.share_modal.share_link_label": "Invite share link",
"collection.share_modal.share_via_post": "Post on Mastodon",
"collection.share_modal.share_via_system": "Share to…",
"collection.share_modal.title": "Share collection",
"collection.share_modal.title_new": "Share your new collection!",
"collection.share_template_other": "Check out this cool collection: {link}",
"collection.share_template_own": "Check out my new collection: {link}",
"collections.account_count": "{count, plural, one {# account} other {# accounts}}",
"collections.accounts.empty_description": "Add up to {count} accounts you follow",
"collections.accounts.empty_title": "This collection is empty",
@@ -448,6 +464,7 @@
"conversation.open": "View conversation",
"conversation.with": "With {names}",
"copy_icon_button.copied": "Copied to clipboard",
"copy_icon_button.copy_this_text": "Copy link to clipboard",
"copypaste.copied": "Copied",
"copypaste.copy_to_clipboard": "Copy to clipboard",
"directory.federated": "From known fediverse",

View File

@@ -271,6 +271,13 @@
"closed_registrations_modal.find_another_server": "Buscar otro servidor",
"closed_registrations_modal.preamble": "Mastodon es descentralizado, por lo que no importa dónde creés tu cuenta, podrás seguir e interactuar con cualquier persona en este servidor. ¡Incluso podés montar tu propio servidor!",
"closed_registrations_modal.title": "Registrarse en Mastodon",
"collection.share_modal.share_link_label": "Enlace para compartir",
"collection.share_modal.share_via_post": "Enviar a Mastodon",
"collection.share_modal.share_via_system": "Compartir en…",
"collection.share_modal.title": "Compartir colección",
"collection.share_modal.title_new": "¡Compartí tu nueva colección!",
"collection.share_template_other": "¡Mirá qué copada está esta colección! {link}",
"collection.share_template_own": "Mirá mi nueva colección: {link}",
"collections.account_count": "{count, plural, one {# hora} other {# horas}}",
"collections.accounts.empty_description": "Agregá hasta {count} cuentas que seguís",
"collections.accounts.empty_title": "Esta colección está vacía",
@@ -448,6 +455,7 @@
"conversation.open": "Ver conversación",
"conversation.with": "Con {names}",
"copy_icon_button.copied": "Copiado en el portapapeles",
"copy_icon_button.copy_this_text": "Copiar enlace al portapapeles",
"copypaste.copied": "Copiado",
"copypaste.copy_to_clipboard": "Copiar al portapapeles",
"directory.federated": "Desde fediverso conocido",

View File

@@ -271,6 +271,13 @@
"closed_registrations_modal.find_another_server": "Buscar otro servidor",
"closed_registrations_modal.preamble": "Mastodon es descentralizado, por lo que no importa dónde crees tu cuenta, podrás seguir e interactuar con cualquier persona en este servidor. ¡Incluso puedes alojarlo tú mismo!",
"closed_registrations_modal.title": "Registrarse en Mastodon",
"collection.share_modal.share_link_label": "Enlace para compartir",
"collection.share_modal.share_via_post": "Publicar en Mastodon",
"collection.share_modal.share_via_system": "Compartir con…",
"collection.share_modal.title": "Compartir la colección",
"collection.share_modal.title_new": "¡Comparte tu nueva colección!",
"collection.share_template_other": "Echa un vistazo a esta increíble colección: {link}",
"collection.share_template_own": "Echa un vistazo a mi nueva colección: {link}",
"collections.account_count": "{count, plural,one {# cuenta} other {# cuentas}}",
"collections.accounts.empty_description": "Añade hasta {count} cuentas que sigues",
"collections.accounts.empty_title": "Esta colección está vacía",
@@ -448,6 +455,7 @@
"conversation.open": "Ver conversación",
"conversation.with": "Con {names}",
"copy_icon_button.copied": "Copiado al portapapeles",
"copy_icon_button.copy_this_text": "Copiar enlace al portapapeles",
"copypaste.copied": "Copiado",
"copypaste.copy_to_clipboard": "Copiar al portapapeles",
"directory.federated": "Desde el fediverso conocido",

View File

@@ -271,6 +271,13 @@
"closed_registrations_modal.find_another_server": "Buscar otro servidor",
"closed_registrations_modal.preamble": "Mastodon es descentralizado, por lo que no importa dónde crees tu cuenta, podrás seguir e interactuar con cualquier persona en este servidor. ¡Incluso puedes alojarlo tú mismo!",
"closed_registrations_modal.title": "Registrarse en Mastodon",
"collection.share_modal.share_link_label": "Enlace para compartir",
"collection.share_modal.share_via_post": "Publicar en Mastodon",
"collection.share_modal.share_via_system": "Compartir con…",
"collection.share_modal.title": "Compartir la colección",
"collection.share_modal.title_new": "¡Comparte tu nueva colección!",
"collection.share_template_other": "Echa un vistazo a esta fantástica colección: {link}",
"collection.share_template_own": "Echa un vistazo a mi nueva colección: {link}",
"collections.account_count": "{count, plural, one {# cuenta} other {# cuentas}}",
"collections.accounts.empty_description": "Añade hasta {count} cuentas que sigas",
"collections.accounts.empty_title": "Esta colección está vacía",
@@ -307,7 +314,7 @@
"collections.no_collections_yet": "Aún no hay colecciones.",
"collections.old_last_post_note": "Última publicación hace más de una semana",
"collections.remove_account": "Borrar esta cuenta",
"collections.report_collection": "Reportar esta colección",
"collections.report_collection": "Informar de esta colección",
"collections.search_accounts_label": "Buscar cuentas para añadir…",
"collections.search_accounts_max_reached": "Has añadido el número máximo de cuentas",
"collections.sensitive": "Sensible",
@@ -448,6 +455,7 @@
"conversation.open": "Ver conversación",
"conversation.with": "Con {names}",
"copy_icon_button.copied": "Copiado al portapapeles",
"copy_icon_button.copy_this_text": "Copiar enlace al portapapeles",
"copypaste.copied": "Copiado",
"copypaste.copy_to_clipboard": "Copiar al portapapeles",
"directory.federated": "Desde el fediverso conocido",
@@ -977,7 +985,7 @@
"report.category.title_account": "perfil",
"report.category.title_status": "publicación",
"report.close": "Hecho",
"report.collection_comment": "¿Por qué quieres reportar esta colección?",
"report.collection_comment": "¿Por qué quieres informar de esta colección?",
"report.comment.title": "¿Hay algo más que creas que deberíamos saber?",
"report.forward": "Reenviar a {target}",
"report.forward_hint": "Esta cuenta es de otro servidor. ¿Enviar una copia anonimizada del informe allí también?",
@@ -999,7 +1007,7 @@
"report.rules.title": "¿Qué normas se están violando?",
"report.statuses.subtitle": "Selecciona todos los que correspondan",
"report.statuses.title": "¿Hay alguna publicación que respalde este informe?",
"report.submission_error": "No se pudo enviar el reporte",
"report.submission_error": "No se pudo enviar el informe",
"report.submission_error_details": "Comprueba tu conexión de red e inténtalo más tarde.",
"report.submit": "Enviar",
"report.target": "Reportando {target}",

View File

@@ -271,6 +271,13 @@
"closed_registrations_modal.find_another_server": "Finn ein annan ambætara",
"closed_registrations_modal.preamble": "Mastodon er desentraliserað, so óansæð hvar tú stovnar tína kontu, so ber til hjá tær at fylgja og virka saman við einum og hvørjum á hesum ambætaranum. Tað ber enntá til at hýsa tí sjálvi!",
"closed_registrations_modal.title": "At stovna kontu á Mastodon",
"collection.share_modal.share_link_label": "Innbjóðingarleinki at deila",
"collection.share_modal.share_via_post": "Posta á Mastodon",
"collection.share_modal.share_via_system": "Deil til…",
"collection.share_modal.title": "Deil savn",
"collection.share_modal.title_new": "Deil títt nýggja savn!",
"collection.share_template_other": "Hygg at hesum kula savninum: {link}",
"collection.share_template_own": "Hygg at mínum nýggja savni: {link}",
"collections.account_count": "{count, plural, one {# konta} other {# kontur}}",
"collections.accounts.empty_description": "Legg afturat upp til {count} kontur, sum tú fylgir",
"collections.accounts.empty_title": "Hetta savnið er tómt",
@@ -448,6 +455,7 @@
"conversation.open": "Vís samrøðu",
"conversation.with": "Við {names}",
"copy_icon_button.copied": "Avritað til setiborðið",
"copy_icon_button.copy_this_text": "Avrita leinki til setiborðið",
"copypaste.copied": "Avritað",
"copypaste.copy_to_clipboard": "Avrita til setiborðið",
"directory.federated": "Frá tí kenda fediversinum",

View File

@@ -48,6 +48,7 @@
"account.featured.hashtags": "Hashtags",
"account.featured_tags.last_status_at": "Dernière publication {date}",
"account.featured_tags.last_status_never": "Aucune publication",
"account.field_overflow": "Voir tout",
"account.filters.all": "Toutes les activités",
"account.filters.boosts_toggle": "Afficher les partages",
"account.filters.posts_boosts": "Messages et partages",

View File

@@ -48,6 +48,7 @@
"account.featured.hashtags": "Hashtags",
"account.featured_tags.last_status_at": "Dernier message le {date}",
"account.featured_tags.last_status_never": "Aucun message",
"account.field_overflow": "Voir tout",
"account.filters.all": "Toutes les activités",
"account.filters.boosts_toggle": "Afficher les partages",
"account.filters.posts_boosts": "Messages et partages",

View File

@@ -271,6 +271,13 @@
"closed_registrations_modal.find_another_server": "Faigh freastalaí eile",
"closed_registrations_modal.preamble": "Ós rud é go bhfuil Mastodon díláraithe, is cuma cá háit a chruthaíonn tú do chuntas, beidh tú in ann idirghníomhú le haon duine ar an bhfreastalaí seo agus iad a leanúint. Is féidir fiú é a féin-óstáil!",
"closed_registrations_modal.title": "Cláraigh le Mastodon",
"collection.share_modal.share_link_label": "Nasc roinnte cuireadh",
"collection.share_modal.share_via_post": "Postáil ar Mastodon",
"collection.share_modal.share_via_system": "Comhroinn le…",
"collection.share_modal.title": "Comhroinn bailiúchán",
"collection.share_modal.title_new": "Roinn do bhailiúchán nua!",
"collection.share_template_other": "Féach ar an mbailiúchán fionnuar seo: {link}",
"collection.share_template_own": "Féach ar mo bhailiúchán nua: {link}",
"collections.account_count": "{count, plural, one {# cuntas} two {# cuntais} few {# cuntais} many {# cuntais} other {# cuntais}}",
"collections.accounts.empty_description": "Cuir suas le {count} cuntas leis a leanann tú",
"collections.accounts.empty_title": "Tá an bailiúchán seo folamh",
@@ -448,6 +455,7 @@
"conversation.open": "Féach ar comhrá",
"conversation.with": "Le {names}",
"copy_icon_button.copied": "Cóipeáladh chuig an ngearrthaisce",
"copy_icon_button.copy_this_text": "Cóipeáil nasc chuig an ghearrthaisce",
"copypaste.copied": "Cóipeáilte",
"copypaste.copy_to_clipboard": "Cóipeáil chuig an ngearrthaisce",
"directory.federated": "Ó chomhchruinne aitheanta",

View File

@@ -271,6 +271,13 @@
"closed_registrations_modal.find_another_server": "Finna annan netþjón",
"closed_registrations_modal.preamble": "Mastodon er ekki miðstýrt, svo það skiptir ekki máli hvar þú býrð til aðgang; þú munt get fylgt eftir og haft samskipti við hvern sem er á þessum þjóni. Þú getur jafnvel hýst þinn eigin Mastodon þjón!",
"closed_registrations_modal.title": "Að nýskrá sig á Mastodon",
"collection.share_modal.share_link_label": "Tengill til að deila",
"collection.share_modal.share_via_post": "Birta á Mastodon",
"collection.share_modal.share_via_system": "Deila með…",
"collection.share_modal.title": "Deila safni",
"collection.share_modal.title_new": "Deildu nýja safninu þínu!",
"collection.share_template_other": "Kíktu á þetta áhugaverða safn: {link}",
"collection.share_template_own": "Kíktu á nýja safnið mitt: {link}",
"collections.account_count": "{count, plural, one {# aðgangur} other {# aðgangar}}",
"collections.accounts.empty_description": "Bættu við allt að {count} aðgöngum sem þú fylgist með",
"collections.accounts.empty_title": "Þetta safn er tómt",
@@ -448,6 +455,7 @@
"conversation.open": "Skoða samtal",
"conversation.with": "Við {names}",
"copy_icon_button.copied": "Afritað á klippispjald",
"copy_icon_button.copy_this_text": "Afrita tengil á klippispjald",
"copypaste.copied": "Afritað",
"copypaste.copy_to_clipboard": "Afrita á klippispjald",
"directory.federated": "Frá samtengdum vefþjónum",

View File

@@ -44,9 +44,11 @@
"account.familiar_followers_two": "Seguito da {name1} e {name2}",
"account.featured": "In primo piano",
"account.featured.accounts": "Profili",
"account.featured.collections": "Collezioni",
"account.featured.hashtags": "Hashtag",
"account.featured_tags.last_status_at": "Ultimo post il {date}",
"account.featured_tags.last_status_never": "Nessun post",
"account.field_overflow": "Mostra il contenuto completo",
"account.filters.all": "Tutte le attività",
"account.filters.boosts_toggle": "Mostra le condivisioni",
"account.filters.posts_boosts": "Post e condivisioni",
@@ -269,6 +271,13 @@
"closed_registrations_modal.find_another_server": "Trova un altro server",
"closed_registrations_modal.preamble": "Mastodon è decentralizzato, quindi, non importa dove crei il tuo profilo, potrai seguire e interagire con chiunque su questo server. Anche se sei tu stesso a ospitarlo!",
"closed_registrations_modal.title": "Registrazione su Mastodon",
"collection.share_modal.share_link_label": "Condividi il collegamento d'invito",
"collection.share_modal.share_via_post": "Pubblica su Mastodon",
"collection.share_modal.share_via_system": "Condividi con…",
"collection.share_modal.title": "Condividi la collezione",
"collection.share_modal.title_new": "Condividi la tua nuova collezione!",
"collection.share_template_other": "Dai un'occhiata a questa fantastica collezione: {link}",
"collection.share_template_own": "Dai un'occhiata alla mia collezione: {link}",
"collections.account_count": "{count, plural, one {# account} other {# account}}",
"collections.accounts.empty_description": "Aggiungi fino a {count} account che segui",
"collections.accounts.empty_title": "Questa collezione è vuota",
@@ -446,6 +455,7 @@
"conversation.open": "Visualizza conversazione",
"conversation.with": "Con {names}",
"copy_icon_button.copied": "Copiato negli appunti",
"copy_icon_button.copy_this_text": "Copia il link negli appunti",
"copypaste.copied": "Copiato",
"copypaste.copy_to_clipboard": "Copia negli Appunti",
"directory.federated": "Da un fediverse noto",

View File

@@ -48,6 +48,7 @@
"account.featured.hashtags": "Hashtags",
"account.featured_tags.last_status_at": "Laatste bericht op {date}",
"account.featured_tags.last_status_never": "Geen berichten",
"account.field_overflow": "Volledige inhoud tonen",
"account.filters.all": "Alle activiteit",
"account.filters.boosts_toggle": "Boosts tonen",
"account.filters.posts_boosts": "Berichten en boosts",
@@ -270,6 +271,12 @@
"closed_registrations_modal.find_another_server": "Een andere server zoeken",
"closed_registrations_modal.preamble": "Mastodon is gedecentraliseerd. Op welke server je ook een account hebt, je kunt overal vandaan mensen op deze server volgen en er mee interactie hebben. Je kunt zelfs zelf een Mastodon-server hosten!",
"closed_registrations_modal.title": "Registreren op Mastodon",
"collection.share_modal.share_via_post": "Bericht op Mastodon",
"collection.share_modal.share_via_system": "Delen met…",
"collection.share_modal.title": "Verzameling delen",
"collection.share_modal.title_new": "Deel je nieuwe verzameling!",
"collection.share_template_other": "Bekijk deze coole verzameling: {link}",
"collection.share_template_own": "Bekijk mijn nieuwe verzameling: {link}",
"collections.account_count": "{count, plural, one {# account} other {# accounts}}",
"collections.accounts.empty_description": "Voeg tot {count} accounts toe die je volgt",
"collections.accounts.empty_title": "Deze verzameling is leeg",
@@ -447,6 +454,7 @@
"conversation.open": "Gesprek tonen",
"conversation.with": "Met {names}",
"copy_icon_button.copied": "Gekopieerd naar klembord",
"copy_icon_button.copy_this_text": "Link kopiëren naar klembord",
"copypaste.copied": "Gekopieerd",
"copypaste.copy_to_clipboard": "Naar klembord kopiëren",
"directory.federated": "Fediverse (wat bekend is)",

View File

@@ -48,6 +48,7 @@
"account.featured.hashtags": "Etiquetas",
"account.featured_tags.last_status_at": "Última publicação em {date}",
"account.featured_tags.last_status_never": "Sem publicações",
"account.field_overflow": "Mostrar todo o conteúdo",
"account.filters.all": "Toda a atividade",
"account.filters.boosts_toggle": "Mostrar partilhas",
"account.filters.posts_boosts": "Publicações e partilhas",
@@ -162,8 +163,10 @@
"account_edit.name_modal.edit_title": "Editar o nome a mostrar",
"account_edit.save": "Guardar",
"account_edit_tags.column_title": "Editar etiquetas em destaque",
"account_edit_tags.help_text": "As etiquetas destacadas ajudam os utilizadores a descobrir e interagir com o seu perfil. Aparecem como filtros na vista de atividade da sua página de perfil.",
"account_edit_tags.search_placeholder": "Insira uma etiqueta…",
"account_edit_tags.suggestions": "Sugestões:",
"account_edit_tags.tag_status_count": "{count, plural, one {# publicação} other {# publicações}}",
"account_note.placeholder": "Clicar para adicionar nota",
"admin.dashboard.daily_retention": "Taxa de retenção de utilizadores por dia após a inscrição",
"admin.dashboard.monthly_retention": "Taxa de retenção de utilizadores por mês após a inscrição",
@@ -267,6 +270,7 @@
"closed_registrations_modal.preamble": "O Mastodon é descentralizado, por isso não importa onde a tua conta é criada, pois continuarás a poder acompanhar e interagir com qualquer um neste servidor. Podes até alojar o teu próprio servidor!",
"closed_registrations_modal.title": "Criar uma conta no Mastodon",
"collections.account_count": "{count, plural, one {# conta} other {# contas}}",
"collections.accounts.empty_description": "Adicione até {count} contas que segue",
"collections.accounts.empty_title": "Esta coleção está vazia",
"collections.collection_description": "Descrição",
"collections.collection_name": "Nome",
@@ -287,15 +291,25 @@
"collections.detail.share": "Partilhar esta coleção",
"collections.edit_details": "Editar detalhes",
"collections.error_loading_collections": "Ocorreu um erro ao tentar carregar as suas coleções.",
"collections.hints.accounts_counter": "{count} / {max} contas",
"collections.hints.add_more_accounts": "Adicione pelo menos {count, plural, one {# conta} other {# contas}} para continuar",
"collections.hints.can_not_remove_more_accounts": "As coleções devem conter pelo menos {count, plural, one {# conta} other {# contas}}. Não é possível remover mais contas.",
"collections.last_updated_at": "Última atualização: {date}",
"collections.manage_accounts": "Gerir contas",
"collections.mark_as_sensitive": "Marcar como sensível",
"collections.mark_as_sensitive_hint": "Oculta a descrição e as contas da coleção por trás de um aviso de conteúdo. O nome da coleção ainda estará visível.",
"collections.name_length_hint": "Limite de 40 carateres",
"collections.new_collection": "Nova coleção",
"collections.no_collections_yet": "Ainda não existem coleções.",
"collections.old_last_post_note": "Última publicação há mais de uma semana",
"collections.remove_account": "Remover esta conta",
"collections.report_collection": "Denunciar esta coleção",
"collections.search_accounts_label": "Procurar contas para adicionar…",
"collections.search_accounts_max_reached": "Já adicionou o máximo de contas",
"collections.sensitive": "Sensível",
"collections.topic_hint": "Adicione uma etiqueta para ajudar outros a entender o tópico principal desta coleção.",
"collections.view_collection": "Ver coleções",
"collections.view_other_collections_by_user": "Ver outras coleções deste utilizador",
"collections.visibility_public": "Pública",
"collections.visibility_public_hint": "Visível nos resultados de pesquisa e outras áreas onde aparecem recomendações.",
"collections.visibility_title": "Visibilidade",
@@ -384,6 +398,9 @@
"confirmations.discard_draft.post.title": "Descartar o rascunho da publicação?",
"confirmations.discard_edit_media.confirm": "Descartar",
"confirmations.discard_edit_media.message": "Tens alterações por guardar na descrição da multimédia ou pré-visualização do conteúdo. Descartar mesmo assim?",
"confirmations.follow_to_collection.confirm": "Seguir e adicionar à coleção",
"confirmations.follow_to_collection.message": "Precisa de seguir {name} para o/a adicionar a uma coleção.",
"confirmations.follow_to_collection.title": "Seguir conta?",
"confirmations.follow_to_list.confirm": "Seguir e adicionar à lista",
"confirmations.follow_to_list.message": "Tens de seguir {name} para o adicionares a uma lista.",
"confirmations.follow_to_list.title": "Seguir utilizador?",
@@ -956,6 +973,7 @@
"report.category.title_account": "perfil",
"report.category.title_status": "publicação",
"report.close": "Concluído",
"report.collection_comment": "Porque quer denunciar esta coleção?",
"report.comment.title": "Há mais alguma coisa que devamos saber?",
"report.forward": "Reencaminhar para {target}",
"report.forward_hint": "A conta pertence a outro servidor. Enviar uma cópia anónima da denúncia para esse servidor também?",
@@ -977,6 +995,8 @@
"report.rules.title": "Que regras estão a ser violadas?",
"report.statuses.subtitle": "Seleciona tudo o que se aplicar",
"report.statuses.title": "Existe alguma publicação que suporte esta denúncia?",
"report.submission_error": "A denúncia não pôde ser enviada",
"report.submission_error_details": "Por favor, verifique a sua ligação à rede e tente novamente mais tarde.",
"report.submit": "Enviar",
"report.target": "A denunciar {target}",
"report.thanks.take_action": "Aqui estão as suas opções para controlar o que vê no Mastodon:",

View File

@@ -269,6 +269,12 @@
"closed_registrations_modal.find_another_server": "Gjeni shërbyes tjetër",
"closed_registrations_modal.preamble": "Mastodon-i është i decentralizuar, ndaj pavarësisht se ku krijoni llogarinë tuaj, do të jeni në gjendje të ndiqni dhe ndërveproni me këdo në këtë shërbyes. Mundeni madje edhe ta strehoni ju vetë!",
"closed_registrations_modal.title": "Po regjistroheni në Mastodon",
"collection.share_modal.share_via_post": "Postoje në Mastodon",
"collection.share_modal.share_via_system": "Ndajeni me të tjerë në…",
"collection.share_modal.title": "Ndani koleksionin me të tjerë",
"collection.share_modal.title_new": "Ndani me të tjerë koleksionin tuaj të ri!",
"collection.share_template_other": "Shihni këtë koleksion të hijshëm: {link}",
"collection.share_template_own": "Shihni koleksionin tim të ri: {link}",
"collections.account_count": "{count, plural, one {# llogari} other {# llogari}}",
"collections.accounts.empty_title": "Ky koleksion është i zbrazët",
"collections.collection_description": "Përshkrim",
@@ -445,6 +451,7 @@
"conversation.open": "Shfaq bisedën",
"conversation.with": "Me {names}",
"copy_icon_button.copied": "U kopjua në të papastër",
"copy_icon_button.copy_this_text": "Kopjoje lidhjen në të papastër",
"copypaste.copied": "U kopjua",
"copypaste.copy_to_clipboard": "Kopjoje në të papastër",
"directory.federated": "Nga fedivers i njohur",

View File

@@ -44,9 +44,11 @@
"account.familiar_followers_two": "{name1} ve {name2} tarafından takip ediliyor",
"account.featured": "Öne çıkan",
"account.featured.accounts": "Profiller",
"account.featured.collections": "Koleksiyonlar",
"account.featured.hashtags": "Etiketler",
"account.featured_tags.last_status_at": "Son gönderinin tarihi {date}",
"account.featured_tags.last_status_never": "Gönderi yok",
"account.field_overflow": "Tüm içeriği göster",
"account.filters.all": "Tüm aktiviteler",
"account.filters.boosts_toggle": "Yeniden paylaşımları göster",
"account.filters.posts_boosts": "Gönderiler ve yeniden paylaşımlar",
@@ -269,6 +271,13 @@
"closed_registrations_modal.find_another_server": "Başka sunucu bul",
"closed_registrations_modal.preamble": "Mastodon merkeziyetsizdir, bu yüzden hesabınızı nerede oluşturursanız oluşturun, bu sunucudaki herhangi birini takip edebilecek veya onunla etkileşebileceksiniz. Hatta kendi sunucunuzu bile barındırabilirsiniz!",
"closed_registrations_modal.title": "Mastodon'a kayıt olmak",
"collection.share_modal.share_link_label": "Davet bağlantısı",
"collection.share_modal.share_via_post": "Mastodon'da paylaş",
"collection.share_modal.share_via_system": "Paylaş…",
"collection.share_modal.title": "Koleksiyonu paylaş",
"collection.share_modal.title_new": "Yeni koleksiyonunuzu paylaşın!",
"collection.share_template_other": "Bu harika koleksiyona göz atın: {link}",
"collection.share_template_own": "Yeni koleksiyonuma göz atın: {link}",
"collections.account_count": "{count, plural, one {# hesap} other {# hesap}}",
"collections.accounts.empty_description": "Takip ettiğiniz hesapların sayısını {count} kadar artırın",
"collections.accounts.empty_title": "Bu koleksiyon boş",
@@ -446,6 +455,7 @@
"conversation.open": "Sohbeti görüntüle",
"conversation.with": "{names} ile",
"copy_icon_button.copied": "Panoya kopyalandı",
"copy_icon_button.copy_this_text": "Bağlantıyı panoya kopyala",
"copypaste.copied": "Kopyalandı",
"copypaste.copy_to_clipboard": "Panoya kopyala",
"directory.federated": "Bilinen fediverse'lerden",

View File

@@ -48,6 +48,7 @@
"account.featured.hashtags": "话题",
"account.featured_tags.last_status_at": "上次发言于 {date}",
"account.featured_tags.last_status_never": "暂无嘟文",
"account.field_overflow": "显示完整内容",
"account.filters.all": "所有活动",
"account.filters.boosts_toggle": "显示转嘟",
"account.filters.posts_boosts": "嘟文与转嘟",
@@ -270,6 +271,13 @@
"closed_registrations_modal.find_another_server": "查找其他服务器",
"closed_registrations_modal.preamble": "Mastodon 是去中心化的,所以无论在哪个实例创建账号,都可以关注本服务器上的账号并与之交流。 或者你还可以自己搭建实例!",
"closed_registrations_modal.title": "注册 Mastodon 账号",
"collection.share_modal.share_link_label": "分享链接",
"collection.share_modal.share_via_post": "在 Mastodon 上发布",
"collection.share_modal.share_via_system": "分享到…",
"collection.share_modal.title": "分享收藏列表",
"collection.share_modal.title_new": "分享你的新收藏列表!",
"collection.share_template_other": "发现了个收藏列表,来看看:{link}",
"collection.share_template_own": "我的新收藏列表,来看看:{link}",
"collections.account_count": "{count, plural, other {# 个账号}}",
"collections.accounts.empty_description": "添加你关注的账号,最多 {count} 个",
"collections.accounts.empty_title": "收藏列表为空",
@@ -447,6 +455,7 @@
"conversation.open": "查看对话",
"conversation.with": "与 {names}",
"copy_icon_button.copied": "已复制到剪贴板",
"copy_icon_button.copy_this_text": "复制链接到剪贴板",
"copypaste.copied": "已复制",
"copypaste.copy_to_clipboard": "复制到剪贴板",
"directory.federated": "来自已知联邦宇宙",

View File

@@ -271,6 +271,13 @@
"closed_registrations_modal.find_another_server": "尋找另一個伺服器",
"closed_registrations_modal.preamble": "Mastodon 是去中心化的,所以無論您於哪個伺服器新增帳號,都可以與此伺服器上的任何人跟隨及互動。您甚至能自行架設自己的伺服器!",
"closed_registrations_modal.title": "註冊 Mastodon",
"collection.share_modal.share_link_label": "分享連結",
"collection.share_modal.share_via_post": "於 Mastodon 上發嘟文",
"collection.share_modal.share_via_system": "分享至...",
"collection.share_modal.title": "分享收藏名單",
"collection.share_modal.title_new": "分享您的新收藏名單!",
"collection.share_template_other": "來看看這個酷酷的收藏名單:{link}",
"collection.share_template_own": "來看看我的新收藏名單:{link}",
"collections.account_count": "{count, plural, other {# 個帳號}}",
"collections.accounts.empty_description": "加入最多 {count} 個您跟隨之帳號",
"collections.accounts.empty_title": "此收藏名單是空的",
@@ -448,6 +455,7 @@
"conversation.open": "檢視對話",
"conversation.with": "與 {names}",
"copy_icon_button.copied": "已複製到剪貼簿",
"copy_icon_button.copy_this_text": "複製連結至剪貼簿",
"copypaste.copied": "已複製",
"copypaste.copy_to_clipboard": "複製到剪貼簿",
"directory.federated": "來自已知聯邦宇宙",

View File

@@ -6408,15 +6408,15 @@ a.status-card {
line-height: 20px;
color: var(--color-text-secondary);
h1 {
:where(h1) {
font-size: 16px;
line-height: 24px;
color: var(--color-text-primary);
font-weight: 500;
}
&:not(:only-child) {
margin-bottom: 8px;
}
:where(h1:not(:only-child)) {
margin-bottom: 8px;
}
strong {