diff --git a/app/soapbox/containers/status_container.tsx b/app/soapbox/containers/status_container.tsx index 3f7c1a34f..89c387ffd 100644 --- a/app/soapbox/containers/status_container.tsx +++ b/app/soapbox/containers/status_container.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import Status, { IStatus } from 'soapbox/components/status'; import { useAppSelector } from 'soapbox/hooks'; @@ -16,14 +16,14 @@ interface IStatusContainer extends Omit { updateScrollBottom?: any, } -const getStatus = makeGetStatus(); - /** * Legacy Status wrapper accepting a status ID instead of the full entity. * @deprecated Use the Status component directly. */ const StatusContainer: React.FC = (props) => { const { id, ...rest } = props; + + const getStatus = useCallback(makeGetStatus(), []); const status = useAppSelector(state => getStatus(state, { id })); if (status) { diff --git a/app/soapbox/features/admin/components/report.tsx b/app/soapbox/features/admin/components/report.tsx index fb1c5bf5d..75c3ecb1d 100644 --- a/app/soapbox/features/admin/components/report.tsx +++ b/app/soapbox/features/admin/components/report.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useCallback, useState } from 'react'; import { useIntl, FormattedMessage, defineMessages } from 'react-intl'; import { Link } from 'react-router-dom'; @@ -10,7 +10,8 @@ import HoverRefWrapper from 'soapbox/components/hover_ref_wrapper'; import { Button, HStack } from 'soapbox/components/ui'; import DropdownMenu from 'soapbox/containers/dropdown_menu_container'; import Accordion from 'soapbox/features/ui/components/accordion'; -import { useAppDispatch } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; +import { makeGetReport } from 'soapbox/selectors'; import ReportStatus from './report_status'; @@ -24,15 +25,21 @@ const messages = defineMessages({ }); interface IReport { - report: AdminReport; + id: string; } -const Report: React.FC = ({ report }) => { +const Report: React.FC = ({ id }) => { const intl = useIntl(); const dispatch = useAppDispatch(); + const getReport = useCallback(makeGetReport(), []); + + const report = useAppSelector((state) => getReport(state, id) as AdminReport | undefined); + const [accordionExpanded, setAccordionExpanded] = useState(false); + if (!report) return null; + const account = report.account as Account; const targetAccount = report.target_account as Account; diff --git a/app/soapbox/features/admin/components/unapproved_account.tsx b/app/soapbox/features/admin/components/unapproved_account.tsx index b5a08c956..a01421643 100644 --- a/app/soapbox/features/admin/components/unapproved_account.tsx +++ b/app/soapbox/features/admin/components/unapproved_account.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import { approveUsers } from 'soapbox/actions/admin'; @@ -13,8 +13,6 @@ const messages = defineMessages({ rejected: { id: 'admin.awaiting_approval.rejected_message', defaultMessage: '{acct} was rejected.' }, }); -const getAccount = makeGetAccount(); - interface IUnapprovedAccount { accountId: string, } @@ -23,6 +21,7 @@ interface IUnapprovedAccount { const UnapprovedAccount: React.FC = ({ accountId }) => { const intl = useIntl(); const dispatch = useAppDispatch(); + const getAccount = useCallback(makeGetAccount(), []); const account = useAppSelector(state => getAccount(state, accountId)); const adminAccount = useAppSelector(state => state.admin.users.get(accountId)); diff --git a/app/soapbox/features/admin/tabs/reports.tsx b/app/soapbox/features/admin/tabs/reports.tsx index 8a2eca8c7..5c2854cc4 100644 --- a/app/soapbox/features/admin/tabs/reports.tsx +++ b/app/soapbox/features/admin/tabs/reports.tsx @@ -4,7 +4,6 @@ import { defineMessages, useIntl } from 'react-intl'; import { fetchReports } from 'soapbox/actions/admin'; import ScrollableList from 'soapbox/components/scrollable_list'; import { useAppSelector, useAppDispatch } from 'soapbox/hooks'; -import { makeGetReport } from 'soapbox/selectors'; import Report from '../components/report'; @@ -14,18 +13,13 @@ const messages = defineMessages({ emptyMessage: { id: 'admin.reports.empty_message', defaultMessage: 'There are no open reports. If a user gets reported, they will show up here.' }, }); -const getReport = makeGetReport(); - const Reports: React.FC = () => { const intl = useIntl(); const dispatch = useAppDispatch(); const [isLoading, setLoading] = useState(true); - const reports = useAppSelector(state => { - const ids = state.admin.openReports; - return ids.toList().map(id => getReport(state, id)); - }); + const reports = useAppSelector(state => state.admin.openReports.toList()); useEffect(() => { dispatch(fetchReports()) @@ -42,7 +36,7 @@ const Reports: React.FC = () => { scrollKey='admin-reports' emptyMessage={intl.formatMessage(messages.emptyMessage)} > - {reports.map(report => report && )} + {reports.map(report => report && )} ); }; diff --git a/app/soapbox/features/aliases/components/account.tsx b/app/soapbox/features/aliases/components/account.tsx index 3d24c0543..be9178b8d 100644 --- a/app/soapbox/features/aliases/components/account.tsx +++ b/app/soapbox/features/aliases/components/account.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import { addToAliases } from 'soapbox/actions/aliases'; @@ -15,8 +15,6 @@ const messages = defineMessages({ add: { id: 'aliases.account.add', defaultMessage: 'Create alias' }, }); -const getAccount = makeGetAccount(); - interface IAccount { accountId: string, aliases: ImmutableList @@ -25,6 +23,8 @@ interface IAccount { const Account: React.FC = ({ accountId, aliases }) => { const intl = useIntl(); const dispatch = useAppDispatch(); + + const getAccount = useCallback(makeGetAccount(), []); const account = useAppSelector((state) => getAccount(state, accountId)); const added = useAppSelector((state) => { diff --git a/app/soapbox/features/birthdays/account.tsx b/app/soapbox/features/birthdays/account.tsx index 738ad5ec9..745ca5268 100644 --- a/app/soapbox/features/birthdays/account.tsx +++ b/app/soapbox/features/birthdays/account.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import Avatar from 'soapbox/components/avatar'; @@ -12,14 +12,14 @@ const messages = defineMessages({ birthday: { id: 'account.birthday', defaultMessage: 'Born {date}' }, }); -const getAccount = makeGetAccount(); - interface IAccount { accountId: string, } const Account: React.FC = ({ accountId }) => { const intl = useIntl(); + const getAccount = useCallback(makeGetAccount(), []); + const account = useAppSelector((state) => getAccount(state, accountId)); // useEffect(() => { @@ -30,7 +30,7 @@ const Account: React.FC = ({ accountId }) => { if (!account) return null; - const birthday = account.get('birthday'); + const birthday = account.birthday; if (!birthday) return null; const formattedBirthday = intl.formatDate(birthday, { day: 'numeric', month: 'short', year: 'numeric' }); @@ -38,7 +38,7 @@ const Account: React.FC = ({ accountId }) => { return (
- +
diff --git a/app/soapbox/features/chats/components/chat.tsx b/app/soapbox/features/chats/components/chat.tsx index 13d2b7fee..2083ce19d 100644 --- a/app/soapbox/features/chats/components/chat.tsx +++ b/app/soapbox/features/chats/components/chat.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import { FormattedMessage } from 'react-intl'; import Avatar from 'soapbox/components/avatar'; @@ -11,14 +11,13 @@ import { makeGetChat } from 'soapbox/selectors'; import type { Account as AccountEntity, Chat as ChatEntity } from 'soapbox/types/entities'; -const getChat = makeGetChat(); - interface IChat { chatId: string, onClick: (chat: any) => void, } const Chat: React.FC = ({ chatId, onClick }) => { + const getChat = useCallback(makeGetChat(), []); const chat = useAppSelector((state) => { const chat = state.chats.items.get(chatId); return chat ? getChat(state, (chat as any).toJS()) : undefined; diff --git a/app/soapbox/features/compose/components/autosuggest_account.tsx b/app/soapbox/features/compose/components/autosuggest_account.tsx index 6b65f8341..511d65fe6 100644 --- a/app/soapbox/features/compose/components/autosuggest_account.tsx +++ b/app/soapbox/features/compose/components/autosuggest_account.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import Account from 'soapbox/components/account'; import { useAppSelector } from 'soapbox/hooks'; @@ -9,7 +9,7 @@ interface IAutosuggestAccount { } const AutosuggestAccount: React.FC = ({ id }) => { - const getAccount = makeGetAccount(); + const getAccount = useCallback(makeGetAccount(), []); const account = useAppSelector((state) => getAccount(state, id)); if (!account) return null; diff --git a/app/soapbox/features/compose/components/reply_mentions.tsx b/app/soapbox/features/compose/components/reply_mentions.tsx index 8a7be612f..c66370859 100644 --- a/app/soapbox/features/compose/components/reply_mentions.tsx +++ b/app/soapbox/features/compose/components/reply_mentions.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import { FormattedList, FormattedMessage } from 'react-intl'; import { useDispatch } from 'react-redux'; @@ -12,8 +12,10 @@ import type { Status as StatusEntity } from 'soapbox/types/entities'; const ReplyMentions: React.FC = () => { const dispatch = useDispatch(); + const getStatus = useCallback(makeGetStatus(), []); + const instance = useAppSelector((state) => state.instance); - const status = useAppSelector(state => makeGetStatus()(state, { id: state.compose.in_reply_to! })); + const status = useAppSelector(state => getStatus(state, { id: state.compose.in_reply_to! })); const to = useAppSelector((state) => state.compose.to); const account = useAppSelector((state) => state.accounts.get(state.me)); diff --git a/app/soapbox/features/compose/containers/quoted_status_container.tsx b/app/soapbox/features/compose/containers/quoted_status_container.tsx index 1c34f6825..10290786f 100644 --- a/app/soapbox/features/compose/containers/quoted_status_container.tsx +++ b/app/soapbox/features/compose/containers/quoted_status_container.tsx @@ -1,15 +1,15 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import { cancelQuoteCompose } from 'soapbox/actions/compose'; import QuotedStatus from 'soapbox/components/quoted-status'; import { useAppSelector, useAppDispatch } from 'soapbox/hooks'; import { makeGetStatus } from 'soapbox/selectors'; -const getStatus = makeGetStatus(); - /** QuotedStatus shown in post composer. */ const QuotedStatusContainer: React.FC = () => { const dispatch = useAppDispatch(); + const getStatus = useCallback(makeGetStatus(), []); + const status = useAppSelector(state => getStatus(state, { id: state.compose.quote! })); const onCancel = () => { diff --git a/app/soapbox/features/embedded-status/index.tsx b/app/soapbox/features/embedded-status/index.tsx index 8187084e0..931b3fb66 100644 --- a/app/soapbox/features/embedded-status/index.tsx +++ b/app/soapbox/features/embedded-status/index.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { useHistory } from 'react-router-dom'; import { fetchStatus } from 'soapbox/actions/statuses'; @@ -16,12 +16,12 @@ interface IEmbeddedStatus { }, } -const getStatus = makeGetStatus(); - /** Status to be presented in an iframe for embeds on external websites. */ const EmbeddedStatus: React.FC = ({ params }) => { const dispatch = useAppDispatch(); const history = useHistory(); + const getStatus = useCallback(makeGetStatus(), []); + const status = useAppSelector(state => getStatus(state, { id: params.statusId })); const [loading, setLoading] = useState(true); diff --git a/app/soapbox/features/follow_requests/components/account_authorize.tsx b/app/soapbox/features/follow_requests/components/account_authorize.tsx index 6573bbd55..b820938d6 100644 --- a/app/soapbox/features/follow_requests/components/account_authorize.tsx +++ b/app/soapbox/features/follow_requests/components/account_authorize.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import { useDispatch } from 'react-redux'; @@ -16,8 +16,6 @@ const messages = defineMessages({ reject: { id: 'follow_request.reject', defaultMessage: 'Reject' }, }); -const getAccount = makeGetAccount(); - interface IAccountAuthorize { id: string, } @@ -25,6 +23,8 @@ interface IAccountAuthorize { const AccountAuthorize: React.FC = ({ id }) => { const intl = useIntl(); const dispatch = useDispatch(); + + const getAccount = useCallback(makeGetAccount(), []); const account = useAppSelector((state) => getAccount(state, id)); diff --git a/app/soapbox/features/list_adder/components/account.tsx b/app/soapbox/features/list_adder/components/account.tsx index 304b32207..e35a0ce96 100644 --- a/app/soapbox/features/list_adder/components/account.tsx +++ b/app/soapbox/features/list_adder/components/account.tsx @@ -1,17 +1,17 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import DisplayName from 'soapbox/components/display-name'; import { Avatar } from 'soapbox/components/ui'; import { useAppSelector } from 'soapbox/hooks'; import { makeGetAccount } from 'soapbox/selectors'; -const getAccount = makeGetAccount(); - interface IAccount { accountId: string, } const Account: React.FC = ({ accountId }) => { + const getAccount = useCallback(makeGetAccount(), []); + const account = useAppSelector((state) => getAccount(state, accountId)); if (!account) return null; diff --git a/app/soapbox/features/list_editor/components/account.tsx b/app/soapbox/features/list_editor/components/account.tsx index 79916ead2..ef1b2e246 100644 --- a/app/soapbox/features/list_editor/components/account.tsx +++ b/app/soapbox/features/list_editor/components/account.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import { removeFromListEditor, addToListEditor } from 'soapbox/actions/lists'; @@ -13,8 +13,6 @@ const messages = defineMessages({ add: { id: 'lists.account.add', defaultMessage: 'Add to list' }, }); -const getAccount = makeGetAccount(); - interface IAccount { accountId: string, } @@ -22,6 +20,7 @@ interface IAccount { const Account: React.FC = ({ accountId }) => { const intl = useIntl(); const dispatch = useAppDispatch(); + const getAccount = useCallback(makeGetAccount(), []); const account = useAppSelector((state) => getAccount(state, accountId)); const isAdded = useAppSelector((state) => state.listEditor.accounts.items.includes(accountId)); diff --git a/app/soapbox/features/notifications/components/notification.tsx b/app/soapbox/features/notifications/components/notification.tsx index b7e528ef5..e1fa87c5a 100644 --- a/app/soapbox/features/notifications/components/notification.tsx +++ b/app/soapbox/features/notifications/components/notification.tsx @@ -20,8 +20,6 @@ import { NotificationType, validType } from 'soapbox/utils/notification'; import type { ScrollPosition } from 'soapbox/components/status'; import type { Account, Status as StatusEntity, Notification as NotificationEntity } from 'soapbox/types/entities'; -const getNotification = makeGetNotification(); - const notificationForScreenReader = (intl: IntlShape, message: string, timestamp: Date) => { const output = [message]; @@ -153,6 +151,8 @@ const Notification: React.FC = (props) => { const dispatch = useAppDispatch(); + const getNotification = useCallback(makeGetNotification(), []); + const notification = useAppSelector((state) => getNotification(state, props.notification)); const history = useHistory(); diff --git a/app/soapbox/features/reply_mentions/account.tsx b/app/soapbox/features/reply_mentions/account.tsx index 5ede1d013..b4ad74bcc 100644 --- a/app/soapbox/features/reply_mentions/account.tsx +++ b/app/soapbox/features/reply_mentions/account.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { useCallback, useEffect } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import { fetchAccount } from 'soapbox/actions/accounts'; @@ -14,8 +14,6 @@ const messages = defineMessages({ add: { id: 'reply_mentions.account.add', defaultMessage: 'Add to mentions' }, }); -const getAccount = makeGetAccount(); - interface IAccount { accountId: string, author: boolean, @@ -24,6 +22,7 @@ interface IAccount { const Account: React.FC = ({ accountId, author }) => { const intl = useIntl(); const dispatch = useAppDispatch(); + const getAccount = useCallback(makeGetAccount(), []); const account = useAppSelector((state) => getAccount(state, accountId)); const added = useAppSelector((state) => !!account && state.compose.to?.includes(account.acct)); diff --git a/app/soapbox/features/status/containers/quoted_status_container.tsx b/app/soapbox/features/status/containers/quoted_status_container.tsx index f64ad2255..a870aa2c8 100644 --- a/app/soapbox/features/status/containers/quoted_status_container.tsx +++ b/app/soapbox/features/status/containers/quoted_status_container.tsx @@ -1,17 +1,17 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import QuotedStatus from 'soapbox/components/quoted-status'; import { useAppSelector } from 'soapbox/hooks'; import { makeGetStatus } from 'soapbox/selectors'; -const getStatus = makeGetStatus(); - interface IQuotedStatusContainer { /** Status ID to the quoted status. */ statusId: string, } const QuotedStatusContainer: React.FC = ({ statusId }) => { + const getStatus = useCallback(makeGetStatus(), []); + const status = useAppSelector(state => getStatus(state, { id: statusId })); if (!status) { diff --git a/app/soapbox/features/status/index.tsx b/app/soapbox/features/status/index.tsx index 189c74853..088d609cb 100644 --- a/app/soapbox/features/status/index.tsx +++ b/app/soapbox/features/status/index.tsx @@ -68,8 +68,6 @@ const messages = defineMessages({ blockAndReport: { id: 'confirmations.block.block_and_report', defaultMessage: 'Block & Report' }, }); -const getStatus = makeGetStatus(); - const getAncestorsIds = createSelector([ (_: RootState, statusId: string | undefined) => statusId, (state: RootState) => state.contexts.inReplyTos, @@ -131,6 +129,7 @@ const Thread: React.FC = (props) => { const dispatch = useAppDispatch(); const settings = useSettings(); + const getStatus = useCallback(makeGetStatus(), []); const me = useAppSelector(state => state.me); const status = useAppSelector(state => getStatus(state, { id: props.params.statusId })); diff --git a/app/soapbox/features/ui/components/mentions_modal.tsx b/app/soapbox/features/ui/components/mentions_modal.tsx index 445858843..0cd104cfb 100644 --- a/app/soapbox/features/ui/components/mentions_modal.tsx +++ b/app/soapbox/features/ui/components/mentions_modal.tsx @@ -1,5 +1,5 @@ import { OrderedSet as ImmutableOrderedSet } from 'immutable'; -import React, { useEffect } from 'react'; +import React, { useCallback, useEffect } from 'react'; import { FormattedMessage } from 'react-intl'; import { fetchStatusWithContext } from 'soapbox/actions/statuses'; @@ -9,8 +9,6 @@ import AccountContainer from 'soapbox/containers/account_container'; import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; import { makeGetStatus } from 'soapbox/selectors'; -const getStatus = makeGetStatus(); - interface IMentionsModal { onClose: (type: string) => void, statusId: string, @@ -18,6 +16,7 @@ interface IMentionsModal { const MentionsModal: React.FC = ({ onClose, statusId }) => { const dispatch = useAppDispatch(); + const getStatus = useCallback(makeGetStatus(), []); const status = useAppSelector((state) => getStatus(state, { id: statusId })); const accountIds = status ? ImmutableOrderedSet(status.mentions.map(m => m.get('id'))) : null; diff --git a/app/soapbox/features/ui/components/timeline.tsx b/app/soapbox/features/ui/components/timeline.tsx index 53d1ad7f8..38c5a4341 100644 --- a/app/soapbox/features/ui/components/timeline.tsx +++ b/app/soapbox/features/ui/components/timeline.tsx @@ -25,7 +25,7 @@ const Timeline: React.FC = ({ ...rest }) => { const dispatch = useAppDispatch(); - const getStatusIds = useCallback(makeGetStatusIds, [])(); + const getStatusIds = useCallback(makeGetStatusIds(), []); const lastStatusId = useAppSelector(state => (state.timelines.get(timelineId)?.items || ImmutableOrderedSet()).last() as string | undefined); const statusIds = useAppSelector(state => getStatusIds(state, { type: timelineId }));