From 7f8594d05ec3af19a1cb89330e8782a58ebcfeb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Wed, 22 Jun 2022 00:29:17 +0200 Subject: [PATCH 1/3] Reducers: TypeScript MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- app/soapbox/actions/__tests__/blocks.test.ts | 6 +- app/soapbox/actions/accounts.ts | 6 +- app/soapbox/actions/blocks.ts | 2 +- app/soapbox/actions/compose.ts | 2 +- app/soapbox/actions/conversations.ts | 2 +- app/soapbox/actions/directory.ts | 2 +- app/soapbox/actions/groups.ts | 4 +- app/soapbox/actions/mutes.ts | 2 +- app/soapbox/components/birthday-panel.tsx | 2 +- app/soapbox/components/sidebar-navigation.tsx | 3 +- app/soapbox/components/ui/modal/modal.tsx | 46 +++-- app/soapbox/features/blocks/index.tsx | 6 +- .../conversations/components/conversation.tsx | 10 +- .../components/conversations_list.tsx | 10 +- app/soapbox/features/directory/index.tsx | 7 +- .../features/follow_requests/index.tsx | 4 +- app/soapbox/features/followers/index.js | 4 +- app/soapbox/features/following/index.js | 4 +- app/soapbox/features/groups/members/index.js | 4 +- .../features/groups/removed_accounts/index.js | 4 +- app/soapbox/features/mutes/index.tsx | 6 +- .../ui/components/birthdays_modal.tsx | 2 +- .../ui/components/crypto_donate_modal.tsx | 12 +- .../components/familiar_followers_modal.tsx | 2 +- .../ui/components/favourites_modal.tsx | 4 +- .../ui/components/pinned_accounts_panel.tsx | 6 +- .../components/profile_familiar_followers.tsx | 2 +- .../ui/components/reactions_modal.tsx | 17 +- .../features/ui/components/reblogs_modal.tsx | 4 +- .../reducers/__tests__/conversations.test.ts | 16 +- .../reducers/__tests__/user_lists.test.ts | 33 +-- .../{conversations.js => conversations.ts} | 39 ++-- .../reducers/{settings.js => settings.ts} | 30 +-- app/soapbox/reducers/user_lists.js | 150 -------------- app/soapbox/reducers/user_lists.ts | 193 ++++++++++++++++++ app/styles/components/crypto-donate.scss | 9 +- 36 files changed, 356 insertions(+), 299 deletions(-) rename app/soapbox/reducers/{conversations.js => conversations.ts} (70%) rename app/soapbox/reducers/{settings.js => settings.ts} (62%) delete mode 100644 app/soapbox/reducers/user_lists.js create mode 100644 app/soapbox/reducers/user_lists.ts diff --git a/app/soapbox/actions/__tests__/blocks.test.ts b/app/soapbox/actions/__tests__/blocks.test.ts index eedf17c30..2d4832007 100644 --- a/app/soapbox/actions/__tests__/blocks.test.ts +++ b/app/soapbox/actions/__tests__/blocks.test.ts @@ -1,4 +1,4 @@ -import { Map as ImmutableMap } from 'immutable'; +import { Record as ImmutableRecord } from 'immutable'; import { __stub } from 'soapbox/api'; import { mockStore } from 'soapbox/jest/test-helpers'; @@ -113,7 +113,7 @@ describe('expandBlocks()', () => { beforeEach(() => { const state = rootReducer(undefined, {}) .set('me', '1234') - .set('user_lists', ImmutableMap({ blocks: { next: null } })); + .set('user_lists', ImmutableRecord({ blocks: { next: null } })()); store = mockStore(state); }); @@ -129,7 +129,7 @@ describe('expandBlocks()', () => { beforeEach(() => { const state = rootReducer(undefined, {}) .set('me', '1234') - .set('user_lists', ImmutableMap({ blocks: { next: 'example' } })); + .set('user_lists', ImmutableRecord({ blocks: { next: 'example' } })()); store = mockStore(state); }); diff --git a/app/soapbox/actions/accounts.ts b/app/soapbox/actions/accounts.ts index e5a92fae9..e3767c7b8 100644 --- a/app/soapbox/actions/accounts.ts +++ b/app/soapbox/actions/accounts.ts @@ -532,7 +532,7 @@ const expandFollowers = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => { if (!isLoggedIn(getState)) return; - const url = getState().user_lists.getIn(['followers', id, 'next']); + const url = getState().user_lists.followers.get(id)?.next as string; if (url === null) { return; @@ -606,7 +606,7 @@ const expandFollowing = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => { if (!isLoggedIn(getState)) return; - const url = getState().user_lists.getIn(['following', id, 'next']); + const url = getState().user_lists.following.get(id)!.next; if (url === null) { return; @@ -713,7 +713,7 @@ const expandFollowRequests = () => (dispatch: AppDispatch, getState: () => RootState) => { if (!isLoggedIn(getState)) return; - const url = getState().user_lists.getIn(['follow_requests', 'next']); + const url = getState().user_lists.follow_requests.next; if (url === null) { return; diff --git a/app/soapbox/actions/blocks.ts b/app/soapbox/actions/blocks.ts index 629be6f90..d3f625884 100644 --- a/app/soapbox/actions/blocks.ts +++ b/app/soapbox/actions/blocks.ts @@ -58,7 +58,7 @@ const expandBlocks = () => (dispatch: React.Dispatch, getState: () => if (!isLoggedIn(getState)) return null; const nextLinkName = getNextLinkName(getState); - const url = getState().user_lists.getIn(['blocks', 'next']); + const url = getState().user_lists.blocks.next; if (url === null) { return null; diff --git a/app/soapbox/actions/compose.ts b/app/soapbox/actions/compose.ts index a6b6dcd13..f2440af8c 100644 --- a/app/soapbox/actions/compose.ts +++ b/app/soapbox/actions/compose.ts @@ -284,7 +284,7 @@ const submitCompose = (routerHistory: History, force = false) => }; dispatch(createStatus(params, idempotencyKey, statusId)).then(function(data) { - if (!statusId && data.visibility === 'direct' && getState().conversations.get('mounted') <= 0 && routerHistory) { + if (!statusId && data.visibility === 'direct' && getState().conversations.mounted <= 0 && routerHistory) { routerHistory.push('/messages'); } handleComposeSubmit(dispatch, getState, data, status); diff --git a/app/soapbox/actions/conversations.ts b/app/soapbox/actions/conversations.ts index a56e02d6f..73507e389 100644 --- a/app/soapbox/actions/conversations.ts +++ b/app/soapbox/actions/conversations.ts @@ -49,7 +49,7 @@ const expandConversations = ({ maxId }: Record = {}) => (dispatch: const params: Record = { max_id: maxId }; if (!maxId) { - params.since_id = getState().conversations.getIn(['items', 0, 'id']); + params.since_id = getState().conversations.items.getIn([0, 'id']); } const isLoadingRecent = !!params.since_id; diff --git a/app/soapbox/actions/directory.ts b/app/soapbox/actions/directory.ts index 98e2dc2b2..37ddcfdfa 100644 --- a/app/soapbox/actions/directory.ts +++ b/app/soapbox/actions/directory.ts @@ -44,7 +44,7 @@ const expandDirectory = (params: Record) => (dispatch: AppDispatch, getState: () => RootState) => { dispatch(expandDirectoryRequest()); - const loadedItems = getState().user_lists.getIn(['directory', 'items']).size; + const loadedItems = getState().user_lists.directory.items.size; api(getState).get('/api/v1/directory', { params: { ...params, offset: loadedItems, limit: 20 } }).then(({ data }) => { dispatch(importFetchedAccounts(data)); diff --git a/app/soapbox/actions/groups.ts b/app/soapbox/actions/groups.ts index a6443758a..808cc3204 100644 --- a/app/soapbox/actions/groups.ts +++ b/app/soapbox/actions/groups.ts @@ -253,7 +253,7 @@ const expandMembers = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => { if (!isLoggedIn(getState)) return; - const url = getState().user_lists.getIn(['groups', id, 'next']); + const url = getState().user_lists.groups.get(id)!.next; if (url === null) { return; @@ -329,7 +329,7 @@ const expandRemovedAccounts = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => { if (!isLoggedIn(getState)) return; - const url = getState().user_lists.getIn(['groups_removed_accounts', id, 'next']); + const url = getState().user_lists.groups_removed_accounts.get(id)!.next; if (url === null) { return; diff --git a/app/soapbox/actions/mutes.ts b/app/soapbox/actions/mutes.ts index 0ec7d147b..050a513f0 100644 --- a/app/soapbox/actions/mutes.ts +++ b/app/soapbox/actions/mutes.ts @@ -57,7 +57,7 @@ const expandMutes = () => if (!isLoggedIn(getState)) return; const nextLinkName = getNextLinkName(getState); - const url = getState().user_lists.getIn(['mutes', 'next']); + const url = getState().user_lists.mutes.next; if (url === null) { return; diff --git a/app/soapbox/components/birthday-panel.tsx b/app/soapbox/components/birthday-panel.tsx index 6008cb423..2cc139357 100644 --- a/app/soapbox/components/birthday-panel.tsx +++ b/app/soapbox/components/birthday-panel.tsx @@ -15,7 +15,7 @@ interface IBirthdayPanel { const BirthdayPanel = ({ limit }: IBirthdayPanel) => { const dispatch = useDispatch(); - const birthdays: ImmutableOrderedSet = useAppSelector(state => state.user_lists.getIn(['birthday_reminders', state.me], ImmutableOrderedSet())); + const birthdays: ImmutableOrderedSet = useAppSelector(state => state.user_lists.birthday_reminders.get(state.me as string)?.items || ImmutableOrderedSet()); const birthdaysToRender = birthdays.slice(0, limit); React.useEffect(() => { diff --git a/app/soapbox/components/sidebar-navigation.tsx b/app/soapbox/components/sidebar-navigation.tsx index 5b8c79230..f4cf64e49 100644 --- a/app/soapbox/components/sidebar-navigation.tsx +++ b/app/soapbox/components/sidebar-navigation.tsx @@ -1,4 +1,3 @@ -import { OrderedSet as ImmutableOrderedSet } from 'immutable'; import React from 'react'; import { FormattedMessage } from 'react-intl'; @@ -19,7 +18,7 @@ const SidebarNavigation = () => { const account = useOwnAccount(); const notificationCount = useAppSelector((state) => state.notifications.get('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.getIn(['follow_requests', 'items'], ImmutableOrderedSet()).count()); + 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); diff --git a/app/soapbox/components/ui/modal/modal.tsx b/app/soapbox/components/ui/modal/modal.tsx index 3b2ba89e9..b019d01e3 100644 --- a/app/soapbox/components/ui/modal/modal.tsx +++ b/app/soapbox/components/ui/modal/modal.tsx @@ -50,7 +50,7 @@ interface IModal { /** Don't focus the "confirm" button on mount. */ skipFocus?: boolean, /** Title text for the modal. */ - title: React.ReactNode, + title?: React.ReactNode, width?: Widths, } @@ -86,28 +86,32 @@ const Modal: React.FC = ({
-
-

- {title} -

+ {title && ( +
+

+ {title} +

- {onClose && ( - - )} -
+ {onClose && ( + + )} +
+ )} -
- {children} -
+ {title ? ( +
+ {children} +
+ ) : children}
diff --git a/app/soapbox/features/blocks/index.tsx b/app/soapbox/features/blocks/index.tsx index c3ea6585f..842950e49 100644 --- a/app/soapbox/features/blocks/index.tsx +++ b/app/soapbox/features/blocks/index.tsx @@ -21,8 +21,8 @@ const Blocks: React.FC = () => { const dispatch = useDispatch(); const intl = useIntl(); - const accountIds = useAppSelector((state) => state.user_lists.getIn(['blocks', 'items'])); - const hasMore = useAppSelector((state) => !!state.user_lists.getIn(['blocks', 'next'])); + const accountIds = useAppSelector((state) => state.user_lists.blocks.items); + const hasMore = useAppSelector((state) => !!state.user_lists.blocks.next); React.useEffect(() => { dispatch(fetchBlocks()); @@ -47,7 +47,7 @@ const Blocks: React.FC = () => { emptyMessage={emptyMessage} itemClassName='pb-4' > - {accountIds.map((id: string) => + {accountIds.map((id) => , )} diff --git a/app/soapbox/features/conversations/components/conversation.tsx b/app/soapbox/features/conversations/components/conversation.tsx index 6f638f2d6..e48c0e17f 100644 --- a/app/soapbox/features/conversations/components/conversation.tsx +++ b/app/soapbox/features/conversations/components/conversation.tsx @@ -5,8 +5,6 @@ import { markConversationRead } from 'soapbox/actions/conversations'; import StatusContainer from 'soapbox/containers/status_container'; import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; -import type { Map as ImmutableMap } from 'immutable'; - interface IConversation { conversationId: string, onMoveUp: (id: string) => void, @@ -18,12 +16,12 @@ const Conversation: React.FC = ({ conversationId, onMoveUp, onMov const history = useHistory(); const { accounts, unread, lastStatusId } = useAppSelector((state) => { - const conversation = state.conversations.get('items').find((x: ImmutableMap) => x.get('id') === conversationId); + const conversation = state.conversations.items.find(x => x.id === conversationId)!; return { - accounts: conversation.get('accounts').map((accountId: string) => state.accounts.get(accountId, null)), - unread: conversation.get('unread'), - lastStatusId: conversation.get('last_status', null), + accounts: conversation.accounts.map((accountId: string) => state.accounts.get(accountId, null)!), + unread: conversation.unread, + lastStatusId: conversation.last_status || null, }; }); diff --git a/app/soapbox/features/conversations/components/conversations_list.tsx b/app/soapbox/features/conversations/components/conversations_list.tsx index 9773b246e..01705fd30 100644 --- a/app/soapbox/features/conversations/components/conversations_list.tsx +++ b/app/soapbox/features/conversations/components/conversations_list.tsx @@ -14,10 +14,10 @@ const ConversationsList: React.FC = () => { const dispatch = useAppDispatch(); const ref = useRef(null); - const conversations = useAppSelector((state) => state.conversations.get('items')); - const isLoading = useAppSelector((state) => state.conversations.get('isLoading', true)); + const conversations = useAppSelector((state) => state.conversations.items); + const isLoading = useAppSelector((state) => state.conversations.isLoading); - const getCurrentIndex = (id: string) => conversations.findIndex((x: any) => x.get('id') === id); + const getCurrentIndex = (id: string) => conversations.findIndex(x => x.id === id); const handleMoveUp = (id: string) => { const elementIndex = getCurrentIndex(id) - 1; @@ -60,8 +60,8 @@ const ConversationsList: React.FC = () => { > {conversations.map((item: any) => ( diff --git a/app/soapbox/features/directory/index.tsx b/app/soapbox/features/directory/index.tsx index c4bb5b4d8..375e99465 100644 --- a/app/soapbox/features/directory/index.tsx +++ b/app/soapbox/features/directory/index.tsx @@ -1,5 +1,4 @@ import classNames from 'classnames'; -import { List as ImmutableList } from 'immutable'; import React, { useEffect, useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import { useDispatch } from 'react-redux'; @@ -28,8 +27,8 @@ const Directory = () => { const { search } = useLocation(); const params = new URLSearchParams(search); - const accountIds = useAppSelector((state) => state.user_lists.getIn(['directory', 'items'], ImmutableList())); - const isLoading = useAppSelector((state) => state.user_lists.getIn(['directory', 'isLoading'], true)); + 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)); @@ -69,7 +68,7 @@ const Directory = () => {
- {accountIds.map((accountId: string) => )} + {accountIds.map((accountId) => )}
diff --git a/app/soapbox/features/follow_requests/index.tsx b/app/soapbox/features/follow_requests/index.tsx index 3c1d647ed..eed1a8e1f 100644 --- a/app/soapbox/features/follow_requests/index.tsx +++ b/app/soapbox/features/follow_requests/index.tsx @@ -24,8 +24,8 @@ const FollowRequests: React.FC = () => { const dispatch = useDispatch(); const intl = useIntl(); - const accountIds = useAppSelector((state) => state.user_lists.getIn(['follow_requests', 'items'])); - const hasMore = useAppSelector((state) => !!state.user_lists.getIn(['follow_requests', 'next'])); + const accountIds = useAppSelector((state) => state.user_lists.follow_requests.items); + const hasMore = useAppSelector((state) => !!state.user_lists.follow_requests.next); React.useEffect(() => { dispatch(fetchFollowRequests()); diff --git a/app/soapbox/features/followers/index.js b/app/soapbox/features/followers/index.js index e728ebddf..7a77e46a5 100644 --- a/app/soapbox/features/followers/index.js +++ b/app/soapbox/features/followers/index.js @@ -48,8 +48,8 @@ const mapStateToProps = (state, { params, withReplies = false }) => { accountId, unavailable, isAccount: !!state.getIn(['accounts', accountId]), - accountIds: state.getIn(['user_lists', 'followers', accountId, 'items']), - hasMore: !!state.getIn(['user_lists', 'followers', accountId, 'next']), + accountIds: state.user_lists.followers.get(accountId)?.items, + hasMore: !!state.user_lists.followers.get(accountId)?.next, diffCount, }; }; diff --git a/app/soapbox/features/following/index.js b/app/soapbox/features/following/index.js index 532a7d2c5..2f654fc5b 100644 --- a/app/soapbox/features/following/index.js +++ b/app/soapbox/features/following/index.js @@ -48,8 +48,8 @@ const mapStateToProps = (state, { params, withReplies = false }) => { accountId, unavailable, isAccount: !!state.getIn(['accounts', accountId]), - accountIds: state.getIn(['user_lists', 'following', accountId, 'items']), - hasMore: !!state.getIn(['user_lists', 'following', accountId, 'next']), + accountIds: state.user_lists.following.get(accountId)?.items, + hasMore: !!state.user_lists.following.get(accountId)?.next, diffCount, }; }; diff --git a/app/soapbox/features/groups/members/index.js b/app/soapbox/features/groups/members/index.js index a7b56aeb1..1e9e39a4d 100644 --- a/app/soapbox/features/groups/members/index.js +++ b/app/soapbox/features/groups/members/index.js @@ -18,8 +18,8 @@ import Column from '../../ui/components/column'; const mapStateToProps = (state, { params: { id } }) => ({ group: state.getIn(['groups', id]), - accountIds: state.getIn(['user_lists', 'groups', id, 'items']), - hasMore: !!state.getIn(['user_lists', 'groups', id, 'next']), + accountIds: state.user_lists.groups.get(id)?.items, + hasMore: !!state.user_lists.groups.get(id)?.next, }); export default @connect(mapStateToProps) diff --git a/app/soapbox/features/groups/removed_accounts/index.js b/app/soapbox/features/groups/removed_accounts/index.js index cc7e1111d..e5d0dae41 100644 --- a/app/soapbox/features/groups/removed_accounts/index.js +++ b/app/soapbox/features/groups/removed_accounts/index.js @@ -23,8 +23,8 @@ const messages = defineMessages({ const mapStateToProps = (state, { params: { id } }) => ({ group: state.getIn(['groups', id]), - accountIds: state.getIn(['user_lists', 'groups_removed_accounts', id, 'items']), - hasMore: !!state.getIn(['user_lists', 'groups_removed_accounts', id, 'next']), + accountIds: state.user_lists.groups_removed_accounts.get(id)?.items, + hasMore: !!state.user_lists.groups_removed_accounts.get(id)?.next, }); export default @connect(mapStateToProps) diff --git a/app/soapbox/features/mutes/index.tsx b/app/soapbox/features/mutes/index.tsx index d564c68e7..caf15ca26 100644 --- a/app/soapbox/features/mutes/index.tsx +++ b/app/soapbox/features/mutes/index.tsx @@ -21,8 +21,8 @@ const Mutes: React.FC = () => { const dispatch = useDispatch(); const intl = useIntl(); - const accountIds = useAppSelector((state) => state.user_lists.getIn(['mutes', 'items'])); - const hasMore = useAppSelector((state) => !!state.user_lists.getIn(['mutes', 'next'])); + const accountIds = useAppSelector((state) => state.user_lists.mutes.items); + const hasMore = useAppSelector((state) => !!state.user_lists.mutes.next); React.useEffect(() => { dispatch(fetchMutes()); @@ -47,7 +47,7 @@ const Mutes: React.FC = () => { emptyMessage={emptyMessage} itemClassName='pb-4' > - {accountIds.map((id: string) => + {accountIds.map((id) => , )} diff --git a/app/soapbox/features/ui/components/birthdays_modal.tsx b/app/soapbox/features/ui/components/birthdays_modal.tsx index f0b25f5f4..a74072a4b 100644 --- a/app/soapbox/features/ui/components/birthdays_modal.tsx +++ b/app/soapbox/features/ui/components/birthdays_modal.tsx @@ -11,7 +11,7 @@ interface IBirthdaysModal { } const BirthdaysModal = ({ onClose }: IBirthdaysModal) => { - const accountIds = useAppSelector(state => state.user_lists.getIn(['birthday_reminders', state.me])); + const accountIds = useAppSelector(state => state.user_lists.birthday_reminders.get(state.me as string)?.items); const onClickClose = () => { onClose('BIRTHDAYS'); diff --git a/app/soapbox/features/ui/components/crypto_donate_modal.tsx b/app/soapbox/features/ui/components/crypto_donate_modal.tsx index 805012d3a..29c2dfaa8 100644 --- a/app/soapbox/features/ui/components/crypto_donate_modal.tsx +++ b/app/soapbox/features/ui/components/crypto_donate_modal.tsx @@ -1,14 +1,18 @@ import React from 'react'; +import { Modal } from 'soapbox/components/ui'; import DetailedCryptoAddress from 'soapbox/features/crypto_donate/components/detailed_crypto_address'; import type { ICryptoAddress } from '../../crypto_donate/components/crypto_address'; -const CryptoDonateModal: React.FC = (props) => { +const CryptoDonateModal: React.FC void }> = ({ onClose, ...props }) => { + return ( -
- -
+ +
+ +
+
); }; diff --git a/app/soapbox/features/ui/components/familiar_followers_modal.tsx b/app/soapbox/features/ui/components/familiar_followers_modal.tsx index 0ec081d58..6692135d0 100644 --- a/app/soapbox/features/ui/components/familiar_followers_modal.tsx +++ b/app/soapbox/features/ui/components/familiar_followers_modal.tsx @@ -17,7 +17,7 @@ interface IFamiliarFollowersModal { const FamiliarFollowersModal = ({ accountId, onClose }: IFamiliarFollowersModal) => { const account = useAppSelector(state => getAccount(state, accountId)); - const familiarFollowerIds: ImmutableOrderedSet = useAppSelector(state => state.user_lists.getIn(['familiar_followers', accountId])); + const familiarFollowerIds: ImmutableOrderedSet = useAppSelector(state => state.user_lists.familiar_followers.get(accountId)?.items || ImmutableOrderedSet()); const onClickClose = () => { onClose('FAMILIAR_FOLLOWERS'); diff --git a/app/soapbox/features/ui/components/favourites_modal.tsx b/app/soapbox/features/ui/components/favourites_modal.tsx index f6089acf6..60bc9123f 100644 --- a/app/soapbox/features/ui/components/favourites_modal.tsx +++ b/app/soapbox/features/ui/components/favourites_modal.tsx @@ -15,7 +15,7 @@ interface IFavouritesModal { const FavouritesModal: React.FC = ({ onClose, statusId }) => { const dispatch = useAppDispatch(); - const accountIds = useAppSelector((state) => state.user_lists.getIn(['favourited_by', statusId])); + const accountIds = useAppSelector((state) => state.user_lists.favourited_by.get(statusId)?.items); const fetchData = () => { dispatch(fetchFavourites(statusId)); @@ -42,7 +42,7 @@ const FavouritesModal: React.FC = ({ onClose, statusId }) => { emptyMessage={emptyMessage} itemClassName='pb-3' > - {accountIds.map((id: string) => + {accountIds.map((id) => , )} diff --git a/app/soapbox/features/ui/components/pinned_accounts_panel.tsx b/app/soapbox/features/ui/components/pinned_accounts_panel.tsx index d364d61e8..1f5d3ebb6 100644 --- a/app/soapbox/features/ui/components/pinned_accounts_panel.tsx +++ b/app/soapbox/features/ui/components/pinned_accounts_panel.tsx @@ -1,4 +1,4 @@ -import { List as ImmutableList } from 'immutable'; +import { OrderedSet as ImmutableOrderedSet } from 'immutable'; import React, { useEffect } from 'react'; import { FormattedMessage } from 'react-intl'; @@ -16,7 +16,7 @@ interface IPinnedAccountsPanel { const PinnedAccountsPanel: React.FC = ({ account, limit }) => { const dispatch = useAppDispatch(); - const pinned = useAppSelector((state) => state.user_lists.getIn(['pinned', account.id, 'items'], ImmutableList())).slice(0, limit); + const pinned = useAppSelector((state) => state.user_lists.pinned.get(account.id)?.items || ImmutableOrderedSet()).slice(0, limit); useEffect(() => { dispatch(fetchPinnedAccounts(account.id)); @@ -36,7 +36,7 @@ const PinnedAccountsPanel: React.FC = ({ account, limit }) }} />} > - {pinned && pinned.map((suggestion: string) => ( + {pinned && pinned.map((suggestion) => ( = ({ account const dispatch = useDispatch(); const me = useAppSelector((state) => state.me); const features = useFeatures(); - const familiarFollowerIds: ImmutableOrderedSet = useAppSelector(state => state.user_lists.getIn(['familiar_followers', account.id], ImmutableOrderedSet())); + const familiarFollowerIds = useAppSelector(state => state.user_lists.familiar_followers.get(account.id)?.items || ImmutableOrderedSet()); const familiarFollowers: ImmutableOrderedSet = useAppSelector(state => familiarFollowerIds.slice(0, 2).map(accountId => getAccount(state, accountId))); useEffect(() => { diff --git a/app/soapbox/features/ui/components/reactions_modal.tsx b/app/soapbox/features/ui/components/reactions_modal.tsx index f343dc5c4..88065b92e 100644 --- a/app/soapbox/features/ui/components/reactions_modal.tsx +++ b/app/soapbox/features/ui/components/reactions_modal.tsx @@ -7,6 +7,7 @@ import ScrollableList from 'soapbox/components/scrollable_list'; import { Emoji, Modal, Spinner, Tabs } from 'soapbox/components/ui'; import AccountContainer from 'soapbox/containers/account_container'; import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; +import { ReactionRecord } from 'soapbox/reducers/user_lists'; import type { Item } from 'soapbox/components/ui/tabs/tabs'; @@ -25,14 +26,10 @@ const ReactionsModal: React.FC = ({ onClose, statusId, reaction const dispatch = useAppDispatch(); const intl = useIntl(); const [reaction, setReaction] = useState(initialReaction); - const reactions = useAppSelector, - count: number, - name: string, - }>>((state) => { - const favourites = state.user_lists.getIn(['favourited_by', statusId]); - const reactions = state.user_lists.getIn(['reactions', statusId]); - return favourites && reactions && ImmutableList(favourites.size ? [{ accounts: favourites, count: favourites.size, name: 'đź‘Ť' }] : []).concat(reactions || []); + const reactions = useAppSelector> | undefined>((state) => { + const favourites = state.user_lists.favourited_by.get(statusId)?.items; + const reactions = state.user_lists.reactions.get(statusId)?.items; + return favourites && reactions && ImmutableList(favourites?.size ? [ReactionRecord({ accounts: favourites, count: favourites.size, name: 'đź‘Ť' })] : []).concat(reactions || []); }); const fetchData = () => { @@ -53,7 +50,7 @@ const ReactionsModal: React.FC = ({ onClose, statusId, reaction }, ]; - reactions.forEach(reaction => items.push( + reactions!.forEach(reaction => items.push( { text:
@@ -73,7 +70,7 @@ const ReactionsModal: React.FC = ({ onClose, statusId, reaction const accounts = reactions && (reaction ? reactions.find(({ name }) => name === reaction)?.accounts.map(account => ({ id: account, reaction: reaction })) - : reactions.map(({ accounts, name }) => accounts.map(account => ({ id: account, reaction: name }))).flatten()) as Array<{ id: string, reaction: string }>; + : reactions.map(({ accounts, name }) => accounts.map(account => ({ id: account, reaction: name }))).flatten()) as ImmutableList<{ id: string, reaction: string }>; let body; diff --git a/app/soapbox/features/ui/components/reblogs_modal.tsx b/app/soapbox/features/ui/components/reblogs_modal.tsx index cb9906bad..23ed5745a 100644 --- a/app/soapbox/features/ui/components/reblogs_modal.tsx +++ b/app/soapbox/features/ui/components/reblogs_modal.tsx @@ -15,7 +15,7 @@ interface IReblogsModal { const ReblogsModal: React.FC = ({ onClose, statusId }) => { const dispatch = useAppDispatch(); - const accountIds = useAppSelector((state) => state.user_lists.getIn(['reblogged_by', statusId])); + const accountIds = useAppSelector((state) => state.user_lists.reblogged_by.get(statusId)?.items); const fetchData = () => { dispatch(fetchReblogs(statusId)); @@ -43,7 +43,7 @@ const ReblogsModal: React.FC = ({ onClose, statusId }) => { emptyMessage={emptyMessage} itemClassName='pb-3' > - {accountIds.map((id: string) => + {accountIds.map((id) => , )} diff --git a/app/soapbox/reducers/__tests__/conversations.test.ts b/app/soapbox/reducers/__tests__/conversations.test.ts index 496ea620d..b92c6b931 100644 --- a/app/soapbox/reducers/__tests__/conversations.test.ts +++ b/app/soapbox/reducers/__tests__/conversations.test.ts @@ -1,4 +1,4 @@ -import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; +import { List as ImmutableList, Record as ImmutableRecord } from 'immutable'; import * as actions from 'soapbox/actions/conversations'; @@ -6,30 +6,30 @@ import reducer from '../conversations'; describe('conversations reducer', () => { it('should return the initial state', () => { - expect(reducer(undefined, {})).toEqual(ImmutableMap({ + expect(reducer(undefined, {} as any)).toMatchObject({ items: ImmutableList(), isLoading: false, hasMore: true, - mounted: false, - })); + mounted: 0, + }); }); it('should handle CONVERSATIONS_FETCH_REQUEST', () => { - const state = ImmutableMap({ isLoading: false }); + const state = ImmutableRecord({ isLoading: false })(); const action = { type: actions.CONVERSATIONS_FETCH_REQUEST, }; - expect(reducer(state, action).toJS()).toMatchObject({ + expect(reducer(state as any, action).toJS()).toMatchObject({ isLoading: true, }); }); it('should handle CONVERSATIONS_FETCH_FAIL', () => { - const state = ImmutableMap({ isLoading: true }); + const state = ImmutableRecord({ isLoading: true })(); const action = { type: actions.CONVERSATIONS_FETCH_FAIL, }; - expect(reducer(state, action).toJS()).toMatchObject({ + expect(reducer(state as any, action).toJS()).toMatchObject({ isLoading: false, }); }); diff --git a/app/soapbox/reducers/__tests__/user_lists.test.ts b/app/soapbox/reducers/__tests__/user_lists.test.ts index 7c5ec7e20..5fbd5f1ea 100644 --- a/app/soapbox/reducers/__tests__/user_lists.test.ts +++ b/app/soapbox/reducers/__tests__/user_lists.test.ts @@ -1,23 +1,24 @@ -import { Map as ImmutableMap } from 'immutable'; +import { OrderedSet as ImmutableOrderedSet } from 'immutable'; import reducer from '../user_lists'; describe('user_lists reducer', () => { it('should return the initial state', () => { - expect(reducer(undefined, {})).toEqual(ImmutableMap({ - followers: ImmutableMap(), - following: ImmutableMap(), - reblogged_by: ImmutableMap(), - favourited_by: ImmutableMap(), - follow_requests: ImmutableMap(), - blocks: ImmutableMap(), - reactions: ImmutableMap(), - mutes: ImmutableMap(), - groups: ImmutableMap(), - groups_removed_accounts: ImmutableMap(), - pinned: ImmutableMap(), - birthday_reminders: ImmutableMap(), - familiar_followers: ImmutableMap(), - })); + expect(reducer(undefined, {} as any)).toMatchObject({ + followers: {}, + following: {}, + reblogged_by: {}, + favourited_by: {}, + reactions: {}, + follow_requests: { next: null, items: ImmutableOrderedSet(), isLoading: false }, + blocks: { next: null, items: ImmutableOrderedSet(), isLoading: false }, + mutes: { next: null, items: ImmutableOrderedSet(), isLoading: false }, + directory: { next: null, items: ImmutableOrderedSet(), isLoading: true }, + groups: {}, + groups_removed_accounts: {}, + pinned: {}, + birthday_reminders: {}, + familiar_followers: {}, + }); }); }); diff --git a/app/soapbox/reducers/conversations.js b/app/soapbox/reducers/conversations.ts similarity index 70% rename from app/soapbox/reducers/conversations.js rename to app/soapbox/reducers/conversations.ts index b001746d7..725f6c3ad 100644 --- a/app/soapbox/reducers/conversations.js +++ b/app/soapbox/reducers/conversations.ts @@ -1,4 +1,4 @@ -import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; +import { List as ImmutableList, Record as ImmutableRecord } from 'immutable'; import { CONVERSATIONS_MOUNT, @@ -11,21 +11,36 @@ import { } from '../actions/conversations'; import compareId from '../compare_id'; -const initialState = ImmutableMap({ - items: ImmutableList(), - isLoading: false, - hasMore: true, - mounted: false, +import type { AnyAction } from 'redux'; +import type { APIEntity } from 'soapbox/types/entities'; + + +const ConversationRecord = ImmutableRecord({ + id: '', + unread: false, + accounts: ImmutableList(), + last_status: null as string | null, + }); -const conversationToMap = item => ImmutableMap({ +const ReducerRecord = ImmutableRecord({ + items: ImmutableList(), + isLoading: false, + hasMore: true, + mounted: 0, +}); + +type State = ReturnType; +type Conversation = ReturnType; + +const conversationToMap = (item: APIEntity) => ConversationRecord({ id: item.id, unread: item.unread, - accounts: ImmutableList(item.accounts.map(a => a.id)), + accounts: ImmutableList(item.accounts.map((a: APIEntity) => a.id)), last_status: item.last_status ? item.last_status.id : null, }); -const updateConversation = (state, item) => state.update('items', list => { +const updateConversation = (state: State, item: APIEntity) => state.update('items', list => { const index = list.findIndex(x => x.get('id') === item.id); const newItem = conversationToMap(item); @@ -36,7 +51,7 @@ const updateConversation = (state, item) => state.update('items', list => { } }); -const expandNormalizedConversations = (state, conversations, next, isLoadingRecent) => { +const expandNormalizedConversations = (state: State, conversations: APIEntity[], next: string | null, isLoadingRecent?: boolean) => { let items = ImmutableList(conversations.map(conversationToMap)); return state.withMutations(mutable => { @@ -52,7 +67,7 @@ const expandNormalizedConversations = (state, conversations, next, isLoadingRece const newItem = items.get(newItemIndex); items = items.delete(newItemIndex); - return newItem; + return newItem!; }); list = list.concat(items); @@ -75,7 +90,7 @@ const expandNormalizedConversations = (state, conversations, next, isLoadingRece }); }; -export default function conversations(state = initialState, action) { +export default function conversations(state = ReducerRecord(), action: AnyAction) { switch (action.type) { case CONVERSATIONS_FETCH_REQUEST: return state.set('isLoading', true); diff --git a/app/soapbox/reducers/settings.js b/app/soapbox/reducers/settings.ts similarity index 62% rename from app/soapbox/reducers/settings.js rename to app/soapbox/reducers/settings.ts index b0329d00d..ee8ddc558 100644 --- a/app/soapbox/reducers/settings.js +++ b/app/soapbox/reducers/settings.ts @@ -1,4 +1,5 @@ import { Map as ImmutableMap, fromJS } from 'immutable'; +import { AnyAction } from 'redux'; import { ME_FETCH_SUCCESS } from 'soapbox/actions/me'; @@ -12,23 +13,24 @@ import { FE_NAME, } from '../actions/settings'; +import type { Emoji } from 'soapbox/components/autosuggest_emoji'; +import type { APIEntity } from 'soapbox/types/entities'; + +type State = ImmutableMap; + +const updateFrequentEmojis = (state: State, emoji: Emoji) => state.update('frequentlyUsedEmojis', ImmutableMap(), map => map.update(emoji.id, 0, (count: number) => count + 1)).set('saved', false); + +const importSettings = (state: State, account: APIEntity) => { + account = fromJS(account); + const prefs = account.getIn(['pleroma', 'settings_store', FE_NAME], ImmutableMap()); + return state.merge(prefs) as State; +}; + // Default settings are in action/settings.js // // Settings should be accessed with `getSettings(getState()).getIn(...)` // instead of directly from the state. -const initialState = ImmutableMap({ - saved: true, -}); - -const updateFrequentEmojis = (state, emoji) => state.update('frequentlyUsedEmojis', ImmutableMap(), map => map.update(emoji.id, 0, count => count + 1)).set('saved', false); - -const importSettings = (state, account) => { - account = fromJS(account); - const prefs = account.getIn(['pleroma', 'settings_store', FE_NAME], ImmutableMap()); - return state.merge(prefs); -}; - -export default function settings(state = initialState, action) { +export default function settings(state: State = ImmutableMap({ saved: true }), action: AnyAction): State { switch (action.type) { case ME_FETCH_SUCCESS: return importSettings(state, action.me); @@ -43,7 +45,7 @@ export default function settings(state = initialState, action) { case SETTING_SAVE: return state.set('saved', true); case SETTINGS_UPDATE: - return fromJS(action.settings); + return ImmutableMap(fromJS(action.settings)); default: return state; } diff --git a/app/soapbox/reducers/user_lists.js b/app/soapbox/reducers/user_lists.js deleted file mode 100644 index 69e8c181c..000000000 --- a/app/soapbox/reducers/user_lists.js +++ /dev/null @@ -1,150 +0,0 @@ -import { - Map as ImmutableMap, - OrderedSet as ImmutableOrderedSet, -} from 'immutable'; - -import { - FOLLOWERS_FETCH_SUCCESS, - FOLLOWERS_EXPAND_SUCCESS, - FOLLOWING_FETCH_SUCCESS, - FOLLOWING_EXPAND_SUCCESS, - FOLLOW_REQUESTS_FETCH_SUCCESS, - FOLLOW_REQUESTS_EXPAND_SUCCESS, - FOLLOW_REQUEST_AUTHORIZE_SUCCESS, - FOLLOW_REQUEST_REJECT_SUCCESS, - PINNED_ACCOUNTS_FETCH_SUCCESS, - BIRTHDAY_REMINDERS_FETCH_SUCCESS, -} from '../actions/accounts'; -import { - BLOCKS_FETCH_SUCCESS, - BLOCKS_EXPAND_SUCCESS, -} from '../actions/blocks'; -import { - DIRECTORY_FETCH_REQUEST, - DIRECTORY_FETCH_SUCCESS, - DIRECTORY_FETCH_FAIL, - DIRECTORY_EXPAND_REQUEST, - DIRECTORY_EXPAND_SUCCESS, - DIRECTORY_EXPAND_FAIL, -} from '../actions/directory'; -import { - FAMILIAR_FOLLOWERS_FETCH_SUCCESS, -} from '../actions/familiar_followers'; -import { - GROUP_MEMBERS_FETCH_SUCCESS, - GROUP_MEMBERS_EXPAND_SUCCESS, - GROUP_REMOVED_ACCOUNTS_FETCH_SUCCESS, - GROUP_REMOVED_ACCOUNTS_EXPAND_SUCCESS, - GROUP_REMOVED_ACCOUNTS_REMOVE_SUCCESS, -} from '../actions/groups'; -import { - REBLOGS_FETCH_SUCCESS, - FAVOURITES_FETCH_SUCCESS, - REACTIONS_FETCH_SUCCESS, -} from '../actions/interactions'; -import { - MUTES_FETCH_SUCCESS, - MUTES_EXPAND_SUCCESS, -} from '../actions/mutes'; -import { - NOTIFICATIONS_UPDATE, -} from '../actions/notifications'; - -const initialState = ImmutableMap({ - followers: ImmutableMap(), - following: ImmutableMap(), - reblogged_by: ImmutableMap(), - favourited_by: ImmutableMap(), - reactions: ImmutableMap(), - follow_requests: ImmutableMap(), - blocks: ImmutableMap(), - mutes: ImmutableMap(), - groups: ImmutableMap(), - groups_removed_accounts: ImmutableMap(), - pinned: ImmutableMap(), - birthday_reminders: ImmutableMap(), - familiar_followers: ImmutableMap(), -}); - -const normalizeList = (state, type, id, accounts, next) => { - return state.setIn([type, id], ImmutableMap({ - next, - items: ImmutableOrderedSet(accounts.map(item => item.id)), - })); -}; - -const appendToList = (state, type, id, accounts, next) => { - return state.updateIn([type, id], map => { - return map.set('next', next).update('items', ImmutableOrderedSet(), list => list.concat(accounts.map(item => item.id))); - }); -}; - -const normalizeFollowRequest = (state, notification) => { - return state.updateIn(['follow_requests', 'items'], ImmutableOrderedSet(), list => { - return ImmutableOrderedSet([notification.account.id]).union(list); - }); -}; - -export default function userLists(state = initialState, action) { - switch (action.type) { - case FOLLOWERS_FETCH_SUCCESS: - return normalizeList(state, 'followers', action.id, action.accounts, action.next); - case FOLLOWERS_EXPAND_SUCCESS: - return appendToList(state, 'followers', action.id, action.accounts, action.next); - case FOLLOWING_FETCH_SUCCESS: - return normalizeList(state, 'following', action.id, action.accounts, action.next); - case FOLLOWING_EXPAND_SUCCESS: - return appendToList(state, 'following', action.id, action.accounts, action.next); - case REBLOGS_FETCH_SUCCESS: - return state.setIn(['reblogged_by', action.id], ImmutableOrderedSet(action.accounts.map(item => item.id))); - case FAVOURITES_FETCH_SUCCESS: - return state.setIn(['favourited_by', action.id], ImmutableOrderedSet(action.accounts.map(item => item.id))); - case REACTIONS_FETCH_SUCCESS: - return state.setIn(['reactions', action.id], action.reactions.map(({ accounts, ...reaction }) => ({ ...reaction, accounts: ImmutableOrderedSet(accounts.map(account => account.id)) }))); - case NOTIFICATIONS_UPDATE: - return action.notification.type === 'follow_request' ? normalizeFollowRequest(state, action.notification) : state; - case FOLLOW_REQUESTS_FETCH_SUCCESS: - return state.setIn(['follow_requests', 'items'], ImmutableOrderedSet(action.accounts.map(item => item.id))).setIn(['follow_requests', 'next'], action.next); - case FOLLOW_REQUESTS_EXPAND_SUCCESS: - return state.updateIn(['follow_requests', 'items'], list => list.concat(action.accounts.map(item => item.id))).setIn(['follow_requests', 'next'], action.next); - case FOLLOW_REQUEST_AUTHORIZE_SUCCESS: - case FOLLOW_REQUEST_REJECT_SUCCESS: - return state.updateIn(['follow_requests', 'items'], list => list.filterNot(item => item === action.id)); - case BLOCKS_FETCH_SUCCESS: - return state.setIn(['blocks', 'items'], ImmutableOrderedSet(action.accounts.map(item => item.id))).setIn(['blocks', 'next'], action.next); - case BLOCKS_EXPAND_SUCCESS: - return state.updateIn(['blocks', 'items'], list => list.concat(action.accounts.map(item => item.id))).setIn(['blocks', 'next'], action.next); - case MUTES_FETCH_SUCCESS: - return state.setIn(['mutes', 'items'], ImmutableOrderedSet(action.accounts.map(item => item.id))).setIn(['mutes', 'next'], action.next); - case MUTES_EXPAND_SUCCESS: - return state.updateIn(['mutes', 'items'], list => list.concat(action.accounts.map(item => item.id))).setIn(['mutes', 'next'], action.next); - case DIRECTORY_FETCH_SUCCESS: - return state.setIn(['directory', 'items'], ImmutableOrderedSet(action.accounts.map(item => item.id))).setIn(['directory', 'isLoading'], false); - case DIRECTORY_EXPAND_SUCCESS: - return state.updateIn(['directory', 'items'], list => list.concat(action.accounts.map(item => item.id))).setIn(['directory', 'isLoading'], false); - case DIRECTORY_FETCH_REQUEST: - case DIRECTORY_EXPAND_REQUEST: - return state.setIn(['directory', 'isLoading'], true); - case DIRECTORY_FETCH_FAIL: - case DIRECTORY_EXPAND_FAIL: - return state.setIn(['directory', 'isLoading'], false); - case GROUP_MEMBERS_FETCH_SUCCESS: - return normalizeList(state, 'groups', action.id, action.accounts, action.next); - case GROUP_MEMBERS_EXPAND_SUCCESS: - return appendToList(state, 'groups', action.id, action.accounts, action.next); - case GROUP_REMOVED_ACCOUNTS_FETCH_SUCCESS: - return normalizeList(state, 'groups_removed_accounts', action.id, action.accounts, action.next); - case GROUP_REMOVED_ACCOUNTS_EXPAND_SUCCESS: - return appendToList(state, 'groups_removed_accounts', action.id, action.accounts, action.next); - case GROUP_REMOVED_ACCOUNTS_REMOVE_SUCCESS: - return state.updateIn(['groups_removed_accounts', action.groupId, 'items'], list => list.filterNot(item => item === action.id)); - case PINNED_ACCOUNTS_FETCH_SUCCESS: - return normalizeList(state, 'pinned', action.id, action.accounts, action.next); - case BIRTHDAY_REMINDERS_FETCH_SUCCESS: - return state.setIn(['birthday_reminders', action.id], ImmutableOrderedSet(action.accounts.map(item => item.id))); - case FAMILIAR_FOLLOWERS_FETCH_SUCCESS: - return state.setIn(['familiar_followers', action.id], ImmutableOrderedSet(action.accounts.map(item => item.id))); - default: - return state; - } -} diff --git a/app/soapbox/reducers/user_lists.ts b/app/soapbox/reducers/user_lists.ts new file mode 100644 index 000000000..99483a854 --- /dev/null +++ b/app/soapbox/reducers/user_lists.ts @@ -0,0 +1,193 @@ +import { + Map as ImmutableMap, + OrderedSet as ImmutableOrderedSet, + Record as ImmutableRecord, +} from 'immutable'; +import { AnyAction } from 'redux'; + +import { + FOLLOWERS_FETCH_SUCCESS, + FOLLOWERS_EXPAND_SUCCESS, + FOLLOWING_FETCH_SUCCESS, + FOLLOWING_EXPAND_SUCCESS, + FOLLOW_REQUESTS_FETCH_SUCCESS, + FOLLOW_REQUESTS_EXPAND_SUCCESS, + FOLLOW_REQUEST_AUTHORIZE_SUCCESS, + FOLLOW_REQUEST_REJECT_SUCCESS, + PINNED_ACCOUNTS_FETCH_SUCCESS, + BIRTHDAY_REMINDERS_FETCH_SUCCESS, +} from '../actions/accounts'; +import { + BLOCKS_FETCH_SUCCESS, + BLOCKS_EXPAND_SUCCESS, +} from '../actions/blocks'; +import { + DIRECTORY_FETCH_REQUEST, + DIRECTORY_FETCH_SUCCESS, + DIRECTORY_FETCH_FAIL, + DIRECTORY_EXPAND_REQUEST, + DIRECTORY_EXPAND_SUCCESS, + DIRECTORY_EXPAND_FAIL, +} from '../actions/directory'; +import { + FAMILIAR_FOLLOWERS_FETCH_SUCCESS, +} from '../actions/familiar_followers'; +import { + GROUP_MEMBERS_FETCH_SUCCESS, + GROUP_MEMBERS_EXPAND_SUCCESS, + GROUP_REMOVED_ACCOUNTS_FETCH_SUCCESS, + GROUP_REMOVED_ACCOUNTS_EXPAND_SUCCESS, + GROUP_REMOVED_ACCOUNTS_REMOVE_SUCCESS, +} from '../actions/groups'; +import { + REBLOGS_FETCH_SUCCESS, + FAVOURITES_FETCH_SUCCESS, + REACTIONS_FETCH_SUCCESS, +} from '../actions/interactions'; +import { + MUTES_FETCH_SUCCESS, + MUTES_EXPAND_SUCCESS, +} from '../actions/mutes'; +import { + NOTIFICATIONS_UPDATE, +} from '../actions/notifications'; + +import type { APIEntity } from 'soapbox/types/entities'; + +const ListRecord = ImmutableRecord({ + next: null as string | null, + items: ImmutableOrderedSet(), + isLoading: false, +}); + +export const ReactionRecord = ImmutableRecord({ + accounts: ImmutableOrderedSet(), + count: 0, + name: '', +}); + +const ReactionListRecord = ImmutableRecord({ + next: null as string | null, + items: ImmutableOrderedSet(), + isLoading: false, +}); + +const ReducerRecord = ImmutableRecord({ + followers: ImmutableMap(), + following: ImmutableMap(), + reblogged_by: ImmutableMap(), + favourited_by: ImmutableMap(), + reactions: ImmutableMap(), + follow_requests: ListRecord(), + blocks: ListRecord(), + mutes: ListRecord(), + directory: ListRecord({ isLoading: true }), + groups: ImmutableMap(), + groups_removed_accounts: ImmutableMap(), + pinned: ImmutableMap(), + birthday_reminders: ImmutableMap(), + familiar_followers: ImmutableMap(), +}); + +type State = ReturnType; +type List = ReturnType; +type Reaction = ReturnType; +type ReactionList = ReturnType; +type Items = ImmutableOrderedSet; +type NestedListPath = ['followers' | 'following' | 'reblogged_by' | 'favourited_by' | 'reactions' | 'groups' | 'groups_removed_accounts' | 'pinned' | 'birthday_reminders' | 'familiar_followers', string]; +type ListPath = ['follow_requests' | 'blocks' | 'mutes' | 'directory']; + +const normalizeList = (state: State, path: NestedListPath | ListPath, accounts: APIEntity[], next?: string | null) => { + + return state.setIn(path, ListRecord({ + next, + items: ImmutableOrderedSet(accounts.map(item => item.id)), + })); +}; + +const appendToList = (state: State, path: NestedListPath | ListPath, accounts: APIEntity[], next: string | null) => { + return state.updateIn(path, map => { + return (map as List).set('next', next).update('items', list => (list as Items).concat(accounts.map(item => item.id))); + }); +}; + +const removeFromList = (state: State, path: NestedListPath | ListPath, accountId: string) => { + return state.updateIn(path, map => { + return (map as List).update('items', list => (list as Items).filterNot(item => item === accountId)); + }); +}; + +const normalizeFollowRequest = (state: State, notification: APIEntity) => { + return state.updateIn(['follow_requests', 'items'], list => { + return ImmutableOrderedSet([notification.account.id]).union(list as Items); + }); +}; + +export default function userLists(state = ReducerRecord(), action: AnyAction) { + switch (action.type) { + case FOLLOWERS_FETCH_SUCCESS: + return normalizeList(state, ['followers', action.id], action.accounts, action.next); + case FOLLOWERS_EXPAND_SUCCESS: + return appendToList(state, ['followers', action.id], action.accounts, action.next); + case FOLLOWING_FETCH_SUCCESS: + return normalizeList(state, ['following', action.id], action.accounts, action.next); + case FOLLOWING_EXPAND_SUCCESS: + return appendToList(state, ['following', action.id], action.accounts, action.next); + case REBLOGS_FETCH_SUCCESS: + return normalizeList(state, ['reblogged_by', action.id], action.accounts); + case FAVOURITES_FETCH_SUCCESS: + return normalizeList(state, ['favourited_by', action.id], action.accounts); + case REACTIONS_FETCH_SUCCESS: + return state.setIn(['reactions', action.id], ReactionListRecord({ + items: ImmutableOrderedSet(action.reactions.map(({ accounts, ...reaction }: APIEntity) => ReactionRecord({ + ...reaction, + accounts: ImmutableOrderedSet(accounts.map((account: APIEntity) => account.id)), + }))), + })); + case NOTIFICATIONS_UPDATE: + return action.notification.type === 'follow_request' ? normalizeFollowRequest(state, action.notification) : state; + case FOLLOW_REQUESTS_FETCH_SUCCESS: + return normalizeList(state, ['follow_requests'], action.accounts, action.next); + case FOLLOW_REQUESTS_EXPAND_SUCCESS: + return appendToList(state, ['follow_requests'], action.accounts, action.next); + case FOLLOW_REQUEST_AUTHORIZE_SUCCESS: + case FOLLOW_REQUEST_REJECT_SUCCESS: + return removeFromList(state, ['follow_requests'], action.id); + case BLOCKS_FETCH_SUCCESS: + return normalizeList(state, ['blocks'], action.accounts, action.next); + case BLOCKS_EXPAND_SUCCESS: + return appendToList(state, ['blocks'], action.accounts, action.next); + case MUTES_FETCH_SUCCESS: + return normalizeList(state, ['mutes'], action.accounts, action.next); + case MUTES_EXPAND_SUCCESS: + return appendToList(state, ['mutes'], action.accounts, action.next); + case DIRECTORY_FETCH_SUCCESS: + return normalizeList(state, ['directory'], action.accounts, action.next); + case DIRECTORY_EXPAND_SUCCESS: + return appendToList(state, ['directory'], action.accounts, action.next); + case DIRECTORY_FETCH_REQUEST: + case DIRECTORY_EXPAND_REQUEST: + return state.setIn(['directory', 'isLoading'], true); + case DIRECTORY_FETCH_FAIL: + case DIRECTORY_EXPAND_FAIL: + return state.setIn(['directory', 'isLoading'], false); + case GROUP_MEMBERS_FETCH_SUCCESS: + return normalizeList(state, ['groups', action.id], action.accounts, action.next); + case GROUP_MEMBERS_EXPAND_SUCCESS: + return appendToList(state, ['groups', action.id], action.accounts, action.next); + case GROUP_REMOVED_ACCOUNTS_FETCH_SUCCESS: + return normalizeList(state, ['groups_removed_accounts', action.id], action.accounts, action.next); + case GROUP_REMOVED_ACCOUNTS_EXPAND_SUCCESS: + return appendToList(state, ['groups_removed_accounts', action.id], action.accounts, action.next); + case GROUP_REMOVED_ACCOUNTS_REMOVE_SUCCESS: + return removeFromList(state, ['groups_removed_accounts', action.groupId], action.id); + case PINNED_ACCOUNTS_FETCH_SUCCESS: + return normalizeList(state, ['pinned', action.id], action.accounts, action.next); + case BIRTHDAY_REMINDERS_FETCH_SUCCESS: + return normalizeList(state, ['birthday_reminders', action.id], action.accounts, action.next); + case FAMILIAR_FOLLOWERS_FETCH_SUCCESS: + return normalizeList(state, ['familiar_followers', action.id], action.accounts, action.next); + default: + return state; + } +} diff --git a/app/styles/components/crypto-donate.scss b/app/styles/components/crypto-donate.scss index c90d279a3..4641c8269 100644 --- a/app/styles/components/crypto-donate.scss +++ b/app/styles/components/crypto-donate.scss @@ -58,12 +58,7 @@ } .crypto-donate-modal { - background: var(--foreground-color); - border-radius: 8px; - padding-bottom: 13px; - - .crypto-address__actions .svg-icon { - width: 22px; - height: 22px; + .crypto-address { + padding: 0; } } From b9fc2c6e587aefeb96b02448101530aa11c58da8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Thu, 23 Jun 2022 20:45:46 +0200 Subject: [PATCH 2/3] reducers/scheduled_statuses: TypeScript MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../features/scheduled_statuses/builder.tsx | 27 ++++----- .../components/scheduled_status.tsx | 2 +- app/soapbox/reducers/pending_statuses.ts | 2 +- app/soapbox/reducers/scheduled_statuses.js | 38 ------------- app/soapbox/reducers/scheduled_statuses.ts | 57 +++++++++++++++++++ 5 files changed, 73 insertions(+), 53 deletions(-) delete mode 100644 app/soapbox/reducers/scheduled_statuses.js create mode 100644 app/soapbox/reducers/scheduled_statuses.ts diff --git a/app/soapbox/features/scheduled_statuses/builder.tsx b/app/soapbox/features/scheduled_statuses/builder.tsx index 2927c8984..9db827412 100644 --- a/app/soapbox/features/scheduled_statuses/builder.tsx +++ b/app/soapbox/features/scheduled_statuses/builder.tsx @@ -3,28 +3,29 @@ import { Map as ImmutableMap } from 'immutable'; import { normalizeStatus } from 'soapbox/normalizers/status'; import { calculateStatus } from 'soapbox/reducers/statuses'; import { makeGetAccount } from 'soapbox/selectors'; -import { RootState } from 'soapbox/store'; -export const buildStatus = (state: RootState, scheduledStatus: ImmutableMap) => { +import type { ScheduledStatus } from 'soapbox/reducers/scheduled_statuses'; +import type { RootState } from 'soapbox/store'; + +export const buildStatus = (state: RootState, scheduledStatus: ScheduledStatus) => { const getAccount = makeGetAccount(); const me = state.me as string; - const params = scheduledStatus.get('params'); const account = getAccount(state, me); const status = ImmutableMap({ account, - content: params.get('text', '').replace(new RegExp('\n', 'g'), '
'), /* eslint-disable-line no-control-regex */ - created_at: params.get('scheduled_at'), - id: scheduledStatus.get('id'), - in_reply_to_id: params.get('in_reply_to_id'), - media_attachments: scheduledStatus.get('media_attachments'), - poll: params.get('poll'), - sensitive: params.get('sensitive'), - uri: `/scheduled_statuses/${scheduledStatus.get('id')}`, - url: `/scheduled_statuses/${scheduledStatus.get('id')}`, - visibility: params.get('visibility'), + content: scheduledStatus.text.replace(new RegExp('\n', 'g'), '
'), /* eslint-disable-line no-control-regex */ + created_at: scheduledStatus.scheduled_at, + id: scheduledStatus.id, + in_reply_to_id: scheduledStatus.in_reply_to_id, + media_attachments: scheduledStatus.media_attachments, + poll: scheduledStatus.poll, + sensitive: scheduledStatus.sensitive, + uri: `/scheduled_statuses/${scheduledStatus.id}`, + url: `/scheduled_statuses/${scheduledStatus.id}`, + visibility: scheduledStatus.visibility, }); return calculateStatus(normalizeStatus(status)); diff --git a/app/soapbox/features/scheduled_statuses/components/scheduled_status.tsx b/app/soapbox/features/scheduled_statuses/components/scheduled_status.tsx index 15a8e1ed9..2e7763126 100644 --- a/app/soapbox/features/scheduled_statuses/components/scheduled_status.tsx +++ b/app/soapbox/features/scheduled_statuses/components/scheduled_status.tsx @@ -20,7 +20,7 @@ interface IScheduledStatus { } const ScheduledStatus: React.FC = ({ statusId, ...other }) => { - const status = useAppSelector((state) => buildStatus(state, state.scheduled_statuses.get(statusId))) as StatusEntity; + const status = useAppSelector((state) => buildStatus(state, state.scheduled_statuses.get(statusId)!)) as StatusEntity; if (!status) return null; diff --git a/app/soapbox/reducers/pending_statuses.ts b/app/soapbox/reducers/pending_statuses.ts index 090b8694a..e0df9f07d 100644 --- a/app/soapbox/reducers/pending_statuses.ts +++ b/app/soapbox/reducers/pending_statuses.ts @@ -1,11 +1,11 @@ import { List as ImmutableList, Map as ImmutableMap, Record as ImmutableRecord, fromJS } from 'immutable'; -import { AnyAction } from 'redux'; import { STATUS_CREATE_REQUEST, STATUS_CREATE_SUCCESS, } from 'soapbox/actions/statuses'; +import type { AnyAction } from 'redux'; import type { StatusVisibility } from 'soapbox/normalizers/status'; const PendingStatusRecord = ImmutableRecord({ diff --git a/app/soapbox/reducers/scheduled_statuses.js b/app/soapbox/reducers/scheduled_statuses.js deleted file mode 100644 index f96573e88..000000000 --- a/app/soapbox/reducers/scheduled_statuses.js +++ /dev/null @@ -1,38 +0,0 @@ -import { Map as ImmutableMap, fromJS } from 'immutable'; - -import { - SCHEDULED_STATUSES_FETCH_SUCCESS, - SCHEDULED_STATUS_CANCEL_REQUEST, - SCHEDULED_STATUS_CANCEL_SUCCESS, -} from 'soapbox/actions/scheduled_statuses'; -import { STATUS_CREATE_SUCCESS } from 'soapbox/actions/statuses'; - -import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer'; - -const importStatus = (state, status) => { - if (!status.scheduled_at) return state; - return state.set(status.id, fromJS(status)); -}; - -const importStatuses = (state, statuses) => - state.withMutations(mutable => statuses.forEach(status => importStatus(mutable, status))); - -const deleteStatus = (state, id) => state.delete(id); - -const initialState = ImmutableMap(); - -export default function scheduled_statuses(state = initialState, action) { - switch (action.type) { - case STATUS_IMPORT: - case STATUS_CREATE_SUCCESS: - return importStatus(state, action.status); - case STATUSES_IMPORT: - case SCHEDULED_STATUSES_FETCH_SUCCESS: - return importStatuses(state, action.statuses); - case SCHEDULED_STATUS_CANCEL_REQUEST: - case SCHEDULED_STATUS_CANCEL_SUCCESS: - return deleteStatus(state, action.id); - default: - return state; - } -} diff --git a/app/soapbox/reducers/scheduled_statuses.ts b/app/soapbox/reducers/scheduled_statuses.ts new file mode 100644 index 000000000..13b4a934d --- /dev/null +++ b/app/soapbox/reducers/scheduled_statuses.ts @@ -0,0 +1,57 @@ +import { List as ImmutableList, Map as ImmutableMap, Record as ImmutableRecord, fromJS } from 'immutable'; + +import { STATUS_IMPORT, STATUSES_IMPORT } from 'soapbox/actions/importer'; +import { + SCHEDULED_STATUSES_FETCH_SUCCESS, + SCHEDULED_STATUS_CANCEL_REQUEST, + SCHEDULED_STATUS_CANCEL_SUCCESS, +} from 'soapbox/actions/scheduled_statuses'; +import { STATUS_CREATE_SUCCESS } from 'soapbox/actions/statuses'; + +import type { AnyAction } from 'redux'; +import type { StatusVisibility } from 'soapbox/normalizers/status'; +import type { APIEntity } from 'soapbox/types/entities'; + +const ScheduledStatusRecord = ImmutableRecord({ + id: '', + scheduled_at: new Date(), + media_attachments: null as ImmutableList> | null, + text: '', + in_reply_to_id: null as string | null, + media_ids: null as ImmutableList | null, + sensitive: false, + spoiler_text: '', + visibility: 'public' as StatusVisibility, + poll: null as ImmutableMap | null, +}); + +export type ScheduledStatus = ReturnType; +type State = ImmutableMap; + +const initialState: State = ImmutableMap(); + +const importStatus = (state: State, { params, ...status }: APIEntity) => { + if (!status.scheduled_at) return state; + return state.set(status.id, ScheduledStatusRecord(ImmutableMap(fromJS({ ...status, ...params })))); +}; + +const importStatuses = (state: State, statuses: APIEntity[]) => + state.withMutations(mutable => statuses.forEach(status => importStatus(mutable, status))); + +const deleteStatus = (state: State, id: string) => state.delete(id); + +export default function scheduled_statuses(state: State = initialState, action: AnyAction) { + switch (action.type) { + case STATUS_IMPORT: + case STATUS_CREATE_SUCCESS: + return importStatus(state, action.status); + case STATUSES_IMPORT: + case SCHEDULED_STATUSES_FETCH_SUCCESS: + return importStatuses(state, action.statuses); + case SCHEDULED_STATUS_CANCEL_REQUEST: + case SCHEDULED_STATUS_CANCEL_SUCCESS: + return deleteStatus(state, action.id); + default: + return state; + } +} From 4e545f063898245bb904ef5b39de6f7dff3209ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Fri, 24 Jun 2022 00:33:23 +0200 Subject: [PATCH 3/3] Reducers: TypeScript MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- app/soapbox/actions/accounts.ts | 3 - app/soapbox/actions/auth.ts | 1 - app/soapbox/actions/filters.ts | 1 - app/soapbox/actions/media.ts | 2 - app/soapbox/actions/moderation.tsx | 1 - .../actions/push_notifications/registerer.ts | 6 +- app/soapbox/actions/statuses.ts | 1 - app/soapbox/actions/timelines.ts | 10 +- app/soapbox/actions/verification.ts | 2 - app/soapbox/components/error_boundary.tsx | 1 - app/soapbox/components/ui/modal/modal.tsx | 1 - .../features/account_gallery/index.tsx | 4 +- .../containers/header_container.js | 1 - .../features/direct_timeline/index.tsx | 2 +- app/soapbox/features/home_timeline/index.tsx | 2 +- app/soapbox/features/list_timeline/index.tsx | 2 +- .../steps/suggested-accounts-step.tsx | 2 - .../status/components/detailed-status.tsx | 1 - .../ui/components/birthdays_modal.tsx | 1 - .../components/familiar_followers_modal.tsx | 1 - .../report-modal/steps/other-actions-step.tsx | 2 +- .../ui/components/reactions_modal.tsx | 1 - .../features/ui/components/reblogs_modal.tsx | 1 - .../features/ui/components/timeline.tsx | 10 +- .../__tests__/push_notifications.test.ts | 10 +- .../reducers/__tests__/timelines.test.ts | 28 ++-- app/soapbox/reducers/conversations.ts | 1 - ...notifications.js => push_notifications.ts} | 25 +++- .../reducers/{timelines.js => timelines.ts} | 138 ++++++++++-------- app/soapbox/selectors/index.ts | 4 +- app/soapbox/stream.ts | 1 - 31 files changed, 130 insertions(+), 136 deletions(-) rename app/soapbox/reducers/{push_notifications.js => push_notifications.ts} (54%) rename app/soapbox/reducers/{timelines.js => timelines.ts} (56%) diff --git a/app/soapbox/actions/accounts.ts b/app/soapbox/actions/accounts.ts index e3767c7b8..60cb6bbe6 100644 --- a/app/soapbox/actions/accounts.ts +++ b/app/soapbox/actions/accounts.ts @@ -410,7 +410,6 @@ const unmuteAccountFail = (error: AxiosError) => ({ error, }); - const subscribeAccount = (id: string, notifications?: boolean) => (dispatch: AppDispatch, getState: () => RootState) => { if (!isLoggedIn(getState)) return null; @@ -465,7 +464,6 @@ const unsubscribeAccountFail = (error: AxiosError) => ({ error, }); - const removeFromFollowers = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => { if (!isLoggedIn(getState)) return; @@ -771,7 +769,6 @@ const authorizeFollowRequestFail = (id: string, error: AxiosError) => ({ error, }); - const rejectFollowRequest = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => { if (!isLoggedIn(getState)) return; diff --git a/app/soapbox/actions/auth.ts b/app/soapbox/actions/auth.ts index 54473346c..40532a582 100644 --- a/app/soapbox/actions/auth.ts +++ b/app/soapbox/actions/auth.ts @@ -269,7 +269,6 @@ export const fetchOwnAccounts = () => }); }; - export const register = (params: Record) => (dispatch: AppDispatch) => { params.fullname = params.username; diff --git a/app/soapbox/actions/filters.ts b/app/soapbox/actions/filters.ts index 16bb23951..61b4e9b63 100644 --- a/app/soapbox/actions/filters.ts +++ b/app/soapbox/actions/filters.ts @@ -65,7 +65,6 @@ const createFilter = (phrase: string, expires_at: string, context: Array }); }; - const deleteFilter = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => { dispatch({ type: FILTERS_DELETE_REQUEST }); diff --git a/app/soapbox/actions/media.ts b/app/soapbox/actions/media.ts index 1a63d87d5..15c637c35 100644 --- a/app/soapbox/actions/media.ts +++ b/app/soapbox/actions/media.ts @@ -22,14 +22,12 @@ const uploadMediaV1 = (data: FormData, onUploadProgress = noOp) => onUploadProgress: onUploadProgress, }); - const uploadMediaV2 = (data: FormData, onUploadProgress = noOp) => (dispatch: any, getState: () => RootState) => api(getState).post('/api/v2/media', data, { onUploadProgress: onUploadProgress, }); - const uploadMedia = (data: FormData, onUploadProgress = noOp) => (dispatch: AppDispatch, getState: () => RootState) => { const state = getState(); diff --git a/app/soapbox/actions/moderation.tsx b/app/soapbox/actions/moderation.tsx index 946157071..69342277b 100644 --- a/app/soapbox/actions/moderation.tsx +++ b/app/soapbox/actions/moderation.tsx @@ -162,7 +162,6 @@ const deleteStatusModal = (intl: IntlShape, statusId: string, afterConfirm = () })); }; - export { deactivateUserModal, deleteUserModal, diff --git a/app/soapbox/actions/push_notifications/registerer.ts b/app/soapbox/actions/push_notifications/registerer.ts index b8d12caa4..893814b02 100644 --- a/app/soapbox/actions/push_notifications/registerer.ts +++ b/app/soapbox/actions/push_notifications/registerer.ts @@ -38,7 +38,7 @@ const unsubscribe = ({ registration, subscription }: { const sendSubscriptionToBackend = (subscription: PushSubscription, me: Me) => (dispatch: AppDispatch, getState: () => RootState) => { - const alerts = getState().push_notifications.get('alerts').toJS(); + const alerts = getState().push_notifications.alerts.toJS(); const params = { subscription, data: { alerts } }; if (me) { @@ -82,7 +82,7 @@ const register = () => // We have a subscription, check if it is still valid const currentServerKey = (new Uint8Array(subscription.options.applicationServerKey!)).toString(); const subscriptionServerKey = urlBase64ToUint8Array(vapidKey).toString(); - const serverEndpoint = getState().push_notifications.getIn(['subscription', 'endpoint']); + const serverEndpoint = getState().push_notifications.subscription?.endpoint; // If the VAPID public key did not change and the endpoint corresponds // to the endpoint saved in the backend, the subscription is valid @@ -136,7 +136,7 @@ const register = () => const saveSettings = () => (dispatch: AppDispatch, getState: () => RootState) => { const state = getState().push_notifications; - const alerts = state.get('alerts'); + const alerts = state.alerts; const data = { alerts }; const me = getState().me; diff --git a/app/soapbox/actions/statuses.ts b/app/soapbox/actions/statuses.ts index f0a6aad15..5cf534446 100644 --- a/app/soapbox/actions/statuses.ts +++ b/app/soapbox/actions/statuses.ts @@ -242,7 +242,6 @@ const fetchStatusWithContext = (id: string) => } }; - const muteStatus = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => { if (!isLoggedIn(getState)) return; diff --git a/app/soapbox/actions/timelines.ts b/app/soapbox/actions/timelines.ts index ec8615def..5d7fac2c4 100644 --- a/app/soapbox/actions/timelines.ts +++ b/app/soapbox/actions/timelines.ts @@ -82,7 +82,7 @@ const updateTimelineQueue = (timeline: string, statusId: string, accept: ((statu const dequeueTimeline = (timelineId: string, expandFunc?: (lastStatusId: string) => void, optionalExpandArgs?: any) => (dispatch: AppDispatch, getState: () => RootState) => { const state = getState(); - const queuedCount = state.timelines.getIn([timelineId, 'totalQueuedItemsCount'], 0); + const queuedCount = state.timelines.get(timelineId)?.totalQueuedItemsCount || 0; if (queuedCount <= 0) return; @@ -136,16 +136,16 @@ const parseTags = (tags: Record = {}, mode: 'any' | 'all' | 'none const expandTimeline = (timelineId: string, path: string, params: Record = {}, done = noOp) => (dispatch: AppDispatch, getState: () => RootState) => { - const timeline = getState().timelines.get(timelineId) || ImmutableMap(); + const timeline = getState().timelines.get(timelineId) || {} as Record; const isLoadingMore = !!params.max_id; - if (timeline.get('isLoading')) { + if (timeline.isLoading) { done(); return dispatch(noOpAsync()); } - if (!params.max_id && !params.pinned && timeline.get('items', ImmutableOrderedSet()).size > 0) { - params.since_id = timeline.getIn(['items', 0]); + if (!params.max_id && !params.pinned && (timeline.items || ImmutableOrderedSet()).size > 0) { + params.since_id = timeline.items || 0; } const isLoadingRecent = !!params.since_id; diff --git a/app/soapbox/actions/verification.ts b/app/soapbox/actions/verification.ts index bc8db0189..ce3d27009 100644 --- a/app/soapbox/actions/verification.ts +++ b/app/soapbox/actions/verification.ts @@ -59,7 +59,6 @@ const removeStoredVerification = () => { localStorage.removeItem(LOCAL_STORAGE_VERIFICATION_KEY); }; - /** * Fetch and return the Registration token for Pepe. */ @@ -207,7 +206,6 @@ const fetchRegistrationToken = () => return null; } - return api(getState).post('/api/v1/pepe/registrations') .then(response => { updateStorage({ token: response.data.access_token }); diff --git a/app/soapbox/components/error_boundary.tsx b/app/soapbox/components/error_boundary.tsx index 6df25cb9d..c1025d465 100644 --- a/app/soapbox/components/error_boundary.tsx +++ b/app/soapbox/components/error_boundary.tsx @@ -171,7 +171,6 @@ class ErrorBoundary extends React.PureComponent { /> )} - {browser && ( diff --git a/app/soapbox/components/ui/modal/modal.tsx b/app/soapbox/components/ui/modal/modal.tsx index b019d01e3..3987d6afe 100644 --- a/app/soapbox/components/ui/modal/modal.tsx +++ b/app/soapbox/components/ui/modal/modal.tsx @@ -128,7 +128,6 @@ const Modal: React.FC = ({ )}
-
{secondaryAction && (