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; } }