Merge remote-tracking branch 'origin/develop' into accounts-scss

environments/review-accounts-s-gnzsor/deployments/2278
Alex Gleason 2 years ago
commit d3f93d0325
No known key found for this signature in database
GPG Key ID: 7211D1F99744FBB7

@ -9,7 +9,7 @@
"declaration-block-no-redundant-longhand-properties": null, "declaration-block-no-redundant-longhand-properties": null,
"declaration-colon-newline-after": null, "declaration-colon-newline-after": null,
"declaration-empty-line-before": "never", "declaration-empty-line-before": "never",
"font-family-no-missing-generic-family-keyword": [true, { "ignoreFontFamilies": ["ForkAwesome", "Font Awesome 5 Free", "OpenDyslexic", "soapbox"] }], "font-family-no-missing-generic-family-keyword": [true, { "ignoreFontFamilies": ["ForkAwesome", "Font Awesome 5 Free"] }],
"max-line-length": null, "max-line-length": null,
"no-descending-specificity": null, "no-descending-specificity": null,
"no-duplicate-selectors": null, "no-duplicate-selectors": null,

@ -1 +1 @@
nodejs 18.12.1 nodejs 18.13.0

@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### Changed
- Posts: letterbox images to 19:6 again. - Posts: letterbox images to 19:6 again.
- Status Info: moved context (repost, pinned) to improve UX. - Status Info: moved context (repost, pinned) to improve UX.
- Posts: remove file icon from empty link previews.
### Fixed ### Fixed
- Layout: use accent color for "floating action button" (mobile compose button). - Layout: use accent color for "floating action button" (mobile compose button).
@ -29,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Modals: close modal when navigating to a different page. - Modals: close modal when navigating to a different page.
- Modals: fix "View context" button in media modal. - Modals: fix "View context" button in media modal.
- Posts: let unauthenticated users to translate posts if allowed by backend. - Posts: let unauthenticated users to translate posts if allowed by backend.
- Chats: fix jumpy scrollbar.
## [3.0.0] - 2022-12-25 ## [3.0.0] - 2022-12-25

@ -1,17 +0,0 @@
import loadPolyfills from './soapbox/load-polyfills';
// Load iframe event listener
require('./soapbox/iframe');
// @ts-ignore
require.context('./assets/images/', true);
// Load stylesheet
require('react-datepicker/dist/react-datepicker.css');
require('./styles/application.scss');
loadPolyfills().then(() => {
require('./soapbox/main').default();
}).catch(e => {
console.error(e);
});

@ -1,94 +0,0 @@
Copyright (c) 2019-07-29, Abbie Gonzalez (https://abbiecod.es|support@abbiecod.es),
with Reserved Font Name OpenDyslexic.
Copyright (c) 12/2012 - 2019
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

@ -10,10 +10,10 @@ import {
} from './importer'; } from './importer';
import type { AxiosError, CancelToken } from 'axios'; import type { AxiosError, CancelToken } from 'axios';
import type { History } from 'history';
import type { Map as ImmutableMap } from 'immutable'; import type { Map as ImmutableMap } from 'immutable';
import type { AppDispatch, RootState } from 'soapbox/store'; import type { AppDispatch, RootState } from 'soapbox/store';
import type { APIEntity, Status } from 'soapbox/types/entities'; import type { APIEntity, Status } from 'soapbox/types/entities';
import type { History } from 'soapbox/types/history';
const ACCOUNT_CREATE_REQUEST = 'ACCOUNT_CREATE_REQUEST'; const ACCOUNT_CREATE_REQUEST = 'ACCOUNT_CREATE_REQUEST';
const ACCOUNT_CREATE_SUCCESS = 'ACCOUNT_CREATE_SUCCESS'; const ACCOUNT_CREATE_SUCCESS = 'ACCOUNT_CREATE_SUCCESS';

@ -6,8 +6,8 @@ import { getFeatures } from 'soapbox/utils/features';
import api, { getLinks } from '../api'; import api, { getLinks } from '../api';
import type { History } from 'history';
import type { AppDispatch, RootState } from 'soapbox/store'; import type { AppDispatch, RootState } from 'soapbox/store';
import type { History } from 'soapbox/types/history';
const CHATS_FETCH_REQUEST = 'CHATS_FETCH_REQUEST'; const CHATS_FETCH_REQUEST = 'CHATS_FETCH_REQUEST';
const CHATS_FETCH_SUCCESS = 'CHATS_FETCH_SUCCESS'; const CHATS_FETCH_SUCCESS = 'CHATS_FETCH_SUCCESS';

@ -19,11 +19,11 @@ import { openModal, closeModal } from './modals';
import { getSettings } from './settings'; import { getSettings } from './settings';
import { createStatus } from './statuses'; import { createStatus } from './statuses';
import type { History } from 'history';
import type { Emoji } from 'soapbox/components/autosuggest-emoji'; import type { Emoji } from 'soapbox/components/autosuggest-emoji';
import type { AutoSuggestion } from 'soapbox/components/autosuggest-input'; import type { AutoSuggestion } from 'soapbox/components/autosuggest-input';
import type { AppDispatch, RootState } from 'soapbox/store'; import type { AppDispatch, RootState } from 'soapbox/store';
import type { Account, APIEntity, Status, Tag } from 'soapbox/types/entities'; import type { Account, APIEntity, Status, Tag } from 'soapbox/types/entities';
import type { History } from 'soapbox/types/history';
const { CancelToken, isCancel } = axios; const { CancelToken, isCancel } = axios;

@ -107,7 +107,10 @@ const updateNotificationsQueue = (notification: APIEntity, intlMessages: Record<
// Desktop notifications // Desktop notifications
try { try {
if (showAlert && !filtered) { // eslint-disable-next-line compat/compat
const isNotificationsEnabled = window.Notification?.permission === 'granted';
if (showAlert && !filtered && isNotificationsEnabled) {
const title = new IntlMessageFormat(intlMessages[`notification.${notification.type}`], intlLocale).format({ name: notification.account.display_name.length > 0 ? notification.account.display_name : notification.account.username }); const title = new IntlMessageFormat(intlMessages[`notification.${notification.type}`], intlLocale).format({ name: notification.account.display_name.length > 0 ? notification.account.display_name : notification.account.username });
const body = (notification.status && notification.status.spoiler_text.length > 0) ? notification.status.spoiler_text : unescapeHTML(notification.status ? notification.status.content : ''); const body = (notification.status && notification.status.spoiler_text.length > 0) ? notification.status.spoiler_text : unescapeHTML(notification.status ? notification.status.content : '');

@ -47,7 +47,6 @@ const defaultSettings = ImmutableMap({
autoloadMore: true, autoloadMore: true,
systemFont: false, systemFont: false,
dyslexicFont: false,
demetricator: false, demetricator: false,
isDeveloper: false, isDeveloper: false,

@ -1,50 +0,0 @@
'use strict';
import 'intl';
import 'intl/locale-data/jsonp/en';
import 'es6-symbol/implement';
// @ts-ignore: No types
import includes from 'array-includes';
// @ts-ignore: No types
import isNaN from 'is-nan';
import assign from 'object-assign';
// @ts-ignore: No types
import values from 'object.values';
import { decode as decodeBase64 } from './utils/base64';
if (!Array.prototype.includes) {
includes.shim();
}
if (!Object.assign) {
Object.assign = assign;
}
if (!Object.values) {
values.shim();
}
if (!Number.isNaN) {
Number.isNaN = isNaN;
}
if (!HTMLCanvasElement.prototype.toBlob) {
const BASE64_MARKER = ';base64,';
Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
value(callback: any, type = 'image/png', quality: any) {
const dataURL = this.toDataURL(type, quality);
let data;
if (dataURL.includes(BASE64_MARKER)) {
const [, base64] = dataURL.split(BASE64_MARKER);
data = decodeBase64(base64);
} else {
[, data] = dataURL.split(',');
}
callback(new Blob([data], { type }));
},
});
}

@ -1,8 +1,8 @@
import React from 'react'; import React from 'react';
import { defineMessages, useIntl } from 'react-intl'; import { defineMessages, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { unblockDomain } from 'soapbox/actions/domain-blocks'; import { unblockDomain } from 'soapbox/actions/domain-blocks';
import { useAppDispatch } from 'soapbox/hooks';
import { HStack, IconButton, Text } from './ui'; import { HStack, IconButton, Text } from './ui';
@ -16,7 +16,7 @@ interface IDomain {
} }
const Domain: React.FC<IDomain> = ({ domain }) => { const Domain: React.FC<IDomain> = ({ domain }) => {
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const intl = useIntl(); const intl = useIntl();
// const onBlockDomain = () => { // const onBlockDomain = () => {

@ -1,12 +1,11 @@
import classNames from 'clsx'; import classNames from 'clsx';
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import { usePopper } from 'react-popper'; import { usePopper } from 'react-popper';
import { useDispatch } from 'react-redux';
import { simpleEmojiReact } from 'soapbox/actions/emoji-reacts'; import { simpleEmojiReact } from 'soapbox/actions/emoji-reacts';
import { openModal } from 'soapbox/actions/modals'; import { openModal } from 'soapbox/actions/modals';
import { EmojiSelector } from 'soapbox/components/ui'; import { EmojiSelector } from 'soapbox/components/ui';
import { useAppSelector, useOwnAccount, useSoapboxConfig } from 'soapbox/hooks'; import { useAppDispatch, useAppSelector, useOwnAccount, useSoapboxConfig } from 'soapbox/hooks';
import { isUserTouching } from 'soapbox/is-mobile'; import { isUserTouching } from 'soapbox/is-mobile';
import { getReactForStatus } from 'soapbox/utils/emoji-reacts'; import { getReactForStatus } from 'soapbox/utils/emoji-reacts';
@ -17,7 +16,7 @@ interface IEmojiButtonWrapper {
/** Provides emoji reaction functionality to the underlying button component */ /** Provides emoji reaction functionality to the underlying button component */
const EmojiButtonWrapper: React.FC<IEmojiButtonWrapper> = ({ statusId, children }): JSX.Element | null => { const EmojiButtonWrapper: React.FC<IEmojiButtonWrapper> = ({ statusId, children }): JSX.Element | null => {
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const ownAccount = useOwnAccount(); const ownAccount = useOwnAccount();
const status = useAppSelector(state => state.statuses.get(statusId)); const status = useAppSelector(state => state.statuses.get(statusId));
const soapboxConfig = useSoapboxConfig(); const soapboxConfig = useSoapboxConfig();

@ -90,7 +90,7 @@ const IconButton: React.FC<IIconButton> = ({
type='button' type='button'
> >
<div> <div>
<Icon className={iconClassName} src={src} fixedWidth aria-hidden='true' /> <Icon className={iconClassName} src={src} aria-hidden='true' />
</div> </div>
{text && <span className='icon-button__text'>{text}</span>} {text && <span className='icon-button__text'>{text}</span>}
</button> </button>

@ -1,27 +1,28 @@
/** /**
* Icon: abstract icon class that can render icons from multiple sets. * Icon: abstact component to render SVG icons.
* @module soapbox/components/icon * @module soapbox/components/icon
* @see soapbox/components/fork_awesome_icon
* @see soapbox/components/svg_icon
*/ */
import classNames from 'clsx';
import React from 'react'; import React from 'react';
import InlineSVG from 'react-inlinesvg'; // eslint-disable-line no-restricted-imports
import ForkAwesomeIcon, { IForkAwesomeIcon } from './fork-awesome-icon'; export interface IIcon extends React.HTMLAttributes<HTMLDivElement> {
import SvgIcon, { ISvgIcon } from './svg-icon'; src: string,
id?: string,
alt?: string,
className?: string,
}
export type IIcon = IForkAwesomeIcon | ISvgIcon; const Icon: React.FC<IIcon> = ({ src, alt, className, ...rest }) => {
return (
const Icon: React.FC<IIcon> = (props) => { <div
if ((props as ISvgIcon).src) { className={classNames('svg-icon', className)}
const { src, ...rest } = (props as ISvgIcon); {...rest}
>
return <SvgIcon src={src} {...rest} />; <InlineSVG src={src} title={alt} loader={<></>} />
} else { </div>
const { id, fixedWidth, ...rest } = (props as IForkAwesomeIcon); );
return <ForkAwesomeIcon id={id} fixedWidth={fixedWidth} {...rest} />;
}
}; };
export default Icon; export default Icon;

@ -1,5 +1,5 @@
import classNames from 'clsx'; import classNames from 'clsx';
import React, { useState, useRef, useEffect } from 'react'; import React, { useState, useRef, useLayoutEffect } from 'react';
import Blurhash from 'soapbox/components/blurhash'; import Blurhash from 'soapbox/components/blurhash';
import Icon from 'soapbox/components/icon'; import Icon from 'soapbox/components/icon';
@ -533,7 +533,7 @@ const MediaGallery: React.FC<IMediaGallery> = (props) => {
/> />
)); ));
useEffect(() => { useLayoutEffect(() => {
if (node.current) { if (node.current) {
const { offsetWidth } = node.current; const { offsetWidth } = node.current;

@ -11,7 +11,6 @@ import { useAppDispatch, usePrevious } from 'soapbox/hooks';
import { queryClient } from 'soapbox/queries/client'; import { queryClient } from 'soapbox/queries/client';
import { IPolicy, PolicyKeys } from 'soapbox/queries/policies'; import { IPolicy, PolicyKeys } from 'soapbox/queries/policies';
import type { UnregisterCallback } from 'history';
import type { ModalType } from 'soapbox/features/ui/components/modal-root'; import type { ModalType } from 'soapbox/features/ui/components/modal-root';
import type { ReducerCompose } from 'soapbox/reducers/compose'; import type { ReducerCompose } from 'soapbox/reducers/compose';
import type { ReducerRecord as ReducerComposeEvent } from 'soapbox/reducers/compose-event'; import type { ReducerRecord as ReducerComposeEvent } from 'soapbox/reducers/compose-event';
@ -55,7 +54,7 @@ const ModalRoot: React.FC<IModalRoot> = ({ children, onCancel, onClose, type })
const ref = useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
const activeElement = useRef<HTMLDivElement | null>(revealed ? document.activeElement as HTMLDivElement | null : null); const activeElement = useRef<HTMLDivElement | null>(revealed ? document.activeElement as HTMLDivElement | null : null);
const modalHistoryKey = useRef<number>(); const modalHistoryKey = useRef<number>();
const unlistenHistory = useRef<UnregisterCallback>(); const unlistenHistory = useRef<ReturnType<typeof history.listen>>();
const prevChildren = usePrevious(children); const prevChildren = usePrevious(children);
const prevType = usePrevious(type); const prevType = usePrevious(type);

@ -24,13 +24,13 @@ type SavedScrollPosition = {
// NOTE: It's crucial to space lists with **padding** instead of margin! // NOTE: It's crucial to space lists with **padding** instead of margin!
// Pass an `itemClassName` like `pb-3`, NOT a `space-y-3` className // Pass an `itemClassName` like `pb-3`, NOT a `space-y-3` className
// https://virtuoso.dev/troubleshooting#list-does-not-scroll-to-the-bottom--items-jump-around // https://virtuoso.dev/troubleshooting#list-does-not-scroll-to-the-bottom--items-jump-around
const Item: Components<Context>['Item'] = ({ context, ...rest }) => ( const Item: Components<JSX.Element, Context>['Item'] = ({ context, ...rest }) => (
<div className={context?.itemClassName} {...rest} /> <div className={context?.itemClassName} {...rest} />
); );
/** Custom Virtuoso List component for the outer container. */ /** Custom Virtuoso List component for the outer container. */
// Ensure the className winds up here // Ensure the className winds up here
const List: Components<Context>['List'] = React.forwardRef((props, ref) => { const List: Components<JSX.Element, Context>['List'] = React.forwardRef((props, ref) => {
const { context, ...rest } = props; const { context, ...rest } = props;
return <div ref={ref} className={context?.listClassName} {...rest} />; return <div ref={ref} className={context?.listClassName} {...rest} />;
}); });

@ -2,7 +2,6 @@
import classNames from 'clsx'; import classNames from 'clsx';
import React from 'react'; import React from 'react';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import { useDispatch } from 'react-redux';
import { Link, NavLink } from 'react-router-dom'; import { Link, NavLink } from 'react-router-dom';
import { fetchOwnAccounts, logOut, switchAccount } from 'soapbox/actions/auth'; import { fetchOwnAccounts, logOut, switchAccount } from 'soapbox/actions/auth';
@ -11,7 +10,7 @@ import { closeSidebar } from 'soapbox/actions/sidebar';
import Account from 'soapbox/components/account'; import Account from 'soapbox/components/account';
import { Stack } from 'soapbox/components/ui'; import { Stack } from 'soapbox/components/ui';
import ProfileStats from 'soapbox/features/ui/components/profile-stats'; import ProfileStats from 'soapbox/features/ui/components/profile-stats';
import { useAppSelector, useFeatures } from 'soapbox/hooks'; import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks';
import { makeGetAccount, makeGetOtherAccounts } from 'soapbox/selectors'; import { makeGetAccount, makeGetOtherAccounts } from 'soapbox/selectors';
import { Divider, HStack, Icon, IconButton, Text } from './ui'; import { Divider, HStack, Icon, IconButton, Text } from './ui';
@ -81,7 +80,7 @@ const getOtherAccounts = makeGetOtherAccounts();
const SidebarMenu: React.FC = (): JSX.Element | null => { const SidebarMenu: React.FC = (): JSX.Element | null => {
const intl = useIntl(); const intl = useIntl();
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const features = useFeatures(); const features = useFeatures();
const getAccount = makeGetAccount(); const getAccount = makeGetAccount();

@ -26,7 +26,7 @@ interface IReadMoreButton {
const ReadMoreButton: React.FC<IReadMoreButton> = ({ onClick }) => ( const ReadMoreButton: React.FC<IReadMoreButton> = ({ onClick }) => (
<button className='flex items-center text-gray-900 dark:text-gray-300 border-0 bg-transparent p-0 pt-2 hover:underline active:underline' onClick={onClick}> <button className='flex items-center text-gray-900 dark:text-gray-300 border-0 bg-transparent p-0 pt-2 hover:underline active:underline' onClick={onClick}>
<FormattedMessage id='status.read_more' defaultMessage='Read more' /> <FormattedMessage id='status.read_more' defaultMessage='Read more' />
<Icon className='inline-block h-5 w-5' src={require('@tabler/icons/chevron-right.svg')} fixedWidth /> <Icon className='inline-block h-5 w-5' src={require('@tabler/icons/chevron-right.svg')} />
</button> </button>
); );

@ -1,29 +0,0 @@
/**
* SvgIcon: abstact component to render SVG icons.
* @module soapbox/components/svg_icon
* @see soapbox/components/icon
*/
import classNames from 'clsx';
import React from 'react';
import InlineSVG from 'react-inlinesvg'; // eslint-disable-line no-restricted-imports
export interface ISvgIcon extends React.HTMLAttributes<HTMLDivElement> {
src: string,
id?: string,
alt?: string,
className?: string,
}
const SvgIcon: React.FC<ISvgIcon> = ({ src, alt, className, ...rest }) => {
return (
<div
className={classNames('svg-icon', className)}
{...rest}
>
<InlineSVG src={src} title={alt} loader={<></>} />
</div>
);
};
export default SvgIcon;

@ -27,7 +27,7 @@ const FormGroup: React.FC<IFormGroup> = (props) => {
if (React.isValidElement(inputChildren[0])) { if (React.isValidElement(inputChildren[0])) {
firstChild = React.cloneElement( firstChild = React.cloneElement(
inputChildren[0], inputChildren[0],
{ id: formFieldId, hasError }, { id: formFieldId },
); );
} }
const isCheckboxFormGroup = firstChild?.type === Checkbox; const isCheckboxFormGroup = firstChild?.type === Checkbox;

@ -33,8 +33,6 @@ interface IInput extends Pick<React.InputHTMLAttributes<HTMLInputElement>, 'maxL
value?: string | number, value?: string | number,
/** Change event handler for the input. */ /** Change event handler for the input. */
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void, onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void,
/** Whether to display the input in red. */
hasError?: boolean,
/** An element to display as prefix to input. Cannot be used with icon. */ /** An element to display as prefix to input. Cannot be used with icon. */
prepend?: React.ReactElement, prepend?: React.ReactElement,
/** An element to display as suffix to input. Cannot be used with password type. */ /** An element to display as suffix to input. Cannot be used with password type. */
@ -48,7 +46,7 @@ const Input = React.forwardRef<HTMLInputElement, IInput>(
(props, ref) => { (props, ref) => {
const intl = useIntl(); const intl = useIntl();
const { type = 'text', icon, className, outerClassName, hasError, append, prepend, theme = 'normal', ...filteredProps } = props; const { type = 'text', icon, className, outerClassName, append, prepend, theme = 'normal', ...filteredProps } = props;
const [revealed, setRevealed] = React.useState(false); const [revealed, setRevealed] = React.useState(false);
@ -91,7 +89,6 @@ const Input = React.forwardRef<HTMLInputElement, IInput>(
'rounded-md bg-white dark:bg-gray-900 border-gray-400 dark:border-gray-800': theme === 'normal', 'rounded-md bg-white dark:bg-gray-900 border-gray-400 dark:border-gray-800': theme === 'normal',
'rounded-full bg-gray-200 border-gray-200 dark:bg-gray-800 dark:border-gray-800 focus:bg-white': theme === 'search', 'rounded-full bg-gray-200 border-gray-200 dark:bg-gray-800 dark:border-gray-800 focus:bg-white': theme === 'search',
'pr-7 rtl:pl-7 rtl:pr-3': isPassword || append, 'pr-7 rtl:pl-7 rtl:pr-3': isPassword || append,
'text-red-600 border-red-600': hasError,
'pl-8': typeof icon !== 'undefined', 'pl-8': typeof icon !== 'undefined',
'pl-16': typeof prepend !== 'undefined', 'pl-16': typeof prepend !== 'undefined',
}, className)} }, className)}

@ -271,7 +271,6 @@ const SoapboxHead: React.FC<ISoapboxHead> = ({ children }) => {
const bodyClass = classNames('bg-white dark:bg-gray-800 text-base h-full', { const bodyClass = classNames('bg-white dark:bg-gray-800 text-base h-full', {
'no-reduce-motion': !settings.get('reduceMotion'), 'no-reduce-motion': !settings.get('reduceMotion'),
'underline-links': settings.get('underlineLinks'), 'underline-links': settings.get('underlineLinks'),
'dyslexic': settings.get('dyslexicFont'),
'demetricator': settings.get('demetricator'), 'demetricator': settings.get('demetricator'),
}); });

@ -1,9 +1,8 @@
import React, { createContext, useContext, useEffect, useMemo, useState } from 'react'; import React, { createContext, useContext, useEffect, useMemo, useState } from 'react';
import { useDispatch } from 'react-redux';
import { useHistory, useParams } from 'react-router-dom'; import { useHistory, useParams } from 'react-router-dom';
import { toggleMainWindow } from 'soapbox/actions/chats'; import { toggleMainWindow } from 'soapbox/actions/chats';
import { useOwnAccount, useSettings } from 'soapbox/hooks'; import { useAppDispatch, useOwnAccount, useSettings } from 'soapbox/hooks';
import { IChat, useChat } from 'soapbox/queries/chats'; import { IChat, useChat } from 'soapbox/queries/chats';
type WindowState = 'open' | 'minimized'; type WindowState = 'open' | 'minimized';
@ -22,7 +21,7 @@ enum ChatWidgetScreens {
const ChatProvider: React.FC = ({ children }) => { const ChatProvider: React.FC = ({ children }) => {
const history = useHistory(); const history = useHistory();
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const settings = useSettings(); const settings = useSettings();
const account = useOwnAccount(); const account = useOwnAccount();

@ -1,4 +0,0 @@
'use strict';
import 'intersection-observer';
import 'requestidlecallback';

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useLayoutEffect, useRef, useState } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
@ -66,10 +66,7 @@ const AccountGallery = () => {
const hasMore = useAppSelector((state) => state.timelines.get(`account:${accountId}:media`)?.hasMore); const hasMore = useAppSelector((state) => state.timelines.get(`account:${accountId}:media`)?.hasMore);
const [width, setWidth] = useState(323); const [width, setWidth] = useState(323);
const node = useRef<HTMLDivElement>(null);
const handleRef = (c: HTMLDivElement) => {
if (c) setWidth(c.offsetWidth);
};
const handleScrollToBottom = () => { const handleScrollToBottom = () => {
if (hasMore) { if (hasMore) {
@ -99,6 +96,12 @@ const AccountGallery = () => {
} }
}; };
useLayoutEffect(() => {
if (node.current) {
setWidth(node.current.offsetWidth);
}
}, [node.current]);
useEffect(() => { useEffect(() => {
if (accountId && accountId !== -1) { if (accountId && accountId !== -1) {
dispatch(fetchAccount(accountId)); dispatch(fetchAccount(accountId));
@ -140,7 +143,7 @@ const AccountGallery = () => {
return ( return (
<Column label={`@${accountUsername}`} transparent withHeader={false}> <Column label={`@${accountUsername}`} transparent withHeader={false}>
<div role='feed' className='flex flex-wrap gap-2' ref={handleRef}> <div role='feed' className='flex flex-wrap gap-2' ref={node}>
{attachments.map((attachment, index) => attachment === null ? ( {attachments.map((attachment, index) => attachment === null ? (
<LoadMoreMedia key={'more:' + attachments.get(index + 1)?.id} maxId={index > 0 ? (attachments.get(index - 1)?.id || null) : null} onLoadMore={handleLoadMore} /> <LoadMoreMedia key={'more:' + attachments.get(index + 1)?.id} maxId={index > 0 ? (attachments.get(index - 1)?.id || null) : null} onLoadMore={handleLoadMore} />
) : ( ) : (

@ -1,12 +1,11 @@
import classNames from 'clsx'; import classNames from 'clsx';
import React from 'react'; import React from 'react';
import { defineMessages, useIntl } from 'react-intl'; import { defineMessages, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { fetchAliasesSuggestions, clearAliasesSuggestions, changeAliasesSuggestions } from 'soapbox/actions/aliases'; import { fetchAliasesSuggestions, clearAliasesSuggestions, changeAliasesSuggestions } from 'soapbox/actions/aliases';
import Icon from 'soapbox/components/icon'; import Icon from 'soapbox/components/icon';
import { Button } from 'soapbox/components/ui'; import { Button } from 'soapbox/components/ui';
import { useAppSelector } from 'soapbox/hooks'; import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
const messages = defineMessages({ const messages = defineMessages({
search: { id: 'aliases.search', defaultMessage: 'Search your old account' }, search: { id: 'aliases.search', defaultMessage: 'Search your old account' },
@ -14,7 +13,7 @@ const messages = defineMessages({
}); });
const Search: React.FC = () => { const Search: React.FC = () => {
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const intl = useIntl(); const intl = useIntl();
const value = useAppSelector(state => state.aliases.suggestions.value); const value = useAppSelector(state => state.aliases.suggestions.value);

@ -1,7 +1,7 @@
import classNames from 'clsx'; import classNames from 'clsx';
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import throttle from 'lodash/throttle'; import throttle from 'lodash/throttle';
import React, { useEffect, useRef, useState } from 'react'; import React, { useEffect, useLayoutEffect, useRef, useState } from 'react';
import { defineMessages, useIntl } from 'react-intl'; import { defineMessages, useIntl } from 'react-intl';
import Icon from 'soapbox/components/icon'; import Icon from 'soapbox/components/icon';
@ -397,7 +397,7 @@ const Audio: React.FC<IAudio> = (props) => {
const progress = Math.min((currentTime / getDuration()) * 100, 100); const progress = Math.min((currentTime / getDuration()) * 100, 100);
useEffect(() => { useLayoutEffect(() => {
if (player.current) { if (player.current) {
_setDimensions(); _setDimensions();
} }

@ -238,7 +238,6 @@ const RegistrationForm: React.FC<IRegistrationForm> = ({ inviteToken }) => {
pattern='^[a-zA-Z\d_-]+' pattern='^[a-zA-Z\d_-]+'
onChange={onUsernameChange} onChange={onUsernameChange}
value={params.get('username', '')} value={params.get('username', '')}
hasError={usernameUnavailable}
required required
/> />
</FormGroup> </FormGroup>

@ -1,13 +1,12 @@
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import React from 'react'; import React from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { fetchBlocks, expandBlocks } from 'soapbox/actions/blocks'; import { fetchBlocks, expandBlocks } from 'soapbox/actions/blocks';
import ScrollableList from 'soapbox/components/scrollable-list'; import ScrollableList from 'soapbox/components/scrollable-list';
import { Column, Spinner } from 'soapbox/components/ui'; import { Column, Spinner } from 'soapbox/components/ui';
import AccountContainer from 'soapbox/containers/account-container'; import AccountContainer from 'soapbox/containers/account-container';
import { useAppSelector } from 'soapbox/hooks'; import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
const messages = defineMessages({ const messages = defineMessages({
heading: { id: 'column.blocks', defaultMessage: 'Blocked users' }, heading: { id: 'column.blocks', defaultMessage: 'Blocked users' },
@ -18,7 +17,7 @@ const handleLoadMore = debounce((dispatch) => {
}, 300, { leading: true }); }, 300, { leading: true });
const Blocks: React.FC = () => { const Blocks: React.FC = () => {
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const intl = useIntl(); const intl = useIntl();
const accountIds = useAppSelector((state) => state.user_lists.blocks.items); const accountIds = useAppSelector((state) => state.user_lists.blocks.items);

@ -66,6 +66,21 @@ const List: Components['List'] = React.forwardRef((props, ref) => {
return <div ref={ref} {...rest} className='mb-2' />; return <div ref={ref} {...rest} className='mb-2' />;
}); });
const Scroller: Components['Scroller'] = React.forwardRef((props, ref) => {
const { style, context, ...rest } = props;
return (
<div
{...rest}
ref={ref}
style={{
...style,
scrollbarGutter: 'stable',
}}
/>
);
});
interface IChatMessageList { interface IChatMessageList {
/** Chat the messages are being rendered from. */ /** Chat the messages are being rendered from. */
chat: IChat, chat: IChat,
@ -472,6 +487,7 @@ const ChatMessageList: React.FC<IChatMessageList> = ({ chat }) => {
}} }}
components={{ components={{
List, List,
Scroller,
Header: () => { Header: () => {
if (hasNextPage || isFetchingNextPage) { if (hasNextPage || isFetchingNextPage) {
return <Spinner withText={false} />; return <Spinner withText={false} />;

@ -6,7 +6,7 @@ import { __stub } from 'soapbox/api';
import { normalizeAccount } from 'soapbox/normalizers'; import { normalizeAccount } from 'soapbox/normalizers';
import { ReducerAccount } from 'soapbox/reducers/accounts'; import { ReducerAccount } from 'soapbox/reducers/accounts';
import { render, screen } from '../../../../../jest/test-helpers'; import { render, screen, waitFor } from '../../../../../jest/test-helpers';
import ChatPage from '../chat-page'; import ChatPage from '../chat-page';
describe('<ChatPage />', () => { describe('<ChatPage />', () => {
@ -48,9 +48,12 @@ describe('<ChatPage />', () => {
await userEvent.click(screen.getByTestId('button')); await userEvent.click(screen.getByTestId('button'));
expect(screen.getByTestId('chat-page')).toBeInTheDocument(); expect(screen.getByTestId('chat-page')).toBeInTheDocument();
await waitFor(() => {
expect(screen.getByTestId('toast')).toHaveTextContent('Chat Settings updated successfully'); expect(screen.getByTestId('toast')).toHaveTextContent('Chat Settings updated successfully');
}); });
}); });
});
describe('when the API returns an error', () => { describe('when the API returns an error', () => {
beforeEach(() => { beforeEach(() => {
@ -77,8 +80,11 @@ describe('<ChatPage />', () => {
it('renders the Chats', async () => { it('renders the Chats', async () => {
render(<ChatPage />, undefined, store); render(<ChatPage />, undefined, store);
await userEvent.click(screen.getByTestId('button')); await userEvent.click(screen.getByTestId('button'));
await waitFor(() => {
expect(screen.getByTestId('toast')).toHaveTextContent('Chat Settings failed to update.'); expect(screen.getByTestId('toast')).toHaveTextContent('Chat Settings failed to update.');
}); });
}); });
}); });
});
}); });

@ -1,5 +1,5 @@
import classNames from 'clsx'; import classNames from 'clsx';
import React, { useEffect, useRef, useState } from 'react'; import React, { useEffect, useLayoutEffect, useRef, useState } from 'react';
import { matchPath, Route, Switch, useHistory } from 'react-router-dom'; import { matchPath, Route, Switch, useHistory } from 'react-router-dom';
import { Stack } from 'soapbox/components/ui'; import { Stack } from 'soapbox/components/ui';
@ -44,7 +44,7 @@ const ChatPage: React.FC<IChatPage> = ({ chatId }) => {
setHeight(fullHeight - top + offset); setHeight(fullHeight - top + offset);
}; };
useEffect(() => { useLayoutEffect(() => {
calculateHeight(); calculateHeight();
}, [containerRef.current]); }, [containerRef.current]);

@ -238,7 +238,7 @@ const ChatPageMain = () => {
<div className='h-full overflow-hidden'> <div className='h-full overflow-hidden'>
<Chat <Chat
className='h-full overflow-hidden' className='h-full'
chat={chat} chat={chat}
inputRef={inputRef} inputRef={inputRef}
/> />

@ -1,9 +1,7 @@
import classNames from 'clsx'; import classNames from 'clsx';
import { Map as ImmutableMap } from 'immutable';
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import { defineMessages, useIntl } from 'react-intl'; import { defineMessages, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { import {
@ -17,7 +15,8 @@ import {
import AutosuggestAccountInput from 'soapbox/components/autosuggest-account-input'; import AutosuggestAccountInput from 'soapbox/components/autosuggest-account-input';
import { Input } from 'soapbox/components/ui'; import { Input } from 'soapbox/components/ui';
import SvgIcon from 'soapbox/components/ui/icon/svg-icon'; import SvgIcon from 'soapbox/components/ui/icon/svg-icon';
import { useAppSelector } from 'soapbox/hooks'; import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
import { AppDispatch, RootState } from 'soapbox/store';
const messages = defineMessages({ const messages = defineMessages({
placeholder: { id: 'search.placeholder', defaultMessage: 'Search' }, placeholder: { id: 'search.placeholder', defaultMessage: 'Search' },
@ -25,7 +24,7 @@ const messages = defineMessages({
}); });
function redirectToAccount(accountId: string, routerHistory: any) { function redirectToAccount(accountId: string, routerHistory: any) {
return (_dispatch: any, getState: () => ImmutableMap<string, any>) => { return (_dispatch: AppDispatch, getState: () => RootState) => {
const acct = getState().getIn(['accounts', accountId, 'acct']); const acct = getState().getIn(['accounts', accountId, 'acct']);
if (acct && routerHistory) { if (acct && routerHistory) {
@ -49,7 +48,7 @@ const Search = (props: ISearch) => {
openInRoute = false, openInRoute = false,
} = props; } = props;
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const history = useHistory(); const history = useHistory();
const intl = useIntl(); const intl = useIntl();

@ -1,9 +1,9 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { changeSettingImmediate } from 'soapbox/actions/settings'; import { changeSettingImmediate } from 'soapbox/actions/settings';
import { Column, Button, Form, FormActions, FormGroup, Input, Text } from 'soapbox/components/ui'; import { Column, Button, Form, FormActions, FormGroup, Input, Text } from 'soapbox/components/ui';
import { useAppDispatch } from 'soapbox/hooks';
import toast from 'soapbox/toast'; import toast from 'soapbox/toast';
const messages = defineMessages({ const messages = defineMessages({
@ -15,7 +15,7 @@ const messages = defineMessages({
}); });
const DevelopersChallenge = () => { const DevelopersChallenge = () => {
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const intl = useIntl(); const intl = useIntl();
const [answer, setAnswer] = useState(''); const [answer, setAnswer] = useState('');

@ -1,11 +1,11 @@
import React from 'react'; import React from 'react';
import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { Link, useHistory } from 'react-router-dom'; import { Link, useHistory } from 'react-router-dom';
import { changeSettingImmediate } from 'soapbox/actions/settings'; import { changeSettingImmediate } from 'soapbox/actions/settings';
import { Column, Text } from 'soapbox/components/ui'; import { Column, Text } from 'soapbox/components/ui';
import SvgIcon from 'soapbox/components/ui/icon/svg-icon'; import SvgIcon from 'soapbox/components/ui/icon/svg-icon';
import { useAppDispatch } from 'soapbox/hooks';
import toast from 'soapbox/toast'; import toast from 'soapbox/toast';
import sourceCode from 'soapbox/utils/code'; import sourceCode from 'soapbox/utils/code';
@ -31,7 +31,7 @@ const DashWidget: React.FC<IDashWidget> = ({ to, onClick, children }) => {
}; };
const Developers: React.FC = () => { const Developers: React.FC = () => {
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const history = useHistory(); const history = useHistory();
const intl = useIntl(); const intl = useIntl();

@ -134,12 +134,6 @@ const SettingsStore: React.FC = () => {
<SettingToggle settings={settings} settingPath={['systemFont']} onChange={onToggleChange} /> <SettingToggle settings={settings} settingPath={['systemFont']} onChange={onToggleChange} />
</ListItem> </ListItem>
<div className='dyslexic'>
<ListItem label={<FormattedMessage id='preferences.fields.dyslexic_font_label' defaultMessage='Dyslexic mode' />}>
<SettingToggle settings={settings} settingPath={['dyslexicFont']} onChange={onToggleChange} />
</ListItem>
</div>
<ListItem <ListItem
label={<FormattedMessage id='preferences.fields.demetricator_label' defaultMessage='Use Demetricator' />} label={<FormattedMessage id='preferences.fields.demetricator_label' defaultMessage='Use Demetricator' />}
hint={<FormattedMessage id='preferences.hints.demetricator' defaultMessage='Decrease social media anxiety by hiding all numbers from the site.' />} hint={<FormattedMessage id='preferences.hints.demetricator' defaultMessage='Decrease social media anxiety by hiding all numbers from the site.' />}

@ -1,13 +1,12 @@
import classNames from 'clsx'; import classNames from 'clsx';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { defineMessages, useIntl } from 'react-intl'; import { defineMessages, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
import { fetchDirectory, expandDirectory } from 'soapbox/actions/directory'; import { fetchDirectory, expandDirectory } from 'soapbox/actions/directory';
import LoadMore from 'soapbox/components/load-more'; import LoadMore from 'soapbox/components/load-more';
import { Column, RadioButton, Stack, Text } from 'soapbox/components/ui'; import { Column, RadioButton, Stack, Text } from 'soapbox/components/ui';
import { useAppSelector, useFeatures, useInstance } from 'soapbox/hooks'; import { useAppDispatch, useAppSelector, useFeatures, useInstance } from 'soapbox/hooks';
import AccountCard from './components/account-card'; import AccountCard from './components/account-card';
@ -21,7 +20,7 @@ const messages = defineMessages({
const Directory = () => { const Directory = () => {
const intl = useIntl(); const intl = useIntl();
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const { search } = useLocation(); const { search } = useLocation();
const params = new URLSearchParams(search); const params = new URLSearchParams(search);
const instance = useInstance(); const instance = useInstance();

@ -1,13 +1,12 @@
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import React from 'react'; import React from 'react';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import { useDispatch } from 'react-redux';
import { fetchDomainBlocks, expandDomainBlocks } from 'soapbox/actions/domain-blocks'; import { fetchDomainBlocks, expandDomainBlocks } from 'soapbox/actions/domain-blocks';
import Domain from 'soapbox/components/domain'; import Domain from 'soapbox/components/domain';
import ScrollableList from 'soapbox/components/scrollable-list'; import ScrollableList from 'soapbox/components/scrollable-list';
import { Column, Spinner } from 'soapbox/components/ui'; import { Column, Spinner } from 'soapbox/components/ui';
import { useAppSelector } from 'soapbox/hooks'; import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
const messages = defineMessages({ const messages = defineMessages({
heading: { id: 'column.domain_blocks', defaultMessage: 'Hidden domains' }, heading: { id: 'column.domain_blocks', defaultMessage: 'Hidden domains' },
@ -19,7 +18,7 @@ const handleLoadMore = debounce((dispatch) => {
}, 300, { leading: true }); }, 300, { leading: true });
const DomainBlocks: React.FC = () => { const DomainBlocks: React.FC = () => {
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const intl = useIntl(); const intl = useIntl();
const domains = useAppSelector((state) => state.domain_lists.blocks.items); const domains = useAppSelector((state) => state.domain_lists.blocks.items);

@ -78,6 +78,9 @@ describe('<FeedCarousel />', () => {
expect(screen.getAllByTestId('carousel-item-avatar')[0]).toHaveClass('ring-primary-600'); expect(screen.getAllByTestId('carousel-item-avatar')[0]).toHaveClass('ring-primary-600');
}); });
// HACK: wait for state change
await new Promise((r) => setTimeout(r, 0));
// Marked as seen, not selected // Marked as seen, not selected
await userEvent.click(screen.getAllByTestId('carousel-item-avatar')[0]); await userEvent.click(screen.getAllByTestId('carousel-item-avatar')[0]);
await waitFor(() => { await waitFor(() => {

@ -1,11 +1,10 @@
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import { defineMessages, useIntl } from 'react-intl'; import { defineMessages, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { authorizeFollowRequest, rejectFollowRequest } from 'soapbox/actions/accounts'; import { authorizeFollowRequest, rejectFollowRequest } from 'soapbox/actions/accounts';
import Account from 'soapbox/components/account'; import Account from 'soapbox/components/account';
import { Button, HStack } from 'soapbox/components/ui'; import { Button, HStack } from 'soapbox/components/ui';
import { useAppSelector } from 'soapbox/hooks'; import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
import { makeGetAccount } from 'soapbox/selectors'; import { makeGetAccount } from 'soapbox/selectors';
const messages = defineMessages({ const messages = defineMessages({
@ -19,7 +18,7 @@ interface IAccountAuthorize {
const AccountAuthorize: React.FC<IAccountAuthorize> = ({ id }) => { const AccountAuthorize: React.FC<IAccountAuthorize> = ({ id }) => {
const intl = useIntl(); const intl = useIntl();
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const getAccount = useCallback(makeGetAccount(), []); const getAccount = useCallback(makeGetAccount(), []);

@ -1,12 +1,11 @@
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import React from 'react'; import React from 'react';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import { useDispatch } from 'react-redux';
import { fetchFollowRequests, expandFollowRequests } from 'soapbox/actions/accounts'; import { fetchFollowRequests, expandFollowRequests } from 'soapbox/actions/accounts';
import ScrollableList from 'soapbox/components/scrollable-list'; import ScrollableList from 'soapbox/components/scrollable-list';
import { Column, Spinner } from 'soapbox/components/ui'; import { Column, Spinner } from 'soapbox/components/ui';
import { useAppSelector } from 'soapbox/hooks'; import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
import AccountAuthorize from './components/account-authorize'; import AccountAuthorize from './components/account-authorize';
@ -19,7 +18,7 @@ const handleLoadMore = debounce((dispatch) => {
}, 300, { leading: true }); }, 300, { leading: true });
const FollowRequests: React.FC = () => { const FollowRequests: React.FC = () => {
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const intl = useIntl(); const intl = useIntl();
const accountIds = useAppSelector((state) => state.user_lists.follow_requests.items); const accountIds = useAppSelector((state) => state.user_lists.follow_requests.items);

@ -37,7 +37,7 @@ const List: React.FC<IList> = ({ listId }) => {
return ( return (
<div className='flex items-center gap-1.5 px-2 py-4 text-black dark:text-white'> <div className='flex items-center gap-1.5 px-2 py-4 text-black dark:text-white'>
<Icon src={require('@tabler/icons/list.svg')} fixedWidth /> <Icon src={require('@tabler/icons/list.svg')} />
<span className='flex-grow'> <span className='flex-grow'>
{list.title} {list.title}
</span> </span>

@ -1,10 +1,9 @@
import React from 'react'; import React from 'react';
import { defineMessages, useIntl } from 'react-intl'; import { defineMessages, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { changeListEditorTitle, submitListEditor } from 'soapbox/actions/lists'; import { changeListEditorTitle, submitListEditor } from 'soapbox/actions/lists';
import { Button, Form, HStack, Input } from 'soapbox/components/ui'; import { Button, Form, HStack, Input } from 'soapbox/components/ui';
import { useAppSelector } from 'soapbox/hooks'; import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
const messages = defineMessages({ const messages = defineMessages({
label: { id: 'lists.new.title_placeholder', defaultMessage: 'New list title' }, label: { id: 'lists.new.title_placeholder', defaultMessage: 'New list title' },
@ -13,7 +12,7 @@ const messages = defineMessages({
}); });
const NewListForm: React.FC = () => { const NewListForm: React.FC = () => {
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const intl = useIntl(); const intl = useIntl();
const value = useAppSelector((state) => state.listEditor.get('title')); const value = useAppSelector((state) => state.listEditor.get('title'));

@ -1,6 +1,5 @@
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import { useDispatch } from 'react-redux';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
@ -9,7 +8,7 @@ import { openModal } from 'soapbox/actions/modals';
import Icon from 'soapbox/components/icon'; import Icon from 'soapbox/components/icon';
import ScrollableList from 'soapbox/components/scrollable-list'; import ScrollableList from 'soapbox/components/scrollable-list';
import { Column, IconButton, Spinner } from 'soapbox/components/ui'; import { Column, IconButton, Spinner } from 'soapbox/components/ui';
import { useAppSelector } from 'soapbox/hooks'; import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
import NewListForm from './components/new-list-form'; import NewListForm from './components/new-list-form';
@ -35,7 +34,7 @@ const getOrderedLists = createSelector([(state: RootState) => state.lists], list
}); });
const Lists: React.FC = () => { const Lists: React.FC = () => {
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const intl = useIntl(); const intl = useIntl();
const lists = useAppSelector((state) => getOrderedLists(state)); const lists = useAppSelector((state) => getOrderedLists(state));
@ -85,7 +84,7 @@ const Lists: React.FC = () => {
> >
{lists.map((list: any) => ( {lists.map((list: any) => (
<Link key={list.id} to={`/list/${list.id}`} className='flex items-center gap-1.5 p-2 text-gray-900 dark:text-gray-100 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg'> <Link key={list.id} to={`/list/${list.id}`} className='flex items-center gap-1.5 p-2 text-gray-900 dark:text-gray-100 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg'>
<Icon src={require('@tabler/icons/list.svg')} fixedWidth /> <Icon src={require('@tabler/icons/list.svg')} />
<span className='flex-grow'> <span className='flex-grow'>
{list.title} {list.title}
</span> </span>

@ -1,13 +1,12 @@
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import React from 'react'; import React from 'react';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import { useDispatch } from 'react-redux';
import { fetchMutes, expandMutes } from 'soapbox/actions/mutes'; import { fetchMutes, expandMutes } from 'soapbox/actions/mutes';
import ScrollableList from 'soapbox/components/scrollable-list'; import ScrollableList from 'soapbox/components/scrollable-list';
import { Column, Spinner } from 'soapbox/components/ui'; import { Column, Spinner } from 'soapbox/components/ui';
import AccountContainer from 'soapbox/containers/account-container'; import AccountContainer from 'soapbox/containers/account-container';
import { useAppSelector } from 'soapbox/hooks'; import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
const messages = defineMessages({ const messages = defineMessages({
heading: { id: 'column.mutes', defaultMessage: 'Muted users' }, heading: { id: 'column.mutes', defaultMessage: 'Muted users' },
@ -18,7 +17,7 @@ const handleLoadMore = debounce((dispatch) => {
}, 300, { leading: true }); }, 300, { leading: true });
const Mutes: React.FC = () => { const Mutes: React.FC = () => {
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const intl = useIntl(); const intl = useIntl();
const accountIds = useAppSelector((state) => state.user_lists.mutes.items); const accountIds = useAppSelector((state) => state.user_lists.mutes.items);

@ -1,12 +1,11 @@
import classNames from 'clsx'; import classNames from 'clsx';
import React from 'react'; import React from 'react';
import { useDispatch } from 'react-redux';
import ReactSwipeableViews from 'react-swipeable-views'; import ReactSwipeableViews from 'react-swipeable-views';
import { endOnboarding } from 'soapbox/actions/onboarding'; import { endOnboarding } from 'soapbox/actions/onboarding';
import LandingGradient from 'soapbox/components/landing-gradient'; import LandingGradient from 'soapbox/components/landing-gradient';
import { HStack } from 'soapbox/components/ui'; import { HStack } from 'soapbox/components/ui';
import { useFeatures } from 'soapbox/hooks'; import { useAppDispatch, useFeatures } from 'soapbox/hooks';
import AvatarSelectionStep from './steps/avatar-selection-step'; import AvatarSelectionStep from './steps/avatar-selection-step';
import BioStep from './steps/bio-step'; import BioStep from './steps/bio-step';
@ -17,7 +16,7 @@ import FediverseStep from './steps/fediverse-step';
import SuggestedAccountsStep from './steps/suggested-accounts-step'; import SuggestedAccountsStep from './steps/suggested-accounts-step';
const OnboardingWizard = () => { const OnboardingWizard = () => {
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const features = useFeatures(); const features = useFeatures();
const [currentStep, setCurrentStep] = React.useState<number>(0); const [currentStep, setCurrentStep] = React.useState<number>(0);

@ -1,11 +1,10 @@
import classNames from 'clsx'; import classNames from 'clsx';
import React from 'react'; import React from 'react';
import { defineMessages, FormattedMessage } from 'react-intl'; import { defineMessages, FormattedMessage } from 'react-intl';
import { useDispatch } from 'react-redux';
import { patchMe } from 'soapbox/actions/me'; import { patchMe } from 'soapbox/actions/me';
import { Avatar, Button, Card, CardBody, Icon, Spinner, Stack, Text } from 'soapbox/components/ui'; import { Avatar, Button, Card, CardBody, Icon, Spinner, Stack, Text } from 'soapbox/components/ui';
import { useOwnAccount } from 'soapbox/hooks'; import { useAppDispatch, useOwnAccount } from 'soapbox/hooks';
import toast from 'soapbox/toast'; import toast from 'soapbox/toast';
import { isDefaultAvatar } from 'soapbox/utils/accounts'; import { isDefaultAvatar } from 'soapbox/utils/accounts';
import resizeImage from 'soapbox/utils/resize-image'; import resizeImage from 'soapbox/utils/resize-image';
@ -17,7 +16,7 @@ const messages = defineMessages({
}); });
const AvatarSelectionStep = ({ onNext }: { onNext: () => void }) => { const AvatarSelectionStep = ({ onNext }: { onNext: () => void }) => {
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const account = useOwnAccount(); const account = useOwnAccount();
const fileInput = React.useRef<HTMLInputElement>(null); const fileInput = React.useRef<HTMLInputElement>(null);

@ -1,10 +1,9 @@
import React from 'react'; import React from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { patchMe } from 'soapbox/actions/me'; import { patchMe } from 'soapbox/actions/me';
import { Button, Card, CardBody, FormGroup, Stack, Text, Textarea } from 'soapbox/components/ui'; import { Button, Card, CardBody, FormGroup, Stack, Text, Textarea } from 'soapbox/components/ui';
import { useOwnAccount } from 'soapbox/hooks'; import { useAppDispatch, useOwnAccount } from 'soapbox/hooks';
import toast from 'soapbox/toast'; import toast from 'soapbox/toast';
import type { AxiosError } from 'axios'; import type { AxiosError } from 'axios';
@ -16,7 +15,7 @@ const messages = defineMessages({
const BioStep = ({ onNext }: { onNext: () => void }) => { const BioStep = ({ onNext }: { onNext: () => void }) => {
const intl = useIntl(); const intl = useIntl();
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const account = useOwnAccount(); const account = useOwnAccount();
const [value, setValue] = React.useState<string>(account?.source.get('note') || ''); const [value, setValue] = React.useState<string>(account?.source.get('note') || '');

@ -1,12 +1,11 @@
import classNames from 'clsx'; import classNames from 'clsx';
import React from 'react'; import React from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { patchMe } from 'soapbox/actions/me'; import { patchMe } from 'soapbox/actions/me';
import StillImage from 'soapbox/components/still-image'; import StillImage from 'soapbox/components/still-image';
import { Avatar, Button, Card, CardBody, Icon, Spinner, Stack, Text } from 'soapbox/components/ui'; import { Avatar, Button, Card, CardBody, Icon, Spinner, Stack, Text } from 'soapbox/components/ui';
import { useOwnAccount } from 'soapbox/hooks'; import { useAppDispatch, useOwnAccount } from 'soapbox/hooks';
import toast from 'soapbox/toast'; import toast from 'soapbox/toast';
import { isDefaultHeader } from 'soapbox/utils/accounts'; import { isDefaultHeader } from 'soapbox/utils/accounts';
import resizeImage from 'soapbox/utils/resize-image'; import resizeImage from 'soapbox/utils/resize-image';
@ -20,7 +19,7 @@ const messages = defineMessages({
const CoverPhotoSelectionStep = ({ onNext }: { onNext: () => void }) => { const CoverPhotoSelectionStep = ({ onNext }: { onNext: () => void }) => {
const intl = useIntl(); const intl = useIntl();
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const account = useOwnAccount(); const account = useOwnAccount();
const fileInput = React.useRef<HTMLInputElement>(null); const fileInput = React.useRef<HTMLInputElement>(null);

@ -1,10 +1,9 @@
import React from 'react'; import React from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { patchMe } from 'soapbox/actions/me'; import { patchMe } from 'soapbox/actions/me';
import { Button, Card, CardBody, FormGroup, Input, Stack, Text } from 'soapbox/components/ui'; import { Button, Card, CardBody, FormGroup, Input, Stack, Text } from 'soapbox/components/ui';
import { useOwnAccount } from 'soapbox/hooks'; import { useAppDispatch, useOwnAccount } from 'soapbox/hooks';
import toast from 'soapbox/toast'; import toast from 'soapbox/toast';
import type { AxiosError } from 'axios'; import type { AxiosError } from 'axios';
@ -16,7 +15,7 @@ const messages = defineMessages({
const DisplayNameStep = ({ onNext }: { onNext: () => void }) => { const DisplayNameStep = ({ onNext }: { onNext: () => void }) => {
const intl = useIntl(); const intl = useIntl();
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const account = useOwnAccount(); const account = useOwnAccount();
const [value, setValue] = React.useState<string>(account?.display_name || ''); const [value, setValue] = React.useState<string>(account?.display_name || '');

@ -1,13 +1,12 @@
import React from 'react'; import React from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { changeSetting } from 'soapbox/actions/settings'; import { changeSetting } from 'soapbox/actions/settings';
import List, { ListItem } from 'soapbox/components/list'; import List, { ListItem } from 'soapbox/components/list';
import { Form } from 'soapbox/components/ui'; import { Form } from 'soapbox/components/ui';
import { SelectDropdown } from 'soapbox/features/forms'; import { SelectDropdown } from 'soapbox/features/forms';
import SettingToggle from 'soapbox/features/notifications/components/setting-toggle'; import SettingToggle from 'soapbox/features/notifications/components/setting-toggle';
import { useFeatures, useSettings } from 'soapbox/hooks'; import { useAppDispatch, useFeatures, useSettings } from 'soapbox/hooks';
import ThemeToggle from '../ui/components/theme-toggle'; import ThemeToggle from '../ui/components/theme-toggle';
@ -89,7 +88,7 @@ const messages = defineMessages({
const Preferences = () => { const Preferences = () => {
const intl = useIntl(); const intl = useIntl();
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const features = useFeatures(); const features = useFeatures();
const settings = useSettings(); const settings = useSettings();

@ -1,6 +1,5 @@
import React from 'react'; import React from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { Link, Redirect } from 'react-router-dom'; import { Link, Redirect } from 'react-router-dom';
import { logIn, verifyCredentials } from 'soapbox/actions/auth'; import { logIn, verifyCredentials } from 'soapbox/actions/auth';
@ -8,7 +7,7 @@ import { fetchInstance } from 'soapbox/actions/instance';
import { openModal } from 'soapbox/actions/modals'; import { openModal } from 'soapbox/actions/modals';
import SiteLogo from 'soapbox/components/site-logo'; import SiteLogo from 'soapbox/components/site-logo';
import { Button, Form, HStack, IconButton, Input, Tooltip } from 'soapbox/components/ui'; import { Button, Form, HStack, IconButton, Input, Tooltip } from 'soapbox/components/ui';
import { useAppSelector, useFeatures, useSoapboxConfig, useOwnAccount, useInstance } from 'soapbox/hooks'; import { useAppSelector, useFeatures, useSoapboxConfig, useOwnAccount, useInstance, useAppDispatch } from 'soapbox/hooks';
import Sonar from './sonar'; import Sonar from './sonar';
@ -25,7 +24,7 @@ const messages = defineMessages({
}); });
const Header = () => { const Header = () => {
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const intl = useIntl(); const intl = useIntl();
const account = useOwnAccount(); const account = useOwnAccount();

@ -2,13 +2,12 @@ import { OrderedSet as ImmutableOrderedSet } from 'immutable';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import React from 'react'; import React from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { expandStatusQuotes, fetchStatusQuotes } from 'soapbox/actions/status-quotes'; import { expandStatusQuotes, fetchStatusQuotes } from 'soapbox/actions/status-quotes';
import StatusList from 'soapbox/components/status-list'; import StatusList from 'soapbox/components/status-list';
import { Column } from 'soapbox/components/ui'; import { Column } from 'soapbox/components/ui';
import { useAppSelector } from 'soapbox/hooks'; import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
const messages = defineMessages({ const messages = defineMessages({
heading: { id: 'column.quotes', defaultMessage: 'Post quotes' }, heading: { id: 'column.quotes', defaultMessage: 'Post quotes' },
@ -18,7 +17,7 @@ const handleLoadMore = debounce((statusId: string, dispatch: React.Dispatch<any>
dispatch(expandStatusQuotes(statusId)), 300, { leading: true }); dispatch(expandStatusQuotes(statusId)), 300, { leading: true });
const Quotes: React.FC = () => { const Quotes: React.FC = () => {
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const intl = useIntl(); const intl = useIntl();
const { statusId } = useParams<{ statusId: string }>(); const { statusId } = useParams<{ statusId: string }>();

@ -1,12 +1,11 @@
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { fetchMfa } from 'soapbox/actions/mfa'; import { fetchMfa } from 'soapbox/actions/mfa';
import List, { ListItem } from 'soapbox/components/list'; import List, { ListItem } from 'soapbox/components/list';
import { Card, CardBody, CardHeader, CardTitle, Column } from 'soapbox/components/ui'; import { Card, CardBody, CardHeader, CardTitle, Column } from 'soapbox/components/ui';
import { useAppSelector, useFeatures, useOwnAccount } from 'soapbox/hooks'; import { useAppDispatch, useAppSelector, useFeatures, useOwnAccount } from 'soapbox/hooks';
import Preferences from '../preferences'; import Preferences from '../preferences';
@ -32,7 +31,7 @@ const messages = defineMessages({
/** User settings page. */ /** User settings page. */
const Settings = () => { const Settings = () => {
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const history = useHistory(); const history = useHistory();
const intl = useIntl(); const intl = useIntl();

@ -3,7 +3,7 @@ import { defineMessages, useIntl } from 'react-intl';
// @ts-ignore // @ts-ignore
import Overlay from 'react-overlays/lib/Overlay'; import Overlay from 'react-overlays/lib/Overlay';
import Icon from 'soapbox/components/icon'; import ForkAwesomeIcon from 'soapbox/components/fork-awesome-icon';
import IconPickerMenu from './icon-picker-menu'; import IconPickerMenu from './icon-picker-menu';
@ -68,7 +68,7 @@ const IconPickerDropdown: React.FC<IIconPickerDropdown> = ({ value, onPickEmoji
onKeyDown={onToggle} onKeyDown={onToggle}
tabIndex={0} tabIndex={0}
> >
<Icon id={value} /> <ForkAwesomeIcon id={value} />
</div> </div>
<Overlay show={active} placement={placement} target={target.current}> <Overlay show={active} placement={placement} target={target.current}>

@ -39,6 +39,7 @@ const messages = defineMessages({
customCssLabel: { id: 'soapbox_config.custom_css.meta_fields.url_placeholder', defaultMessage: 'URL' }, customCssLabel: { id: 'soapbox_config.custom_css.meta_fields.url_placeholder', defaultMessage: 'URL' },
rawJSONLabel: { id: 'soapbox_config.raw_json_label', defaultMessage: 'Advanced: Edit raw JSON data' }, rawJSONLabel: { id: 'soapbox_config.raw_json_label', defaultMessage: 'Advanced: Edit raw JSON data' },
rawJSONHint: { id: 'soapbox_config.raw_json_hint', defaultMessage: 'Edit the settings data directly. Changes made directly to the JSON file will override the form fields above. Click "Save" to apply your changes.' }, rawJSONHint: { id: 'soapbox_config.raw_json_hint', defaultMessage: 'Edit the settings data directly. Changes made directly to the JSON file will override the form fields above. Click "Save" to apply your changes.' },
rawJSONInvalid: { id: 'soapbox_config.raw_json_invalid', defaultMessage: 'is invalid' },
verifiedCanEditNameLabel: { id: 'soapbox_config.verified_can_edit_name_label', defaultMessage: 'Allow verified users to edit their own display name.' }, verifiedCanEditNameLabel: { id: 'soapbox_config.verified_can_edit_name_label', defaultMessage: 'Allow verified users to edit their own display name.' },
displayFqnLabel: { id: 'soapbox_config.display_fqn_label', defaultMessage: 'Display domain (eg @user@domain) for local accounts.' }, displayFqnLabel: { id: 'soapbox_config.display_fqn_label', defaultMessage: 'Display domain (eg @user@domain) for local accounts.' },
greentextLabel: { id: 'soapbox_config.greentext_label', defaultMessage: 'Enable greentext support' }, greentextLabel: { id: 'soapbox_config.greentext_label', defaultMessage: 'Enable greentext support' },
@ -394,11 +395,13 @@ const SoapboxConfig: React.FC = () => {
expanded={jsonEditorExpanded} expanded={jsonEditorExpanded}
onToggle={toggleJSONEditor} onToggle={toggleJSONEditor}
> >
<FormGroup hintText={intl.formatMessage(messages.rawJSONHint)}> <FormGroup
hintText={intl.formatMessage(messages.rawJSONHint)}
errors={jsonValid ? undefined : [intl.formatMessage(messages.rawJSONInvalid)]}
>
<Textarea <Textarea
value={rawJSON} value={rawJSON}
onChange={handleEditJSON} onChange={handleEditJSON}
hasError={!jsonValid}
isCodeEditor isCodeEditor
rows={12} rows={12}
/> />

@ -153,7 +153,7 @@ const Card: React.FC<ICard> = ({
</Stack> </Stack>
); );
let embed: React.ReactNode = ''; let embed: React.ReactNode = null;
const canvas = ( const canvas = (
<Blurhash <Blurhash
@ -240,12 +240,6 @@ const Card: React.FC<ICard> = ({
{thumbnail} {thumbnail}
</div> </div>
); );
} else {
embed = (
<div className='status-card__image status-card__image--empty'>
<Icon src={require('@tabler/icons/file-text.svg')} />
</div>
);
} }
return ( return (

@ -1,9 +1,9 @@
import React from 'react'; import React from 'react';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import { useDispatch } from 'react-redux';
import { importFetchedStatuses } from 'soapbox/actions/importer'; import { importFetchedStatuses } from 'soapbox/actions/importer';
import { expandTimelineSuccess } from 'soapbox/actions/timelines'; import { expandTimelineSuccess } from 'soapbox/actions/timelines';
import { useAppDispatch } from 'soapbox/hooks';
import { Column } from '../../components/ui'; import { Column } from '../../components/ui';
import Timeline from '../ui/components/timeline'; import Timeline from '../ui/components/timeline';
@ -31,7 +31,7 @@ const onlyMedia = false;
const TestTimeline: React.FC = () => { const TestTimeline: React.FC = () => {
const intl = useIntl(); const intl = useIntl();
const dispatch = useDispatch(); const dispatch = useAppDispatch();
React.useEffect(() => { React.useEffect(() => {
dispatch(importFetchedStatuses(MOCK_STATUSES)); dispatch(importFetchedStatuses(MOCK_STATUSES));

@ -48,6 +48,8 @@ describe('<UI />', () => {
await waitFor(() => { await waitFor(() => {
expect(screen.getByTestId('cta-banner')).toHaveTextContent('Sign up now to discuss'); expect(screen.getByTestId('cta-banner')).toHaveTextContent('Sign up now to discuss');
}, {
timeout: 2000,
}); });
}); });
}); });

@ -1,6 +1,5 @@
import React from 'react'; import React from 'react';
import { defineMessages, useIntl } from 'react-intl'; import { defineMessages, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { import {
followAccount, followAccount,
@ -14,7 +13,7 @@ import {
} from 'soapbox/actions/accounts'; } from 'soapbox/actions/accounts';
import { openModal } from 'soapbox/actions/modals'; import { openModal } from 'soapbox/actions/modals';
import { Button, HStack } from 'soapbox/components/ui'; import { Button, HStack } from 'soapbox/components/ui';
import { useAppSelector, useFeatures } from 'soapbox/hooks'; import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks';
import type { Account as AccountEntity } from 'soapbox/types/entities'; import type { Account as AccountEntity } from 'soapbox/types/entities';
@ -49,7 +48,7 @@ interface IActionButton {
* `actionType` prop. * `actionType` prop.
*/ */
const ActionButton: React.FC<IActionButton> = ({ account, actionType, small }) => { const ActionButton: React.FC<IActionButton> = ({ account, actionType, small }) => {
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const features = useFeatures(); const features = useFeatures();
const intl = useIntl(); const intl = useIntl();

@ -1,13 +1,12 @@
import classNames from 'clsx'; import classNames from 'clsx';
import React from 'react'; import React from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { useDispatch } from 'react-redux';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { logOut } from 'soapbox/actions/auth'; import { logOut } from 'soapbox/actions/auth';
import { Text } from 'soapbox/components/ui'; import { Text } from 'soapbox/components/ui';
import emojify from 'soapbox/features/emoji/emoji'; import emojify from 'soapbox/features/emoji/emoji';
import { useSoapboxConfig, useOwnAccount, useFeatures } from 'soapbox/hooks'; import { useSoapboxConfig, useOwnAccount, useFeatures, useAppDispatch } from 'soapbox/hooks';
import sourceCode from 'soapbox/utils/code'; import sourceCode from 'soapbox/utils/code';
interface IFooterLink { interface IFooterLink {
@ -29,7 +28,7 @@ const LinkFooter: React.FC = (): JSX.Element => {
const features = useFeatures(); const features = useFeatures();
const soapboxConfig = useSoapboxConfig(); const soapboxConfig = useSoapboxConfig();
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const onClickLogOut: React.EventHandler<React.MouseEvent> = (e) => { const onClickLogOut: React.EventHandler<React.MouseEvent> = (e) => {
dispatch(logOut()); dispatch(logOut());
@ -76,7 +75,7 @@ const LinkFooter: React.FC = (): JSX.Element => {
defaultMessage='{code_name} is open source software. You can contribute or report issues at {code_link} (v{code_version}).' defaultMessage='{code_name} is open source software. You can contribute or report issues at {code_link} (v{code_version}).'
values={{ values={{
code_name: sourceCode.displayName, code_name: sourceCode.displayName,
code_link: <Text theme='subtle'><a className='underline' href={sourceCode.url} rel='noopener' target='_blank'>{sourceCode.repository}</a></Text>, code_link: <Text theme='subtle' tag='span'><a className='underline' href={sourceCode.url} rel='noopener' target='_blank'>{sourceCode.repository}</a></Text>,
code_version: sourceCode.version, code_version: sourceCode.version,
}} }}
/> />

@ -1,45 +0,0 @@
import React, { useEffect } from 'react';
import { NavLink } from 'react-router-dom';
import { createSelector } from 'reselect';
import { fetchLists } from 'soapbox/actions/lists';
import Icon from 'soapbox/components/icon';
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
import type { List as ImmutableList } from 'immutable';
import type { RootState } from 'soapbox/store';
import type { List as ListEntity } from 'soapbox/types/entities';
const getOrderedLists = createSelector([(state: RootState) => state.lists], lists => {
if (!lists) {
return lists;
}
return lists.toList().filter(item => !!item).sort((a, b) => (a as ListEntity).title.localeCompare((b as ListEntity).title)).take(4) as ImmutableList<ListEntity>;
});
const ListPanel = () => {
const dispatch = useAppDispatch();
const lists = useAppSelector((state) => getOrderedLists(state));
useEffect(() => {
dispatch(fetchLists());
}, []);
if (!lists || lists.isEmpty()) {
return null;
}
return (
<div>
<hr />
{lists.map(list => (
<NavLink key={list.id} className='column-link column-link--transparent' strict to={`/list/${list.id}`}><Icon className='column-link__icon' id='list-ul' fixedWidth />{list.title}</NavLink>
))}
</div>
);
};
export default ListPanel;

@ -4,7 +4,7 @@ import React from 'react';
import { __stub } from 'soapbox/api'; import { __stub } from 'soapbox/api';
import { render, screen } from '../../../../../../jest/test-helpers'; import { render, screen, waitFor } from '../../../../../../jest/test-helpers';
import { normalizeAccount, normalizeStatus } from '../../../../../../normalizers'; import { normalizeAccount, normalizeStatus } from '../../../../../../normalizers';
import ReportModal from '../report-modal'; import ReportModal from '../report-modal';
@ -64,6 +64,9 @@ describe('<ReportModal />', () => {
await user.click(screen.getByTestId('rule-1')); await user.click(screen.getByTestId('rule-1'));
await user.click(screen.getByText(/Next/)); await user.click(screen.getByText(/Next/));
await user.click(screen.getByText(/Submit/)); await user.click(screen.getByText(/Submit/));
await waitFor(() => {
expect(screen.getByText(/Thanks for submitting your report/)).toBeInTheDocument(); expect(screen.getByText(/Thanks for submitting your report/)).toBeInTheDocument();
}); });
});
}); });

@ -1,14 +1,13 @@
import { OrderedSet } from 'immutable'; import { OrderedSet } from 'immutable';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import Toggle from 'react-toggle'; import Toggle from 'react-toggle';
import { changeReportBlock, changeReportForward } from 'soapbox/actions/reports'; import { changeReportBlock, changeReportForward } from 'soapbox/actions/reports';
import { fetchRules } from 'soapbox/actions/rules'; import { fetchRules } from 'soapbox/actions/rules';
import { Button, FormGroup, HStack, Stack, Text } from 'soapbox/components/ui'; import { Button, FormGroup, HStack, Stack, Text } from 'soapbox/components/ui';
import StatusCheckBox from 'soapbox/features/report/components/status-check-box'; import StatusCheckBox from 'soapbox/features/report/components/status-check-box';
import { useAppSelector, useFeatures } from 'soapbox/hooks'; import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks';
import { isRemote, getDomain } from 'soapbox/utils/accounts'; import { isRemote, getDomain } from 'soapbox/utils/accounts';
import type { ReducerAccount } from 'soapbox/reducers/accounts'; import type { ReducerAccount } from 'soapbox/reducers/accounts';
@ -26,7 +25,7 @@ interface IOtherActionsStep {
} }
const OtherActionsStep = ({ account }: IOtherActionsStep) => { const OtherActionsStep = ({ account }: IOtherActionsStep) => {
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const features = useFeatures(); const features = useFeatures();
const intl = useIntl(); const intl = useIntl();

@ -1,12 +1,11 @@
import classNames from 'clsx'; import classNames from 'clsx';
import React, { useEffect, useMemo, useRef, useState } from 'react'; import React, { useEffect, useMemo, useRef, useState } from 'react';
import { defineMessages, useIntl } from 'react-intl'; import { defineMessages, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { changeReportComment, changeReportRule } from 'soapbox/actions/reports'; import { changeReportComment, changeReportRule } from 'soapbox/actions/reports';
import { fetchRules } from 'soapbox/actions/rules'; import { fetchRules } from 'soapbox/actions/rules';
import { FormGroup, Stack, Text, Textarea } from 'soapbox/components/ui'; import { FormGroup, Stack, Text, Textarea } from 'soapbox/components/ui';
import { useAppSelector } from 'soapbox/hooks'; import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
import type { ReducerAccount } from 'soapbox/reducers/accounts'; import type { ReducerAccount } from 'soapbox/reducers/accounts';
@ -22,7 +21,7 @@ interface IReasonStep {
const RULES_HEIGHT = 385; const RULES_HEIGHT = 385;
const ReasonStep = (_props: IReasonStep) => { const ReasonStep = (_props: IReasonStep) => {
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const intl = useIntl(); const intl = useIntl();
const rulesListRef = useRef(null); const rulesListRef = useRef(null);

@ -1,7 +1,6 @@
import classNames from 'clsx'; import classNames from 'clsx';
import React, { useRef, useState } from 'react'; import React, { useRef, useState } from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { Link, Redirect } from 'react-router-dom'; import { Link, Redirect } from 'react-router-dom';
import { logIn, verifyCredentials } from 'soapbox/actions/auth'; import { logIn, verifyCredentials } from 'soapbox/actions/auth';
@ -10,7 +9,7 @@ import { openSidebar } from 'soapbox/actions/sidebar';
import SiteLogo from 'soapbox/components/site-logo'; import SiteLogo from 'soapbox/components/site-logo';
import { Avatar, Button, Form, HStack, IconButton, Input, Tooltip } from 'soapbox/components/ui'; import { Avatar, Button, Form, HStack, IconButton, Input, Tooltip } from 'soapbox/components/ui';
import Search from 'soapbox/features/compose/components/search'; import Search from 'soapbox/features/compose/components/search';
import { useOwnAccount, useSoapboxConfig } from 'soapbox/hooks'; import { useAppDispatch, useOwnAccount, useSoapboxConfig } from 'soapbox/hooks';
import ProfileDropdown from './profile-dropdown'; import ProfileDropdown from './profile-dropdown';
@ -24,7 +23,7 @@ const messages = defineMessages({
}); });
const Navbar = () => { const Navbar = () => {
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const intl = useIntl(); const intl = useIntl();
const node = useRef(null); const node = useRef(null);

@ -70,6 +70,7 @@ const PendingStatus: React.FC<IPendingStatus> = ({ idempotencyKey, className, mu
account={account} account={account}
timestamp={status.created_at} timestamp={status.created_at}
hideActions hideActions
withLinkToProfile={false}
/> />
</HStack> </HStack>
</div> </div>

@ -1,13 +1,12 @@
import throttle from 'lodash/throttle'; import throttle from 'lodash/throttle';
import React from 'react'; import React from 'react';
import { defineMessages, useIntl } from 'react-intl'; import { defineMessages, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { fetchOwnAccounts, logOut, switchAccount } from 'soapbox/actions/auth'; import { fetchOwnAccounts, logOut, switchAccount } from 'soapbox/actions/auth';
import Account from 'soapbox/components/account'; import Account from 'soapbox/components/account';
import { Menu, MenuButton, MenuDivider, MenuItem, MenuLink, MenuList } from 'soapbox/components/ui'; import { Menu, MenuButton, MenuDivider, MenuItem, MenuLink, MenuList } from 'soapbox/components/ui';
import { useAppSelector, useFeatures } from 'soapbox/hooks'; import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks';
import { makeGetAccount } from 'soapbox/selectors'; import { makeGetAccount } from 'soapbox/selectors';
import ThemeToggle from './theme-toggle'; import ThemeToggle from './theme-toggle';
@ -35,7 +34,7 @@ type IMenuItem = {
const getAccount = makeGetAccount(); const getAccount = makeGetAccount();
const ProfileDropdown: React.FC<IProfileDropdown> = ({ account, children }) => { const ProfileDropdown: React.FC<IProfileDropdown> = ({ account, children }) => {
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const features = useFeatures(); const features = useFeatures();
const intl = useIntl(); const intl = useIntl();

@ -1,7 +1,6 @@
import { OrderedSet as ImmutableOrderedSet } from 'immutable'; import { OrderedSet as ImmutableOrderedSet } from 'immutable';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { FormattedList, FormattedMessage } from 'react-intl'; import { FormattedList, FormattedMessage } from 'react-intl';
import { useDispatch } from 'react-redux';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { fetchAccountFamiliarFollowers } from 'soapbox/actions/familiar-followers'; import { fetchAccountFamiliarFollowers } from 'soapbox/actions/familiar-followers';
@ -9,7 +8,7 @@ import { openModal } from 'soapbox/actions/modals';
import HoverRefWrapper from 'soapbox/components/hover-ref-wrapper'; import HoverRefWrapper from 'soapbox/components/hover-ref-wrapper';
import { Text } from 'soapbox/components/ui'; import { Text } from 'soapbox/components/ui';
import VerificationBadge from 'soapbox/components/verification-badge'; import VerificationBadge from 'soapbox/components/verification-badge';
import { useAppSelector, useFeatures } from 'soapbox/hooks'; import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks';
import { makeGetAccount } from 'soapbox/selectors'; import { makeGetAccount } from 'soapbox/selectors';
import type { Account } from 'soapbox/types/entities'; import type { Account } from 'soapbox/types/entities';
@ -21,7 +20,7 @@ interface IProfileFamiliarFollowers {
} }
const ProfileFamiliarFollowers: React.FC<IProfileFamiliarFollowers> = ({ account }) => { const ProfileFamiliarFollowers: React.FC<IProfileFamiliarFollowers> = ({ account }) => {
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const me = useAppSelector((state) => state.me); const me = useAppSelector((state) => state.me);
const features = useFeatures(); const features = useFeatures();
const familiarFollowerIds = useAppSelector(state => state.user_lists.familiar_followers.get(account.id)?.items || ImmutableOrderedSet<string>()); const familiarFollowerIds = useAppSelector(state => state.user_lists.familiar_followers.get(account.id)?.items || ImmutableOrderedSet<string>());

@ -1,12 +1,11 @@
import { List as ImmutableList } from 'immutable'; import { List as ImmutableList } from 'immutable';
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { useDispatch } from 'react-redux';
import { openModal } from 'soapbox/actions/modals'; import { openModal } from 'soapbox/actions/modals';
import { expandAccountMediaTimeline } from 'soapbox/actions/timelines'; import { expandAccountMediaTimeline } from 'soapbox/actions/timelines';
import { Spinner, Text, Widget } from 'soapbox/components/ui'; import { Spinner, Text, Widget } from 'soapbox/components/ui';
import { useAppSelector } from 'soapbox/hooks'; import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
import { getAccountGallery } from 'soapbox/selectors'; import { getAccountGallery } from 'soapbox/selectors';
import MediaItem from '../../account-gallery/components/media-item'; import MediaItem from '../../account-gallery/components/media-item';
@ -18,7 +17,7 @@ interface IProfileMediaPanel {
} }
const ProfileMediaPanel: React.FC<IProfileMediaPanel> = ({ account }) => { const ProfileMediaPanel: React.FC<IProfileMediaPanel> = ({ account }) => {
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import Icon from 'soapbox/components/icon'; import ForkAwesomeIcon from 'soapbox/components/fork-awesome-icon';
import { Widget, Stack, Text } from 'soapbox/components/ui'; import { Widget, Stack, Text } from 'soapbox/components/ui';
import { useInstance, useSettings, useSoapboxConfig } from 'soapbox/hooks'; import { useInstance, useSettings, useSoapboxConfig } from 'soapbox/hooks';
@ -20,7 +20,7 @@ const PromoPanel: React.FC = () => {
{promoItems.map((item, i) => ( {promoItems.map((item, i) => (
<Text key={i}> <Text key={i}>
<a className='flex items-center' href={item.url} target='_blank'> <a className='flex items-center' href={item.url} target='_blank'>
<Icon id={item.icon} className='flex-none text-lg mr-2 rtl:mr-0 rtl:ml-2' fixedWidth /> <ForkAwesomeIcon id={item.icon} className='flex-none text-lg mr-2 rtl:mr-0 rtl:ml-2' fixedWidth />
{item.textLocales.get(locale) || item.text} {item.textLocales.get(locale) || item.text}
</a> </a>
</Text> </Text>

@ -1,14 +1,13 @@
import React from 'react'; import React from 'react';
import { useDispatch } from 'react-redux';
import { changeSetting } from 'soapbox/actions/settings'; import { changeSetting } from 'soapbox/actions/settings';
import { useSettings } from 'soapbox/hooks'; import { useAppDispatch, useSettings } from 'soapbox/hooks';
import ThemeSelector from './theme-selector'; import ThemeSelector from './theme-selector';
/** Stateful theme selector. */ /** Stateful theme selector. */
const ThemeToggle: React.FC = () => { const ThemeToggle: React.FC = () => {
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const themeMode = useSettings().get('themeMode'); const themeMode = useSettings().get('themeMode');
const handleChange = (themeMode: string) => { const handleChange = (themeMode: string) => {

@ -26,10 +26,10 @@ const UserPanel: React.FC<IUserPanel> = ({ accountId, action, badges, domain })
const fqn = useAppSelector((state) => displayFqn(state)); const fqn = useAppSelector((state) => displayFqn(state));
if (!account) return null; if (!account) return null;
const displayNameHtml = { __html: account.get('display_name_html') }; const displayNameHtml = { __html: account.display_name_html };
const acct = !account.get('acct').includes('@') && domain ? `${account.get('acct')}@${domain}` : account.get('acct'); const acct = !account.acct.includes('@') && domain ? `${account.acct}@${domain}` : account.acct;
const header = account.get('header'); const header = account.header;
const verified = account.get('verified'); const verified = account.verified;
return ( return (
<div className='relative'> <div className='relative'>
@ -43,11 +43,11 @@ const UserPanel: React.FC<IUserPanel> = ({ accountId, action, badges, domain })
<HStack justifyContent='between'> <HStack justifyContent='between'>
<Link <Link
to={`/@${account.get('acct')}`} to={`/@${account.acct}`}
title={acct} title={acct}
className='-mt-12 block' className='-mt-12 block'
> >
<Avatar src={account.avatar} className='h-20 w-20 bg-gray-50 ring-2 ring-white overflow-hidden' /> <Avatar src={account.avatar} size={80} className='h-20 w-20 bg-gray-50 ring-2 ring-white overflow-hidden' />
</Link> </Link>
{action && ( {action && (
@ -57,7 +57,7 @@ const UserPanel: React.FC<IUserPanel> = ({ accountId, action, badges, domain })
</Stack> </Stack>
<Stack> <Stack>
<Link to={`/@${account.get('acct')}`}> <Link to={`/@${account.acct}`}>
<HStack space={1} alignItems='center'> <HStack space={1} alignItems='center'>
<Text size='lg' weight='bold' dangerouslySetInnerHTML={displayNameHtml} /> <Text size='lg' weight='bold' dangerouslySetInnerHTML={displayNameHtml} />
@ -77,11 +77,11 @@ const UserPanel: React.FC<IUserPanel> = ({ accountId, action, badges, domain })
</Stack> </Stack>
<HStack alignItems='center' space={3}> <HStack alignItems='center' space={3}>
{account.get('followers_count') >= 0 && ( {account.followers_count >= 0 && (
<Link to={`/@${account.get('acct')}/followers`} title={intl.formatNumber(account.get('followers_count'))}> <Link to={`/@${account.acct}/followers`} title={intl.formatNumber(account.followers_count)}>
<HStack alignItems='center' space={1}> <HStack alignItems='center' space={1}>
<Text theme='primary' weight='bold' size='sm'> <Text theme='primary' weight='bold' size='sm'>
{shortNumberFormat(account.get('followers_count'))} {shortNumberFormat(account.followers_count)}
</Text> </Text>
<Text weight='bold' size='sm'> <Text weight='bold' size='sm'>
<FormattedMessage id='account.followers' defaultMessage='Followers' /> <FormattedMessage id='account.followers' defaultMessage='Followers' />
@ -90,11 +90,11 @@ const UserPanel: React.FC<IUserPanel> = ({ accountId, action, badges, domain })
</Link> </Link>
)} )}
{account.get('following_count') >= 0 && ( {account.following_count >= 0 && (
<Link to={`/@${account.get('acct')}/following`} title={intl.formatNumber(account.get('following_count'))}> <Link to={`/@${account.acct}/following`} title={intl.formatNumber(account.following_count)}>
<HStack alignItems='center' space={1}> <HStack alignItems='center' space={1}>
<Text theme='primary' weight='bold' size='sm'> <Text theme='primary' weight='bold' size='sm'>
{shortNumberFormat(account.get('following_count'))} {shortNumberFormat(account.following_count)}
</Text> </Text>
<Text weight='bold' size='sm'> <Text weight='bold' size='sm'>
<FormattedMessage id='account.follows' defaultMessage='Follows' /> <FormattedMessage id='account.follows' defaultMessage='Follows' />

@ -472,7 +472,7 @@ const UI: React.FC = ({ children }) => {
navigator.serviceWorker.addEventListener('message', handleServiceWorkerPostMessage); navigator.serviceWorker.addEventListener('message', handleServiceWorkerPostMessage);
} }
if (typeof window.Notification !== 'undefined' && Notification.permission === 'default') { if (window.Notification?.permission === 'default') {
window.setTimeout(() => Notification.requestPermission(), 120 * 1000); window.setTimeout(() => Notification.requestPermission(), 120 * 1000);
} }

@ -30,7 +30,10 @@ describe('<Registration />', () => {
fireEvent.submit(screen.getByTestId('button'), { preventDefault: () => {} }); fireEvent.submit(screen.getByTestId('button'), { preventDefault: () => {} });
}); });
await waitFor(() => {
expect(screen.getByTestId('toast')).toHaveTextContent(/welcome to/i); expect(screen.getByTestId('toast')).toHaveTextContent(/welcome to/i);
});
expect(screen.queryAllByRole('heading')).toHaveLength(0); expect(screen.queryAllByRole('heading')).toHaveLength(0);
}); });
}); });
@ -38,7 +41,11 @@ describe('<Registration />', () => {
describe('with invalid data', () => { describe('with invalid data', () => {
it('handles 422 errors', async() => { it('handles 422 errors', async() => {
__stub(mock => { __stub(mock => {
mock.onPost('/api/v1/pepe/accounts').reply(422, {}); mock.onPost('/api/v1/pepe/accounts').reply(
422, {
error: 'user_taken',
},
);
}); });
render(<Registration />); render(<Registration />);
@ -47,8 +54,32 @@ describe('<Registration />', () => {
fireEvent.submit(screen.getByTestId('button'), { preventDefault: () => {} }); fireEvent.submit(screen.getByTestId('button'), { preventDefault: () => {} });
}); });
await waitFor(() => {
expect(screen.getByTestId('toast')).toHaveTextContent(/this username has already been taken/i); expect(screen.getByTestId('toast')).toHaveTextContent(/this username has already been taken/i);
}); });
});
it('handles 422 errors with messages', async() => {
__stub(mock => {
mock.onPost('/api/v1/pepe/accounts').reply(
422, {
error: 'user_vip',
message: 'This username is unavailable.',
},
);
});
render(<Registration />);
await waitFor(() => {
fireEvent.submit(screen.getByTestId('button'), { preventDefault: () => {} });
});
await waitFor(() => {
expect(screen.getByTestId('toast')).toHaveTextContent(/this username is unavailable/i);
});
});
it('handles generic errors', async() => { it('handles generic errors', async() => {
__stub(mock => { __stub(mock => {
@ -61,9 +92,11 @@ describe('<Registration />', () => {
fireEvent.submit(screen.getByTestId('button'), { preventDefault: () => {} }); fireEvent.submit(screen.getByTestId('button'), { preventDefault: () => {} });
}); });
await waitFor(() => {
expect(screen.getByTestId('toast')).toHaveTextContent(/failed to register your account/i); expect(screen.getByTestId('toast')).toHaveTextContent(/failed to register your account/i);
}); });
}); });
});
describe('validations', () => { describe('validations', () => {
it('should undisable button with valid password', async() => { it('should undisable button with valid password', async() => {

@ -58,9 +58,11 @@ const Registration = () => {
intl.formatMessage(messages.success, { siteTitle: instance.title }), intl.formatMessage(messages.success, { siteTitle: instance.title }),
); );
}) })
.catch((error: AxiosError) => { .catch((errorResponse: AxiosError<{ error: string, message: string }>) => {
if (error?.response?.status === 422) { const error = errorResponse.response?.data?.error;
toast.error(intl.formatMessage(messages.usernameTaken));
if (error) {
toast.error(errorResponse.response?.data?.message || intl.formatMessage(messages.usernameTaken));
} else { } else {
toast.error(intl.formatMessage(messages.error)); toast.error(intl.formatMessage(messages.error));
} }

@ -60,7 +60,9 @@ describe('<EmailVerification />', () => {
); );
}); });
await waitFor(() => {
expect(screen.getByTestId('form-group-error')).toHaveTextContent('is taken'); expect(screen.getByTestId('form-group-error')).toHaveTextContent('is taken');
}); });
}); });
});
}); });

@ -37,8 +37,10 @@ describe('<SmsVerification />', () => {
); );
}); });
await waitFor(() => {
expect(screen.getByRole('heading')).toHaveTextContent('Verification code'); expect(screen.getByRole('heading')).toHaveTextContent('Verification code');
expect(screen.getByTestId('toast')).toHaveTextContent('A verification code has been sent to your phone number.'); expect(screen.getByTestId('toast')).toHaveTextContent('A verification code has been sent to your phone number.');
});
act(() => { act(() => {
toast.remove(); toast.remove();
@ -68,8 +70,10 @@ describe('<SmsVerification />', () => {
); );
}); });
await waitFor(() => {
expect(screen.getByRole('heading')).toHaveTextContent('Verification code'); expect(screen.getByRole('heading')).toHaveTextContent('Verification code');
expect(screen.getByTestId('toast')).toHaveTextContent('A verification code has been sent to your phone number.'); expect(screen.getByTestId('toast')).toHaveTextContent('A verification code has been sent to your phone number.');
});
act(() => { act(() => {
toast.remove(); toast.remove();
@ -82,9 +86,11 @@ describe('<SmsVerification />', () => {
await userEvent.type(screen.getByLabelText('Digit 5'), '5'); await userEvent.type(screen.getByLabelText('Digit 5'), '5');
await userEvent.type(screen.getByLabelText('Digit 6'), '6'); await userEvent.type(screen.getByLabelText('Digit 6'), '6');
await waitFor(() => {
expect(screen.getByTestId('toast')).toHaveTextContent('Your SMS token has expired.'); expect(screen.getByTestId('toast')).toHaveTextContent('Your SMS token has expired.');
}); });
}); });
});
describe('with invalid data', () => { describe('with invalid data', () => {
beforeEach(() => { beforeEach(() => {
@ -106,7 +112,9 @@ describe('<SmsVerification />', () => {
); );
}); });
await waitFor(() => {
expect(screen.getByTestId('toast')).toHaveTextContent('Failed to send SMS message to your phone number.'); expect(screen.getByTestId('toast')).toHaveTextContent('Failed to send SMS message to your phone number.');
}); });
}); });
});
}); });

@ -1,6 +1,5 @@
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { useDispatch } from 'react-redux';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { logOut } from 'soapbox/actions/auth'; import { logOut } from 'soapbox/actions/auth';
@ -8,10 +7,10 @@ import { openModal } from 'soapbox/actions/modals';
import LandingGradient from 'soapbox/components/landing-gradient'; import LandingGradient from 'soapbox/components/landing-gradient';
import SiteLogo from 'soapbox/components/site-logo'; import SiteLogo from 'soapbox/components/site-logo';
import { Button, Stack, Text } from 'soapbox/components/ui'; import { Button, Stack, Text } from 'soapbox/components/ui';
import { useInstance, useOwnAccount } from 'soapbox/hooks'; import { useAppDispatch, useInstance, useOwnAccount } from 'soapbox/hooks';
const WaitlistPage = () => { const WaitlistPage = () => {
const dispatch = useDispatch(); const dispatch = useAppDispatch();
const instance = useInstance(); const instance = useInstance();
const me = useOwnAccount(); const me = useOwnAccount();

@ -1,7 +1,7 @@
import classNames from 'clsx'; import classNames from 'clsx';
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import throttle from 'lodash/throttle'; import throttle from 'lodash/throttle';
import React, { useCallback, useEffect, useRef, useState } from 'react'; import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
import { defineMessages, useIntl } from 'react-intl'; import { defineMessages, useIntl } from 'react-intl';
import Blurhash from 'soapbox/components/blurhash'; import Blurhash from 'soapbox/components/blurhash';
@ -159,7 +159,7 @@ const Video: React.FC<IVideo> = ({
} }
}; };
useEffect(() => { useLayoutEffect(() => {
setDimensions(); setDimensions();
}, [player.current]); }, [player.current]);

@ -25,7 +25,7 @@ const useDimensions = (): UseDimensionsResult => {
); );
useEffect((): any => { useEffect((): any => {
if (!element) return null; if (!element) return;
observer.observe(element); observer.observe(element);
return () => { return () => {

@ -1,5 +1,6 @@
'use strict'; 'use strict';
import { act } from '@testing-library/react';
import { toast } from 'react-hot-toast'; import { toast } from 'react-hot-toast';
import { __clear as clearApiMocks } from '../api/__mocks__'; import { __clear as clearApiMocks } from '../api/__mocks__';
@ -17,7 +18,9 @@ require('fake-indexeddb/auto');
// Clear toasts after each test. // Clear toasts after each test.
afterEach(() => { afterEach(() => {
act(() => {
toast.remove(); toast.remove();
});
}); });
const intersectionObserverMock = () => ({ observe: () => null, disconnect: () => null }); const intersectionObserverMock = () => ({ observe: () => null, disconnect: () => null });

@ -1,46 +0,0 @@
'use strict';
// Convenience function to load polyfills and return a promise when it's done.
// If there are no polyfills, then this is just Promise.resolve() which means
// it will execute in the same tick of the event loop (i.e. near-instant).
function importBasePolyfills() {
return import(/* webpackChunkName: "base_polyfills" */ './base-polyfills');
}
function importExtraPolyfills() {
return import(/* webpackChunkName: "extra_polyfills" */ './extra-polyfills');
}
function loadPolyfills() {
const needsBasePolyfills = !(
// @ts-ignore
Array.prototype.includes &&
// @ts-ignore
HTMLCanvasElement.prototype.toBlob &&
window.Intl &&
// @ts-ignore
Number.isNaN &&
// @ts-ignore
Object.assign &&
// @ts-ignore
Object.values &&
window.Symbol
);
// Older versions of Firefox and Safari do not have IntersectionObserver.
// This avoids shipping them all the polyfills.
const needsExtraPolyfills = !(
window.IntersectionObserver &&
window.IntersectionObserverEntry &&
'isIntersecting' in IntersectionObserverEntry.prototype &&
window.requestIdleCallback
);
return Promise.all([
needsBasePolyfills && importBasePolyfills(),
needsExtraPolyfills && importExtraPolyfills(),
]);
}
export default loadPolyfills;

@ -147,7 +147,6 @@
"alert.unexpected.links.support": "الدعم", "alert.unexpected.links.support": "الدعم",
"alert.unexpected.message": "حدث خطأ ما.", "alert.unexpected.message": "حدث خطأ ما.",
"alert.unexpected.return_home": "العودة للصفحة الرئيسة", "alert.unexpected.return_home": "العودة للصفحة الرئيسة",
"alert.unexpected.title": "المعذرة!",
"aliases.account.add": "إنشاء اسم مستعار", "aliases.account.add": "إنشاء اسم مستعار",
"aliases.account_label": "الحساب القديم:", "aliases.account_label": "الحساب القديم:",
"aliases.aliases_list_delete": "إلغاء ربط الاسم المستعار", "aliases.aliases_list_delete": "إلغاء ربط الاسم المستعار",
@ -186,18 +185,9 @@
"bundle_modal_error.message": "حدث خطأ في أثناء تحميل الصفحة.", "bundle_modal_error.message": "حدث خطأ في أثناء تحميل الصفحة.",
"bundle_modal_error.retry": "إعادة المحاولة", "bundle_modal_error.retry": "إعادة المحاولة",
"card.back.label": "العودة", "card.back.label": "العودة",
"chat_box.actions.send": "إرسال",
"chat_box.input.placeholder": "إرسال رسالة…",
"chat_panels.main_window.empty": "لا توجد رسائل، لبدء المحادثات زُر المِلف الشخصي لمستخدم ما.",
"chat_panels.main_window.title": "المحادثات",
"chat_window.close": "إغلاق المحادثة",
"chats.actions.delete": "حذف الرسالة", "chats.actions.delete": "حذف الرسالة",
"chats.actions.more": "المزيد", "chats.actions.more": "المزيد",
"chats.actions.report": "الإبلاغ عن المستخدم", "chats.actions.report": "الإبلاغ عن المستخدم",
"chats.attachment": "مُرفق",
"chats.attachment_image": "صورة",
"chats.audio_toggle_off": "الإشعارات الصوتية غير مُفعّلة",
"chats.audio_toggle_on": "الإشعارات الصوتية مُفعّلة",
"chats.dividers.today": "اليوم", "chats.dividers.today": "اليوم",
"chats.search_placeholder": "بَدْء دردشة مع…", "chats.search_placeholder": "بَدْء دردشة مع…",
"column.admin.awaiting_approval": "في انتظار الموافقة", "column.admin.awaiting_approval": "في انتظار الموافقة",
@ -270,13 +260,11 @@
"column.public": "الاتحاد الاجتماعي", "column.public": "الاتحاد الاجتماعي",
"column.reactions": "تفاعلات", "column.reactions": "تفاعلات",
"column.reblogs": "إعادة النشر", "column.reblogs": "إعادة النشر",
"column.remote": "الاتحاد الاجتماعي",
"column.scheduled_statuses": "منشورات مُجدولة", "column.scheduled_statuses": "منشورات مُجدولة",
"column.search": "البحث", "column.search": "البحث",
"column.settings_store": "مخزن الإعدادات", "column.settings_store": "مخزن الإعدادات",
"column.soapbox_config": "تهيئة بسّام", "column.soapbox_config": "تهيئة بسّام",
"column.test": "تجربة الخط الزمني", "column.test": "تجربة الخط الزمني",
"column_back_button.label": "الرجوع",
"column_forbidden.body": "ليست لديك الصلاحيات للدخول إلى هذه الصفحة.", "column_forbidden.body": "ليست لديك الصلاحيات للدخول إلى هذه الصفحة.",
"column_forbidden.title": "محظور", "column_forbidden.title": "محظور",
"common.cancel": "إلغاء", "common.cancel": "إلغاء",
@ -372,8 +360,6 @@
"confirmations.scheduled_status_delete.heading": "إلغاء جدولة المنشور", "confirmations.scheduled_status_delete.heading": "إلغاء جدولة المنشور",
"confirmations.scheduled_status_delete.message": "هل تود حقا حذف هذا المنشور المجدول", "confirmations.scheduled_status_delete.message": "هل تود حقا حذف هذا المنشور المجدول",
"confirmations.unfollow.confirm": "إلغاء المتابعة", "confirmations.unfollow.confirm": "إلغاء المتابعة",
"confirmations.unfollow.heading": "إلغاء متابعة {name}",
"confirmations.unfollow.message": "هل تود حقًّا إلغاء متابعة {name}؟",
"crypto_donate.explanation_box.message": "{siteTitle} يقبل العملات الرقمية . بإمكانك التبرع عبر أي من هذه العناوين في الأسفل . شكرا لدعمك!", "crypto_donate.explanation_box.message": "{siteTitle} يقبل العملات الرقمية . بإمكانك التبرع عبر أي من هذه العناوين في الأسفل . شكرا لدعمك!",
"crypto_donate.explanation_box.title": "يتم إرسال العملات الرقمية", "crypto_donate.explanation_box.title": "يتم إرسال العملات الرقمية",
"crypto_donate_panel.actions.view": "انقر لإظهار {count} {count, plural, one {wallet} other {wallets}}", "crypto_donate_panel.actions.view": "انقر لإظهار {count} {count, plural, one {wallet} other {wallets}}",
@ -822,7 +808,6 @@
"preferences.fields.display_media.default": "اخف الوسائط المصنفة بحساس", "preferences.fields.display_media.default": "اخف الوسائط المصنفة بحساس",
"preferences.fields.display_media.hide_all": "اخف جميع الوسائط", "preferences.fields.display_media.hide_all": "اخف جميع الوسائط",
"preferences.fields.display_media.show_all": "اظهر جميع الوسائط", "preferences.fields.display_media.show_all": "اظهر جميع الوسائط",
"preferences.fields.dyslexic_font_label": "وضع عسر القراءة",
"preferences.fields.expand_spoilers_label": "توسيع المنشورات المعلّمة بتحذير دائمًا", "preferences.fields.expand_spoilers_label": "توسيع المنشورات المعلّمة بتحذير دائمًا",
"preferences.fields.language_label": "لغة الواجهة", "preferences.fields.language_label": "لغة الواجهة",
"preferences.fields.media_display_label": "عرض الوسائط", "preferences.fields.media_display_label": "عرض الوسائط",
@ -834,7 +819,6 @@
"preferences.fields.underline_links_label": "إظهار الروابط في المنشورات من خلال وضع خط تحت الرابط", "preferences.fields.underline_links_label": "إظهار الروابط في المنشورات من خلال وضع خط تحت الرابط",
"preferences.fields.unfollow_modal_label": "أظهار إشعار لتأكيد إلغاء المتابعة قبل التنفيذ", "preferences.fields.unfollow_modal_label": "أظهار إشعار لتأكيد إلغاء المتابعة قبل التنفيذ",
"preferences.hints.demetricator": "تقليل قلق التواصل الاجتماعي من خلال إخفاء جميع الأرقام في المنصّة.", "preferences.hints.demetricator": "تقليل قلق التواصل الاجتماعي من خلال إخفاء جميع الأرقام في المنصّة.",
"preferences.hints.feed": "في الخط الزمني الخاص بك",
"preferences.notifications.advanced": "عرض جميع تصنيفات الإشعارات", "preferences.notifications.advanced": "عرض جميع تصنيفات الإشعارات",
"preferences.options.content_type_markdown": "ماركداون", "preferences.options.content_type_markdown": "ماركداون",
"preferences.options.content_type_plaintext": "نص عادي", "preferences.options.content_type_plaintext": "نص عادي",
@ -1016,7 +1000,6 @@
"sms_verification.sent.body": "لقد أرسلنا لك رمزًا مكونًا من 6 أرقام عبر رسالة نصية قصيرة. رجاءً أدخله في الأسفل.", "sms_verification.sent.body": "لقد أرسلنا لك رمزًا مكونًا من 6 أرقام عبر رسالة نصية قصيرة. رجاءً أدخله في الأسفل.",
"sms_verification.sent.header": "تأكيد الرمز", "sms_verification.sent.header": "تأكيد الرمز",
"sms_verification.success": "تم إرسال رمز التحقق إلى رقم هاتفك.", "sms_verification.success": "تم إرسال رمز التحقق إلى رقم هاتفك.",
"toast.view": "عرض",
"soapbox_config.authenticated_profile_hint": "على المستخدمين أن يُسجلوا دخولهم كي يتمكنوا من عرض الردود والوسائط على الحسابات الشخصية للمستخدمين.", "soapbox_config.authenticated_profile_hint": "على المستخدمين أن يُسجلوا دخولهم كي يتمكنوا من عرض الردود والوسائط على الحسابات الشخصية للمستخدمين.",
"soapbox_config.authenticated_profile_label": "حسابات شخصية تتطلب تسجيل الدخول لتصفحها", "soapbox_config.authenticated_profile_label": "حسابات شخصية تتطلب تسجيل الدخول لتصفحها",
"soapbox_config.copyright_footer.meta_fields.label_placeholder": "حاشية حقوق الطبع والنشر", "soapbox_config.copyright_footer.meta_fields.label_placeholder": "حاشية حقوق الطبع والنشر",
@ -1029,8 +1012,6 @@
"soapbox_config.display_fqn_label": "عرض اسم النطاق للحسابات المحلية (مثال zaki@instance)", "soapbox_config.display_fqn_label": "عرض اسم النطاق للحسابات المحلية (مثال zaki@instance)",
"soapbox_config.feed_injection_hint": "حقن الخط الزمني بمحتوى إضافي مثل حسابات مقترحة.", "soapbox_config.feed_injection_hint": "حقن الخط الزمني بمحتوى إضافي مثل حسابات مقترحة.",
"soapbox_config.feed_injection_label": "حقن الخط الزمني", "soapbox_config.feed_injection_label": "حقن الخط الزمني",
"soapbox_config.fields.accent_color_label": "لون التمييز",
"soapbox_config.fields.brand_color_label": "لون العلامة التجارية",
"soapbox_config.fields.crypto_addresses_label": "عناوين العملات الافتراضية", "soapbox_config.fields.crypto_addresses_label": "عناوين العملات الافتراضية",
"soapbox_config.fields.home_footer_fields_label": "العناصر في حاشية الصفحة الرئيسية", "soapbox_config.fields.home_footer_fields_label": "العناصر في حاشية الصفحة الرئيسية",
"soapbox_config.fields.logo_label": "الشعار", "soapbox_config.fields.logo_label": "الشعار",
@ -1139,7 +1120,6 @@
"sw.update_text": "هناك تحديث متوفر", "sw.update_text": "هناك تحديث متوفر",
"sw.url": "رابط الإسكربت", "sw.url": "رابط الإسكربت",
"tabs_bar.all": "الكل", "tabs_bar.all": "الكل",
"tabs_bar.chats": "المحادثات",
"tabs_bar.dashboard": "لوحة التحكم", "tabs_bar.dashboard": "لوحة التحكم",
"tabs_bar.fediverse": "الكون الفيدرالي الإجتماعي", "tabs_bar.fediverse": "الكون الفيدرالي الإجتماعي",
"tabs_bar.home": "الرئيسية", "tabs_bar.home": "الرئيسية",
@ -1161,10 +1141,10 @@
"time_remaining.minutes": "{number, plural, one {# دقيقة} other {# دقائق}} متبقية", "time_remaining.minutes": "{number, plural, one {# دقيقة} other {# دقائق}} متبقية",
"time_remaining.moments": "لحظات متبقية", "time_remaining.moments": "لحظات متبقية",
"time_remaining.seconds": "{number, plural, one {# ثانية} other {# ثوانٍ}} متبقية", "time_remaining.seconds": "{number, plural, one {# ثانية} other {# ثوانٍ}} متبقية",
"toast.view": "عرض",
"trends.count_by_accounts": "{count} {rawCount, plural, one {person} آخرون {people}} يتحدثون", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} آخرون {people}} يتحدثون",
"trends.title": "الشائع", "trends.title": "الشائع",
"trendsPanel.viewAll": "إظهار الكل", "trendsPanel.viewAll": "إظهار الكل",
"ui.beforeunload": "محتوى المسودة سيضيع اذا خرجت",
"unauthorized_modal.text": "يجب عليك تسجيل الدخول لتتمكن من القيام بذلك.", "unauthorized_modal.text": "يجب عليك تسجيل الدخول لتتمكن من القيام بذلك.",
"unauthorized_modal.title": "التسجيل في {site_title}", "unauthorized_modal.title": "التسجيل في {site_title}",
"upload_area.title": "اسحب ملف وافلته لتحميله", "upload_area.title": "اسحب ملف وافلته لتحميله",

@ -147,7 +147,6 @@
"alert.unexpected.links.support": "Support", "alert.unexpected.links.support": "Support",
"alert.unexpected.message": "Asocedió un fallu inesperáu.", "alert.unexpected.message": "Asocedió un fallu inesperáu.",
"alert.unexpected.return_home": "Return Home", "alert.unexpected.return_home": "Return Home",
"alert.unexpected.title": "¡Ups!",
"aliases.account.add": "Create alias", "aliases.account.add": "Create alias",
"aliases.account_label": "Old account:", "aliases.account_label": "Old account:",
"aliases.aliases_list_delete": "Unlink alias", "aliases.aliases_list_delete": "Unlink alias",
@ -186,18 +185,9 @@
"bundle_modal_error.message": "Something went wrong while loading this modal.", "bundle_modal_error.message": "Something went wrong while loading this modal.",
"bundle_modal_error.retry": "Try again", "bundle_modal_error.retry": "Try again",
"card.back.label": "Back", "card.back.label": "Back",
"chat_box.actions.send": "Send",
"chat_box.input.placeholder": "Send a message…",
"chat_panels.main_window.empty": "No chats found. To start a chat, visit a user's profile.",
"chat_panels.main_window.title": "Chats",
"chat_window.close": "Close chat",
"chats.actions.delete": "Delete message", "chats.actions.delete": "Delete message",
"chats.actions.more": "More", "chats.actions.more": "More",
"chats.actions.report": "Report user", "chats.actions.report": "Report user",
"chats.attachment": "Attachment",
"chats.attachment_image": "Image",
"chats.audio_toggle_off": "Audio notification off",
"chats.audio_toggle_on": "Audio notification on",
"chats.dividers.today": "Today", "chats.dividers.today": "Today",
"chats.search_placeholder": "Start a chat with…", "chats.search_placeholder": "Start a chat with…",
"column.admin.awaiting_approval": "Awaiting Approval", "column.admin.awaiting_approval": "Awaiting Approval",
@ -270,13 +260,11 @@
"column.public": "Llinia temporal federada", "column.public": "Llinia temporal federada",
"column.reactions": "Reactions", "column.reactions": "Reactions",
"column.reblogs": "Reposts", "column.reblogs": "Reposts",
"column.remote": "Federated timeline",
"column.scheduled_statuses": "Scheduled Posts", "column.scheduled_statuses": "Scheduled Posts",
"column.search": "Search", "column.search": "Search",
"column.settings_store": "Settings store", "column.settings_store": "Settings store",
"column.soapbox_config": "Soapbox config", "column.soapbox_config": "Soapbox config",
"column.test": "Test timeline", "column.test": "Test timeline",
"column_back_button.label": "Atrás",
"column_forbidden.body": "You do not have permission to access this page.", "column_forbidden.body": "You do not have permission to access this page.",
"column_forbidden.title": "Forbidden", "column_forbidden.title": "Forbidden",
"common.cancel": "Cancel", "common.cancel": "Cancel",
@ -372,8 +360,6 @@
"confirmations.scheduled_status_delete.heading": "Cancel scheduled post", "confirmations.scheduled_status_delete.heading": "Cancel scheduled post",
"confirmations.scheduled_status_delete.message": "Are you sure you want to cancel this scheduled post?", "confirmations.scheduled_status_delete.message": "Are you sure you want to cancel this scheduled post?",
"confirmations.unfollow.confirm": "Unfollow", "confirmations.unfollow.confirm": "Unfollow",
"confirmations.unfollow.heading": "Unfollow {name}",
"confirmations.unfollow.message": "¿De xuru que quies dexar de siguir a {name}?",
"crypto_donate.explanation_box.message": "{siteTitle} accepts cryptocurrency donations. You may send a donation to any of the addresses below. Thank you for your support!", "crypto_donate.explanation_box.message": "{siteTitle} accepts cryptocurrency donations. You may send a donation to any of the addresses below. Thank you for your support!",
"crypto_donate.explanation_box.title": "Sending cryptocurrency donations", "crypto_donate.explanation_box.title": "Sending cryptocurrency donations",
"crypto_donate_panel.actions.view": "Click to see {count} {count, plural, one {wallet} other {wallets}}", "crypto_donate_panel.actions.view": "Click to see {count} {count, plural, one {wallet} other {wallets}}",
@ -822,7 +808,6 @@
"preferences.fields.display_media.default": "Hide media marked as sensitive", "preferences.fields.display_media.default": "Hide media marked as sensitive",
"preferences.fields.display_media.hide_all": "Always hide media", "preferences.fields.display_media.hide_all": "Always hide media",
"preferences.fields.display_media.show_all": "Always show media", "preferences.fields.display_media.show_all": "Always show media",
"preferences.fields.dyslexic_font_label": "Dyslexic mode",
"preferences.fields.expand_spoilers_label": "Always expand posts marked with content warnings", "preferences.fields.expand_spoilers_label": "Always expand posts marked with content warnings",
"preferences.fields.language_label": "Language", "preferences.fields.language_label": "Language",
"preferences.fields.media_display_label": "Media display", "preferences.fields.media_display_label": "Media display",
@ -834,7 +819,6 @@
"preferences.fields.underline_links_label": "Always underline links in posts", "preferences.fields.underline_links_label": "Always underline links in posts",
"preferences.fields.unfollow_modal_label": "Show confirmation dialog before unfollowing someone", "preferences.fields.unfollow_modal_label": "Show confirmation dialog before unfollowing someone",
"preferences.hints.demetricator": "Decrease social media anxiety by hiding all numbers from the site.", "preferences.hints.demetricator": "Decrease social media anxiety by hiding all numbers from the site.",
"preferences.hints.feed": "In your home feed",
"preferences.notifications.advanced": "Show all notification categories", "preferences.notifications.advanced": "Show all notification categories",
"preferences.options.content_type_markdown": "Markdown", "preferences.options.content_type_markdown": "Markdown",
"preferences.options.content_type_plaintext": "Plain text", "preferences.options.content_type_plaintext": "Plain text",
@ -1016,7 +1000,6 @@
"sms_verification.sent.body": "We sent you a 6-digit code via SMS. Enter it below.", "sms_verification.sent.body": "We sent you a 6-digit code via SMS. Enter it below.",
"sms_verification.sent.header": "Verification code", "sms_verification.sent.header": "Verification code",
"sms_verification.success": "A verification code has been sent to your phone number.", "sms_verification.success": "A verification code has been sent to your phone number.",
"toast.view": "View",
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
"soapbox_config.authenticated_profile_label": "Profiles require authentication", "soapbox_config.authenticated_profile_label": "Profiles require authentication",
"soapbox_config.copyright_footer.meta_fields.label_placeholder": "Copyright footer", "soapbox_config.copyright_footer.meta_fields.label_placeholder": "Copyright footer",
@ -1029,8 +1012,6 @@
"soapbox_config.display_fqn_label": "Display domain (eg @user@domain) for local accounts.", "soapbox_config.display_fqn_label": "Display domain (eg @user@domain) for local accounts.",
"soapbox_config.feed_injection_hint": "Inject the feed with additional content, such as suggested profiles.", "soapbox_config.feed_injection_hint": "Inject the feed with additional content, such as suggested profiles.",
"soapbox_config.feed_injection_label": "Feed injection", "soapbox_config.feed_injection_label": "Feed injection",
"soapbox_config.fields.accent_color_label": "Accent color",
"soapbox_config.fields.brand_color_label": "Brand color",
"soapbox_config.fields.crypto_addresses_label": "Cryptocurrency addresses", "soapbox_config.fields.crypto_addresses_label": "Cryptocurrency addresses",
"soapbox_config.fields.home_footer_fields_label": "Home footer items", "soapbox_config.fields.home_footer_fields_label": "Home footer items",
"soapbox_config.fields.logo_label": "Logo", "soapbox_config.fields.logo_label": "Logo",
@ -1139,7 +1120,6 @@
"sw.update_text": "An update is available.", "sw.update_text": "An update is available.",
"sw.url": "Script URL", "sw.url": "Script URL",
"tabs_bar.all": "All", "tabs_bar.all": "All",
"tabs_bar.chats": "Chats",
"tabs_bar.dashboard": "Dashboard", "tabs_bar.dashboard": "Dashboard",
"tabs_bar.fediverse": "Fediverse", "tabs_bar.fediverse": "Fediverse",
"tabs_bar.home": "Aniciu", "tabs_bar.home": "Aniciu",
@ -1161,10 +1141,10 @@
"time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left", "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left",
"time_remaining.moments": "Moments remaining", "time_remaining.moments": "Moments remaining",
"time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left",
"toast.view": "View",
"trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking",
"trends.title": "Trends", "trends.title": "Trends",
"trendsPanel.viewAll": "View all", "trendsPanel.viewAll": "View all",
"ui.beforeunload": "El borrador va perdese si coles de Soapbox.",
"unauthorized_modal.text": "You need to be logged in to do that.", "unauthorized_modal.text": "You need to be logged in to do that.",
"unauthorized_modal.title": "Sign up for {site_title}", "unauthorized_modal.title": "Sign up for {site_title}",
"upload_area.title": "Drag & drop to upload", "upload_area.title": "Drag & drop to upload",

@ -147,7 +147,6 @@
"alert.unexpected.links.support": "Support", "alert.unexpected.links.support": "Support",
"alert.unexpected.message": "An unexpected error occurred.", "alert.unexpected.message": "An unexpected error occurred.",
"alert.unexpected.return_home": "Return Home", "alert.unexpected.return_home": "Return Home",
"alert.unexpected.title": "Oops!",
"aliases.account.add": "Create alias", "aliases.account.add": "Create alias",
"aliases.account_label": "Old account:", "aliases.account_label": "Old account:",
"aliases.aliases_list_delete": "Unlink alias", "aliases.aliases_list_delete": "Unlink alias",
@ -186,18 +185,9 @@
"bundle_modal_error.message": "Something went wrong while loading this modal.", "bundle_modal_error.message": "Something went wrong while loading this modal.",
"bundle_modal_error.retry": "Try again", "bundle_modal_error.retry": "Try again",
"card.back.label": "Back", "card.back.label": "Back",
"chat_box.actions.send": "Send",
"chat_box.input.placeholder": "Send a message…",
"chat_panels.main_window.empty": "No chats found. To start a chat, visit a user's profile.",
"chat_panels.main_window.title": "Chats",
"chat_window.close": "Close chat",
"chats.actions.delete": "Delete message", "chats.actions.delete": "Delete message",
"chats.actions.more": "More", "chats.actions.more": "More",
"chats.actions.report": "Report user", "chats.actions.report": "Report user",
"chats.attachment": "Attachment",
"chats.attachment_image": "Image",
"chats.audio_toggle_off": "Audio notification off",
"chats.audio_toggle_on": "Audio notification on",
"chats.dividers.today": "Today", "chats.dividers.today": "Today",
"chats.search_placeholder": "Start a chat with…", "chats.search_placeholder": "Start a chat with…",
"column.admin.awaiting_approval": "Awaiting Approval", "column.admin.awaiting_approval": "Awaiting Approval",
@ -270,13 +260,11 @@
"column.public": "Публичен канал", "column.public": "Публичен канал",
"column.reactions": "Reactions", "column.reactions": "Reactions",
"column.reblogs": "Reposts", "column.reblogs": "Reposts",
"column.remote": "Federated timeline",
"column.scheduled_statuses": "Scheduled Posts", "column.scheduled_statuses": "Scheduled Posts",
"column.search": "Search", "column.search": "Search",
"column.settings_store": "Settings store", "column.settings_store": "Settings store",
"column.soapbox_config": "Soapbox config", "column.soapbox_config": "Soapbox config",
"column.test": "Test timeline", "column.test": "Test timeline",
"column_back_button.label": "Назад",
"column_forbidden.body": "You do not have permission to access this page.", "column_forbidden.body": "You do not have permission to access this page.",
"column_forbidden.title": "Forbidden", "column_forbidden.title": "Forbidden",
"common.cancel": "Cancel", "common.cancel": "Cancel",
@ -372,8 +360,6 @@
"confirmations.scheduled_status_delete.heading": "Cancel scheduled post", "confirmations.scheduled_status_delete.heading": "Cancel scheduled post",
"confirmations.scheduled_status_delete.message": "Are you sure you want to cancel this scheduled post?", "confirmations.scheduled_status_delete.message": "Are you sure you want to cancel this scheduled post?",
"confirmations.unfollow.confirm": "Unfollow", "confirmations.unfollow.confirm": "Unfollow",
"confirmations.unfollow.heading": "Unfollow {name}",
"confirmations.unfollow.message": "Are you sure you want to unfollow {name}?",
"crypto_donate.explanation_box.message": "{siteTitle} accepts cryptocurrency donations. You may send a donation to any of the addresses below. Thank you for your support!", "crypto_donate.explanation_box.message": "{siteTitle} accepts cryptocurrency donations. You may send a donation to any of the addresses below. Thank you for your support!",
"crypto_donate.explanation_box.title": "Sending cryptocurrency donations", "crypto_donate.explanation_box.title": "Sending cryptocurrency donations",
"crypto_donate_panel.actions.view": "Click to see {count} {count, plural, one {wallet} other {wallets}}", "crypto_donate_panel.actions.view": "Click to see {count} {count, plural, one {wallet} other {wallets}}",
@ -822,7 +808,6 @@
"preferences.fields.display_media.default": "Hide media marked as sensitive", "preferences.fields.display_media.default": "Hide media marked as sensitive",
"preferences.fields.display_media.hide_all": "Always hide media", "preferences.fields.display_media.hide_all": "Always hide media",
"preferences.fields.display_media.show_all": "Always show media", "preferences.fields.display_media.show_all": "Always show media",
"preferences.fields.dyslexic_font_label": "Dyslexic mode",
"preferences.fields.expand_spoilers_label": "Always expand posts marked with content warnings", "preferences.fields.expand_spoilers_label": "Always expand posts marked with content warnings",
"preferences.fields.language_label": "Language", "preferences.fields.language_label": "Language",
"preferences.fields.media_display_label": "Media display", "preferences.fields.media_display_label": "Media display",
@ -834,7 +819,6 @@
"preferences.fields.underline_links_label": "Always underline links in posts", "preferences.fields.underline_links_label": "Always underline links in posts",
"preferences.fields.unfollow_modal_label": "Show confirmation dialog before unfollowing someone", "preferences.fields.unfollow_modal_label": "Show confirmation dialog before unfollowing someone",
"preferences.hints.demetricator": "Decrease social media anxiety by hiding all numbers from the site.", "preferences.hints.demetricator": "Decrease social media anxiety by hiding all numbers from the site.",
"preferences.hints.feed": "In your home feed",
"preferences.notifications.advanced": "Show all notification categories", "preferences.notifications.advanced": "Show all notification categories",
"preferences.options.content_type_markdown": "Markdown", "preferences.options.content_type_markdown": "Markdown",
"preferences.options.content_type_plaintext": "Plain text", "preferences.options.content_type_plaintext": "Plain text",
@ -1016,7 +1000,6 @@
"sms_verification.sent.body": "We sent you a 6-digit code via SMS. Enter it below.", "sms_verification.sent.body": "We sent you a 6-digit code via SMS. Enter it below.",
"sms_verification.sent.header": "Verification code", "sms_verification.sent.header": "Verification code",
"sms_verification.success": "A verification code has been sent to your phone number.", "sms_verification.success": "A verification code has been sent to your phone number.",
"toast.view": "View",
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
"soapbox_config.authenticated_profile_label": "Profiles require authentication", "soapbox_config.authenticated_profile_label": "Profiles require authentication",
"soapbox_config.copyright_footer.meta_fields.label_placeholder": "Copyright footer", "soapbox_config.copyright_footer.meta_fields.label_placeholder": "Copyright footer",
@ -1029,8 +1012,6 @@
"soapbox_config.display_fqn_label": "Display domain (eg @user@domain) for local accounts.", "soapbox_config.display_fqn_label": "Display domain (eg @user@domain) for local accounts.",
"soapbox_config.feed_injection_hint": "Inject the feed with additional content, such as suggested profiles.", "soapbox_config.feed_injection_hint": "Inject the feed with additional content, such as suggested profiles.",
"soapbox_config.feed_injection_label": "Feed injection", "soapbox_config.feed_injection_label": "Feed injection",
"soapbox_config.fields.accent_color_label": "Accent color",
"soapbox_config.fields.brand_color_label": "Brand color",
"soapbox_config.fields.crypto_addresses_label": "Cryptocurrency addresses", "soapbox_config.fields.crypto_addresses_label": "Cryptocurrency addresses",
"soapbox_config.fields.home_footer_fields_label": "Home footer items", "soapbox_config.fields.home_footer_fields_label": "Home footer items",
"soapbox_config.fields.logo_label": "Logo", "soapbox_config.fields.logo_label": "Logo",
@ -1139,7 +1120,6 @@
"sw.update_text": "An update is available.", "sw.update_text": "An update is available.",
"sw.url": "Script URL", "sw.url": "Script URL",
"tabs_bar.all": "All", "tabs_bar.all": "All",
"tabs_bar.chats": "Chats",
"tabs_bar.dashboard": "Dashboard", "tabs_bar.dashboard": "Dashboard",
"tabs_bar.fediverse": "Fediverse", "tabs_bar.fediverse": "Fediverse",
"tabs_bar.home": "Начало", "tabs_bar.home": "Начало",
@ -1161,10 +1141,10 @@
"time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left", "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left",
"time_remaining.moments": "Moments remaining", "time_remaining.moments": "Moments remaining",
"time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left",
"toast.view": "View",
"trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking",
"trends.title": "Trends", "trends.title": "Trends",
"trendsPanel.viewAll": "View all", "trendsPanel.viewAll": "View all",
"ui.beforeunload": "Your draft will be lost if you leave.",
"unauthorized_modal.text": "You need to be logged in to do that.", "unauthorized_modal.text": "You need to be logged in to do that.",
"unauthorized_modal.title": "Sign up for {site_title}", "unauthorized_modal.title": "Sign up for {site_title}",
"upload_area.title": "Drag & drop to upload", "upload_area.title": "Drag & drop to upload",

@ -147,7 +147,6 @@
"alert.unexpected.links.support": "Support", "alert.unexpected.links.support": "Support",
"alert.unexpected.message": "অপ্রত্যাশিত একটি সমস্যা হয়েছে।", "alert.unexpected.message": "অপ্রত্যাশিত একটি সমস্যা হয়েছে।",
"alert.unexpected.return_home": "Return Home", "alert.unexpected.return_home": "Return Home",
"alert.unexpected.title": "ওহো!",
"aliases.account.add": "Create alias", "aliases.account.add": "Create alias",
"aliases.account_label": "Old account:", "aliases.account_label": "Old account:",
"aliases.aliases_list_delete": "Unlink alias", "aliases.aliases_list_delete": "Unlink alias",
@ -186,18 +185,9 @@
"bundle_modal_error.message": "এই অংশটি দেখাতে যেয়ে কোনো সমস্যা হয়েছে।", "bundle_modal_error.message": "এই অংশটি দেখাতে যেয়ে কোনো সমস্যা হয়েছে।",
"bundle_modal_error.retry": "আবার চেষ্টা করুন", "bundle_modal_error.retry": "আবার চেষ্টা করুন",
"card.back.label": "Back", "card.back.label": "Back",
"chat_box.actions.send": "Send",
"chat_box.input.placeholder": "Send a message…",
"chat_panels.main_window.empty": "No chats found. To start a chat, visit a user's profile.",
"chat_panels.main_window.title": "Chats",
"chat_window.close": "Close chat",
"chats.actions.delete": "Delete message", "chats.actions.delete": "Delete message",
"chats.actions.more": "More", "chats.actions.more": "More",
"chats.actions.report": "Report user", "chats.actions.report": "Report user",
"chats.attachment": "Attachment",
"chats.attachment_image": "Image",
"chats.audio_toggle_off": "Audio notification off",
"chats.audio_toggle_on": "Audio notification on",
"chats.dividers.today": "Today", "chats.dividers.today": "Today",
"chats.search_placeholder": "Start a chat with…", "chats.search_placeholder": "Start a chat with…",
"column.admin.awaiting_approval": "Awaiting Approval", "column.admin.awaiting_approval": "Awaiting Approval",
@ -270,13 +260,11 @@
"column.public": "যুক্ত সময়রেখা", "column.public": "যুক্ত সময়রেখা",
"column.reactions": "Reactions", "column.reactions": "Reactions",
"column.reblogs": "Reposts", "column.reblogs": "Reposts",
"column.remote": "Federated timeline",
"column.scheduled_statuses": "Scheduled Posts", "column.scheduled_statuses": "Scheduled Posts",
"column.search": "Search", "column.search": "Search",
"column.settings_store": "Settings store", "column.settings_store": "Settings store",
"column.soapbox_config": "Soapbox config", "column.soapbox_config": "Soapbox config",
"column.test": "Test timeline", "column.test": "Test timeline",
"column_back_button.label": "পেছনে",
"column_forbidden.body": "You do not have permission to access this page.", "column_forbidden.body": "You do not have permission to access this page.",
"column_forbidden.title": "Forbidden", "column_forbidden.title": "Forbidden",
"common.cancel": "Cancel", "common.cancel": "Cancel",
@ -372,8 +360,6 @@
"confirmations.scheduled_status_delete.heading": "Cancel scheduled post", "confirmations.scheduled_status_delete.heading": "Cancel scheduled post",
"confirmations.scheduled_status_delete.message": "Are you sure you want to cancel this scheduled post?", "confirmations.scheduled_status_delete.message": "Are you sure you want to cancel this scheduled post?",
"confirmations.unfollow.confirm": "অনুসরণ করা বাতিল করতে", "confirmations.unfollow.confirm": "অনুসরণ করা বাতিল করতে",
"confirmations.unfollow.heading": "Unfollow {name}",
"confirmations.unfollow.message": "আপনি কি নিশ্চিত {name} কে আর অনুসরণ করতে চান না ?",
"crypto_donate.explanation_box.message": "{siteTitle} accepts cryptocurrency donations. You may send a donation to any of the addresses below. Thank you for your support!", "crypto_donate.explanation_box.message": "{siteTitle} accepts cryptocurrency donations. You may send a donation to any of the addresses below. Thank you for your support!",
"crypto_donate.explanation_box.title": "Sending cryptocurrency donations", "crypto_donate.explanation_box.title": "Sending cryptocurrency donations",
"crypto_donate_panel.actions.view": "Click to see {count} {count, plural, one {wallet} other {wallets}}", "crypto_donate_panel.actions.view": "Click to see {count} {count, plural, one {wallet} other {wallets}}",
@ -822,7 +808,6 @@
"preferences.fields.display_media.default": "Hide media marked as sensitive", "preferences.fields.display_media.default": "Hide media marked as sensitive",
"preferences.fields.display_media.hide_all": "Always hide media", "preferences.fields.display_media.hide_all": "Always hide media",
"preferences.fields.display_media.show_all": "Always show media", "preferences.fields.display_media.show_all": "Always show media",
"preferences.fields.dyslexic_font_label": "Dyslexic mode",
"preferences.fields.expand_spoilers_label": "Always expand posts marked with content warnings", "preferences.fields.expand_spoilers_label": "Always expand posts marked with content warnings",
"preferences.fields.language_label": "Language", "preferences.fields.language_label": "Language",
"preferences.fields.media_display_label": "Media display", "preferences.fields.media_display_label": "Media display",
@ -834,7 +819,6 @@
"preferences.fields.underline_links_label": "Always underline links in posts", "preferences.fields.underline_links_label": "Always underline links in posts",
"preferences.fields.unfollow_modal_label": "Show confirmation dialog before unfollowing someone", "preferences.fields.unfollow_modal_label": "Show confirmation dialog before unfollowing someone",
"preferences.hints.demetricator": "Decrease social media anxiety by hiding all numbers from the site.", "preferences.hints.demetricator": "Decrease social media anxiety by hiding all numbers from the site.",
"preferences.hints.feed": "In your home feed",
"preferences.notifications.advanced": "Show all notification categories", "preferences.notifications.advanced": "Show all notification categories",
"preferences.options.content_type_markdown": "Markdown", "preferences.options.content_type_markdown": "Markdown",
"preferences.options.content_type_plaintext": "Plain text", "preferences.options.content_type_plaintext": "Plain text",
@ -1016,7 +1000,6 @@
"sms_verification.sent.body": "We sent you a 6-digit code via SMS. Enter it below.", "sms_verification.sent.body": "We sent you a 6-digit code via SMS. Enter it below.",
"sms_verification.sent.header": "Verification code", "sms_verification.sent.header": "Verification code",
"sms_verification.success": "A verification code has been sent to your phone number.", "sms_verification.success": "A verification code has been sent to your phone number.",
"toast.view": "View",
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
"soapbox_config.authenticated_profile_label": "Profiles require authentication", "soapbox_config.authenticated_profile_label": "Profiles require authentication",
"soapbox_config.copyright_footer.meta_fields.label_placeholder": "Copyright footer", "soapbox_config.copyright_footer.meta_fields.label_placeholder": "Copyright footer",
@ -1029,8 +1012,6 @@
"soapbox_config.display_fqn_label": "Display domain (eg @user@domain) for local accounts.", "soapbox_config.display_fqn_label": "Display domain (eg @user@domain) for local accounts.",
"soapbox_config.feed_injection_hint": "Inject the feed with additional content, such as suggested profiles.", "soapbox_config.feed_injection_hint": "Inject the feed with additional content, such as suggested profiles.",
"soapbox_config.feed_injection_label": "Feed injection", "soapbox_config.feed_injection_label": "Feed injection",
"soapbox_config.fields.accent_color_label": "Accent color",
"soapbox_config.fields.brand_color_label": "Brand color",
"soapbox_config.fields.crypto_addresses_label": "Cryptocurrency addresses", "soapbox_config.fields.crypto_addresses_label": "Cryptocurrency addresses",
"soapbox_config.fields.home_footer_fields_label": "Home footer items", "soapbox_config.fields.home_footer_fields_label": "Home footer items",
"soapbox_config.fields.logo_label": "Logo", "soapbox_config.fields.logo_label": "Logo",
@ -1139,7 +1120,6 @@
"sw.update_text": "An update is available.", "sw.update_text": "An update is available.",
"sw.url": "Script URL", "sw.url": "Script URL",
"tabs_bar.all": "All", "tabs_bar.all": "All",
"tabs_bar.chats": "Chats",
"tabs_bar.dashboard": "Dashboard", "tabs_bar.dashboard": "Dashboard",
"tabs_bar.fediverse": "Fediverse", "tabs_bar.fediverse": "Fediverse",
"tabs_bar.home": "বাড়ি", "tabs_bar.home": "বাড়ি",
@ -1161,10 +1141,10 @@
"time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} বাকি আছে", "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} বাকি আছে",
"time_remaining.moments": "সময় বাকি আছে", "time_remaining.moments": "সময় বাকি আছে",
"time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} বাকি আছে", "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} বাকি আছে",
"toast.view": "View",
"trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} কথা বলছে", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} কথা বলছে",
"trends.title": "Trends", "trends.title": "Trends",
"trendsPanel.viewAll": "View all", "trendsPanel.viewAll": "View all",
"ui.beforeunload": "যে পর্যন্ত এটা লেখা হয়েছে, Soapbox থেকে চলে গেলে এটা মুছে যাবে।",
"unauthorized_modal.text": "You need to be logged in to do that.", "unauthorized_modal.text": "You need to be logged in to do that.",
"unauthorized_modal.title": "Sign up for {site_title}", "unauthorized_modal.title": "Sign up for {site_title}",
"upload_area.title": "টেনে এখানে ছেড়ে দিলে এখানে যুক্ত করা যাবে", "upload_area.title": "টেনে এখানে ছেড়ে দিলে এখানে যুক্ত করা যাবে",

@ -147,7 +147,6 @@
"alert.unexpected.links.support": "Support", "alert.unexpected.links.support": "Support",
"alert.unexpected.message": "Ur fazi dic'hortozet zo degouezhet.", "alert.unexpected.message": "Ur fazi dic'hortozet zo degouezhet.",
"alert.unexpected.return_home": "Return Home", "alert.unexpected.return_home": "Return Home",
"alert.unexpected.title": "C'hem !",
"aliases.account.add": "Create alias", "aliases.account.add": "Create alias",
"aliases.account_label": "Old account:", "aliases.account_label": "Old account:",
"aliases.aliases_list_delete": "Unlink alias", "aliases.aliases_list_delete": "Unlink alias",
@ -186,18 +185,9 @@
"bundle_modal_error.message": "Something went wrong while loading this modal.", "bundle_modal_error.message": "Something went wrong while loading this modal.",
"bundle_modal_error.retry": "Klask endro", "bundle_modal_error.retry": "Klask endro",
"card.back.label": "Back", "card.back.label": "Back",
"chat_box.actions.send": "Send",
"chat_box.input.placeholder": "Send a message…",
"chat_panels.main_window.empty": "No chats found. To start a chat, visit a user's profile.",
"chat_panels.main_window.title": "Chats",
"chat_window.close": "Close chat",
"chats.actions.delete": "Delete message", "chats.actions.delete": "Delete message",
"chats.actions.more": "More", "chats.actions.more": "More",
"chats.actions.report": "Report user", "chats.actions.report": "Report user",
"chats.attachment": "Attachment",
"chats.attachment_image": "Image",
"chats.audio_toggle_off": "Audio notification off",
"chats.audio_toggle_on": "Audio notification on",
"chats.dividers.today": "Today", "chats.dividers.today": "Today",
"chats.search_placeholder": "Start a chat with…", "chats.search_placeholder": "Start a chat with…",
"column.admin.awaiting_approval": "Awaiting Approval", "column.admin.awaiting_approval": "Awaiting Approval",
@ -270,13 +260,11 @@
"column.public": "Federated timeline", "column.public": "Federated timeline",
"column.reactions": "Reactions", "column.reactions": "Reactions",
"column.reblogs": "Reposts", "column.reblogs": "Reposts",
"column.remote": "Federated timeline",
"column.scheduled_statuses": "Scheduled Posts", "column.scheduled_statuses": "Scheduled Posts",
"column.search": "Search", "column.search": "Search",
"column.settings_store": "Settings store", "column.settings_store": "Settings store",
"column.soapbox_config": "Soapbox config", "column.soapbox_config": "Soapbox config",
"column.test": "Test timeline", "column.test": "Test timeline",
"column_back_button.label": "Back",
"column_forbidden.body": "You do not have permission to access this page.", "column_forbidden.body": "You do not have permission to access this page.",
"column_forbidden.title": "Forbidden", "column_forbidden.title": "Forbidden",
"common.cancel": "Cancel", "common.cancel": "Cancel",
@ -372,8 +360,6 @@
"confirmations.scheduled_status_delete.heading": "Cancel scheduled post", "confirmations.scheduled_status_delete.heading": "Cancel scheduled post",
"confirmations.scheduled_status_delete.message": "Are you sure you want to cancel this scheduled post?", "confirmations.scheduled_status_delete.message": "Are you sure you want to cancel this scheduled post?",
"confirmations.unfollow.confirm": "Unfollow", "confirmations.unfollow.confirm": "Unfollow",
"confirmations.unfollow.heading": "Unfollow {name}",
"confirmations.unfollow.message": "Are you sure you want to unfollow {name}?",
"crypto_donate.explanation_box.message": "{siteTitle} accepts cryptocurrency donations. You may send a donation to any of the addresses below. Thank you for your support!", "crypto_donate.explanation_box.message": "{siteTitle} accepts cryptocurrency donations. You may send a donation to any of the addresses below. Thank you for your support!",
"crypto_donate.explanation_box.title": "Sending cryptocurrency donations", "crypto_donate.explanation_box.title": "Sending cryptocurrency donations",
"crypto_donate_panel.actions.view": "Click to see {count} {count, plural, one {wallet} other {wallets}}", "crypto_donate_panel.actions.view": "Click to see {count} {count, plural, one {wallet} other {wallets}}",
@ -822,7 +808,6 @@
"preferences.fields.display_media.default": "Hide media marked as sensitive", "preferences.fields.display_media.default": "Hide media marked as sensitive",
"preferences.fields.display_media.hide_all": "Always hide media", "preferences.fields.display_media.hide_all": "Always hide media",
"preferences.fields.display_media.show_all": "Always show media", "preferences.fields.display_media.show_all": "Always show media",
"preferences.fields.dyslexic_font_label": "Dyslexic mode",
"preferences.fields.expand_spoilers_label": "Always expand posts marked with content warnings", "preferences.fields.expand_spoilers_label": "Always expand posts marked with content warnings",
"preferences.fields.language_label": "Language", "preferences.fields.language_label": "Language",
"preferences.fields.media_display_label": "Media display", "preferences.fields.media_display_label": "Media display",
@ -834,7 +819,6 @@
"preferences.fields.underline_links_label": "Always underline links in posts", "preferences.fields.underline_links_label": "Always underline links in posts",
"preferences.fields.unfollow_modal_label": "Show confirmation dialog before unfollowing someone", "preferences.fields.unfollow_modal_label": "Show confirmation dialog before unfollowing someone",
"preferences.hints.demetricator": "Decrease social media anxiety by hiding all numbers from the site.", "preferences.hints.demetricator": "Decrease social media anxiety by hiding all numbers from the site.",
"preferences.hints.feed": "In your home feed",
"preferences.notifications.advanced": "Show all notification categories", "preferences.notifications.advanced": "Show all notification categories",
"preferences.options.content_type_markdown": "Markdown", "preferences.options.content_type_markdown": "Markdown",
"preferences.options.content_type_plaintext": "Plain text", "preferences.options.content_type_plaintext": "Plain text",
@ -1016,7 +1000,6 @@
"sms_verification.sent.body": "We sent you a 6-digit code via SMS. Enter it below.", "sms_verification.sent.body": "We sent you a 6-digit code via SMS. Enter it below.",
"sms_verification.sent.header": "Verification code", "sms_verification.sent.header": "Verification code",
"sms_verification.success": "A verification code has been sent to your phone number.", "sms_verification.success": "A verification code has been sent to your phone number.",
"toast.view": "View",
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
"soapbox_config.authenticated_profile_label": "Profiles require authentication", "soapbox_config.authenticated_profile_label": "Profiles require authentication",
"soapbox_config.copyright_footer.meta_fields.label_placeholder": "Copyright footer", "soapbox_config.copyright_footer.meta_fields.label_placeholder": "Copyright footer",
@ -1029,8 +1012,6 @@
"soapbox_config.display_fqn_label": "Display domain (eg @user@domain) for local accounts.", "soapbox_config.display_fqn_label": "Display domain (eg @user@domain) for local accounts.",
"soapbox_config.feed_injection_hint": "Inject the feed with additional content, such as suggested profiles.", "soapbox_config.feed_injection_hint": "Inject the feed with additional content, such as suggested profiles.",
"soapbox_config.feed_injection_label": "Feed injection", "soapbox_config.feed_injection_label": "Feed injection",
"soapbox_config.fields.accent_color_label": "Accent color",
"soapbox_config.fields.brand_color_label": "Brand color",
"soapbox_config.fields.crypto_addresses_label": "Cryptocurrency addresses", "soapbox_config.fields.crypto_addresses_label": "Cryptocurrency addresses",
"soapbox_config.fields.home_footer_fields_label": "Home footer items", "soapbox_config.fields.home_footer_fields_label": "Home footer items",
"soapbox_config.fields.logo_label": "Logo", "soapbox_config.fields.logo_label": "Logo",
@ -1139,7 +1120,6 @@
"sw.update_text": "An update is available.", "sw.update_text": "An update is available.",
"sw.url": "Script URL", "sw.url": "Script URL",
"tabs_bar.all": "All", "tabs_bar.all": "All",
"tabs_bar.chats": "Chats",
"tabs_bar.dashboard": "Dashboard", "tabs_bar.dashboard": "Dashboard",
"tabs_bar.fediverse": "Fediverse", "tabs_bar.fediverse": "Fediverse",
"tabs_bar.home": "Home", "tabs_bar.home": "Home",
@ -1161,10 +1141,10 @@
"time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left", "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left",
"time_remaining.moments": "Moments remaining", "time_remaining.moments": "Moments remaining",
"time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left",
"toast.view": "View",
"trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking",
"trends.title": "Trends", "trends.title": "Trends",
"trendsPanel.viewAll": "View all", "trendsPanel.viewAll": "View all",
"ui.beforeunload": "Your draft will be lost if you leave.",
"unauthorized_modal.text": "You need to be logged in to do that.", "unauthorized_modal.text": "You need to be logged in to do that.",
"unauthorized_modal.title": "Sign up for {site_title}", "unauthorized_modal.title": "Sign up for {site_title}",
"upload_area.title": "Drag & drop to upload", "upload_area.title": "Drag & drop to upload",

@ -147,7 +147,6 @@
"alert.unexpected.links.support": "Support", "alert.unexpected.links.support": "Support",
"alert.unexpected.message": "S'ha produït un error inesperat.", "alert.unexpected.message": "S'ha produït un error inesperat.",
"alert.unexpected.return_home": "Return Home", "alert.unexpected.return_home": "Return Home",
"alert.unexpected.title": "Vaja!",
"aliases.account.add": "Create alias", "aliases.account.add": "Create alias",
"aliases.account_label": "Old account:", "aliases.account_label": "Old account:",
"aliases.aliases_list_delete": "Unlink alias", "aliases.aliases_list_delete": "Unlink alias",
@ -186,18 +185,9 @@
"bundle_modal_error.message": "S'ha produït un error en carregar aquest component.", "bundle_modal_error.message": "S'ha produït un error en carregar aquest component.",
"bundle_modal_error.retry": "Torna-ho a provar", "bundle_modal_error.retry": "Torna-ho a provar",
"card.back.label": "Back", "card.back.label": "Back",
"chat_box.actions.send": "Envia",
"chat_box.input.placeholder": "Envia un missatge…",
"chat_panels.main_window.empty": "Cap xat trobat. Per arrencar un xat, visita el perfil d'un usuari.",
"chat_panels.main_window.title": "Xats",
"chat_window.close": "Close chat",
"chats.actions.delete": "Elimina missatge", "chats.actions.delete": "Elimina missatge",
"chats.actions.more": "Més", "chats.actions.more": "Més",
"chats.actions.report": "Denunciar usuari", "chats.actions.report": "Denunciar usuari",
"chats.attachment": "Attachment",
"chats.attachment_image": "Image",
"chats.audio_toggle_off": "Notificació amb so desactivada",
"chats.audio_toggle_on": "Notificació amb so activada",
"chats.dividers.today": "Avui", "chats.dividers.today": "Avui",
"chats.search_placeholder": "Start a chat with…", "chats.search_placeholder": "Start a chat with…",
"column.admin.awaiting_approval": "Esperant aprovació", "column.admin.awaiting_approval": "Esperant aprovació",
@ -270,13 +260,11 @@
"column.public": "Línia de temps federada", "column.public": "Línia de temps federada",
"column.reactions": "Reactions", "column.reactions": "Reactions",
"column.reblogs": "Reposts", "column.reblogs": "Reposts",
"column.remote": "Línia de temps federada",
"column.scheduled_statuses": "Scheduled Posts", "column.scheduled_statuses": "Scheduled Posts",
"column.search": "Search", "column.search": "Search",
"column.settings_store": "Settings store", "column.settings_store": "Settings store",
"column.soapbox_config": "Configuració de Soapbox", "column.soapbox_config": "Configuració de Soapbox",
"column.test": "Test timeline", "column.test": "Test timeline",
"column_back_button.label": "Enrere",
"column_forbidden.body": "You do not have permission to access this page.", "column_forbidden.body": "You do not have permission to access this page.",
"column_forbidden.title": "Forbidden", "column_forbidden.title": "Forbidden",
"common.cancel": "Cancel", "common.cancel": "Cancel",
@ -372,8 +360,6 @@
"confirmations.scheduled_status_delete.heading": "Cancel scheduled post", "confirmations.scheduled_status_delete.heading": "Cancel scheduled post",
"confirmations.scheduled_status_delete.message": "Are you sure you want to cancel this scheduled post?", "confirmations.scheduled_status_delete.message": "Are you sure you want to cancel this scheduled post?",
"confirmations.unfollow.confirm": "Deixa de seguir", "confirmations.unfollow.confirm": "Deixa de seguir",
"confirmations.unfollow.heading": "Unfollow {name}",
"confirmations.unfollow.message": "Estàs segur que vols deixar de seguir {name}?",
"crypto_donate.explanation_box.message": "{siteTitle} accepts cryptocurrency donations. You may send a donation to any of the addresses below. Thank you for your support!", "crypto_donate.explanation_box.message": "{siteTitle} accepts cryptocurrency donations. You may send a donation to any of the addresses below. Thank you for your support!",
"crypto_donate.explanation_box.title": "Sending cryptocurrency donations", "crypto_donate.explanation_box.title": "Sending cryptocurrency donations",
"crypto_donate_panel.actions.view": "Click to see {count} {count, plural, one {wallet} other {wallets}}", "crypto_donate_panel.actions.view": "Click to see {count} {count, plural, one {wallet} other {wallets}}",
@ -822,7 +808,6 @@
"preferences.fields.display_media.default": "Amaga les imatges marcades com a sensibles", "preferences.fields.display_media.default": "Amaga les imatges marcades com a sensibles",
"preferences.fields.display_media.hide_all": "Oculta sempre les imatges", "preferences.fields.display_media.hide_all": "Oculta sempre les imatges",
"preferences.fields.display_media.show_all": "Mostra sempre les imatges", "preferences.fields.display_media.show_all": "Mostra sempre les imatges",
"preferences.fields.dyslexic_font_label": "Dyslexic mode",
"preferences.fields.expand_spoilers_label": "Expandeix sempre els missatges marcats amb avisos de contingut", "preferences.fields.expand_spoilers_label": "Expandeix sempre els missatges marcats amb avisos de contingut",
"preferences.fields.language_label": "Llengua", "preferences.fields.language_label": "Llengua",
"preferences.fields.media_display_label": "Visualització multimèdia", "preferences.fields.media_display_label": "Visualització multimèdia",
@ -834,7 +819,6 @@
"preferences.fields.underline_links_label": "Always underline links in posts", "preferences.fields.underline_links_label": "Always underline links in posts",
"preferences.fields.unfollow_modal_label": "Show confirmation dialog before unfollowing someone", "preferences.fields.unfollow_modal_label": "Show confirmation dialog before unfollowing someone",
"preferences.hints.demetricator": "Decrease social media anxiety by hiding all numbers from the site.", "preferences.hints.demetricator": "Decrease social media anxiety by hiding all numbers from the site.",
"preferences.hints.feed": "In your home feed",
"preferences.notifications.advanced": "Show all notification categories", "preferences.notifications.advanced": "Show all notification categories",
"preferences.options.content_type_markdown": "Markdown", "preferences.options.content_type_markdown": "Markdown",
"preferences.options.content_type_plaintext": "Plain text", "preferences.options.content_type_plaintext": "Plain text",
@ -1016,7 +1000,6 @@
"sms_verification.sent.body": "We sent you a 6-digit code via SMS. Enter it below.", "sms_verification.sent.body": "We sent you a 6-digit code via SMS. Enter it below.",
"sms_verification.sent.header": "Verification code", "sms_verification.sent.header": "Verification code",
"sms_verification.success": "A verification code has been sent to your phone number.", "sms_verification.success": "A verification code has been sent to your phone number.",
"toast.view": "View",
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
"soapbox_config.authenticated_profile_label": "Profiles require authentication", "soapbox_config.authenticated_profile_label": "Profiles require authentication",
"soapbox_config.copyright_footer.meta_fields.label_placeholder": "Peu de pàgina dels drets d'autor", "soapbox_config.copyright_footer.meta_fields.label_placeholder": "Peu de pàgina dels drets d'autor",
@ -1029,8 +1012,6 @@
"soapbox_config.display_fqn_label": "Mostra el domini (eg @user@domain) per als comptes locals.", "soapbox_config.display_fqn_label": "Mostra el domini (eg @user@domain) per als comptes locals.",
"soapbox_config.feed_injection_hint": "Inject the feed with additional content, such as suggested profiles.", "soapbox_config.feed_injection_hint": "Inject the feed with additional content, such as suggested profiles.",
"soapbox_config.feed_injection_label": "Feed injection", "soapbox_config.feed_injection_label": "Feed injection",
"soapbox_config.fields.accent_color_label": "Accent color",
"soapbox_config.fields.brand_color_label": "Color de la marca",
"soapbox_config.fields.crypto_addresses_label": "Cryptocurrency addresses", "soapbox_config.fields.crypto_addresses_label": "Cryptocurrency addresses",
"soapbox_config.fields.home_footer_fields_label": "Elements de peu de pàgina d'inici", "soapbox_config.fields.home_footer_fields_label": "Elements de peu de pàgina d'inici",
"soapbox_config.fields.logo_label": "Logo", "soapbox_config.fields.logo_label": "Logo",
@ -1139,7 +1120,6 @@
"sw.update_text": "An update is available.", "sw.update_text": "An update is available.",
"sw.url": "Script URL", "sw.url": "Script URL",
"tabs_bar.all": "All", "tabs_bar.all": "All",
"tabs_bar.chats": "Xats",
"tabs_bar.dashboard": "Tauler", "tabs_bar.dashboard": "Tauler",
"tabs_bar.fediverse": "Fediverse", "tabs_bar.fediverse": "Fediverse",
"tabs_bar.home": "Inici", "tabs_bar.home": "Inici",
@ -1161,10 +1141,10 @@
"time_remaining.minutes": "{number, plural, one {# minut} other {# minuts}} restants", "time_remaining.minutes": "{number, plural, one {# minut} other {# minuts}} restants",
"time_remaining.moments": "Moments restants", "time_remaining.moments": "Moments restants",
"time_remaining.seconds": "{number, plural, one {# segon} other {# segons}} restants", "time_remaining.seconds": "{number, plural, one {# segon} other {# segons}} restants",
"toast.view": "View",
"trends.count_by_accounts": "{count} {rawCount, plural, one {persona} other {persones}} parlant-hi", "trends.count_by_accounts": "{count} {rawCount, plural, one {persona} other {persones}} parlant-hi",
"trends.title": "Tendències", "trends.title": "Tendències",
"trendsPanel.viewAll": "View all", "trendsPanel.viewAll": "View all",
"ui.beforeunload": "El teu esborrany es perdrà si surts de Soapbox.",
"unauthorized_modal.text": "Heu d'iniciar sessió per fer això.", "unauthorized_modal.text": "Heu d'iniciar sessió per fer això.",
"unauthorized_modal.title": "Registrar-se a {site_title}", "unauthorized_modal.title": "Registrar-se a {site_title}",
"upload_area.title": "Arrossega i deixa anar per a carregar", "upload_area.title": "Arrossega i deixa anar per a carregar",

@ -147,7 +147,6 @@
"alert.unexpected.links.support": "Support", "alert.unexpected.links.support": "Support",
"alert.unexpected.message": "Un prublemu inaspettatu hè accadutu.", "alert.unexpected.message": "Un prublemu inaspettatu hè accadutu.",
"alert.unexpected.return_home": "Return Home", "alert.unexpected.return_home": "Return Home",
"alert.unexpected.title": "Uups!",
"aliases.account.add": "Create alias", "aliases.account.add": "Create alias",
"aliases.account_label": "Old account:", "aliases.account_label": "Old account:",
"aliases.aliases_list_delete": "Unlink alias", "aliases.aliases_list_delete": "Unlink alias",
@ -186,18 +185,9 @@
"bundle_modal_error.message": "C'hè statu un prublemu caricandu st'elementu.", "bundle_modal_error.message": "C'hè statu un prublemu caricandu st'elementu.",
"bundle_modal_error.retry": "Pruvà torna", "bundle_modal_error.retry": "Pruvà torna",
"card.back.label": "Back", "card.back.label": "Back",
"chat_box.actions.send": "Send",
"chat_box.input.placeholder": "Send a message…",
"chat_panels.main_window.empty": "No chats found. To start a chat, visit a user's profile.",
"chat_panels.main_window.title": "Chats",
"chat_window.close": "Close chat",
"chats.actions.delete": "Delete message", "chats.actions.delete": "Delete message",
"chats.actions.more": "More", "chats.actions.more": "More",
"chats.actions.report": "Report user", "chats.actions.report": "Report user",
"chats.attachment": "Attachment",
"chats.attachment_image": "Image",
"chats.audio_toggle_off": "Audio notification off",
"chats.audio_toggle_on": "Audio notification on",
"chats.dividers.today": "Today", "chats.dividers.today": "Today",
"chats.search_placeholder": "Start a chat with…", "chats.search_placeholder": "Start a chat with…",
"column.admin.awaiting_approval": "Awaiting Approval", "column.admin.awaiting_approval": "Awaiting Approval",
@ -270,13 +260,11 @@
"column.public": "Linea pubblica glubale", "column.public": "Linea pubblica glubale",
"column.reactions": "Reactions", "column.reactions": "Reactions",
"column.reblogs": "Reposts", "column.reblogs": "Reposts",
"column.remote": "Federated timeline",
"column.scheduled_statuses": "Scheduled Posts", "column.scheduled_statuses": "Scheduled Posts",
"column.search": "Search", "column.search": "Search",
"column.settings_store": "Settings store", "column.settings_store": "Settings store",
"column.soapbox_config": "Soapbox config", "column.soapbox_config": "Soapbox config",
"column.test": "Test timeline", "column.test": "Test timeline",
"column_back_button.label": "Ritornu",
"column_forbidden.body": "You do not have permission to access this page.", "column_forbidden.body": "You do not have permission to access this page.",
"column_forbidden.title": "Forbidden", "column_forbidden.title": "Forbidden",
"common.cancel": "Cancel", "common.cancel": "Cancel",
@ -372,8 +360,6 @@
"confirmations.scheduled_status_delete.heading": "Cancel scheduled post", "confirmations.scheduled_status_delete.heading": "Cancel scheduled post",
"confirmations.scheduled_status_delete.message": "Are you sure you want to cancel this scheduled post?", "confirmations.scheduled_status_delete.message": "Are you sure you want to cancel this scheduled post?",
"confirmations.unfollow.confirm": "Disabbunassi", "confirmations.unfollow.confirm": "Disabbunassi",
"confirmations.unfollow.heading": "Unfollow {name}",
"confirmations.unfollow.message": "Site sicuru·a ch'ùn vulete più siguità @{name}?",
"crypto_donate.explanation_box.message": "{siteTitle} accepts cryptocurrency donations. You may send a donation to any of the addresses below. Thank you for your support!", "crypto_donate.explanation_box.message": "{siteTitle} accepts cryptocurrency donations. You may send a donation to any of the addresses below. Thank you for your support!",
"crypto_donate.explanation_box.title": "Sending cryptocurrency donations", "crypto_donate.explanation_box.title": "Sending cryptocurrency donations",
"crypto_donate_panel.actions.view": "Click to see {count} {count, plural, one {wallet} other {wallets}}", "crypto_donate_panel.actions.view": "Click to see {count} {count, plural, one {wallet} other {wallets}}",
@ -822,7 +808,6 @@
"preferences.fields.display_media.default": "Hide media marked as sensitive", "preferences.fields.display_media.default": "Hide media marked as sensitive",
"preferences.fields.display_media.hide_all": "Always hide media", "preferences.fields.display_media.hide_all": "Always hide media",
"preferences.fields.display_media.show_all": "Always show media", "preferences.fields.display_media.show_all": "Always show media",
"preferences.fields.dyslexic_font_label": "Dyslexic mode",
"preferences.fields.expand_spoilers_label": "Always expand posts marked with content warnings", "preferences.fields.expand_spoilers_label": "Always expand posts marked with content warnings",
"preferences.fields.language_label": "Language", "preferences.fields.language_label": "Language",
"preferences.fields.media_display_label": "Media display", "preferences.fields.media_display_label": "Media display",
@ -834,7 +819,6 @@
"preferences.fields.underline_links_label": "Always underline links in posts", "preferences.fields.underline_links_label": "Always underline links in posts",
"preferences.fields.unfollow_modal_label": "Show confirmation dialog before unfollowing someone", "preferences.fields.unfollow_modal_label": "Show confirmation dialog before unfollowing someone",
"preferences.hints.demetricator": "Decrease social media anxiety by hiding all numbers from the site.", "preferences.hints.demetricator": "Decrease social media anxiety by hiding all numbers from the site.",
"preferences.hints.feed": "In your home feed",
"preferences.notifications.advanced": "Show all notification categories", "preferences.notifications.advanced": "Show all notification categories",
"preferences.options.content_type_markdown": "Markdown", "preferences.options.content_type_markdown": "Markdown",
"preferences.options.content_type_plaintext": "Plain text", "preferences.options.content_type_plaintext": "Plain text",
@ -1016,7 +1000,6 @@
"sms_verification.sent.body": "We sent you a 6-digit code via SMS. Enter it below.", "sms_verification.sent.body": "We sent you a 6-digit code via SMS. Enter it below.",
"sms_verification.sent.header": "Verification code", "sms_verification.sent.header": "Verification code",
"sms_verification.success": "A verification code has been sent to your phone number.", "sms_verification.success": "A verification code has been sent to your phone number.",
"toast.view": "View",
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
"soapbox_config.authenticated_profile_label": "Profiles require authentication", "soapbox_config.authenticated_profile_label": "Profiles require authentication",
"soapbox_config.copyright_footer.meta_fields.label_placeholder": "Copyright footer", "soapbox_config.copyright_footer.meta_fields.label_placeholder": "Copyright footer",
@ -1029,8 +1012,6 @@
"soapbox_config.display_fqn_label": "Display domain (eg @user@domain) for local accounts.", "soapbox_config.display_fqn_label": "Display domain (eg @user@domain) for local accounts.",
"soapbox_config.feed_injection_hint": "Inject the feed with additional content, such as suggested profiles.", "soapbox_config.feed_injection_hint": "Inject the feed with additional content, such as suggested profiles.",
"soapbox_config.feed_injection_label": "Feed injection", "soapbox_config.feed_injection_label": "Feed injection",
"soapbox_config.fields.accent_color_label": "Accent color",
"soapbox_config.fields.brand_color_label": "Brand color",
"soapbox_config.fields.crypto_addresses_label": "Cryptocurrency addresses", "soapbox_config.fields.crypto_addresses_label": "Cryptocurrency addresses",
"soapbox_config.fields.home_footer_fields_label": "Home footer items", "soapbox_config.fields.home_footer_fields_label": "Home footer items",
"soapbox_config.fields.logo_label": "Logo", "soapbox_config.fields.logo_label": "Logo",
@ -1139,7 +1120,6 @@
"sw.update_text": "An update is available.", "sw.update_text": "An update is available.",
"sw.url": "Script URL", "sw.url": "Script URL",
"tabs_bar.all": "All", "tabs_bar.all": "All",
"tabs_bar.chats": "Chats",
"tabs_bar.dashboard": "Dashboard", "tabs_bar.dashboard": "Dashboard",
"tabs_bar.fediverse": "Fediverse", "tabs_bar.fediverse": "Fediverse",
"tabs_bar.home": "Accolta", "tabs_bar.home": "Accolta",
@ -1161,10 +1141,10 @@
"time_remaining.minutes": "{number, plural, one {# minuta ferma} other {# minute fermanu}} left", "time_remaining.minutes": "{number, plural, one {# minuta ferma} other {# minute fermanu}} left",
"time_remaining.moments": "Ci fermanu qualchi mumentu", "time_remaining.moments": "Ci fermanu qualchi mumentu",
"time_remaining.seconds": "{number, plural, one {# siconda ferma} other {# siconde fermanu}}", "time_remaining.seconds": "{number, plural, one {# siconda ferma} other {# siconde fermanu}}",
"toast.view": "View",
"trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} parlanu", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} parlanu",
"trends.title": "Trends", "trends.title": "Trends",
"trendsPanel.viewAll": "View all", "trendsPanel.viewAll": "View all",
"ui.beforeunload": "A bruttacopia sarà persa s'ellu hè chjosu Soapbox.",
"unauthorized_modal.text": "You need to be logged in to do that.", "unauthorized_modal.text": "You need to be logged in to do that.",
"unauthorized_modal.title": "Sign up for {site_title}", "unauthorized_modal.title": "Sign up for {site_title}",
"upload_area.title": "Drag & drop per caricà un fugliale", "upload_area.title": "Drag & drop per caricà un fugliale",

@ -147,7 +147,6 @@
"alert.unexpected.links.support": "Support", "alert.unexpected.links.support": "Support",
"alert.unexpected.message": "Došlo k neočekávané chybě.", "alert.unexpected.message": "Došlo k neočekávané chybě.",
"alert.unexpected.return_home": "Return Home", "alert.unexpected.return_home": "Return Home",
"alert.unexpected.title": "Jejda!",
"aliases.account.add": "Vytvořit alias", "aliases.account.add": "Vytvořit alias",
"aliases.account_label": "Starý účet:", "aliases.account_label": "Starý účet:",
"aliases.aliases_list_delete": "Unlink alias", "aliases.aliases_list_delete": "Unlink alias",
@ -186,18 +185,9 @@
"bundle_modal_error.message": "Při načítání tohoto komponentu se něco pokazilo.", "bundle_modal_error.message": "Při načítání tohoto komponentu se něco pokazilo.",
"bundle_modal_error.retry": "Zkusit znovu", "bundle_modal_error.retry": "Zkusit znovu",
"card.back.label": "Back", "card.back.label": "Back",
"chat_box.actions.send": "Poslat",
"chat_box.input.placeholder": "Poslat zprávu…",
"chat_panels.main_window.empty": "Žádné chaty nenalezeny. Chat můžete začít na něčím profilu.",
"chat_panels.main_window.title": "Chaty",
"chat_window.close": "Close chat",
"chats.actions.delete": "Odstranit zprávu", "chats.actions.delete": "Odstranit zprávu",
"chats.actions.more": "Více", "chats.actions.more": "Více",
"chats.actions.report": "Nahlásit uživatele", "chats.actions.report": "Nahlásit uživatele",
"chats.attachment": "Attachment",
"chats.attachment_image": "Image",
"chats.audio_toggle_off": "Audio upozornění vypnuté",
"chats.audio_toggle_on": "Audio upozornění zapnoté",
"chats.dividers.today": "Dnes", "chats.dividers.today": "Dnes",
"chats.search_placeholder": "Chatovat s…", "chats.search_placeholder": "Chatovat s…",
"column.admin.awaiting_approval": "Čeká na schválení", "column.admin.awaiting_approval": "Čeká na schválení",
@ -270,13 +260,11 @@
"column.public": "Federovaná časová osa", "column.public": "Federovaná časová osa",
"column.reactions": "Reactions", "column.reactions": "Reactions",
"column.reblogs": "Reposts", "column.reblogs": "Reposts",
"column.remote": "Federated timeline",
"column.scheduled_statuses": "Scheduled Posts", "column.scheduled_statuses": "Scheduled Posts",
"column.search": "Search", "column.search": "Search",
"column.settings_store": "Settings store", "column.settings_store": "Settings store",
"column.soapbox_config": "Soapbox nastavení", "column.soapbox_config": "Soapbox nastavení",
"column.test": "Test timeline", "column.test": "Test timeline",
"column_back_button.label": "Zpět",
"column_forbidden.body": "You do not have permission to access this page.", "column_forbidden.body": "You do not have permission to access this page.",
"column_forbidden.title": "Forbidden", "column_forbidden.title": "Forbidden",
"common.cancel": "Zrušit", "common.cancel": "Zrušit",
@ -372,8 +360,6 @@
"confirmations.scheduled_status_delete.heading": "Zrušit naplánovaný příspěvek", "confirmations.scheduled_status_delete.heading": "Zrušit naplánovaný příspěvek",
"confirmations.scheduled_status_delete.message": "Určitě chcete tento naplánovaný příspěvěk zrušit?", "confirmations.scheduled_status_delete.message": "Určitě chcete tento naplánovaný příspěvěk zrušit?",
"confirmations.unfollow.confirm": "Přestat sledovat", "confirmations.unfollow.confirm": "Přestat sledovat",
"confirmations.unfollow.heading": "Přestat selovat uživatele {name}",
"confirmations.unfollow.message": "jste si jistý/á, že chcete přestat sledovat uživatele {name}?",
"crypto_donate.explanation_box.message": "{siteTitle} accepts cryptocurrency donations. You may send a donation to any of the addresses below. Thank you for your support!", "crypto_donate.explanation_box.message": "{siteTitle} accepts cryptocurrency donations. You may send a donation to any of the addresses below. Thank you for your support!",
"crypto_donate.explanation_box.title": "Sending cryptocurrency donations", "crypto_donate.explanation_box.title": "Sending cryptocurrency donations",
"crypto_donate_panel.actions.view": "Click to see {count} {count, plural, one {wallet} other {wallets}}", "crypto_donate_panel.actions.view": "Click to see {count} {count, plural, one {wallet} other {wallets}}",
@ -822,7 +808,6 @@
"preferences.fields.display_media.default": "Skrývat média označená jako citlivá", "preferences.fields.display_media.default": "Skrývat média označená jako citlivá",
"preferences.fields.display_media.hide_all": "Vždy skrývat média", "preferences.fields.display_media.hide_all": "Vždy skrývat média",
"preferences.fields.display_media.show_all": "Vždy zobrazovat média", "preferences.fields.display_media.show_all": "Vždy zobrazovat média",
"preferences.fields.dyslexic_font_label": "Dyslexic mode",
"preferences.fields.expand_spoilers_label": "Vždy rozbalit příspěvky označené varováním", "preferences.fields.expand_spoilers_label": "Vždy rozbalit příspěvky označené varováním",
"preferences.fields.language_label": "Jazyk", "preferences.fields.language_label": "Jazyk",
"preferences.fields.media_display_label": "Zobrazování médií", "preferences.fields.media_display_label": "Zobrazování médií",
@ -834,7 +819,6 @@
"preferences.fields.underline_links_label": "Always underline links in posts", "preferences.fields.underline_links_label": "Always underline links in posts",
"preferences.fields.unfollow_modal_label": "Show confirmation dialog before unfollowing someone", "preferences.fields.unfollow_modal_label": "Show confirmation dialog before unfollowing someone",
"preferences.hints.demetricator": "Decrease social media anxiety by hiding all numbers from the site.", "preferences.hints.demetricator": "Decrease social media anxiety by hiding all numbers from the site.",
"preferences.hints.feed": "Ve vaší časové ose",
"preferences.notifications.advanced": "Show all notification categories", "preferences.notifications.advanced": "Show all notification categories",
"preferences.options.content_type_markdown": "Markdown", "preferences.options.content_type_markdown": "Markdown",
"preferences.options.content_type_plaintext": "Prostý text", "preferences.options.content_type_plaintext": "Prostý text",
@ -1016,7 +1000,6 @@
"sms_verification.sent.body": "We sent you a 6-digit code via SMS. Enter it below.", "sms_verification.sent.body": "We sent you a 6-digit code via SMS. Enter it below.",
"sms_verification.sent.header": "Verification code", "sms_verification.sent.header": "Verification code",
"sms_verification.success": "A verification code has been sent to your phone number.", "sms_verification.success": "A verification code has been sent to your phone number.",
"toast.view": "View",
"soapbox_config.authenticated_profile_hint": "Pouze přihlášení uživatelé mohou prohlížet odpovědi a média na uživatelských profilech.", "soapbox_config.authenticated_profile_hint": "Pouze přihlášení uživatelé mohou prohlížet odpovědi a média na uživatelských profilech.",
"soapbox_config.authenticated_profile_label": "Profily vyžadují přihlášení", "soapbox_config.authenticated_profile_label": "Profily vyžadují přihlášení",
"soapbox_config.copyright_footer.meta_fields.label_placeholder": "Copyright v zápatí", "soapbox_config.copyright_footer.meta_fields.label_placeholder": "Copyright v zápatí",
@ -1029,8 +1012,6 @@
"soapbox_config.display_fqn_label": "Zobrazit doménu (např @uzivatel@domena) u místních účtů.", "soapbox_config.display_fqn_label": "Zobrazit doménu (např @uzivatel@domena) u místních účtů.",
"soapbox_config.feed_injection_hint": "Inject the feed with additional content, such as suggested profiles.", "soapbox_config.feed_injection_hint": "Inject the feed with additional content, such as suggested profiles.",
"soapbox_config.feed_injection_label": "Feed injection", "soapbox_config.feed_injection_label": "Feed injection",
"soapbox_config.fields.accent_color_label": "Vedlejší barva",
"soapbox_config.fields.brand_color_label": "Hlavní barva",
"soapbox_config.fields.crypto_addresses_label": "Adresy kryptoměn", "soapbox_config.fields.crypto_addresses_label": "Adresy kryptoměn",
"soapbox_config.fields.home_footer_fields_label": "Zápatí časové osy", "soapbox_config.fields.home_footer_fields_label": "Zápatí časové osy",
"soapbox_config.fields.logo_label": "Logo", "soapbox_config.fields.logo_label": "Logo",
@ -1139,7 +1120,6 @@
"sw.update_text": "Je dostupná nová verze.", "sw.update_text": "Je dostupná nová verze.",
"sw.url": "Script URL", "sw.url": "Script URL",
"tabs_bar.all": "All", "tabs_bar.all": "All",
"tabs_bar.chats": "Chaty",
"tabs_bar.dashboard": "Ovládací panel", "tabs_bar.dashboard": "Ovládací panel",
"tabs_bar.fediverse": "Fediverse", "tabs_bar.fediverse": "Fediverse",
"tabs_bar.home": "Domů", "tabs_bar.home": "Domů",
@ -1161,10 +1141,10 @@
"time_remaining.minutes": "{number, plural, one {Zbývá # minuta} few {Zbývají # minuty} many {Zbývá # minuty} other {Zbývá # minut}}", "time_remaining.minutes": "{number, plural, one {Zbývá # minuta} few {Zbývají # minuty} many {Zbývá # minuty} other {Zbývá # minut}}",
"time_remaining.moments": "Zbývá několik sekund", "time_remaining.moments": "Zbývá několik sekund",
"time_remaining.seconds": "{number, plural, one {Zbývá # sekunda} few {Zbývají # sekundy} many {Zbývá # sekundy} other {Zbývá # sekund}}", "time_remaining.seconds": "{number, plural, one {Zbývá # sekunda} few {Zbývají # sekundy} many {Zbývá # sekundy} other {Zbývá # sekund}}",
"toast.view": "View",
"trends.count_by_accounts": "{count} {rawCount, plural, one {člověk} few {lidé} many {lidí} other {lidí}} hovoří", "trends.count_by_accounts": "{count} {rawCount, plural, one {člověk} few {lidé} many {lidí} other {lidí}} hovoří",
"trends.title": "Trendy", "trends.title": "Trendy",
"trendsPanel.viewAll": "View all", "trendsPanel.viewAll": "View all",
"ui.beforeunload": "Váš koncept se ztratí, pokud Soapbox opustíte.",
"unauthorized_modal.text": "Nejprve se přihlašte.", "unauthorized_modal.text": "Nejprve se přihlašte.",
"unauthorized_modal.title": "Registrovat se na {site_title}", "unauthorized_modal.title": "Registrovat se na {site_title}",
"upload_area.title": "Přetažením nahrajete", "upload_area.title": "Přetažením nahrajete",

@ -147,7 +147,6 @@
"alert.unexpected.links.support": "Support", "alert.unexpected.links.support": "Support",
"alert.unexpected.message": "Digwyddodd gwall annisgwyl.", "alert.unexpected.message": "Digwyddodd gwall annisgwyl.",
"alert.unexpected.return_home": "Return Home", "alert.unexpected.return_home": "Return Home",
"alert.unexpected.title": "Wps!",
"aliases.account.add": "Create alias", "aliases.account.add": "Create alias",
"aliases.account_label": "Old account:", "aliases.account_label": "Old account:",
"aliases.aliases_list_delete": "Unlink alias", "aliases.aliases_list_delete": "Unlink alias",
@ -186,18 +185,9 @@
"bundle_modal_error.message": "Aeth rhywbeth o'i le tra'n llwytho'r elfen hon.", "bundle_modal_error.message": "Aeth rhywbeth o'i le tra'n llwytho'r elfen hon.",
"bundle_modal_error.retry": "Ceiswich eto", "bundle_modal_error.retry": "Ceiswich eto",
"card.back.label": "Back", "card.back.label": "Back",
"chat_box.actions.send": "Send",
"chat_box.input.placeholder": "Send a message…",
"chat_panels.main_window.empty": "No chats found. To start a chat, visit a user's profile.",
"chat_panels.main_window.title": "Chats",
"chat_window.close": "Close chat",
"chats.actions.delete": "Delete message", "chats.actions.delete": "Delete message",
"chats.actions.more": "More", "chats.actions.more": "More",
"chats.actions.report": "Report user", "chats.actions.report": "Report user",
"chats.attachment": "Attachment",
"chats.attachment_image": "Image",
"chats.audio_toggle_off": "Audio notification off",
"chats.audio_toggle_on": "Audio notification on",
"chats.dividers.today": "Today", "chats.dividers.today": "Today",
"chats.search_placeholder": "Start a chat with…", "chats.search_placeholder": "Start a chat with…",
"column.admin.awaiting_approval": "Awaiting Approval", "column.admin.awaiting_approval": "Awaiting Approval",
@ -270,13 +260,11 @@
"column.public": "Ffrwd y ffederasiwn", "column.public": "Ffrwd y ffederasiwn",
"column.reactions": "Reactions", "column.reactions": "Reactions",
"column.reblogs": "Reposts", "column.reblogs": "Reposts",
"column.remote": "Federated timeline",
"column.scheduled_statuses": "Scheduled Posts", "column.scheduled_statuses": "Scheduled Posts",
"column.search": "Search", "column.search": "Search",
"column.settings_store": "Settings store", "column.settings_store": "Settings store",
"column.soapbox_config": "Soapbox config", "column.soapbox_config": "Soapbox config",
"column.test": "Test timeline", "column.test": "Test timeline",
"column_back_button.label": "Nôl",
"column_forbidden.body": "You do not have permission to access this page.", "column_forbidden.body": "You do not have permission to access this page.",
"column_forbidden.title": "Forbidden", "column_forbidden.title": "Forbidden",
"common.cancel": "Cancel", "common.cancel": "Cancel",
@ -372,8 +360,6 @@
"confirmations.scheduled_status_delete.heading": "Cancel scheduled post", "confirmations.scheduled_status_delete.heading": "Cancel scheduled post",
"confirmations.scheduled_status_delete.message": "Are you sure you want to cancel this scheduled post?", "confirmations.scheduled_status_delete.message": "Are you sure you want to cancel this scheduled post?",
"confirmations.unfollow.confirm": "Dad-ddilynwch", "confirmations.unfollow.confirm": "Dad-ddilynwch",
"confirmations.unfollow.heading": "Unfollow {name}",
"confirmations.unfollow.message": "Ydych chi'n sicr eich bod am ddad-ddilyn {name}?",
"crypto_donate.explanation_box.message": "{siteTitle} accepts cryptocurrency donations. You may send a donation to any of the addresses below. Thank you for your support!", "crypto_donate.explanation_box.message": "{siteTitle} accepts cryptocurrency donations. You may send a donation to any of the addresses below. Thank you for your support!",
"crypto_donate.explanation_box.title": "Sending cryptocurrency donations", "crypto_donate.explanation_box.title": "Sending cryptocurrency donations",
"crypto_donate_panel.actions.view": "Click to see {count} {count, plural, one {wallet} other {wallets}}", "crypto_donate_panel.actions.view": "Click to see {count} {count, plural, one {wallet} other {wallets}}",
@ -822,7 +808,6 @@
"preferences.fields.display_media.default": "Hide media marked as sensitive", "preferences.fields.display_media.default": "Hide media marked as sensitive",
"preferences.fields.display_media.hide_all": "Always hide media", "preferences.fields.display_media.hide_all": "Always hide media",
"preferences.fields.display_media.show_all": "Always show media", "preferences.fields.display_media.show_all": "Always show media",
"preferences.fields.dyslexic_font_label": "Dyslexic mode",
"preferences.fields.expand_spoilers_label": "Always expand posts marked with content warnings", "preferences.fields.expand_spoilers_label": "Always expand posts marked with content warnings",
"preferences.fields.language_label": "Language", "preferences.fields.language_label": "Language",
"preferences.fields.media_display_label": "Media display", "preferences.fields.media_display_label": "Media display",
@ -834,7 +819,6 @@
"preferences.fields.underline_links_label": "Always underline links in posts", "preferences.fields.underline_links_label": "Always underline links in posts",
"preferences.fields.unfollow_modal_label": "Show confirmation dialog before unfollowing someone", "preferences.fields.unfollow_modal_label": "Show confirmation dialog before unfollowing someone",
"preferences.hints.demetricator": "Decrease social media anxiety by hiding all numbers from the site.", "preferences.hints.demetricator": "Decrease social media anxiety by hiding all numbers from the site.",
"preferences.hints.feed": "In your home feed",
"preferences.notifications.advanced": "Show all notification categories", "preferences.notifications.advanced": "Show all notification categories",
"preferences.options.content_type_markdown": "Markdown", "preferences.options.content_type_markdown": "Markdown",
"preferences.options.content_type_plaintext": "Plain text", "preferences.options.content_type_plaintext": "Plain text",
@ -1016,7 +1000,6 @@
"sms_verification.sent.body": "We sent you a 6-digit code via SMS. Enter it below.", "sms_verification.sent.body": "We sent you a 6-digit code via SMS. Enter it below.",
"sms_verification.sent.header": "Verification code", "sms_verification.sent.header": "Verification code",
"sms_verification.success": "A verification code has been sent to your phone number.", "sms_verification.success": "A verification code has been sent to your phone number.",
"toast.view": "View",
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
"soapbox_config.authenticated_profile_label": "Profiles require authentication", "soapbox_config.authenticated_profile_label": "Profiles require authentication",
"soapbox_config.copyright_footer.meta_fields.label_placeholder": "Copyright footer", "soapbox_config.copyright_footer.meta_fields.label_placeholder": "Copyright footer",
@ -1029,8 +1012,6 @@
"soapbox_config.display_fqn_label": "Display domain (eg @user@domain) for local accounts.", "soapbox_config.display_fqn_label": "Display domain (eg @user@domain) for local accounts.",
"soapbox_config.feed_injection_hint": "Inject the feed with additional content, such as suggested profiles.", "soapbox_config.feed_injection_hint": "Inject the feed with additional content, such as suggested profiles.",
"soapbox_config.feed_injection_label": "Feed injection", "soapbox_config.feed_injection_label": "Feed injection",
"soapbox_config.fields.accent_color_label": "Accent color",
"soapbox_config.fields.brand_color_label": "Brand color",
"soapbox_config.fields.crypto_addresses_label": "Cryptocurrency addresses", "soapbox_config.fields.crypto_addresses_label": "Cryptocurrency addresses",
"soapbox_config.fields.home_footer_fields_label": "Home footer items", "soapbox_config.fields.home_footer_fields_label": "Home footer items",
"soapbox_config.fields.logo_label": "Logo", "soapbox_config.fields.logo_label": "Logo",
@ -1139,7 +1120,6 @@
"sw.update_text": "An update is available.", "sw.update_text": "An update is available.",
"sw.url": "Script URL", "sw.url": "Script URL",
"tabs_bar.all": "All", "tabs_bar.all": "All",
"tabs_bar.chats": "Chats",
"tabs_bar.dashboard": "Dashboard", "tabs_bar.dashboard": "Dashboard",
"tabs_bar.fediverse": "Fediverse", "tabs_bar.fediverse": "Fediverse",
"tabs_bar.home": "Hafan", "tabs_bar.home": "Hafan",
@ -1161,10 +1141,10 @@
"time_remaining.minutes": "{number, plural, one {# funud} other {# o funudau}} ar ôl", "time_remaining.minutes": "{number, plural, one {# funud} other {# o funudau}} ar ôl",
"time_remaining.moments": "Munudau ar ôl", "time_remaining.moments": "Munudau ar ôl",
"time_remaining.seconds": "{number, plural, one {# eiliad} other {# o eiliadau}} ar ôl", "time_remaining.seconds": "{number, plural, one {# eiliad} other {# o eiliadau}} ar ôl",
"toast.view": "View",
"trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} yn siarad", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} yn siarad",
"trends.title": "Trends", "trends.title": "Trends",
"trendsPanel.viewAll": "View all", "trendsPanel.viewAll": "View all",
"ui.beforeunload": "Mi fyddwch yn colli eich drafft os gadewch Soapbox.",
"unauthorized_modal.text": "You need to be logged in to do that.", "unauthorized_modal.text": "You need to be logged in to do that.",
"unauthorized_modal.title": "Sign up for {site_title}", "unauthorized_modal.title": "Sign up for {site_title}",
"upload_area.title": "Llusgwch & gollwing i uwchlwytho", "upload_area.title": "Llusgwch & gollwing i uwchlwytho",

@ -147,7 +147,6 @@
"alert.unexpected.links.support": "Support", "alert.unexpected.links.support": "Support",
"alert.unexpected.message": "Der opstod en uventet fejl.", "alert.unexpected.message": "Der opstod en uventet fejl.",
"alert.unexpected.return_home": "Return Home", "alert.unexpected.return_home": "Return Home",
"alert.unexpected.title": "Ups!",
"aliases.account.add": "Create alias", "aliases.account.add": "Create alias",
"aliases.account_label": "Old account:", "aliases.account_label": "Old account:",
"aliases.aliases_list_delete": "Unlink alias", "aliases.aliases_list_delete": "Unlink alias",
@ -186,18 +185,9 @@
"bundle_modal_error.message": "Noget gik galt under indlæsningen af dette komponent.", "bundle_modal_error.message": "Noget gik galt under indlæsningen af dette komponent.",
"bundle_modal_error.retry": "Prøv igen", "bundle_modal_error.retry": "Prøv igen",
"card.back.label": "Back", "card.back.label": "Back",
"chat_box.actions.send": "Send",
"chat_box.input.placeholder": "Send a message…",
"chat_panels.main_window.empty": "No chats found. To start a chat, visit a user's profile.",
"chat_panels.main_window.title": "Chats",
"chat_window.close": "Close chat",
"chats.actions.delete": "Delete message", "chats.actions.delete": "Delete message",
"chats.actions.more": "More", "chats.actions.more": "More",
"chats.actions.report": "Report user", "chats.actions.report": "Report user",
"chats.attachment": "Attachment",
"chats.attachment_image": "Image",
"chats.audio_toggle_off": "Audio notification off",
"chats.audio_toggle_on": "Audio notification on",
"chats.dividers.today": "Today", "chats.dividers.today": "Today",
"chats.search_placeholder": "Start a chat with…", "chats.search_placeholder": "Start a chat with…",
"column.admin.awaiting_approval": "Awaiting Approval", "column.admin.awaiting_approval": "Awaiting Approval",
@ -270,13 +260,11 @@
"column.public": "Fælles tidslinje", "column.public": "Fælles tidslinje",
"column.reactions": "Reactions", "column.reactions": "Reactions",
"column.reblogs": "Reposts", "column.reblogs": "Reposts",
"column.remote": "Federated timeline",
"column.scheduled_statuses": "Scheduled Posts", "column.scheduled_statuses": "Scheduled Posts",
"column.search": "Search", "column.search": "Search",
"column.settings_store": "Settings store", "column.settings_store": "Settings store",
"column.soapbox_config": "Soapbox config", "column.soapbox_config": "Soapbox config",
"column.test": "Test timeline", "column.test": "Test timeline",
"column_back_button.label": "Tilbage",
"column_forbidden.body": "You do not have permission to access this page.", "column_forbidden.body": "You do not have permission to access this page.",
"column_forbidden.title": "Forbidden", "column_forbidden.title": "Forbidden",
"common.cancel": "Cancel", "common.cancel": "Cancel",
@ -372,8 +360,6 @@
"confirmations.scheduled_status_delete.heading": "Cancel scheduled post", "confirmations.scheduled_status_delete.heading": "Cancel scheduled post",
"confirmations.scheduled_status_delete.message": "Are you sure you want to cancel this scheduled post?", "confirmations.scheduled_status_delete.message": "Are you sure you want to cancel this scheduled post?",
"confirmations.unfollow.confirm": "Følg ikke længere", "confirmations.unfollow.confirm": "Følg ikke længere",
"confirmations.unfollow.heading": "Unfollow {name}",
"confirmations.unfollow.message": "Er du sikker på, du ikke længere vil følge {name}?",
"crypto_donate.explanation_box.message": "{siteTitle} accepts cryptocurrency donations. You may send a donation to any of the addresses below. Thank you for your support!", "crypto_donate.explanation_box.message": "{siteTitle} accepts cryptocurrency donations. You may send a donation to any of the addresses below. Thank you for your support!",
"crypto_donate.explanation_box.title": "Sending cryptocurrency donations", "crypto_donate.explanation_box.title": "Sending cryptocurrency donations",
"crypto_donate_panel.actions.view": "Click to see {count} {count, plural, one {wallet} other {wallets}}", "crypto_donate_panel.actions.view": "Click to see {count} {count, plural, one {wallet} other {wallets}}",
@ -822,7 +808,6 @@
"preferences.fields.display_media.default": "Hide media marked as sensitive", "preferences.fields.display_media.default": "Hide media marked as sensitive",
"preferences.fields.display_media.hide_all": "Always hide media", "preferences.fields.display_media.hide_all": "Always hide media",
"preferences.fields.display_media.show_all": "Always show media", "preferences.fields.display_media.show_all": "Always show media",
"preferences.fields.dyslexic_font_label": "Dyslexic mode",
"preferences.fields.expand_spoilers_label": "Always expand posts marked with content warnings", "preferences.fields.expand_spoilers_label": "Always expand posts marked with content warnings",
"preferences.fields.language_label": "Language", "preferences.fields.language_label": "Language",
"preferences.fields.media_display_label": "Media display", "preferences.fields.media_display_label": "Media display",
@ -834,7 +819,6 @@
"preferences.fields.underline_links_label": "Always underline links in posts", "preferences.fields.underline_links_label": "Always underline links in posts",
"preferences.fields.unfollow_modal_label": "Show confirmation dialog before unfollowing someone", "preferences.fields.unfollow_modal_label": "Show confirmation dialog before unfollowing someone",
"preferences.hints.demetricator": "Decrease social media anxiety by hiding all numbers from the site.", "preferences.hints.demetricator": "Decrease social media anxiety by hiding all numbers from the site.",
"preferences.hints.feed": "In your home feed",
"preferences.notifications.advanced": "Show all notification categories", "preferences.notifications.advanced": "Show all notification categories",
"preferences.options.content_type_markdown": "Markdown", "preferences.options.content_type_markdown": "Markdown",
"preferences.options.content_type_plaintext": "Plain text", "preferences.options.content_type_plaintext": "Plain text",
@ -1016,7 +1000,6 @@
"sms_verification.sent.body": "We sent you a 6-digit code via SMS. Enter it below.", "sms_verification.sent.body": "We sent you a 6-digit code via SMS. Enter it below.",
"sms_verification.sent.header": "Verification code", "sms_verification.sent.header": "Verification code",
"sms_verification.success": "A verification code has been sent to your phone number.", "sms_verification.success": "A verification code has been sent to your phone number.",
"toast.view": "View",
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
"soapbox_config.authenticated_profile_label": "Profiles require authentication", "soapbox_config.authenticated_profile_label": "Profiles require authentication",
"soapbox_config.copyright_footer.meta_fields.label_placeholder": "Copyright footer", "soapbox_config.copyright_footer.meta_fields.label_placeholder": "Copyright footer",
@ -1029,8 +1012,6 @@
"soapbox_config.display_fqn_label": "Display domain (eg @user@domain) for local accounts.", "soapbox_config.display_fqn_label": "Display domain (eg @user@domain) for local accounts.",
"soapbox_config.feed_injection_hint": "Inject the feed with additional content, such as suggested profiles.", "soapbox_config.feed_injection_hint": "Inject the feed with additional content, such as suggested profiles.",
"soapbox_config.feed_injection_label": "Feed injection", "soapbox_config.feed_injection_label": "Feed injection",
"soapbox_config.fields.accent_color_label": "Accent color",
"soapbox_config.fields.brand_color_label": "Brand color",
"soapbox_config.fields.crypto_addresses_label": "Cryptocurrency addresses", "soapbox_config.fields.crypto_addresses_label": "Cryptocurrency addresses",
"soapbox_config.fields.home_footer_fields_label": "Home footer items", "soapbox_config.fields.home_footer_fields_label": "Home footer items",
"soapbox_config.fields.logo_label": "Logo", "soapbox_config.fields.logo_label": "Logo",
@ -1139,7 +1120,6 @@
"sw.update_text": "An update is available.", "sw.update_text": "An update is available.",
"sw.url": "Script URL", "sw.url": "Script URL",
"tabs_bar.all": "All", "tabs_bar.all": "All",
"tabs_bar.chats": "Chats",
"tabs_bar.dashboard": "Dashboard", "tabs_bar.dashboard": "Dashboard",
"tabs_bar.fediverse": "Fediverse", "tabs_bar.fediverse": "Fediverse",
"tabs_bar.home": "Hjem", "tabs_bar.home": "Hjem",
@ -1161,10 +1141,10 @@
"time_remaining.minutes": "{number, plural, one {# minut} other {# minutter}} tilbage", "time_remaining.minutes": "{number, plural, one {# minut} other {# minutter}} tilbage",
"time_remaining.moments": "Få øjeblikke tilbage", "time_remaining.moments": "Få øjeblikke tilbage",
"time_remaining.seconds": "{number, plural, one {# sekund} other {# sekunder}} tilbage", "time_remaining.seconds": "{number, plural, one {# sekund} other {# sekunder}} tilbage",
"toast.view": "View",
"trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {personer}} snakker", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {personer}} snakker",
"trends.title": "Trends", "trends.title": "Trends",
"trendsPanel.viewAll": "View all", "trendsPanel.viewAll": "View all",
"ui.beforeunload": "Din kladde vil gå tabt hvis du forlader Soapbox.",
"unauthorized_modal.text": "You need to be logged in to do that.", "unauthorized_modal.text": "You need to be logged in to do that.",
"unauthorized_modal.title": "Sign up for {site_title}", "unauthorized_modal.title": "Sign up for {site_title}",
"upload_area.title": "Træk og slip for at uploade", "upload_area.title": "Træk og slip for at uploade",

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save