Merge remote-tracking branch 'parent/main' into upstream-20241203

This commit is contained in:
KMY 2024-12-04 11:56:14 +09:00
commit 0a0b0d46ea
52 changed files with 599 additions and 451 deletions

View file

@ -399,7 +399,7 @@ The following changelog entries focus on changes visible to users, administrator
- Fix empty environment variables not using default nil value (#27400 by @renchap) - Fix empty environment variables not using default nil value (#27400 by @renchap)
- Fix language sorting in settings (#27158 by @gunchleoc) - Fix language sorting in settings (#27158 by @gunchleoc)
## |4.2.11] - 2024-08-16 ## [4.2.11] - 2024-08-16
### Added ### Added

View file

@ -426,7 +426,7 @@ GEM
net-smtp (0.5.0) net-smtp (0.5.0)
net-protocol net-protocol
nio4r (2.7.4) nio4r (2.7.4)
nokogiri (1.16.7) nokogiri (1.16.8)
mini_portile2 (~> 2.8.2) mini_portile2 (~> 2.8.2)
racc (~> 1.4) racc (~> 1.4)
oj (3.16.7) oj (3.16.7)
@ -632,9 +632,9 @@ GEM
activesupport (>= 5.0.0) activesupport (>= 5.0.0)
minitest minitest
nokogiri (>= 1.6) nokogiri (>= 1.6)
rails-html-sanitizer (1.6.0) rails-html-sanitizer (1.6.1)
loofah (~> 2.21) loofah (~> 2.21)
nokogiri (~> 1.14) nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
rails-i18n (7.0.10) rails-i18n (7.0.10)
i18n (>= 0.7, < 2) i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 8) railties (>= 6.0.0, < 8)

View file

@ -149,6 +149,7 @@ module ApplicationHelper
output << content_for(:body_classes) output << content_for(:body_classes)
output << "theme-#{current_theme.parameterize}" output << "theme-#{current_theme.parameterize}"
output << 'system-font' if current_account&.user&.setting_system_font_ui output << 'system-font' if current_account&.user&.setting_system_font_ui
output << 'custom-scrollbars' unless current_account&.user&.setting_system_scrollbars_ui
output << (current_account&.user&.setting_reduce_motion ? 'reduce-motion' : 'no-reduce-motion') output << (current_account&.user&.setting_reduce_motion ? 'reduce-motion' : 'no-reduce-motion')
output << 'rtl' if locale_direction == 'rtl' output << 'rtl' if locale_direction == 'rtl'
output << "content-font-size__#{current_account&.user&.setting_content_font_size}" output << "content-font-size__#{current_account&.user&.setting_content_font_size}"

View file

@ -1,66 +0,0 @@
import { defineMessages } from 'react-intl';
import { AxiosError } from 'axios';
const messages = defineMessages({
unexpectedTitle: { id: 'alert.unexpected.title', defaultMessage: 'Oops!' },
unexpectedMessage: { id: 'alert.unexpected.message', defaultMessage: 'An unexpected error occurred.' },
rateLimitedTitle: { id: 'alert.rate_limited.title', defaultMessage: 'Rate limited' },
rateLimitedMessage: { id: 'alert.rate_limited.message', defaultMessage: 'Please retry after {retry_time, time, medium}.' },
});
export const ALERT_SHOW = 'ALERT_SHOW';
export const ALERT_DISMISS = 'ALERT_DISMISS';
export const ALERT_CLEAR = 'ALERT_CLEAR';
export const ALERT_NOOP = 'ALERT_NOOP';
export const dismissAlert = alert => ({
type: ALERT_DISMISS,
alert,
});
export const clearAlert = () => ({
type: ALERT_CLEAR,
});
export const showAlert = alert => ({
type: ALERT_SHOW,
alert,
});
export const showAlertForError = (error, skipNotFound = false) => {
if (error.response) {
const { data, status, statusText, headers } = error.response;
// Skip these errors as they are reflected in the UI
if (skipNotFound && (status === 404 || status === 410)) {
return { type: ALERT_NOOP };
}
// Rate limit errors
if (status === 429 && headers['x-ratelimit-reset']) {
return showAlert({
title: messages.rateLimitedTitle,
message: messages.rateLimitedMessage,
values: { 'retry_time': new Date(headers['x-ratelimit-reset']) },
});
}
return showAlert({
title: `${status}`,
message: data.error || statusText,
});
}
// An aborted request, e.g. due to reloading the browser window, it not really error
if (error.code === AxiosError.ECONNABORTED) {
return { type: ALERT_NOOP };
}
console.error(error);
return showAlert({
title: messages.unexpectedTitle,
message: messages.unexpectedMessage,
});
};

View file

@ -0,0 +1,90 @@
import { defineMessages } from 'react-intl';
import type { MessageDescriptor } from 'react-intl';
import { AxiosError } from 'axios';
import type { AxiosResponse } from 'axios';
interface Alert {
title: string | MessageDescriptor;
message: string | MessageDescriptor;
values?: Record<string, string | number | Date>;
}
interface ApiErrorResponse {
error?: string;
}
const messages = defineMessages({
unexpectedTitle: { id: 'alert.unexpected.title', defaultMessage: 'Oops!' },
unexpectedMessage: {
id: 'alert.unexpected.message',
defaultMessage: 'An unexpected error occurred.',
},
rateLimitedTitle: {
id: 'alert.rate_limited.title',
defaultMessage: 'Rate limited',
},
rateLimitedMessage: {
id: 'alert.rate_limited.message',
defaultMessage: 'Please retry after {retry_time, time, medium}.',
},
});
export const ALERT_SHOW = 'ALERT_SHOW';
export const ALERT_DISMISS = 'ALERT_DISMISS';
export const ALERT_CLEAR = 'ALERT_CLEAR';
export const ALERT_NOOP = 'ALERT_NOOP';
export const dismissAlert = (alert: Alert) => ({
type: ALERT_DISMISS,
alert,
});
export const clearAlert = () => ({
type: ALERT_CLEAR,
});
export const showAlert = (alert: Alert) => ({
type: ALERT_SHOW,
alert,
});
export const showAlertForError = (error: unknown, skipNotFound = false) => {
if (error instanceof AxiosError && error.response) {
const { status, statusText, headers } = error.response;
const { data } = error.response as AxiosResponse<ApiErrorResponse>;
// Skip these errors as they are reflected in the UI
if (skipNotFound && (status === 404 || status === 410)) {
return { type: ALERT_NOOP };
}
// Rate limit errors
if (status === 429 && headers['x-ratelimit-reset']) {
return showAlert({
title: messages.rateLimitedTitle,
message: messages.rateLimitedMessage,
values: {
retry_time: new Date(headers['x-ratelimit-reset'] as string),
},
});
}
return showAlert({
title: `${status}`,
message: data.error ?? statusText,
});
}
// An aborted request, e.g. due to reloading the browser window, it not really error
if (error instanceof AxiosError && error.code === AxiosError.ECONNABORTED) {
return { type: ALERT_NOOP };
}
console.error(error);
return showAlert({
title: messages.unexpectedTitle,
message: messages.unexpectedMessage,
});
};

View file

@ -5,3 +5,16 @@ export const apiSubmitAccountNote = (id: string, value: string) =>
apiRequestPost<ApiRelationshipJSON>(`v1/accounts/${id}/note`, { apiRequestPost<ApiRelationshipJSON>(`v1/accounts/${id}/note`, {
comment: value, comment: value,
}); });
export const apiFollowAccount = (
id: string,
params?: {
reblogs: boolean;
},
) =>
apiRequestPost<ApiRelationshipJSON>(`v1/accounts/${id}/follow`, {
...params,
});
export const apiUnfollowAccount = (id: string) =>
apiRequestPost<ApiRelationshipJSON>(`v1/accounts/${id}/unfollow`);

View file

@ -1,188 +0,0 @@
import PropTypes from 'prop-types';
import { useCallback } from 'react';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import { Link } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes';
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
import { EmptyAccount } from 'mastodon/components/empty_account';
import { FollowButton } from 'mastodon/components/follow_button';
import { ShortNumber } from 'mastodon/components/short_number';
import { VerifiedBadge } from 'mastodon/components/verified_badge';
import DropdownMenuContainer from '../containers/dropdown_menu_container';
import { me } from '../initial_state';
import { Avatar } from './avatar';
import { Button } from './button';
import { FollowersCounter } from './counters';
import { DisplayName } from './display_name';
import { RelativeTimestamp } from './relative_timestamp';
const messages = defineMessages({
unblock: { id: 'account.unblock_short', defaultMessage: 'Unblock' },
unmute: { id: 'account.unmute_short', defaultMessage: 'Unmute' },
mute_notifications: { id: 'account.mute_notifications_short', defaultMessage: 'Mute notifications' },
unmute_notifications: { id: 'account.unmute_notifications_short', defaultMessage: 'Unmute notifications' },
mute: { id: 'account.mute_short', defaultMessage: 'Mute' },
block: { id: 'account.block_short', defaultMessage: 'Block' },
more: { id: 'status.more', defaultMessage: 'More' },
});
const Account = ({ size = 46, account, onBlock, onMute, onMuteNotifications, hidden, hideButtons, minimal, defaultAction, children, withBio }) => {
const intl = useIntl();
const handleBlock = useCallback(() => {
onBlock(account);
}, [onBlock, account]);
const handleMute = useCallback(() => {
onMute(account);
}, [onMute, account]);
const handleMuteNotifications = useCallback(() => {
onMuteNotifications(account, true);
}, [onMuteNotifications, account]);
const handleUnmuteNotifications = useCallback(() => {
onMuteNotifications(account, false);
}, [onMuteNotifications, account]);
if (!account) {
return <EmptyAccount size={size} minimal={minimal} />;
}
if (hidden) {
return (
<>
{account.get('display_name')}
{account.get('username')}
</>
);
}
let buttons;
if (!hideButtons && account.get('id') !== me && account.get('relationship', null) !== null) {
const requested = account.getIn(['relationship', 'requested']);
const blocking = account.getIn(['relationship', 'blocking']);
const muting = account.getIn(['relationship', 'muting']);
if (requested) {
buttons = <FollowButton accountId={account.get('id')} />;
} else if (blocking) {
buttons = <Button text={intl.formatMessage(messages.unblock)} onClick={handleBlock} />;
} else if (muting) {
let menu;
if (account.getIn(['relationship', 'muting_notifications'])) {
menu = [{ text: intl.formatMessage(messages.unmute_notifications), action: handleUnmuteNotifications }];
} else {
menu = [{ text: intl.formatMessage(messages.mute_notifications), action: handleMuteNotifications }];
}
buttons = (
<>
<DropdownMenuContainer
items={menu}
icon='ellipsis-h'
iconComponent={MoreHorizIcon}
direction='right'
title={intl.formatMessage(messages.more)}
/>
<Button text={intl.formatMessage(messages.unmute)} onClick={handleMute} />
</>
);
} else if (defaultAction === 'mute') {
buttons = <Button text={intl.formatMessage(messages.mute)} onClick={handleMute} />;
} else if (defaultAction === 'block') {
buttons = <Button text={intl.formatMessage(messages.block)} onClick={handleBlock} />;
} else {
buttons = <FollowButton accountId={account.get('id')} />;
}
} else if (!hideButtons) {
buttons = <FollowButton accountId={account.get('id')} />;
}
let muteTimeRemaining;
if (account.get('mute_expires_at')) {
muteTimeRemaining = <>· <RelativeTimestamp timestamp={account.get('mute_expires_at')} futureDate /></>;
}
let verification;
const firstVerifiedField = account.get('fields').find(item => !!item.get('verified_at'));
if (firstVerifiedField) {
verification = <VerifiedBadge link={firstVerifiedField.get('value')} />;
}
return (
<div className={classNames('account', { 'account--minimal': minimal })}>
<div className='account__wrapper'>
<Link key={account.get('id')} className='account__display-name' title={account.get('acct')} to={`/@${account.get('acct')}`} data-hover-card-account={account.get('id')}>
<div className='account__avatar-wrapper'>
<Avatar account={account} size={size} />
</div>
<div className='account__contents'>
<DisplayName account={account} />
{!minimal && (
<div className='account__details'>
<ShortNumber value={account.get('followers_count')} renderer={FollowersCounter}
isHide={account.getIn(['other_settings', 'hide_followers_count'])} /> {verification} {muteTimeRemaining}
</div>
)}
</div>
</Link>
{!minimal && children && (
<div>
<div>
{children}
</div>
<div className='account__relationship'>
{buttons}
</div>
</div>
)}
{!minimal && !children && (
<div className='account__relationship'>
{buttons}
</div>
)}
</div>
{withBio && (account.get('note').length > 0 ? (
<div
className='account__note translate'
dangerouslySetInnerHTML={{ __html: account.get('note_emojified') }}
/>
) : (
<div className='account__note account__note--missing'><FormattedMessage id='account.no_bio' defaultMessage='No description provided.' /></div>
))}
</div>
);
};
Account.propTypes = {
size: PropTypes.number,
account: ImmutablePropTypes.record,
onBlock: PropTypes.func,
onMute: PropTypes.func,
onMuteNotifications: PropTypes.func,
hidden: PropTypes.bool,
hideButtons: PropTypes.bool,
minimal: PropTypes.bool,
defaultAction: PropTypes.string,
withBio: PropTypes.bool,
children: PropTypes.any,
};
export default Account;

View file

@ -0,0 +1,260 @@
import type { ReactNode } from 'react';
import React, { useCallback } from 'react';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import { Link } from 'react-router-dom';
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
import {
blockAccount,
unblockAccount,
muteAccount,
unmuteAccount,
} from 'mastodon/actions/accounts';
import { initMuteModal } from 'mastodon/actions/mutes';
import { Avatar } from 'mastodon/components/avatar';
import { Button } from 'mastodon/components/button';
import { FollowersCounter } from 'mastodon/components/counters';
import { DisplayName } from 'mastodon/components/display_name';
import { FollowButton } from 'mastodon/components/follow_button';
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
import { ShortNumber } from 'mastodon/components/short_number';
import { Skeleton } from 'mastodon/components/skeleton';
import { VerifiedBadge } from 'mastodon/components/verified_badge';
import DropdownMenu from 'mastodon/containers/dropdown_menu_container';
import { me } from 'mastodon/initial_state';
import { useAppSelector, useAppDispatch } from 'mastodon/store';
const messages = defineMessages({
follow: { id: 'account.follow', defaultMessage: 'Follow' },
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
cancel_follow_request: {
id: 'account.cancel_follow_request',
defaultMessage: 'Withdraw follow request',
},
unblock: { id: 'account.unblock_short', defaultMessage: 'Unblock' },
unmute: { id: 'account.unmute_short', defaultMessage: 'Unmute' },
mute_notifications: {
id: 'account.mute_notifications_short',
defaultMessage: 'Mute notifications',
},
unmute_notifications: {
id: 'account.unmute_notifications_short',
defaultMessage: 'Unmute notifications',
},
mute: { id: 'account.mute_short', defaultMessage: 'Mute' },
block: { id: 'account.block_short', defaultMessage: 'Block' },
more: { id: 'status.more', defaultMessage: 'More' },
});
export const Account: React.FC<{
size?: number;
id: string;
hidden?: boolean;
minimal?: boolean;
defaultAction?: 'block' | 'mute';
withBio?: boolean;
hideButtons?: boolean;
children?: ReactNode;
}> = ({
id,
size = 46,
hidden,
minimal,
defaultAction,
withBio,
hideButtons,
children,
}) => {
const intl = useIntl();
const account = useAppSelector((state) => state.accounts.get(id));
const relationship = useAppSelector((state) => state.relationships.get(id));
const dispatch = useAppDispatch();
const handleBlock = useCallback(() => {
if (relationship?.blocking) {
dispatch(unblockAccount(id));
} else {
dispatch(blockAccount(id));
}
}, [dispatch, id, relationship]);
const handleMute = useCallback(() => {
if (relationship?.muting) {
dispatch(unmuteAccount(id));
} else {
dispatch(initMuteModal(account));
}
}, [dispatch, id, account, relationship]);
const handleMuteNotifications = useCallback(() => {
dispatch(muteAccount(id, true));
}, [dispatch, id]);
const handleUnmuteNotifications = useCallback(() => {
dispatch(muteAccount(id, false));
}, [dispatch, id]);
if (hidden) {
return (
<>
{account?.display_name}
{account?.username}
</>
);
}
let buttons;
if (account && account.id !== me && relationship) {
const { requested, blocking, muting } = relationship;
if (requested) {
buttons = <FollowButton accountId={id} />;
} else if (blocking) {
buttons = (
<Button
text={intl.formatMessage(messages.unblock)}
onClick={handleBlock}
/>
);
} else if (muting) {
const menu = [
{
text: intl.formatMessage(
relationship.muting_notifications
? messages.unmute_notifications
: messages.mute_notifications,
),
action: relationship.muting_notifications
? handleUnmuteNotifications
: handleMuteNotifications,
},
];
buttons = (
<>
<DropdownMenu
items={menu}
icon='ellipsis-h'
iconComponent={MoreHorizIcon}
direction='right'
title={intl.formatMessage(messages.more)}
/>
<Button
text={intl.formatMessage(messages.unmute)}
onClick={handleMute}
/>
</>
);
} else if (defaultAction === 'mute') {
buttons = (
<Button text={intl.formatMessage(messages.mute)} onClick={handleMute} />
);
} else if (defaultAction === 'block') {
buttons = (
<Button
text={intl.formatMessage(messages.block)}
onClick={handleBlock}
/>
);
} else {
buttons = <FollowButton accountId={id} />;
}
} else {
buttons = <FollowButton accountId={id} />;
}
if (hideButtons) {
buttons = null;
}
let muteTimeRemaining;
if (account?.mute_expires_at) {
muteTimeRemaining = (
<>
· <RelativeTimestamp timestamp={account.mute_expires_at} futureDate />
</>
);
}
let verification;
const firstVerifiedField = account?.fields.find((item) => !!item.verified_at);
if (firstVerifiedField) {
verification = <VerifiedBadge link={firstVerifiedField.value} />;
}
return (
<div className={classNames('account', { 'account--minimal': minimal })}>
<div className='account__wrapper'>
<Link
className='account__display-name'
title={account?.acct}
to={`/@${account?.acct}`}
data-hover-card-account={id}
>
<div className='account__avatar-wrapper'>
{account ? (
<Avatar account={account} size={size} />
) : (
<Skeleton width={size} height={size} />
)}
</div>
<div className='account__contents'>
<DisplayName account={account} />
{!minimal && (
<div className='account__details'>
{account ? (
<>
<ShortNumber
value={account.followers_count}
renderer={FollowersCounter}
isHide={account.other_settings.hide_followers_count}
/>{' '}
{verification} {muteTimeRemaining}
</>
) : (
<Skeleton width='7ch' />
)}
</div>
)}
</div>
</Link>
{!minimal && children && (
<div>
<div>{children}</div>
<div className='account__relationship'>{buttons}</div>
</div>
)}
{!minimal && !children && (
<div className='account__relationship'>{buttons}</div>
)}
</div>
{account &&
withBio &&
(account.note.length > 0 ? (
<div
className='account__note translate'
dangerouslySetInnerHTML={{ __html: account.note_emojified }}
/>
) : (
<div className='account__note account__note--missing'>
<FormattedMessage
id='account.no_bio'
defaultMessage='No description provided.'
/>
</div>
))}
</div>
);
};

View file

@ -1,33 +0,0 @@
import React from 'react';
import classNames from 'classnames';
import { DisplayName } from 'mastodon/components/display_name';
import { Skeleton } from 'mastodon/components/skeleton';
interface Props {
size?: number;
minimal?: boolean;
}
export const EmptyAccount: React.FC<Props> = ({
size = 46,
minimal = false,
}) => {
return (
<div className={classNames('account', { 'account--minimal': minimal })}>
<div className='account__wrapper'>
<div className='account__display-name'>
<div className='account__avatar-wrapper'>
<Skeleton width={size} height={size} />
</div>
<div>
<DisplayName />
<Skeleton width='7ch' />
</div>
</div>
</div>
</div>
);
};

View file

@ -8,10 +8,10 @@ import { Link } from 'react-router-dom';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { fetchServer } from 'mastodon/actions/server'; import { fetchServer } from 'mastodon/actions/server';
import { Account } from 'mastodon/components/account';
import { ServerHeroImage } from 'mastodon/components/server_hero_image'; import { ServerHeroImage } from 'mastodon/components/server_hero_image';
import { ShortNumber } from 'mastodon/components/short_number'; import { ShortNumber } from 'mastodon/components/short_number';
import { Skeleton } from 'mastodon/components/skeleton'; import { Skeleton } from 'mastodon/components/skeleton';
import Account from 'mastodon/containers/account_container';
import { domain } from 'mastodon/initial_state'; import { domain } from 'mastodon/initial_state';
const messages = defineMessages({ const messages = defineMessages({

View file

@ -1,60 +0,0 @@
import { injectIntl } from 'react-intl';
import { connect } from 'react-redux';
import { openModal } from 'mastodon/actions/modal';
import {
followAccount,
blockAccount,
unblockAccount,
muteAccount,
unmuteAccount,
} from '../actions/accounts';
import { initMuteModal } from '../actions/mutes';
import Account from '../components/account';
import { makeGetAccount } from '../selectors';
const makeMapStateToProps = () => {
const getAccount = makeGetAccount();
const mapStateToProps = (state, props) => ({
account: getAccount(state, props.id),
});
return mapStateToProps;
};
const mapDispatchToProps = (dispatch) => ({
onFollow (account) {
if (account.getIn(['relationship', 'following']) || account.getIn(['relationship', 'requested'])) {
dispatch(openModal({ modalType: 'CONFIRM_UNFOLLOW', modalProps: { account } }));
} else {
dispatch(followAccount(account.get('id')));
}
},
onBlock (account) {
if (account.getIn(['relationship', 'blocking'])) {
dispatch(unblockAccount(account.get('id')));
} else {
dispatch(blockAccount(account.get('id')));
}
},
onMute (account) {
if (account.getIn(['relationship', 'muting'])) {
dispatch(unmuteAccount(account.get('id')));
} else {
dispatch(initMuteModal(account));
}
},
onMuteNotifications (account, notifications) {
dispatch(muteAccount(account.get('id'), notifications));
},
});
export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Account));

View file

@ -15,11 +15,11 @@ import DisabledIcon from '@/material-icons/400-24px/close-fill.svg?react';
import EnabledIcon from '@/material-icons/400-24px/done-fill.svg?react'; import EnabledIcon from '@/material-icons/400-24px/done-fill.svg?react';
import ExpandMoreIcon from '@/material-icons/400-24px/expand_more.svg?react'; import ExpandMoreIcon from '@/material-icons/400-24px/expand_more.svg?react';
import { fetchServer, fetchExtendedDescription, fetchDomainBlocks } from 'mastodon/actions/server'; import { fetchServer, fetchExtendedDescription, fetchDomainBlocks } from 'mastodon/actions/server';
import { Account } from 'mastodon/components/account';
import Column from 'mastodon/components/column'; import Column from 'mastodon/components/column';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import { ServerHeroImage } from 'mastodon/components/server_hero_image'; import { ServerHeroImage } from 'mastodon/components/server_hero_image';
import { Skeleton } from 'mastodon/components/skeleton'; import { Skeleton } from 'mastodon/components/skeleton';
import Account from 'mastodon/containers/account_container';
import LinkFooter from 'mastodon/features/ui/components/link_footer'; import LinkFooter from 'mastodon/features/ui/components/link_footer';
const messages = defineMessages({ const messages = defineMessages({

View file

@ -1,6 +1,7 @@
/* eslint-disable react/jsx-no-useless-fragment */ /* eslint-disable react/jsx-no-useless-fragment */
import { FormattedMessage, FormattedNumber } from 'react-intl'; import { FormattedMessage, FormattedNumber } from 'react-intl';
import { domain } from 'mastodon/initial_state';
import type { Percentiles } from 'mastodon/models/annual_report'; import type { Percentiles } from 'mastodon/models/annual_report';
export const Percentile: React.FC<{ export const Percentile: React.FC<{
@ -12,7 +13,7 @@ export const Percentile: React.FC<{
<div className='annual-report__bento__box annual-report__summary__percentile'> <div className='annual-report__bento__box annual-report__summary__percentile'>
<FormattedMessage <FormattedMessage
id='annual_report.summary.percentile.text' id='annual_report.summary.percentile.text'
defaultMessage='<topLabel>That puts you in the top</topLabel><percentage></percentage><bottomLabel>of Mastodon users.</bottomLabel>' defaultMessage='<topLabel>That puts you in the top</topLabel><percentage></percentage><bottomLabel>of {domain} users.</bottomLabel>'
values={{ values={{
topLabel: (str) => ( topLabel: (str) => (
<div className='annual-report__summary__percentile__label'> <div className='annual-report__summary__percentile__label'>
@ -44,6 +45,8 @@ export const Percentile: React.FC<{
)} )}
</div> </div>
), ),
domain,
}} }}
> >
{(message) => <>{message}</>} {(message) => <>{message}</>}

View file

@ -9,11 +9,11 @@ import { connect } from 'react-redux';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import BlockIcon from '@/material-icons/400-24px/block-fill.svg?react'; import BlockIcon from '@/material-icons/400-24px/block-fill.svg?react';
import { Account } from 'mastodon/components/account';
import { fetchBlocks, expandBlocks } from '../../actions/blocks'; import { fetchBlocks, expandBlocks } from '../../actions/blocks';
import { LoadingIndicator } from '../../components/loading_indicator'; import { LoadingIndicator } from '../../components/loading_indicator';
import ScrollableList from '../../components/scrollable_list'; import ScrollableList from '../../components/scrollable_list';
import AccountContainer from '../../containers/account_container';
import Column from '../ui/components/column'; import Column from '../ui/components/column';
const messages = defineMessages({ const messages = defineMessages({
@ -70,7 +70,7 @@ class Blocks extends ImmutablePureComponent {
bindToDocument={!multiColumn} bindToDocument={!multiColumn}
> >
{accountIds.map(id => {accountIds.map(id =>
<AccountContainer key={id} id={id} defaultAction='block' />, <Account key={id} id={id} defaultAction='block' />,
)} )}
</ScrollableList> </ScrollableList>
</Column> </Column>

View file

@ -6,7 +6,7 @@ import { useSelector, useDispatch } from 'react-redux';
import CloseIcon from '@/material-icons/400-24px/close.svg?react'; import CloseIcon from '@/material-icons/400-24px/close.svg?react';
import { cancelReplyCompose } from 'mastodon/actions/compose'; import { cancelReplyCompose } from 'mastodon/actions/compose';
import Account from 'mastodon/components/account'; import { Account } from 'mastodon/components/account';
import { IconButton } from 'mastodon/components/icon_button'; import { IconButton } from 'mastodon/components/icon_button';
import { me } from 'mastodon/initial_state'; import { me } from 'mastodon/initial_state';
@ -20,7 +20,6 @@ const messages = defineMessages({
export const NavigationBar = () => { export const NavigationBar = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const intl = useIntl(); const intl = useIntl();
const account = useSelector(state => state.getIn(['accounts', me]));
const isReplying = useSelector(state => !!state.getIn(['compose', 'in_reply_to'])); const isReplying = useSelector(state => !!state.getIn(['compose', 'in_reply_to']));
const handleCancelClick = useCallback(() => { const handleCancelClick = useCallback(() => {
@ -29,7 +28,7 @@ export const NavigationBar = () => {
return ( return (
<div className='navigation-bar'> <div className='navigation-bar'>
<Account account={account} minimal /> <Account id={me} minimal />
{isReplying ? <IconButton title={intl.formatMessage(messages.cancel)} iconComponent={CloseIcon} onClick={handleCancelClick} /> : <ActionBar />} {isReplying ? <IconButton title={intl.formatMessage(messages.cancel)} iconComponent={CloseIcon} onClick={handleCancelClick} /> : <ActionBar />}
</div> </div>
); );

View file

@ -6,6 +6,7 @@ import FindInPageIcon from '@/material-icons/400-24px/find_in_page.svg?react';
import PeopleIcon from '@/material-icons/400-24px/group.svg?react'; import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
import TagIcon from '@/material-icons/400-24px/tag.svg?react'; import TagIcon from '@/material-icons/400-24px/tag.svg?react';
import { expandSearch } from 'mastodon/actions/search'; import { expandSearch } from 'mastodon/actions/search';
import { Account } from 'mastodon/components/account';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import { LoadMore } from 'mastodon/components/load_more'; import { LoadMore } from 'mastodon/components/load_more';
import { LoadingIndicator } from 'mastodon/components/loading_indicator'; import { LoadingIndicator } from 'mastodon/components/loading_indicator';
@ -13,7 +14,6 @@ import { SearchSection } from 'mastodon/features/explore/components/search_secti
import { useAppDispatch, useAppSelector } from 'mastodon/store'; import { useAppDispatch, useAppSelector } from 'mastodon/store';
import { ImmutableHashtag as Hashtag } from '../../../components/hashtag'; import { ImmutableHashtag as Hashtag } from '../../../components/hashtag';
import AccountContainer from '../../../containers/account_container';
import StatusContainer from '../../../containers/status_container'; import StatusContainer from '../../../containers/status_container';
const INITIAL_PAGE_LIMIT = 10; const INITIAL_PAGE_LIMIT = 10;
@ -49,7 +49,7 @@ export const SearchResults = () => {
if (results.get('accounts') && results.get('accounts').size > 0) { if (results.get('accounts') && results.get('accounts').size > 0) {
accounts = ( accounts = (
<SearchSection title={<><Icon id='users' icon={PeopleIcon} /><FormattedMessage id='search_results.accounts' defaultMessage='Profiles' /></>}> <SearchSection title={<><Icon id='users' icon={PeopleIcon} /><FormattedMessage id='search_results.accounts' defaultMessage='Profiles' /></>}>
{withoutLastResult(results.get('accounts')).map(accountId => <AccountContainer key={accountId} id={accountId} />)} {withoutLastResult(results.get('accounts')).map(accountId => <Account key={accountId} id={accountId} />)}
{(results.get('accounts').size > INITIAL_PAGE_LIMIT && results.get('accounts').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={handleLoadMoreAccounts} />} {(results.get('accounts').size > INITIAL_PAGE_LIMIT && results.get('accounts').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={handleLoadMoreAccounts} />}
</SearchSection> </SearchSection>
); );

View file

@ -10,12 +10,13 @@ import { connect } from 'react-redux';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import RefreshIcon from '@/material-icons/400-24px/refresh.svg?react'; import RefreshIcon from '@/material-icons/400-24px/refresh.svg?react';
import { fetchEmojiReactions, expandEmojiReactions } from 'mastodon/actions/interactions'; import { fetchEmojiReactions, expandEmojiReactions } from 'mastodon/actions/interactions';
import { Account } from 'mastodon/components/account';
import ColumnHeader from 'mastodon/components/column_header'; import ColumnHeader from 'mastodon/components/column_header';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import ScrollableList from 'mastodon/components/scrollable_list'; import ScrollableList from 'mastodon/components/scrollable_list';
import AccountContainer from 'mastodon/containers/account_container';
import Column from 'mastodon/features/ui/components/column'; import Column from 'mastodon/features/ui/components/column';
@ -101,11 +102,11 @@ class EmojiReactions extends ImmutablePureComponent {
bindToDocument={!multiColumn} bindToDocument={!multiColumn}
> >
{Object.keys(groups).map((key) =>( {Object.keys(groups).map((key) =>(
<AccountContainer key={key} id={key} withNote={false} hideButtons> <Account key={key} id={key} hideButtons>
<div style={{ 'maxWidth': '100px' }}> <div style={{ 'maxWidth': '100px' }}>
{groups[key].map((value, index2) => <EmojiView key={index2} name={value.name} url={value.url} staticUrl={value.static_url} />)} {groups[key].map((value, index2) => <EmojiView key={index2} name={value.name} url={value.url} staticUrl={value.static_url} />)}
</div> </div>
</AccountContainer> </Account>
))} ))}
</ScrollableList> </ScrollableList>

View file

@ -13,10 +13,10 @@ import FindInPageIcon from '@/material-icons/400-24px/find_in_page.svg?react';
import PeopleIcon from '@/material-icons/400-24px/group.svg?react'; import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
import TagIcon from '@/material-icons/400-24px/tag.svg?react'; import TagIcon from '@/material-icons/400-24px/tag.svg?react';
import { submitSearch, expandSearch } from 'mastodon/actions/search'; import { submitSearch, expandSearch } from 'mastodon/actions/search';
import { Account } from 'mastodon/components/account';
import { ImmutableHashtag as Hashtag } from 'mastodon/components/hashtag'; import { ImmutableHashtag as Hashtag } from 'mastodon/components/hashtag';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import ScrollableList from 'mastodon/components/scrollable_list'; import ScrollableList from 'mastodon/components/scrollable_list';
import Account from 'mastodon/containers/account_container';
import Status from 'mastodon/containers/status_container'; import Status from 'mastodon/containers/status_container';
import { SearchSection } from './components/search_section'; import { SearchSection } from './components/search_section';

View file

@ -14,11 +14,11 @@ import { debounce } from 'lodash';
import RefreshIcon from '@/material-icons/400-24px/refresh.svg?react'; import RefreshIcon from '@/material-icons/400-24px/refresh.svg?react';
import { fetchFavourites, expandFavourites } from 'mastodon/actions/interactions'; import { fetchFavourites, expandFavourites } from 'mastodon/actions/interactions';
import { Account } from 'mastodon/components/account';
import ColumnHeader from 'mastodon/components/column_header'; import ColumnHeader from 'mastodon/components/column_header';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import { LoadingIndicator } from 'mastodon/components/loading_indicator'; import { LoadingIndicator } from 'mastodon/components/loading_indicator';
import ScrollableList from 'mastodon/components/scrollable_list'; import ScrollableList from 'mastodon/components/scrollable_list';
import AccountContainer from 'mastodon/containers/account_container';
import Column from 'mastodon/features/ui/components/column'; import Column from 'mastodon/features/ui/components/column';
const messages = defineMessages({ const messages = defineMessages({
@ -89,7 +89,7 @@ class Favourites extends ImmutablePureComponent {
bindToDocument={!multiColumn} bindToDocument={!multiColumn}
> >
{accountIds.map(id => {accountIds.map(id =>
<AccountContainer key={id} id={id} withNote={false} />, <Account key={id} id={id} />,
)} )}
</ScrollableList> </ScrollableList>

View file

@ -8,6 +8,7 @@ import { connect } from 'react-redux';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { Account } from 'mastodon/components/account';
import { TimelineHint } from 'mastodon/components/timeline_hint'; import { TimelineHint } from 'mastodon/components/timeline_hint';
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error'; import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
import { isHideItem, me } from 'mastodon/initial_state'; import { isHideItem, me } from 'mastodon/initial_state';
@ -24,7 +25,6 @@ import {
import { ColumnBackButton } from '../../components/column_back_button'; import { ColumnBackButton } from '../../components/column_back_button';
import { LoadingIndicator } from '../../components/loading_indicator'; import { LoadingIndicator } from '../../components/loading_indicator';
import ScrollableList from '../../components/scrollable_list'; import ScrollableList from '../../components/scrollable_list';
import AccountContainer from '../../containers/account_container';
import { LimitedAccountHint } from '../account_timeline/components/limited_account_hint'; import { LimitedAccountHint } from '../account_timeline/components/limited_account_hint';
import HeaderContainer from '../account_timeline/containers/header_container'; import HeaderContainer from '../account_timeline/containers/header_container';
import Column from '../ui/components/column'; import Column from '../ui/components/column';
@ -179,7 +179,7 @@ class Followers extends ImmutablePureComponent {
bindToDocument={!multiColumn} bindToDocument={!multiColumn}
> >
{forceEmptyState ? [] : accountIds.map(id => {forceEmptyState ? [] : accountIds.map(id =>
<AccountContainer key={id} id={id} withNote={false} />, <Account key={id} id={id} />,
)} )}
</ScrollableList> </ScrollableList>
</Column> </Column>

View file

@ -8,6 +8,7 @@ import { connect } from 'react-redux';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { Account } from 'mastodon/components/account';
import { TimelineHint } from 'mastodon/components/timeline_hint'; import { TimelineHint } from 'mastodon/components/timeline_hint';
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error'; import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
import { isHideItem, me } from 'mastodon/initial_state'; import { isHideItem, me } from 'mastodon/initial_state';
@ -24,7 +25,6 @@ import {
import { ColumnBackButton } from '../../components/column_back_button'; import { ColumnBackButton } from '../../components/column_back_button';
import { LoadingIndicator } from '../../components/loading_indicator'; import { LoadingIndicator } from '../../components/loading_indicator';
import ScrollableList from '../../components/scrollable_list'; import ScrollableList from '../../components/scrollable_list';
import AccountContainer from '../../containers/account_container';
import { LimitedAccountHint } from '../account_timeline/components/limited_account_hint'; import { LimitedAccountHint } from '../account_timeline/components/limited_account_hint';
import HeaderContainer from '../account_timeline/containers/header_container'; import HeaderContainer from '../account_timeline/containers/header_container';
import Column from '../ui/components/column'; import Column from '../ui/components/column';
@ -177,7 +177,7 @@ class Following extends ImmutablePureComponent {
bindToDocument={!multiColumn} bindToDocument={!multiColumn}
> >
{forceEmptyState ? [] : filteredAccountIds.map(id => {forceEmptyState ? [] : filteredAccountIds.map(id =>
<AccountContainer key={id} id={id} withNote={false} />, <Account key={id} id={id} />,
)} )}
</ScrollableList> </ScrollableList>
</Column> </Column>

View file

@ -9,9 +9,13 @@ import { useDebouncedCallback } from 'use-debounce';
import ListAltIcon from '@/material-icons/400-24px/list_alt.svg?react'; import ListAltIcon from '@/material-icons/400-24px/list_alt.svg?react';
import SquigglyArrow from '@/svg-icons/squiggly_arrow.svg?react'; import SquigglyArrow from '@/svg-icons/squiggly_arrow.svg?react';
import { fetchRelationships } from 'mastodon/actions/accounts';
import { showAlertForError } from 'mastodon/actions/alerts';
import { importFetchedAccounts } from 'mastodon/actions/importer'; import { importFetchedAccounts } from 'mastodon/actions/importer';
import { fetchList } from 'mastodon/actions/lists'; import { fetchList } from 'mastodon/actions/lists';
import { openModal } from 'mastodon/actions/modal';
import { apiRequest } from 'mastodon/api'; import { apiRequest } from 'mastodon/api';
import { apiFollowAccount } from 'mastodon/api/accounts';
import { import {
apiGetAccounts, apiGetAccounts,
apiAddAccountToList, apiAddAccountToList,
@ -28,13 +32,14 @@ import { DisplayName } from 'mastodon/components/display_name';
import ScrollableList from 'mastodon/components/scrollable_list'; import ScrollableList from 'mastodon/components/scrollable_list';
import { ShortNumber } from 'mastodon/components/short_number'; import { ShortNumber } from 'mastodon/components/short_number';
import { VerifiedBadge } from 'mastodon/components/verified_badge'; import { VerifiedBadge } from 'mastodon/components/verified_badge';
import { me } from 'mastodon/initial_state';
import { useAppDispatch, useAppSelector } from 'mastodon/store'; import { useAppDispatch, useAppSelector } from 'mastodon/store';
const messages = defineMessages({ const messages = defineMessages({
heading: { id: 'column.list_members', defaultMessage: 'Manage list members' }, heading: { id: 'column.list_members', defaultMessage: 'Manage list members' },
placeholder: { placeholder: {
id: 'lists.search_placeholder', id: 'lists.search',
defaultMessage: 'Search people you follow', defaultMessage: 'Search',
}, },
enterSearch: { id: 'lists.add_to_list', defaultMessage: 'Add to list' }, enterSearch: { id: 'lists.add_to_list', defaultMessage: 'Add to list' },
add: { id: 'lists.add_member', defaultMessage: 'Add' }, add: { id: 'lists.add_member', defaultMessage: 'Add' },
@ -51,17 +56,51 @@ const AccountItem: React.FC<{
onToggle: (accountId: string) => void; onToggle: (accountId: string) => void;
}> = ({ accountId, listId, partOfList, onToggle }) => { }> = ({ accountId, listId, partOfList, onToggle }) => {
const intl = useIntl(); const intl = useIntl();
const dispatch = useAppDispatch();
const account = useAppSelector((state) => state.accounts.get(accountId)); const account = useAppSelector((state) => state.accounts.get(accountId));
const relationship = useAppSelector((state) =>
accountId ? state.relationships.get(accountId) : undefined,
);
const following =
accountId === me || relationship?.following || relationship?.requested;
useEffect(() => {
if (accountId) {
dispatch(fetchRelationships([accountId]));
}
}, [dispatch, accountId]);
const handleClick = useCallback(() => { const handleClick = useCallback(() => {
if (partOfList) { if (partOfList) {
void apiRemoveAccountFromList(listId, accountId); void apiRemoveAccountFromList(listId, accountId);
} else {
void apiAddAccountToList(listId, accountId);
}
onToggle(accountId); onToggle(accountId);
}, [accountId, listId, partOfList, onToggle]); } else {
if (following) {
void apiAddAccountToList(listId, accountId);
onToggle(accountId);
} else {
dispatch(
openModal({
modalType: 'CONFIRM_FOLLOW_TO_LIST',
modalProps: {
accountId,
onConfirm: () => {
apiFollowAccount(accountId)
.then(() => apiAddAccountToList(listId, accountId))
.then(() => {
onToggle(accountId);
return '';
})
.catch((err: unknown) => {
dispatch(showAlertForError(err));
});
},
},
}),
);
}
}
}, [dispatch, accountId, following, listId, partOfList, onToggle]);
if (!account) { if (!account) {
return null; return null;
@ -187,8 +226,7 @@ const ListMembers: React.FC<{
signal: searchRequestRef.current.signal, signal: searchRequestRef.current.signal,
params: { params: {
q: value, q: value,
resolve: false, resolve: true,
following: true,
}, },
}) })
.then((data) => { .then((data) => {

View file

@ -11,10 +11,10 @@ import { connect } from 'react-redux';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { fetchMentionedUsers, expandMentionedUsers } from 'mastodon/actions/interactions'; import { fetchMentionedUsers, expandMentionedUsers } from 'mastodon/actions/interactions';
import { Account } from 'mastodon/components/account';
import ColumnHeader from 'mastodon/components/column_header'; import ColumnHeader from 'mastodon/components/column_header';
import { LoadingIndicator } from 'mastodon/components/loading_indicator'; import { LoadingIndicator } from 'mastodon/components/loading_indicator';
import ScrollableList from 'mastodon/components/scrollable_list'; import ScrollableList from 'mastodon/components/scrollable_list';
import AccountContainer from 'mastodon/containers/account_container';
import Column from 'mastodon/features/ui/components/column'; import Column from 'mastodon/features/ui/components/column';
const mapStateToProps = (state, props) => ({ const mapStateToProps = (state, props) => ({
@ -74,7 +74,7 @@ class MentionedUsers extends ImmutablePureComponent {
bindToDocument={!multiColumn} bindToDocument={!multiColumn}
> >
{accountIds.map(id => {accountIds.map(id =>
<AccountContainer key={id} id={id} withNote={false} />, <Account key={id} id={id} />,
)} )}
</ScrollableList> </ScrollableList>

View file

@ -11,11 +11,11 @@ import { connect } from 'react-redux';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import VolumeOffIcon from '@/material-icons/400-24px/volume_off.svg?react'; import VolumeOffIcon from '@/material-icons/400-24px/volume_off.svg?react';
import { Account } from 'mastodon/components/account';
import { fetchMutes, expandMutes } from '../../actions/mutes'; import { fetchMutes, expandMutes } from '../../actions/mutes';
import { LoadingIndicator } from '../../components/loading_indicator'; import { LoadingIndicator } from '../../components/loading_indicator';
import ScrollableList from '../../components/scrollable_list'; import ScrollableList from '../../components/scrollable_list';
import AccountContainer from '../../containers/account_container';
import Column from '../ui/components/column'; import Column from '../ui/components/column';
const messages = defineMessages({ const messages = defineMessages({
@ -72,7 +72,7 @@ class Mutes extends ImmutablePureComponent {
bindToDocument={!multiColumn} bindToDocument={!multiColumn}
> >
{accountIds.map(id => {accountIds.map(id =>
<AccountContainer key={id} id={id} defaultAction='mute' />, <Account key={id} id={id} defaultAction='mute' />,
)} )}
</ScrollableList> </ScrollableList>

View file

@ -20,9 +20,9 @@ import PersonIcon from '@/material-icons/400-24px/person-fill.svg?react';
import PersonAddIcon from '@/material-icons/400-24px/person_add-fill.svg?react'; import PersonAddIcon from '@/material-icons/400-24px/person_add-fill.svg?react';
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react'; import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react'; import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
import { Account } from 'mastodon/components/account';
import EmojiView from 'mastodon/components/emoji_view'; import EmojiView from 'mastodon/components/emoji_view';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import AccountContainer from 'mastodon/containers/account_container';
import StatusContainer from 'mastodon/containers/status_container'; import StatusContainer from 'mastodon/containers/status_container';
import { me } from 'mastodon/initial_state'; import { me } from 'mastodon/initial_state';
import { WithRouterPropTypes } from 'mastodon/utils/react_router'; import { WithRouterPropTypes } from 'mastodon/utils/react_router';
@ -153,7 +153,7 @@ class Notification extends ImmutablePureComponent {
</span> </span>
</div> </div>
<AccountContainer id={account.get('id')} hidden={this.props.hidden} /> <Account id={account.get('id')} hidden={this.props.hidden} />
</div> </div>
</HotKeys> </HotKeys>
); );
@ -173,7 +173,7 @@ class Notification extends ImmutablePureComponent {
</span> </span>
</div> </div>
<FollowRequestContainer id={account.get('id')} withNote={false} hidden={this.props.hidden} /> <FollowRequestContainer id={account.get('id')} hidden={this.props.hidden} />
</div> </div>
</HotKeys> </HotKeys>
); );
@ -533,7 +533,7 @@ class Notification extends ImmutablePureComponent {
</span> </span>
</div> </div>
<AccountContainer id={account.get('id')} hidden={this.props.hidden} /> <Account id={account.get('id')} hidden={this.props.hidden} />
</div> </div>
</HotKeys> </HotKeys>
); );

View file

@ -14,11 +14,11 @@ import { fetchSuggestions } from 'mastodon/actions/suggestions';
import { markAsPartial } from 'mastodon/actions/timelines'; import { markAsPartial } from 'mastodon/actions/timelines';
import { apiRequest } from 'mastodon/api'; import { apiRequest } from 'mastodon/api';
import type { ApiAccountJSON } from 'mastodon/api_types/accounts'; import type { ApiAccountJSON } from 'mastodon/api_types/accounts';
import { Account } from 'mastodon/components/account';
import { Column } from 'mastodon/components/column'; import { Column } from 'mastodon/components/column';
import { ColumnHeader } from 'mastodon/components/column_header'; import { ColumnHeader } from 'mastodon/components/column_header';
import { ColumnSearchHeader } from 'mastodon/components/column_search_header'; import { ColumnSearchHeader } from 'mastodon/components/column_search_header';
import ScrollableList from 'mastodon/components/scrollable_list'; import ScrollableList from 'mastodon/components/scrollable_list';
import Account from 'mastodon/containers/account_container';
import { useAppSelector, useAppDispatch } from 'mastodon/store'; import { useAppSelector, useAppDispatch } from 'mastodon/store';
const messages = defineMessages({ const messages = defineMessages({
@ -170,12 +170,7 @@ export const Follows: React.FC<{
} }
> >
{displayedAccountIds.map((accountId) => ( {displayedAccountIds.map((accountId) => (
<Account <Account id={accountId} key={accountId} withBio />
/* @ts-expect-error inferred props are wrong */
id={accountId}
key={accountId}
withBio
/>
))} ))}
</ScrollableList> </ScrollableList>

View file

@ -11,13 +11,13 @@ import { connect } from 'react-redux';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import RefreshIcon from '@/material-icons/400-24px/refresh.svg?react'; import RefreshIcon from '@/material-icons/400-24px/refresh.svg?react';
import { Account } from 'mastodon/components/account';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import { fetchReblogs, expandReblogs } from '../../actions/interactions'; import { fetchReblogs, expandReblogs } from '../../actions/interactions';
import ColumnHeader from '../../components/column_header'; import ColumnHeader from '../../components/column_header';
import { LoadingIndicator } from '../../components/loading_indicator'; import { LoadingIndicator } from '../../components/loading_indicator';
import ScrollableList from '../../components/scrollable_list'; import ScrollableList from '../../components/scrollable_list';
import AccountContainer from '../../containers/account_container';
import Column from '../ui/components/column'; import Column from '../ui/components/column';
const messages = defineMessages({ const messages = defineMessages({
@ -88,7 +88,7 @@ class Reblogs extends ImmutablePureComponent {
bindToDocument={!multiColumn} bindToDocument={!multiColumn}
> >
{accountIds.map(id => {accountIds.map(id =>
<AccountContainer key={id} id={id} withNote={false} />, <Account key={id} id={id} />,
)} )}
</ScrollableList> </ScrollableList>

View file

@ -0,0 +1,43 @@
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { useAppSelector } from 'mastodon/store';
import type { BaseConfirmationModalProps } from './confirmation_modal';
import { ConfirmationModal } from './confirmation_modal';
const messages = defineMessages({
title: {
id: 'confirmations.follow_to_list.title',
defaultMessage: 'Follow user?',
},
confirm: {
id: 'confirmations.follow_to_list.confirm',
defaultMessage: 'Follow and add to list',
},
});
export const ConfirmFollowToListModal: React.FC<
{
accountId: string;
onConfirm: () => void;
} & BaseConfirmationModalProps
> = ({ accountId, onConfirm, onClose }) => {
const intl = useIntl();
const account = useAppSelector((state) => state.accounts.get(accountId));
return (
<ConfirmationModal
title={intl.formatMessage(messages.title)}
message={
<FormattedMessage
id='confirmations.follow_to_list.message'
defaultMessage='You need to be following {name} to add them to a list.'
values={{ name: <strong>@{account?.acct}</strong> }}
/>
}
confirm={intl.formatMessage(messages.confirm)}
onConfirm={onConfirm}
onClose={onClose}
/>
);
};

View file

@ -9,3 +9,4 @@ export { ConfirmEditStatusModal } from './edit_status';
export { ConfirmUnfollowModal } from './unfollow'; export { ConfirmUnfollowModal } from './unfollow';
export { ConfirmClearNotificationsModal } from './clear_notifications'; export { ConfirmClearNotificationsModal } from './clear_notifications';
export { ConfirmLogOutModal } from './log_out'; export { ConfirmLogOutModal } from './log_out';
export { ConfirmFollowToListModal } from './follow_to_list';

View file

@ -41,6 +41,7 @@ import {
ConfirmUnfollowModal, ConfirmUnfollowModal,
ConfirmClearNotificationsModal, ConfirmClearNotificationsModal,
ConfirmLogOutModal, ConfirmLogOutModal,
ConfirmFollowToListModal,
} from './confirmation_modals'; } from './confirmation_modals';
import FocalPointModal from './focal_point_modal'; import FocalPointModal from './focal_point_modal';
import ImageModal from './image_modal'; import ImageModal from './image_modal';
@ -65,6 +66,7 @@ export const MODAL_COMPONENTS = {
'CONFIRM_UNFOLLOW': () => Promise.resolve({ default: ConfirmUnfollowModal }), 'CONFIRM_UNFOLLOW': () => Promise.resolve({ default: ConfirmUnfollowModal }),
'CONFIRM_CLEAR_NOTIFICATIONS': () => Promise.resolve({ default: ConfirmClearNotificationsModal }), 'CONFIRM_CLEAR_NOTIFICATIONS': () => Promise.resolve({ default: ConfirmClearNotificationsModal }),
'CONFIRM_LOG_OUT': () => Promise.resolve({ default: ConfirmLogOutModal }), 'CONFIRM_LOG_OUT': () => Promise.resolve({ default: ConfirmLogOutModal }),
'CONFIRM_FOLLOW_TO_LIST': () => Promise.resolve({ default: ConfirmFollowToListModal }),
'MUTE': MuteModal, 'MUTE': MuteModal,
'BLOCK': BlockModal, 'BLOCK': BlockModal,
'DOMAIN_BLOCK': DomainBlockModal, 'DOMAIN_BLOCK': DomainBlockModal,

View file

@ -101,6 +101,7 @@
"annual_report.summary.highlighted_post.possessive": "του χρήστη {name}", "annual_report.summary.highlighted_post.possessive": "του χρήστη {name}",
"annual_report.summary.most_used_app.most_used_app": "πιο χρησιμοποιημένη εφαρμογή", "annual_report.summary.most_used_app.most_used_app": "πιο χρησιμοποιημένη εφαρμογή",
"annual_report.summary.most_used_hashtag.most_used_hashtag": "πιο χρησιμοποιημένη ετικέτα", "annual_report.summary.most_used_hashtag.most_used_hashtag": "πιο χρησιμοποιημένη ετικέτα",
"annual_report.summary.most_used_hashtag.none": "Κανένα",
"annual_report.summary.new_posts.new_posts": "νέες αναρτήσεις", "annual_report.summary.new_posts.new_posts": "νέες αναρτήσεις",
"annual_report.summary.percentile.text": "<topLabel>Αυτό σε βάζει στην κορυφή του </topLabel><percentage></percentage><bottomLabel>των χρηστών του Mastodon.</bottomLabel>", "annual_report.summary.percentile.text": "<topLabel>Αυτό σε βάζει στην κορυφή του </topLabel><percentage></percentage><bottomLabel>των χρηστών του Mastodon.</bottomLabel>",
"annual_report.summary.percentile.we_wont_tell_bernie": "Δεν θα το πούμε στον Bernie.", "annual_report.summary.percentile.we_wont_tell_bernie": "Δεν θα το πούμε στον Bernie.",
@ -128,6 +129,7 @@
"bundle_column_error.routing.body": "Η επιθυμητή σελίδα δεν βρέθηκε. Είναι σωστό το URL στο πεδίο διευθύνσεων;", "bundle_column_error.routing.body": "Η επιθυμητή σελίδα δεν βρέθηκε. Είναι σωστό το URL στο πεδίο διευθύνσεων;",
"bundle_column_error.routing.title": "404", "bundle_column_error.routing.title": "404",
"bundle_modal_error.close": "Κλείσιμο", "bundle_modal_error.close": "Κλείσιμο",
"bundle_modal_error.message": "Κάτι πήγε στραβά κατά τη φόρτωση αυτής της οθόνης.",
"bundle_modal_error.retry": "Δοκίμασε ξανά", "bundle_modal_error.retry": "Δοκίμασε ξανά",
"closed_registrations.other_server_instructions": "Καθώς το Mastodon είναι αποκεντρωμένο, μπορείς να δημιουργήσεις λογαριασμό σε άλλον διακομιστή αλλά να συνεχίσεις να αλληλεπιδράς με αυτόν.", "closed_registrations.other_server_instructions": "Καθώς το Mastodon είναι αποκεντρωμένο, μπορείς να δημιουργήσεις λογαριασμό σε άλλον διακομιστή αλλά να συνεχίσεις να αλληλεπιδράς με αυτόν.",
"closed_registrations_modal.description": "Η δημιουργία λογαριασμού στον {domain} προς το παρόν δεν είναι δυνατή, αλλά λάβε υπόψη ότι δεν χρειάζεσαι λογαριασμό ειδικά στον {domain} για να χρησιμοποιήσεις το Mastodon.", "closed_registrations_modal.description": "Η δημιουργία λογαριασμού στον {domain} προς το παρόν δεν είναι δυνατή, αλλά λάβε υπόψη ότι δεν χρειάζεσαι λογαριασμό ειδικά στον {domain} για να χρησιμοποιήσεις το Mastodon.",
@ -138,13 +140,16 @@
"column.blocks": "Αποκλεισμένοι χρήστες", "column.blocks": "Αποκλεισμένοι χρήστες",
"column.bookmarks": "Σελιδοδείκτες", "column.bookmarks": "Σελιδοδείκτες",
"column.community": "Τοπική ροή", "column.community": "Τοπική ροή",
"column.create_list": "Δημιουργία λίστας",
"column.direct": "Ιδιωτικές αναφορές", "column.direct": "Ιδιωτικές αναφορές",
"column.directory": "Περιήγηση στα προφίλ", "column.directory": "Περιήγηση στα προφίλ",
"column.domain_blocks": "Αποκλεισμένοι τομείς", "column.domain_blocks": "Αποκλεισμένοι τομείς",
"column.edit_list": "Επεξεργασία λίστας",
"column.favourites": "Αγαπημένα", "column.favourites": "Αγαπημένα",
"column.firehose": "Ζωντανές ροές", "column.firehose": "Ζωντανές ροές",
"column.follow_requests": "Αιτήματα ακολούθησης", "column.follow_requests": "Αιτήματα ακολούθησης",
"column.home": "Αρχική", "column.home": "Αρχική",
"column.list_members": "Διαχείριση μελών λίστας",
"column.lists": "Λίστες", "column.lists": "Λίστες",
"column.mutes": "Αποσιωπημένοι χρήστες", "column.mutes": "Αποσιωπημένοι χρήστες",
"column.notifications": "Ειδοποιήσεις", "column.notifications": "Ειδοποιήσεις",
@ -157,6 +162,7 @@
"column_header.pin": "Καρφίτσωμα", "column_header.pin": "Καρφίτσωμα",
"column_header.show_settings": "Εμφάνιση ρυθμίσεων", "column_header.show_settings": "Εμφάνιση ρυθμίσεων",
"column_header.unpin": "Ξεκαρφίτσωμα", "column_header.unpin": "Ξεκαρφίτσωμα",
"column_search.cancel": "Ακύρωση",
"column_subheading.settings": "Ρυθμίσεις", "column_subheading.settings": "Ρυθμίσεις",
"community.column_settings.local_only": "Τοπικά μόνο", "community.column_settings.local_only": "Τοπικά μόνο",
"community.column_settings.media_only": "Μόνο πολυμέσα", "community.column_settings.media_only": "Μόνο πολυμέσα",
@ -230,6 +236,8 @@
"disabled_account_banner.text": "Ο λογαριασμός σου {disabledAccount} είναι προς το παρόν απενεργοποιημένος.", "disabled_account_banner.text": "Ο λογαριασμός σου {disabledAccount} είναι προς το παρόν απενεργοποιημένος.",
"dismissable_banner.community_timeline": "Αυτές είναι οι πιο πρόσφατες δημόσιες αναρτήσεις ατόμων των οποίων οι λογαριασμοί φιλοξενούνται στο {domain}.", "dismissable_banner.community_timeline": "Αυτές είναι οι πιο πρόσφατες δημόσιες αναρτήσεις ατόμων των οποίων οι λογαριασμοί φιλοξενούνται στο {domain}.",
"dismissable_banner.dismiss": "Παράβλεψη", "dismissable_banner.dismiss": "Παράβλεψη",
"dismissable_banner.explore_links": "Αυτές οι ιστορίες ειδήσεων μοιράζονται περισσότερο στο fediverse σήμερα. Νεότερες ιστορίες ειδήσεων που δημοσιεύτηκαν από πιο διαφορετικά άτομα κατατάσσονται υψηλότερα.",
"dismissable_banner.explore_statuses": "Αυτές οι αναρτήσεις από όλο το fediverse κερδίζουν την προσοχή σήμερα. Νεότερες αναρτήσεις με περισσότερες ενισχύσεις και αγαπημένα κατατάσσονται υψηλότερα.",
"domain_block_modal.block": "Αποκλεισμός διακομιστή", "domain_block_modal.block": "Αποκλεισμός διακομιστή",
"domain_block_modal.block_account_instead": "Αποκλεισμός @{name} αντ' αυτού", "domain_block_modal.block_account_instead": "Αποκλεισμός @{name} αντ' αυτού",
"domain_block_modal.they_can_interact_with_old_posts": "Άτομα από αυτόν τον διακομιστή μπορούν να αλληλεπιδράσουν με τις παλιές αναρτήσεις σου.", "domain_block_modal.they_can_interact_with_old_posts": "Άτομα από αυτόν τον διακομιστή μπορούν να αλληλεπιδράσουν με τις παλιές αναρτήσεις σου.",

View file

@ -116,7 +116,7 @@
"annual_report.summary.most_used_hashtag.most_used_hashtag": "most used hashtag", "annual_report.summary.most_used_hashtag.most_used_hashtag": "most used hashtag",
"annual_report.summary.most_used_hashtag.none": "None", "annual_report.summary.most_used_hashtag.none": "None",
"annual_report.summary.new_posts.new_posts": "new posts", "annual_report.summary.new_posts.new_posts": "new posts",
"annual_report.summary.percentile.text": "<topLabel>That puts you in the top</topLabel><percentage></percentage><bottomLabel>of Mastodon users.</bottomLabel>", "annual_report.summary.percentile.text": "<topLabel>That puts you in the top</topLabel><percentage></percentage><bottomLabel>of {domain} users.</bottomLabel>",
"annual_report.summary.percentile.we_wont_tell_bernie": "We won't tell Bernie.", "annual_report.summary.percentile.we_wont_tell_bernie": "We won't tell Bernie.",
"annual_report.summary.thanks": "Thanks for being part of Mastodon!", "annual_report.summary.thanks": "Thanks for being part of Mastodon!",
"antennas.accounts": "{count} accounts", "antennas.accounts": "{count} accounts",
@ -342,6 +342,9 @@
"confirmations.edit.confirm": "Edit", "confirmations.edit.confirm": "Edit",
"confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?", "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
"confirmations.edit.title": "Overwrite post?", "confirmations.edit.title": "Overwrite post?",
"confirmations.follow_to_list.confirm": "Follow and add to list",
"confirmations.follow_to_list.message": "You need to be following {name} to add them to a list.",
"confirmations.follow_to_list.title": "Follow user?",
"confirmations.logout.confirm": "Log out", "confirmations.logout.confirm": "Log out",
"confirmations.logout.message": "Are you sure you want to log out?", "confirmations.logout.message": "Are you sure you want to log out?",
"confirmations.logout.title": "Log out?", "confirmations.logout.title": "Log out?",
@ -644,7 +647,7 @@
"lists.replies_policy.none": "No one", "lists.replies_policy.none": "No one",
"lists.save": "Save", "lists.save": "Save",
"lists.save_to_edit_member": "You can edit list members after saving.", "lists.save_to_edit_member": "You can edit list members after saving.",
"lists.search_placeholder": "Search people you follow", "lists.search": "Search",
"lists.show_replies_to": "Include replies from list members to", "lists.show_replies_to": "Include replies from list members to",
"load_pending": "{count, plural, one {# new item} other {# new items}}", "load_pending": "{count, plural, one {# new item} other {# new items}}",
"loading_indicator.label": "Loading…", "loading_indicator.label": "Loading…",

View file

@ -0,0 +1 @@
{}

View file

@ -89,7 +89,7 @@
"announcement.announcement": "Объявление", "announcement.announcement": "Объявление",
"annual_report.summary.archetype.booster": "Репостер", "annual_report.summary.archetype.booster": "Репостер",
"annual_report.summary.archetype.lurker": "Молчун", "annual_report.summary.archetype.lurker": "Молчун",
"annual_report.summary.archetype.oracle": "Шаман", "annual_report.summary.archetype.oracle": "Гуру",
"annual_report.summary.archetype.pollster": "Опросчик", "annual_report.summary.archetype.pollster": "Опросчик",
"annual_report.summary.archetype.replier": "Душа компании", "annual_report.summary.archetype.replier": "Душа компании",
"annual_report.summary.followers.followers": "подписчиков", "annual_report.summary.followers.followers": "подписчиков",
@ -284,7 +284,7 @@
"empty_column.account_timeline": "Здесь нет постов!", "empty_column.account_timeline": "Здесь нет постов!",
"empty_column.account_unavailable": "Профиль недоступен", "empty_column.account_unavailable": "Профиль недоступен",
"empty_column.blocks": "Вы ещё никого не заблокировали.", "empty_column.blocks": "Вы ещё никого не заблокировали.",
"empty_column.bookmarked_statuses": "У вас пока нет постов в закладках. Как добавите один, он отобразится здесь.", "empty_column.bookmarked_statuses": "У вас пока нет закладок. Когда вы добавляете пост в закладки, он появляется здесь.",
"empty_column.community": "Локальная лента пуста. Напишите что-нибудь, чтобы разогреть народ!", "empty_column.community": "Локальная лента пуста. Напишите что-нибудь, чтобы разогреть народ!",
"empty_column.direct": "У вас пока нет личных сообщений. Как только вы отправите или получите сообщение, оно появится здесь.", "empty_column.direct": "У вас пока нет личных сообщений. Как только вы отправите или получите сообщение, оно появится здесь.",
"empty_column.domain_blocks": "Скрытых доменов пока нет.", "empty_column.domain_blocks": "Скрытых доменов пока нет.",
@ -293,9 +293,9 @@
"empty_column.favourites": "Никто ещё не добавил этот пост в «Избранное». Как только кто-то это сделает, это отобразится здесь.", "empty_column.favourites": "Никто ещё не добавил этот пост в «Избранное». Как только кто-то это сделает, это отобразится здесь.",
"empty_column.follow_requests": "Вам ещё не приходили запросы на подписку. Все новые запросы будут показаны здесь.", "empty_column.follow_requests": "Вам ещё не приходили запросы на подписку. Все новые запросы будут показаны здесь.",
"empty_column.followed_tags": "Вы еще не подписались ни на один хэштег. Когда вы это сделаете, они появятся здесь.", "empty_column.followed_tags": "Вы еще не подписались ни на один хэштег. Когда вы это сделаете, они появятся здесь.",
"empty_column.hashtag": "С этим хэштегом пока ещё ничего не постили.", "empty_column.hashtag": "С этим хэштегом пока ещё ничего не публиковали.",
"empty_column.home": "Ваша лента совсем пуста! Подписывайтесь на других, чтобы заполнить её.", "empty_column.home": "Ваша лента совсем пуста! Подписывайтесь на других, чтобы заполнить её.",
"empty_column.list": "В этом списке пока ничего нет.", "empty_column.list": "В этом списке пока ничего нет. Когда пользователи в списке публикуют новые посты, они появляются здесь.",
"empty_column.mutes": "Вы ещё никого не добавляли в список игнорируемых.", "empty_column.mutes": "Вы ещё никого не добавляли в список игнорируемых.",
"empty_column.notification_requests": "Здесь ничего нет! Когда вы получите новые уведомления, они здесь появятся согласно вашим настройкам.", "empty_column.notification_requests": "Здесь ничего нет! Когда вы получите новые уведомления, они здесь появятся согласно вашим настройкам.",
"empty_column.notifications": "У вас пока нет уведомлений. Взаимодействуйте с другими, чтобы завести разговор.", "empty_column.notifications": "У вас пока нет уведомлений. Взаимодействуйте с другими, чтобы завести разговор.",
@ -566,7 +566,7 @@
"notification.moderation_warning.action_sensitive": "С этого момента ваши сообщения будут помечены как деликатные.", "notification.moderation_warning.action_sensitive": "С этого момента ваши сообщения будут помечены как деликатные.",
"notification.moderation_warning.action_silence": "Ваша учётная запись была ограничена.", "notification.moderation_warning.action_silence": "Ваша учётная запись была ограничена.",
"notification.moderation_warning.action_suspend": "Действие вашей учётной записи приостановлено.", "notification.moderation_warning.action_suspend": "Действие вашей учётной записи приостановлено.",
"notification.own_poll": "Ваш опрос закончился", "notification.own_poll": "Ваш опрос завершился",
"notification.poll": "Голосование, в котором вы приняли участие, завершилось", "notification.poll": "Голосование, в котором вы приняли участие, завершилось",
"notification.reblog": "{name} продвинул(а) ваш пост", "notification.reblog": "{name} продвинул(а) ваш пост",
"notification.reblog.name_and_others_with_link": "{name} и ещё <a>{count, plural, one {# пользователь} few {# пользователя} other {# пользователей}}</a> продвинули ваш пост", "notification.reblog.name_and_others_with_link": "{name} и ещё <a>{count, plural, one {# пользователь} few {# пользователя} other {# пользователей}}</a> продвинули ваш пост",
@ -575,7 +575,7 @@
"notification.relationships_severance_event.domain_block": "Администратор {from} заблокировал {target} включая {followersCount} ваших подписчиков и {followingCount, plural, one {# аккаунт} few {# аккаунта} other {# аккаунтов}}, на которые вы подписаны.", "notification.relationships_severance_event.domain_block": "Администратор {from} заблокировал {target} включая {followersCount} ваших подписчиков и {followingCount, plural, one {# аккаунт} few {# аккаунта} other {# аккаунтов}}, на которые вы подписаны.",
"notification.relationships_severance_event.learn_more": "Узнать больше", "notification.relationships_severance_event.learn_more": "Узнать больше",
"notification.relationships_severance_event.user_domain_block": "Вы заблокировали {target} включая {followersCount} ваших подписчиков и {followingCount, plural, one {# аккаунт} few {# аккаунта} other {# аккаунтов}}, на которые вы подписаны.", "notification.relationships_severance_event.user_domain_block": "Вы заблокировали {target} включая {followersCount} ваших подписчиков и {followingCount, plural, one {# аккаунт} few {# аккаунта} other {# аккаунтов}}, на которые вы подписаны.",
"notification.status": "{name} только что запостил", "notification.status": "{name} опубликовал(а) новый пост",
"notification.update": "{name} изменил(а) пост", "notification.update": "{name} изменил(а) пост",
"notification_requests.accept": "Принять", "notification_requests.accept": "Принять",
"notification_requests.accept_multiple": "{count, plural, one {Принять # запрос…} few {Принять # запроса…} other {Принять # запросов…}}", "notification_requests.accept_multiple": "{count, plural, one {Принять # запрос…} few {Принять # запроса…} other {Принять # запросов…}}",
@ -722,7 +722,7 @@
"report.close": "Готово", "report.close": "Готово",
"report.comment.title": "Есть ли что-нибудь ещё, что нам стоит знать?", "report.comment.title": "Есть ли что-нибудь ещё, что нам стоит знать?",
"report.forward": "Переслать в {target}", "report.forward": "Переслать в {target}",
"report.forward_hint": "Эта учётная запись расположена на другом узле. Отправить туда анонимную копию вашей жалобы?", "report.forward_hint": "Эта учётная запись расположена на другом сервере. Отправить туда анонимную копию вашей жалобы?",
"report.mute": "Игнорировать", "report.mute": "Игнорировать",
"report.mute_explanation": "Вы не будете видеть их посты. Они по-прежнему могут подписываться на вас и видеть ваши посты, но не будут знать, что они в списке игнорируемых.", "report.mute_explanation": "Вы не будете видеть их посты. Они по-прежнему могут подписываться на вас и видеть ваши посты, но не будут знать, что они в списке игнорируемых.",
"report.next": "Далее", "report.next": "Далее",
@ -837,7 +837,7 @@
"status.replied_to": "Ответил(а) {name}", "status.replied_to": "Ответил(а) {name}",
"status.reply": "Ответить", "status.reply": "Ответить",
"status.replyAll": "Ответить всем", "status.replyAll": "Ответить всем",
"status.report": "Пожаловаться", "status.report": "Пожаловаться на @{name}",
"status.sensitive_warning": "Содержимое «деликатного характера»", "status.sensitive_warning": "Содержимое «деликатного характера»",
"status.share": "Поделиться", "status.share": "Поделиться",
"status.show_less_all": "Свернуть все спойлеры в ветке", "status.show_less_all": "Свернуть все спойлеры в ветке",
@ -875,6 +875,7 @@
"upload_form.drag_and_drop.on_drag_cancel": "Перетаскивание было отменено. Вложение медиа {item} было удалено.", "upload_form.drag_and_drop.on_drag_cancel": "Перетаскивание было отменено. Вложение медиа {item} было удалено.",
"upload_form.drag_and_drop.on_drag_end": "Медиа вложение {item} было удалено.", "upload_form.drag_and_drop.on_drag_end": "Медиа вложение {item} было удалено.",
"upload_form.drag_and_drop.on_drag_over": "Медиа вложение {item} было перемещено.", "upload_form.drag_and_drop.on_drag_over": "Медиа вложение {item} было перемещено.",
"upload_form.drag_and_drop.on_drag_start": "Загружается медиафайл {item}.",
"upload_form.edit": "Изменить", "upload_form.edit": "Изменить",
"upload_form.thumbnail": "Изменить обложку", "upload_form.thumbnail": "Изменить обложку",
"upload_form.video_description": "Опишите видео для людей с нарушением слуха или зрения", "upload_form.video_description": "Опишите видео для людей с нарушением слуха или зрения",

View file

@ -93,7 +93,7 @@
"annual_report.summary.archetype.pollster": "投票狂魔", "annual_report.summary.archetype.pollster": "投票狂魔",
"annual_report.summary.archetype.replier": "评论区原住民", "annual_report.summary.archetype.replier": "评论区原住民",
"annual_report.summary.followers.followers": "关注者", "annual_report.summary.followers.followers": "关注者",
"annual_report.summary.followers.total": "{count} 人", "annual_report.summary.followers.total": "{count} 人",
"annual_report.summary.here_it_is": "你的 {year} 年度回顾在此:", "annual_report.summary.here_it_is": "你的 {year} 年度回顾在此:",
"annual_report.summary.highlighted_post.by_favourites": "最受欢迎嘟嘟", "annual_report.summary.highlighted_post.by_favourites": "最受欢迎嘟嘟",
"annual_report.summary.highlighted_post.by_reblogs": "传播最广嘟嘟", "annual_report.summary.highlighted_post.by_reblogs": "传播最广嘟嘟",
@ -102,10 +102,10 @@
"annual_report.summary.most_used_app.most_used_app": "最常用的应用", "annual_report.summary.most_used_app.most_used_app": "最常用的应用",
"annual_report.summary.most_used_hashtag.most_used_hashtag": "最常用的话题", "annual_report.summary.most_used_hashtag.most_used_hashtag": "最常用的话题",
"annual_report.summary.most_used_hashtag.none": "无", "annual_report.summary.most_used_hashtag.none": "无",
"annual_report.summary.new_posts.new_posts": "嘟", "annual_report.summary.new_posts.new_posts": "新嘟嘟",
"annual_report.summary.percentile.text": "<topLabel>这使你跻身 Mastodon 用户的前</topLabel><percentage></percentage><bottomLabel></bottomLabel>", "annual_report.summary.percentile.text": "<topLabel>这使你跻身 Mastodon 用户的前</topLabel><percentage></percentage><bottomLabel></bottomLabel>",
"annual_report.summary.percentile.we_wont_tell_bernie": "我们打死也不会告诉扣税国王的(他知道的话要来收你发嘟税了)。", "annual_report.summary.percentile.we_wont_tell_bernie": "我们打死也不会告诉扣税国王的。",
"annual_report.summary.thanks": "谢你这一年和 Mastodon 上的大家一起嘟嘟!", "annual_report.summary.thanks": "谢你这一年和 Mastodon 上的大家一起嘟嘟!",
"attachments_list.unprocessed": "(未处理)", "attachments_list.unprocessed": "(未处理)",
"audio.hide": "隐藏音频", "audio.hide": "隐藏音频",
"block_modal.remote_users_caveat": "我们将要求服务器 {domain} 尊重你的决定。然而,我们无法保证对方一定遵从,因为某些服务器可能会以不同的方案处理屏蔽操作。公开嘟文仍然可能对未登录的用户可见。", "block_modal.remote_users_caveat": "我们将要求服务器 {domain} 尊重你的决定。然而,我们无法保证对方一定遵从,因为某些服务器可能会以不同的方案处理屏蔽操作。公开嘟文仍然可能对未登录的用户可见。",
@ -392,7 +392,7 @@
"home.hide_announcements": "隐藏公告", "home.hide_announcements": "隐藏公告",
"home.pending_critical_update.body": "请尽快更新你的 Mastodon 服务器!", "home.pending_critical_update.body": "请尽快更新你的 Mastodon 服务器!",
"home.pending_critical_update.link": "查看更新", "home.pending_critical_update.link": "查看更新",
"home.pending_critical_update.title": "紧急安全更新可用", "home.pending_critical_update.title": "紧急安全更新!",
"home.show_announcements": "显示公告", "home.show_announcements": "显示公告",
"ignore_notifications_modal.disclaimer": "Mastodon无法通知对方用户你忽略了他们的通知。忽略通知不会阻止消息本身的发送。", "ignore_notifications_modal.disclaimer": "Mastodon无法通知对方用户你忽略了他们的通知。忽略通知不会阻止消息本身的发送。",
"ignore_notifications_modal.filter_instead": "改为过滤", "ignore_notifications_modal.filter_instead": "改为过滤",

View file

@ -523,6 +523,13 @@ a.sparkline {
} }
} }
.notification-group--annual-report {
.notification-group__icon,
.notification-group__main .link-button {
color: var(--indigo-3);
}
}
@supports not selector(::-webkit-scrollbar) { @supports not selector(::-webkit-scrollbar) {
html { html {
scrollbar-color: rgba($action-button-color, 0.25) scrollbar-color: rgba($action-button-color, 0.25)
@ -530,13 +537,8 @@ a.sparkline {
} }
} }
.custom-scrollbars {
::-webkit-scrollbar-thumb { ::-webkit-scrollbar-thumb {
opacity: 0.25; opacity: 0.25;
} }
.notification-group--annual-report {
.notification-group__icon,
.notification-group__main .link-button {
color: var(--indigo-3);
}
} }

View file

@ -59,6 +59,7 @@ table {
} }
} }
.custom-scrollbars {
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 8px; width: 8px;
height: 8px; height: 8px;
@ -80,3 +81,4 @@ table {
::-webkit-scrollbar-corner { ::-webkit-scrollbar-corner {
background: transparent; background: transparent;
} }
}

View file

@ -131,6 +131,10 @@ module User::HasSettings
settings['reject_send_limited_to_suspects'] settings['reject_send_limited_to_suspects']
end end
def setting_system_scrollbars_ui
settings['web.use_system_scrollbars']
end
def setting_noindex def setting_noindex
settings['noindex'] settings['noindex']
end end

View file

@ -59,6 +59,7 @@ class UserSettings
setting :use_custom_css, default: false setting :use_custom_css, default: false
setting :content_font_size, default: 'medium', in: %w(medium large x_large xx_large) setting :content_font_size, default: 'medium', in: %w(medium large x_large xx_large)
setting :bookmark_category_needed, default: false setting :bookmark_category_needed, default: false
setting :use_system_scrollbars, default: false
setting :disable_swiping, default: false setting :disable_swiping, default: false
setting :disable_hover_cards, default: false setting :disable_hover_cards, default: false
setting :delete_modal, default: true setting :delete_modal, default: true

View file

@ -60,6 +60,7 @@
= ff.input :'web.disable_swiping', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_disable_swiping') = ff.input :'web.disable_swiping', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_disable_swiping')
= ff.input :'web.disable_hover_cards', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_disable_hover_cards') = ff.input :'web.disable_hover_cards', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_disable_hover_cards')
= ff.input :'web.use_system_font', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_system_font_ui') = ff.input :'web.use_system_font', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_system_font_ui')
= ff.input :'web.use_system_scrollbars', wrapper: :with_label, hint: I18n.t('simple_form.hints.defaults.setting_system_scrollbars_ui'), label: I18n.t('simple_form.labels.defaults.setting_system_scrollbars_ui')
.fields-group .fields-group
= ff.input :'web.content_font_size', = ff.input :'web.content_font_size',

View file

@ -0,0 +1 @@
nan:

View file

@ -61,6 +61,7 @@ bg:
demote: Понижаване demote: Понижаване
destroyed_msg: Данните на %{username} вече са на опашка за незабавно изтриване destroyed_msg: Данните на %{username} вече са на опашка за незабавно изтриване
disable: Замразяване disable: Замразяване
disable_sign_in_token_auth: Изключване на удостоверяването с маркер по е-поща
disable_two_factor_authentication: Изключване на 2факт. удостов. disable_two_factor_authentication: Изключване на 2факт. удостов.
disabled: Замразено disabled: Замразено
display_name: Име на показ display_name: Име на показ
@ -69,6 +70,7 @@ bg:
email: Имейл email: Имейл
email_status: Състояние на имейл email_status: Състояние на имейл
enable: Размразяване enable: Размразяване
enable_sign_in_token_auth: Задействане на удостоверяването с маркер по е-поща
enabled: Включено enabled: Включено
enabled_msg: Успешно размразяване на акаунта на %{username} enabled_msg: Успешно размразяване на акаунта на %{username}
followers: Последователи followers: Последователи
@ -133,6 +135,7 @@ bg:
resubscribe: Абониране пак resubscribe: Абониране пак
role: Роля role: Роля
search: Търсене search: Търсене
search_same_email_domain: Други потребители със същия домейн за е-поща
search_same_ip: Други потребители със същия IP search_same_ip: Други потребители със същия IP
security: Сигурност security: Сигурност
security_measures: security_measures:
@ -173,10 +176,12 @@ bg:
approve_appeal: Одобряване на обжалването approve_appeal: Одобряване на обжалването
approve_user: Одобряване на потребител approve_user: Одобряване на потребител
assigned_to_self_report: Назначете доклад assigned_to_self_report: Назначете доклад
change_email_user: Промяна на имейл за потребител
change_role_user: Промяна на роля за потребител change_role_user: Промяна на роля за потребител
confirm_user: Потвърждаване на потребител confirm_user: Потвърждаване на потребител
create_account_warning: Създаване на предупреждение create_account_warning: Създаване на предупреждение
create_announcement: Създаване на оповестяване create_announcement: Създаване на оповестяване
create_canonical_email_block: Сътворяване на блоккиране за е-поща
create_custom_emoji: Създаване на персонализирано емоджи create_custom_emoji: Създаване на персонализирано емоджи
create_domain_allow: Създаване на позволение за домейна create_domain_allow: Създаване на позволение за домейна
create_domain_block: Създаване на блокиране за домейна create_domain_block: Създаване на блокиране за домейна
@ -916,6 +921,8 @@ bg:
allow_provider: Позволяване на публикуващия allow_provider: Позволяване на публикуващия
confirm_allow: Наистина ли искате да позволите избраните връзки? confirm_allow: Наистина ли искате да позволите избраните връзки?
confirm_allow_provider: Наистина ли искате да позволите избраните доставчици? confirm_allow_provider: Наистина ли искате да позволите избраните доставчици?
confirm_disallow: Наистина ли искате да забраните избраните връзки?
confirm_disallow_provider: Наистина ли искате да забраните избраните доставчици?
description_html: Това са връзки, които в момента са много пъти споделяни от акаунти, чиито публикации сървърът ви вижда. Може да помогне на потребителите ви да разберат какво се случва по света. Никоя връзка няма да се показва публично, докато не одобрите публикуващия. Може още и да одобрявате или отхвърляте отделни връзки. description_html: Това са връзки, които в момента са много пъти споделяни от акаунти, чиито публикации сървърът ви вижда. Може да помогне на потребителите ви да разберат какво се случва по света. Никоя връзка няма да се показва публично, докато не одобрите публикуващия. Може още и да одобрявате или отхвърляте отделни връзки.
disallow: Забранявам връзката disallow: Забранявам връзката
disallow_provider: Забраняване на публикуващия disallow_provider: Забраняване на публикуващия
@ -941,13 +948,15 @@ bg:
allow_account: Позволяване на автора allow_account: Позволяване на автора
confirm_allow: Наистина ли искате да позволите избраните статуси? confirm_allow: Наистина ли искате да позволите избраните статуси?
confirm_allow_account: Наистина ли искате да позволите избраните акаунти? confirm_allow_account: Наистина ли искате да позволите избраните акаунти?
description_html: Това са публикации, за които сървърът ви знае, че са често споделяни или харесвани в момента. Това може да помогне на вашите нови и завръщащи се потребители да открият повече хора за следване. Никоя от публикациите няма да бъде показана публично, докато не одобрите автора и докато авторът не позволи акаунтът му да бъде предлган на другите. Също така можете да позволявате или отхвърляте отделни публикации. confirm_disallow: Наистина ли искате да забраните избраните статуси?
confirm_disallow_account: Наистина ли искате да забраните избраните акаунти?
description_html: Има публикации, за които сървърът ви знае, че в момента са често споделяни или любими. Биха помогнали на вашите нови и завръщащи се потребители да открият повече хора за последване. Никоя от публикациите няма да се показва публично, докато не одобрите автора и докато авторът не позволи акаунтът му да се предлага на другите. Може също така да позволявате или отхвърляте отделни публикации.
disallow: Забраняване на публикацията disallow: Забраняване на публикацията
disallow_account: Забрана на автора disallow_account: Забрана на автора
no_status_selected: Няма промяна, тъй като няма избрана нашумяла публикация no_status_selected: Няма промяна, тъй като няма избрана нашумяла публикация
not_discoverable: Авторът не е избрал да е откриваем not_discoverable: Авторът не е избрал да е откриваем
shared_by: shared_by:
one: Споделено или харесано веднъж one: Еднократно споделено или любимо
other: Споделено или харесано %{friendly_count} пъти other: Споделено или харесано %{friendly_count} пъти
title: Налагащи се публикации title: Налагащи се публикации
tags: tags:
@ -973,6 +982,7 @@ bg:
used_by_over_week: used_by_over_week:
one: Употребено от един човек през последната седмица one: Употребено от един човек през последната седмица
other: Използвано от %{count} души през последната седмица other: Използвано от %{count} души през последната седмица
title: Препоръки и насоки на развитие
trending: Изгряващи trending: Изгряващи
warning_presets: warning_presets:
add_new: Добавяне на ново add_new: Добавяне на ново
@ -1059,6 +1069,7 @@ bg:
application_mailer: application_mailer:
notification_preferences: Промяна на предпочитанията за е-поща notification_preferences: Промяна на предпочитанията за е-поща
salutation: "%{name}," salutation: "%{name},"
settings: 'Промяна на предпочитанията за имейл: %{link}'
unsubscribe: Стоп на абонамента unsubscribe: Стоп на абонамента
view: 'Преглед:' view: 'Преглед:'
view_profile: Преглед на профила view_profile: Преглед на профила
@ -1078,6 +1089,7 @@ bg:
hint_html: Просто още едно нещо! Трябва да потвърдим, че сте човек (това е с цел предпазване на нежелани съобщения!). Разгадайте капчата долу и щракнете на "Продължаване". hint_html: Просто още едно нещо! Трябва да потвърдим, че сте човек (това е с цел предпазване на нежелани съобщения!). Разгадайте капчата долу и щракнете на "Продължаване".
title: Проверка за сигурност title: Проверка за сигурност
confirmations: confirmations:
awaiting_review: Вашият адрес на е-поща е потвърден! Служителите на %{domain} сега разглеждат регистрацията ви. Ще получите е-писмо, ако одобрят акаунта ви!
awaiting_review_title: Вашата регистрация се преглежда awaiting_review_title: Вашата регистрация се преглежда
clicking_this_link: щракване на тази връзка clicking_this_link: щракване на тази връзка
login_link: влизане login_link: влизане
@ -1426,8 +1438,12 @@ bg:
confirmation_html: Наистина ли искате да спрете абонамента от получаването на %{type} за Mastodon в %{domain} към имейла си при %{email}? Може винаги пак да се абонирате от своите <a href="%{settings_path}">настройки за известяване по е-поща</a>. confirmation_html: Наистина ли искате да спрете абонамента от получаването на %{type} за Mastodon в %{domain} към имейла си при %{email}? Може винаги пак да се абонирате от своите <a href="%{settings_path}">настройки за известяване по е-поща</a>.
emails: emails:
notification_emails: notification_emails:
favourite: е-писма за известия с любими
follow: е-писма с известия за последване
follow_request: е-писма със заявки за следване
mention: е-писма с известия за споменаване mention: е-писма с известия за споменаване
reblog: е-писма с известия за подсилване reblog: е-писма с известия за подсилване
resubscribe_html: Ако погрешка сте спрели абонамента, то може пак да се абонирате от своите <a href="%{settings_path}">настройки за известия по е-поща</a>.
success_html: Повече няма да получавате %{type} за Mastodon на %{domain} към имейла си при %{email}. success_html: Повече няма да получавате %{type} за Mastodon на %{domain} към имейла си при %{email}.
title: Спиране на абонамента title: Спиране на абонамента
media_attachments: media_attachments:
@ -1482,8 +1498,8 @@ bg:
sign_up: sign_up:
subject: "%{name} се регистрира" subject: "%{name} се регистрира"
favourite: favourite:
body: 'Вашата публикация беше харесана от %{name}:' body: 'Ваша публикация е любима за %{name}:'
subject: "%{name} хареса вашата публикация" subject: "%{name} означи като любима ваша публикация"
title: Нова харесана публикация title: Нова харесана публикация
follow: follow:
body: "%{name} те последва!" body: "%{name} те последва!"
@ -1745,8 +1761,8 @@ bg:
keep_polls_hint: Не изтрива запитвания keep_polls_hint: Не изтрива запитвания
keep_self_bookmark: Запазване на публикации, добавени в отметки keep_self_bookmark: Запазване на публикации, добавени в отметки
keep_self_bookmark_hint: Не се изтриват ваши публикации, ако сте ги добавили към отметки keep_self_bookmark_hint: Не се изтриват ваши публикации, ако сте ги добавили към отметки
keep_self_fav: Задържане на публикации, които сте харесали keep_self_fav: Задържане на любимите ви публикации
keep_self_fav_hint: Не се изтриват публикации, които сте харесали keep_self_fav_hint: Да не се изтриват ваши публикации, ако са ви любими
min_age: min_age:
'1209600': 2 седмици '1209600': 2 седмици
'15778476': 6 месеца '15778476': 6 месеца
@ -1757,7 +1773,7 @@ bg:
'63113904': 2 години '63113904': 2 години
'7889238': 3 месеца '7889238': 3 месеца
min_age_label: Възрастов праг min_age_label: Възрастов праг
min_favs: Запазване на харесани публикации поне min_favs: Задържане поне на любимите публикации
min_favs_hint: Не се изтрива никоя от публикациите, които сте харесали поне толкова пъти. Оставете празно, за да изтриете публикациите независимо от броя харесвания min_favs_hint: Не се изтрива никоя от публикациите, които сте харесали поне толкова пъти. Оставете празно, за да изтриете публикациите независимо от броя харесвания
min_reblogs: Запазване на публикации с поне толкова раздувания min_reblogs: Запазване на публикации с поне толкова раздувания
min_reblogs_hint: Не се изтриват ваши публикации, споделени поне толкова пъти. Оставете празно, за да изтриете публикациите независимо от броя на техния раздувания min_reblogs_hint: Не се изтриват ваши публикации, споделени поне толкова пъти. Оставете празно, за да изтриете публикациите независимо от броя на техния раздувания

View file

@ -0,0 +1 @@
nan:

View file

@ -0,0 +1 @@
nan:

View file

@ -281,7 +281,7 @@ fi:
reject_appeal_html: "%{name} hylkäsi käyttäjän %{target} valituksen moderointipäätöksestä" reject_appeal_html: "%{name} hylkäsi käyttäjän %{target} valituksen moderointipäätöksestä"
reject_user_html: "%{name} hylkäsi käyttäjän %{target} rekisteröitymisen" reject_user_html: "%{name} hylkäsi käyttäjän %{target} rekisteröitymisen"
remove_avatar_user_html: "%{name} poisti käyttäjän %{target} profiilikuvan" remove_avatar_user_html: "%{name} poisti käyttäjän %{target} profiilikuvan"
reopen_report_html: "%{name} avasi uudelleen raportin %{target}" reopen_report_html: "%{name} avasi raportin %{target} uudelleen"
resend_user_html: "%{name} lähetti vahvistussähköpostiviestin uudelleen käyttäjälle %{target}" resend_user_html: "%{name} lähetti vahvistussähköpostiviestin uudelleen käyttäjälle %{target}"
reset_password_user_html: "%{name} palautti käyttäjän %{target} salasanan" reset_password_user_html: "%{name} palautti käyttäjän %{target} salasanan"
resolve_report_html: "%{name} ratkaisi raportin %{target}" resolve_report_html: "%{name} ratkaisi raportin %{target}"
@ -828,7 +828,7 @@ fi:
batch: batch:
add_to_report: Lisää raporttiin nro %{id} add_to_report: Lisää raporttiin nro %{id}
remove_from_report: Poista raportista remove_from_report: Poista raportista
report: Raportti report: Raportoi
contents: Sisältö contents: Sisältö
deleted: Poistettu deleted: Poistettu
favourites: Suosikit favourites: Suosikit

1
config/locales/nan.yml Normal file
View file

@ -0,0 +1 @@
nan:

View file

@ -854,6 +854,7 @@ ru:
back_to_account: Назад к учётной записи back_to_account: Назад к учётной записи
back_to_report: Вернуться к жалобе back_to_report: Вернуться к жалобе
batch: batch:
add_to_report: Добавить к жалобе №%{id}
remove_from_report: Убрать из жалобы remove_from_report: Убрать из жалобы
report: Пожаловаться report: Пожаловаться
contents: Содержание contents: Содержание

View file

@ -84,6 +84,7 @@ en:
setting_show_application: 投稿するのに使用したアプリが投稿の詳細ビューに表示されるようになります setting_show_application: 投稿するのに使用したアプリが投稿の詳細ビューに表示されるようになります
setting_stay_privacy: If you choose to enable this setting, please consider manually setting the visibility of the boost setting_stay_privacy: If you choose to enable this setting, please consider manually setting the visibility of the boost
setting_stop_emoji_reaction_streaming: Helps to save communication capacity. setting_stop_emoji_reaction_streaming: Helps to save communication capacity.
setting_system_scrollbars_ui: Applies only to desktop browsers based on Safari and Chrome
setting_use_blurhash: Gradients are based on the colors of the hidden visuals but obfuscate any details setting_use_blurhash: Gradients are based on the colors of the hidden visuals but obfuscate any details
setting_use_pending_items: Hide timeline updates behind a click instead of automatically scrolling the feed setting_use_pending_items: Hide timeline updates behind a click instead of automatically scrolling the feed
username: You can use letters, numbers, and underscores username: You can use letters, numbers, and underscores
@ -324,6 +325,7 @@ en:
setting_stay_privacy: Keep visibility after post setting_stay_privacy: Keep visibility after post
setting_stop_emoji_reaction_streaming: Disable emoji reaction streamings setting_stop_emoji_reaction_streaming: Disable emoji reaction streamings
setting_system_font_ui: Use system's default font setting_system_font_ui: Use system's default font
setting_system_scrollbars_ui: Use system's default scrollbar
setting_theme: Site theme setting_theme: Site theme
setting_translatable_private: Allow other users translation of your private posts setting_translatable_private: Allow other users translation of your private posts
setting_trends: Show today's trends setting_trends: Show today's trends

View file

@ -0,0 +1 @@
nan:

View file

@ -60,6 +60,7 @@ tr:
setting_display_media_default: Hassas olarak işaretlenmiş medyayı gizle setting_display_media_default: Hassas olarak işaretlenmiş medyayı gizle
setting_display_media_hide_all: Medyayı her zaman gizle setting_display_media_hide_all: Medyayı her zaman gizle
setting_display_media_show_all: Medyayı her zaman göster setting_display_media_show_all: Medyayı her zaman göster
setting_system_scrollbars_ui: Yalnızca Safari ve Chrome tabanlı masaüstü tarayıcılar için geçerlidir
setting_use_blurhash: Gradyenler gizli görsellerin renklerine dayanır, ancak detayları gizler setting_use_blurhash: Gradyenler gizli görsellerin renklerine dayanır, ancak detayları gizler
setting_use_pending_items: Akışı otomatik olarak kaydırmak yerine, zaman çizelgesi güncellemelerini tek bir tıklamayla gizleyin setting_use_pending_items: Akışı otomatik olarak kaydırmak yerine, zaman çizelgesi güncellemelerini tek bir tıklamayla gizleyin
username: Harfleri, sayıları veya alt çizgi kullanabilirsiniz username: Harfleri, sayıları veya alt çizgi kullanabilirsiniz
@ -223,6 +224,7 @@ tr:
setting_hide_network: Sosyal grafiğini gizle setting_hide_network: Sosyal grafiğini gizle
setting_reduce_motion: Animasyonlarda hareketi azalt setting_reduce_motion: Animasyonlarda hareketi azalt
setting_system_font_ui: Sistemin varsayılan yazı tipini kullan setting_system_font_ui: Sistemin varsayılan yazı tipini kullan
setting_system_scrollbars_ui: Sistemin varsayılan kaydırma çubuğunu kullan
setting_theme: Site teması setting_theme: Site teması
setting_trends: Bugünün gündemini göster setting_trends: Bugünün gündemini göster
setting_unfollow_modal: Birini takip etmeden önce onay iletişim kutusu göster setting_unfollow_modal: Birini takip etmeden önce onay iletişim kutusu göster

View file

@ -53,7 +53,7 @@
"@gamestdio/websocket": "^0.3.2", "@gamestdio/websocket": "^0.3.2",
"@github/webauthn-json": "^2.1.1", "@github/webauthn-json": "^2.1.1",
"@hello-pangea/dnd": "^17.0.0", "@hello-pangea/dnd": "^17.0.0",
"@rails/ujs": "7.1.402", "@rails/ujs": "7.1.500",
"@reduxjs/toolkit": "^2.0.1", "@reduxjs/toolkit": "^2.0.1",
"@svgr/webpack": "^5.5.0", "@svgr/webpack": "^5.5.0",
"arrow-key-navigation": "^1.2.0", "arrow-key-navigation": "^1.2.0",

View file

@ -2849,7 +2849,7 @@ __metadata:
"@gamestdio/websocket": "npm:^0.3.2" "@gamestdio/websocket": "npm:^0.3.2"
"@github/webauthn-json": "npm:^2.1.1" "@github/webauthn-json": "npm:^2.1.1"
"@hello-pangea/dnd": "npm:^17.0.0" "@hello-pangea/dnd": "npm:^17.0.0"
"@rails/ujs": "npm:7.1.402" "@rails/ujs": "npm:7.1.500"
"@reduxjs/toolkit": "npm:^2.0.1" "@reduxjs/toolkit": "npm:^2.0.1"
"@svgr/webpack": "npm:^5.5.0" "@svgr/webpack": "npm:^5.5.0"
"@testing-library/dom": "npm:^10.2.0" "@testing-library/dom": "npm:^10.2.0"
@ -3148,10 +3148,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@rails/ujs@npm:7.1.402": "@rails/ujs@npm:7.1.500":
version: 7.1.402 version: 7.1.500
resolution: "@rails/ujs@npm:7.1.402" resolution: "@rails/ujs@npm:7.1.500"
checksum: 10c0/ccab74b8013ed8a8ab8d7497d0fa510a6ec079725b5fcf679936d80c342940e462b60243ad2cb98128f29db5708a094e319767e8f33a18eb63ceb745de63d1e0 checksum: 10c0/365f9a3944454d64c83463de017d9be7064494d6376c1f4d8cbff38c0f278bac7d9ab85f19b31abb70f0e775f30b64ad682fd4545bc27b5d91baef3618642b9f
languageName: node languageName: node
linkType: hard linkType: hard