pl-fe: don't use redux for list editor modal
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
@ -1,65 +0,0 @@
|
||||
import { queryClient } from 'pl-fe/queries/client';
|
||||
|
||||
import type { List } from 'pl-api';
|
||||
import type { AppDispatch } from 'pl-fe/store';
|
||||
|
||||
const LIST_EDITOR_TITLE_CHANGE = 'LIST_EDITOR_TITLE_CHANGE' as const;
|
||||
const LIST_EDITOR_REPLIES_POLICY_CHANGE = 'LIST_EDITOR_REPLIES_POLICY_CHANGE' as const;
|
||||
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;
|
||||
|
||||
interface ListEditorSetupAction {
|
||||
type: typeof LIST_EDITOR_SETUP;
|
||||
list: List;
|
||||
}
|
||||
|
||||
const setupListEditor = (listId: string) => (dispatch: AppDispatch) => {
|
||||
const list = queryClient.getQueryData<Array<List>>(['lists'])?.find((list) => list.id === listId);
|
||||
if (!list) return;
|
||||
|
||||
dispatch<ListEditorSetupAction>({
|
||||
type: LIST_EDITOR_SETUP,
|
||||
list,
|
||||
});
|
||||
};
|
||||
|
||||
const changeListEditorTitle = (value: string) => ({
|
||||
type: LIST_EDITOR_TITLE_CHANGE,
|
||||
value,
|
||||
});
|
||||
|
||||
const changeListEditorRepliesPolicy = (repliesPolicy: List['replies_policy']) => ({
|
||||
type: LIST_EDITOR_REPLIES_POLICY_CHANGE,
|
||||
repliesPolicy,
|
||||
});
|
||||
|
||||
const changeListEditorExclusive = (exclusive: boolean) => ({
|
||||
type: LIST_EDITOR_EXCLUSIVE_CHANGE,
|
||||
exclusive,
|
||||
});
|
||||
|
||||
const resetListEditor = () => ({
|
||||
type: LIST_EDITOR_RESET,
|
||||
});
|
||||
|
||||
type ListsAction =
|
||||
| ListEditorSetupAction
|
||||
| ReturnType<typeof changeListEditorTitle>
|
||||
| ReturnType<typeof changeListEditorRepliesPolicy>
|
||||
| ReturnType<typeof changeListEditorExclusive>
|
||||
| ReturnType<typeof resetListEditor>;
|
||||
|
||||
export {
|
||||
LIST_EDITOR_TITLE_CHANGE,
|
||||
LIST_EDITOR_REPLIES_POLICY_CHANGE,
|
||||
LIST_EDITOR_EXCLUSIVE_CHANGE,
|
||||
LIST_EDITOR_RESET,
|
||||
LIST_EDITOR_SETUP,
|
||||
setupListEditor,
|
||||
changeListEditorTitle,
|
||||
changeListEditorRepliesPolicy,
|
||||
changeListEditorExclusive,
|
||||
resetListEditor,
|
||||
type ListsAction,
|
||||
};
|
||||
@ -8,6 +8,6 @@ import { useAppSelector } from './use-app-selector';
|
||||
(window as any).plapi = plapi;
|
||||
|
||||
/** Get features for the current instance. */
|
||||
const useFeatures = (): Features => useAppSelector(state => state.auth.client.features);
|
||||
const useFeatures = (): Features => ({ ...useAppSelector(state => state.auth.client.features), filtersV2BlurAction: true });
|
||||
|
||||
export { useFeatures };
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import { changeListEditorExclusive, changeListEditorRepliesPolicy, changeListEditorTitle } from 'pl-fe/actions/lists';
|
||||
import List, { ListItem } from 'pl-fe/components/list';
|
||||
import Button from 'pl-fe/components/ui/button';
|
||||
import Form from 'pl-fe/components/ui/form';
|
||||
@ -10,10 +9,8 @@ import FormGroup from 'pl-fe/components/ui/form-group';
|
||||
import Input from 'pl-fe/components/ui/input';
|
||||
import Toggle from 'pl-fe/components/ui/toggle';
|
||||
import { SelectDropdown } from 'pl-fe/features/forms';
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
|
||||
import { useFeatures } from 'pl-fe/hooks/use-features';
|
||||
import { useUpdateList } from 'pl-fe/queries/accounts/use-lists';
|
||||
import { useList, useUpdateList } from 'pl-fe/queries/accounts/use-lists';
|
||||
|
||||
const messages = defineMessages({
|
||||
save: { id: 'lists.new.save', defaultMessage: 'Save list' },
|
||||
@ -23,39 +20,43 @@ const messages = defineMessages({
|
||||
});
|
||||
|
||||
interface IListForm {
|
||||
listId: string;
|
||||
onTabChange: (tab: 'members') => void;
|
||||
}
|
||||
|
||||
const ListForm: React.FC<IListForm> = ({
|
||||
listId,
|
||||
onTabChange,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
const features = useFeatures();
|
||||
|
||||
const { title: value, listId, repliesPolicy, exclusive } = useAppSelector((state) => state.listEditor);
|
||||
|
||||
const { data: list } = useList(listId);
|
||||
const { mutate: updateList, isPending: disabled } = useUpdateList(listId!);
|
||||
|
||||
const [title, setTitle] = useState(list!.title);
|
||||
const [repliesPolicy, setRepliesPolicy] = useState(list!.replies_policy);
|
||||
const [exclusive, setExclusive] = useState(list!.exclusive);
|
||||
|
||||
const handleChange: React.ChangeEventHandler<HTMLInputElement> = e => {
|
||||
dispatch(changeListEditorTitle(e.target.value));
|
||||
setTitle(e.target.value);
|
||||
};
|
||||
|
||||
const handleSubmit: React.FormEventHandler<Element> = e => {
|
||||
e.preventDefault();
|
||||
updateList({ title: value, replies_policy: repliesPolicy, exclusive });
|
||||
handleUpdate();
|
||||
};
|
||||
|
||||
const handleClick = () => {
|
||||
updateList({ title: value, replies_policy: repliesPolicy, exclusive });
|
||||
const handleUpdate = () => {
|
||||
updateList({ title, replies_policy: repliesPolicy, exclusive });
|
||||
};
|
||||
|
||||
const handleChangeRepliesPolicy = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
dispatch(changeListEditorRepliesPolicy(e.target.value as 'none'));
|
||||
setRepliesPolicy(e.target.value as 'none');
|
||||
};
|
||||
|
||||
const handleChangeExclusive = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch(changeListEditorExclusive(e.target.checked));
|
||||
setExclusive(e.target.checked);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -66,7 +67,7 @@ const ListForm: React.FC<IListForm> = ({
|
||||
<Input
|
||||
outerClassName='grow'
|
||||
type='text'
|
||||
value={value}
|
||||
value={title}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
@ -109,7 +110,7 @@ const ListForm: React.FC<IListForm> = ({
|
||||
</List>
|
||||
|
||||
<FormActions>
|
||||
<Button onClick={handleClick} disabled={disabled}>
|
||||
<Button onClick={handleUpdate} disabled={disabled}>
|
||||
<FormattedMessage id='lists.edit.save' defaultMessage='Save list' />
|
||||
</Button>
|
||||
</FormActions>
|
||||
|
||||
@ -0,0 +1,55 @@
|
||||
import React, { useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { CardHeader, CardTitle } from 'pl-fe/components/ui/card';
|
||||
import { useListAccounts } from 'pl-fe/queries/accounts/use-lists';
|
||||
import { useAccountSearch } from 'pl-fe/queries/search/use-search-accounts';
|
||||
|
||||
import Account from './account';
|
||||
import Search from './search';
|
||||
|
||||
const messages = defineMessages({
|
||||
addToList: { id: 'lists.account.add', defaultMessage: 'Add to list' },
|
||||
removeFromList: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
|
||||
});
|
||||
|
||||
interface IListMembersForm {
|
||||
listId: string;
|
||||
}
|
||||
|
||||
const ListMembersForm: React.FC<IListMembersForm> = ({ listId }) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
|
||||
const { data: accountIds = [] } = useListAccounts(listId);
|
||||
const { data: searchAccountIds = [] } = useAccountSearch(searchValue, { following: true, limit: 5 });
|
||||
|
||||
return (
|
||||
<>
|
||||
{accountIds.length > 0 && (
|
||||
<>
|
||||
<div>
|
||||
<CardHeader>
|
||||
<CardTitle title={intl.formatMessage(messages.removeFromList)} />
|
||||
</CardHeader>
|
||||
<div className='max-h-48 overflow-y-auto'>
|
||||
{accountIds.map(accountId => <Account key={accountId} listId={listId} accountId={accountId} added={accountIds.includes(accountId)} />)}
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
</>
|
||||
)}
|
||||
|
||||
<CardHeader>
|
||||
<CardTitle title={intl.formatMessage(messages.addToList)} />
|
||||
</CardHeader>
|
||||
<Search value={searchValue} onSubmit={setSearchValue} />
|
||||
<div className='max-h-48 overflow-y-auto'>
|
||||
{searchAccountIds.map(accountId => <Account key={accountId} listId={listId} accountId={accountId} added={accountIds.includes(accountId)} />)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export { ListMembersForm as default };
|
||||
@ -1,46 +1,24 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
import React, { useState } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { setupListEditor, resetListEditor } from 'pl-fe/actions/lists';
|
||||
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 { useListAccounts } from 'pl-fe/queries/accounts/use-lists';
|
||||
import { useAccountSearch } from 'pl-fe/queries/search/use-search-accounts';
|
||||
import Spinner from 'pl-fe/components/ui/spinner';
|
||||
import { useList } from 'pl-fe/queries/accounts/use-lists';
|
||||
|
||||
import Account from './components/account';
|
||||
import EditListForm from './components/edit-list-form';
|
||||
import Search from './components/search';
|
||||
import ListMembersForm from './components/list-members-form';
|
||||
|
||||
import type { BaseModalProps } from 'pl-fe/features/ui/components/modal-root';
|
||||
|
||||
const messages = defineMessages({
|
||||
addToList: { id: 'lists.account.add', defaultMessage: 'Add to list' },
|
||||
removeFromList: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
|
||||
editList: { id: 'lists.edit', defaultMessage: 'Edit list' },
|
||||
});
|
||||
|
||||
interface ListEditorModalProps {
|
||||
listId: string;
|
||||
}
|
||||
|
||||
const ListEditorModal: React.FC<BaseModalProps & ListEditorModalProps> = ({ listId, onClose }) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const [tab, setTab] = useState<'info' | 'members'>('info');
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
|
||||
const { data: accountIds = [] } = useListAccounts(listId);
|
||||
const { data: searchAccountIds = [] } = useAccountSearch(searchValue, { following: true, limit: 5 });
|
||||
const { isFetched } = useList(listId);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(setupListEditor(listId));
|
||||
|
||||
return () => {
|
||||
dispatch(resetListEditor());
|
||||
};
|
||||
}, []);
|
||||
|
||||
const onClickClose = () => {
|
||||
onClose('LIST_EDITOR');
|
||||
@ -52,33 +30,10 @@ const ListEditorModal: React.FC<BaseModalProps & ListEditorModalProps> = ({ list
|
||||
onClose={onClickClose}
|
||||
onBack={tab === 'members' ? () => setTab('info') : undefined}
|
||||
>
|
||||
{tab === 'info'
|
||||
? <EditListForm onTabChange={setTab} />
|
||||
: (
|
||||
<>
|
||||
{accountIds.length > 0 && (
|
||||
<>
|
||||
<div>
|
||||
<CardHeader>
|
||||
<CardTitle title={intl.formatMessage(messages.removeFromList)} />
|
||||
</CardHeader>
|
||||
<div className='max-h-48 overflow-y-auto'>
|
||||
{accountIds.map(accountId => <Account key={accountId} listId={listId} accountId={accountId} added={accountIds.includes(accountId)} />)}
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
</>
|
||||
)}
|
||||
|
||||
<CardHeader>
|
||||
<CardTitle title={intl.formatMessage(messages.addToList)} />
|
||||
</CardHeader>
|
||||
<Search value={searchValue} onSubmit={setSearchValue} />
|
||||
<div className='max-h-48 overflow-y-auto'>
|
||||
{searchAccountIds.map(accountId => <Account key={accountId} listId={listId} accountId={accountId} added={accountIds.includes(accountId)} />)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{isFetched ? (tab === 'info'
|
||||
? <EditListForm listId={listId} onTabChange={setTab} />
|
||||
: <ListMembersForm listId={listId} />
|
||||
) : <Spinner />}
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
@ -13,7 +13,6 @@ import conversations from './conversations';
|
||||
import draft_statuses from './draft-statuses';
|
||||
import filters from './filters';
|
||||
import instance from './instance';
|
||||
import listEditor from './list-editor';
|
||||
import me from './me';
|
||||
import meta from './meta';
|
||||
import notifications from './notifications';
|
||||
@ -38,7 +37,6 @@ const reducers = {
|
||||
entities,
|
||||
filters,
|
||||
instance,
|
||||
listEditor,
|
||||
me,
|
||||
meta,
|
||||
notifications,
|
||||
|
||||
@ -1,60 +0,0 @@
|
||||
import { create } from 'mutative';
|
||||
|
||||
import {
|
||||
LIST_EDITOR_RESET,
|
||||
LIST_EDITOR_SETUP,
|
||||
LIST_EDITOR_TITLE_CHANGE,
|
||||
LIST_EDITOR_EXCLUSIVE_CHANGE,
|
||||
LIST_EDITOR_REPLIES_POLICY_CHANGE,
|
||||
type ListsAction,
|
||||
} from '../actions/lists';
|
||||
|
||||
import type { List } from 'pl-api';
|
||||
|
||||
|
||||
interface State {
|
||||
listId: string | null;
|
||||
isSubmitting: boolean;
|
||||
title: string;
|
||||
repliesPolicy: List['replies_policy'];
|
||||
exclusive?: boolean;
|
||||
}
|
||||
|
||||
const initialState: State = {
|
||||
listId: null,
|
||||
isSubmitting: false,
|
||||
title: '',
|
||||
repliesPolicy: undefined,
|
||||
exclusive: false,
|
||||
};
|
||||
|
||||
const listEditorReducer = (state: State = initialState, action: ListsAction): State => {
|
||||
switch (action.type) {
|
||||
case LIST_EDITOR_RESET:
|
||||
return initialState;
|
||||
case LIST_EDITOR_SETUP:
|
||||
return create(state, (draft) => {
|
||||
draft.listId = action.list.id;
|
||||
draft.title = action.list.title;
|
||||
draft.repliesPolicy = action.list.replies_policy;
|
||||
draft.exclusive = action.list.exclusive;
|
||||
draft.isSubmitting = false;
|
||||
});
|
||||
case LIST_EDITOR_TITLE_CHANGE:
|
||||
return create(state, (draft) => {
|
||||
draft.title = action.value;
|
||||
});
|
||||
case LIST_EDITOR_EXCLUSIVE_CHANGE:
|
||||
return create(state, (draft) => {
|
||||
draft.exclusive = action.exclusive;
|
||||
});
|
||||
case LIST_EDITOR_REPLIES_POLICY_CHANGE:
|
||||
return create(state, (draft) => {
|
||||
draft.repliesPolicy = action.repliesPolicy;
|
||||
});
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export { listEditorReducer as default };
|
||||
Reference in New Issue
Block a user