Reducers: TypeScript, fixes

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
environments/review-develop-3zknud/deployments/194^2
marcin mikołajczak 2022-06-05 16:17:26 +02:00
rodzic 5bb26c9b47
commit 41a2b1f08f
15 zmienionych plików z 142 dodań i 119 usunięć

Wyświetl plik

@ -249,7 +249,7 @@ export interface IDropdown extends RouteComponentProps {
) => void, ) => void,
onClose?: (id: number) => void, onClose?: (id: number) => void,
dropdownPlacement?: string, dropdownPlacement?: string,
openDropdownId?: number, openDropdownId?: number | null,
openedViaKeyboard?: boolean, openedViaKeyboard?: boolean,
text?: string, text?: string,
onShiftClick?: React.EventHandler<React.MouseEvent | React.KeyboardEvent>, onShiftClick?: React.EventHandler<React.MouseEvent | React.KeyboardEvent>,

Wyświetl plik

@ -7,8 +7,6 @@ import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
import Column from '../ui/components/column'; import Column from '../ui/components/column';
import type { Map as ImmutableMap } from 'immutable';
const messages = defineMessages({ const messages = defineMessages({
heading: { id: 'column.admin.moderation_log', defaultMessage: 'Moderation Log' }, heading: { id: 'column.admin.moderation_log', defaultMessage: 'Moderation Log' },
emptyMessage: { id: 'admin.moderation_log.empty_message', defaultMessage: 'You have not performed any moderation actions yet. When you do, a history will be shown here.' }, emptyMessage: { id: 'admin.moderation_log.empty_message', defaultMessage: 'You have not performed any moderation actions yet. When you do, a history will be shown here.' },
@ -18,8 +16,10 @@ const ModerationLog = () => {
const intl = useIntl(); const intl = useIntl();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const items = useAppSelector((state) => state.admin_log.get('index').map((i: number) => state.admin_log.getIn(['items', String(i)]))) as ImmutableMap<string, any>; const items = useAppSelector((state) => {
const hasMore = useAppSelector((state) => state.admin_log.get('total', 0) - state.admin_log.get('index').count() > 0); return state.admin_log.index.map((i) => state.admin_log.items.get(String(i)));
});
const hasMore = useAppSelector((state) => state.admin_log.total - state.admin_log.index.count() > 0);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [lastPage, setLastPage] = useState(0); const [lastPage, setLastPage] = useState(0);
@ -56,12 +56,12 @@ const ModerationLog = () => {
hasMore={hasMore} hasMore={hasMore}
onLoadMore={handleLoadMore} onLoadMore={handleLoadMore}
> >
{items.map((item, i) => ( {items.map((item) => item && (
<div className='logentry' key={i}> <div className='logentry' key={item.id}>
<div className='logentry__message'>{item.get('message')}</div> <div className='logentry__message'>{item.message}</div>
<div className='logentry__timestamp'> <div className='logentry__timestamp'>
<FormattedDate <FormattedDate
value={new Date(item.get('time') * 1000)} value={new Date(item.time * 1000)}
hour12={false} hour12={false}
year='numeric' year='numeric'
month='short' month='short'

Wyświetl plik

@ -20,9 +20,9 @@ const Bookmarks: React.FC = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const intl = useIntl(); const intl = useIntl();
const statusIds = useAppSelector((state) => state.status_lists.getIn(['bookmarks', 'items'])); const statusIds = useAppSelector((state) => state.status_lists.get('bookmarks')!.items);
const isLoading = useAppSelector((state) => state.status_lists.getIn(['bookmarks', 'isLoading'], true)); const isLoading = useAppSelector((state) => state.status_lists.get('bookmarks')!.isLoading);
const hasMore = useAppSelector((state) => !!state.status_lists.getIn(['bookmarks', 'next'])); const hasMore = useAppSelector((state) => !!state.status_lists.get('bookmarks')!.next);
React.useEffect(() => { React.useEffect(() => {
dispatch(fetchBookmarkedStatuses()); dispatch(fetchBookmarkedStatuses());
@ -43,7 +43,7 @@ const Bookmarks: React.FC = () => {
statusIds={statusIds} statusIds={statusIds}
scrollKey='bookmarked_statuses' scrollKey='bookmarked_statuses'
hasMore={hasMore} hasMore={hasMore}
isLoading={isLoading} isLoading={typeof isLoading === 'boolean' ? isLoading : true}
onLoadMore={() => handleLoadMore(dispatch)} onLoadMore={() => handleLoadMore(dispatch)}
onRefresh={handleRefresh} onRefresh={handleRefresh}
emptyMessage={emptyMessage} emptyMessage={emptyMessage}

Wyświetl plik

@ -32,9 +32,9 @@ const mapStateToProps = (state, { params }) => {
if (isMyAccount) { if (isMyAccount) {
return { return {
isMyAccount, isMyAccount,
statusIds: state.getIn(['status_lists', 'favourites', 'items']), statusIds: state.status_lists.get('favourites').items,
isLoading: state.getIn(['status_lists', 'favourites', 'isLoading'], true), isLoading: state.status_lists.get('favourites').isLoading,
hasMore: !!state.getIn(['status_lists', 'favourites', 'next']), hasMore: !!state.status_lists.get('favourites').next,
}; };
} }
@ -57,9 +57,9 @@ const mapStateToProps = (state, { params }) => {
unavailable, unavailable,
username, username,
isAccount: !!state.getIn(['accounts', accountId]), isAccount: !!state.getIn(['accounts', accountId]),
statusIds: state.getIn(['status_lists', `favourites:${accountId}`, 'items'], []), statusIds: state.status_lists.get(`favourites:${accountId}`)?.items || [],
isLoading: state.getIn(['status_lists', `favourites:${accountId}`, 'isLoading'], true), isLoading: state.status_lists.get(`favourites:${accountId}`)?.isLoading,
hasMore: !!state.getIn(['status_lists', `favourites:${accountId}`, 'next']), hasMore: !!state.status_lists.get(`favourites:${accountId}`)?.next,
}; };
}; };
@ -147,7 +147,7 @@ class Favourites extends ImmutablePureComponent {
statusIds={statusIds} statusIds={statusIds}
scrollKey='favourited_statuses' scrollKey='favourited_statuses'
hasMore={hasMore} hasMore={hasMore}
isLoading={isLoading} isLoading={typeof isLoading === 'boolean' ? isLoading : true}
onLoadMore={this.handleLoadMore} onLoadMore={this.handleLoadMore}
emptyMessage={emptyMessage} emptyMessage={emptyMessage}
/> />

Wyświetl plik

@ -11,8 +11,8 @@ import Account from './account';
const FollowRecommendationsList: React.FC = () => { const FollowRecommendationsList: React.FC = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const suggestions = useAppSelector((state) => state.suggestions.get('items')); const suggestions = useAppSelector((state) => state.suggestions.items);
const isLoading = useAppSelector((state) => state.suggestions.get('isLoading')); const isLoading = useAppSelector((state) => state.suggestions.isLoading);
useEffect(() => { useEffect(() => {
if (suggestions.size === 0) { if (suggestions.size === 0) {
@ -30,8 +30,8 @@ const FollowRecommendationsList: React.FC = () => {
return ( return (
<div className='column-list'> <div className='column-list'>
{suggestions.size > 0 ? suggestions.map((suggestion: { account: string }, idx: number) => ( {suggestions.size > 0 ? suggestions.map((suggestion) => (
<Account key={idx} id={suggestion.account} /> <Account key={suggestion.account} id={suggestion.account} />
)) : ( )) : (
<div className='column-list__empty-message'> <div className='column-list__empty-message'>
<FormattedMessage id='empty_column.follow_recommendations' defaultMessage='Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.' /> <FormattedMessage id='empty_column.follow_recommendations' defaultMessage='Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.' />

Wyświetl plik

@ -1,4 +1,3 @@
import { Map as ImmutableMap } from 'immutable';
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import * as React from 'react'; import * as React from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
@ -13,9 +12,9 @@ import { useAppSelector } from 'soapbox/hooks';
const SuggestedAccountsStep = ({ onNext }: { onNext: () => void }) => { const SuggestedAccountsStep = ({ onNext }: { onNext: () => void }) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const suggestions = useAppSelector((state) => state.suggestions.get('items')); const suggestions = useAppSelector((state) => state.suggestions.items);
const hasMore = useAppSelector((state) => !!state.suggestions.get('next')); const hasMore = useAppSelector((state) => !!state.suggestions.next);
const isLoading = useAppSelector((state) => state.suggestions.get('isLoading')); const isLoading = useAppSelector((state) => state.suggestions.isLoading);
const handleLoadMore = debounce(() => { const handleLoadMore = debounce(() => {
if (isLoading) { if (isLoading) {
@ -40,11 +39,11 @@ const SuggestedAccountsStep = ({ onNext }: { onNext: () => void }) => {
useWindowScroll={false} useWindowScroll={false}
style={{ height: 320 }} style={{ height: 320 }}
> >
{suggestions.map((suggestion: ImmutableMap<string, any>) => ( {suggestions.map((suggestion) => (
<div key={suggestion.get('account')} className='py-2'> <div key={suggestion.account} className='py-2'>
<AccountContainer <AccountContainer
// @ts-ignore: TS thinks `id` is passed to <Account>, but it isn't // @ts-ignore: TS thinks `id` is passed to <Account>, but it isn't
id={suggestion.get('account')} id={suggestion.account}
showProfileHoverCard={false} showProfileHoverCard={false}
/> />
</div> </div>

Wyświetl plik

@ -21,8 +21,8 @@ const mapStateToProps = (state, { params }) => {
const meUsername = state.getIn(['accounts', me, 'username'], ''); const meUsername = state.getIn(['accounts', me, 'username'], '');
return { return {
isMyAccount: (username.toLowerCase() === meUsername.toLowerCase()), isMyAccount: (username.toLowerCase() === meUsername.toLowerCase()),
statusIds: state.getIn(['status_lists', 'pins', 'items']), statusIds: state.status_lists.get('pins').items,
hasMore: !!state.getIn(['status_lists', 'pins', 'next']), hasMore: !!state.status_lists.get('pins').next,
}; };
}; };

Wyświetl plik

@ -22,9 +22,9 @@ const ScheduledStatuses = () => {
const intl = useIntl(); const intl = useIntl();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const statusIds = useAppSelector((state) => state.status_lists.getIn(['scheduled_statuses', 'items'])); const statusIds = useAppSelector((state) => state.status_lists.get('scheduled_statuses')!.items);
const isLoading = useAppSelector((state) => state.status_lists.getIn(['scheduled_statuses', 'isLoading'])); const isLoading = useAppSelector((state) => state.status_lists.get('scheduled_statuses')!.isLoading);
const hasMore = useAppSelector((state) => !!state.status_lists.getIn(['scheduled_statuses', 'next'])); const hasMore = useAppSelector((state) => !!state.status_lists.get('scheduled_statuses')!.next);
useEffect(() => { useEffect(() => {
dispatch(fetchScheduledStatuses()); dispatch(fetchScheduledStatuses());
@ -37,7 +37,7 @@ const ScheduledStatuses = () => {
<ScrollableList <ScrollableList
scrollKey='scheduled_statuses' scrollKey='scheduled_statuses'
hasMore={hasMore} hasMore={hasMore}
isLoading={isLoading} isLoading={typeof isLoading === 'boolean' ? isLoading : true}
onLoadMore={() => handleLoadMore(dispatch)} onLoadMore={() => handleLoadMore(dispatch)}
emptyMessage={emptyMessage} emptyMessage={emptyMessage}
> >

Wyświetl plik

@ -1,4 +1,4 @@
import { Map as ImmutableMap, fromJS } from 'immutable'; import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet } from 'immutable';
import React from 'react'; import React from 'react';
import { render, screen } from '../../../../jest/test-helpers'; import { render, screen } from '../../../../jest/test-helpers';
@ -16,12 +16,12 @@ describe('<WhoToFollow />', () => {
avatar: 'test.jpg', avatar: 'test.jpg',
}), }),
}), }),
suggestions: ImmutableMap({ suggestions: {
items: fromJS([{ items: ImmutableOrderedSet([{
source: 'staff', source: 'staff',
account: '1', account: '1',
}]), }]),
}), },
}; };
render(<WhoToFollowPanel limit={1} />, null, store); render(<WhoToFollowPanel limit={1} />, null, store);
@ -44,8 +44,8 @@ describe('<WhoToFollow />', () => {
avatar: 'test.jpg', avatar: 'test.jpg',
}), }),
}), }),
suggestions: ImmutableMap({ suggestions: {
items: fromJS([ items: ImmutableOrderedSet([
{ {
source: 'staff', source: 'staff',
account: '1', account: '1',
@ -55,7 +55,7 @@ describe('<WhoToFollow />', () => {
account: '2', account: '2',
}, },
]), ]),
}), },
}; };
render(<WhoToFollowPanel limit={3} />, null, store); render(<WhoToFollowPanel limit={3} />, null, store);
@ -78,8 +78,8 @@ describe('<WhoToFollow />', () => {
avatar: 'test.jpg', avatar: 'test.jpg',
}), }),
}), }),
suggestions: ImmutableMap({ suggestions: {
items: fromJS([ items: ImmutableOrderedSet([
{ {
source: 'staff', source: 'staff',
account: '1', account: '1',
@ -89,7 +89,7 @@ describe('<WhoToFollow />', () => {
account: '2', account: '2',
}, },
]), ]),
}), },
}; };
render(<WhoToFollowPanel limit={1} />, null, store); render(<WhoToFollowPanel limit={1} />, null, store);
@ -112,9 +112,9 @@ describe('<WhoToFollow />', () => {
avatar: 'test.jpg', avatar: 'test.jpg',
}), }),
}), }),
suggestions: ImmutableMap({ suggestions: {
items: fromJS([]), items: ImmutableOrderedSet([]),
}), },
}; };
render(<WhoToFollowPanel limit={1} />, null, store); render(<WhoToFollowPanel limit={1} />, null, store);

Wyświetl plik

@ -1,6 +1,5 @@
import { Map as ImmutableMap } from 'immutable';
import * as React from 'react'; import * as React from 'react';
import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux'; import { useDispatch } from 'react-redux';
import { fetchSuggestions, dismissSuggestion } from 'soapbox/actions/suggestions'; import { fetchSuggestions, dismissSuggestion } from 'soapbox/actions/suggestions';
@ -8,6 +7,8 @@ import { Widget } from 'soapbox/components/ui';
import AccountContainer from 'soapbox/containers/account_container'; import AccountContainer from 'soapbox/containers/account_container';
import { useAppSelector } from 'soapbox/hooks'; import { useAppSelector } from 'soapbox/hooks';
import type { Account as AccountEntity } from 'soapbox/types/entities';
const messages = defineMessages({ const messages = defineMessages({
dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' }, dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' },
}); });
@ -20,11 +21,11 @@ const WhoToFollowPanel = ({ limit }: IWhoToFollowPanel) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const intl = useIntl(); const intl = useIntl();
const suggestions = useAppSelector((state) => state.suggestions.get('items')); const suggestions = useAppSelector((state) => state.suggestions.items);
const suggestionsToRender = suggestions.slice(0, limit); const suggestionsToRender = suggestions.slice(0, limit);
const handleDismiss = (account: ImmutableMap<string, any>) => { const handleDismiss = (account: AccountEntity) => {
dispatch(dismissSuggestion(account.get('id'))); dispatch(dismissSuggestion(account.id));
}; };
React.useEffect(() => { React.useEffect(() => {
@ -45,11 +46,11 @@ const WhoToFollowPanel = ({ limit }: IWhoToFollowPanel) => {
title={<FormattedMessage id='who_to_follow.title' defaultMessage='People To Follow' />} title={<FormattedMessage id='who_to_follow.title' defaultMessage='People To Follow' />}
// onAction={handleAction} // onAction={handleAction}
> >
{suggestionsToRender.map((suggestion: ImmutableMap<string, any>) => ( {suggestionsToRender.map((suggestion) => (
<AccountContainer <AccountContainer
key={suggestion.get('account')} key={suggestion.account}
// @ts-ignore: TS thinks `id` is passed to <Account>, but it isn't // @ts-ignore: TS thinks `id` is passed to <Account>, but it isn't
id={suggestion.get('account')} id={suggestion.account}
actionIcon={require('@tabler/icons/icons/x.svg')} actionIcon={require('@tabler/icons/icons/x.svg')}
actionTitle={intl.formatMessage(messages.dismissSuggestion)} actionTitle={intl.formatMessage(messages.dismissSuggestion)}
onActionClick={handleDismiss} onActionClick={handleDismiss}

Wyświetl plik

@ -1,30 +0,0 @@
import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet } from 'immutable';
import reducer from '../status_lists';
describe('status_lists reducer', () => {
it('should return the initial state', () => {
expect(reducer(undefined, {})).toEqual(ImmutableMap({
favourites: ImmutableMap({
next: null,
loaded: false,
items: ImmutableOrderedSet(),
}),
bookmarks: ImmutableMap({
next: null,
loaded: false,
items: ImmutableOrderedSet(),
}),
pins: ImmutableMap({
next: null,
loaded: false,
items: ImmutableOrderedSet(),
}),
scheduled_statuses: ImmutableMap({
next: null,
loaded: false,
items: ImmutableOrderedSet(),
}),
}));
});
});

Wyświetl plik

@ -0,0 +1,32 @@
import reducer from '../status_lists';
describe('status_lists reducer', () => {
it('should return the initial state', () => {
expect(reducer(undefined, {} as any).toJS()).toEqual({
favourites: {
next: null,
loaded: false,
isLoading: null,
items: [],
},
bookmarks: {
next: null,
loaded: false,
isLoading: null,
items: [],
},
pins: {
next: null,
loaded: false,
isLoading: null,
items: [],
},
scheduled_statuses: {
next: null,
loaded: false,
isLoading: null,
items: [],
},
});
});
});

Wyświetl plik

@ -2,30 +2,37 @@ import {
Map as ImmutableMap, Map as ImmutableMap,
Record as ImmutableRecord, Record as ImmutableRecord,
OrderedSet as ImmutableOrderedSet, OrderedSet as ImmutableOrderedSet,
fromJS,
} from 'immutable'; } from 'immutable';
import { ADMIN_LOG_FETCH_SUCCESS } from 'soapbox/actions/admin'; import { ADMIN_LOG_FETCH_SUCCESS } from 'soapbox/actions/admin';
import type { AnyAction } from 'redux'; import type { AnyAction } from 'redux';
const LogEntryRecord = ImmutableRecord({
data: ImmutableMap<string, any>(),
id: 0,
message: '',
time: 0,
});
const ReducerRecord = ImmutableRecord({ const ReducerRecord = ImmutableRecord({
items: ImmutableMap(), items: ImmutableMap<string, LogEntry>(),
index: ImmutableOrderedSet(), index: ImmutableOrderedSet<number>(),
total: 0, total: 0,
}); });
type LogEntry = ReturnType<typeof LogEntryRecord>;
type State = ReturnType<typeof ReducerRecord>; type State = ReturnType<typeof ReducerRecord>;
type APIEntity = Record<string, any>; type APIEntity = Record<string, any>;
type APIEntities = Array<APIEntity>; type APIEntities = Array<APIEntity>;
const parseItems = (items: APIEntities) => { const parseItems = (items: APIEntities) => {
const ids: Array<number> = []; const ids: Array<number> = [];
const map: Record<number, any> = {}; const map: Record<string, LogEntry> = {};
items.forEach(item => { items.forEach(item => {
ids.push(item.id); ids.push(item.id);
map[item.id] = item; map[item.id] = LogEntryRecord(item);
}); });
return { ids: ids, map: map }; return { ids: ids, map: map };
@ -36,7 +43,7 @@ const importItems = (state: State, items: APIEntities, total: number) => {
return state.withMutations(state => { return state.withMutations(state => {
state.update('index', v => v.union(ids)); state.update('index', v => v.union(ids));
state.update('items', v => v.merge(fromJS(map))); state.update('items', v => v.merge(map));
state.set('total', total); state.set('total', total);
}); });
}; };

Wyświetl plik

@ -6,10 +6,11 @@ import {
} from '../actions/dropdown_menu'; } from '../actions/dropdown_menu';
import type { AnyAction } from 'redux'; import type { AnyAction } from 'redux';
import type { DropdownPlacement } from 'soapbox/components/dropdown_menu';
const ReducerRecord = ImmutableRecord({ const ReducerRecord = ImmutableRecord({
openId: null as number | null, openId: null as number | null,
placement: null as 'top' | 'bottom' | null, placement: null as any as DropdownPlacement,
keyboard: false, keyboard: false,
}); });

Wyświetl plik

@ -1,4 +1,8 @@
import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet } from 'immutable'; import {
Map as ImmutableMap,
OrderedSet as ImmutableOrderedSet,
Record as ImmutableRecord,
} from 'immutable';
import { import {
BOOKMARKED_STATUSES_FETCH_REQUEST, BOOKMARKED_STATUSES_FETCH_REQUEST,
@ -44,29 +48,38 @@ import {
SCHEDULED_STATUS_CANCEL_SUCCESS, SCHEDULED_STATUS_CANCEL_SUCCESS,
} from '../actions/scheduled_statuses'; } from '../actions/scheduled_statuses';
const initialMap = ImmutableMap({ import type { AnyAction } from 'redux';
next: null, import type { Status as StatusEntity } from 'soapbox/types/entities';
const StatusListRecord = ImmutableRecord({
next: null as string | null,
loaded: false, loaded: false,
items: ImmutableOrderedSet(), isLoading: null as boolean | null,
items: ImmutableOrderedSet<string>(),
}); });
const initialState = ImmutableMap({ type State = ImmutableMap<string, StatusList>;
favourites: initialMap, type StatusList = ReturnType<typeof StatusListRecord>;
bookmarks: initialMap, type Status = string | StatusEntity;
pins: initialMap, type Statuses = Array<string | StatusEntity>;
scheduled_statuses: initialMap,
const initialState: State = ImmutableMap({
favourites: StatusListRecord(),
bookmarks: StatusListRecord(),
pins: StatusListRecord(),
scheduled_statuses: StatusListRecord(),
}); });
const getStatusId = status => typeof status === 'string' ? status : status.get('id'); const getStatusId = (status: string | StatusEntity) => typeof status === 'string' ? status : status.id;
const getStatusIds = (statuses = []) => ( const getStatusIds = (statuses: Statuses = []) => (
ImmutableOrderedSet(statuses.map(status => status.id)) ImmutableOrderedSet(statuses.map(getStatusId))
); );
const setLoading = (state, listType, loading) => state.setIn([listType, 'isLoading'], loading); const setLoading = (state: State, listType: string, loading: boolean) => state.setIn([listType, 'isLoading'], loading);
const normalizeList = (state, listType, statuses, next) => { const normalizeList = (state: State, listType: string, statuses: Statuses, next: string | null) => {
return state.update(listType, initialMap, listMap => listMap.withMutations(map => { return state.update(listType, StatusListRecord(), listMap => listMap.withMutations(map => {
map.set('next', next); map.set('next', next);
map.set('loaded', true); map.set('loaded', true);
map.set('isLoading', false); map.set('isLoading', false);
@ -74,29 +87,29 @@ const normalizeList = (state, listType, statuses, next) => {
})); }));
}; };
const appendToList = (state, listType, statuses, next) => { const appendToList = (state: State, listType: string, statuses: Statuses, next: string | null) => {
const newIds = getStatusIds(statuses); const newIds = getStatusIds(statuses);
return state.update(listType, initialMap, listMap => listMap.withMutations(map => { return state.update(listType, StatusListRecord(), listMap => listMap.withMutations(map => {
map.set('next', next); map.set('next', next);
map.set('isLoading', false); map.set('isLoading', false);
map.update('items', ImmutableOrderedSet(), items => items.union(newIds)); map.update('items', items => items.union(newIds));
})); }));
}; };
const prependOneToList = (state, listType, status) => { const prependOneToList = (state: State, listType: string, status: Status) => {
const statusId = getStatusId(status); const statusId = getStatusId(status);
return state.updateIn([listType, 'items'], ImmutableOrderedSet(), items => { return state.updateIn([listType, 'items'], ImmutableOrderedSet(), items => {
return ImmutableOrderedSet([statusId]).union(items); return ImmutableOrderedSet([statusId]).union(items as ImmutableOrderedSet<string>);
}); });
}; };
const removeOneFromList = (state, listType, status) => { const removeOneFromList = (state: State, listType: string, status: Status) => {
const statusId = getStatusId(status); const statusId = getStatusId(status);
return state.updateIn([listType, 'items'], ImmutableOrderedSet(), items => items.delete(statusId)); return state.updateIn([listType, 'items'], ImmutableOrderedSet(), items => (items as ImmutableOrderedSet<string>).delete(statusId));
}; };
export default function statusLists(state = initialState, action) { export default function statusLists(state = initialState, action: AnyAction) {
switch (action.type) { switch (action.type) {
case FAVOURITED_STATUSES_FETCH_REQUEST: case FAVOURITED_STATUSES_FETCH_REQUEST:
case FAVOURITED_STATUSES_EXPAND_REQUEST: case FAVOURITED_STATUSES_EXPAND_REQUEST:
@ -154,7 +167,7 @@ export default function statusLists(state = initialState, action) {
return appendToList(state, 'scheduled_statuses', action.statuses, action.next); return appendToList(state, 'scheduled_statuses', action.statuses, action.next);
case SCHEDULED_STATUS_CANCEL_REQUEST: case SCHEDULED_STATUS_CANCEL_REQUEST:
case SCHEDULED_STATUS_CANCEL_SUCCESS: case SCHEDULED_STATUS_CANCEL_SUCCESS:
return removeOneFromList(state, 'scheduled_statuses', action.id || action.status.get('id')); return removeOneFromList(state, 'scheduled_statuses', action.id || action.status.id);
default: default:
return state; return state;
} }