Merge remote-tracking branch 'soapbox/develop' into cleanup
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import { getFeatures } from 'soapbox/utils/features';
|
||||
|
||||
import api, { getLinks } from '../api';
|
||||
|
||||
import type { AxiosError } from 'axios';
|
||||
@@ -18,10 +20,17 @@ const SCHEDULED_STATUS_CANCEL_FAIL = 'SCHEDULED_STATUS_CANCEL_FAIL';
|
||||
|
||||
const fetchScheduledStatuses = () =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (getState().status_lists.get('scheduled_statuses')?.isLoading) {
|
||||
const state = getState();
|
||||
|
||||
if (state.status_lists.get('scheduled_statuses')?.isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
const instance = state.instance;
|
||||
const features = getFeatures(instance);
|
||||
|
||||
if (!features.scheduledStatuses) return;
|
||||
|
||||
dispatch(fetchScheduledStatusesRequest());
|
||||
|
||||
api(getState).get('/api/v1/scheduled_statuses').then(response => {
|
||||
|
||||
@@ -17,6 +17,8 @@ const fetchTrendingStatuses = () =>
|
||||
const instance = state.instance;
|
||||
const features = getFeatures(instance);
|
||||
|
||||
if (!features.trendingStatuses && !features.trendingTruths) return;
|
||||
|
||||
dispatch({ type: TRENDING_STATUSES_FETCH_REQUEST });
|
||||
return api(getState).get(features.trendingTruths ? '/api/v1/truth/trending/truths' : '/api/v1/trends/statuses').then(({ data: statuses }) => {
|
||||
dispatch(importFetchedStatuses(statuses));
|
||||
|
||||
@@ -62,7 +62,6 @@ export const baseClient = (accessToken?: string | null, baseURL: string = ''): A
|
||||
headers: Object.assign(accessToken ? {
|
||||
'Authorization': `Bearer ${accessToken}`,
|
||||
} : {}),
|
||||
|
||||
transformResponse: [maybeParseJSON],
|
||||
});
|
||||
};
|
||||
|
||||
@@ -53,8 +53,7 @@ const AutosuggestAccountInput: React.FC<IAutosuggestAccountInput> = ({
|
||||
setAccountIds(ImmutableOrderedSet(accountIds));
|
||||
})
|
||||
.catch(noOp);
|
||||
|
||||
}, 900, { leading: true, trailing: true }), [limit]);
|
||||
}, 900, { leading: false, trailing: true }), [limit]);
|
||||
|
||||
const handleChange: React.ChangeEventHandler<HTMLInputElement> = e => {
|
||||
refreshCancelToken();
|
||||
|
||||
@@ -294,7 +294,7 @@ const MediaGallery: React.FC<IMediaGallery> = (props) => {
|
||||
const aspectRatio = media.getIn([0, 'meta', 'original', 'aspect']) as number | undefined;
|
||||
|
||||
const getHeight = () => {
|
||||
if (!aspectRatio) return w;
|
||||
if (!aspectRatio) return w * 9 / 16;
|
||||
if (isPanoramic(aspectRatio)) return Math.floor(w / maximumAspectRatio);
|
||||
if (isPortrait(aspectRatio)) return Math.floor(w / minimumAspectRatio);
|
||||
return Math.floor(w / aspectRatio);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable jsx-a11y/interactive-supports-focus */
|
||||
import classNames from 'clsx';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
|
||||
@@ -136,218 +137,234 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames('sidebar-menu__root', {
|
||||
'sidebar-menu__root--visible': sidebarOpen,
|
||||
})}
|
||||
aria-expanded={sidebarOpen}
|
||||
className={
|
||||
classNames({
|
||||
'z-[1000]': sidebarOpen,
|
||||
hidden: !sidebarOpen,
|
||||
})
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={classNames({
|
||||
'fixed inset-0 bg-gray-500/90 dark:bg-gray-700/90 z-1000': true,
|
||||
'hidden': !sidebarOpen,
|
||||
})}
|
||||
className='fixed inset-0 bg-gray-500/90 dark:bg-gray-700/90'
|
||||
role='button'
|
||||
onClick={handleClose}
|
||||
>
|
||||
<IconButton
|
||||
title={intl.formatMessage(messages.close)}
|
||||
onClick={handleClose}
|
||||
src={require('@tabler/icons/x.svg')}
|
||||
ref={closeButtonRef}
|
||||
iconClassName='h-6 w-6'
|
||||
className='fixed top-5 right-5 text-gray-600 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300'
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
|
||||
<div className='sidebar-menu'>
|
||||
<div className='relative overflow-y-scroll overflow-auto h-full w-full'>
|
||||
<div className='p-4'>
|
||||
<Stack space={4}>
|
||||
<Link to={`/@${account.acct}`} onClick={onClose}>
|
||||
<Account account={account} showProfileHoverCard={false} withLinkToProfile={false} />
|
||||
</Link>
|
||||
|
||||
<ProfileStats
|
||||
account={account}
|
||||
onClickHandler={handleClose}
|
||||
/>
|
||||
<div className='fixed inset-0 z-[1000] flex'>
|
||||
<div
|
||||
className={
|
||||
classNames({
|
||||
'flex flex-col flex-1 bg-white dark:bg-primary-900 -translate-x-full rtl:translate-x-full w-full max-w-xs': true,
|
||||
'!translate-x-0': sidebarOpen,
|
||||
})
|
||||
}
|
||||
>
|
||||
<IconButton
|
||||
title={intl.formatMessage(messages.close)}
|
||||
onClick={handleClose}
|
||||
src={require('@tabler/icons/x.svg')}
|
||||
ref={closeButtonRef}
|
||||
iconClassName='h-6 w-6'
|
||||
className='absolute top-0 right-0 -mr-11 mt-2 text-gray-600 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300'
|
||||
/>
|
||||
|
||||
<div className='relative overflow-y-scroll overflow-auto h-full w-full'>
|
||||
<div className='p-4'>
|
||||
<Stack space={4}>
|
||||
<Divider />
|
||||
<Link to={`/@${account.acct}`} onClick={onClose}>
|
||||
<Account account={account} showProfileHoverCard={false} withLinkToProfile={false} />
|
||||
</Link>
|
||||
|
||||
<SidebarLink
|
||||
to={`/@${account.acct}`}
|
||||
icon={require('@tabler/icons/user.svg')}
|
||||
text={intl.formatMessage(messages.profile)}
|
||||
onClick={onClose}
|
||||
<ProfileStats
|
||||
account={account}
|
||||
onClickHandler={handleClose}
|
||||
/>
|
||||
|
||||
{(account.locked || followRequestsCount > 0) && (
|
||||
<SidebarLink
|
||||
to='/follow_requests'
|
||||
icon={require('@tabler/icons/user-plus.svg')}
|
||||
text={intl.formatMessage(messages.followRequests)}
|
||||
onClick={onClose}
|
||||
/>
|
||||
)}
|
||||
|
||||
{features.bookmarks && (
|
||||
<SidebarLink
|
||||
to='/bookmarks'
|
||||
icon={require('@tabler/icons/bookmark.svg')}
|
||||
text={intl.formatMessage(messages.bookmarks)}
|
||||
onClick={onClose}
|
||||
/>
|
||||
)}
|
||||
|
||||
{features.lists && (
|
||||
<SidebarLink
|
||||
to='/lists'
|
||||
icon={require('@tabler/icons/list.svg')}
|
||||
text={intl.formatMessage(messages.lists)}
|
||||
onClick={onClose}
|
||||
/>
|
||||
)}
|
||||
|
||||
{features.events && (
|
||||
<SidebarLink
|
||||
to='/events'
|
||||
icon={require('@tabler/icons/calendar-event.svg')}
|
||||
text={intl.formatMessage(messages.events)}
|
||||
onClick={onClose}
|
||||
/>
|
||||
)}
|
||||
|
||||
{settings.get('isDeveloper') && (
|
||||
<SidebarLink
|
||||
to='/developers'
|
||||
icon={require('@tabler/icons/code.svg')}
|
||||
text={intl.formatMessage(messages.developers)}
|
||||
onClick={onClose}
|
||||
/>
|
||||
)}
|
||||
|
||||
{features.publicTimeline && <>
|
||||
<Stack space={4}>
|
||||
<Divider />
|
||||
|
||||
<SidebarLink
|
||||
to='/timeline/local'
|
||||
icon={features.federating ? require('@tabler/icons/affiliate.svg') : require('@tabler/icons/world.svg')}
|
||||
text={features.federating ? <FormattedMessage id='tabs_bar.local' defaultMessage='Local' /> : <FormattedMessage id='tabs_bar.all' defaultMessage='All' />}
|
||||
to={`/@${account.acct}`}
|
||||
icon={require('@tabler/icons/user.svg')}
|
||||
text={intl.formatMessage(messages.profile)}
|
||||
onClick={onClose}
|
||||
/>
|
||||
|
||||
{(account.locked || followRequestsCount > 0) && (
|
||||
<SidebarLink
|
||||
to='/follow_requests'
|
||||
icon={require('@tabler/icons/user-plus.svg')}
|
||||
text={intl.formatMessage(messages.followRequests)}
|
||||
onClick={onClose}
|
||||
/>
|
||||
)}
|
||||
|
||||
{features.bookmarks && (
|
||||
<SidebarLink
|
||||
to='/bookmarks'
|
||||
icon={require('@tabler/icons/bookmark.svg')}
|
||||
text={intl.formatMessage(messages.bookmarks)}
|
||||
onClick={onClose}
|
||||
/>
|
||||
)}
|
||||
|
||||
{features.lists && (
|
||||
<SidebarLink
|
||||
to='/lists'
|
||||
icon={require('@tabler/icons/list.svg')}
|
||||
text={intl.formatMessage(messages.lists)}
|
||||
onClick={onClose}
|
||||
/>
|
||||
)}
|
||||
|
||||
{features.events && (
|
||||
<SidebarLink
|
||||
to='/events'
|
||||
icon={require('@tabler/icons/calendar-event.svg')}
|
||||
text={intl.formatMessage(messages.events)}
|
||||
onClick={onClose}
|
||||
/>
|
||||
)}
|
||||
|
||||
{settings.get('isDeveloper') && (
|
||||
<SidebarLink
|
||||
to='/developers'
|
||||
icon={require('@tabler/icons/code.svg')}
|
||||
text={intl.formatMessage(messages.developers)}
|
||||
onClick={onClose}
|
||||
/>
|
||||
)}
|
||||
|
||||
{features.publicTimeline && <>
|
||||
<Divider />
|
||||
|
||||
<SidebarLink
|
||||
to='/timeline/local'
|
||||
icon={features.federating ? require('@tabler/icons/affiliate.svg') : require('@tabler/icons/world.svg')}
|
||||
text={features.federating ? <FormattedMessage id='tabs_bar.local' defaultMessage='Local' /> : <FormattedMessage id='tabs_bar.all' defaultMessage='All' />}
|
||||
onClick={onClose}
|
||||
/>
|
||||
|
||||
{features.federating && (
|
||||
<SidebarLink
|
||||
to='/timeline/fediverse'
|
||||
icon={require('@tabler/icons/topology-star-ring-3.svg')}
|
||||
text={<FormattedMessage id='tabs_bar.fediverse' defaultMessage='Fediverse' />}
|
||||
onClick={onClose}
|
||||
/>
|
||||
)}
|
||||
</>}
|
||||
|
||||
<Divider />
|
||||
|
||||
<SidebarLink
|
||||
to='/blocks'
|
||||
icon={require('@tabler/icons/ban.svg')}
|
||||
text={intl.formatMessage(messages.blocks)}
|
||||
onClick={onClose}
|
||||
/>
|
||||
|
||||
<SidebarLink
|
||||
to='/mutes'
|
||||
icon={require('@tabler/icons/circle-x.svg')}
|
||||
text={intl.formatMessage(messages.mutes)}
|
||||
onClick={onClose}
|
||||
/>
|
||||
|
||||
<SidebarLink
|
||||
to='/settings/preferences'
|
||||
icon={require('@tabler/icons/settings.svg')}
|
||||
text={intl.formatMessage(messages.preferences)}
|
||||
onClick={onClose}
|
||||
/>
|
||||
|
||||
{features.federating && (
|
||||
<SidebarLink
|
||||
to='/timeline/fediverse'
|
||||
icon={require('@tabler/icons/topology-star-ring-3.svg')}
|
||||
text={<FormattedMessage id='tabs_bar.fediverse' defaultMessage='Fediverse' />}
|
||||
to='/domain_blocks'
|
||||
icon={require('@tabler/icons/ban.svg')}
|
||||
text={intl.formatMessage(messages.domainBlocks)}
|
||||
onClick={onClose}
|
||||
/>
|
||||
)}
|
||||
</>}
|
||||
|
||||
<Divider />
|
||||
|
||||
<SidebarLink
|
||||
to='/blocks'
|
||||
icon={require('@tabler/icons/ban.svg')}
|
||||
text={intl.formatMessage(messages.blocks)}
|
||||
onClick={onClose}
|
||||
/>
|
||||
|
||||
<SidebarLink
|
||||
to='/mutes'
|
||||
icon={require('@tabler/icons/circle-x.svg')}
|
||||
text={intl.formatMessage(messages.mutes)}
|
||||
onClick={onClose}
|
||||
/>
|
||||
|
||||
<SidebarLink
|
||||
to='/settings/preferences'
|
||||
icon={require('@tabler/icons/settings.svg')}
|
||||
text={intl.formatMessage(messages.preferences)}
|
||||
onClick={onClose}
|
||||
/>
|
||||
|
||||
{features.federating && (
|
||||
<SidebarLink
|
||||
to='/domain_blocks'
|
||||
icon={require('@tabler/icons/ban.svg')}
|
||||
text={intl.formatMessage(messages.domainBlocks)}
|
||||
onClick={onClose}
|
||||
/>
|
||||
)}
|
||||
|
||||
{features.filters && (
|
||||
<SidebarLink
|
||||
to='/filters'
|
||||
icon={require('@tabler/icons/filter.svg')}
|
||||
text={intl.formatMessage(messages.filters)}
|
||||
onClick={onClose}
|
||||
/>
|
||||
)}
|
||||
|
||||
{account.admin && (
|
||||
<SidebarLink
|
||||
to='/soapbox/config'
|
||||
icon={require('@tabler/icons/settings.svg')}
|
||||
text={intl.formatMessage(messages.soapboxConfig)}
|
||||
onClick={onClose}
|
||||
/>
|
||||
)}
|
||||
|
||||
{features.import && (
|
||||
<SidebarLink
|
||||
to='/settings/import'
|
||||
icon={require('@tabler/icons/cloud-upload.svg')}
|
||||
text={intl.formatMessage(messages.importData)}
|
||||
onClick={onClose}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Divider />
|
||||
|
||||
<SidebarLink
|
||||
to='/logout'
|
||||
icon={require('@tabler/icons/logout.svg')}
|
||||
text={intl.formatMessage(messages.logout)}
|
||||
onClick={onClickLogOut}
|
||||
/>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Stack space={4}>
|
||||
<button type='button' onClick={handleSwitcherClick} className='py-1'>
|
||||
<HStack alignItems='center' justifyContent='between'>
|
||||
<Text tag='span'>
|
||||
<FormattedMessage id='profile_dropdown.switch_account' defaultMessage='Switch accounts' />
|
||||
</Text>
|
||||
|
||||
<Icon
|
||||
src={require('@tabler/icons/chevron-down.svg')}
|
||||
className={classNames('w-4 h-4 text-gray-900 dark:text-gray-100 transition-transform', {
|
||||
'rotate-180': switcher,
|
||||
})}
|
||||
/>
|
||||
</HStack>
|
||||
</button>
|
||||
|
||||
{switcher && (
|
||||
<div className='border-t-2 border-gray-100 dark:border-gray-800 border-solid'>
|
||||
{otherAccounts.map(account => renderAccount(account))}
|
||||
|
||||
<NavLink className='flex items-center py-2 space-x-1' to='/login/add' onClick={handleClose}>
|
||||
<Icon className='text-primary-500 w-4 h-4' src={require('@tabler/icons/plus.svg')} />
|
||||
<Text size='sm' weight='medium'>{intl.formatMessage(messages.addAccount)}</Text>
|
||||
</NavLink>
|
||||
</div>
|
||||
{features.filters && (
|
||||
<SidebarLink
|
||||
to='/filters'
|
||||
icon={require('@tabler/icons/filter.svg')}
|
||||
text={intl.formatMessage(messages.filters)}
|
||||
onClick={onClose}
|
||||
/>
|
||||
)}
|
||||
|
||||
{account.admin && (
|
||||
<SidebarLink
|
||||
to='/soapbox/config'
|
||||
icon={require('@tabler/icons/settings.svg')}
|
||||
text={intl.formatMessage(messages.soapboxConfig)}
|
||||
onClick={onClose}
|
||||
/>
|
||||
)}
|
||||
|
||||
{features.import && (
|
||||
<SidebarLink
|
||||
to='/settings/import'
|
||||
icon={require('@tabler/icons/cloud-upload.svg')}
|
||||
text={intl.formatMessage(messages.importData)}
|
||||
onClick={onClose}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Divider />
|
||||
|
||||
<SidebarLink
|
||||
to='/logout'
|
||||
icon={require('@tabler/icons/logout.svg')}
|
||||
text={intl.formatMessage(messages.logout)}
|
||||
onClick={onClickLogOut}
|
||||
/>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Stack space={4}>
|
||||
<button type='button' onClick={handleSwitcherClick} className='py-1'>
|
||||
<HStack alignItems='center' justifyContent='between'>
|
||||
<Text tag='span'>
|
||||
<FormattedMessage id='profile_dropdown.switch_account' defaultMessage='Switch accounts' />
|
||||
</Text>
|
||||
|
||||
<Icon
|
||||
src={require('@tabler/icons/chevron-down.svg')}
|
||||
className={classNames('w-4 h-4 text-gray-900 dark:text-gray-100 transition-transform', {
|
||||
'rotate-180': switcher,
|
||||
})}
|
||||
/>
|
||||
</HStack>
|
||||
</button>
|
||||
|
||||
{switcher && (
|
||||
<div className='border-t-2 border-gray-100 dark:border-gray-800 border-solid'>
|
||||
{otherAccounts.map(account => renderAccount(account))}
|
||||
|
||||
<NavLink className='flex items-center py-2 space-x-1' to='/login/add' onClick={handleClose}>
|
||||
<Icon className='text-primary-500 w-4 h-4' src={require('@tabler/icons/plus.svg')} />
|
||||
<Text size='sm' weight='medium'>{intl.formatMessage(messages.addAccount)}</Text>
|
||||
</NavLink>
|
||||
</div>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Dummy element to keep Close Icon visible */}
|
||||
<div
|
||||
aria-hidden
|
||||
className='w-14 flex-shrink-0'
|
||||
onClick={handleClose}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -11,7 +11,7 @@ const Select = React.forwardRef<HTMLSelectElement, ISelect>((props, ref) => {
|
||||
return (
|
||||
<select
|
||||
ref={ref}
|
||||
className={`w-full pl-3 pr-10 py-2 text-base border-gray-300 dark:border-gray-800 focus:outline-none focus:ring-primary-500 focus:border-primary-500 dark:bg-gray-900 dark:text-gray-100 dark:ring-1 dark:ring-gray-800 dark:focus:ring-primary-500 dark:focus:border-primary-500 sm:text-sm rounded-md disabled:opacity-50 ${className}`}
|
||||
className={`w-full pl-3 pr-10 py-2 text-base truncate border-gray-300 dark:border-gray-800 focus:outline-none focus:ring-primary-500 focus:border-primary-500 dark:bg-gray-900 dark:text-gray-100 dark:ring-1 dark:ring-gray-800 dark:focus:ring-primary-500 dark:focus:border-primary-500 sm:text-sm rounded-md disabled:opacity-50 ${className}`}
|
||||
{...filteredProps}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -2,6 +2,3 @@
|
||||
|
||||
import 'intersection-observer';
|
||||
import 'requestidlecallback';
|
||||
import objectFitImages from 'object-fit-images';
|
||||
|
||||
objectFitImages();
|
||||
|
||||
@@ -95,7 +95,7 @@ const AccountGallery = () => {
|
||||
const media = (attachment.status as Status).media_attachments;
|
||||
const index = media.findIndex((x) => x.id === attachment.id);
|
||||
|
||||
dispatch(openModal('MEDIA', { media, index, status: attachment.status, account: attachment.account }));
|
||||
dispatch(openModal('MEDIA', { media, index, status: attachment.status }));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { __stub } from 'soapbox/api';
|
||||
import { ChatContext } from 'soapbox/contexts/chat-context';
|
||||
import { StatProvider } from 'soapbox/contexts/stat-context';
|
||||
import chats from 'soapbox/jest/fixtures/chats.json';
|
||||
import { render, screen, waitFor } from 'soapbox/jest/test-helpers';
|
||||
import { mockStore, render, rootState, screen, waitFor } from 'soapbox/jest/test-helpers';
|
||||
|
||||
import ChatPane from '../chat-pane';
|
||||
|
||||
@@ -23,7 +23,12 @@ const renderComponentWithChatContext = (store = {}) => render(
|
||||
|
||||
describe('<ChatPane />', () => {
|
||||
describe('when there are no chats', () => {
|
||||
let store: ReturnType<typeof mockStore>;
|
||||
|
||||
beforeEach(() => {
|
||||
const state = rootState.setIn(['instance', 'version'], '2.7.2 (compatible; Pleroma 2.2.0)');
|
||||
store = mockStore(state);
|
||||
|
||||
__stub((mock) => {
|
||||
mock.onGet('/api/v1/pleroma/chats').reply(200, [], {
|
||||
link: null,
|
||||
@@ -32,7 +37,7 @@ describe('<ChatPane />', () => {
|
||||
});
|
||||
|
||||
it('renders the blankslate', async () => {
|
||||
renderComponentWithChatContext();
|
||||
renderComponentWithChatContext(store);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('chat-pane-blankslate')).toBeInTheDocument();
|
||||
@@ -57,4 +62,4 @@ describe('<ChatPane />', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@ import React from 'react';
|
||||
|
||||
import { __stub } from 'soapbox/api';
|
||||
|
||||
import { render, screen } from '../../../../jest/test-helpers';
|
||||
import { render, screen, waitFor } from '../../../../jest/test-helpers';
|
||||
import Search from '../search';
|
||||
|
||||
describe('<Search />', () => {
|
||||
@@ -22,7 +22,9 @@ describe('<Search />', () => {
|
||||
|
||||
await user.type(screen.getByLabelText('Search'), '@jus');
|
||||
|
||||
expect(screen.getByLabelText('Search')).toHaveValue('@jus');
|
||||
expect(screen.getByTestId('account')).toBeInTheDocument();
|
||||
await waitFor(() => {
|
||||
expect(screen.getByLabelText('Search')).toHaveValue('@jus');
|
||||
expect(screen.getByTestId('account')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -29,7 +29,7 @@ const PlaceholderMediaGallery: React.FC<IPlaceholderMediaGallery> = ({ media, de
|
||||
let itemsDimensions: Record<string, string>[] = [];
|
||||
|
||||
if (size === 1) {
|
||||
style.height = width;
|
||||
style.height = width! * 9 / 16;
|
||||
|
||||
itemsDimensions = [
|
||||
{ w: '100%', h: '100%' },
|
||||
|
||||
@@ -52,7 +52,7 @@ const Settings = () => {
|
||||
const isMfaEnabled = mfa.getIn(['settings', 'totp']);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchMfa());
|
||||
if (features.security) dispatch(fetchMfa());
|
||||
}, [dispatch]);
|
||||
|
||||
if (!account) return null;
|
||||
|
||||
@@ -111,7 +111,7 @@ const Card: React.FC<ICard> = ({
|
||||
|
||||
// Constrain to a sane limit
|
||||
// https://en.wikipedia.org/wiki/Aspect_ratio_(image)
|
||||
return Math.min(Math.max(1, ratio), 4);
|
||||
return Math.min(Math.max(9 / 16, ratio), 4);
|
||||
};
|
||||
|
||||
const interactive = card.type !== 'link';
|
||||
|
||||
@@ -27,7 +27,7 @@ const FloatingActionButton: React.FC<IFloatingActionButton> = () => {
|
||||
onClick={handleOpenComposeModal}
|
||||
className={clsx(
|
||||
'p-4 inline-flex items-center border font-medium rounded-full focus:outline-none focus:ring-2 focus:ring-offset-2 appearance-none transition-all',
|
||||
'bg-primary-500 hover:bg-primary-400 dark:hover:bg-primary-600 border-transparent focus:bg-primary-500 text-gray-100 focus:ring-primary-300',
|
||||
'border-transparent bg-secondary-500 hover:bg-secondary-400 focus:bg-secondary-500 text-gray-100 focus:ring-secondary-300',
|
||||
)}
|
||||
aria-label={intl.formatMessage(messages.publish)}
|
||||
>
|
||||
|
||||
@@ -13,7 +13,7 @@ import Video from 'soapbox/features/video';
|
||||
import ImageLoader from '../image-loader';
|
||||
|
||||
import type { List as ImmutableList } from 'immutable';
|
||||
import type { Account, Attachment, Status } from 'soapbox/types/entities';
|
||||
import type { Attachment, Status } from 'soapbox/types/entities';
|
||||
|
||||
const messages = defineMessages({
|
||||
close: { id: 'lightbox.close', defaultMessage: 'Close' },
|
||||
@@ -24,7 +24,6 @@ const messages = defineMessages({
|
||||
interface IMediaModal {
|
||||
media: ImmutableList<Attachment>,
|
||||
status?: Status,
|
||||
account: Account,
|
||||
index: number,
|
||||
time?: number,
|
||||
onClose: () => void,
|
||||
@@ -34,7 +33,6 @@ const MediaModal: React.FC<IMediaModal> = (props) => {
|
||||
const {
|
||||
media,
|
||||
status,
|
||||
account,
|
||||
onClose,
|
||||
time = 0,
|
||||
} = props;
|
||||
@@ -94,9 +92,9 @@ const MediaModal: React.FC<IMediaModal> = (props) => {
|
||||
};
|
||||
|
||||
const handleStatusClick: React.MouseEventHandler = e => {
|
||||
if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||
if (status && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||
e.preventDefault();
|
||||
history.push(`/@${account.acct}/posts/${status?.id}`);
|
||||
history.push(`/@${status.getIn(['account', 'acct'])}/posts/${status?.id}`);
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
@@ -170,7 +168,7 @@ const MediaModal: React.FC<IMediaModal> = (props) => {
|
||||
const width = (attachment.meta.getIn(['original', 'width']) || undefined) as number | undefined;
|
||||
const height = (attachment.meta.getIn(['original', 'height']) || undefined) as number | undefined;
|
||||
|
||||
const link = (status && account && (
|
||||
const link = (status && (
|
||||
<a href={status.url} onClick={handleStatusClick}>
|
||||
<FormattedMessage id='lightbox.view_context' defaultMessage='View context' />
|
||||
</a>
|
||||
|
||||
@@ -31,7 +31,7 @@ const ProfileMediaPanel: React.FC<IProfileMediaPanel> = ({ account }) => {
|
||||
const media = attachment.getIn(['status', 'media_attachments']) as ImmutableList<Attachment>;
|
||||
const index = media.findIndex(x => x.id === attachment.id);
|
||||
|
||||
dispatch(openModal('MEDIA', { media, index, status: attachment.status, account: attachment.account }));
|
||||
dispatch(openModal('MEDIA', { media, index, status: attachment.status }));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -28,15 +28,13 @@ function loadPolyfills() {
|
||||
window.Symbol
|
||||
);
|
||||
|
||||
// Latest version of Firefox and Safari do not have IntersectionObserver.
|
||||
// Edge does not have requestIdleCallback and object-fit CSS property.
|
||||
// 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 &&
|
||||
'object-fit' in (new Image()).style
|
||||
window.requestIdleCallback
|
||||
);
|
||||
|
||||
return Promise.all([
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -110,12 +110,15 @@
|
||||
"admin.reports.empty_message": "There are no open reports. If a user gets reported, they will show up here.",
|
||||
"admin.reports.report_closed_message": "Report on @{name} was closed",
|
||||
"admin.reports.report_title": "Report on {acct}",
|
||||
"admin.software.backend": "Backend",
|
||||
"admin.software.frontend": "Frontend",
|
||||
"admin.statuses.actions.delete_status": "Delete post",
|
||||
"admin.statuses.actions.mark_status_not_sensitive": "Mark post not sensitive",
|
||||
"admin.statuses.actions.mark_status_sensitive": "Mark post sensitive",
|
||||
"admin.statuses.status_deleted_message": "Post by @{acct} was deleted",
|
||||
"admin.statuses.status_marked_message_not_sensitive": "Post by @{acct} was marked not sensitive",
|
||||
"admin.statuses.status_marked_message_sensitive": "Post by @{acct} was marked sensitive",
|
||||
"admin.theme.title": "Theme",
|
||||
"admin.user_index.empty": "No users found.",
|
||||
"admin.user_index.search_input_placeholder": "Who are you looking for?",
|
||||
"admin.users.actions.deactivate_user": "Deactivate @{name}",
|
||||
@@ -147,7 +150,6 @@
|
||||
"alert.unexpected.links.support": "Support",
|
||||
"alert.unexpected.message": "Something went wrong.",
|
||||
"alert.unexpected.return_home": "Return Home",
|
||||
"alert.unexpected.title": "Oops!",
|
||||
"aliases.account.add": "Create alias",
|
||||
"aliases.account_label": "Old account:",
|
||||
"aliases.aliases_list_delete": "Unlink alias",
|
||||
@@ -186,19 +188,78 @@
|
||||
"bundle_modal_error.message": "Something went wrong while loading this modal.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"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",
|
||||
"chat.actions.send": "Send",
|
||||
"chat.failed_to_send": "Message failed to send.",
|
||||
"chat.input.placeholder": "Type a message",
|
||||
"chat.page_settings.accepting_messages.label": "Allow users to start a new chat with you",
|
||||
"chat.page_settings.play_sounds.label": "Play a sound when you receive a message",
|
||||
"chat.page_settings.preferences": "Preferences",
|
||||
"chat.page_settings.privacy": "Privacy",
|
||||
"chat.page_settings.submit": "Save",
|
||||
"chat.page_settings.title": "Message Settings",
|
||||
"chat.retry": "Retry?",
|
||||
"chat.welcome.accepting_messages.label": "Allow users to start a new chat with you",
|
||||
"chat.welcome.notice": "You can change these settings later.",
|
||||
"chat.welcome.submit": "Save & Continue",
|
||||
"chat.welcome.subtitle": "Exchange direct messages with other users.",
|
||||
"chat.welcome.title": "Welcome to {br} Chats!",
|
||||
"chat_composer.unblock": "Unblock",
|
||||
"chat_list_item.blocked_you": "This user has blocked you",
|
||||
"chat_list_item.blocking": "You have blocked this user",
|
||||
"chat_message_list.blocked": "You blocked this user",
|
||||
"chat_message_list.blockedBy": "You are blocked by",
|
||||
"chat_message_list.network_failure.action": "Try again",
|
||||
"chat_message_list.network_failure.subtitle": "We encountered a network failure.",
|
||||
"chat_message_list.network_failure.title": "Whoops!",
|
||||
"chat_message_list_intro.actions.accept": "Accept",
|
||||
"chat_message_list_intro.actions.leave_chat": "Leave chat",
|
||||
"chat_message_list_intro.actions.message_lifespan": "Messages older than {day} days are deleted.",
|
||||
"chat_message_list_intro.actions.report": "Report",
|
||||
"chat_message_list_intro.intro": "wants to start a chat with you",
|
||||
"chat_message_list_intro.leave_chat.confirm": "Leave Chat",
|
||||
"chat_message_list_intro.leave_chat.heading": "Leave Chat",
|
||||
"chat_message_list_intro.leave_chat.message": "Are you sure you want to leave this chat? Messages will be deleted for you and this chat will be removed from your inbox.",
|
||||
"chat_search.blankslate.body": "Search for someone to chat with.",
|
||||
"chat_search.blankslate.title": "Start a chat",
|
||||
"chat_search.empty_results_blankslate.action": "Message someone",
|
||||
"chat_search.empty_results_blankslate.body": "Try searching for another name.",
|
||||
"chat_search.empty_results_blankslate.title": "No matches found",
|
||||
"chat_search.title": "Messages",
|
||||
"chat_settings.auto_delete.14days": "14 days",
|
||||
"chat_settings.auto_delete.2minutes": "2 minutes",
|
||||
"chat_settings.auto_delete.30days": "30 days",
|
||||
"chat_settings.auto_delete.7days": "7 days",
|
||||
"chat_settings.auto_delete.90days": "90 days",
|
||||
"chat_settings.auto_delete.days": "{day} days",
|
||||
"chat_settings.auto_delete.hint": "Sent messages will auto-delete after the time period selected",
|
||||
"chat_settings.auto_delete.label": "Auto-delete messages",
|
||||
"chat_settings.block.confirm": "Block",
|
||||
"chat_settings.block.heading": "Block @{acct}",
|
||||
"chat_settings.block.message": "Blocking will prevent this profile from direct messaging you and viewing your content. You can unblock later.",
|
||||
"chat_settings.leave.confirm": "Leave Chat",
|
||||
"chat_settings.leave.heading": "Leave Chat",
|
||||
"chat_settings.leave.message": "Are you sure you want to leave this chat? Messages will be deleted for you and this chat will be removed from your inbox.",
|
||||
"chat_settings.options.block_user": "Block @{acct}",
|
||||
"chat_settings.options.leave_chat": "Leave Chat",
|
||||
"chat_settings.options.report_user": "Report @{acct}",
|
||||
"chat_settings.options.unblock_user": "Unblock @{acct}",
|
||||
"chat_settings.title": "Chat Details",
|
||||
"chat_settings.unblock.confirm": "Unblock",
|
||||
"chat_settings.unblock.heading": "Unblock @{acct}",
|
||||
"chat_settings.unblock.message": "Unblocking will allow this profile to direct message you and view your content.",
|
||||
"chat_window.auto_delete_label": "Auto-delete after {day} days",
|
||||
"chat_window.auto_delete_tooltip": "Chat messages are set to auto-delete after {day} days upon sending.",
|
||||
"chats.actions.copy": "Copy",
|
||||
"chats.actions.delete": "Delete message",
|
||||
"chats.actions.deleteForMe": "Delete for me",
|
||||
"chats.actions.more": "More",
|
||||
"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.main.blankslate.new_chat": "Message someone",
|
||||
"chats.main.blankslate.subtitle": "Search for someone to chat with",
|
||||
"chats.main.blankslate.title": "No messages yet",
|
||||
"chats.main.blankslate_with_chats.subtitle": "Select from one of your open chats or create a new message.",
|
||||
"chats.main.blankslate_with_chats.title": "Select a chat",
|
||||
"chats.search_placeholder": "Start a chat with…",
|
||||
"column.admin.awaiting_approval": "Awaiting Approval",
|
||||
"column.admin.dashboard": "Dashboard",
|
||||
@@ -226,6 +287,9 @@
|
||||
"column.directory": "Browse profiles",
|
||||
"column.domain_blocks": "Hidden domains",
|
||||
"column.edit_profile": "Edit profile",
|
||||
"column.event_map": "Event location",
|
||||
"column.event_participants": "Event participants",
|
||||
"column.events": "Events",
|
||||
"column.export_data": "Export data",
|
||||
"column.familiar_followers": "People you know following {name}",
|
||||
"column.favourited_statuses": "Liked posts",
|
||||
@@ -268,15 +332,14 @@
|
||||
"column.pins": "Pinned posts",
|
||||
"column.preferences": "Preferences",
|
||||
"column.public": "Federated timeline",
|
||||
"column.quotes": "Post quotes",
|
||||
"column.reactions": "Reactions",
|
||||
"column.reblogs": "Reposts",
|
||||
"column.remote": "Federated timeline",
|
||||
"column.scheduled_statuses": "Scheduled Posts",
|
||||
"column.search": "Search",
|
||||
"column.settings_store": "Settings store",
|
||||
"column.soapbox_config": "Soapbox config",
|
||||
"column.test": "Test timeline",
|
||||
"column_back_button.label": "Back",
|
||||
"column_forbidden.body": "You do not have permission to access this page.",
|
||||
"column_forbidden.title": "Forbidden",
|
||||
"common.cancel": "Cancel",
|
||||
@@ -286,7 +349,33 @@
|
||||
"compose.edit_success": "Your post was edited",
|
||||
"compose.invalid_schedule": "You must schedule a post at least 5 minutes out.",
|
||||
"compose.submit_success": "Your post was sent!",
|
||||
"compose_event.create": "Create",
|
||||
"compose_event.edit_success": "Your event was edited",
|
||||
"compose_event.fields.approval_required": "I want to approve participation requests manually",
|
||||
"compose_event.fields.banner_label": "Event banner",
|
||||
"compose_event.fields.description_hint": "Markdown syntax is supported",
|
||||
"compose_event.fields.description_label": "Event description",
|
||||
"compose_event.fields.description_placeholder": "Description",
|
||||
"compose_event.fields.end_time_label": "Event end date",
|
||||
"compose_event.fields.end_time_placeholder": "Event ends on…",
|
||||
"compose_event.fields.has_end_time": "The event has end date",
|
||||
"compose_event.fields.location_label": "Event location",
|
||||
"compose_event.fields.name_label": "Event name",
|
||||
"compose_event.fields.name_placeholder": "Name",
|
||||
"compose_event.fields.start_time_label": "Event start date",
|
||||
"compose_event.fields.start_time_placeholder": "Event begins on…",
|
||||
"compose_event.participation_requests.authorize": "Authorize",
|
||||
"compose_event.participation_requests.authorize_success": "User accepted",
|
||||
"compose_event.participation_requests.reject": "Reject",
|
||||
"compose_event.participation_requests.reject_success": "User rejected",
|
||||
"compose_event.reset_location": "Reset location",
|
||||
"compose_event.submit_success": "Your event was created",
|
||||
"compose_event.tabs.edit": "Edit details",
|
||||
"compose_event.tabs.pending": "Manage requests",
|
||||
"compose_event.update": "Update",
|
||||
"compose_event.upload_banner": "Upload event banner",
|
||||
"compose_form.direct_message_warning": "This post will only be sent to the mentioned users.",
|
||||
"compose_form.event_placeholder": "Post to this event",
|
||||
"compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
|
||||
"compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
|
||||
"compose_form.lock_disclaimer.lock": "locked",
|
||||
@@ -342,15 +431,22 @@
|
||||
"confirmations.cancel_editing.confirm": "Cancel editing",
|
||||
"confirmations.cancel_editing.heading": "Cancel post editing",
|
||||
"confirmations.cancel_editing.message": "Are you sure you want to cancel editing this post? All changes will be lost.",
|
||||
"confirmations.cancel_event_editing.heading": "Cancel event editing",
|
||||
"confirmations.cancel_event_editing.message": "Are you sure you want to cancel editing this event? All changes will be lost.",
|
||||
"confirmations.delete.confirm": "Delete",
|
||||
"confirmations.delete.heading": "Delete post",
|
||||
"confirmations.delete.message": "Are you sure you want to delete this post?",
|
||||
"confirmations.delete_event.confirm": "Delete",
|
||||
"confirmations.delete_event.heading": "Delete event",
|
||||
"confirmations.delete_event.message": "Are you sure you want to delete this event?",
|
||||
"confirmations.delete_list.confirm": "Delete",
|
||||
"confirmations.delete_list.heading": "Delete list",
|
||||
"confirmations.delete_list.message": "Are you sure you want to permanently delete this list?",
|
||||
"confirmations.domain_block.confirm": "Hide entire domain",
|
||||
"confirmations.domain_block.heading": "Block {domain}",
|
||||
"confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications.",
|
||||
"confirmations.leave_event.confirm": "Leave event",
|
||||
"confirmations.leave_event.message": "If you want to rejoin the event, the request will be manually reviewed again. Are you sure you want to proceed?",
|
||||
"confirmations.mute.confirm": "Mute",
|
||||
"confirmations.mute.heading": "Mute @{name}",
|
||||
"confirmations.mute.message": "Are you sure you want to mute {name}?",
|
||||
@@ -400,6 +496,7 @@
|
||||
"developers.navigation.network_error_label": "Network error",
|
||||
"developers.navigation.service_worker_label": "Service Worker",
|
||||
"developers.navigation.settings_store_label": "Settings store",
|
||||
"developers.navigation.show_toast": "Trigger Toast",
|
||||
"developers.navigation.test_timeline_label": "Test timeline",
|
||||
"developers.settings_store.advanced": "Advanced settings",
|
||||
"developers.settings_store.hint": "It is possible to directly edit your user settings here. BE CAREFUL! Editing this section can break your account, and you will only be able to recover through the API.",
|
||||
@@ -498,6 +595,8 @@
|
||||
"empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
|
||||
"empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
|
||||
"empty_column.domain_blocks": "There are no hidden domains yet.",
|
||||
"empty_column.event_participant_requests": "There are no pending event participation requests.",
|
||||
"empty_column.event_participants": "No one joined this event yet. When someone does, they will show up here.",
|
||||
"empty_column.favourited_statuses": "You don't have any liked posts yet. When you like one, it will show up here.",
|
||||
"empty_column.favourites": "No one has liked this post yet. When someone does, they will show up here.",
|
||||
"empty_column.filters": "You haven't created any muted words yet.",
|
||||
@@ -514,12 +613,36 @@
|
||||
"empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.",
|
||||
"empty_column.notifications_filtered": "You don't have any notifications of this type yet.",
|
||||
"empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other servers to fill it up",
|
||||
"empty_column.quotes": "This post has not been quoted yet.",
|
||||
"empty_column.remote": "There is nothing here! Manually follow users from {instance} to fill it up.",
|
||||
"empty_column.scheduled_statuses": "You don't have any scheduled statuses yet. When you add one, it will show up here.",
|
||||
"empty_column.search.accounts": "There are no people results for \"{term}\"",
|
||||
"empty_column.search.hashtags": "There are no hashtags results for \"{term}\"",
|
||||
"empty_column.search.statuses": "There are no posts results for \"{term}\"",
|
||||
"empty_column.test": "The test timeline is empty.",
|
||||
"event.banner": "Event banner",
|
||||
"event.copy": "Copy link to event",
|
||||
"event.date": "Date",
|
||||
"event.description": "Description",
|
||||
"event.discussion.empty": "No one has commented this event yet. When someone does, they will appear here.",
|
||||
"event.export_ics": "Export to your calendar",
|
||||
"event.external": "View event on {domain}",
|
||||
"event.join_state.accept": "Going",
|
||||
"event.join_state.empty": "Participate",
|
||||
"event.join_state.pending": "Pending",
|
||||
"event.join_state.rejected": "Going",
|
||||
"event.location": "Location",
|
||||
"event.manage": "Manage",
|
||||
"event.organized_by": "Organized by {name}",
|
||||
"event.participants": "{count} {rawCount, plural, one {person} other {people}} going",
|
||||
"event.show_on_map": "Show on map",
|
||||
"event.website": "External links",
|
||||
"event_map.navigate": "Navigate",
|
||||
"events.create_event": "Create event",
|
||||
"events.joined_events": "Joined events",
|
||||
"events.joined_events.empty": "You haven't joined any event yet.",
|
||||
"events.recent_events": "Recent events",
|
||||
"events.recent_events.empty": "There are no public events yet.",
|
||||
"export_data.actions.export": "Export",
|
||||
"export_data.actions.export_blocks": "Export blocks",
|
||||
"export_data.actions.export_follows": "Export follows",
|
||||
@@ -600,6 +723,12 @@
|
||||
"intervals.full.days": "{number, plural, one {# day} other {# days}}",
|
||||
"intervals.full.hours": "{number, plural, one {# hour} other {# hours}}",
|
||||
"intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}",
|
||||
"join_event.hint": "You can tell the organizer why do you want to participate in this event:",
|
||||
"join_event.join": "Request join",
|
||||
"join_event.placeholder": "Message to organizer",
|
||||
"join_event.request_success": "Requested to join the event",
|
||||
"join_event.success": "Joined the event",
|
||||
"join_event.title": "Join event",
|
||||
"keyboard_shortcuts.back": "to navigate back",
|
||||
"keyboard_shortcuts.blocked": "to open blocked users list",
|
||||
"keyboard_shortcuts.boost": "to repost",
|
||||
@@ -648,6 +777,7 @@
|
||||
"lists.search": "Search among people you follow",
|
||||
"lists.subheading": "Your lists",
|
||||
"loading_indicator.label": "Loading…",
|
||||
"location_search.placeholder": "Find an address",
|
||||
"login.fields.instance_label": "Instance",
|
||||
"login.fields.instance_placeholder": "example.com",
|
||||
"login.fields.otp_code_hint": "Enter the two-factor code generated by your phone app or use one of your recovery codes",
|
||||
@@ -696,6 +826,8 @@
|
||||
"missing_description_modal.text": "You have not entered a description for all attachments. Continue anyway?",
|
||||
"missing_indicator.label": "Not found",
|
||||
"missing_indicator.sublabel": "This resource could not be found",
|
||||
"modals.policy.submit": "Accept & Continue",
|
||||
"modals.policy.updateTitle": "You’ve scored the latest version of {siteTitle}! Take a moment to review the exciting new things we’ve been working on.",
|
||||
"moderation_overlay.contact": "Contact",
|
||||
"moderation_overlay.hide": "Hide content",
|
||||
"moderation_overlay.show": "Show Content",
|
||||
@@ -722,8 +854,10 @@
|
||||
"navigation_bar.compose": "Compose a post",
|
||||
"navigation_bar.compose_direct": "Direct message",
|
||||
"navigation_bar.compose_edit": "Edit post",
|
||||
"navigation_bar.compose_event": "Manage event",
|
||||
"navigation_bar.compose_quote": "Quote post",
|
||||
"navigation_bar.compose_reply": "Reply to post",
|
||||
"navigation_bar.create_event": "Create new event",
|
||||
"navigation_bar.domain_blocks": "Domain blocks",
|
||||
"navigation_bar.favourites": "Likes",
|
||||
"navigation_bar.filters": "Filters",
|
||||
@@ -746,6 +880,9 @@
|
||||
"notification.others": " + {count} {count, plural, one {other} other {others}}",
|
||||
"notification.pleroma:chat_mention": "{name} sent you a message",
|
||||
"notification.pleroma:emoji_reaction": "{name} reacted to your post",
|
||||
"notification.pleroma:event_reminder": "An event you are participating in starts soon",
|
||||
"notification.pleroma:participation_accepted": "You were accepted to join the event",
|
||||
"notification.pleroma:participation_request": "{name} wants to join your event",
|
||||
"notification.poll": "A poll you have voted in has ended",
|
||||
"notification.reblog": "{name} reposted your post",
|
||||
"notification.status": "{name} just posted",
|
||||
@@ -819,6 +956,8 @@
|
||||
"preferences.fields.content_type_label": "Default post format",
|
||||
"preferences.fields.delete_modal_label": "Show confirmation dialog before deleting a post",
|
||||
"preferences.fields.demetricator_label": "Use Demetricator",
|
||||
"preferences.fields.demo_hint": "Use the default Soapbox logo and color scheme. Useful for taking screenshots.",
|
||||
"preferences.fields.demo_label": "Demo mode",
|
||||
"preferences.fields.display_media.default": "Hide posts marked as sensitive",
|
||||
"preferences.fields.display_media.hide_all": "Always hide posts",
|
||||
"preferences.fields.display_media.show_all": "Always show posts",
|
||||
@@ -834,7 +973,6 @@
|
||||
"preferences.fields.underline_links_label": "Always underline links in posts",
|
||||
"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.feed": "In your home feed",
|
||||
"preferences.notifications.advanced": "Show all notification categories",
|
||||
"preferences.options.content_type_markdown": "Markdown",
|
||||
"preferences.options.content_type_plaintext": "Plain text",
|
||||
@@ -907,6 +1045,8 @@
|
||||
"remote_instance.unpin_host": "Unpin {host}",
|
||||
"remote_interaction.account_placeholder": "Enter your username@domain you want to act from",
|
||||
"remote_interaction.divider": "or",
|
||||
"remote_interaction.event_join": "Proceed to join",
|
||||
"remote_interaction.event_join_title": "Join an event remotely",
|
||||
"remote_interaction.favourite": "Proceed to like",
|
||||
"remote_interaction.favourite_title": "Like a post remotely",
|
||||
"remote_interaction.follow": "Proceed to follow",
|
||||
@@ -928,6 +1068,8 @@
|
||||
"reply_mentions.reply_empty": "Replying to post",
|
||||
"report.block": "Block {target}",
|
||||
"report.block_hint": "Do you also want to block this account?",
|
||||
"report.chatMessage.context": "When reporting a user’s message, the five messages before and five messages after the one selected will be passed along to our moderation team for context.",
|
||||
"report.chatMessage.title": "Report message",
|
||||
"report.confirmation.content": "If we find that this account is violating the {link} we will take further action on the matter.",
|
||||
"report.confirmation.title": "Thanks for submitting your report.",
|
||||
"report.done": "Done",
|
||||
@@ -989,6 +1131,7 @@
|
||||
"settings.configure_mfa": "Configure MFA",
|
||||
"settings.delete_account": "Delete Account",
|
||||
"settings.edit_profile": "Edit Profile",
|
||||
"settings.messages.label": "Allow users to start a new chat with you",
|
||||
"settings.other": "Other options",
|
||||
"settings.preferences": "Preferences",
|
||||
"settings.profile": "Profile",
|
||||
@@ -1016,7 +1159,6 @@
|
||||
"sms_verification.sent.body": "We sent you a 6-digit code via SMS. Enter it below.",
|
||||
"sms_verification.sent.header": "Verification code",
|
||||
"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_label": "Profiles require authentication",
|
||||
"soapbox_config.copyright_footer.meta_fields.label_placeholder": "Copyright footer",
|
||||
@@ -1029,9 +1171,8 @@
|
||||
"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_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.edit_theme_label": "Edit theme",
|
||||
"soapbox_config.fields.home_footer_fields_label": "Home footer items",
|
||||
"soapbox_config.fields.logo_label": "Logo",
|
||||
"soapbox_config.fields.promo_panel_fields_label": "Promo panel items",
|
||||
@@ -1039,6 +1180,7 @@
|
||||
"soapbox_config.greentext_label": "Enable greentext support",
|
||||
"soapbox_config.headings.advanced": "Advanced",
|
||||
"soapbox_config.headings.cryptocurrency": "Cryptocurrency",
|
||||
"soapbox_config.headings.events": "Events",
|
||||
"soapbox_config.headings.navigation": "Navigation",
|
||||
"soapbox_config.headings.options": "Options",
|
||||
"soapbox_config.headings.theme": "Theme",
|
||||
@@ -1060,6 +1202,8 @@
|
||||
"soapbox_config.single_user_mode_label": "Single user mode",
|
||||
"soapbox_config.single_user_mode_profile_hint": "@handle",
|
||||
"soapbox_config.single_user_mode_profile_label": "Main user handle",
|
||||
"soapbox_config.tile_server_attribution_label": "Map tiles attribution",
|
||||
"soapbox_config.tile_server_label": "Map tile server",
|
||||
"soapbox_config.verified_can_edit_name_label": "Allow verified users to edit their own display name.",
|
||||
"sponsored.info.message": "{siteTitle} displays ads to help fund our service.",
|
||||
"sponsored.info.title": "Why am I seeing this ad?",
|
||||
@@ -1081,6 +1225,7 @@
|
||||
"status.favourite": "Like",
|
||||
"status.filtered": "Filtered",
|
||||
"status.interactions.favourites": "{count, plural, one {Like} other {Likes}}",
|
||||
"status.interactions.quotes": "{count, plural, one {Quote} other {Quotes}}",
|
||||
"status.interactions.reblogs": "{count, plural, one {Repost} other {Reposts}}",
|
||||
"status.load_more": "Load more",
|
||||
"status.mention": "Mention @{name}",
|
||||
@@ -1139,7 +1284,6 @@
|
||||
"sw.update_text": "An update is available.",
|
||||
"sw.url": "Script URL",
|
||||
"tabs_bar.all": "All",
|
||||
"tabs_bar.chats": "Chats",
|
||||
"tabs_bar.dashboard": "Dashboard",
|
||||
"tabs_bar.fediverse": "Fediverse",
|
||||
"tabs_bar.home": "Home",
|
||||
@@ -1149,6 +1293,13 @@
|
||||
"tabs_bar.profile": "Profile",
|
||||
"tabs_bar.search": "Search",
|
||||
"tabs_bar.settings": "Settings",
|
||||
"theme_editor.Reset": "Reset",
|
||||
"theme_editor.export": "Export theme",
|
||||
"theme_editor.import": "Import theme",
|
||||
"theme_editor.import_success": "Theme was successfully imported!",
|
||||
"theme_editor.restore": "Restore default theme",
|
||||
"theme_editor.save": "Save theme",
|
||||
"theme_editor.saved": "Theme updated!",
|
||||
"theme_toggle.dark": "Dark",
|
||||
"theme_toggle.light": "Light",
|
||||
"theme_toggle.system": "System",
|
||||
@@ -1161,10 +1312,10 @@
|
||||
"time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left",
|
||||
"time_remaining.moments": "Moments remaining",
|
||||
"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.title": "Trends",
|
||||
"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.title": "Sign up for {site_title}",
|
||||
"upload_area.title": "Drag & drop to upload",
|
||||
|
||||
@@ -112,6 +112,17 @@ const fixAkkoma = (instance: ImmutableMap<string, any>) => {
|
||||
}
|
||||
};
|
||||
|
||||
/** Set Takahe version to a Pleroma-like string */
|
||||
const fixTakahe = (instance: ImmutableMap<string, any>) => {
|
||||
const version: string = instance.get('version', '');
|
||||
|
||||
if (version.startsWith('takahe/')) {
|
||||
return instance.set('version', `0.0.0 (compatible; Takahe ${version.slice(7)})`);
|
||||
} else {
|
||||
return instance;
|
||||
}
|
||||
};
|
||||
|
||||
// Normalize instance (Pleroma, Mastodon, etc.) to Mastodon's format
|
||||
export const normalizeInstance = (instance: Record<string, any>) => {
|
||||
return InstanceRecord(
|
||||
@@ -131,6 +142,7 @@ export const normalizeInstance = (instance: Record<string, any>) => {
|
||||
|
||||
// Normalize version
|
||||
normalizeVersion(instance);
|
||||
fixTakahe(instance);
|
||||
fixAkkoma(instance);
|
||||
|
||||
// Merge defaults
|
||||
|
||||
@@ -178,7 +178,8 @@ describe('useChats', () => {
|
||||
|
||||
describe('with a successful request', () => {
|
||||
beforeEach(() => {
|
||||
store = mockStore(rootState);
|
||||
const state = rootState.setIn(['instance', 'version'], '2.7.2 (compatible; Pleroma 2.2.0)');
|
||||
store = mockStore(state);
|
||||
|
||||
__stub((mock) => {
|
||||
mock.onGet('/api/v1/pleroma/chats')
|
||||
@@ -378,4 +379,4 @@ describe('useChatActions', () => {
|
||||
expect((nextQueryData as any).message_expiration).toBe(1200);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -157,6 +157,7 @@ const useChats = (search?: string) => {
|
||||
|
||||
const queryInfo = useInfiniteQuery(ChatKeys.chatSearch(search), ({ pageParam }) => getChats(pageParam), {
|
||||
keepPreviousData: true,
|
||||
enabled: features.chats,
|
||||
getNextPageParam: (config) => {
|
||||
if (config.hasMore) {
|
||||
return { link: config.link };
|
||||
|
||||
@@ -64,6 +64,12 @@ export const GLITCH = 'glitch';
|
||||
*/
|
||||
export const AKKOMA = 'akkoma';
|
||||
|
||||
/**
|
||||
* Takahē, backend with support for serving multiple domains.
|
||||
* @see {@link https://jointakahe.org/}
|
||||
*/
|
||||
export const TAKAHE = 'Takahe';
|
||||
|
||||
/** Parse features for the given instance */
|
||||
const getInstanceFeatures = (instance: Instance) => {
|
||||
const v = parseVersion(instance.version);
|
||||
@@ -288,6 +294,7 @@ const getInstanceFeatures = (instance: Instance) => {
|
||||
v.software === MASTODON && gte(v.compatVersion, '2.6.0'),
|
||||
v.software === PLEROMA && gte(v.version, '0.9.9'),
|
||||
v.software === PIXELFED,
|
||||
v.software === TAKAHE,
|
||||
]),
|
||||
|
||||
/**
|
||||
@@ -299,6 +306,14 @@ const getInstanceFeatures = (instance: Instance) => {
|
||||
v.software === PLEROMA && gte(v.version, '0.9.9'),
|
||||
]),
|
||||
|
||||
editProfile: any([
|
||||
v.software === MASTODON,
|
||||
v.software === MITRA,
|
||||
v.software === PIXELFED,
|
||||
v.software === PLEROMA,
|
||||
v.software === TRUTHSOCIAL,
|
||||
]),
|
||||
|
||||
editStatuses: any([
|
||||
v.software === MASTODON && gte(v.version, '3.5.0'),
|
||||
features.includes('editing'),
|
||||
@@ -574,6 +589,7 @@ const getInstanceFeatures = (instance: Instance) => {
|
||||
publicTimeline: any([
|
||||
v.software === MASTODON,
|
||||
v.software === PLEROMA,
|
||||
v.software === TAKAHE,
|
||||
]),
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export const minimumAspectRatio = 1; // Square
|
||||
export const minimumAspectRatio = 9 / 16; // Portrait phone
|
||||
export const maximumAspectRatio = 10; // Generous min-height
|
||||
|
||||
export const isPanoramic = (ar: number) => {
|
||||
|
||||
Reference in New Issue
Block a user