pl-fe: migrate list editing modals to tanstack query

Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
nicole mikołajczyk
2025-07-28 08:06:07 +02:00
parent 03f41fbe28
commit 822fff36f8
12 changed files with 90 additions and 432 deletions

View File

@ -1,5 +1,4 @@
import { queryClient } from 'pl-fe/queries/client';
import { selectAccount } from 'pl-fe/selectors';
import toast from 'pl-fe/toast';
import { isLoggedIn } from 'pl-fe/utils/auth';
@ -7,7 +6,7 @@ import { getClient } from '../api';
import { importEntities } from './importer';
import type { Account, List, PaginatedResponse } from 'pl-api';
import type { Account, List } from 'pl-api';
import type { AppDispatch, RootState } from 'pl-fe/store';
const LIST_EDITOR_TITLE_CHANGE = 'LIST_EDITOR_TITLE_CHANGE' as const;
@ -16,25 +15,10 @@ const LIST_EDITOR_EXCLUSIVE_CHANGE = 'LIST_EDITOR_EXCLUSIVE_CHANGE' as const;
const LIST_EDITOR_RESET = 'LIST_EDITOR_RESET' as const;
const LIST_EDITOR_SETUP = 'LIST_EDITOR_SETUP' as const;
const LIST_ACCOUNTS_FETCH_REQUEST = 'LIST_ACCOUNTS_FETCH_REQUEST' as const;
const LIST_ACCOUNTS_FETCH_SUCCESS = 'LIST_ACCOUNTS_FETCH_SUCCESS' as const;
const LIST_ACCOUNTS_FETCH_FAIL = 'LIST_ACCOUNTS_FETCH_FAIL' as const;
const LIST_EDITOR_SUGGESTIONS_CHANGE = 'LIST_EDITOR_SUGGESTIONS_CHANGE' as const;
const LIST_EDITOR_SUGGESTIONS_READY = 'LIST_EDITOR_SUGGESTIONS_READY' as const;
const LIST_EDITOR_SUGGESTIONS_CLEAR = 'LIST_EDITOR_SUGGESTIONS_CLEAR' as const;
const LIST_EDITOR_ADD_SUCCESS = 'LIST_EDITOR_ADD_SUCCESS' as const;
const LIST_EDITOR_REMOVE_SUCCESS = 'LIST_EDITOR_REMOVE_SUCCESS' as const;
const LIST_ADDER_RESET = 'LIST_ADDER_RESET' as const;
const LIST_ADDER_SETUP = 'LIST_ADDER_SETUP' as const;
const LIST_ADDER_LISTS_FETCH_REQUEST = 'LIST_ADDER_LISTS_FETCH_REQUEST' as const;
const LIST_ADDER_LISTS_FETCH_SUCCESS = 'LIST_ADDER_LISTS_FETCH_SUCCESS' as const;
const LIST_ADDER_LISTS_FETCH_FAIL = 'LIST_ADDER_LISTS_FETCH_FAIL' as const;
interface ListEditorSetupAction {
type: typeof LIST_EDITOR_SETUP;
list: List;
@ -48,8 +32,6 @@ const setupListEditor = (listId: string) => (dispatch: AppDispatch) => {
type: LIST_EDITOR_SETUP,
list,
});
dispatch(fetchListAccounts(listId));
};
const changeListEditorTitle = (value: string) => ({
@ -71,35 +53,6 @@ const resetListEditor = () => ({
type: LIST_EDITOR_RESET,
});
const fetchListAccounts = (listId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
if (!isLoggedIn(getState)) return;
dispatch(fetchListAccountsRequest(listId));
return getClient(getState()).lists.getListAccounts(listId).then(({ items, next }) => {
dispatch(importEntities({ accounts: items }));
dispatch(fetchListAccountsSuccess(listId, items, next));
}).catch(err => dispatch(fetchListAccountsFail(listId, err)));
};
const fetchListAccountsRequest = (listId: string) => ({
type: LIST_ACCOUNTS_FETCH_REQUEST,
listId,
});
const fetchListAccountsSuccess = (listId: string, accounts: Account[], next: (() => Promise<PaginatedResponse<Account>>) | null) => ({
type: LIST_ACCOUNTS_FETCH_SUCCESS,
listId,
accounts,
next,
});
const fetchListAccountsFail = (listId: string, error: unknown) => ({
type: LIST_ACCOUNTS_FETCH_FAIL,
listId,
error,
});
const fetchListSuggestions = (q: string) => (dispatch: AppDispatch, getState: () => RootState) => {
if (!isLoggedIn(getState)) return;
@ -124,114 +77,15 @@ const changeListSuggestions = (value: string) => ({
value,
});
const addToListEditor = (accountId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
dispatch(addToList(getState().listEditor.listId!, accountId));
};
const addToList = (listId: string, accountId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
if (!isLoggedIn(getState)) return;
return getClient(getState()).lists.addListAccounts(listId, [accountId])
.then(() => dispatch(addToListSuccess(listId, accountId)));
};
const addToListSuccess = (listId: string, accountId: string) => ({
type: LIST_EDITOR_ADD_SUCCESS,
listId,
accountId,
});
const removeFromListEditor = (accountId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
dispatch(removeFromList(getState().listEditor.listId!, accountId));
};
const removeFromList = (listId: string, accountId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
if (!isLoggedIn(getState)) return;
return getClient(getState()).lists.deleteListAccounts(listId, [accountId])
.then(() => dispatch(removeFromListSuccess(listId, accountId)));
};
const removeFromListSuccess = (listId: string, accountId: string) => ({
type: LIST_EDITOR_REMOVE_SUCCESS,
listId,
accountId,
});
const resetListAdder = () => ({
type: LIST_ADDER_RESET,
});
interface ListAdderSetupAction {
type: typeof LIST_ADDER_SETUP;
account: Account;
}
const setupListAdder = (accountId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
const account = selectAccount(getState(), accountId);
if (!account) return;
dispatch<ListAdderSetupAction>({
type: LIST_ADDER_SETUP,
account,
});
dispatch(fetchAccountLists(accountId));
};
const fetchAccountLists = (accountId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
if (!isLoggedIn(getState)) return;
dispatch(fetchAccountListsRequest(accountId));
return getClient(getState()).accounts.getAccountLists(accountId)
.then((data) => dispatch(fetchAccountListsSuccess(accountId, data)))
.catch(err => dispatch(fetchAccountListsFail(accountId, err)));
};
const fetchAccountListsRequest = (listId: string) => ({
type: LIST_ADDER_LISTS_FETCH_REQUEST,
listId,
});
const fetchAccountListsSuccess = (listId: string, lists: Array<List>) => ({
type: LIST_ADDER_LISTS_FETCH_SUCCESS,
listId,
lists,
});
const fetchAccountListsFail = (listId: string, err: unknown) => ({
type: LIST_ADDER_LISTS_FETCH_FAIL,
listId,
err,
});
const addToListAdder = (listId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
dispatch(addToList(listId, getState().listAdder.accountId!));
};
const removeFromListAdder = (listId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
dispatch(removeFromList(listId, getState().listAdder.accountId!));
};
type ListsAction =
| ListEditorSetupAction
| ReturnType<typeof changeListEditorTitle>
| ReturnType<typeof changeListEditorRepliesPolicy>
| ReturnType<typeof changeListEditorExclusive>
| ReturnType<typeof resetListEditor>
| ReturnType<typeof fetchListAccountsRequest>
| ReturnType<typeof fetchListAccountsSuccess>
| ReturnType<typeof fetchListAccountsFail>
| ReturnType<typeof fetchListSuggestionsReady>
| ReturnType<typeof clearListSuggestions>
| ReturnType<typeof changeListSuggestions>
| ReturnType<typeof addToListSuccess>
| ReturnType<typeof removeFromListSuccess>
| ReturnType<typeof resetListAdder>
| ListAdderSetupAction
| ReturnType<typeof fetchAccountListsRequest>
| ReturnType<typeof fetchAccountListsSuccess>
| ReturnType<typeof fetchAccountListsFail>;
| ReturnType<typeof changeListSuggestions>;
export {
LIST_EDITOR_TITLE_CHANGE,
@ -239,19 +93,9 @@ export {
LIST_EDITOR_EXCLUSIVE_CHANGE,
LIST_EDITOR_RESET,
LIST_EDITOR_SETUP,
LIST_ACCOUNTS_FETCH_REQUEST,
LIST_ACCOUNTS_FETCH_SUCCESS,
LIST_ACCOUNTS_FETCH_FAIL,
LIST_EDITOR_SUGGESTIONS_CHANGE,
LIST_EDITOR_SUGGESTIONS_READY,
LIST_EDITOR_SUGGESTIONS_CLEAR,
LIST_EDITOR_ADD_SUCCESS,
LIST_EDITOR_REMOVE_SUCCESS,
LIST_ADDER_RESET,
LIST_ADDER_SETUP,
LIST_ADDER_LISTS_FETCH_REQUEST,
LIST_ADDER_LISTS_FETCH_SUCCESS,
LIST_ADDER_LISTS_FETCH_FAIL,
setupListEditor,
changeListEditorTitle,
changeListEditorRepliesPolicy,
@ -260,11 +104,5 @@ export {
fetchListSuggestions,
clearListSuggestions,
changeListSuggestions,
addToListEditor,
removeFromListEditor,
resetListAdder,
setupListAdder,
addToListAdder,
removeFromListAdder,
type ListsAction,
};

View File

@ -71,7 +71,7 @@ const messages = defineMessages({
bite: { id: 'account.bite', defaultMessage: 'Bite @{name}' },
removeFromFollowers: { id: 'account.remove_from_followers', defaultMessage: 'Remove this follower' },
adminAccount: { id: 'status.admin_account', defaultMessage: 'Moderate @{name}' },
add_or_remove_from_list: { id: 'account.add_or_remove_from_list', defaultMessage: 'Add or Remove from lists' },
add_or_remove_from_list: { id: 'account.add_or_remove_from_list', defaultMessage: 'Add or remove from lists' },
search: { id: 'account.search', defaultMessage: 'Search from @{name}' },
searchSelf: { id: 'account.search_self', defaultMessage: 'Search your posts' },
unfollowConfirm: { id: 'confirmations.unfollow.confirm', defaultMessage: 'Unfollow' },

View File

@ -1,12 +1,9 @@
import React from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { removeFromListAdder, addToListAdder } from 'pl-fe/actions/lists';
import Icon from 'pl-fe/components/icon';
import IconButton from 'pl-fe/components/icon-button';
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
import { useList } from 'pl-fe/queries/accounts/use-lists';
import { useAddAccountsToList, useList, useRemoveAccountsFromList } from 'pl-fe/queries/accounts/use-lists';
const messages = defineMessages({
remove: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
@ -14,18 +11,21 @@ const messages = defineMessages({
});
interface IList {
accountId: string;
listId: string;
added?: boolean;
}
const List: React.FC<IList> = ({ listId }) => {
const List: React.FC<IList> = ({ listId, accountId, added }) => {
const intl = useIntl();
const dispatch = useAppDispatch();
const { data: list } = useList(listId);
const added = useAppSelector((state) => state.listAdder.lists.items.includes(listId));
const onRemove = () => dispatch(removeFromListAdder(listId));
const onAdd = () => dispatch(addToListAdder(listId));
const { mutate: addToList } = useAddAccountsToList(listId);
const { mutate: removeFromList } = useRemoveAccountsFromList(listId);
const onAdd = () => addToList([accountId]);
const onRemove = () => removeFromList([accountId]);
if (!list) return null;

View File

@ -1,13 +1,11 @@
import React, { useEffect } from 'react';
import React from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { setupListAdder, resetListAdder } from 'pl-fe/actions/lists';
import { CardHeader, CardTitle } from 'pl-fe/components/ui/card';
import Modal from 'pl-fe/components/ui/modal';
import AccountContainer from 'pl-fe/containers/account-container';
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
import { NewListForm, getOrderedLists } from 'pl-fe/pages/account-lists/lists';
import { useLists } from 'pl-fe/queries/accounts/use-lists';
import { useLists, useListsForAccount } from 'pl-fe/queries/accounts/use-lists';
import List from './components/list';
@ -24,25 +22,18 @@ interface ListAdderModalProps {
const ListAdderModal: React.FC<BaseModalProps & ListAdderModalProps> = ({ accountId, onClose }) => {
const intl = useIntl();
const dispatch = useAppDispatch();
const { data: accountListIds = [] } = useListsForAccount(accountId);
const { data: listIds = [] } = useLists((lists) => getOrderedLists(lists).map(list => list.id));
useEffect(() => {
dispatch(setupListAdder(accountId));
return () => {
dispatch(resetListAdder());
};
}, []);
const onClickClose = () => {
onClose('LIST_ADDER');
};
return (
<Modal
title={<FormattedMessage id='list_adder.header_title' defaultMessage='Add or Remove from Lists' />}
title={<FormattedMessage id='list_adder.header_title' defaultMessage='Add or remove from lists' />}
onClose={onClickClose}
>
<AccountContainer id={accountId} withRelationship={false} />
@ -60,7 +51,7 @@ const ListAdderModal: React.FC<BaseModalProps & ListAdderModalProps> = ({ accoun
<CardTitle title={intl.formatMessage(messages.subheading)} />
</CardHeader>
<div>
{listIds.map(listId => <List key={listId} listId={listId} />)}
{listIds.map(listId => <List key={listId} accountId={accountId} listId={listId} added={accountListIds.includes(listId)} />)}
</div>
</Modal>
);

View File

@ -1,12 +1,10 @@
import React from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { removeFromListEditor, addToListEditor } from 'pl-fe/actions/lists';
import IconButton from 'pl-fe/components/icon-button';
import HStack from 'pl-fe/components/ui/hstack';
import AccountContainer from 'pl-fe/containers/account-container';
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
import { useAddAccountsToList, useRemoveAccountsFromList } from 'pl-fe/queries/accounts/use-lists';
const messages = defineMessages({
remove: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
@ -14,21 +12,23 @@ const messages = defineMessages({
});
interface IAccount {
listId: string;
accountId: string;
added?: boolean;
}
const Account: React.FC<IAccount> = ({ accountId }) => {
const Account: React.FC<IAccount> = ({ listId, accountId, added }) => {
const intl = useIntl();
const dispatch = useAppDispatch();
const isAdded = useAppSelector((state) => state.listEditor.accounts.items.includes(accountId));
const { mutate: addToList } = useAddAccountsToList(listId);
const { mutate: removeFromList } = useRemoveAccountsFromList(listId);
const onRemove = () => dispatch(removeFromListEditor(accountId));
const onAdd = () => dispatch(addToListEditor(accountId));
const onAdd = () => addToList([accountId]);
const onRemove = () => removeFromList([accountId]);
let button;
if (isAdded) {
if (added) {
button = <IconButton src={require('@tabler/icons/outline/x.svg')} iconClassName='h-5 w-5' title={intl.formatMessage(messages.remove)} onClick={onRemove} />;
} else {
button = <IconButton src={require('@tabler/icons/outline/plus.svg')} iconClassName='h-5 w-5' title={intl.formatMessage(messages.add)} onClick={onAdd} />;

View File

@ -6,6 +6,7 @@ import { CardHeader, CardTitle } from 'pl-fe/components/ui/card';
import Modal from 'pl-fe/components/ui/modal';
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
import { useListAccounts } from 'pl-fe/queries/accounts/use-lists';
import Account from './components/account';
import EditListForm from './components/edit-list-form';
@ -29,7 +30,7 @@ const ListEditorModal: React.FC<BaseModalProps & ListEditorModalProps> = ({ list
const [tab, setTab] = useState<'info' | 'members'>('info');
const accountIds = useAppSelector((state) => state.listEditor.accounts.items);
const { data: accountIds = [] } = useListAccounts(listId);
const searchAccountIds = useAppSelector((state) => state.listEditor.suggestions.items);
useEffect(() => {
@ -61,7 +62,7 @@ const ListEditorModal: React.FC<BaseModalProps & ListEditorModalProps> = ({ list
<CardTitle title={intl.formatMessage(messages.removeFromList)} />
</CardHeader>
<div className='max-h-48 overflow-y-auto'>
{accountIds.map(accountId => <Account key={accountId} accountId={accountId} />)}
{accountIds.map(accountId => <Account key={accountId} listId={listId} accountId={accountId} added={accountIds.includes(accountId)} />)}
</div>
</div>
<br />
@ -73,7 +74,7 @@ const ListEditorModal: React.FC<BaseModalProps & ListEditorModalProps> = ({ list
</CardHeader>
<Search />
<div className='max-h-48 overflow-y-auto'>
{searchAccountIds.map(accountId => <Account key={accountId} accountId={accountId} />)}
{searchAccountIds.map(accountId => <Account key={accountId} listId={listId} accountId={accountId} added={accountIds.includes(accountId)} />)}
</div>
</>
)}

View File

@ -1,11 +1,14 @@
import { useMutation, useQuery } from '@tanstack/react-query';
import { type InfiniteData, useMutation, useQuery } from '@tanstack/react-query';
import { useClient } from 'pl-fe/hooks/use-client';
import { useFeatures } from 'pl-fe/hooks/use-features';
import { queryClient } from '../client';
import { filterById } from '../utils/filter-id';
import { makePaginatedResponseQuery } from '../utils/make-paginated-response-query';
import { minifyAccountList } from '../utils/minify-list';
import type { CreateListParams, List, UpdateListParams } from 'pl-api';
import type { CreateListParams, List, PaginatedResponse, UpdateListParams } from 'pl-api';
const useLists = <T>(
select?: ((data: Array<List>) => T),
@ -58,4 +61,48 @@ const useUpdateList = (listId: string) => {
});
};
export { useLists, useList, useCreateList, useDeleteList, useUpdateList };
const useListAccounts = makePaginatedResponseQuery(
(listId: string) => ['accountsLists', 'lists', listId],
(client, [listId]) => client.lists.getListAccounts(listId).then(minifyAccountList),
);
const useAddAccountsToList = (listId: string) => {
const client = useClient();
return useMutation({
mutationKey: ['accountsLists', 'lists', listId, 'add'],
mutationFn: (accountIds: Array<string>) => client.lists.addListAccounts(listId, accountIds),
onSettled: (_, __, accountIds) => {
queryClient.invalidateQueries({ queryKey: ['accountsLists', 'lists', listId] });
accountIds.forEach((accountId) =>
queryClient.setQueryData<Array<string>>(['lists', 'forAccount', accountId], (listIds) => listIds ? [...listIds, listId] : undefined),
);
},
});
};
const useRemoveAccountsFromList = (listId: string) => {
const client = useClient();
return useMutation({
mutationKey: ['accountsLists', 'lists', listId, 'remove'],
mutationFn: (accountIds: Array<string>) => client.lists.deleteListAccounts(listId, accountIds),
onSettled: (_, __, accountIds) => {
queryClient.setQueryData<InfiniteData<PaginatedResponse<string>>>(['accountsLists', 'lists', listId], filterById(accountIds));
accountIds.forEach((accountId) =>
queryClient.setQueryData<Array<string>>(['lists', 'forAccount', accountId], listIds => listIds?.filter(id => id !== listId)),
);
},
});
};
const useListsForAccount = (accountId: string) => {
const client = useClient();
return useQuery({
queryKey: ['lists', 'forAccount', accountId],
queryFn: () => client.accounts.getAccountLists(accountId).then((lists) => lists.map((list) => list.id)),
});
};
export { useLists, useList, useCreateList, useDeleteList, useUpdateList, useListAccounts, useAddAccountsToList, useRemoveAccountsFromList, useListsForAccount };

View File

@ -3,19 +3,24 @@ import { create } from 'mutative';
import type { InfiniteData } from '@tanstack/react-query';
import type { PaginatedResponse } from 'pl-api';
const filterById = (filteredId: string) => (data: InfiniteData<PaginatedResponse<string, true>, unknown> | undefined) => {
const filterById = (filteredId: string | Array<string>) => (data: InfiniteData<PaginatedResponse<string, true>, unknown> | undefined) => {
if (data) {
return create((data), data => {
let found = false;
let found = 0;
data.pages.forEach((page) => {
page.items = page.items.filter((id) => {
if (id === filteredId) found = true;
if (Array.isArray(filteredId)) {
const includes = filteredId.includes(id);
if (includes) found += 1;
return !includes;
}
if (id === filteredId) found = 1;
return id !== filteredId;
});
});
if (found) data.pages.forEach((page) => {
if (page.total) page.total -= 1;
if (page.total) page.total -= found;
});
});
}

View File

@ -13,7 +13,6 @@ import conversations from './conversations';
import draft_statuses from './draft-statuses';
import filters from './filters';
import instance from './instance';
import listAdder from './list-adder';
import listEditor from './list-editor';
import me from './me';
import meta from './meta';
@ -39,7 +38,6 @@ const reducers = {
entities,
filters,
instance,
listAdder,
listEditor,
me,
meta,

View File

@ -1,116 +0,0 @@
import { List as ImmutableList, Record as ImmutableRecord } from 'immutable';
import * as actions from 'pl-fe/actions/lists';
import reducer from './list-adder';
describe('list_adder reducer', () => {
it('should return the initial state', () => {
expect(reducer(undefined, {} as any)).toMatchObject({
accountId: null,
lists: {
items: ImmutableList(),
loaded: false,
isLoading: false,
},
});
});
it('should handle LIST_ADDER_RESET', () => {
const state = ImmutableRecord({
accountId: null,
lists: ImmutableRecord({
items: ImmutableList<string>(),
loaded: false,
isLoading: false,
})(),
})();
const action = {
type: actions.LIST_ADDER_RESET,
};
expect(reducer(state, action)).toMatchObject({
accountId: null,
lists: {
items: ImmutableList(),
loaded: false,
isLoading: false,
},
});
});
it('should handle LIST_ADDER_LISTS_FETCH_REQUEST', () => {
const state = ImmutableRecord({
accountId: null,
lists: ImmutableRecord({
items: ImmutableList<string>(),
loaded: false,
isLoading: false,
})(),
})();
const action = {
type: actions.LIST_ADDER_LISTS_FETCH_REQUEST,
};
expect(reducer(state, action)).toMatchObject({
accountId: null,
lists: {
items: ImmutableList(),
loaded: false,
isLoading: true,
},
});
});
it('should handle LIST_ADDER_LISTS_FETCH_FAIL', () => {
const state = ImmutableRecord({
accountId: null,
lists: ImmutableRecord({
items: ImmutableList<string>(),
loaded: false,
isLoading: false,
})(),
})();
const action = {
type: actions.LIST_ADDER_LISTS_FETCH_FAIL,
};
expect(reducer(state, action)).toMatchObject({
accountId: null,
lists: {
items: ImmutableList(),
loaded: false,
isLoading: false,
},
});
});
// it('should handle LIST_ADDER_LISTS_FETCH_SUCCESS', () => {
// const state = ImmutableMap({
// accountId: null,
//
// lists: ImmutableMap({
// items: ImmutableList(),
// loaded: false,
// isLoading: false,
// }),
// });
// const action = {
// type: actions.LIST_ADDER_LISTS_FETCH_SUCCESS,
// };
// expect(reducer(state, action)).toEqual(ImmutableMap({
// accountId: null,
//
// lists: ImmutableMap({
// items: ImmutableList(),
// loaded: true,
// isLoading: false,
// }),
// }));
// });
});

View File

@ -1,67 +0,0 @@
import { create } from 'mutative';
import {
LIST_ADDER_RESET,
LIST_ADDER_SETUP,
LIST_ADDER_LISTS_FETCH_REQUEST,
LIST_ADDER_LISTS_FETCH_SUCCESS,
LIST_ADDER_LISTS_FETCH_FAIL,
LIST_EDITOR_ADD_SUCCESS,
LIST_EDITOR_REMOVE_SUCCESS,
type ListsAction,
} from '../actions/lists';
interface State {
accountId: string | null;
lists: {
items: Array<string>;
loaded: boolean;
isLoading: boolean;
};
}
const initialState: State = {
accountId: null,
lists: {
items: [],
loaded: false,
isLoading: false,
},
};
const listAdderReducer = (state: State = initialState, action: ListsAction): State => {
switch (action.type) {
case LIST_ADDER_RESET:
return initialState;
case LIST_ADDER_SETUP:
return create(state, (draft) => {
draft.accountId = action.account.id;
});
case LIST_ADDER_LISTS_FETCH_REQUEST:
return create(state, (draft) => {
draft.lists.isLoading = true;
});
case LIST_ADDER_LISTS_FETCH_FAIL:
return create(state, (draft) => {
draft.lists.isLoading = false;
});
case LIST_ADDER_LISTS_FETCH_SUCCESS:
return create(state, (draft) => {
draft.lists.isLoading = false;
draft.lists.loaded = true;
draft.lists.items = action.lists.map((item: { id: string }) => item.id);
});
case LIST_EDITOR_ADD_SUCCESS:
return create(state, (draft) => {
draft.lists.items = [action.listId, ...draft.lists.items];
});
case LIST_EDITOR_REMOVE_SUCCESS:
return create(state, (draft) => {
draft.lists.items = draft.lists.items.filter(id => id !== action.listId);
});
default:
return state;
}
};
export { listAdderReducer as default };

View File

@ -4,14 +4,9 @@ import {
LIST_EDITOR_RESET,
LIST_EDITOR_SETUP,
LIST_EDITOR_TITLE_CHANGE,
LIST_ACCOUNTS_FETCH_REQUEST,
LIST_ACCOUNTS_FETCH_SUCCESS,
LIST_ACCOUNTS_FETCH_FAIL,
LIST_EDITOR_SUGGESTIONS_READY,
LIST_EDITOR_SUGGESTIONS_CLEAR,
LIST_EDITOR_SUGGESTIONS_CHANGE,
LIST_EDITOR_ADD_SUCCESS,
LIST_EDITOR_REMOVE_SUCCESS,
type ListsAction,
LIST_EDITOR_EXCLUSIVE_CHANGE,
LIST_EDITOR_REPLIES_POLICY_CHANGE,
@ -27,12 +22,6 @@ interface State {
repliesPolicy: List['replies_policy'];
exclusive?: boolean;
accounts: {
items: Array<string>;
loaded: boolean;
isLoading: boolean;
};
suggestions: {
value: string;
items: Array<string>;
@ -46,12 +35,6 @@ const initialState: State = {
repliesPolicy: undefined,
exclusive: false,
accounts: {
items: [],
loaded: false,
isLoading: false,
},
suggestions: {
value: '',
items: [],
@ -82,20 +65,6 @@ const listEditorReducer = (state: State = initialState, action: ListsAction): St
return create(state, (draft) => {
draft.repliesPolicy = action.repliesPolicy;
});
case LIST_ACCOUNTS_FETCH_REQUEST:
return create(state, (draft) => {
draft.accounts.isLoading = true;
});
case LIST_ACCOUNTS_FETCH_FAIL:
return create(state, (draft) => {
draft.accounts.isLoading = false;
});
case LIST_ACCOUNTS_FETCH_SUCCESS:
return create(state, (draft) => {
draft.accounts.isLoading = false;
draft.accounts.loaded = true;
draft.accounts.items = action.accounts.map((item: { id: string }) => item.id);
});
case LIST_EDITOR_SUGGESTIONS_CHANGE:
return create(state, (draft) => {
draft.suggestions.value = action.value;
@ -109,14 +78,6 @@ const listEditorReducer = (state: State = initialState, action: ListsAction): St
draft.suggestions.items = [];
draft.suggestions.value = '';
});
case LIST_EDITOR_ADD_SUCCESS:
return create(state, (draft) => {
draft.accounts.items = [action.accountId, ...draft.accounts.items];
});
case LIST_EDITOR_REMOVE_SUCCESS:
return create(state, (draft) => {
draft.accounts.items = draft.accounts.items.filter(id => id !== action.accountId);
});
default:
return state;
}