diff --git a/app/soapbox/actions/aliases.ts b/app/soapbox/actions/aliases.ts index b7856cbe0..e485ea14e 100644 --- a/app/soapbox/actions/aliases.ts +++ b/app/soapbox/actions/aliases.ts @@ -10,8 +10,8 @@ import { importFetchedAccounts } from './importer'; import { patchMeSuccess } from './me'; import type { AxiosError } from 'axios'; +import type { Account } from 'soapbox/schemas'; import type { AppDispatch, RootState } from 'soapbox/store'; -import type { APIEntity, Account } from 'soapbox/types/entities'; const ALIASES_FETCH_REQUEST = 'ALIASES_FETCH_REQUEST'; const ALIASES_FETCH_SUCCESS = 'ALIASES_FETCH_SUCCESS'; @@ -56,7 +56,7 @@ const fetchAliasesRequest = () => ({ type: ALIASES_FETCH_REQUEST, }); -const fetchAliasesSuccess = (aliases: APIEntity[]) => ({ +const fetchAliasesSuccess = (aliases: unknown[]) => ({ type: ALIASES_FETCH_SUCCESS, value: aliases, }); @@ -82,7 +82,7 @@ const fetchAliasesSuggestions = (q: string) => }).catch(error => toast.showAlertForError(error)); }; -const fetchAliasesSuggestionsReady = (query: string, accounts: APIEntity[]) => ({ +const fetchAliasesSuggestionsReady = (query: string, accounts: unknown[]) => ({ type: ALIASES_SUGGESTIONS_READY, query, accounts, diff --git a/app/soapbox/containers/account-container.tsx b/app/soapbox/containers/account-container.tsx index 54c6db64e..6b41aef87 100644 --- a/app/soapbox/containers/account-container.tsx +++ b/app/soapbox/containers/account-container.tsx @@ -1,17 +1,14 @@ -import React, { useCallback } from 'react'; +import React from 'react'; -import { useAppSelector } from 'soapbox/hooks'; - -import Account, { IAccount } from '../components/account'; -import { makeGetAccount } from '../selectors'; +import { useAccount } from 'soapbox/api/hooks'; +import Account, { IAccount } from 'soapbox/components/account'; interface IAccountContainer extends Omit { id: string } const AccountContainer: React.FC = ({ id, ...props }) => { - const getAccount = useCallback(makeGetAccount(), []); - const account = useAppSelector(state => getAccount(state, id)); + const { account } = useAccount(id); return ( diff --git a/app/soapbox/features/admin/components/unapproved-account.tsx b/app/soapbox/features/admin/components/unapproved-account.tsx index 9aa1ba4fe..519d2cd6a 100644 --- a/app/soapbox/features/admin/components/unapproved-account.tsx +++ b/app/soapbox/features/admin/components/unapproved-account.tsx @@ -1,10 +1,10 @@ -import React, { useCallback } from 'react'; +import React from 'react'; import { approveUsers, deleteUsers } from 'soapbox/actions/admin'; +import { useAccount } from 'soapbox/api/hooks'; import { AuthorizeRejectButtons } from 'soapbox/components/authorize-reject-buttons'; import { Stack, HStack, Text } from 'soapbox/components/ui'; import { useAppSelector, useAppDispatch } from 'soapbox/hooks'; -import { makeGetAccount } from 'soapbox/selectors'; interface IUnapprovedAccount { accountId: string @@ -13,9 +13,8 @@ interface IUnapprovedAccount { /** Displays an unapproved account for moderation purposes. */ const UnapprovedAccount: React.FC = ({ accountId }) => { const dispatch = useAppDispatch(); - const getAccount = useCallback(makeGetAccount(), []); - const account = useAppSelector(state => getAccount(state, accountId)); + const { account } = useAccount(accountId); const adminAccount = useAppSelector(state => state.admin.users.get(accountId)); if (!account) return null; diff --git a/app/soapbox/features/aliases/components/account.tsx b/app/soapbox/features/aliases/components/account.tsx index f0aa77e8c..741b36add 100644 --- a/app/soapbox/features/aliases/components/account.tsx +++ b/app/soapbox/features/aliases/components/account.tsx @@ -1,12 +1,12 @@ -import React, { useCallback } from 'react'; +import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; import { addToAliases } from 'soapbox/actions/aliases'; +import { useAccount } from 'soapbox/api/hooks'; import AccountComponent from 'soapbox/components/account'; import IconButton from 'soapbox/components/icon-button'; import { HStack } from 'soapbox/components/ui'; import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks'; -import { makeGetAccount } from 'soapbox/selectors'; const messages = defineMessages({ add: { id: 'aliases.account.add', defaultMessage: 'Create alias' }, @@ -22,18 +22,12 @@ const Account: React.FC = ({ accountId, aliases }) => { const dispatch = useAppDispatch(); const features = useFeatures(); - const getAccount = useCallback(makeGetAccount(), []); - const account = useAppSelector((state) => getAccount(state, accountId)); const me = useAppSelector((state) => state.me); + const { account } = useAccount(accountId); - const added = useAppSelector((state) => { - const account = getAccount(state, accountId); - const apId = account?.pleroma?.ap_id; - const name = features.accountMoving ? account?.acct : apId; - if (!name) return false; - - return aliases.includes(name); - }); + const apId = account?.pleroma?.ap_id; + const name = features.accountMoving ? account?.acct : apId; + const added = name ? aliases.includes(name) : false; const handleOnAdd = () => dispatch(addToAliases(account!)); diff --git a/app/soapbox/features/birthdays/account.tsx b/app/soapbox/features/birthdays/account.tsx index 21aec8cd2..ba37ece9e 100644 --- a/app/soapbox/features/birthdays/account.tsx +++ b/app/soapbox/features/birthdays/account.tsx @@ -1,11 +1,10 @@ -import React, { useCallback } from 'react'; +import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; +import { useAccount } from 'soapbox/api/hooks'; import AccountComponent from 'soapbox/components/account'; import Icon from 'soapbox/components/icon'; import { HStack } from 'soapbox/components/ui'; -import { useAppSelector } from 'soapbox/hooks'; -import { makeGetAccount } from 'soapbox/selectors'; const messages = defineMessages({ birthday: { id: 'account.birthday', defaultMessage: 'Born {date}' }, @@ -17,9 +16,7 @@ interface IAccount { const Account: React.FC = ({ accountId }) => { const intl = useIntl(); - const getAccount = useCallback(makeGetAccount(), []); - - const account = useAppSelector((state) => getAccount(state, accountId)); + const { account } = useAccount(accountId); if (!account) return null; diff --git a/app/soapbox/features/compose/components/autosuggest-account.tsx b/app/soapbox/features/compose/components/autosuggest-account.tsx index 345459b71..6c87c6dc7 100644 --- a/app/soapbox/features/compose/components/autosuggest-account.tsx +++ b/app/soapbox/features/compose/components/autosuggest-account.tsx @@ -1,17 +1,14 @@ -import React, { useCallback } from 'react'; +import React from 'react'; +import { useAccount } from 'soapbox/api/hooks'; import Account from 'soapbox/components/account'; -import { useAppSelector } from 'soapbox/hooks'; -import { makeGetAccount } from 'soapbox/selectors'; interface IAutosuggestAccount { id: string } const AutosuggestAccount: React.FC = ({ id }) => { - const getAccount = useCallback(makeGetAccount(), []); - const account = useAppSelector((state) => getAccount(state, id)); - + const { account } = useAccount(id); if (!account) return null; return ; diff --git a/app/soapbox/features/directory/components/account-card.tsx b/app/soapbox/features/directory/components/account-card.tsx index 0a5707c4c..9e1ff92ae 100644 --- a/app/soapbox/features/directory/components/account-card.tsx +++ b/app/soapbox/features/directory/components/account-card.tsx @@ -3,24 +3,22 @@ import React from 'react'; import { FormattedMessage } from 'react-intl'; import { getSettings } from 'soapbox/actions/settings'; +import { useAccount } from 'soapbox/api/hooks'; import Account from 'soapbox/components/account'; import Badge from 'soapbox/components/badge'; import RelativeTimestamp from 'soapbox/components/relative-timestamp'; import { Stack, Text } from 'soapbox/components/ui'; import ActionButton from 'soapbox/features/ui/components/action-button'; import { useAppSelector } from 'soapbox/hooks'; -import { makeGetAccount } from 'soapbox/selectors'; import { shortNumberFormat } from 'soapbox/utils/numbers'; -const getAccount = makeGetAccount(); - interface IAccountCard { id: string } const AccountCard: React.FC = ({ id }) => { const me = useAppSelector((state) => state.me); - const account = useAppSelector((state) => getAccount(state, id)); + const { account } = useAccount(id); const autoPlayGif = useAppSelector((state) => getSettings(state).get('autoPlayGif')); if (!account) return null; diff --git a/app/soapbox/features/follow-requests/components/account-authorize.tsx b/app/soapbox/features/follow-requests/components/account-authorize.tsx index 9e1387ba2..3eead9df8 100644 --- a/app/soapbox/features/follow-requests/components/account-authorize.tsx +++ b/app/soapbox/features/follow-requests/components/account-authorize.tsx @@ -1,10 +1,10 @@ -import React, { useCallback } from 'react'; +import React from 'react'; import { authorizeFollowRequest, rejectFollowRequest } from 'soapbox/actions/accounts'; +import { useAccount } from 'soapbox/api/hooks'; import Account from 'soapbox/components/account'; import { AuthorizeRejectButtons } from 'soapbox/components/authorize-reject-buttons'; -import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; -import { makeGetAccount } from 'soapbox/selectors'; +import { useAppDispatch } from 'soapbox/hooks'; interface IAccountAuthorize { id: string @@ -12,9 +12,7 @@ interface IAccountAuthorize { const AccountAuthorize: React.FC = ({ id }) => { const dispatch = useAppDispatch(); - - const getAccount = useCallback(makeGetAccount(), []); - const account = useAppSelector((state) => getAccount(state, id)); + const { account } = useAccount(id); const onAuthorize = () => dispatch(authorizeFollowRequest(id)); const onReject = () => dispatch(rejectFollowRequest(id)); diff --git a/app/soapbox/features/group/group-blocked-members.tsx b/app/soapbox/features/group/group-blocked-members.tsx index f45e8259f..d6f994168 100644 --- a/app/soapbox/features/group/group-blocked-members.tsx +++ b/app/soapbox/features/group/group-blocked-members.tsx @@ -1,13 +1,12 @@ -import React, { useCallback, useEffect } from 'react'; +import React, { useEffect } from 'react'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; import { fetchGroupBlocks, groupUnblock } from 'soapbox/actions/groups'; -import { useGroup } from 'soapbox/api/hooks'; +import { useAccount, useGroup } from 'soapbox/api/hooks'; import Account from 'soapbox/components/account'; import ScrollableList from 'soapbox/components/scrollable-list'; import { Button, Column, HStack, Spinner } from 'soapbox/components/ui'; import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; -import { makeGetAccount } from 'soapbox/selectors'; import toast from 'soapbox/toast'; import ColumnForbidden from '../ui/components/column-forbidden'; @@ -28,10 +27,7 @@ interface IBlockedMember { const BlockedMember: React.FC = ({ accountId, groupId }) => { const intl = useIntl(); const dispatch = useAppDispatch(); - - const getAccount = useCallback(makeGetAccount(), []); - - const account = useAppSelector((state) => getAccount(state, accountId)); + const { account } = useAccount(accountId); if (!account) return null; diff --git a/app/soapbox/features/reply-mentions/account.tsx b/app/soapbox/features/reply-mentions/account.tsx index be6b61166..108d71b54 100644 --- a/app/soapbox/features/reply-mentions/account.tsx +++ b/app/soapbox/features/reply-mentions/account.tsx @@ -1,13 +1,13 @@ -import React, { useCallback, useEffect } from 'react'; +import React, { useEffect } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import { fetchAccount } from 'soapbox/actions/accounts'; import { addToMentions, removeFromMentions } from 'soapbox/actions/compose'; +import { useAccount } from 'soapbox/api/hooks'; import AccountComponent from 'soapbox/components/account'; import IconButton from 'soapbox/components/icon-button'; import { HStack } from 'soapbox/components/ui'; -import { useAppDispatch, useAppSelector, useCompose } from 'soapbox/hooks'; -import { makeGetAccount } from 'soapbox/selectors'; +import { useAppDispatch, useCompose } from 'soapbox/hooks'; const messages = defineMessages({ remove: { id: 'reply_mentions.account.remove', defaultMessage: 'Remove from mentions' }, @@ -23,11 +23,9 @@ interface IAccount { const Account: React.FC = ({ composeId, accountId, author }) => { const intl = useIntl(); const dispatch = useAppDispatch(); - const getAccount = useCallback(makeGetAccount(), []); const compose = useCompose(composeId); - - const account = useAppSelector((state) => getAccount(state, accountId)); + const { account } = useAccount(accountId); const added = !!account && compose.to?.includes(account.acct); const onRemove = () => dispatch(removeFromMentions(composeId, accountId)); diff --git a/app/soapbox/features/scheduled-statuses/builder.tsx b/app/soapbox/features/scheduled-statuses/builder.tsx index dbd40f101..95ab0d878 100644 --- a/app/soapbox/features/scheduled-statuses/builder.tsx +++ b/app/soapbox/features/scheduled-statuses/builder.tsx @@ -1,18 +1,15 @@ import { Map as ImmutableMap } from 'immutable'; +import { Entities } from 'soapbox/entity-store/entities'; import { normalizeStatus } from 'soapbox/normalizers/status'; import { calculateStatus } from 'soapbox/reducers/statuses'; -import { makeGetAccount } from 'soapbox/selectors'; import type { ScheduledStatus } from 'soapbox/reducers/scheduled-statuses'; import type { RootState } from 'soapbox/store'; export const buildStatus = (state: RootState, scheduledStatus: ScheduledStatus) => { - const getAccount = makeGetAccount(); - const me = state.me as string; - - const account = getAccount(state, me); + const account = state.entities[Entities.ACCOUNTS]?.store[me]; const status = ImmutableMap({ account, diff --git a/app/soapbox/features/ui/components/modals/account-moderation-modal/account-moderation-modal.tsx b/app/soapbox/features/ui/components/modals/account-moderation-modal/account-moderation-modal.tsx index 258653295..f7f1606aa 100644 --- a/app/soapbox/features/ui/components/modals/account-moderation-modal/account-moderation-modal.tsx +++ b/app/soapbox/features/ui/components/modals/account-moderation-modal/account-moderation-modal.tsx @@ -9,13 +9,13 @@ import { setBadges as saveBadges, } from 'soapbox/actions/admin'; import { deactivateUserModal, deleteUserModal } from 'soapbox/actions/moderation'; +import { useAccount } from 'soapbox/api/hooks'; import Account from 'soapbox/components/account'; import List, { ListItem } from 'soapbox/components/list'; import MissingIndicator from 'soapbox/components/missing-indicator'; import OutlineBox from 'soapbox/components/outline-box'; import { Button, Text, HStack, Modal, Stack, Toggle } from 'soapbox/components/ui'; -import { useAppDispatch, useAppSelector, useFeatures, useOwnAccount } from 'soapbox/hooks'; -import { makeGetAccount } from 'soapbox/selectors'; +import { useAppDispatch, useFeatures, useOwnAccount } from 'soapbox/hooks'; import toast from 'soapbox/toast'; import { isLocal } from 'soapbox/utils/accounts'; import { getBadges } from 'soapbox/utils/badges'; @@ -23,8 +23,6 @@ import { getBadges } from 'soapbox/utils/badges'; import BadgeInput from './badge-input'; import StaffRolePicker from './staff-role-picker'; -const getAccount = makeGetAccount(); - const messages = defineMessages({ userVerified: { id: 'admin.users.user_verified_message', defaultMessage: '@{acct} was verified' }, userUnverified: { id: 'admin.users.user_unverified_message', defaultMessage: '@{acct} was unverified' }, @@ -49,7 +47,7 @@ const AccountModerationModal: React.FC = ({ onClose, ac const ownAccount = useOwnAccount(); const features = useFeatures(); - const account = useAppSelector(state => getAccount(state, accountId)); + const { account } = useAccount(accountId); const accountBadges = account ? getBadges(account) : []; const [badges, setBadges] = useState(accountBadges); @@ -138,7 +136,7 @@ const AccountModerationModal: React.FC = ({ onClose, ac {features.suggestionsV2 && ( }> diff --git a/app/soapbox/features/ui/components/modals/account-moderation-modal/staff-role-picker.tsx b/app/soapbox/features/ui/components/modals/account-moderation-modal/staff-role-picker.tsx index 3d8ea9993..2aa972fe6 100644 --- a/app/soapbox/features/ui/components/modals/account-moderation-modal/staff-role-picker.tsx +++ b/app/soapbox/features/ui/components/modals/account-moderation-modal/staff-role-picker.tsx @@ -6,13 +6,13 @@ import { SelectDropdown } from 'soapbox/features/forms'; import { useAppDispatch } from 'soapbox/hooks'; import toast from 'soapbox/toast'; -import type { Account as AccountEntity } from 'soapbox/types/entities'; +import type { Account as AccountEntity } from 'soapbox/schemas'; /** Staff role. */ type AccountRole = 'user' | 'moderator' | 'admin'; /** Get the highest staff role associated with the account. */ -const getRole = (account: AccountEntity): AccountRole => { +const getRole = (account: Pick): AccountRole => { if (account.admin) { return 'admin'; } else if (account.moderator) { @@ -34,7 +34,7 @@ const messages = defineMessages({ interface IStaffRolePicker { /** Account whose role to change. */ - account: AccountEntity + account: Pick } /** Picker for setting the staff role of an account. */ diff --git a/app/soapbox/features/ui/components/modals/account-note-modal.tsx b/app/soapbox/features/ui/components/modals/account-note-modal.tsx index b34e5e132..95afc614e 100644 --- a/app/soapbox/features/ui/components/modals/account-note-modal.tsx +++ b/app/soapbox/features/ui/components/modals/account-note-modal.tsx @@ -3,23 +3,22 @@ import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { changeAccountNoteComment, submitAccountNote } from 'soapbox/actions/account-notes'; import { closeModal } from 'soapbox/actions/modals'; +import { useAccount } from 'soapbox/api/hooks'; import { Modal, Text } from 'soapbox/components/ui'; import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; -import { makeGetAccount } from 'soapbox/selectors'; const messages = defineMessages({ placeholder: { id: 'account_note.placeholder', defaultMessage: 'No comment provided' }, save: { id: 'account_note.save', defaultMessage: 'Save' }, }); -const getAccount = makeGetAccount(); - const AccountNoteModal = () => { const intl = useIntl(); const dispatch = useAppDispatch(); const isSubmitting = useAppSelector((state) => state.account_notes.edit.isSubmitting); - const account = useAppSelector((state) => getAccount(state, state.account_notes.edit.account!)); + const accountId = useAppSelector((state) => state.account_notes.edit.account); + const { account } = useAccount(accountId || undefined); const comment = useAppSelector((state) => state.account_notes.edit.comment); const onClose = () => { diff --git a/app/soapbox/features/ui/components/modals/mute-modal.tsx b/app/soapbox/features/ui/components/modals/mute-modal.tsx index 90f32ec35..aa2209fd0 100644 --- a/app/soapbox/features/ui/components/modals/mute-modal.tsx +++ b/app/soapbox/features/ui/components/modals/mute-modal.tsx @@ -4,17 +4,16 @@ import { FormattedMessage } from 'react-intl'; import { muteAccount } from 'soapbox/actions/accounts'; import { closeModal } from 'soapbox/actions/modals'; import { toggleHideNotifications, changeMuteDuration } from 'soapbox/actions/mutes'; +import { useAccount } from 'soapbox/api/hooks'; import { Modal, HStack, Stack, Text, Toggle } from 'soapbox/components/ui'; import DurationSelector from 'soapbox/features/compose/components/polls/duration-selector'; import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks'; -import { makeGetAccount } from 'soapbox/selectors'; - -const getAccount = makeGetAccount(); const MuteModal = () => { const dispatch = useAppDispatch(); - const account = useAppSelector((state) => getAccount(state, state.mutes.new.accountId!)); + const accountId = useAppSelector((state) => state.mutes.new.accountId); + const { account } = useAccount(accountId || undefined); const notifications = useAppSelector((state) => state.mutes.new.notifications); const duration = useAppSelector((state) => state.mutes.new.duration); const mutesDuration = useFeatures().mutesDuration; diff --git a/app/soapbox/features/ui/components/user-panel.tsx b/app/soapbox/features/ui/components/user-panel.tsx index 08642d801..bfbba1b3f 100644 --- a/app/soapbox/features/ui/components/user-panel.tsx +++ b/app/soapbox/features/ui/components/user-panel.tsx @@ -2,17 +2,15 @@ import React from 'react'; import { FormattedMessage, useIntl } from 'react-intl'; import { Link } from 'react-router-dom'; +import { useAccount } from 'soapbox/api/hooks'; import StillImage from 'soapbox/components/still-image'; import { Avatar, HStack, Stack, Text } from 'soapbox/components/ui'; import VerificationBadge from 'soapbox/components/verification-badge'; import { useAppSelector } from 'soapbox/hooks'; -import { makeGetAccount } from 'soapbox/selectors'; import { getAcct } from 'soapbox/utils/accounts'; import { shortNumberFormat } from 'soapbox/utils/numbers'; import { displayFqn } from 'soapbox/utils/state'; -const getAccount = makeGetAccount(); - interface IUserPanel { accountId: string action?: JSX.Element @@ -22,7 +20,7 @@ interface IUserPanel { const UserPanel: React.FC = ({ accountId, action, badges, domain }) => { const intl = useIntl(); - const account = useAppSelector((state) => getAccount(state, accountId)); + const { account } = useAccount(accountId); const fqn = useAppSelector((state) => displayFqn(state)); if (!account) return null; diff --git a/app/soapbox/reducers/mutes.ts b/app/soapbox/reducers/mutes.ts index 4c0b08c39..0034a6c56 100644 --- a/app/soapbox/reducers/mutes.ts +++ b/app/soapbox/reducers/mutes.ts @@ -10,7 +10,7 @@ import type { AnyAction } from 'redux'; const NewMuteRecord = ImmutableRecord({ isSubmitting: false, - accountId: null, + accountId: null as string | null, notifications: true, duration: 0, });