pl-fe: migrate lists to tanstack query

Signed-off-by: Nicole Mikołajczyk <git@mkljczk.pl>
This commit is contained in:
Nicole Mikołajczyk
2025-04-21 23:30:58 +02:00
parent 024160d00a
commit bfef2fadd7
12 changed files with 96 additions and 263 deletions

View File

@ -1,15 +1,14 @@
import React, { useEffect, useMemo } from 'react';
import React, { useMemo } from 'react';
import { useIntl, defineMessages, IntlShape } from 'react-intl';
import { changeComposeFederated, changeComposeVisibility } from 'pl-fe/actions/compose';
import { fetchLists } from 'pl-fe/actions/lists';
import DropdownMenu, { MenuItem } from 'pl-fe/components/dropdown-menu';
import Button from 'pl-fe/components/ui/button';
import { getOrderedLists } from 'pl-fe/features/lists';
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
import { useCompose } from 'pl-fe/hooks/use-compose';
import { useFeatures } from 'pl-fe/hooks/use-features';
import { useLists } from 'pl-fe/queries/accounts/use-lists';
import type { Features } from 'pl-api';
@ -111,7 +110,7 @@ const PrivacyDropdown: React.FC<IPrivacyDropdown> = ({
const dispatch = useAppDispatch();
const compose = useCompose(composeId);
const lists = useAppSelector((state) => getOrderedLists(state));
const { data: lists = [] } = useLists(getOrderedLists);
const value = compose.privacy;
const unavailable = compose.id;
@ -131,10 +130,6 @@ const PrivacyDropdown: React.FC<IPrivacyDropdown> = ({
})),
}));
useEffect(() => {
if (features.addressableLists) dispatch(fetchLists());
}, []);
if (features.localOnlyStatuses) items.push({
icon: require('@tabler/icons/outline/affiliate.svg'),
text: intl.formatMessage(messages.local_short),

View File

@ -2,7 +2,6 @@ import React, { useEffect } from 'react';
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
import { useParams } from 'react-router-dom';
import { deleteList, fetchList } from 'pl-fe/actions/lists';
import { fetchListTimeline } from 'pl-fe/actions/timelines';
import { useListStream } from 'pl-fe/api/hooks/streaming/use-list-stream';
import DropdownMenu from 'pl-fe/components/dropdown-menu';
@ -11,9 +10,9 @@ import Button from 'pl-fe/components/ui/button';
import Column from 'pl-fe/components/ui/column';
import Spinner from 'pl-fe/components/ui/spinner';
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
import { useIsMobile } from 'pl-fe/hooks/use-is-mobile';
import { useTheme } from 'pl-fe/hooks/use-theme';
import { useDeleteList, useList } from 'pl-fe/queries/accounts/use-lists';
import { useModalsStore } from 'pl-fe/stores/modals';
import Timeline from '../ui/components/timeline';
@ -34,12 +33,12 @@ const ListTimeline: React.FC = () => {
const isMobile = useIsMobile();
const { openModal } = useModalsStore();
const list = useAppSelector((state) => state.lists[id]);
const { data: list, isFetching } = useList(id);
const { mutate: deleteList } = useDeleteList();
useListStream(id);
useEffect(() => {
dispatch(fetchList(id));
dispatch(fetchListTimeline(id));
}, [id]);
@ -59,14 +58,14 @@ const ListTimeline: React.FC = () => {
message: intl.formatMessage(messages.deleteMessage),
confirm: intl.formatMessage(messages.deleteConfirm),
onConfirm: () => {
dispatch(deleteList(id));
deleteList(id);
},
});
};
const title = list ? list.title : id;
if (typeof list === 'undefined') {
if (!list && isFetching) {
return (
<Column>
<div>
@ -74,7 +73,7 @@ const ListTimeline: React.FC = () => {
</div>
</Column>
);
} else if (list === false) {
} else if (!list) {
return (
<MissingIndicator />
);

View File

@ -1,13 +1,14 @@
import React from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { changeListEditorTitle, submitListEditor } from 'pl-fe/actions/lists';
import { changeListEditorTitle } from 'pl-fe/actions/lists';
import Button from 'pl-fe/components/ui/button';
import Form from 'pl-fe/components/ui/form';
import HStack from 'pl-fe/components/ui/hstack';
import Input from 'pl-fe/components/ui/input';
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
import { useCreateList } from 'pl-fe/queries/accounts/use-lists';
const messages = defineMessages({
label: { id: 'lists.new.title_placeholder', defaultMessage: 'New list title' },
@ -19,8 +20,9 @@ const NewListForm: React.FC = () => {
const dispatch = useAppDispatch();
const intl = useIntl();
const value = useAppSelector((state) => state.listEditor.title);
const disabled = useAppSelector((state) => !!state.listEditor.isSubmitting);
const { title: value, isSubmitting: disabled } = useAppSelector((state) => state.listEditor);
const { mutate: createList } = useCreateList();
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
dispatch(changeListEditorTitle(e.target.value));
@ -28,7 +30,7 @@ const NewListForm: React.FC = () => {
const handleSubmit = (e: React.FormEvent<Element>) => {
e.preventDefault();
dispatch(submitListEditor(true));
createList({ title: value });
};
const label = intl.formatMessage(messages.label);

View File

@ -1,8 +1,6 @@
import React, { useEffect } from 'react';
import React from 'react';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import { createSelector } from 'reselect';
import { fetchLists } from 'pl-fe/actions/lists';
import List, { ListItem } from 'pl-fe/components/list';
import Card from 'pl-fe/components/ui/card';
import Column from 'pl-fe/components/ui/column';
@ -10,36 +8,29 @@ import HStack from 'pl-fe/components/ui/hstack';
import Icon from 'pl-fe/components/ui/icon';
import Spinner from 'pl-fe/components/ui/spinner';
import Stack from 'pl-fe/components/ui/stack';
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
import { useLists } from 'pl-fe/queries/accounts/use-lists';
import NewListForm from './components/new-list-form';
import type { List as ListEntity } from 'pl-api';
import type { RootState } from 'pl-fe/store';
const messages = defineMessages({
heading: { id: 'column.lists', defaultMessage: 'Lists' },
subheading: { id: 'lists.subheading', defaultMessage: 'Your lists' },
});
const getOrderedLists = createSelector([(state: RootState) => state.lists], lists => {
const getOrderedLists = (lists: Array<ListEntity>) => {
if (!lists) {
return lists;
}
return Object.values(lists).filter((item): item is ListEntity => !!item).sort((a, b) => a.title.localeCompare(b.title));
});
};
const Lists: React.FC = () => {
const dispatch = useAppDispatch();
const intl = useIntl();
const lists = useAppSelector((state) => getOrderedLists(state));
useEffect(() => {
dispatch(fetchLists());
}, []);
const { data: lists } = useLists(getOrderedLists);
if (!lists) {
return (

View File

@ -6,6 +6,7 @@ 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';
const messages = defineMessages({
remove: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
@ -20,7 +21,7 @@ const List: React.FC<IList> = ({ listId }) => {
const intl = useIntl();
const dispatch = useAppDispatch();
const list = useAppSelector((state) => state.lists[listId]);
const { data: list } = useList(listId);
const added = useAppSelector((state) => state.listAdder.lists.items.includes(listId));
const onRemove = () => dispatch(removeFromListAdder(listId));

View File

@ -8,7 +8,7 @@ import AccountContainer from 'pl-fe/containers/account-container';
import { getOrderedLists } from 'pl-fe/features/lists';
import NewListForm from 'pl-fe/features/lists/components/new-list-form';
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
import { useLists } from 'pl-fe/queries/accounts/use-lists';
import List from './components/list';
@ -27,7 +27,7 @@ const ListAdderModal: React.FC<BaseModalProps & ListAdderModalProps> = ({ accoun
const intl = useIntl();
const dispatch = useAppDispatch();
const listIds = useAppSelector((state) => getOrderedLists(state).map(list => list.id));
const { data: listIds = [] } = useLists((lists) => getOrderedLists(lists).map(list => list.id));
useEffect(() => {
dispatch(setupListAdder(accountId));

View File

@ -1,13 +1,14 @@
import React from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { changeListEditorTitle, submitListEditor } from 'pl-fe/actions/lists';
import { changeListEditorTitle } from 'pl-fe/actions/lists';
import Button from 'pl-fe/components/ui/button';
import Form from 'pl-fe/components/ui/form';
import HStack from 'pl-fe/components/ui/hstack';
import Input from 'pl-fe/components/ui/input';
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
import { useUpdateList } from 'pl-fe/queries/accounts/use-lists';
const messages = defineMessages({
title: { id: 'lists.edit.submit', defaultMessage: 'Change title' },
@ -18,8 +19,9 @@ const ListForm = () => {
const intl = useIntl();
const dispatch = useAppDispatch();
const value = useAppSelector((state) => state.listEditor.title);
const disabled = useAppSelector((state) => !state.listEditor.isChanged);
const { title: value, listId } = useAppSelector((state) => state.listEditor);
const { mutate: updateList, isPending: disabled } = useUpdateList(listId!);
const handleChange: React.ChangeEventHandler<HTMLInputElement> = e => {
dispatch(changeListEditorTitle(e.target.value));
@ -27,11 +29,11 @@ const ListForm = () => {
const handleSubmit: React.FormEventHandler<Element> = e => {
e.preventDefault();
dispatch(submitListEditor(false));
updateList({ title: value });
};
const handleClick = () => {
dispatch(submitListEditor(false));
updateList({ title: value });
};
const save = intl.formatMessage(messages.save);