diff --git a/.tool-versions b/.tool-versions index 5686ee0dbc..efc600fbd2 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -nodejs 18.12.1 +nodejs 18.13.0 diff --git a/app/soapbox/components/domain.tsx b/app/soapbox/components/domain.tsx index 191dc38723..117b941207 100644 --- a/app/soapbox/components/domain.tsx +++ b/app/soapbox/components/domain.tsx @@ -1,8 +1,8 @@ import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { unblockDomain } from 'soapbox/actions/domain-blocks'; +import { useAppDispatch } from 'soapbox/hooks'; import { HStack, IconButton, Text } from './ui'; @@ -16,7 +16,7 @@ interface IDomain { } const Domain: React.FC = ({ domain }) => { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const intl = useIntl(); // const onBlockDomain = () => { diff --git a/app/soapbox/components/emoji-button-wrapper.tsx b/app/soapbox/components/emoji-button-wrapper.tsx index cc528b1940..f0d287aa63 100644 --- a/app/soapbox/components/emoji-button-wrapper.tsx +++ b/app/soapbox/components/emoji-button-wrapper.tsx @@ -1,12 +1,11 @@ import classNames from 'clsx'; import React, { useState, useEffect, useRef } from 'react'; import { usePopper } from 'react-popper'; -import { useDispatch } from 'react-redux'; import { simpleEmojiReact } from 'soapbox/actions/emoji-reacts'; import { openModal } from 'soapbox/actions/modals'; import { EmojiSelector } from 'soapbox/components/ui'; -import { useAppSelector, useOwnAccount, useSoapboxConfig } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector, useOwnAccount, useSoapboxConfig } from 'soapbox/hooks'; import { isUserTouching } from 'soapbox/is-mobile'; import { getReactForStatus } from 'soapbox/utils/emoji-reacts'; @@ -17,7 +16,7 @@ interface IEmojiButtonWrapper { /** Provides emoji reaction functionality to the underlying button component */ const EmojiButtonWrapper: React.FC = ({ statusId, children }): JSX.Element | null => { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const ownAccount = useOwnAccount(); const status = useAppSelector(state => state.statuses.get(statusId)); const soapboxConfig = useSoapboxConfig(); diff --git a/app/soapbox/components/icon-button.tsx b/app/soapbox/components/icon-button.tsx index 71f1109956..9927ae8df9 100644 --- a/app/soapbox/components/icon-button.tsx +++ b/app/soapbox/components/icon-button.tsx @@ -90,7 +90,7 @@ const IconButton: React.FC = ({ type='button' >
-
{text && {text}} diff --git a/app/soapbox/components/icon.tsx b/app/soapbox/components/icon.tsx index f03f405801..8e875ed0ec 100644 --- a/app/soapbox/components/icon.tsx +++ b/app/soapbox/components/icon.tsx @@ -1,27 +1,28 @@ /** - * Icon: abstract icon class that can render icons from multiple sets. + * Icon: abstact component to render SVG icons. * @module soapbox/components/icon - * @see soapbox/components/fork_awesome_icon - * @see soapbox/components/svg_icon */ +import classNames from 'clsx'; import React from 'react'; +import InlineSVG from 'react-inlinesvg'; // eslint-disable-line no-restricted-imports -import ForkAwesomeIcon, { IForkAwesomeIcon } from './fork-awesome-icon'; -import SvgIcon, { ISvgIcon } from './svg-icon'; +export interface IIcon extends React.HTMLAttributes { + src: string, + id?: string, + alt?: string, + className?: string, +} -export type IIcon = IForkAwesomeIcon | ISvgIcon; - -const Icon: React.FC = (props) => { - if ((props as ISvgIcon).src) { - const { src, ...rest } = (props as ISvgIcon); - - return ; - } else { - const { id, fixedWidth, ...rest } = (props as IForkAwesomeIcon); - - return ; - } +const Icon: React.FC = ({ src, alt, className, ...rest }) => { + return ( +
+ } /> +
+ ); }; export default Icon; diff --git a/app/soapbox/components/media-gallery.tsx b/app/soapbox/components/media-gallery.tsx index 75ff9f7982..9b9d1fb7d7 100644 --- a/app/soapbox/components/media-gallery.tsx +++ b/app/soapbox/components/media-gallery.tsx @@ -1,5 +1,5 @@ import classNames from 'clsx'; -import React, { useState, useRef, useEffect } from 'react'; +import React, { useState, useRef, useLayoutEffect } from 'react'; import Blurhash from 'soapbox/components/blurhash'; import Icon from 'soapbox/components/icon'; @@ -533,7 +533,7 @@ const MediaGallery: React.FC = (props) => { /> )); - useEffect(() => { + useLayoutEffect(() => { if (node.current) { const { offsetWidth } = node.current; diff --git a/app/soapbox/components/sidebar-menu.tsx b/app/soapbox/components/sidebar-menu.tsx index 34fd62260f..766d3dc87e 100644 --- a/app/soapbox/components/sidebar-menu.tsx +++ b/app/soapbox/components/sidebar-menu.tsx @@ -2,7 +2,6 @@ import classNames from 'clsx'; import React from 'react'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { Link, NavLink } from 'react-router-dom'; import { fetchOwnAccounts, logOut, switchAccount } from 'soapbox/actions/auth'; @@ -11,7 +10,7 @@ import { closeSidebar } from 'soapbox/actions/sidebar'; import Account from 'soapbox/components/account'; import { Stack } from 'soapbox/components/ui'; import ProfileStats from 'soapbox/features/ui/components/profile-stats'; -import { useAppSelector, useFeatures } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks'; import { makeGetAccount, makeGetOtherAccounts } from 'soapbox/selectors'; import { Divider, HStack, Icon, IconButton, Text } from './ui'; @@ -81,7 +80,7 @@ const getOtherAccounts = makeGetOtherAccounts(); const SidebarMenu: React.FC = (): JSX.Element | null => { const intl = useIntl(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const features = useFeatures(); const getAccount = makeGetAccount(); diff --git a/app/soapbox/components/status-content.tsx b/app/soapbox/components/status-content.tsx index df35a90ac3..c361c22898 100644 --- a/app/soapbox/components/status-content.tsx +++ b/app/soapbox/components/status-content.tsx @@ -26,7 +26,7 @@ interface IReadMoreButton { const ReadMoreButton: React.FC = ({ onClick }) => ( ); diff --git a/app/soapbox/components/svg-icon.tsx b/app/soapbox/components/svg-icon.tsx deleted file mode 100644 index cd8942f68c..0000000000 --- a/app/soapbox/components/svg-icon.tsx +++ /dev/null @@ -1,29 +0,0 @@ -/** - * SvgIcon: abstact component to render SVG icons. - * @module soapbox/components/svg_icon - * @see soapbox/components/icon - */ - -import classNames from 'clsx'; -import React from 'react'; -import InlineSVG from 'react-inlinesvg'; // eslint-disable-line no-restricted-imports - -export interface ISvgIcon extends React.HTMLAttributes { - src: string, - id?: string, - alt?: string, - className?: string, -} - -const SvgIcon: React.FC = ({ src, alt, className, ...rest }) => { - return ( -
- } /> -
- ); -}; - -export default SvgIcon; diff --git a/app/soapbox/contexts/chat-context.tsx b/app/soapbox/contexts/chat-context.tsx index 5b72207c17..8c972a6d05 100644 --- a/app/soapbox/contexts/chat-context.tsx +++ b/app/soapbox/contexts/chat-context.tsx @@ -1,9 +1,8 @@ import React, { createContext, useContext, useEffect, useMemo, useState } from 'react'; -import { useDispatch } from 'react-redux'; import { useHistory, useParams } from 'react-router-dom'; import { toggleMainWindow } from 'soapbox/actions/chats'; -import { useOwnAccount, useSettings } from 'soapbox/hooks'; +import { useAppDispatch, useOwnAccount, useSettings } from 'soapbox/hooks'; import { IChat, useChat } from 'soapbox/queries/chats'; type WindowState = 'open' | 'minimized'; @@ -22,7 +21,7 @@ enum ChatWidgetScreens { const ChatProvider: React.FC = ({ children }) => { const history = useHistory(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const settings = useSettings(); const account = useOwnAccount(); diff --git a/app/soapbox/features/aliases/components/search.tsx b/app/soapbox/features/aliases/components/search.tsx index f64c4a3323..4a2ca3c760 100644 --- a/app/soapbox/features/aliases/components/search.tsx +++ b/app/soapbox/features/aliases/components/search.tsx @@ -1,12 +1,11 @@ import classNames from 'clsx'; import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { fetchAliasesSuggestions, clearAliasesSuggestions, changeAliasesSuggestions } from 'soapbox/actions/aliases'; import Icon from 'soapbox/components/icon'; import { Button } from 'soapbox/components/ui'; -import { useAppSelector } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; const messages = defineMessages({ search: { id: 'aliases.search', defaultMessage: 'Search your old account' }, @@ -14,7 +13,7 @@ const messages = defineMessages({ }); const Search: React.FC = () => { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const intl = useIntl(); const value = useAppSelector(state => state.aliases.suggestions.value); diff --git a/app/soapbox/features/audio/index.tsx b/app/soapbox/features/audio/index.tsx index ecbae5b166..17854386ec 100644 --- a/app/soapbox/features/audio/index.tsx +++ b/app/soapbox/features/audio/index.tsx @@ -1,7 +1,7 @@ import classNames from 'clsx'; import debounce from 'lodash/debounce'; import throttle from 'lodash/throttle'; -import React, { useEffect, useRef, useState } from 'react'; +import React, { useEffect, useLayoutEffect, useRef, useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import Icon from 'soapbox/components/icon'; @@ -397,7 +397,7 @@ const Audio: React.FC = (props) => { const progress = Math.min((currentTime / getDuration()) * 100, 100); - useEffect(() => { + useLayoutEffect(() => { if (player.current) { _setDimensions(); } diff --git a/app/soapbox/features/blocks/index.tsx b/app/soapbox/features/blocks/index.tsx index 730ef36436..c9d8a50c59 100644 --- a/app/soapbox/features/blocks/index.tsx +++ b/app/soapbox/features/blocks/index.tsx @@ -1,13 +1,12 @@ import debounce from 'lodash/debounce'; import React from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { fetchBlocks, expandBlocks } from 'soapbox/actions/blocks'; import ScrollableList from 'soapbox/components/scrollable-list'; import { Column, Spinner } from 'soapbox/components/ui'; import AccountContainer from 'soapbox/containers/account-container'; -import { useAppSelector } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; const messages = defineMessages({ heading: { id: 'column.blocks', defaultMessage: 'Blocked users' }, @@ -18,7 +17,7 @@ const handleLoadMore = debounce((dispatch) => { }, 300, { leading: true }); const Blocks: React.FC = () => { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const intl = useIntl(); const accountIds = useAppSelector((state) => state.user_lists.blocks.items); diff --git a/app/soapbox/features/chats/components/chat-page/__tests__/chat-page.test.tsx b/app/soapbox/features/chats/components/chat-page/__tests__/chat-page.test.tsx index ddac6c8bc2..21ed9ddb0b 100644 --- a/app/soapbox/features/chats/components/chat-page/__tests__/chat-page.test.tsx +++ b/app/soapbox/features/chats/components/chat-page/__tests__/chat-page.test.tsx @@ -6,7 +6,7 @@ import { __stub } from 'soapbox/api'; import { normalizeAccount } from 'soapbox/normalizers'; import { ReducerAccount } from 'soapbox/reducers/accounts'; -import { render, screen } from '../../../../../jest/test-helpers'; +import { render, screen, waitFor } from '../../../../../jest/test-helpers'; import ChatPage from '../chat-page'; describe('', () => { @@ -48,7 +48,10 @@ describe('', () => { await userEvent.click(screen.getByTestId('button')); expect(screen.getByTestId('chat-page')).toBeInTheDocument(); - expect(screen.getByTestId('toast')).toHaveTextContent('Chat Settings updated successfully'); + + await waitFor(() => { + expect(screen.getByTestId('toast')).toHaveTextContent('Chat Settings updated successfully'); + }); }); }); @@ -77,7 +80,10 @@ describe('', () => { it('renders the Chats', async () => { render(, undefined, store); await userEvent.click(screen.getByTestId('button')); - expect(screen.getByTestId('toast')).toHaveTextContent('Chat Settings failed to update.'); + + await waitFor(() => { + expect(screen.getByTestId('toast')).toHaveTextContent('Chat Settings failed to update.'); + }); }); }); }); diff --git a/app/soapbox/features/chats/components/chat-page/chat-page.tsx b/app/soapbox/features/chats/components/chat-page/chat-page.tsx index 407d49e7be..dbe50d3855 100644 --- a/app/soapbox/features/chats/components/chat-page/chat-page.tsx +++ b/app/soapbox/features/chats/components/chat-page/chat-page.tsx @@ -1,5 +1,5 @@ import classNames from 'clsx'; -import React, { useEffect, useRef, useState } from 'react'; +import React, { useEffect, useLayoutEffect, useRef, useState } from 'react'; import { matchPath, Route, Switch, useHistory } from 'react-router-dom'; import { Stack } from 'soapbox/components/ui'; @@ -44,7 +44,7 @@ const ChatPage: React.FC = ({ chatId }) => { setHeight(fullHeight - top + offset); }; - useEffect(() => { + useLayoutEffect(() => { calculateHeight(); }, [containerRef.current]); diff --git a/app/soapbox/features/compose/components/search.tsx b/app/soapbox/features/compose/components/search.tsx index 7961004409..5c7db5f05b 100644 --- a/app/soapbox/features/compose/components/search.tsx +++ b/app/soapbox/features/compose/components/search.tsx @@ -1,9 +1,7 @@ import classNames from 'clsx'; -import { Map as ImmutableMap } from 'immutable'; import debounce from 'lodash/debounce'; import React, { useCallback } from 'react'; import { defineMessages, useIntl } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { useHistory } from 'react-router-dom'; import { @@ -17,7 +15,8 @@ import { import AutosuggestAccountInput from 'soapbox/components/autosuggest-account-input'; import { Input } from 'soapbox/components/ui'; import SvgIcon from 'soapbox/components/ui/icon/svg-icon'; -import { useAppSelector } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; +import { AppDispatch, RootState } from 'soapbox/store'; const messages = defineMessages({ placeholder: { id: 'search.placeholder', defaultMessage: 'Search' }, @@ -25,7 +24,7 @@ const messages = defineMessages({ }); function redirectToAccount(accountId: string, routerHistory: any) { - return (_dispatch: any, getState: () => ImmutableMap) => { + return (_dispatch: AppDispatch, getState: () => RootState) => { const acct = getState().getIn(['accounts', accountId, 'acct']); if (acct && routerHistory) { @@ -49,7 +48,7 @@ const Search = (props: ISearch) => { openInRoute = false, } = props; - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const history = useHistory(); const intl = useIntl(); diff --git a/app/soapbox/features/developers/developers-challenge.tsx b/app/soapbox/features/developers/developers-challenge.tsx index e431f3242b..ee83ba8091 100644 --- a/app/soapbox/features/developers/developers-challenge.tsx +++ b/app/soapbox/features/developers/developers-challenge.tsx @@ -1,9 +1,9 @@ import React, { useState } from 'react'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { changeSettingImmediate } from 'soapbox/actions/settings'; import { Column, Button, Form, FormActions, FormGroup, Input, Text } from 'soapbox/components/ui'; +import { useAppDispatch } from 'soapbox/hooks'; import toast from 'soapbox/toast'; const messages = defineMessages({ @@ -15,7 +15,7 @@ const messages = defineMessages({ }); const DevelopersChallenge = () => { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const intl = useIntl(); const [answer, setAnswer] = useState(''); diff --git a/app/soapbox/features/developers/developers-menu.tsx b/app/soapbox/features/developers/developers-menu.tsx index 070c0f95e6..61e8c1de6c 100644 --- a/app/soapbox/features/developers/developers-menu.tsx +++ b/app/soapbox/features/developers/developers-menu.tsx @@ -1,11 +1,11 @@ import React from 'react'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { Link, useHistory } from 'react-router-dom'; import { changeSettingImmediate } from 'soapbox/actions/settings'; import { Column, Text } from 'soapbox/components/ui'; import SvgIcon from 'soapbox/components/ui/icon/svg-icon'; +import { useAppDispatch } from 'soapbox/hooks'; import toast from 'soapbox/toast'; import sourceCode from 'soapbox/utils/code'; @@ -31,7 +31,7 @@ const DashWidget: React.FC = ({ to, onClick, children }) => { }; const Developers: React.FC = () => { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const history = useHistory(); const intl = useIntl(); diff --git a/app/soapbox/features/directory/index.tsx b/app/soapbox/features/directory/index.tsx index 70d227868c..13ad6b8163 100644 --- a/app/soapbox/features/directory/index.tsx +++ b/app/soapbox/features/directory/index.tsx @@ -1,13 +1,12 @@ import classNames from 'clsx'; import React, { useEffect, useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; -import { useDispatch } from 'react-redux'; 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, useFeatures, useInstance } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector, useFeatures, useInstance } from 'soapbox/hooks'; import AccountCard from './components/account-card'; @@ -21,7 +20,7 @@ const messages = defineMessages({ const Directory = () => { const intl = useIntl(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const { search } = useLocation(); const params = new URLSearchParams(search); const instance = useInstance(); diff --git a/app/soapbox/features/domain-blocks/index.tsx b/app/soapbox/features/domain-blocks/index.tsx index ee96f2c56e..17d6f4757a 100644 --- a/app/soapbox/features/domain-blocks/index.tsx +++ b/app/soapbox/features/domain-blocks/index.tsx @@ -1,13 +1,12 @@ import debounce from 'lodash/debounce'; import React from 'react'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { fetchDomainBlocks, expandDomainBlocks } from 'soapbox/actions/domain-blocks'; import Domain from 'soapbox/components/domain'; import ScrollableList from 'soapbox/components/scrollable-list'; import { Column, Spinner } from 'soapbox/components/ui'; -import { useAppSelector } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; const messages = defineMessages({ heading: { id: 'column.domain_blocks', defaultMessage: 'Hidden domains' }, @@ -19,7 +18,7 @@ const handleLoadMore = debounce((dispatch) => { }, 300, { leading: true }); const DomainBlocks: React.FC = () => { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const intl = useIntl(); const domains = useAppSelector((state) => state.domain_lists.blocks.items); diff --git a/app/soapbox/features/feed-filtering/__tests__/feed-carousel.test.tsx b/app/soapbox/features/feed-filtering/__tests__/feed-carousel.test.tsx index 71d1c9f296..bb6048edb2 100644 --- a/app/soapbox/features/feed-filtering/__tests__/feed-carousel.test.tsx +++ b/app/soapbox/features/feed-filtering/__tests__/feed-carousel.test.tsx @@ -78,6 +78,9 @@ describe('', () => { expect(screen.getAllByTestId('carousel-item-avatar')[0]).toHaveClass('ring-primary-600'); }); + // HACK: wait for state change + await new Promise((r) => setTimeout(r, 0)); + // Marked as seen, not selected await userEvent.click(screen.getAllByTestId('carousel-item-avatar')[0]); await waitFor(() => { diff --git a/app/soapbox/features/follow-requests/components/account-authorize.tsx b/app/soapbox/features/follow-requests/components/account-authorize.tsx index b73d0a719b..b99f4c0fc6 100644 --- a/app/soapbox/features/follow-requests/components/account-authorize.tsx +++ b/app/soapbox/features/follow-requests/components/account-authorize.tsx @@ -1,11 +1,10 @@ import React, { useCallback } from 'react'; import { defineMessages, useIntl } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { authorizeFollowRequest, rejectFollowRequest } from 'soapbox/actions/accounts'; import Account from 'soapbox/components/account'; import { Button, HStack } from 'soapbox/components/ui'; -import { useAppSelector } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; import { makeGetAccount } from 'soapbox/selectors'; const messages = defineMessages({ @@ -19,7 +18,7 @@ interface IAccountAuthorize { const AccountAuthorize: React.FC = ({ id }) => { const intl = useIntl(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const getAccount = useCallback(makeGetAccount(), []); diff --git a/app/soapbox/features/follow-requests/index.tsx b/app/soapbox/features/follow-requests/index.tsx index 3d8700900b..f89681a01a 100644 --- a/app/soapbox/features/follow-requests/index.tsx +++ b/app/soapbox/features/follow-requests/index.tsx @@ -1,12 +1,11 @@ import debounce from 'lodash/debounce'; import React from 'react'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { fetchFollowRequests, expandFollowRequests } from 'soapbox/actions/accounts'; import ScrollableList from 'soapbox/components/scrollable-list'; import { Column, Spinner } from 'soapbox/components/ui'; -import { useAppSelector } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; import AccountAuthorize from './components/account-authorize'; @@ -19,7 +18,7 @@ const handleLoadMore = debounce((dispatch) => { }, 300, { leading: true }); const FollowRequests: React.FC = () => { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const intl = useIntl(); const accountIds = useAppSelector((state) => state.user_lists.follow_requests.items); diff --git a/app/soapbox/features/list-adder/components/list.tsx b/app/soapbox/features/list-adder/components/list.tsx index f1fb3ee40f..b02800c921 100644 --- a/app/soapbox/features/list-adder/components/list.tsx +++ b/app/soapbox/features/list-adder/components/list.tsx @@ -37,7 +37,7 @@ const List: React.FC = ({ listId }) => { return (
- + {list.title} diff --git a/app/soapbox/features/lists/components/new-list-form.tsx b/app/soapbox/features/lists/components/new-list-form.tsx index 645d6199cc..2f3ef50ba4 100644 --- a/app/soapbox/features/lists/components/new-list-form.tsx +++ b/app/soapbox/features/lists/components/new-list-form.tsx @@ -1,10 +1,9 @@ import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { changeListEditorTitle, submitListEditor } from 'soapbox/actions/lists'; import { Button, Form, HStack, Input } from 'soapbox/components/ui'; -import { useAppSelector } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; const messages = defineMessages({ label: { id: 'lists.new.title_placeholder', defaultMessage: 'New list title' }, @@ -13,7 +12,7 @@ const messages = defineMessages({ }); const NewListForm: React.FC = () => { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const intl = useIntl(); const value = useAppSelector((state) => state.listEditor.get('title')); diff --git a/app/soapbox/features/lists/index.tsx b/app/soapbox/features/lists/index.tsx index 082da7c5d2..5b6fed627f 100644 --- a/app/soapbox/features/lists/index.tsx +++ b/app/soapbox/features/lists/index.tsx @@ -1,6 +1,5 @@ import React, { useEffect } from 'react'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { Link } from 'react-router-dom'; import { createSelector } from 'reselect'; @@ -9,7 +8,7 @@ import { openModal } from 'soapbox/actions/modals'; import Icon from 'soapbox/components/icon'; import ScrollableList from 'soapbox/components/scrollable-list'; import { Column, IconButton, Spinner } from 'soapbox/components/ui'; -import { useAppSelector } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; import NewListForm from './components/new-list-form'; @@ -35,7 +34,7 @@ const getOrderedLists = createSelector([(state: RootState) => state.lists], list }); const Lists: React.FC = () => { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const intl = useIntl(); const lists = useAppSelector((state) => getOrderedLists(state)); @@ -85,7 +84,7 @@ const Lists: React.FC = () => { > {lists.map((list: any) => ( - + {list.title} diff --git a/app/soapbox/features/mutes/index.tsx b/app/soapbox/features/mutes/index.tsx index e7e8365aad..818fdec573 100644 --- a/app/soapbox/features/mutes/index.tsx +++ b/app/soapbox/features/mutes/index.tsx @@ -1,13 +1,12 @@ import debounce from 'lodash/debounce'; import React from 'react'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { fetchMutes, expandMutes } from 'soapbox/actions/mutes'; import ScrollableList from 'soapbox/components/scrollable-list'; import { Column, Spinner } from 'soapbox/components/ui'; import AccountContainer from 'soapbox/containers/account-container'; -import { useAppSelector } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; const messages = defineMessages({ heading: { id: 'column.mutes', defaultMessage: 'Muted users' }, @@ -18,7 +17,7 @@ const handleLoadMore = debounce((dispatch) => { }, 300, { leading: true }); const Mutes: React.FC = () => { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const intl = useIntl(); const accountIds = useAppSelector((state) => state.user_lists.mutes.items); diff --git a/app/soapbox/features/onboarding/onboarding-wizard.tsx b/app/soapbox/features/onboarding/onboarding-wizard.tsx index f892048852..25e4ad1283 100644 --- a/app/soapbox/features/onboarding/onboarding-wizard.tsx +++ b/app/soapbox/features/onboarding/onboarding-wizard.tsx @@ -1,12 +1,11 @@ import classNames from 'clsx'; import React from 'react'; -import { useDispatch } from 'react-redux'; import ReactSwipeableViews from 'react-swipeable-views'; import { endOnboarding } from 'soapbox/actions/onboarding'; import LandingGradient from 'soapbox/components/landing-gradient'; import { HStack } from 'soapbox/components/ui'; -import { useFeatures } from 'soapbox/hooks'; +import { useAppDispatch, useFeatures } from 'soapbox/hooks'; import AvatarSelectionStep from './steps/avatar-selection-step'; import BioStep from './steps/bio-step'; @@ -17,7 +16,7 @@ import FediverseStep from './steps/fediverse-step'; import SuggestedAccountsStep from './steps/suggested-accounts-step'; const OnboardingWizard = () => { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const features = useFeatures(); const [currentStep, setCurrentStep] = React.useState(0); diff --git a/app/soapbox/features/onboarding/steps/avatar-selection-step.tsx b/app/soapbox/features/onboarding/steps/avatar-selection-step.tsx index b2c17fe642..ab9956dac5 100644 --- a/app/soapbox/features/onboarding/steps/avatar-selection-step.tsx +++ b/app/soapbox/features/onboarding/steps/avatar-selection-step.tsx @@ -1,11 +1,10 @@ import classNames from 'clsx'; import React from 'react'; import { defineMessages, FormattedMessage } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { patchMe } from 'soapbox/actions/me'; import { Avatar, Button, Card, CardBody, Icon, Spinner, Stack, Text } from 'soapbox/components/ui'; -import { useOwnAccount } from 'soapbox/hooks'; +import { useAppDispatch, useOwnAccount } from 'soapbox/hooks'; import toast from 'soapbox/toast'; import { isDefaultAvatar } from 'soapbox/utils/accounts'; import resizeImage from 'soapbox/utils/resize-image'; @@ -17,7 +16,7 @@ const messages = defineMessages({ }); const AvatarSelectionStep = ({ onNext }: { onNext: () => void }) => { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const account = useOwnAccount(); const fileInput = React.useRef(null); diff --git a/app/soapbox/features/onboarding/steps/bio-step.tsx b/app/soapbox/features/onboarding/steps/bio-step.tsx index cf090a45fa..d0a5e37a96 100644 --- a/app/soapbox/features/onboarding/steps/bio-step.tsx +++ b/app/soapbox/features/onboarding/steps/bio-step.tsx @@ -1,10 +1,9 @@ import React from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { patchMe } from 'soapbox/actions/me'; import { Button, Card, CardBody, FormGroup, Stack, Text, Textarea } from 'soapbox/components/ui'; -import { useOwnAccount } from 'soapbox/hooks'; +import { useAppDispatch, useOwnAccount } from 'soapbox/hooks'; import toast from 'soapbox/toast'; import type { AxiosError } from 'axios'; @@ -16,7 +15,7 @@ const messages = defineMessages({ const BioStep = ({ onNext }: { onNext: () => void }) => { const intl = useIntl(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const account = useOwnAccount(); const [value, setValue] = React.useState(account?.source.get('note') || ''); diff --git a/app/soapbox/features/onboarding/steps/cover-photo-selection-step.tsx b/app/soapbox/features/onboarding/steps/cover-photo-selection-step.tsx index 4cf2493ad2..d7b2132474 100644 --- a/app/soapbox/features/onboarding/steps/cover-photo-selection-step.tsx +++ b/app/soapbox/features/onboarding/steps/cover-photo-selection-step.tsx @@ -1,12 +1,11 @@ import classNames from 'clsx'; import React from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { patchMe } from 'soapbox/actions/me'; import StillImage from 'soapbox/components/still-image'; import { Avatar, Button, Card, CardBody, Icon, Spinner, Stack, Text } from 'soapbox/components/ui'; -import { useOwnAccount } from 'soapbox/hooks'; +import { useAppDispatch, useOwnAccount } from 'soapbox/hooks'; import toast from 'soapbox/toast'; import { isDefaultHeader } from 'soapbox/utils/accounts'; import resizeImage from 'soapbox/utils/resize-image'; @@ -20,7 +19,7 @@ const messages = defineMessages({ const CoverPhotoSelectionStep = ({ onNext }: { onNext: () => void }) => { const intl = useIntl(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const account = useOwnAccount(); const fileInput = React.useRef(null); diff --git a/app/soapbox/features/onboarding/steps/display-name-step.tsx b/app/soapbox/features/onboarding/steps/display-name-step.tsx index 7d2b13925c..246ed3b93e 100644 --- a/app/soapbox/features/onboarding/steps/display-name-step.tsx +++ b/app/soapbox/features/onboarding/steps/display-name-step.tsx @@ -1,10 +1,9 @@ import React from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { patchMe } from 'soapbox/actions/me'; import { Button, Card, CardBody, FormGroup, Input, Stack, Text } from 'soapbox/components/ui'; -import { useOwnAccount } from 'soapbox/hooks'; +import { useAppDispatch, useOwnAccount } from 'soapbox/hooks'; import toast from 'soapbox/toast'; import type { AxiosError } from 'axios'; @@ -16,7 +15,7 @@ const messages = defineMessages({ const DisplayNameStep = ({ onNext }: { onNext: () => void }) => { const intl = useIntl(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const account = useOwnAccount(); const [value, setValue] = React.useState(account?.display_name || ''); diff --git a/app/soapbox/features/preferences/index.tsx b/app/soapbox/features/preferences/index.tsx index ebfd025470..09ea08eea0 100644 --- a/app/soapbox/features/preferences/index.tsx +++ b/app/soapbox/features/preferences/index.tsx @@ -1,13 +1,12 @@ import React from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { changeSetting } from 'soapbox/actions/settings'; import List, { ListItem } from 'soapbox/components/list'; import { Form } from 'soapbox/components/ui'; import { SelectDropdown } from 'soapbox/features/forms'; import SettingToggle from 'soapbox/features/notifications/components/setting-toggle'; -import { useFeatures, useSettings } from 'soapbox/hooks'; +import { useAppDispatch, useFeatures, useSettings } from 'soapbox/hooks'; import ThemeToggle from '../ui/components/theme-toggle'; @@ -89,7 +88,7 @@ const messages = defineMessages({ const Preferences = () => { const intl = useIntl(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const features = useFeatures(); const settings = useSettings(); diff --git a/app/soapbox/features/public-layout/components/header.tsx b/app/soapbox/features/public-layout/components/header.tsx index 4fef3df36b..49f801d84d 100644 --- a/app/soapbox/features/public-layout/components/header.tsx +++ b/app/soapbox/features/public-layout/components/header.tsx @@ -1,6 +1,5 @@ import React from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { Link, Redirect } from 'react-router-dom'; import { logIn, verifyCredentials } from 'soapbox/actions/auth'; @@ -8,7 +7,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, useInstance } from 'soapbox/hooks'; +import { useAppSelector, useFeatures, useSoapboxConfig, useOwnAccount, useInstance, useAppDispatch } from 'soapbox/hooks'; import Sonar from './sonar'; @@ -25,7 +24,7 @@ const messages = defineMessages({ }); const Header = () => { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const intl = useIntl(); const account = useOwnAccount(); diff --git a/app/soapbox/features/quotes/index.tsx b/app/soapbox/features/quotes/index.tsx index a93fc8317f..6ba38fe106 100644 --- a/app/soapbox/features/quotes/index.tsx +++ b/app/soapbox/features/quotes/index.tsx @@ -2,13 +2,12 @@ import { OrderedSet as ImmutableOrderedSet } from 'immutable'; import { debounce } from 'lodash'; import React from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { useParams } from 'react-router-dom'; import { expandStatusQuotes, fetchStatusQuotes } from 'soapbox/actions/status-quotes'; import StatusList from 'soapbox/components/status-list'; import { Column } from 'soapbox/components/ui'; -import { useAppSelector } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; const messages = defineMessages({ heading: { id: 'column.quotes', defaultMessage: 'Post quotes' }, @@ -18,7 +17,7 @@ const handleLoadMore = debounce((statusId: string, dispatch: React.Dispatch dispatch(expandStatusQuotes(statusId)), 300, { leading: true }); const Quotes: React.FC = () => { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const intl = useIntl(); const { statusId } = useParams<{ statusId: string }>(); diff --git a/app/soapbox/features/settings/index.tsx b/app/soapbox/features/settings/index.tsx index 06b8bf8a26..a0842984aa 100644 --- a/app/soapbox/features/settings/index.tsx +++ b/app/soapbox/features/settings/index.tsx @@ -1,12 +1,11 @@ import React, { useEffect } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; -import { useDispatch } from 'react-redux'; 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, useFeatures, useOwnAccount } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector, useFeatures, useOwnAccount } from 'soapbox/hooks'; import Preferences from '../preferences'; @@ -32,7 +31,7 @@ const messages = defineMessages({ /** User settings page. */ const Settings = () => { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const history = useHistory(); const intl = useIntl(); diff --git a/app/soapbox/features/soapbox-config/components/icon-picker-dropdown.tsx b/app/soapbox/features/soapbox-config/components/icon-picker-dropdown.tsx index d23e9f004f..9a5f903992 100644 --- a/app/soapbox/features/soapbox-config/components/icon-picker-dropdown.tsx +++ b/app/soapbox/features/soapbox-config/components/icon-picker-dropdown.tsx @@ -3,7 +3,7 @@ import { defineMessages, useIntl } from 'react-intl'; // @ts-ignore import Overlay from 'react-overlays/lib/Overlay'; -import Icon from 'soapbox/components/icon'; +import ForkAwesomeIcon from 'soapbox/components/fork-awesome-icon'; import IconPickerMenu from './icon-picker-menu'; @@ -68,7 +68,7 @@ const IconPickerDropdown: React.FC = ({ value, onPickEmoji onKeyDown={onToggle} tabIndex={0} > - +
diff --git a/app/soapbox/features/test-timeline/index.tsx b/app/soapbox/features/test-timeline/index.tsx index 1c64ecad53..51ab1491e0 100644 --- a/app/soapbox/features/test-timeline/index.tsx +++ b/app/soapbox/features/test-timeline/index.tsx @@ -1,9 +1,9 @@ import React from 'react'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { importFetchedStatuses } from 'soapbox/actions/importer'; import { expandTimelineSuccess } from 'soapbox/actions/timelines'; +import { useAppDispatch } from 'soapbox/hooks'; import { Column } from '../../components/ui'; import Timeline from '../ui/components/timeline'; @@ -31,7 +31,7 @@ const onlyMedia = false; const TestTimeline: React.FC = () => { const intl = useIntl(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); React.useEffect(() => { dispatch(importFetchedStatuses(MOCK_STATUSES)); diff --git a/app/soapbox/features/ui/components/action-button.tsx b/app/soapbox/features/ui/components/action-button.tsx index 299702d946..b3fd2a38b0 100644 --- a/app/soapbox/features/ui/components/action-button.tsx +++ b/app/soapbox/features/ui/components/action-button.tsx @@ -1,6 +1,5 @@ import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { followAccount, @@ -14,7 +13,7 @@ import { } from 'soapbox/actions/accounts'; import { openModal } from 'soapbox/actions/modals'; import { Button, HStack } from 'soapbox/components/ui'; -import { useAppSelector, useFeatures } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks'; import type { Account as AccountEntity } from 'soapbox/types/entities'; @@ -49,7 +48,7 @@ interface IActionButton { * `actionType` prop. */ const ActionButton: React.FC = ({ account, actionType, small }) => { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const features = useFeatures(); const intl = useIntl(); diff --git a/app/soapbox/features/ui/components/link-footer.tsx b/app/soapbox/features/ui/components/link-footer.tsx index bc1c67fd37..20c04f1e6c 100644 --- a/app/soapbox/features/ui/components/link-footer.tsx +++ b/app/soapbox/features/ui/components/link-footer.tsx @@ -1,13 +1,12 @@ import classNames from 'clsx'; import React from 'react'; import { FormattedMessage } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { Link } from 'react-router-dom'; import { logOut } from 'soapbox/actions/auth'; import { Text } from 'soapbox/components/ui'; import emojify from 'soapbox/features/emoji/emoji'; -import { useSoapboxConfig, useOwnAccount, useFeatures } from 'soapbox/hooks'; +import { useSoapboxConfig, useOwnAccount, useFeatures, useAppDispatch } from 'soapbox/hooks'; import sourceCode from 'soapbox/utils/code'; interface IFooterLink { @@ -29,7 +28,7 @@ const LinkFooter: React.FC = (): JSX.Element => { const features = useFeatures(); const soapboxConfig = useSoapboxConfig(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const onClickLogOut: React.EventHandler = (e) => { dispatch(logOut()); diff --git a/app/soapbox/features/ui/components/list-panel.tsx b/app/soapbox/features/ui/components/list-panel.tsx deleted file mode 100644 index 8160217e9f..0000000000 --- a/app/soapbox/features/ui/components/list-panel.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import React, { useEffect } from 'react'; -import { NavLink } from 'react-router-dom'; -import { createSelector } from 'reselect'; - -import { fetchLists } from 'soapbox/actions/lists'; -import Icon from 'soapbox/components/icon'; -import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; - -import type { List as ImmutableList } from 'immutable'; -import type { RootState } from 'soapbox/store'; -import type { List as ListEntity } from 'soapbox/types/entities'; - -const getOrderedLists = createSelector([(state: RootState) => state.lists], lists => { - if (!lists) { - return lists; - } - - return lists.toList().filter(item => !!item).sort((a, b) => (a as ListEntity).title.localeCompare((b as ListEntity).title)).take(4) as ImmutableList; -}); - -const ListPanel = () => { - const dispatch = useAppDispatch(); - - const lists = useAppSelector((state) => getOrderedLists(state)); - - useEffect(() => { - dispatch(fetchLists()); - }, []); - - if (!lists || lists.isEmpty()) { - return null; - } - - return ( -
-
- - {lists.map(list => ( - {list.title} - ))} -
- ); -}; - -export default ListPanel; diff --git a/app/soapbox/features/ui/components/modals/report-modal/__tests__/report-modal.test.tsx b/app/soapbox/features/ui/components/modals/report-modal/__tests__/report-modal.test.tsx index a2dee08b52..269f97146c 100644 --- a/app/soapbox/features/ui/components/modals/report-modal/__tests__/report-modal.test.tsx +++ b/app/soapbox/features/ui/components/modals/report-modal/__tests__/report-modal.test.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { __stub } from 'soapbox/api'; -import { render, screen } from '../../../../../../jest/test-helpers'; +import { render, screen, waitFor } from '../../../../../../jest/test-helpers'; import { normalizeAccount, normalizeStatus } from '../../../../../../normalizers'; import ReportModal from '../report-modal'; @@ -64,6 +64,9 @@ describe('', () => { await user.click(screen.getByTestId('rule-1')); await user.click(screen.getByText(/Next/)); await user.click(screen.getByText(/Submit/)); - expect(screen.getByText(/Thanks for submitting your report/)).toBeInTheDocument(); + + await waitFor(() => { + expect(screen.getByText(/Thanks for submitting your report/)).toBeInTheDocument(); + }); }); }); diff --git a/app/soapbox/features/ui/components/modals/report-modal/steps/other-actions-step.tsx b/app/soapbox/features/ui/components/modals/report-modal/steps/other-actions-step.tsx index 964fbd1ca1..8c4def9682 100644 --- a/app/soapbox/features/ui/components/modals/report-modal/steps/other-actions-step.tsx +++ b/app/soapbox/features/ui/components/modals/report-modal/steps/other-actions-step.tsx @@ -1,14 +1,13 @@ import { OrderedSet } from 'immutable'; import React, { useEffect, useState } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; -import { useDispatch } from 'react-redux'; import Toggle from 'react-toggle'; import { changeReportBlock, changeReportForward } from 'soapbox/actions/reports'; import { fetchRules } from 'soapbox/actions/rules'; import { Button, FormGroup, HStack, Stack, Text } from 'soapbox/components/ui'; import StatusCheckBox from 'soapbox/features/report/components/status-check-box'; -import { useAppSelector, useFeatures } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks'; import { isRemote, getDomain } from 'soapbox/utils/accounts'; import type { ReducerAccount } from 'soapbox/reducers/accounts'; @@ -26,7 +25,7 @@ interface IOtherActionsStep { } const OtherActionsStep = ({ account }: IOtherActionsStep) => { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const features = useFeatures(); const intl = useIntl(); diff --git a/app/soapbox/features/ui/components/modals/report-modal/steps/reason-step.tsx b/app/soapbox/features/ui/components/modals/report-modal/steps/reason-step.tsx index 871ade6c47..6cf653c6b0 100644 --- a/app/soapbox/features/ui/components/modals/report-modal/steps/reason-step.tsx +++ b/app/soapbox/features/ui/components/modals/report-modal/steps/reason-step.tsx @@ -1,12 +1,11 @@ import classNames from 'clsx'; import React, { useEffect, useMemo, useRef, useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { changeReportComment, changeReportRule } from 'soapbox/actions/reports'; import { fetchRules } from 'soapbox/actions/rules'; import { FormGroup, Stack, Text, Textarea } from 'soapbox/components/ui'; -import { useAppSelector } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; import type { ReducerAccount } from 'soapbox/reducers/accounts'; @@ -22,7 +21,7 @@ interface IReasonStep { const RULES_HEIGHT = 385; const ReasonStep = (_props: IReasonStep) => { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const intl = useIntl(); const rulesListRef = useRef(null); diff --git a/app/soapbox/features/ui/components/navbar.tsx b/app/soapbox/features/ui/components/navbar.tsx index 72ba8c7925..d9829afcaf 100644 --- a/app/soapbox/features/ui/components/navbar.tsx +++ b/app/soapbox/features/ui/components/navbar.tsx @@ -1,7 +1,6 @@ import classNames from 'clsx'; import React, { useRef, useState } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { Link, Redirect } from 'react-router-dom'; import { logIn, verifyCredentials } from 'soapbox/actions/auth'; @@ -10,7 +9,7 @@ import { openSidebar } from 'soapbox/actions/sidebar'; import SiteLogo from 'soapbox/components/site-logo'; import { Avatar, Button, Form, HStack, IconButton, Input, Tooltip } from 'soapbox/components/ui'; import Search from 'soapbox/features/compose/components/search'; -import { useOwnAccount, useSoapboxConfig } from 'soapbox/hooks'; +import { useAppDispatch, useOwnAccount, useSoapboxConfig } from 'soapbox/hooks'; import ProfileDropdown from './profile-dropdown'; @@ -24,7 +23,7 @@ const messages = defineMessages({ }); const Navbar = () => { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const intl = useIntl(); const node = useRef(null); diff --git a/app/soapbox/features/ui/components/profile-dropdown.tsx b/app/soapbox/features/ui/components/profile-dropdown.tsx index e14c139e3c..346784b63d 100644 --- a/app/soapbox/features/ui/components/profile-dropdown.tsx +++ b/app/soapbox/features/ui/components/profile-dropdown.tsx @@ -1,13 +1,12 @@ import throttle from 'lodash/throttle'; import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { Link } from 'react-router-dom'; import { fetchOwnAccounts, logOut, switchAccount } from 'soapbox/actions/auth'; import Account from 'soapbox/components/account'; import { Menu, MenuButton, MenuDivider, MenuItem, MenuLink, MenuList } from 'soapbox/components/ui'; -import { useAppSelector, useFeatures } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks'; import { makeGetAccount } from 'soapbox/selectors'; import ThemeToggle from './theme-toggle'; @@ -35,7 +34,7 @@ type IMenuItem = { const getAccount = makeGetAccount(); const ProfileDropdown: React.FC = ({ account, children }) => { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const features = useFeatures(); const intl = useIntl(); diff --git a/app/soapbox/features/ui/components/profile-familiar-followers.tsx b/app/soapbox/features/ui/components/profile-familiar-followers.tsx index 5a9d9ede9c..48edb47166 100644 --- a/app/soapbox/features/ui/components/profile-familiar-followers.tsx +++ b/app/soapbox/features/ui/components/profile-familiar-followers.tsx @@ -1,7 +1,6 @@ import { OrderedSet as ImmutableOrderedSet } from 'immutable'; import React, { useEffect } from 'react'; import { FormattedList, FormattedMessage } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { Link } from 'react-router-dom'; import { fetchAccountFamiliarFollowers } from 'soapbox/actions/familiar-followers'; @@ -9,7 +8,7 @@ import { openModal } from 'soapbox/actions/modals'; import HoverRefWrapper from 'soapbox/components/hover-ref-wrapper'; import { Text } from 'soapbox/components/ui'; import VerificationBadge from 'soapbox/components/verification-badge'; -import { useAppSelector, useFeatures } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks'; import { makeGetAccount } from 'soapbox/selectors'; import type { Account } from 'soapbox/types/entities'; @@ -21,7 +20,7 @@ interface IProfileFamiliarFollowers { } const ProfileFamiliarFollowers: React.FC = ({ account }) => { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const me = useAppSelector((state) => state.me); const features = useFeatures(); const familiarFollowerIds = useAppSelector(state => state.user_lists.familiar_followers.get(account.id)?.items || ImmutableOrderedSet()); diff --git a/app/soapbox/features/ui/components/profile-media-panel.tsx b/app/soapbox/features/ui/components/profile-media-panel.tsx index cb71d777c8..47234dd062 100644 --- a/app/soapbox/features/ui/components/profile-media-panel.tsx +++ b/app/soapbox/features/ui/components/profile-media-panel.tsx @@ -1,12 +1,11 @@ import { List as ImmutableList } from 'immutable'; import React, { useState, useEffect } from 'react'; import { FormattedMessage } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { openModal } from 'soapbox/actions/modals'; import { expandAccountMediaTimeline } from 'soapbox/actions/timelines'; import { Spinner, Text, Widget } from 'soapbox/components/ui'; -import { useAppSelector } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; import { getAccountGallery } from 'soapbox/selectors'; import MediaItem from '../../account-gallery/components/media-item'; @@ -18,7 +17,7 @@ interface IProfileMediaPanel { } const ProfileMediaPanel: React.FC = ({ account }) => { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const [loading, setLoading] = useState(true); diff --git a/app/soapbox/features/ui/components/promo-panel.tsx b/app/soapbox/features/ui/components/promo-panel.tsx index 12798dc063..778302f11b 100644 --- a/app/soapbox/features/ui/components/promo-panel.tsx +++ b/app/soapbox/features/ui/components/promo-panel.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import Icon from 'soapbox/components/icon'; +import ForkAwesomeIcon from 'soapbox/components/fork-awesome-icon'; import { Widget, Stack, Text } from 'soapbox/components/ui'; import { useInstance, useSettings, useSoapboxConfig } from 'soapbox/hooks'; @@ -20,7 +20,7 @@ const PromoPanel: React.FC = () => { {promoItems.map((item, i) => ( - + {item.textLocales.get(locale) || item.text} diff --git a/app/soapbox/features/ui/components/theme-toggle.tsx b/app/soapbox/features/ui/components/theme-toggle.tsx index 3465190b94..5cfd09d4d4 100644 --- a/app/soapbox/features/ui/components/theme-toggle.tsx +++ b/app/soapbox/features/ui/components/theme-toggle.tsx @@ -1,14 +1,13 @@ import React from 'react'; -import { useDispatch } from 'react-redux'; import { changeSetting } from 'soapbox/actions/settings'; -import { useSettings } from 'soapbox/hooks'; +import { useAppDispatch, useSettings } from 'soapbox/hooks'; import ThemeSelector from './theme-selector'; /** Stateful theme selector. */ const ThemeToggle: React.FC = () => { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const themeMode = useSettings().get('themeMode'); const handleChange = (themeMode: string) => { diff --git a/app/soapbox/features/verification/__tests__/registration.test.tsx b/app/soapbox/features/verification/__tests__/registration.test.tsx index fbde2fce36..b408947f6a 100644 --- a/app/soapbox/features/verification/__tests__/registration.test.tsx +++ b/app/soapbox/features/verification/__tests__/registration.test.tsx @@ -30,7 +30,10 @@ describe('', () => { fireEvent.submit(screen.getByTestId('button'), { preventDefault: () => {} }); }); - expect(screen.getByTestId('toast')).toHaveTextContent(/welcome to/i); + await waitFor(() => { + expect(screen.getByTestId('toast')).toHaveTextContent(/welcome to/i); + }); + expect(screen.queryAllByRole('heading')).toHaveLength(0); }); }); @@ -47,7 +50,9 @@ describe('', () => { fireEvent.submit(screen.getByTestId('button'), { preventDefault: () => {} }); }); - expect(screen.getByTestId('toast')).toHaveTextContent(/this username has already been taken/i); + await waitFor(() => { + expect(screen.getByTestId('toast')).toHaveTextContent(/this username has already been taken/i); + }); }); it('handles generic errors', async() => { @@ -61,7 +66,9 @@ describe('', () => { fireEvent.submit(screen.getByTestId('button'), { preventDefault: () => {} }); }); - expect(screen.getByTestId('toast')).toHaveTextContent(/failed to register your account/i); + await waitFor(() => { + expect(screen.getByTestId('toast')).toHaveTextContent(/failed to register your account/i); + }); }); }); diff --git a/app/soapbox/features/verification/steps/__tests__/email-verification.test.tsx b/app/soapbox/features/verification/steps/__tests__/email-verification.test.tsx index d1c71649f9..38e7cf8a79 100644 --- a/app/soapbox/features/verification/steps/__tests__/email-verification.test.tsx +++ b/app/soapbox/features/verification/steps/__tests__/email-verification.test.tsx @@ -60,7 +60,9 @@ describe('', () => { ); }); - expect(screen.getByTestId('form-group-error')).toHaveTextContent('is taken'); + await waitFor(() => { + expect(screen.getByTestId('form-group-error')).toHaveTextContent('is taken'); + }); }); }); }); diff --git a/app/soapbox/features/verification/steps/__tests__/sms-verification.test.tsx b/app/soapbox/features/verification/steps/__tests__/sms-verification.test.tsx index 097bb29118..d837f46b7d 100644 --- a/app/soapbox/features/verification/steps/__tests__/sms-verification.test.tsx +++ b/app/soapbox/features/verification/steps/__tests__/sms-verification.test.tsx @@ -37,8 +37,10 @@ describe('', () => { ); }); - expect(screen.getByRole('heading')).toHaveTextContent('Verification code'); - expect(screen.getByTestId('toast')).toHaveTextContent('A verification code has been sent to your phone number.'); + await waitFor(() => { + expect(screen.getByRole('heading')).toHaveTextContent('Verification code'); + expect(screen.getByTestId('toast')).toHaveTextContent('A verification code has been sent to your phone number.'); + }); act(() => { toast.remove(); @@ -68,8 +70,10 @@ describe('', () => { ); }); - expect(screen.getByRole('heading')).toHaveTextContent('Verification code'); - expect(screen.getByTestId('toast')).toHaveTextContent('A verification code has been sent to your phone number.'); + await waitFor(() => { + expect(screen.getByRole('heading')).toHaveTextContent('Verification code'); + expect(screen.getByTestId('toast')).toHaveTextContent('A verification code has been sent to your phone number.'); + }); act(() => { toast.remove(); @@ -82,7 +86,9 @@ describe('', () => { await userEvent.type(screen.getByLabelText('Digit 5'), '5'); await userEvent.type(screen.getByLabelText('Digit 6'), '6'); - expect(screen.getByTestId('toast')).toHaveTextContent('Your SMS token has expired.'); + await waitFor(() => { + expect(screen.getByTestId('toast')).toHaveTextContent('Your SMS token has expired.'); + }); }); }); @@ -106,7 +112,9 @@ describe('', () => { ); }); - expect(screen.getByTestId('toast')).toHaveTextContent('Failed to send SMS message to your phone number.'); + await waitFor(() => { + expect(screen.getByTestId('toast')).toHaveTextContent('Failed to send SMS message to your phone number.'); + }); }); }); }); diff --git a/app/soapbox/features/verification/waitlist-page.tsx b/app/soapbox/features/verification/waitlist-page.tsx index 0525bcef01..67de7ac755 100644 --- a/app/soapbox/features/verification/waitlist-page.tsx +++ b/app/soapbox/features/verification/waitlist-page.tsx @@ -1,6 +1,5 @@ import React, { useEffect } from 'react'; import { FormattedMessage } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { Link } from 'react-router-dom'; import { logOut } from 'soapbox/actions/auth'; @@ -8,10 +7,10 @@ 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 { useInstance, useOwnAccount } from 'soapbox/hooks'; +import { useAppDispatch, useInstance, useOwnAccount } from 'soapbox/hooks'; const WaitlistPage = () => { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const instance = useInstance(); const me = useOwnAccount(); diff --git a/app/soapbox/features/video/index.tsx b/app/soapbox/features/video/index.tsx index 71aaae65da..bd63528e4c 100644 --- a/app/soapbox/features/video/index.tsx +++ b/app/soapbox/features/video/index.tsx @@ -1,7 +1,7 @@ import classNames from 'clsx'; import debounce from 'lodash/debounce'; import throttle from 'lodash/throttle'; -import React, { useCallback, useEffect, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import Blurhash from 'soapbox/components/blurhash'; @@ -159,7 +159,7 @@ const Video: React.FC = ({ } }; - useEffect(() => { + useLayoutEffect(() => { setDimensions(); }, [player.current]); diff --git a/app/soapbox/hooks/useDimensions.ts b/app/soapbox/hooks/useDimensions.ts index bf7fc78b88..b08c05f4e3 100644 --- a/app/soapbox/hooks/useDimensions.ts +++ b/app/soapbox/hooks/useDimensions.ts @@ -25,7 +25,7 @@ const useDimensions = (): UseDimensionsResult => { ); useEffect((): any => { - if (!element) return null; + if (!element) return; observer.observe(element); return () => { diff --git a/app/soapbox/jest/test-setup.ts b/app/soapbox/jest/test-setup.ts index dc27e9a89d..d4d16859b7 100644 --- a/app/soapbox/jest/test-setup.ts +++ b/app/soapbox/jest/test-setup.ts @@ -1,5 +1,6 @@ 'use strict'; +import { act } from '@testing-library/react'; import { toast } from 'react-hot-toast'; import { __clear as clearApiMocks } from '../api/__mocks__'; @@ -17,7 +18,9 @@ require('fake-indexeddb/auto'); // Clear toasts after each test. afterEach(() => { - toast.remove(); + act(() => { + toast.remove(); + }); }); const intersectionObserverMock = () => ({ observe: () => null, disconnect: () => null }); diff --git a/app/soapbox/main.tsx b/app/soapbox/main.tsx index e2c9a6b2d1..610f44f1d8 100644 --- a/app/soapbox/main.tsx +++ b/app/soapbox/main.tsx @@ -1,7 +1,7 @@ import * as OfflinePluginRuntime from '@lcdp/offline-plugin/runtime'; import React from 'react'; import 'react-datepicker/dist/react-datepicker.css'; -import ReactDOM from 'react-dom'; +import { createRoot } from 'react-dom/client'; import { defineMessages } from 'react-intl'; import { setSwUpdating } from 'soapbox/actions/sw'; @@ -35,9 +35,10 @@ if (BuildConfig.NODE_ENV === 'production') { } ready(() => { - const mountNode = document.getElementById('soapbox') as HTMLElement; + const container = document.getElementById('soapbox') as HTMLElement; + const root = createRoot(container); - ReactDOM.render(, mountNode); + root.render(); if (BuildConfig.NODE_ENV === 'production') { // avoid offline in dev mode because it's harder to debug diff --git a/app/soapbox/queries/__tests__/relationships.test.ts b/app/soapbox/queries/__tests__/relationships.test.ts index 65b5e544fe..6466da7ff1 100644 --- a/app/soapbox/queries/__tests__/relationships.test.ts +++ b/app/soapbox/queries/__tests__/relationships.test.ts @@ -30,7 +30,7 @@ describe('useFetchRelationships()', () => { }); it('is successful', async() => { - const { result } = renderHook(() => { + renderHook(() => { const fetchRelationships = useFetchRelationships(); useEffect(() => { @@ -40,11 +40,11 @@ describe('useFetchRelationships()', () => { return fetchRelationships; }, undefined, store); - await waitFor(() => expect(result.current.isLoading).toBe(false)); - - expect(store.getState().relationships.size).toBe(1); - expect(store.getState().relationships.getIn([id, 'id'])).toBe(id); - expect(store.getState().relationships.getIn([id, 'blocked_by'])).toBe(true); + await waitFor(() => { + expect(store.getState().relationships.size).toBe(1); + expect(store.getState().relationships.getIn([id, 'id'])).toBe(id); + expect(store.getState().relationships.getIn([id, 'blocked_by'])).toBe(true); + }); }); }); @@ -60,7 +60,7 @@ describe('useFetchRelationships()', () => { }); it('is successful', async() => { - const { result } = renderHook(() => { + renderHook(() => { const fetchRelationships = useFetchRelationships(); useEffect(() => { @@ -70,11 +70,11 @@ describe('useFetchRelationships()', () => { return fetchRelationships; }, undefined, store); - await waitFor(() => expect(result.current.isLoading).toBe(false)); - - expect(store.getState().relationships.size).toBe(2); - expect(store.getState().relationships.getIn([ids[0], 'id'])).toBe(ids[0]); - expect(store.getState().relationships.getIn([ids[1], 'id'])).toBe(ids[1]); + await waitFor(() => { + expect(store.getState().relationships.size).toBe(2); + expect(store.getState().relationships.getIn([ids[0], 'id'])).toBe(ids[0]); + expect(store.getState().relationships.getIn([ids[1], 'id'])).toBe(ids[1]); + }); }); }); }); diff --git a/package.json b/package.json index 4421270e44..94443f46e7 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "@tailwindcss/line-clamp": "^0.4.2", "@tailwindcss/typography": "^0.5.7", "@tanstack/react-query": "^4.0.10", - "@testing-library/react": "^12.1.4", + "@testing-library/react": "^13.0.0", "@types/escape-html": "^1.0.1", "@types/http-link-header": "^1.0.3", "@types/jest": "^29.0.0", @@ -146,10 +146,10 @@ "prop-types": "^15.5.10", "punycode": "^2.1.1", "qrcode.react": "^3.0.2", - "react": "^17.0.2", + "react": "^18.0.0", "react-color": "^2.19.3", "react-datepicker": "^4.8.0", - "react-dom": "^17.0.2", + "react-dom": "^18.0.0", "react-helmet": "^6.1.0", "react-hot-toast": "^2.4.0", "react-hotkeys": "^1.1.4", @@ -161,7 +161,7 @@ "react-otp-input": "^2.4.0", "react-overlays": "^0.9.0", "react-popper": "^2.3.0", - "react-redux": "^7.2.5", + "react-redux": "^8.0.0", "react-router-dom": "^5.3.0", "react-router-scroll-4": "^1.0.0-beta.2", "react-simple-pull-to-refresh": "^1.3.3", diff --git a/yarn.lock b/yarn.lock index 28a27099d2..3b6e56469c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2391,14 +2391,14 @@ "@types/use-sync-external-store" "^0.0.3" use-sync-external-store "^1.2.0" -"@testing-library/dom@^8.0.0": - version "8.12.0" - resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.12.0.tgz#fef5e545533fb084175dda6509ee71d7d2f72e23" - integrity sha512-rBrJk5WjI02X1edtiUcZhgyhgBhiut96r5Jp8J5qktKdcvLcZpKDW8i2hkGMMItxrghjXuQ5AM6aE0imnFawaw== +"@testing-library/dom@^8.5.0": + version "8.19.1" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.19.1.tgz#0e2dafd281dedb930bb235eac1045470b4129d0e" + integrity sha512-P6iIPyYQ+qH8CvGauAqanhVnjrnRe0IZFSYCeGkSRW9q3u8bdVn2NPI+lasFyVsEQn1J/IFmp5Aax41+dAP9wg== dependencies: "@babel/code-frame" "^7.10.4" "@babel/runtime" "^7.12.5" - "@types/aria-query" "^4.2.0" + "@types/aria-query" "^5.0.1" aria-query "^5.0.0" chalk "^4.1.0" dom-accessibility-api "^0.5.9" @@ -2428,14 +2428,14 @@ "@babel/runtime" "^7.12.5" react-error-boundary "^3.1.0" -"@testing-library/react@^12.1.4": - version "12.1.4" - resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.1.4.tgz#09674b117e550af713db3f4ec4c0942aa8bbf2c0" - integrity sha512-jiPKOm7vyUw311Hn/HlNQ9P8/lHNtArAx0PisXyFixDDvfl8DbD6EUdbshK5eqauvBSvzZd19itqQ9j3nferJA== +"@testing-library/react@^13.0.0": + version "13.4.0" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-13.4.0.tgz#6a31e3bf5951615593ad984e96b9e5e2d9380966" + integrity sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw== dependencies: "@babel/runtime" "^7.12.5" - "@testing-library/dom" "^8.0.0" - "@types/react-dom" "*" + "@testing-library/dom" "^8.5.0" + "@types/react-dom" "^18.0.0" "@testing-library/user-event@^14.0.3": version "14.0.3" @@ -2472,10 +2472,10 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== -"@types/aria-query@^4.2.0": - version "4.2.2" - resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.2.tgz#ed4e0ad92306a704f9fb132a0cfcf77486dbe2bc" - integrity sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig== +"@types/aria-query@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.1.tgz#3286741fb8f1e1580ac28784add4c7a1d49bdfbc" + integrity sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q== "@types/babel__core@^7.1.12", "@types/babel__core@^7.1.14": version "7.1.19" @@ -2660,7 +2660,7 @@ resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.11.tgz#56588b17ae8f50c53983a524fc3cc47437969d64" integrity sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA== -"@types/hoist-non-react-statics@^3.3.0", "@types/hoist-non-react-statics@^3.3.1": +"@types/hoist-non-react-statics@^3.3.1": version "3.3.1" resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== @@ -2850,10 +2850,10 @@ date-fns "^2.0.1" react-popper "^2.2.5" -"@types/react-dom@*": - version "17.0.14" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.14.tgz#c8f917156b652ddf807711f5becbd2ab018dea9f" - integrity sha512-H03xwEP1oXmSfl3iobtmQ/2dHF5aBHr8aUMwyGZya6OW45G+xtdzmq6HkncefiBt5JU8DVyaWl/nWZbjZCnzAQ== +"@types/react-dom@^18.0.0": + version "18.0.10" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.10.tgz#3b66dec56aa0f16a6cc26da9e9ca96c35c0b4352" + integrity sha512-E42GW/JA4Qv15wQdqJq8DL4JhNpB3prJgjgapN3qJT9K2zO5IIAQh4VXvCEDupoqAwnz0cY4RlXeC/ajX5SFHg== dependencies: "@types/react" "*" @@ -2871,16 +2871,6 @@ dependencies: "@types/react" "*" -"@types/react-redux@^7.1.16": - version "7.1.18" - resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.18.tgz#2bf8fd56ebaae679a90ebffe48ff73717c438e04" - integrity sha512-9iwAsPyJ9DLTRH+OFeIrm9cAbIj1i2ANL3sKQFATqnPWRbg+jEFXyZOKHiQK/N86pNRXbb4HRxAxo0SIX1XwzQ== - dependencies: - "@types/hoist-non-react-statics" "^3.3.0" - "@types/react" "*" - hoist-non-react-statics "^3.3.0" - redux "^4.0.0" - "@types/react-router-dom@^5.3.3": version "5.3.3" resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.3.3.tgz#e9d6b4a66fcdbd651a5f106c2656a30088cc1e83" @@ -9439,14 +9429,13 @@ react-datepicker@^4.8.0: react-onclickoutside "^6.12.0" react-popper "^2.2.5" -react-dom@^17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" - integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== +react-dom@^18.0.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" + integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" - scheduler "^0.20.2" + scheduler "^0.23.0" react-error-boundary@^3.1.0: version "3.1.4" @@ -9548,7 +9537,7 @@ react-intl@^5.0.0: intl-messageformat "9.9.1" tslib "^2.1.0" -react-is@^16.13.1, react-is@^16.3.2, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: +react-is@^16.3.2, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -9615,17 +9604,17 @@ react-popper@^2.3.0: react-fast-compare "^3.0.1" warning "^4.0.2" -react-redux@^7.2.5: - version "7.2.5" - resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.5.tgz#213c1b05aa1187d9c940ddfc0b29450957f6a3b8" - integrity sha512-Dt29bNyBsbQaysp6s/dN0gUodcq+dVKKER8Qv82UrpeygwYeX1raTtil7O/fftw/rFqzaf6gJhDZRkkZnn6bjg== +react-redux@^8.0.0: + version "8.0.5" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.0.5.tgz#e5fb8331993a019b8aaf2e167a93d10af469c7bd" + integrity sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw== dependencies: "@babel/runtime" "^7.12.1" - "@types/react-redux" "^7.1.16" + "@types/hoist-non-react-statics" "^3.3.1" + "@types/use-sync-external-store" "^0.0.3" hoist-non-react-statics "^3.3.2" - loose-envify "^1.4.0" - prop-types "^15.7.2" - react-is "^16.13.1" + react-is "^18.0.0" + use-sync-external-store "^1.0.0" react-refresh@^0.14.0: version "0.14.0" @@ -9758,13 +9747,12 @@ react-virtuoso@^3.1.3: "@virtuoso.dev/react-urx" "^0.2.12" "@virtuoso.dev/urx" "^0.2.12" -react@^17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" - integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== +react@^18.0.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" + integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" reactcss@^1.2.0: version "1.2.3" @@ -9872,13 +9860,6 @@ redux-thunk@^2.4.1: resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.1.tgz#0dd8042cf47868f4b29699941de03c9301a75714" integrity sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q== -redux@^4.0.0, redux@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.1.tgz#76f1c439bb42043f985fbd9bf21990e60bd67f47" - integrity sha512-hZQZdDEM25UY2P493kPYuKqviVwZ58lEmGQNeQ+gXa+U0gYPUBf7NKYazbe3m+bs/DzM/ahN12DbF+NG8i0CWw== - dependencies: - "@babel/runtime" "^7.9.2" - redux@^4.0.5: version "4.1.2" resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.2.tgz#140f35426d99bb4729af760afcf79eaaac407104" @@ -9886,6 +9867,13 @@ redux@^4.0.5: dependencies: "@babel/runtime" "^7.9.2" +redux@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.1.tgz#76f1c439bb42043f985fbd9bf21990e60bd67f47" + integrity sha512-hZQZdDEM25UY2P493kPYuKqviVwZ58lEmGQNeQ+gXa+U0gYPUBf7NKYazbe3m+bs/DzM/ahN12DbF+NG8i0CWw== + dependencies: + "@babel/runtime" "^7.9.2" + redux@^4.1.2: version "4.2.0" resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.0.tgz#46f10d6e29b6666df758780437651eeb2b969f13" @@ -10195,13 +10183,12 @@ saxes@^6.0.0: dependencies: xmlchars "^2.2.0" -scheduler@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" - integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== +scheduler@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" + integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" schema-utils@*, schema-utils@^3.0, schema-utils@^3.0.0, schema-utils@^3.1.0, schema-utils@^3.1.1: version "3.1.1" @@ -11345,7 +11332,7 @@ use-latest@^1.2.1: dependencies: use-isomorphic-layout-effect "^1.1.1" -use-sync-external-store@^1.2.0: +use-sync-external-store@^1.0.0, use-sync-external-store@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==