diff --git a/app/soapbox/components/birthday-input.tsx b/app/soapbox/components/birthday-input.tsx index 21a10a1e5..9da717e0d 100644 --- a/app/soapbox/components/birthday-input.tsx +++ b/app/soapbox/components/birthday-input.tsx @@ -4,7 +4,7 @@ import { defineMessages, useIntl } from 'react-intl'; import IconButton from 'soapbox/components/icon-button'; import BundleContainer from 'soapbox/features/ui/containers/bundle-container'; import { DatePicker } from 'soapbox/features/ui/util/async-components'; -import { useAppSelector, useFeatures } from 'soapbox/hooks'; +import { useInstance, useFeatures } from 'soapbox/hooks'; const messages = defineMessages({ birthdayPlaceholder: { id: 'edit_profile.fields.birthday_placeholder', defaultMessage: 'Your birthday' }, @@ -23,9 +23,10 @@ interface IBirthdayInput { const BirthdayInput: React.FC = ({ value, onChange, required }) => { const intl = useIntl(); const features = useFeatures(); + const instance = useInstance(); const supportsBirthdays = features.birthdays; - const minAge = useAppSelector((state) => state.instance.pleroma.getIn(['metadata', 'birthday_min_age'])) as number; + const minAge = instance.pleroma.getIn(['metadata', 'birthday_min_age']) as number; const maxDate = useMemo(() => { if (!supportsBirthdays) return null; diff --git a/app/soapbox/components/gdpr-banner.tsx b/app/soapbox/components/gdpr-banner.tsx index 94ea0cf59..0c9ea85c8 100644 --- a/app/soapbox/components/gdpr-banner.tsx +++ b/app/soapbox/components/gdpr-banner.tsx @@ -3,7 +3,7 @@ import React, { useState } from 'react'; import { FormattedMessage } from 'react-intl'; import { Banner, Button, HStack, Stack, Text } from 'soapbox/components/ui'; -import { useAppSelector, useSoapboxConfig } from 'soapbox/hooks'; +import { useAppSelector, useInstance, useSoapboxConfig } from 'soapbox/hooks'; const acceptedGdpr = !!localStorage.getItem('soapbox:gdpr'); @@ -13,9 +13,9 @@ const GdprBanner: React.FC = () => { const [shown, setShown] = useState(acceptedGdpr); const [slideout, setSlideout] = useState(false); + const instance = useInstance(); const soapbox = useSoapboxConfig(); const isLoggedIn = useAppSelector(state => !!state.me); - const siteTitle = useAppSelector(state => state.instance.title); const handleAccept = () => { localStorage.setItem('soapbox:gdpr', 'true'); @@ -34,14 +34,14 @@ const GdprBanner: React.FC = () => {
- + diff --git a/app/soapbox/components/helmet.tsx b/app/soapbox/components/helmet.tsx index d3a5bc021..23a73e744 100644 --- a/app/soapbox/components/helmet.tsx +++ b/app/soapbox/components/helmet.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { Helmet as ReactHelmet } from 'react-helmet'; -import { useAppSelector, useSettings } from 'soapbox/hooks'; +import { useAppSelector, useInstance, useSettings } from 'soapbox/hooks'; import { RootState } from 'soapbox/store'; import FaviconService from 'soapbox/utils/favicon-service'; @@ -16,7 +16,7 @@ const getNotifTotals = (state: RootState): number => { }; const Helmet: React.FC = ({ children }) => { - const title = useAppSelector((state) => state.instance.title); + const instance = useInstance(); const unreadCount = useAppSelector((state) => getNotifTotals(state)); const demetricator = useSettings().get('demetricator'); @@ -40,8 +40,8 @@ const Helmet: React.FC = ({ children }) => { return ( {children} diff --git a/app/soapbox/components/sidebar-navigation.tsx b/app/soapbox/components/sidebar-navigation.tsx index 28d64ed8f..4e8e73e5d 100644 --- a/app/soapbox/components/sidebar-navigation.tsx +++ b/app/soapbox/components/sidebar-navigation.tsx @@ -1,11 +1,9 @@ import React from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; -import { getSettings } from 'soapbox/actions/settings'; import DropdownMenu from 'soapbox/containers/dropdown-menu-container'; import ComposeButton from 'soapbox/features/ui/components/compose-button'; -import { useAppSelector, useOwnAccount } from 'soapbox/hooks'; -import { getFeatures } from 'soapbox/utils/features'; +import { useAppSelector, useFeatures, useOwnAccount, useSettings } from 'soapbox/hooks'; import SidebarNavigationLink from './sidebar-navigation-link'; @@ -22,16 +20,14 @@ const messages = defineMessages({ const SidebarNavigation = () => { const intl = useIntl(); - const instance = useAppSelector((state) => state.instance); - const settings = useAppSelector((state) => getSettings(state)); + const features = useFeatures(); + const settings = useSettings(); const account = useOwnAccount(); const notificationCount = useAppSelector((state) => state.notifications.unread); const chatsCount = useAppSelector((state) => state.chats.items.reduce((acc, curr) => acc + Math.min(curr.unread || 0, 1), 0)); const followRequestsCount = useAppSelector((state) => state.user_lists.follow_requests.items.count()); const dashboardCount = useAppSelector((state) => state.admin.openReports.count() + state.admin.awaitingApproval.count()); - const features = getFeatures(instance); - const makeMenu = (): Menu => { const menu: Menu = []; diff --git a/app/soapbox/components/thumb-navigation.tsx b/app/soapbox/components/thumb-navigation.tsx index 45a980f86..16007104a 100644 --- a/app/soapbox/components/thumb-navigation.tsx +++ b/app/soapbox/components/thumb-navigation.tsx @@ -2,15 +2,14 @@ import React from 'react'; import { FormattedMessage } from 'react-intl'; import ThumbNavigationLink from 'soapbox/components/thumb-navigation-link'; -import { useAppSelector, useOwnAccount } from 'soapbox/hooks'; -import { getFeatures } from 'soapbox/utils/features'; +import { useAppSelector, useFeatures, useOwnAccount } from 'soapbox/hooks'; const ThumbNavigation: React.FC = (): JSX.Element => { const account = useOwnAccount(); const notificationCount = useAppSelector((state) => state.notifications.unread); const chatsCount = useAppSelector((state) => state.chats.items.reduce((acc, curr) => acc + Math.min(curr.unread || 0, 1), 0)); const dashboardCount = useAppSelector((state) => state.admin.openReports.count() + state.admin.awaitingApproval.count()); - const features = getFeatures(useAppSelector((state) => state.instance)); + const features = useFeatures(); /** Conditionally render the supported messages link */ const renderMessagesLink = (): React.ReactNode => { diff --git a/app/soapbox/containers/soapbox.tsx b/app/soapbox/containers/soapbox.tsx index ce02d068d..74f53967d 100644 --- a/app/soapbox/containers/soapbox.tsx +++ b/app/soapbox/containers/soapbox.tsx @@ -37,6 +37,7 @@ import { useSettings, useTheme, useLocale, + useInstance, } from 'soapbox/hooks'; import MESSAGES from 'soapbox/locales/messages'; import { queryClient } from 'soapbox/queries/client'; @@ -85,7 +86,7 @@ const loadInitial = () => { const SoapboxMount = () => { useCachedLocationHandler(); const me = useAppSelector(state => state.me); - const instance = useAppSelector(state => state.instance); + const instance = useInstance(); const account = useOwnAccount(); const soapboxConfig = useSoapboxConfig(); const features = useFeatures(); diff --git a/app/soapbox/features/account-gallery/index.tsx b/app/soapbox/features/account-gallery/index.tsx index da6fb226a..9fda9751f 100644 --- a/app/soapbox/features/account-gallery/index.tsx +++ b/app/soapbox/features/account-gallery/index.tsx @@ -11,9 +11,8 @@ import { expandAccountMediaTimeline } from 'soapbox/actions/timelines'; import LoadMore from 'soapbox/components/load-more'; import MissingIndicator from 'soapbox/components/missing-indicator'; import { Column, Spinner } from 'soapbox/components/ui'; -import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks'; import { getAccountGallery, findAccountByUsername } from 'soapbox/selectors'; -import { getFeatures } from 'soapbox/utils/features'; import MediaItem from './components/media-item'; @@ -38,11 +37,11 @@ const LoadMoreMedia: React.FC = ({ maxId, onLoadMore }) => { const AccountGallery = () => { const dispatch = useAppDispatch(); const { username } = useParams<{ username: string }>(); + const features = useFeatures(); const { accountId, unavailable, accountUsername } = useAppSelector((state) => { const me = state.me; const accountFetchError = (state.accounts.get(-1)?.username || '').toLowerCase() === username.toLowerCase(); - const features = getFeatures(state.instance); let accountId: string | -1 | null = -1; let accountUsername = username; diff --git a/app/soapbox/features/admin/components/registration-mode-picker.tsx b/app/soapbox/features/admin/components/registration-mode-picker.tsx index 5808ace63..2c830153a 100644 --- a/app/soapbox/features/admin/components/registration-mode-picker.tsx +++ b/app/soapbox/features/admin/components/registration-mode-picker.tsx @@ -9,7 +9,7 @@ import { RadioGroup, RadioItem, } from 'soapbox/features/forms'; -import { useAppSelector, useAppDispatch } from 'soapbox/hooks'; +import { useAppDispatch, useInstance } from 'soapbox/hooks'; import type { Instance } from 'soapbox/types/entities'; @@ -42,8 +42,9 @@ const modeFromInstance = (instance: Instance): RegistrationMode => { const RegistrationModePicker: React.FC = () => { const intl = useIntl(); const dispatch = useAppDispatch(); + const instance = useInstance(); - const mode = useAppSelector(state => modeFromInstance(state.instance)); + const mode = modeFromInstance(instance); const onChange: React.ChangeEventHandler = e => { const config = generateConfig(e.target.value as RegistrationMode); diff --git a/app/soapbox/features/admin/tabs/dashboard.tsx b/app/soapbox/features/admin/tabs/dashboard.tsx index 495f0ff37..e773228bb 100644 --- a/app/soapbox/features/admin/tabs/dashboard.tsx +++ b/app/soapbox/features/admin/tabs/dashboard.tsx @@ -4,7 +4,7 @@ import { Link } from 'react-router-dom'; import { getSubscribersCsv, getUnsubscribersCsv, getCombinedCsv } from 'soapbox/actions/email-list'; import { Text } from 'soapbox/components/ui'; -import { useAppSelector, useAppDispatch, useOwnAccount, useFeatures } from 'soapbox/hooks'; +import { useAppDispatch, useOwnAccount, useFeatures, useInstance } from 'soapbox/hooks'; import sourceCode from 'soapbox/utils/code'; import { parseVersion } from 'soapbox/utils/features'; import { isNumber } from 'soapbox/utils/numbers'; @@ -27,7 +27,7 @@ const download = (response: AxiosResponse, filename: string) => { const Dashboard: React.FC = () => { const dispatch = useAppDispatch(); - const instance = useAppSelector(state => state.instance); + const instance = useInstance(); const features = useFeatures(); const account = useOwnAccount(); diff --git a/app/soapbox/features/ads/components/ad.tsx b/app/soapbox/features/ads/components/ad.tsx index 73446989d..0ffa28217 100644 --- a/app/soapbox/features/ads/components/ad.tsx +++ b/app/soapbox/features/ads/components/ad.tsx @@ -4,7 +4,7 @@ import { FormattedMessage } from 'react-intl'; import { Avatar, Card, HStack, Icon, IconButton, Stack, Text } from 'soapbox/components/ui'; import StatusCard from 'soapbox/features/status/components/card'; -import { useAppSelector } from 'soapbox/hooks'; +import { useInstance } from 'soapbox/hooks'; import type { Ad as AdEntity } from 'soapbox/types/soapbox'; @@ -15,7 +15,7 @@ interface IAd { /** Displays an ad in sponsored post format. */ const Ad: React.FC = ({ ad }) => { const queryClient = useQueryClient(); - const instance = useAppSelector(state => state.instance); + const instance = useInstance(); const timer = useRef(undefined); const infobox = useRef(null); diff --git a/app/soapbox/features/aliases/components/account.tsx b/app/soapbox/features/aliases/components/account.tsx index 1687b85e6..701931837 100644 --- a/app/soapbox/features/aliases/components/account.tsx +++ b/app/soapbox/features/aliases/components/account.tsx @@ -5,9 +5,8 @@ import { addToAliases } from 'soapbox/actions/aliases'; import AccountComponent from 'soapbox/components/account'; import IconButton from 'soapbox/components/icon-button'; import { HStack } from 'soapbox/components/ui'; -import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks'; import { makeGetAccount } from 'soapbox/selectors'; -import { getFeatures } from 'soapbox/utils/features'; import type { List as ImmutableList } from 'immutable'; @@ -23,21 +22,19 @@ interface IAccount { const Account: React.FC = ({ accountId, aliases }) => { const intl = useIntl(); const dispatch = useAppDispatch(); + const features = useFeatures(); const getAccount = useCallback(makeGetAccount(), []); - const account = useAppSelector((state) => getAccount(state, accountId)); - const added = useAppSelector((state) => { - const instance = state.instance; - const features = getFeatures(instance); + const me = useAppSelector((state) => state.me); + const added = useAppSelector((state) => { const account = getAccount(state, accountId); const apId = account?.pleroma.get('ap_id'); const name = features.accountMoving ? account?.acct : apId; return aliases.includes(name); }); - const me = useAppSelector((state) => state.me); const handleOnAdd = () => dispatch(addToAliases(account!)); diff --git a/app/soapbox/features/aliases/index.tsx b/app/soapbox/features/aliases/index.tsx index cf5ee3e16..54e0d254d 100644 --- a/app/soapbox/features/aliases/index.tsx +++ b/app/soapbox/features/aliases/index.tsx @@ -6,9 +6,7 @@ import { fetchAliases, removeFromAliases } from 'soapbox/actions/aliases'; import Icon from 'soapbox/components/icon'; import ScrollableList from 'soapbox/components/scrollable-list'; import { CardHeader, CardTitle, Column, HStack, Text } from 'soapbox/components/ui'; -import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; -import { makeGetAccount } from 'soapbox/selectors'; -import { getFeatures } from 'soapbox/utils/features'; +import { useAppDispatch, useAppSelector, useFeatures, useOwnAccount } from 'soapbox/hooks'; import Account from './components/account'; import Search from './components/search'; @@ -22,22 +20,21 @@ const messages = defineMessages({ delete: { id: 'column.aliases.delete', defaultMessage: 'Delete' }, }); -const getAccount = makeGetAccount(); const Aliases = () => { const intl = useIntl(); const dispatch = useAppDispatch(); + const features = useFeatures(); + const account = useOwnAccount(); const aliases = useAppSelector((state) => { - const me = state.me as string; - const account = getAccount(state, me); - - const instance = state.instance; - const features = getFeatures(instance); - - if (features.accountMoving) return state.aliases.aliases.items; - return account!.pleroma.get('also_known_as'); + if (features.accountMoving) { + return state.aliases.aliases.items; + } else { + return account!.pleroma.get('also_known_as'); + } }) as ImmutableList; + const searchAccountIds = useAppSelector((state) => state.aliases.suggestions.items); const loaded = useAppSelector((state) => state.aliases.suggestions.loaded); diff --git a/app/soapbox/features/auth-layout/index.tsx b/app/soapbox/features/auth-layout/index.tsx index b4f7ee12b..629bb3c9b 100644 --- a/app/soapbox/features/auth-layout/index.tsx +++ b/app/soapbox/features/auth-layout/index.tsx @@ -4,7 +4,7 @@ import { Link, Redirect, Route, Switch, useHistory, useLocation } from 'react-ro import LandingGradient from 'soapbox/components/landing-gradient'; import SiteLogo from 'soapbox/components/site-logo'; -import { useAppSelector, useFeatures, useSoapboxConfig, useOwnAccount } from 'soapbox/hooks'; +import { useAppSelector, useFeatures, useSoapboxConfig, useOwnAccount, useInstance } from 'soapbox/hooks'; import { Button, Card, CardBody } from '../../components/ui'; import LoginPage from '../auth-login/components/login-page'; @@ -27,12 +27,11 @@ const AuthLayout = () => { const { search } = useLocation(); const account = useOwnAccount(); - const siteTitle = useAppSelector(state => state.instance.title); - const soapboxConfig = useSoapboxConfig(); - const pepeEnabled = soapboxConfig.getIn(['extensions', 'pepe', 'enabled']) === true; - + const instance = useInstance(); const features = useFeatures(); - const instance = useAppSelector((state) => state.instance); + const soapboxConfig = useSoapboxConfig(); + + const pepeEnabled = soapboxConfig.getIn(['extensions', 'pepe', 'enabled']) === true; const isOpen = features.accountCreation && instance.registrations; const pepeOpen = useAppSelector(state => state.verification.instance.get('registrations') === true); const isLoginPage = history.location.pathname === '/login'; @@ -47,7 +46,7 @@ const AuthLayout = () => {
- +
diff --git a/app/soapbox/features/auth-login/components/consumers-list.tsx b/app/soapbox/features/auth-login/components/consumers-list.tsx index 7136d08e4..95f9e6b3e 100644 --- a/app/soapbox/features/auth-login/components/consumers-list.tsx +++ b/app/soapbox/features/auth-login/components/consumers-list.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { FormattedMessage } from 'react-intl'; import { Card, HStack, Text } from 'soapbox/components/ui'; -import { useAppSelector } from 'soapbox/hooks'; +import { useInstance } from 'soapbox/hooks'; import ConsumerButton from './consumer-button'; @@ -12,7 +12,8 @@ interface IConsumersList { /** Displays OAuth consumers to log in with. */ const ConsumersList: React.FC = () => { - const providers = useAppSelector(state => ImmutableList(state.instance.pleroma.get('oauth_consumer_strategies'))); + const instance = useInstance(); + const providers = ImmutableList(instance.pleroma.get('oauth_consumer_strategies')); if (providers.size > 0) { return ( diff --git a/app/soapbox/features/auth-login/components/registration-form.tsx b/app/soapbox/features/auth-login/components/registration-form.tsx index edd55deb3..0f825ea8c 100644 --- a/app/soapbox/features/auth-login/components/registration-form.tsx +++ b/app/soapbox/features/auth-login/components/registration-form.tsx @@ -12,7 +12,7 @@ import { openModal } from 'soapbox/actions/modals'; import BirthdayInput from 'soapbox/components/birthday-input'; import { Checkbox, Form, FormGroup, FormActions, Button, Input, Textarea } from 'soapbox/components/ui'; import CaptchaField from 'soapbox/features/auth-login/components/captcha'; -import { useAppSelector, useAppDispatch, useSettings, useFeatures } from 'soapbox/hooks'; +import { useAppDispatch, useSettings, useFeatures, useInstance } from 'soapbox/hooks'; const messages = defineMessages({ username: { id: 'registration.fields.username_placeholder', defaultMessage: 'Username' }, @@ -42,7 +42,7 @@ const RegistrationForm: React.FC = ({ inviteToken }) => { const settings = useSettings(); const features = useFeatures(); - const instance = useAppSelector(state => state.instance); + const instance = useInstance(); const locale = settings.get('locale'); const needsConfirmation = !!instance.pleroma.getIn(['metadata', 'account_activation_required']); diff --git a/app/soapbox/features/compose/components/compose-form.tsx b/app/soapbox/features/compose/components/compose-form.tsx index 1267c8753..01f16d96e 100644 --- a/app/soapbox/features/compose/components/compose-form.tsx +++ b/app/soapbox/features/compose/components/compose-form.tsx @@ -17,7 +17,7 @@ import AutosuggestInput, { AutoSuggestion } from 'soapbox/components/autosuggest import AutosuggestTextarea from 'soapbox/components/autosuggest-textarea'; import Icon from 'soapbox/components/icon'; import { Button, HStack, Stack } from 'soapbox/components/ui'; -import { useAppDispatch, useAppSelector, useCompose, useFeatures, usePrevious } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector, useCompose, useFeatures, useInstance, usePrevious } from 'soapbox/hooks'; import { isMobile } from 'soapbox/is-mobile'; import QuotedStatusContainer from '../containers/quoted-status-container'; @@ -67,11 +67,12 @@ const ComposeForm = ({ id, shouldCondense, autoFocus, clickab const history = useHistory(); const intl = useIntl(); const dispatch = useAppDispatch(); + const { configuration } = useInstance(); const compose = useCompose(id); const showSearch = useAppSelector((state) => state.search.submitted && !state.search.hidden); const isModalOpen = useAppSelector((state) => !!(state.modals.size && state.modals.last()!.modalType === 'COMPOSE')); - const maxTootChars = useAppSelector((state) => state.instance.getIn(['configuration', 'statuses', 'max_characters'])) as number; + const maxTootChars = configuration.getIn(['statuses', 'max_characters']) as number; const scheduledStatusCount = useAppSelector((state) => state.get('scheduled_statuses').size); const features = useFeatures(); diff --git a/app/soapbox/features/compose/components/polls/poll-form.tsx b/app/soapbox/features/compose/components/polls/poll-form.tsx index 77f47bdfc..5780cab1b 100644 --- a/app/soapbox/features/compose/components/polls/poll-form.tsx +++ b/app/soapbox/features/compose/components/polls/poll-form.tsx @@ -4,10 +4,11 @@ import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { addPollOption, changePollOption, changePollSettings, clearComposeSuggestions, fetchComposeSuggestions, removePoll, removePollOption, selectComposeSuggestion } from 'soapbox/actions/compose'; import AutosuggestInput from 'soapbox/components/autosuggest-input'; import { Button, Divider, HStack, Stack, Text, Toggle } from 'soapbox/components/ui'; -import { useAppDispatch, useAppSelector, useCompose } from 'soapbox/hooks'; +import { useAppDispatch, useCompose, useInstance } from 'soapbox/hooks'; import DurationSelector from './duration-selector'; +import type { Map as ImmutableMap } from 'immutable'; import type { AutoSuggestion } from 'soapbox/components/autosuggest-input'; const messages = defineMessages({ @@ -110,16 +111,17 @@ interface IPollForm { const PollForm: React.FC = ({ composeId }) => { const dispatch = useAppDispatch(); const intl = useIntl(); + const { configuration } = useInstance(); const compose = useCompose(composeId); - const pollLimits = useAppSelector((state) => state.instance.getIn(['configuration', 'polls']) as any); + const pollLimits = configuration.get('polls') as ImmutableMap; const options = compose.poll?.options; const expiresIn = compose.poll?.expires_in; const isMultiple = compose.poll?.multiple; - const maxOptions = pollLimits.get('max_options'); - const maxOptionChars = pollLimits.get('max_characters_per_option'); + const maxOptions = pollLimits.get('max_options') as number; + const maxOptionChars = pollLimits.get('max_characters_per_option') as number; const onRemoveOption = (index: number) => dispatch(removePollOption(composeId, index)); const onChangeOption = (index: number, title: string) => dispatch(changePollOption(composeId, index, title)); diff --git a/app/soapbox/features/compose/components/reply-mentions.tsx b/app/soapbox/features/compose/components/reply-mentions.tsx index 865f6539d..e9d5c8d57 100644 --- a/app/soapbox/features/compose/components/reply-mentions.tsx +++ b/app/soapbox/features/compose/components/reply-mentions.tsx @@ -3,10 +3,9 @@ import { FormattedList, FormattedMessage } from 'react-intl'; import { useDispatch } from 'react-redux'; import { openModal } from 'soapbox/actions/modals'; -import { useAppSelector, useCompose } from 'soapbox/hooks'; +import { useAppSelector, useCompose, useFeatures } from 'soapbox/hooks'; import { statusToMentionsAccountIdsArray } from 'soapbox/reducers/compose'; import { makeGetStatus } from 'soapbox/selectors'; -import { getFeatures } from 'soapbox/utils/features'; import type { Status as StatusEntity } from 'soapbox/types/entities'; @@ -16,18 +15,15 @@ interface IReplyMentions { const ReplyMentions: React.FC = ({ composeId }) => { const dispatch = useDispatch(); - const getStatus = useCallback(makeGetStatus(), []); - + const features = useFeatures(); const compose = useCompose(composeId); - const instance = useAppSelector((state) => state.instance); + const getStatus = useCallback(makeGetStatus(), []); const status = useAppSelector(state => getStatus(state, { id: compose.in_reply_to! })); const to = compose.to; const account = useAppSelector((state) => state.accounts.get(state.me)); - const { explicitAddressing } = getFeatures(instance); - - if (!explicitAddressing || !status || !to) { + if (!features.explicitAddressing || !status || !to) { return null; } diff --git a/app/soapbox/features/compose/components/upload-button.tsx b/app/soapbox/features/compose/components/upload-button.tsx index aa611bf75..8623ee2ae 100644 --- a/app/soapbox/features/compose/components/upload-button.tsx +++ b/app/soapbox/features/compose/components/upload-button.tsx @@ -2,7 +2,7 @@ import React, { useRef } from 'react'; import { defineMessages, IntlShape, useIntl } from 'react-intl'; import { IconButton } from 'soapbox/components/ui'; -import { useAppSelector } from 'soapbox/hooks'; +import { useInstance } from 'soapbox/hooks'; import type { List as ImmutableList } from 'immutable'; @@ -29,9 +29,10 @@ const UploadButton: React.FC = ({ resetFileKey, }) => { const intl = useIntl(); + const { configuration } = useInstance(); const fileElement = useRef(null); - const attachmentTypes = useAppSelector(state => state.instance.configuration.getIn(['media_attachments', 'supported_mime_types']) as ImmutableList); + const attachmentTypes = configuration.getIn(['media_attachments', 'supported_mime_types']) as ImmutableList; const handleChange: React.ChangeEventHandler = (e) => { if (e.target.files?.length) { diff --git a/app/soapbox/features/compose/components/upload.tsx b/app/soapbox/features/compose/components/upload.tsx index 769efcdd1..030e00fbe 100644 --- a/app/soapbox/features/compose/components/upload.tsx +++ b/app/soapbox/features/compose/components/upload.tsx @@ -10,7 +10,7 @@ import { openModal } from 'soapbox/actions/modals'; import Blurhash from 'soapbox/components/blurhash'; import Icon from 'soapbox/components/icon'; import IconButton from 'soapbox/components/icon-button'; -import { useAppDispatch, useAppSelector, useCompose } from 'soapbox/hooks'; +import { useAppDispatch, useCompose, useInstance } from 'soapbox/hooks'; import Motion from '../../ui/util/optional-motion'; @@ -70,9 +70,9 @@ const Upload: React.FC = ({ composeId, id }) => { const intl = useIntl(); const history = useHistory(); const dispatch = useAppDispatch(); + const { description_limit: descriptionLimit } = useInstance(); - const media = useCompose(composeId).media_attachments.find(item => item.get('id') === id)!; - const descriptionLimit = useAppSelector((state) => state.instance.get('description_limit')); + const media = useCompose(composeId).media_attachments.find(item => item.id === id)!; const [hovered, setHovered] = useState(false); const [focused, setFocused] = useState(false); diff --git a/app/soapbox/features/crypto-donate/components/crypto-donate-panel.tsx b/app/soapbox/features/crypto-donate/components/crypto-donate-panel.tsx index 3151738df..7105fdf28 100644 --- a/app/soapbox/features/crypto-donate/components/crypto-donate-panel.tsx +++ b/app/soapbox/features/crypto-donate/components/crypto-donate-panel.tsx @@ -3,7 +3,7 @@ import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; import { useHistory } from 'react-router-dom'; import { Text, Widget } from 'soapbox/components/ui'; -import { useAppSelector, useSoapboxConfig } from 'soapbox/hooks'; +import { useInstance, useSoapboxConfig } from 'soapbox/hooks'; import SiteWallet from './site-wallet'; @@ -18,9 +18,9 @@ interface ICryptoDonatePanel { const CryptoDonatePanel: React.FC = ({ limit = 3 }): JSX.Element | null => { const intl = useIntl(); const history = useHistory(); + const instance = useInstance(); const addresses = useSoapboxConfig().get('cryptoAddresses'); - const siteTitle = useAppSelector((state) => state.instance.title); if (limit === 0 || addresses.size === 0) { return null; @@ -40,7 +40,7 @@ const CryptoDonatePanel: React.FC = ({ limit = 3 }): JSX.Ele diff --git a/app/soapbox/features/crypto-donate/index.tsx b/app/soapbox/features/crypto-donate/index.tsx index 820124b9d..f2affe4bd 100644 --- a/app/soapbox/features/crypto-donate/index.tsx +++ b/app/soapbox/features/crypto-donate/index.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; import { Accordion, Column, Stack } from 'soapbox/components/ui'; -import { useAppSelector } from 'soapbox/hooks'; +import { useInstance } from 'soapbox/hooks'; import SiteWallet from './components/site-wallet'; @@ -11,9 +11,10 @@ const messages = defineMessages({ }); const CryptoDonate: React.FC = (): JSX.Element => { - const [explanationBoxExpanded, toggleExplanationBox] = useState(true); - const siteTitle = useAppSelector((state) => state.instance.title); const intl = useIntl(); + const instance = useInstance(); + + const [explanationBoxExpanded, toggleExplanationBox] = useState(true); return ( @@ -26,7 +27,7 @@ const CryptoDonate: React.FC = (): JSX.Element => { diff --git a/app/soapbox/features/directory/index.tsx b/app/soapbox/features/directory/index.tsx index 1266ed0ab..70d227868 100644 --- a/app/soapbox/features/directory/index.tsx +++ b/app/soapbox/features/directory/index.tsx @@ -7,8 +7,7 @@ import { useLocation } from 'react-router-dom'; import { fetchDirectory, expandDirectory } from 'soapbox/actions/directory'; import LoadMore from 'soapbox/components/load-more'; import { Column, RadioButton, Stack, Text } from 'soapbox/components/ui'; -import { useAppSelector } from 'soapbox/hooks'; -import { getFeatures } from 'soapbox/utils/features'; +import { useAppSelector, useFeatures, useInstance } from 'soapbox/hooks'; import AccountCard from './components/account-card'; @@ -25,11 +24,11 @@ const Directory = () => { const dispatch = useDispatch(); const { search } = useLocation(); const params = new URLSearchParams(search); + const instance = useInstance(); + const features = useFeatures(); const accountIds = useAppSelector((state) => state.user_lists.directory.items); const isLoading = useAppSelector((state) => state.user_lists.directory.isLoading); - const title = useAppSelector((state) => state.instance.get('title')); - const features = useAppSelector((state) => getFeatures(state.instance)); const [order, setOrder] = useState(params.get('order') || 'active'); const [local, setLocal] = useState(!!params.get('local')); @@ -71,7 +70,7 @@ const Directory = () => {
Fediverse filter
- +
diff --git a/app/soapbox/features/edit-profile/index.tsx b/app/soapbox/features/edit-profile/index.tsx index 2f77e1142..97fdd7e50 100644 --- a/app/soapbox/features/edit-profile/index.tsx +++ b/app/soapbox/features/edit-profile/index.tsx @@ -19,7 +19,7 @@ import { Textarea, Toggle, } from 'soapbox/components/ui'; -import { useAppSelector, useAppDispatch, useOwnAccount, useFeatures } from 'soapbox/hooks'; +import { useAppDispatch, useOwnAccount, useFeatures, useInstance } from 'soapbox/hooks'; import { normalizeAccount } from 'soapbox/normalizers'; import resizeImage from 'soapbox/utils/resize-image'; @@ -171,10 +171,11 @@ const ProfileField: StreamfieldComponent = ({ value, on const EditProfile: React.FC = () => { const intl = useIntl(); const dispatch = useAppDispatch(); + const instance = useInstance(); const account = useOwnAccount(); const features = useFeatures(); - const maxFields = useAppSelector(state => state.instance.pleroma.getIn(['metadata', 'fields_limits', 'max_fields'], 4) as number); + const maxFields = instance.pleroma.getIn(['metadata', 'fields_limits', 'max_fields'], 4) as number; const [isLoading, setLoading] = useState(false); const [data, setData] = useState({}); diff --git a/app/soapbox/features/federation-restrictions/components/instance-restrictions.tsx b/app/soapbox/features/federation-restrictions/components/instance-restrictions.tsx index a912f3f4d..b8c29b882 100644 --- a/app/soapbox/features/federation-restrictions/components/instance-restrictions.tsx +++ b/app/soapbox/features/federation-restrictions/components/instance-restrictions.tsx @@ -5,7 +5,7 @@ import { FormattedMessage } from 'react-intl'; import Icon from 'soapbox/components/icon'; import { HStack, Stack, Text } from 'soapbox/components/ui'; -import { useAppSelector } from 'soapbox/hooks'; +import { useInstance } from 'soapbox/hooks'; import type { Map as ImmutableMap } from 'immutable'; @@ -38,7 +38,7 @@ interface IInstanceRestrictions { } const InstanceRestrictions: React.FC = ({ remoteInstance }) => { - const instance = useAppSelector(state => state.instance); + const instance = useInstance(); const renderRestrictions = () => { const items = []; diff --git a/app/soapbox/features/federation-restrictions/index.tsx b/app/soapbox/features/federation-restrictions/index.tsx index fae7994cb..f0b1a3ae2 100644 --- a/app/soapbox/features/federation-restrictions/index.tsx +++ b/app/soapbox/features/federation-restrictions/index.tsx @@ -1,9 +1,9 @@ -import React, { useState } from 'react'; +import React, { useState, useCallback } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import ScrollableList from 'soapbox/components/scrollable-list'; import { Accordion } from 'soapbox/components/ui'; -import { useAppSelector } from 'soapbox/hooks'; +import { useAppSelector, useInstance } from 'soapbox/hooks'; import { makeGetHosts } from 'soapbox/selectors'; import { federationRestrictionsDisclosed } from 'soapbox/utils/state'; @@ -21,12 +21,12 @@ const messages = defineMessages({ notDisclosed: { id: 'federation_restrictions.not_disclosed_message', defaultMessage: '{siteTitle} does not disclose federation restrictions through the API.' }, }); -const getHosts = makeGetHosts(); - const FederationRestrictions = () => { const intl = useIntl(); + const instance = useInstance(); + + const getHosts = useCallback(makeGetHosts(), []); - const siteTitle = useAppSelector((state) => state.instance.get('title')); const hosts = useAppSelector((state) => getHosts(state)) as ImmutableOrderedSet; const disclosed = useAppSelector((state) => federationRestrictionsDisclosed(state)); @@ -45,11 +45,11 @@ const FederationRestrictions = () => { expanded={explanationBoxExpanded} onToggle={toggleExplanationBox} > - {intl.formatMessage(messages.boxMessage, { siteTitle })} + {intl.formatMessage(messages.boxMessage, { siteTitle: instance.title })}
- + {hosts.map((host) => )}
diff --git a/app/soapbox/features/home-timeline/index.tsx b/app/soapbox/features/home-timeline/index.tsx index 8c5522c66..83bdf6c9d 100644 --- a/app/soapbox/features/home-timeline/index.tsx +++ b/app/soapbox/features/home-timeline/index.tsx @@ -8,7 +8,7 @@ import { expandHomeTimeline } from 'soapbox/actions/timelines'; import PullToRefresh from 'soapbox/components/pull-to-refresh'; import { Column, Stack, Text } from 'soapbox/components/ui'; import Timeline from 'soapbox/features/ui/components/timeline'; -import { useAppSelector, useAppDispatch, useFeatures } from 'soapbox/hooks'; +import { useAppSelector, useAppDispatch, useFeatures, useInstance } from 'soapbox/hooks'; import { clearFeedAccountId } from '../../actions/timelines'; @@ -20,12 +20,12 @@ const HomeTimeline: React.FC = () => { const intl = useIntl(); const dispatch = useAppDispatch(); const features = useFeatures(); + const instance = useInstance(); const polling = useRef(null); const isPartial = useAppSelector(state => state.timelines.get('home')?.isPartial === true); const currentAccountId = useAppSelector(state => state.timelines.get('home')?.feedAccountId as string | undefined); - const siteTitle = useAppSelector(state => state.instance.title); const currentAccountRelationship = useAppSelector(state => currentAccountId ? state.relationships.get(currentAccountId) : null); const handleLoadMore = (maxId: string) => { @@ -104,7 +104,7 @@ const HomeTimeline: React.FC = () => { @@ -116,7 +116,7 @@ const HomeTimeline: React.FC = () => { values={{ public: ( - + ), }} diff --git a/app/soapbox/features/landing-page/index.tsx b/app/soapbox/features/landing-page/index.tsx index b508ff2d9..b7a0d418c 100644 --- a/app/soapbox/features/landing-page/index.tsx +++ b/app/soapbox/features/landing-page/index.tsx @@ -6,7 +6,7 @@ import Markup from 'soapbox/components/markup'; import { Button, Card, CardBody, Stack, Text } from 'soapbox/components/ui'; import VerificationBadge from 'soapbox/components/verification-badge'; import RegistrationForm from 'soapbox/features/auth-login/components/registration-form'; -import { useAppDispatch, useAppSelector, useFeatures, useSoapboxConfig } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector, useFeatures, useInstance, useSoapboxConfig } from 'soapbox/hooks'; import { capitalize } from 'soapbox/utils/strings'; const LandingPage = () => { @@ -15,7 +15,7 @@ const LandingPage = () => { const soapboxConfig = useSoapboxConfig(); const pepeEnabled = soapboxConfig.getIn(['extensions', 'pepe', 'enabled']) === true; - const instance = useAppSelector((state) => state.instance); + const instance = useInstance(); const pepeOpen = useAppSelector(state => state.verification.instance.get('registrations') === true); /** Registrations are closed */ diff --git a/app/soapbox/features/migration/index.tsx b/app/soapbox/features/migration/index.tsx index d84e05576..edf8ea6f7 100644 --- a/app/soapbox/features/migration/index.tsx +++ b/app/soapbox/features/migration/index.tsx @@ -5,7 +5,7 @@ import { Link } from 'react-router-dom'; import { moveAccount } from 'soapbox/actions/security'; import snackbar from 'soapbox/actions/snackbar'; import { Button, Column, Form, FormActions, FormGroup, Input, Text } from 'soapbox/components/ui'; -import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; +import { useAppDispatch, useInstance } from 'soapbox/hooks'; const messages = defineMessages({ heading: { id: 'column.migration', defaultMessage: 'Account migration' }, @@ -21,8 +21,9 @@ const messages = defineMessages({ const Migration = () => { const intl = useIntl(); const dispatch = useAppDispatch(); + const instance = useInstance(); - const cooldownPeriod = useAppSelector((state) => state.instance.pleroma.getIn(['metadata', 'migration_cooldown_period'])) as number | undefined; + const cooldownPeriod = instance.pleroma.getIn(['metadata', 'migration_cooldown_period']) as number | undefined; const [targetAccount, setTargetAccount] = useState(''); const [password, setPassword] = useState(''); diff --git a/app/soapbox/features/notifications/components/notification.tsx b/app/soapbox/features/notifications/components/notification.tsx index b50d953c8..97f38a7bf 100644 --- a/app/soapbox/features/notifications/components/notification.tsx +++ b/app/soapbox/features/notifications/components/notification.tsx @@ -12,7 +12,7 @@ import Icon from 'soapbox/components/icon'; import { HStack, Text, Emoji } from 'soapbox/components/ui'; import AccountContainer from 'soapbox/containers/account-container'; import StatusContainer from 'soapbox/containers/status-container'; -import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector, useInstance } from 'soapbox/hooks'; import { makeGetNotification } from 'soapbox/selectors'; import { NotificationType, validType } from 'soapbox/utils/notification'; @@ -157,7 +157,7 @@ const Notification: React.FC = (props) => { const history = useHistory(); const intl = useIntl(); - const instance = useAppSelector((state) => state.instance); + const instance = useInstance(); const type = notification.type; const { account, status } = notification; diff --git a/app/soapbox/features/onboarding/steps/fediverse-step.tsx b/app/soapbox/features/onboarding/steps/fediverse-step.tsx index 4d83c5b98..83a5795eb 100644 --- a/app/soapbox/features/onboarding/steps/fediverse-step.tsx +++ b/app/soapbox/features/onboarding/steps/fediverse-step.tsx @@ -3,13 +3,13 @@ import { FormattedMessage } from 'react-intl'; import Account from 'soapbox/components/account'; import { Button, Card, CardBody, Icon, Stack, Text } from 'soapbox/components/ui'; -import { useAppSelector, useOwnAccount } from 'soapbox/hooks'; +import { useInstance, useOwnAccount } from 'soapbox/hooks'; import type { Account as AccountEntity } from 'soapbox/types/entities'; const FediverseStep = ({ onNext }: { onNext: () => void }) => { - const siteTitle = useAppSelector((state) => state.instance.title); const account = useOwnAccount() as AccountEntity; + const instance = useInstance(); return ( @@ -22,7 +22,7 @@ const FediverseStep = ({ onNext }: { onNext: () => void }) => { id='onboarding.fediverse.title' defaultMessage='{siteTitle} is just one part of the Fediverse' values={{ - siteTitle, + siteTitle: instance.title, }} /> @@ -35,7 +35,7 @@ const FediverseStep = ({ onNext }: { onNext: () => void }) => { id='onboarding.fediverse.message' defaultMessage='The Fediverse is a social network made up of thousands of diverse and independently-run social media sites (aka "servers"). You can follow users — and like, repost, and reply to posts — from most other Fediverse servers, because they can communicate with {siteTitle}.' values={{ - siteTitle, + siteTitle: instance.title, }} /> diff --git a/app/soapbox/features/public-layout/components/header.tsx b/app/soapbox/features/public-layout/components/header.tsx index ea276797e..4fef3df36 100644 --- a/app/soapbox/features/public-layout/components/header.tsx +++ b/app/soapbox/features/public-layout/components/header.tsx @@ -8,7 +8,7 @@ import { fetchInstance } from 'soapbox/actions/instance'; import { openModal } from 'soapbox/actions/modals'; import SiteLogo from 'soapbox/components/site-logo'; import { Button, Form, HStack, IconButton, Input, Tooltip } from 'soapbox/components/ui'; -import { useAppSelector, useFeatures, useSoapboxConfig, useOwnAccount } from 'soapbox/hooks'; +import { useAppSelector, useFeatures, useSoapboxConfig, useOwnAccount, useInstance } from 'soapbox/hooks'; import Sonar from './sonar'; @@ -34,7 +34,7 @@ const Header = () => { const { links } = soapboxConfig; const features = useFeatures(); - const instance = useAppSelector((state) => state.instance); + const instance = useInstance(); const isOpen = features.accountCreation && instance.registrations; const pepeOpen = useAppSelector(state => state.verification.instance.get('registrations') === true); diff --git a/app/soapbox/features/public-timeline/index.tsx b/app/soapbox/features/public-timeline/index.tsx index 6fec372ce..2515877f2 100644 --- a/app/soapbox/features/public-timeline/index.tsx +++ b/app/soapbox/features/public-timeline/index.tsx @@ -8,7 +8,7 @@ import { expandPublicTimeline } from 'soapbox/actions/timelines'; import PullToRefresh from 'soapbox/components/pull-to-refresh'; import SubNavigation from 'soapbox/components/sub-navigation'; import { Accordion, Column } from 'soapbox/components/ui'; -import { useAppDispatch, useAppSelector, useSettings } from 'soapbox/hooks'; +import { useAppDispatch, useInstance, useSettings } from 'soapbox/hooks'; import PinnedHostsPicker from '../remote-timeline/components/pinned-hosts-picker'; import Timeline from '../ui/components/timeline'; @@ -22,12 +22,12 @@ const CommunityTimeline = () => { const intl = useIntl(); const dispatch = useAppDispatch(); + const instance = useInstance(); const settings = useSettings(); const onlyMedia = settings.getIn(['public', 'other', 'onlyMedia']); const timelineId = 'public'; - const siteTitle = useAppSelector((state) => state.instance.title); const explanationBoxExpanded = settings.get('explanationBox'); const showExplanationBox = settings.get('showExplanationBox'); @@ -79,13 +79,13 @@ const CommunityTimeline = () => { id='fediverse_tab.explanation_box.explanation' defaultMessage='{site_title} is part of the Fediverse, a social network made up of thousands of independent social media sites (aka "servers"). The posts you see here are from 3rd-party servers. You have the freedom to engage with them, or to block any server you don't like. Pay attention to the full username after the second @ symbol to know which server a post is from. To see only {site_title} posts, visit {local}.' values={{ - site_title: siteTitle, + site_title: instance.title, local: ( ), diff --git a/app/soapbox/features/register-invite/index.tsx b/app/soapbox/features/register-invite/index.tsx index c97469011..c1f21f0ea 100644 --- a/app/soapbox/features/register-invite/index.tsx +++ b/app/soapbox/features/register-invite/index.tsx @@ -4,7 +4,7 @@ import { useParams } from 'react-router-dom'; import { Stack, CardTitle, Text } from 'soapbox/components/ui'; import RegistrationForm from 'soapbox/features/auth-login/components/registration-form'; -import { useAppSelector } from 'soapbox/hooks'; +import { useInstance } from 'soapbox/hooks'; interface RegisterInviteParams { token: string, @@ -12,14 +12,14 @@ interface RegisterInviteParams { /** Page to register with an invitation. */ const RegisterInvite: React.FC = () => { + const instance = useInstance(); const { token } = useParams(); - const siteTitle = useAppSelector(state => state.instance.title); const title = ( ); diff --git a/app/soapbox/features/server-info/index.tsx b/app/soapbox/features/server-info/index.tsx index 94619f72f..bc0b03e9b 100644 --- a/app/soapbox/features/server-info/index.tsx +++ b/app/soapbox/features/server-info/index.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; import { Column, Divider, Stack, Text } from 'soapbox/components/ui'; -import { useAppSelector } from 'soapbox/hooks'; +import { useInstance } from 'soapbox/hooks'; import LinkFooter from '../ui/components/link-footer'; import PromoPanel from '../ui/components/promo-panel'; @@ -13,7 +13,7 @@ const messages = defineMessages({ const ServerInfo = () => { const intl = useIntl(); - const instance = useAppSelector((state) => state.instance); + const instance = useInstance(); return ( diff --git a/app/soapbox/features/settings/index.tsx b/app/soapbox/features/settings/index.tsx index 88ec78def..f6bc4c37e 100644 --- a/app/soapbox/features/settings/index.tsx +++ b/app/soapbox/features/settings/index.tsx @@ -6,8 +6,7 @@ import { useHistory } from 'react-router-dom'; import { fetchMfa } from 'soapbox/actions/mfa'; import List, { ListItem } from 'soapbox/components/list'; import { Card, CardBody, CardHeader, CardTitle, Column } from 'soapbox/components/ui'; -import { useAppSelector, useOwnAccount } from 'soapbox/hooks'; -import { getFeatures } from 'soapbox/utils/features'; +import { useAppSelector, useFeatures, useOwnAccount } from 'soapbox/hooks'; import Preferences from '../preferences'; @@ -36,7 +35,7 @@ const Settings = () => { const intl = useIntl(); const mfa = useAppSelector((state) => state.security.get('mfa')); - const features = useAppSelector((state) => getFeatures(state.instance)); + const features = useFeatures(); const account = useOwnAccount(); const navigateToChangeEmail = () => history.push('/settings/email'); diff --git a/app/soapbox/features/status/components/thread-login-cta.tsx b/app/soapbox/features/status/components/thread-login-cta.tsx index e73a8e18b..26462cd20 100644 --- a/app/soapbox/features/status/components/thread-login-cta.tsx +++ b/app/soapbox/features/status/components/thread-login-cta.tsx @@ -2,12 +2,12 @@ import React from 'react'; import { FormattedMessage } from 'react-intl'; import { Card, CardTitle, Text, Stack, Button } from 'soapbox/components/ui'; -import { useAppSelector, useSoapboxConfig } from 'soapbox/hooks'; +import { useInstance, useSoapboxConfig } from 'soapbox/hooks'; /** Prompts logged-out users to log in when viewing a thread. */ const ThreadLoginCta: React.FC = () => { + const instance = useInstance(); const { displayCta } = useSoapboxConfig(); - const siteTitle = useAppSelector(state => state.instance.title); if (!displayCta) return null; @@ -19,7 +19,7 @@ const ThreadLoginCta: React.FC = () => { diff --git a/app/soapbox/features/ui/components/cta-banner.tsx b/app/soapbox/features/ui/components/cta-banner.tsx index fa4ceba90..87860b318 100644 --- a/app/soapbox/features/ui/components/cta-banner.tsx +++ b/app/soapbox/features/ui/components/cta-banner.tsx @@ -2,11 +2,11 @@ import React from 'react'; import { FormattedMessage } from 'react-intl'; import { Banner, Button, HStack, Stack, Text } from 'soapbox/components/ui'; -import { useAppSelector, useSoapboxConfig } from 'soapbox/hooks'; +import { useAppSelector, useInstance, useSoapboxConfig } from 'soapbox/hooks'; const CtaBanner = () => { + const instance = useInstance(); const { displayCta, singleUserMode } = useSoapboxConfig(); - const siteTitle = useAppSelector((state) => state.instance.title); const me = useAppSelector((state) => state.me); if (me || !displayCta || singleUserMode) return null; @@ -17,7 +17,7 @@ const CtaBanner = () => { - + diff --git a/app/soapbox/features/ui/components/modals/hotkeys-modal.tsx b/app/soapbox/features/ui/components/modals/hotkeys-modal.tsx index 1f44c9f9a..870ed8736 100644 --- a/app/soapbox/features/ui/components/modals/hotkeys-modal.tsx +++ b/app/soapbox/features/ui/components/modals/hotkeys-modal.tsx @@ -2,8 +2,7 @@ import React from 'react'; import { FormattedMessage } from 'react-intl'; import { Modal } from 'soapbox/components/ui'; -import { useAppSelector } from 'soapbox/hooks'; -import { getFeatures } from 'soapbox/utils/features'; +import { useFeatures } from 'soapbox/hooks'; interface IHotkeysModal { onClose: () => void, @@ -22,7 +21,7 @@ const TableCell: React.FC<{ children: React.ReactNode }> = ({ children }) => ( ); const HotkeysModal: React.FC = ({ onClose }) => { - const features = useAppSelector((state) => getFeatures(state.instance)); + const features = useFeatures(); return ( = ({ onClose }) => { const pepeEnabled = soapboxConfig.getIn(['extensions', 'pepe', 'enabled']) === true; const { links } = soapboxConfig; - const instance = useAppSelector((state) => state.instance); + const instance = useInstance(); const features = useFeatures(); const isOpen = features.accountCreation && instance.registrations; diff --git a/app/soapbox/features/ui/components/modals/unauthorized-modal.tsx b/app/soapbox/features/ui/components/modals/unauthorized-modal.tsx index ec6865b23..b36547eae 100644 --- a/app/soapbox/features/ui/components/modals/unauthorized-modal.tsx +++ b/app/soapbox/features/ui/components/modals/unauthorized-modal.tsx @@ -5,7 +5,7 @@ import { useHistory } from 'react-router-dom'; import { remoteInteraction } from 'soapbox/actions/interactions'; import snackbar from 'soapbox/actions/snackbar'; import { Button, Modal, Stack, Text } from 'soapbox/components/ui'; -import { useAppSelector, useAppDispatch, useFeatures, useSoapboxConfig } from 'soapbox/hooks'; +import { useAppSelector, useAppDispatch, useFeatures, useSoapboxConfig, useInstance } from 'soapbox/hooks'; const messages = defineMessages({ close: { id: 'lightbox.close', defaultMessage: 'Close' }, @@ -29,9 +29,9 @@ const UnauthorizedModal: React.FC = ({ action, onClose, acco const intl = useIntl(); const history = useHistory(); const dispatch = useAppDispatch(); + const instance = useInstance(); const { singleUserMode } = useSoapboxConfig(); - const siteTitle = useAppSelector(state => state.instance.title); const username = useAppSelector(state => state.accounts.get(accountId)?.display_name); const features = useFeatures(); @@ -121,7 +121,7 @@ const UnauthorizedModal: React.FC = ({ action, onClose, acco
{!singleUserMode && ( - + )} @@ -135,7 +135,7 @@ const UnauthorizedModal: React.FC = ({ action, onClose, acco return ( } + title={} onClose={onClickClose} confirmationAction={onLogin} confirmationText={} diff --git a/app/soapbox/features/ui/components/modals/verify-sms-modal.tsx b/app/soapbox/features/ui/components/modals/verify-sms-modal.tsx index ee45e4947..a6d327272 100644 --- a/app/soapbox/features/ui/components/modals/verify-sms-modal.tsx +++ b/app/soapbox/features/ui/components/modals/verify-sms-modal.tsx @@ -7,7 +7,7 @@ import { closeModal } from 'soapbox/actions/modals'; import snackbar from 'soapbox/actions/snackbar'; import { reConfirmPhoneVerification, reRequestPhoneVerification } from 'soapbox/actions/verification'; import { FormGroup, PhoneInput, Modal, Stack, Text } from 'soapbox/components/ui'; -import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector, useInstance } from 'soapbox/hooks'; import { getAccessToken } from 'soapbox/utils/auth'; const messages = defineMessages({ @@ -56,8 +56,8 @@ enum Statuses { const VerifySmsModal: React.FC = ({ onClose }) => { const dispatch = useAppDispatch(); const intl = useIntl(); + const instance = useInstance(); const accessToken = useAppSelector((state) => getAccessToken(state)); - const title = useAppSelector((state) => state.instance.title); const isLoading = useAppSelector((state) => state.verification.isLoading); const [status, setStatus] = useState(Statuses.IDLE); @@ -143,7 +143,7 @@ const VerifySmsModal: React.FC = ({ onClose }) => { id='sms_verification.modal.verify_help_text' defaultMessage='Verify your phone number to start using {instance}.' values={{ - instance: title, + instance: instance.title, }} /> diff --git a/app/soapbox/features/ui/components/panels/sign-up-panel.tsx b/app/soapbox/features/ui/components/panels/sign-up-panel.tsx index cafcdc5c4..318080770 100644 --- a/app/soapbox/features/ui/components/panels/sign-up-panel.tsx +++ b/app/soapbox/features/ui/components/panels/sign-up-panel.tsx @@ -2,11 +2,11 @@ import React from 'react'; import { FormattedMessage } from 'react-intl'; import { Button, Stack, Text } from 'soapbox/components/ui'; -import { useAppSelector, useSoapboxConfig } from 'soapbox/hooks'; +import { useAppSelector, useInstance, useSoapboxConfig } from 'soapbox/hooks'; const SignUpPanel = () => { + const instance = useInstance(); const { singleUserMode } = useSoapboxConfig(); - const siteTitle = useAppSelector((state) => state.instance.title); const me = useAppSelector((state) => state.me); if (me || singleUserMode) return null; @@ -15,7 +15,7 @@ const SignUpPanel = () => { - + diff --git a/app/soapbox/features/ui/components/promo-panel.tsx b/app/soapbox/features/ui/components/promo-panel.tsx index 9add33b27..12798dc06 100644 --- a/app/soapbox/features/ui/components/promo-panel.tsx +++ b/app/soapbox/features/ui/components/promo-panel.tsx @@ -2,20 +2,20 @@ import React from 'react'; import Icon from 'soapbox/components/icon'; import { Widget, Stack, Text } from 'soapbox/components/ui'; -import { useAppSelector, useSettings, useSoapboxConfig } from 'soapbox/hooks'; +import { useInstance, useSettings, useSoapboxConfig } from 'soapbox/hooks'; const PromoPanel: React.FC = () => { + const instance = useInstance(); const { promoPanel } = useSoapboxConfig(); const settings = useSettings(); - const siteTitle = useAppSelector(state => state.instance.title); const promoItems = promoPanel.get('items'); const locale = settings.get('locale'); if (!promoItems || promoItems.isEmpty()) return null; return ( - + {promoItems.map((item, i) => ( diff --git a/app/soapbox/features/ui/index.tsx b/app/soapbox/features/ui/index.tsx index 019c14997..c280aafb1 100644 --- a/app/soapbox/features/ui/index.tsx +++ b/app/soapbox/features/ui/index.tsx @@ -25,18 +25,15 @@ import Icon from 'soapbox/components/icon'; import SidebarNavigation from 'soapbox/components/sidebar-navigation'; import ThumbNavigation from 'soapbox/components/thumb-navigation'; import { Layout } from 'soapbox/components/ui'; -import { useAppDispatch, useAppSelector, useOwnAccount, useSoapboxConfig, useFeatures } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector, useOwnAccount, useSoapboxConfig, useFeatures, useInstance } from 'soapbox/hooks'; import AdminPage from 'soapbox/pages/admin-page'; import DefaultPage from 'soapbox/pages/default-page'; -// import GroupsPage from 'soapbox/pages/groups_page'; -// import GroupPage from 'soapbox/pages/group_page'; import HomePage from 'soapbox/pages/home-page'; import ProfilePage from 'soapbox/pages/profile-page'; import RemoteInstancePage from 'soapbox/pages/remote-instance-page'; import StatusPage from 'soapbox/pages/status-page'; import { getAccessToken, getVapidKey } from 'soapbox/utils/auth'; import { isStandalone } from 'soapbox/utils/state'; -// import GroupSidebarPanel from '../groups/sidebar_panel'; import BackgroundShapes from './components/background-shapes'; import Navbar from './components/navbar'; @@ -190,18 +187,6 @@ const SwitchingColumnsArea: React.FC = ({ children }) => { )} - {/* Gab groups */} - {/* - - - - - - - - - */} - {/* Mastodon web routes */} @@ -331,6 +316,7 @@ const UI: React.FC = ({ children }) => { const intl = useIntl(); const history = useHistory(); const dispatch = useAppDispatch(); + const instance = useInstance(); const [draggingOver, setDraggingOver] = useState(false); const [mobile, setMobile] = useState(isMobile(window.innerWidth)); @@ -347,7 +333,7 @@ const UI: React.FC = ({ children }) => { const dropdownMenuIsOpen = useAppSelector(state => state.dropdown_menu.openId !== null); const accessToken = useAppSelector(state => getAccessToken(state)); - const streamingUrl = useAppSelector(state => state.instance.urls.get('streaming_api')); + const streamingUrl = instance.urls.get('streaming_api'); const standalone = useAppSelector(isStandalone); const handleDragEnter = (e: DragEvent) => { diff --git a/app/soapbox/features/verification/registration.tsx b/app/soapbox/features/verification/registration.tsx index 6539ca8c8..d19eb03d9 100644 --- a/app/soapbox/features/verification/registration.tsx +++ b/app/soapbox/features/verification/registration.tsx @@ -8,7 +8,7 @@ import { startOnboarding } from 'soapbox/actions/onboarding'; import snackbar from 'soapbox/actions/snackbar'; import { createAccount, removeStoredVerification } from 'soapbox/actions/verification'; import { Button, Form, FormGroup, Input, Text } from 'soapbox/components/ui'; -import { useAppDispatch, useAppSelector, useSoapboxConfig } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector, useInstance, useSoapboxConfig } from 'soapbox/hooks'; import { getRedirectUrl } from 'soapbox/utils/redirect'; import PasswordIndicator from './components/password-indicator'; @@ -32,11 +32,11 @@ const initialState = { const Registration = () => { const dispatch = useAppDispatch(); const intl = useIntl(); + const instance = useInstance(); const soapboxConfig = useSoapboxConfig(); const { links } = soapboxConfig; const isLoading = useAppSelector((state) => state.verification.isLoading as boolean); - const siteTitle = useAppSelector((state) => state.instance.title); const [state, setState] = React.useState(initialState); const [shouldRedirect, setShouldRedirect] = React.useState(false); @@ -56,7 +56,7 @@ const Registration = () => { dispatch(startOnboarding()); dispatch( snackbar.success( - intl.formatMessage(messages.success, { siteTitle }), + intl.formatMessage(messages.success, { siteTitle: instance.title }), ), ); }) diff --git a/app/soapbox/features/verification/steps/age-verification.tsx b/app/soapbox/features/verification/steps/age-verification.tsx index bb35ffe40..52f6566df 100644 --- a/app/soapbox/features/verification/steps/age-verification.tsx +++ b/app/soapbox/features/verification/steps/age-verification.tsx @@ -4,7 +4,7 @@ import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import snackbar from 'soapbox/actions/snackbar'; import { verifyAge } from 'soapbox/actions/verification'; import { Button, Datepicker, Form, Text } from 'soapbox/components/ui'; -import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector, useInstance } from 'soapbox/hooks'; const messages = defineMessages({ fail: { @@ -24,10 +24,10 @@ function meetsAgeMinimum(birthday: Date, ageMinimum: number) { const AgeVerification = () => { const intl = useIntl(); const dispatch = useAppDispatch(); + const instance = useInstance(); const isLoading = useAppSelector((state) => state.verification.isLoading) as boolean; const ageMinimum = useAppSelector((state) => state.verification.ageMinimum) as any; - const siteTitle = useAppSelector((state) => state.instance.title); const [date, setDate] = React.useState(''); const isValid = typeof date === 'object'; @@ -65,7 +65,7 @@ const AgeVerification = () => { id='age_verification.body' defaultMessage='{siteTitle} requires users to be at least {ageMinimum} years old to access its platform. Anyone under the age of {ageMinimum} years old cannot access this platform.' values={{ - siteTitle, + siteTitle: instance.title, ageMinimum, }} /> diff --git a/app/soapbox/features/verification/waitlist-page.tsx b/app/soapbox/features/verification/waitlist-page.tsx index ebfe6a00d..0525bcef0 100644 --- a/app/soapbox/features/verification/waitlist-page.tsx +++ b/app/soapbox/features/verification/waitlist-page.tsx @@ -8,11 +8,11 @@ import { openModal } from 'soapbox/actions/modals'; import LandingGradient from 'soapbox/components/landing-gradient'; import SiteLogo from 'soapbox/components/site-logo'; import { Button, Stack, Text } from 'soapbox/components/ui'; -import { useAppSelector, useOwnAccount } from 'soapbox/hooks'; +import { useInstance, useOwnAccount } from 'soapbox/hooks'; -const WaitlistPage = (/* { account } */) => { +const WaitlistPage = () => { const dispatch = useDispatch(); - const title = useAppSelector((state) => state.instance.title); + const instance = useInstance(); const me = useOwnAccount(); const isSmsVerified = me?.source.get('sms_verified'); @@ -59,7 +59,7 @@ const WaitlistPage = (/* { account } */) => { diff --git a/app/soapbox/hooks/index.ts b/app/soapbox/hooks/index.ts index 32413dd8f..41f1ebfd4 100644 --- a/app/soapbox/hooks/index.ts +++ b/app/soapbox/hooks/index.ts @@ -5,6 +5,7 @@ export { useAppSelector } from './useAppSelector'; export { useCompose } from './useCompose'; export { useDimensions } from './useDimensions'; export { useFeatures } from './useFeatures'; +export { useInstance } from './useInstance'; export { useLocale } from './useLocale'; export { useOnScreen } from './useOnScreen'; export { useOwnAccount } from './useOwnAccount'; diff --git a/app/soapbox/hooks/useFeatures.ts b/app/soapbox/hooks/useFeatures.ts index 1fefa6bce..bd0929bf5 100644 --- a/app/soapbox/hooks/useFeatures.ts +++ b/app/soapbox/hooks/useFeatures.ts @@ -1,9 +1,9 @@ -import { useAppSelector } from 'soapbox/hooks'; -import { getFeatures } from 'soapbox/utils/features'; +import { getFeatures, Features } from 'soapbox/utils/features'; -import type { Features } from 'soapbox/utils/features'; +import { useInstance } from './useInstance'; -/** Get features for the current instance */ +/** Get features for the current instance. */ export const useFeatures = (): Features => { - return useAppSelector((state) => getFeatures(state.instance)); + const instance = useInstance(); + return getFeatures(instance); }; diff --git a/app/soapbox/hooks/useInstance.ts b/app/soapbox/hooks/useInstance.ts new file mode 100644 index 000000000..1a6c18362 --- /dev/null +++ b/app/soapbox/hooks/useInstance.ts @@ -0,0 +1,6 @@ +import { useAppSelector } from 'soapbox/hooks'; + +/** Get the Instance for the current backend. */ +export const useInstance = () => { + return useAppSelector((state) => state.instance); +}; diff --git a/app/soapbox/hooks/useOwnAccount.ts b/app/soapbox/hooks/useOwnAccount.ts index ea541dfee..15c6a83af 100644 --- a/app/soapbox/hooks/useOwnAccount.ts +++ b/app/soapbox/hooks/useOwnAccount.ts @@ -1,13 +1,21 @@ +import { useCallback } from 'react'; + import { useAppSelector } from 'soapbox/hooks'; import { makeGetAccount } from 'soapbox/selectors'; import type { Account } from 'soapbox/types/entities'; -// FIXME: There is no reason this selector shouldn't be global accross the whole app -// FIXME: getAccount() has the wrong type?? -const getAccount: (state: any, accountId: any) => any = makeGetAccount(); - -/** Get the logged-in account from the store, if any */ +/** Get the logged-in account from the store, if any. */ export const useOwnAccount = (): Account | null => { - return useAppSelector((state) => getAccount(state, state.me)); + const getAccount = useCallback(makeGetAccount(), []); + + return useAppSelector((state) => { + const { me } = state; + + if (typeof me === 'string') { + return getAccount(state, me); + } else { + return null; + } + }); };