diff --git a/app/soapbox/components/list.tsx b/app/soapbox/components/list.tsx index dad4c972a..cbf183805 100644 --- a/app/soapbox/components/list.tsx +++ b/app/soapbox/components/list.tsx @@ -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 = ({ children }) => ( interface IListItem { label: React.ReactNode hint?: React.ReactNode + to?: string onClick?(): void onSelect?(): void isSelected?: boolean children?: React.ReactNode } -const ListItem: React.FC = ({ label, hint, children, onClick, onSelect, isSelected }) => { +const ListItem: React.FC = ({ label, hint, children, to, onClick, onSelect, isSelected }) => { const id = uuidv4(); const domId = `list-group-${id}`; @@ -33,9 +35,9 @@ const ListItem: React.FC = ({ 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 Comp = to ? Link : (onClick ? 'a' : 'div'); + const LabelComp = to || onClick || onSelect ? 'span' : 'label'; + const linkProps = to ? { to } : (onClick || onSelect ? { onClick: onClick || onSelect, onKeyDown, tabIndex: 0, role: 'link' } : {}); const renderChildren = React.useCallback(() => { return React.Children.map(children, (child) => { @@ -58,7 +60,7 @@ const ListItem: React.FC = ({ label, hint, children, onClick, onSelec return ( @@ -70,7 +72,7 @@ const ListItem: React.FC = ({ label, hint, children, onClick, onSelec ) : null} - {onClick ? ( + {(to || onClick) ? ( {children} @@ -105,7 +107,7 @@ const ListItem: React.FC = ({ label, hint, children, onClick, onSelec ) : null} - {typeof onClick === 'undefined' && typeof onSelect === 'undefined' ? renderChildren() : null} + {typeof to === 'undefined' && typeof onClick === 'undefined' && typeof onSelect === 'undefined' ? renderChildren() : null} ); }; diff --git a/app/soapbox/features/admin/tabs/dashboard.tsx b/app/soapbox/features/admin/tabs/dashboard.tsx index 9f7279a40..b8b9efcac 100644 --- a/app/soapbox/features/admin/tabs/dashboard.tsx +++ b/app/soapbox/features/admin/tabs/dashboard.tsx @@ -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 = () => { {account.admin && ( } /> )} } /> {features.announcements && ( } /> )} diff --git a/app/soapbox/features/edit-email/index.tsx b/app/soapbox/features/edit-email/index.tsx index ee7e1d35c..70d511ce1 100644 --- a/app/soapbox/features/edit-email/index.tsx +++ b/app/soapbox/features/edit-email/index.tsx @@ -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 ( - - - - +
+ + - + - - - - - + + + - - - - - - - - - -
-
+ + + + +
); }; diff --git a/app/soapbox/features/edit-password/index.tsx b/app/soapbox/features/edit-password/index.tsx index eb6b1c100..026515d53 100644 --- a/app/soapbox/features/edit-password/index.tsx +++ b/app/soapbox/features/edit-password/index.tsx @@ -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 ( - - - - - + +
+ + + - - - - - + + - - + {passwordRequirements && ( + + )} + - {passwordRequirements && ( - - )} - + + + - - - + + - - - - - - -
-
+ + +
); }; diff --git a/app/soapbox/features/group/manage-group.tsx b/app/soapbox/features/group/manage-group.tsx index d57b8792a..625d28478 100644 --- a/app/soapbox/features/group/manage-group.tsx +++ b/app/soapbox/features/group/manage-group.tsx @@ -76,10 +76,6 @@ const ManageGroup: React.FC = ({ 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 ( @@ -90,7 +86,7 @@ const ManageGroup: React.FC = ({ params }) => { - + @@ -103,10 +99,10 @@ const ManageGroup: React.FC = ({ params }) => { {backend.software !== TRUTHSOCIAL && ( - + )} - + {isOwner && ( diff --git a/app/soapbox/features/groups/components/group-link-preview.tsx b/app/soapbox/features/groups/components/group-link-preview.tsx index 98ca03076..0b2cb7cdd 100644 --- a/app/soapbox/features/groups/components/group-link-preview.tsx +++ b/app/soapbox/features/groups/components/group-link-preview.tsx @@ -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 = ({ card }) => { - const history = useHistory(); - const { group } = card; if (!group) return null; - const navigateToGroup = () => history.push(`/group/${group.slug}`); - return (
= ({ card }) => { } /> - @@ -40,4 +35,4 @@ const GroupLinkPreview: React.FC = ({ card }) => { ); }; -export { GroupLinkPreview }; \ No newline at end of file +export { GroupLinkPreview }; diff --git a/app/soapbox/features/settings/index.tsx b/app/soapbox/features/settings/index.tsx index 96b349bd4..1cd30fa4b 100644 --- a/app/soapbox/features/settings/index.tsx +++ b/app/soapbox/features/settings/index.tsx @@ -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 = () => { - + {displayName} @@ -90,8 +74,8 @@ const Settings = () => { - - + + @@ -105,9 +89,9 @@ const Settings = () => { {features.security && ( <> - - - + + + {isMfaEnabled ? intl.formatMessage(messages.mfaEnabled) : @@ -117,7 +101,7 @@ const Settings = () => { )} {features.sessions && ( - + )} @@ -153,25 +137,25 @@ const Settings = () => { {features.importData && ( - + )} {features.exportData && ( - + )} {features.backups && ( - + )} {features.federating && (features.accountMoving ? ( - + ) : features.accountAliases && ( - + ))} {features.security && ( - {intl.formatMessage(messages.deleteAccount)}} onClick={navigateToDeleteAccount} /> + {intl.formatMessage(messages.deleteAccount)}} to='/settings/account' /> )} diff --git a/app/soapbox/features/soapbox-config/index.tsx b/app/soapbox/features/soapbox-config/index.tsx index b700e6401..15295e212 100644 --- a/app/soapbox/features/soapbox-config/index.tsx +++ b/app/soapbox/features/soapbox-config/index.tsx @@ -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 = { 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(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 = () => { } - onClick={navigateToThemeEditor} + to='/soapbox/admin/theme' /> diff --git a/app/soapbox/features/status/components/status-interaction-bar.tsx b/app/soapbox/features/status/components/status-interaction-bar.tsx index bbbf0c115..9647120ff 100644 --- a/app/soapbox/features/status/components/status-interaction-bar.tsx +++ b/app/soapbox/features/status/components/status-interaction-bar.tsx @@ -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 = ({ 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 = ({ status }): JSX. return null; }; - const navigateToQuotes: React.EventHandler = (e) => { - e.preventDefault(); - - history.push(`/@${status.getIn(['account', 'acct'])}/posts/${status.id}/quotes`); - }; - const getQuotes = () => { if (status.quotes_count) { return ( - + = ({ status }): JSX. interface IInteractionCounter { count: number - onClick?: React.MouseEventHandler children: React.ReactNode + onClick?: React.MouseEventHandler + to?: string } -const InteractionCounter: React.FC = ({ count, onClick, children }) => { +const InteractionCounter: React.FC = ({ 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 = ( + + + {shortNumberFormat(count)} + + + + {children} + + + ); + + if (to) { + return ( + + {body} + + ); + } + return ( ); };