Merge branch 'links-a11y' into 'develop'
Prefer accessible links See merge request soapbox-pub/soapbox!2648
This commit is contained in:
commit
631c94f771
9 changed files with 148 additions and 185 deletions
|
@ -1,5 +1,6 @@
|
|||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { SelectDropdown } from '../features/forms';
|
||||
|
@ -17,13 +18,14 @@ const List: React.FC<IList> = ({ children }) => (
|
|||
interface IListItem {
|
||||
label: React.ReactNode
|
||||
hint?: React.ReactNode
|
||||
to?: string
|
||||
onClick?(): void
|
||||
onSelect?(): void
|
||||
isSelected?: boolean
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
const ListItem: React.FC<IListItem> = ({ label, hint, children, onClick, onSelect, isSelected }) => {
|
||||
const ListItem: React.FC<IListItem> = ({ label, hint, children, to, onClick, onSelect, isSelected }) => {
|
||||
const id = uuidv4();
|
||||
const domId = `list-group-${id}`;
|
||||
|
||||
|
@ -33,9 +35,7 @@ const ListItem: React.FC<IListItem> = ({ label, hint, children, onClick, onSelec
|
|||
}
|
||||
};
|
||||
|
||||
const Comp = onClick ? 'a' : 'div';
|
||||
const LabelComp = onClick || onSelect ? 'span' : 'label';
|
||||
const linkProps = onClick || onSelect ? { onClick: onClick || onSelect, onKeyDown, tabIndex: 0, role: 'link' } : {};
|
||||
const LabelComp = to || onClick || onSelect ? 'span' : 'label';
|
||||
|
||||
const renderChildren = React.useCallback(() => {
|
||||
return React.Children.map(children, (child) => {
|
||||
|
@ -55,13 +55,12 @@ const ListItem: React.FC<IListItem> = ({ label, hint, children, onClick, onSelec
|
|||
});
|
||||
}, [children, domId]);
|
||||
|
||||
return (
|
||||
<Comp
|
||||
className={clsx('flex items-center justify-between overflow-hidden bg-gradient-to-r from-gradient-start/20 to-gradient-end/20 px-4 py-2 first:rounded-t-lg last:rounded-b-lg dark:from-gradient-start/10 dark:to-gradient-end/10', {
|
||||
'cursor-pointer hover:from-gradient-start/30 hover:to-gradient-end/30 dark:hover:from-gradient-start/5 dark:hover:to-gradient-end/5': typeof onClick !== 'undefined' || typeof onSelect !== 'undefined',
|
||||
})}
|
||||
{...linkProps}
|
||||
>
|
||||
const className = clsx('flex items-center justify-between overflow-hidden bg-gradient-to-r from-gradient-start/20 to-gradient-end/20 px-4 py-2 first:rounded-t-lg last:rounded-b-lg dark:from-gradient-start/10 dark:to-gradient-end/10', {
|
||||
'cursor-pointer hover:from-gradient-start/30 hover:to-gradient-end/30 dark:hover:from-gradient-start/5 dark:hover:to-gradient-end/5': typeof to !== 'undefined' || typeof onClick !== 'undefined' || typeof onSelect !== 'undefined',
|
||||
});
|
||||
|
||||
const body = (
|
||||
<>
|
||||
<div className='flex flex-col py-1.5 pr-4 rtl:pl-4 rtl:pr-0'>
|
||||
<LabelComp className='text-gray-900 dark:text-gray-100' htmlFor={domId}>{label}</LabelComp>
|
||||
|
||||
|
@ -70,7 +69,7 @@ const ListItem: React.FC<IListItem> = ({ label, hint, children, onClick, onSelec
|
|||
) : null}
|
||||
</div>
|
||||
|
||||
{onClick ? (
|
||||
{(to || onClick) ? (
|
||||
<HStack space={1} alignItems='center' className='overflow-hidden text-gray-700 dark:text-gray-600'>
|
||||
{children}
|
||||
|
||||
|
@ -105,8 +104,24 @@ const ListItem: React.FC<IListItem> = ({ label, hint, children, onClick, onSelec
|
|||
</div>
|
||||
) : null}
|
||||
|
||||
{typeof onClick === 'undefined' && typeof onSelect === 'undefined' ? renderChildren() : null}
|
||||
</Comp>
|
||||
{typeof to === 'undefined' && typeof onClick === 'undefined' && typeof onSelect === 'undefined' ? renderChildren() : null}
|
||||
</>
|
||||
);
|
||||
|
||||
if (to) return (
|
||||
<Link className={className} to={to}>
|
||||
{body}
|
||||
</Link>
|
||||
);
|
||||
|
||||
const Comp = onClick ? 'a' : 'div';
|
||||
const linkProps = onClick || onSelect ? { onClick: onClick || onSelect, onKeyDown, tabIndex: 0, role: 'link' } : {};
|
||||
|
||||
return (
|
||||
<Comp
|
||||
className={className}
|
||||
{...linkProps}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { getSubscribersCsv, getUnsubscribersCsv, getCombinedCsv } from 'soapbox/actions/email-list';
|
||||
import List, { ListItem } from 'soapbox/components/list';
|
||||
|
@ -15,7 +14,6 @@ import RegistrationModePicker from '../components/registration-mode-picker';
|
|||
|
||||
const Dashboard: React.FC = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const history = useHistory();
|
||||
const instance = useInstance();
|
||||
const features = useFeatures();
|
||||
const { account } = useOwnAccount();
|
||||
|
@ -41,10 +39,6 @@ const Dashboard: React.FC = () => {
|
|||
e.preventDefault();
|
||||
};
|
||||
|
||||
const navigateToSoapboxConfig = () => history.push('/soapbox/config');
|
||||
const navigateToModerationLog = () => history.push('/soapbox/admin/log');
|
||||
const navigateToAnnouncements = () => history.push('/soapbox/admin/announcements');
|
||||
|
||||
const v = parseVersion(instance.version);
|
||||
|
||||
const userCount = instance.stats.get('user_count');
|
||||
|
@ -87,19 +81,19 @@ const Dashboard: React.FC = () => {
|
|||
<List>
|
||||
{account.admin && (
|
||||
<ListItem
|
||||
onClick={navigateToSoapboxConfig}
|
||||
to='/soapbox/config'
|
||||
label={<FormattedMessage id='navigation_bar.soapbox_config' defaultMessage='Soapbox config' />}
|
||||
/>
|
||||
)}
|
||||
|
||||
<ListItem
|
||||
onClick={navigateToModerationLog}
|
||||
to='/soapbox/admin/log'
|
||||
label={<FormattedMessage id='column.admin.moderation_log' defaultMessage='Moderation Log' />}
|
||||
/>
|
||||
|
||||
{features.announcements && (
|
||||
<ListItem
|
||||
onClick={navigateToAnnouncements}
|
||||
to='/soapbox/admin/announcements'
|
||||
label={<FormattedMessage id='column.admin.announcements' defaultMessage='Announcements' />}
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { changeEmail } from 'soapbox/actions/security';
|
||||
import { Button, Card, CardBody, CardHeader, CardTitle, Column, Form, FormActions, FormGroup, Input } from 'soapbox/components/ui';
|
||||
import { Button, Column, Form, FormActions, FormGroup, Input } from 'soapbox/components/ui';
|
||||
import { useAppDispatch } from 'soapbox/hooks';
|
||||
import toast from 'soapbox/toast';
|
||||
|
||||
|
@ -48,47 +48,33 @@ const EditEmail = () => {
|
|||
}, [email, password, dispatch, intl]);
|
||||
|
||||
return (
|
||||
<Column
|
||||
label={intl.formatMessage(messages.header)}
|
||||
transparent
|
||||
withHeader={false}
|
||||
>
|
||||
<Card variant='rounded'>
|
||||
<CardHeader backHref='/settings'>
|
||||
<CardTitle
|
||||
title={intl.formatMessage(messages.header)}
|
||||
<Column label={intl.formatMessage(messages.header)} backHref='/settings'>
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<FormGroup labelText={intl.formatMessage(messages.emailFieldLabel)}>
|
||||
<Input
|
||||
type='text'
|
||||
placeholder={intl.formatMessage(messages.emailFieldPlaceholder)}
|
||||
name='email'
|
||||
autoComplete='off'
|
||||
onChange={handleInputChange}
|
||||
value={email}
|
||||
/>
|
||||
</CardHeader>
|
||||
</FormGroup>
|
||||
|
||||
<CardBody>
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<FormGroup labelText={intl.formatMessage(messages.emailFieldLabel)}>
|
||||
<Input
|
||||
type='text'
|
||||
placeholder={intl.formatMessage(messages.emailFieldPlaceholder)}
|
||||
name='email'
|
||||
autoComplete='off'
|
||||
onChange={handleInputChange}
|
||||
value={email}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup labelText={intl.formatMessage(messages.passwordFieldLabel)}>
|
||||
<Input
|
||||
type='password'
|
||||
name='password'
|
||||
onChange={handleInputChange}
|
||||
value={password}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup labelText={intl.formatMessage(messages.passwordFieldLabel)}>
|
||||
<Input
|
||||
type='password'
|
||||
name='password'
|
||||
onChange={handleInputChange}
|
||||
value={password}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormActions>
|
||||
<Button to='/settings' theme='tertiary'>{intl.formatMessage(messages.cancel)}</Button>
|
||||
<Button type='submit' theme='primary' disabled={isLoading}>{intl.formatMessage(messages.submit)}</Button>
|
||||
</FormActions>
|
||||
</Form>
|
||||
</CardBody>
|
||||
</Card>
|
||||
<FormActions>
|
||||
<Button to='/settings' theme='tertiary'>{intl.formatMessage(messages.cancel)}</Button>
|
||||
<Button type='submit' theme='primary' disabled={isLoading}>{intl.formatMessage(messages.submit)}</Button>
|
||||
</FormActions>
|
||||
</Form>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { changePassword } from 'soapbox/actions/security';
|
||||
import { Button, Card, CardBody, CardHeader, CardTitle, Column, Form, FormActions, FormGroup, Input } from 'soapbox/components/ui';
|
||||
import { Button, Column, Form, FormActions, FormGroup, Input } from 'soapbox/components/ui';
|
||||
import { useAppDispatch, useFeatures } from 'soapbox/hooks';
|
||||
import toast from 'soapbox/toast';
|
||||
|
||||
|
@ -55,57 +55,49 @@ const EditPassword = () => {
|
|||
}, [currentPassword, newPassword, newPasswordConfirmation, dispatch, intl]);
|
||||
|
||||
return (
|
||||
<Column label={intl.formatMessage(messages.header)} transparent withHeader={false}>
|
||||
<Card variant='rounded'>
|
||||
<CardHeader backHref='/settings'>
|
||||
<CardTitle title={intl.formatMessage(messages.header)} />
|
||||
</CardHeader>
|
||||
<Column label={intl.formatMessage(messages.header)} backHref='/settings'>
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<FormGroup labelText={intl.formatMessage(messages.oldPasswordFieldLabel)}>
|
||||
<Input
|
||||
type='password'
|
||||
name='currentPassword'
|
||||
onChange={handleInputChange}
|
||||
value={currentPassword}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<CardBody>
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<FormGroup labelText={intl.formatMessage(messages.oldPasswordFieldLabel)}>
|
||||
<Input
|
||||
type='password'
|
||||
name='currentPassword'
|
||||
onChange={handleInputChange}
|
||||
value={currentPassword}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup labelText={intl.formatMessage(messages.newPasswordFieldLabel)}>
|
||||
<Input
|
||||
type='password'
|
||||
name='newPassword'
|
||||
onChange={handleInputChange}
|
||||
value={newPassword}
|
||||
/>
|
||||
|
||||
<FormGroup labelText={intl.formatMessage(messages.newPasswordFieldLabel)}>
|
||||
<Input
|
||||
type='password'
|
||||
name='newPassword'
|
||||
onChange={handleInputChange}
|
||||
value={newPassword}
|
||||
/>
|
||||
{passwordRequirements && (
|
||||
<PasswordIndicator password={newPassword} onChange={setHasValidPassword} />
|
||||
)}
|
||||
</FormGroup>
|
||||
|
||||
{passwordRequirements && (
|
||||
<PasswordIndicator password={newPassword} onChange={setHasValidPassword} />
|
||||
)}
|
||||
</FormGroup>
|
||||
<FormGroup labelText={intl.formatMessage(messages.confirmationFieldLabel)}>
|
||||
<Input
|
||||
type='password'
|
||||
name='newPasswordConfirmation'
|
||||
onChange={handleInputChange}
|
||||
value={newPasswordConfirmation}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup labelText={intl.formatMessage(messages.confirmationFieldLabel)}>
|
||||
<Input
|
||||
type='password'
|
||||
name='newPasswordConfirmation'
|
||||
onChange={handleInputChange}
|
||||
value={newPasswordConfirmation}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormActions>
|
||||
<Button to='/settings' theme='tertiary'>
|
||||
{intl.formatMessage(messages.cancel)}
|
||||
</Button>
|
||||
|
||||
<FormActions>
|
||||
<Button to='/settings' theme='tertiary'>
|
||||
{intl.formatMessage(messages.cancel)}
|
||||
</Button>
|
||||
|
||||
<Button type='submit' theme='primary' disabled={isLoading || !hasValidPassword}>
|
||||
{intl.formatMessage(messages.submit)}
|
||||
</Button>
|
||||
</FormActions>
|
||||
</Form>
|
||||
</CardBody>
|
||||
</Card>
|
||||
<Button type='submit' theme='primary' disabled={isLoading || !hasValidPassword}>
|
||||
{intl.formatMessage(messages.submit)}
|
||||
</Button>
|
||||
</FormActions>
|
||||
</Form>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -76,10 +76,6 @@ const ManageGroup: React.FC<IManageGroup> = ({ params }) => {
|
|||
},
|
||||
}));
|
||||
|
||||
const navigateToEdit = () => history.push(`/group/${group.slug}/manage/edit`);
|
||||
const navigateToPending = () => history.push(`/group/${group.slug}/manage/requests`);
|
||||
const navigateToBlocks = () => history.push(`/group/${group.slug}/manage/blocks`);
|
||||
|
||||
return (
|
||||
<Column label={intl.formatMessage(messages.heading)} backHref={`/group/${group.slug}`}>
|
||||
<CardBody className='space-y-4'>
|
||||
|
@ -90,7 +86,7 @@ const ManageGroup: React.FC<IManageGroup> = ({ params }) => {
|
|||
</CardHeader>
|
||||
|
||||
<List>
|
||||
<ListItem label={intl.formatMessage(messages.editGroup)} onClick={navigateToEdit}>
|
||||
<ListItem label={intl.formatMessage(messages.editGroup)} to={`/group/${group.slug}/manage/edit`}>
|
||||
<span dangerouslySetInnerHTML={{ __html: group.display_name_html }} />
|
||||
</ListItem>
|
||||
</List>
|
||||
|
@ -103,10 +99,10 @@ const ManageGroup: React.FC<IManageGroup> = ({ params }) => {
|
|||
|
||||
<List>
|
||||
{backend.software !== TRUTHSOCIAL && (
|
||||
<ListItem label={intl.formatMessage(messages.pendingRequests)} onClick={navigateToPending} />
|
||||
<ListItem label={intl.formatMessage(messages.pendingRequests)} to={`/group/${group.slug}/manage/requests`} />
|
||||
)}
|
||||
|
||||
<ListItem label={intl.formatMessage(messages.blockedMembers)} onClick={navigateToBlocks} />
|
||||
<ListItem label={intl.formatMessage(messages.blockedMembers)} to={`/group/${group.slug}/manage/blocks`} />
|
||||
</List>
|
||||
|
||||
{isOwner && (
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { Avatar, Button, CardTitle, Stack } from 'soapbox/components/ui';
|
||||
import { type Card as StatusCard } from 'soapbox/types/entities';
|
||||
|
@ -9,13 +8,9 @@ interface IGroupLinkPreview {
|
|||
}
|
||||
|
||||
const GroupLinkPreview: React.FC<IGroupLinkPreview> = ({ card }) => {
|
||||
const history = useHistory();
|
||||
|
||||
const { group } = card;
|
||||
if (!group) return null;
|
||||
|
||||
const navigateToGroup = () => history.push(`/group/${group.slug}`);
|
||||
|
||||
return (
|
||||
<Stack className='cursor-default overflow-hidden rounded-lg border border-gray-300 text-center dark:border-gray-800'>
|
||||
<div
|
||||
|
@ -32,7 +27,7 @@ const GroupLinkPreview: React.FC<IGroupLinkPreview> = ({ card }) => {
|
|||
<Stack space={4} className='p-4'>
|
||||
<CardTitle title={<span dangerouslySetInnerHTML={{ __html: group.display_name_html }} />} />
|
||||
|
||||
<Button theme='primary' onClick={navigateToGroup} block>
|
||||
<Button theme='primary' to={`/group/${group.slug}`} block>
|
||||
View Group
|
||||
</Button>
|
||||
</Stack>
|
||||
|
@ -40,4 +35,4 @@ const GroupLinkPreview: React.FC<IGroupLinkPreview> = ({ card }) => {
|
|||
);
|
||||
};
|
||||
|
||||
export { GroupLinkPreview };
|
||||
export { GroupLinkPreview };
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { fetchMfa } from 'soapbox/actions/mfa';
|
||||
import List, { ListItem } from 'soapbox/components/list';
|
||||
|
@ -38,27 +37,12 @@ const messages = defineMessages({
|
|||
/** User settings page. */
|
||||
const Settings = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const history = useHistory();
|
||||
const intl = useIntl();
|
||||
|
||||
const mfa = useAppSelector((state) => state.security.get('mfa'));
|
||||
const features = useFeatures();
|
||||
const { account } = useOwnAccount();
|
||||
|
||||
const navigateToChangeEmail = () => history.push('/settings/email');
|
||||
const navigateToChangePassword = () => history.push('/settings/password');
|
||||
const navigateToMfa = () => history.push('/settings/mfa');
|
||||
const navigateToSessions = () => history.push('/settings/tokens');
|
||||
const navigateToEditProfile = () => history.push('/settings/profile');
|
||||
const navigateToDeleteAccount = () => history.push('/settings/account');
|
||||
const navigateToMoveAccount = () => history.push('/settings/migration');
|
||||
const navigateToAliases = () => history.push('/settings/aliases');
|
||||
const navigateToBackups = () => history.push('/settings/backups');
|
||||
const navigateToImportData = () => history.push('/settings/import');
|
||||
const navigateToExportData = () => history.push('/settings/export');
|
||||
const navigateToMutes = () => history.push('/mutes');
|
||||
const navigateToBlocks = () => history.push('/blocks');
|
||||
|
||||
const isMfaEnabled = mfa.getIn(['settings', 'totp']);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -78,7 +62,7 @@ const Settings = () => {
|
|||
|
||||
<CardBody>
|
||||
<List>
|
||||
<ListItem label={intl.formatMessage(messages.editProfile)} onClick={navigateToEditProfile}>
|
||||
<ListItem label={intl.formatMessage(messages.editProfile)} to='/settings/profile'>
|
||||
<span className='max-w-full truncate'>{displayName}</span>
|
||||
</ListItem>
|
||||
</List>
|
||||
|
@ -90,8 +74,8 @@ const Settings = () => {
|
|||
|
||||
<CardBody>
|
||||
<List>
|
||||
<ListItem label={intl.formatMessage(messages.mutes)} onClick={navigateToMutes} />
|
||||
<ListItem label={intl.formatMessage(messages.blocks)} onClick={navigateToBlocks} />
|
||||
<ListItem label={intl.formatMessage(messages.mutes)} to='/mutes' />
|
||||
<ListItem label={intl.formatMessage(messages.blocks)} to='/blocks' />
|
||||
</List>
|
||||
</CardBody>
|
||||
|
||||
|
@ -105,9 +89,9 @@ const Settings = () => {
|
|||
<List>
|
||||
{features.security && (
|
||||
<>
|
||||
<ListItem label={intl.formatMessage(messages.changeEmail)} onClick={navigateToChangeEmail} />
|
||||
<ListItem label={intl.formatMessage(messages.changePassword)} onClick={navigateToChangePassword} />
|
||||
<ListItem label={intl.formatMessage(messages.configureMfa)} onClick={navigateToMfa}>
|
||||
<ListItem label={intl.formatMessage(messages.changeEmail)} to='/settings/email' />
|
||||
<ListItem label={intl.formatMessage(messages.changePassword)} to='/settings/password' />
|
||||
<ListItem label={intl.formatMessage(messages.configureMfa)} to='/settings/mfa'>
|
||||
<span>
|
||||
{isMfaEnabled ?
|
||||
intl.formatMessage(messages.mfaEnabled) :
|
||||
|
@ -117,7 +101,7 @@ const Settings = () => {
|
|||
</>
|
||||
)}
|
||||
{features.sessions && (
|
||||
<ListItem label={intl.formatMessage(messages.sessions)} onClick={navigateToSessions} />
|
||||
<ListItem label={intl.formatMessage(messages.sessions)} to='/settings/tokens' />
|
||||
)}
|
||||
</List>
|
||||
</CardBody>
|
||||
|
@ -153,25 +137,25 @@ const Settings = () => {
|
|||
<CardBody>
|
||||
<List>
|
||||
{features.importData && (
|
||||
<ListItem label={intl.formatMessage(messages.importData)} onClick={navigateToImportData} />
|
||||
<ListItem label={intl.formatMessage(messages.importData)} to='/settings/import' />
|
||||
)}
|
||||
|
||||
{features.exportData && (
|
||||
<ListItem label={intl.formatMessage(messages.exportData)} onClick={navigateToExportData} />
|
||||
<ListItem label={intl.formatMessage(messages.exportData)} to='/settings/export' />
|
||||
)}
|
||||
|
||||
{features.backups && (
|
||||
<ListItem label={intl.formatMessage(messages.backups)} onClick={navigateToBackups} />
|
||||
<ListItem label={intl.formatMessage(messages.backups)} to='/settings/backups' />
|
||||
)}
|
||||
|
||||
{features.federating && (features.accountMoving ? (
|
||||
<ListItem label={intl.formatMessage(messages.accountMigration)} onClick={navigateToMoveAccount} />
|
||||
<ListItem label={intl.formatMessage(messages.accountMigration)} to='/settings/migrations' />
|
||||
) : features.accountAliases && (
|
||||
<ListItem label={intl.formatMessage(messages.accountAliases)} onClick={navigateToAliases} />
|
||||
<ListItem label={intl.formatMessage(messages.accountAliases)} to='/settings/aliases' />
|
||||
))}
|
||||
|
||||
{features.security && (
|
||||
<ListItem label={<Text theme='danger'>{intl.formatMessage(messages.deleteAccount)}</Text>} onClick={navigateToDeleteAccount} />
|
||||
<ListItem label={<Text theme='danger'>{intl.formatMessage(messages.deleteAccount)}</Text>} to='/settings/account' />
|
||||
)}
|
||||
</List>
|
||||
</CardBody>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { updateSoapboxConfig } from 'soapbox/actions/admin';
|
||||
import { uploadMedia } from 'soapbox/actions/media';
|
||||
|
@ -70,7 +69,6 @@ const templates: Record<string, Template> = {
|
|||
|
||||
const SoapboxConfig: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
const history = useHistory();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const features = useFeatures();
|
||||
|
@ -83,8 +81,6 @@ const SoapboxConfig: React.FC = () => {
|
|||
const [rawJSON, setRawJSON] = useState<string>(JSON.stringify(initialData, null, 2));
|
||||
const [jsonValid, setJsonValid] = useState(true);
|
||||
|
||||
const navigateToThemeEditor = () => history.push('/soapbox/admin/theme');
|
||||
|
||||
const soapbox = useMemo(() => {
|
||||
return normalizeSoapboxConfig(data);
|
||||
}, [data]);
|
||||
|
@ -211,7 +207,7 @@ const SoapboxConfig: React.FC = () => {
|
|||
|
||||
<ListItem
|
||||
label={<FormattedMessage id='soapbox_config.fields.edit_theme_label' defaultMessage='Edit theme' />}
|
||||
onClick={navigateToThemeEditor}
|
||||
to='/soapbox/admin/theme'
|
||||
/>
|
||||
</List>
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import clsx from 'clsx';
|
|||
import { List as ImmutableList } from 'immutable';
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { openModal } from 'soapbox/actions/modals';
|
||||
import { HStack, Text, Emoji } from 'soapbox/components/ui';
|
||||
|
@ -17,8 +17,6 @@ interface IStatusInteractionBar {
|
|||
}
|
||||
|
||||
const StatusInteractionBar: React.FC<IStatusInteractionBar> = ({ status }): JSX.Element | null => {
|
||||
const history = useHistory();
|
||||
|
||||
const me = useAppSelector(({ me }) => me);
|
||||
const { allowedEmoji } = useSoapboxConfig();
|
||||
const dispatch = useAppDispatch();
|
||||
|
@ -91,16 +89,10 @@ const StatusInteractionBar: React.FC<IStatusInteractionBar> = ({ status }): JSX.
|
|||
return null;
|
||||
};
|
||||
|
||||
const navigateToQuotes: React.EventHandler<React.MouseEvent> = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
history.push(`/@${status.getIn(['account', 'acct'])}/posts/${status.id}/quotes`);
|
||||
};
|
||||
|
||||
const getQuotes = () => {
|
||||
if (status.quotes_count) {
|
||||
return (
|
||||
<InteractionCounter count={status.quotes_count} onClick={navigateToQuotes}>
|
||||
<InteractionCounter count={status.quotes_count} to={`/@${status.getIn(['account', 'acct'])}/posts/${status.id}/quotes`}>
|
||||
<FormattedMessage
|
||||
id='status.interactions.quotes'
|
||||
defaultMessage='{count, plural, one {Quote} other {Quotes}}'
|
||||
|
@ -209,34 +201,47 @@ const StatusInteractionBar: React.FC<IStatusInteractionBar> = ({ status }): JSX.
|
|||
|
||||
interface IInteractionCounter {
|
||||
count: number
|
||||
onClick?: React.MouseEventHandler<HTMLButtonElement>
|
||||
children: React.ReactNode
|
||||
onClick?: React.MouseEventHandler<HTMLButtonElement>
|
||||
to?: string
|
||||
}
|
||||
|
||||
const InteractionCounter: React.FC<IInteractionCounter> = ({ count, onClick, children }) => {
|
||||
const InteractionCounter: React.FC<IInteractionCounter> = ({ count, children, onClick, to }) => {
|
||||
const features = useFeatures();
|
||||
|
||||
const className = clsx({
|
||||
'text-gray-600 dark:text-gray-700': true,
|
||||
'hover:underline': features.exposableReactions,
|
||||
'cursor-default': !features.exposableReactions,
|
||||
});
|
||||
|
||||
const body = (
|
||||
<HStack space={1} alignItems='center'>
|
||||
<Text weight='bold'>
|
||||
{shortNumberFormat(count)}
|
||||
</Text>
|
||||
|
||||
<Text tag='div' theme='muted'>
|
||||
{children}
|
||||
</Text>
|
||||
</HStack>
|
||||
);
|
||||
|
||||
if (to) {
|
||||
return (
|
||||
<Link to={to} className={className}>
|
||||
{body}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
type='button'
|
||||
onClick={onClick}
|
||||
className={
|
||||
clsx({
|
||||
'text-gray-600 dark:text-gray-700': true,
|
||||
'hover:underline': features.exposableReactions,
|
||||
'cursor-default': !features.exposableReactions,
|
||||
})
|
||||
}
|
||||
className={className}
|
||||
>
|
||||
<HStack space={1} alignItems='center'>
|
||||
<Text weight='bold'>
|
||||
{shortNumberFormat(count)}
|
||||
</Text>
|
||||
|
||||
<Text tag='div' theme='muted'>
|
||||
{children}
|
||||
</Text>
|
||||
</HStack>
|
||||
{body}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue