pl-fe: migrate favourites list to tanstack query
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
@ -1,200 +0,0 @@
|
||||
import { isLoggedIn } from 'pl-fe/utils/auth';
|
||||
|
||||
import { getClient } from '../api';
|
||||
|
||||
import { importEntities } from './importer';
|
||||
|
||||
import type { PaginatedResponse, Status } from 'pl-api';
|
||||
import type { AppDispatch, RootState } from 'pl-fe/store';
|
||||
|
||||
const FAVOURITED_STATUSES_FETCH_REQUEST = 'FAVOURITED_STATUSES_FETCH_REQUEST' as const;
|
||||
const FAVOURITED_STATUSES_FETCH_SUCCESS = 'FAVOURITED_STATUSES_FETCH_SUCCESS' as const;
|
||||
const FAVOURITED_STATUSES_FETCH_FAIL = 'FAVOURITED_STATUSES_FETCH_FAIL' as const;
|
||||
|
||||
const FAVOURITED_STATUSES_EXPAND_REQUEST = 'FAVOURITED_STATUSES_EXPAND_REQUEST' as const;
|
||||
const FAVOURITED_STATUSES_EXPAND_SUCCESS = 'FAVOURITED_STATUSES_EXPAND_SUCCESS' as const;
|
||||
const FAVOURITED_STATUSES_EXPAND_FAIL = 'FAVOURITED_STATUSES_EXPAND_FAIL' as const;
|
||||
|
||||
const ACCOUNT_FAVOURITED_STATUSES_FETCH_REQUEST = 'ACCOUNT_FAVOURITED_STATUSES_FETCH_REQUEST' as const;
|
||||
const ACCOUNT_FAVOURITED_STATUSES_FETCH_SUCCESS = 'ACCOUNT_FAVOURITED_STATUSES_FETCH_SUCCESS' as const;
|
||||
const ACCOUNT_FAVOURITED_STATUSES_FETCH_FAIL = 'ACCOUNT_FAVOURITED_STATUSES_FETCH_FAIL' as const;
|
||||
|
||||
const ACCOUNT_FAVOURITED_STATUSES_EXPAND_REQUEST = 'ACCOUNT_FAVOURITED_STATUSES_EXPAND_REQUEST' as const;
|
||||
const ACCOUNT_FAVOURITED_STATUSES_EXPAND_SUCCESS = 'ACCOUNT_FAVOURITED_STATUSES_EXPAND_SUCCESS' as const;
|
||||
const ACCOUNT_FAVOURITED_STATUSES_EXPAND_FAIL = 'ACCOUNT_FAVOURITED_STATUSES_EXPAND_FAIL' as const;
|
||||
|
||||
const fetchFavouritedStatuses = () =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (!isLoggedIn(getState)) return;
|
||||
|
||||
if (getState().status_lists.favourites?.isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(fetchFavouritedStatusesRequest());
|
||||
|
||||
return getClient(getState()).myAccount.getFavourites().then(response => {
|
||||
dispatch(importEntities({ statuses: response.items }));
|
||||
dispatch(fetchFavouritedStatusesSuccess(response.items, response.next));
|
||||
}).catch(error => {
|
||||
dispatch(fetchFavouritedStatusesFail(error));
|
||||
});
|
||||
};
|
||||
|
||||
const fetchFavouritedStatusesRequest = () => ({
|
||||
type: FAVOURITED_STATUSES_FETCH_REQUEST,
|
||||
});
|
||||
|
||||
const fetchFavouritedStatusesSuccess = (statuses: Array<Status>, next: (() => Promise<PaginatedResponse<Status>>) | null) => ({
|
||||
type: FAVOURITED_STATUSES_FETCH_SUCCESS,
|
||||
statuses,
|
||||
next,
|
||||
});
|
||||
|
||||
const fetchFavouritedStatusesFail = (error: unknown) => ({
|
||||
type: FAVOURITED_STATUSES_FETCH_FAIL,
|
||||
error,
|
||||
});
|
||||
|
||||
const expandFavouritedStatuses = () =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (!isLoggedIn(getState)) return;
|
||||
|
||||
const next = getState().status_lists.favourites?.next || null;
|
||||
|
||||
if (next === null || getState().status_lists.favourites?.isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(expandFavouritedStatusesRequest());
|
||||
|
||||
return next().then(response => {
|
||||
dispatch(importEntities({ statuses: response.items }));
|
||||
dispatch(expandFavouritedStatusesSuccess(response.items, response.next));
|
||||
}).catch(error => {
|
||||
dispatch(expandFavouritedStatusesFail(error));
|
||||
});
|
||||
};
|
||||
|
||||
const expandFavouritedStatusesRequest = () => ({
|
||||
type: FAVOURITED_STATUSES_EXPAND_REQUEST,
|
||||
});
|
||||
|
||||
const expandFavouritedStatusesSuccess = (statuses: Array<Status>, next: (() => Promise<PaginatedResponse<Status>>) | null) => ({
|
||||
type: FAVOURITED_STATUSES_EXPAND_SUCCESS,
|
||||
statuses,
|
||||
next,
|
||||
});
|
||||
|
||||
const expandFavouritedStatusesFail = (error: unknown) => ({
|
||||
type: FAVOURITED_STATUSES_EXPAND_FAIL,
|
||||
error,
|
||||
});
|
||||
|
||||
const fetchAccountFavouritedStatuses = (accountId: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (!isLoggedIn(getState)) return;
|
||||
|
||||
if (getState().status_lists[`favourites:${accountId}`]?.isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(fetchAccountFavouritedStatusesRequest(accountId));
|
||||
|
||||
return getClient(getState).accounts.getAccountFavourites(accountId).then(response => {
|
||||
dispatch(importEntities({ statuses: response.items }));
|
||||
dispatch(fetchAccountFavouritedStatusesSuccess(accountId, response.items, response.next));
|
||||
}).catch(error => {
|
||||
dispatch(fetchAccountFavouritedStatusesFail(accountId, error));
|
||||
});
|
||||
};
|
||||
|
||||
const fetchAccountFavouritedStatusesRequest = (accountId: string) => ({
|
||||
type: ACCOUNT_FAVOURITED_STATUSES_FETCH_REQUEST,
|
||||
accountId,
|
||||
});
|
||||
|
||||
const fetchAccountFavouritedStatusesSuccess = (accountId: string, statuses: Array<Status>, next: (() => Promise<PaginatedResponse<Status>>) | null) => ({
|
||||
type: ACCOUNT_FAVOURITED_STATUSES_FETCH_SUCCESS,
|
||||
accountId,
|
||||
statuses,
|
||||
next,
|
||||
});
|
||||
|
||||
const fetchAccountFavouritedStatusesFail = (accountId: string, error: unknown) => ({
|
||||
type: ACCOUNT_FAVOURITED_STATUSES_FETCH_FAIL,
|
||||
accountId,
|
||||
error,
|
||||
});
|
||||
|
||||
const expandAccountFavouritedStatuses = (accountId: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (!isLoggedIn(getState)) return;
|
||||
|
||||
const next = getState().status_lists[`favourites:${accountId}`]?.next || null;
|
||||
|
||||
if (next === null || getState().status_lists[`favourites:${accountId}`]?.isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(expandAccountFavouritedStatusesRequest(accountId));
|
||||
|
||||
return next().then(response => {
|
||||
dispatch(importEntities({ statuses: response.items }));
|
||||
dispatch(expandAccountFavouritedStatusesSuccess(accountId, response.items, response.next));
|
||||
}).catch(error => {
|
||||
dispatch(expandAccountFavouritedStatusesFail(accountId, error));
|
||||
});
|
||||
};
|
||||
|
||||
const expandAccountFavouritedStatusesRequest = (accountId: string) => ({
|
||||
type: ACCOUNT_FAVOURITED_STATUSES_EXPAND_REQUEST,
|
||||
accountId,
|
||||
});
|
||||
|
||||
const expandAccountFavouritedStatusesSuccess = (accountId: string, statuses: Array<Status>, next: (() => Promise<PaginatedResponse<Status>>) | null) => ({
|
||||
type: ACCOUNT_FAVOURITED_STATUSES_EXPAND_SUCCESS,
|
||||
accountId,
|
||||
statuses,
|
||||
next,
|
||||
});
|
||||
|
||||
const expandAccountFavouritedStatusesFail = (accountId: string, error: unknown) => ({
|
||||
type: ACCOUNT_FAVOURITED_STATUSES_EXPAND_FAIL,
|
||||
accountId,
|
||||
error,
|
||||
});
|
||||
|
||||
type FavouritesAction =
|
||||
ReturnType<typeof fetchFavouritedStatusesRequest>
|
||||
| ReturnType<typeof fetchFavouritedStatusesSuccess>
|
||||
| ReturnType<typeof fetchFavouritedStatusesFail>
|
||||
| ReturnType<typeof expandFavouritedStatusesRequest>
|
||||
| ReturnType<typeof expandFavouritedStatusesSuccess>
|
||||
| ReturnType<typeof expandFavouritedStatusesFail>
|
||||
| ReturnType<typeof fetchAccountFavouritedStatusesRequest>
|
||||
| ReturnType<typeof fetchAccountFavouritedStatusesSuccess>
|
||||
| ReturnType<typeof fetchAccountFavouritedStatusesFail>
|
||||
| ReturnType<typeof expandAccountFavouritedStatusesRequest>
|
||||
| ReturnType<typeof expandAccountFavouritedStatusesSuccess>
|
||||
| ReturnType<typeof expandAccountFavouritedStatusesFail>;
|
||||
|
||||
export {
|
||||
FAVOURITED_STATUSES_FETCH_REQUEST,
|
||||
FAVOURITED_STATUSES_FETCH_SUCCESS,
|
||||
FAVOURITED_STATUSES_FETCH_FAIL,
|
||||
FAVOURITED_STATUSES_EXPAND_REQUEST,
|
||||
FAVOURITED_STATUSES_EXPAND_SUCCESS,
|
||||
FAVOURITED_STATUSES_EXPAND_FAIL,
|
||||
ACCOUNT_FAVOURITED_STATUSES_FETCH_REQUEST,
|
||||
ACCOUNT_FAVOURITED_STATUSES_FETCH_SUCCESS,
|
||||
ACCOUNT_FAVOURITED_STATUSES_FETCH_FAIL,
|
||||
ACCOUNT_FAVOURITED_STATUSES_EXPAND_REQUEST,
|
||||
ACCOUNT_FAVOURITED_STATUSES_EXPAND_SUCCESS,
|
||||
ACCOUNT_FAVOURITED_STATUSES_EXPAND_FAIL,
|
||||
fetchFavouritedStatuses,
|
||||
expandFavouritedStatuses,
|
||||
fetchAccountFavouritedStatuses,
|
||||
expandAccountFavouritedStatuses,
|
||||
type FavouritesAction,
|
||||
};
|
||||
@ -1,16 +1,12 @@
|
||||
import debounce from 'lodash/debounce';
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import React from 'react';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import { fetchAccount, fetchAccountByUsername } from 'pl-fe/actions/accounts';
|
||||
import { fetchFavouritedStatuses, expandFavouritedStatuses, fetchAccountFavouritedStatuses, expandAccountFavouritedStatuses } from 'pl-fe/actions/favourites';
|
||||
import { useAccountLookup } from 'pl-fe/api/hooks/accounts/use-account-lookup';
|
||||
import MissingIndicator from 'pl-fe/components/missing-indicator';
|
||||
import StatusList from 'pl-fe/components/status-list';
|
||||
import Column from 'pl-fe/components/ui/column';
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
|
||||
import { useOwnAccount } from 'pl-fe/hooks/use-own-account';
|
||||
import { useFavourites } from 'pl-fe/queries/status-lists/use-favourites';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.favourited_statuses', defaultMessage: 'Liked posts' },
|
||||
@ -25,45 +21,14 @@ interface IFavourites {
|
||||
/** Timeline displaying a user's favourited statuses. */
|
||||
const FavouritedStatusesPage: React.FC<IFavourites> = ({ params }) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
const { account: ownAccount } = useOwnAccount();
|
||||
const { account, isUnavailable } = useAccountLookup(params?.username, { withRelationship: true });
|
||||
|
||||
const username = params?.username || '';
|
||||
const isOwnAccount = username.toLowerCase() === ownAccount?.acct?.toLowerCase();
|
||||
const accountId = isOwnAccount ? undefined : account?.id;
|
||||
|
||||
const timelineKey = isOwnAccount ? 'favourites' : `favourites:${account?.id}`;
|
||||
const statusIds = useAppSelector(state => state.status_lists[timelineKey]?.items || []);
|
||||
const isLoading = useAppSelector(state => state.status_lists[timelineKey]?.isLoading === true);
|
||||
const hasMore = useAppSelector(state => !!state.status_lists[timelineKey]?.next);
|
||||
|
||||
const handleLoadMore = useCallback(debounce(() => {
|
||||
if (isOwnAccount) {
|
||||
dispatch(expandFavouritedStatuses());
|
||||
} else if (account) {
|
||||
dispatch(expandAccountFavouritedStatuses(account.id));
|
||||
}
|
||||
}, 300, { leading: true }), [account?.id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOwnAccount)
|
||||
dispatch(fetchFavouritedStatuses());
|
||||
else {
|
||||
if (account) {
|
||||
dispatch(fetchAccount(account.id));
|
||||
dispatch(fetchAccountFavouritedStatuses(account.id));
|
||||
} else {
|
||||
dispatch(fetchAccountByUsername(username));
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (account && !isOwnAccount) {
|
||||
dispatch(fetchAccount(account.id));
|
||||
dispatch(fetchAccountFavouritedStatuses(account.id));
|
||||
}
|
||||
}, [account?.id]);
|
||||
const { data: statusIds = [], isFetching, hasNextPage, fetchNextPage } = useFavourites(accountId);
|
||||
|
||||
if (isUnavailable) {
|
||||
return (
|
||||
@ -89,10 +54,10 @@ const FavouritedStatusesPage: React.FC<IFavourites> = ({ params }) => {
|
||||
<Column label={intl.formatMessage(messages.heading)} withHeader={false} transparent>
|
||||
<StatusList
|
||||
statusIds={statusIds}
|
||||
scrollKey='favourited_statuses'
|
||||
hasMore={hasMore}
|
||||
isLoading={isLoading}
|
||||
onLoadMore={handleLoadMore}
|
||||
scrollKey={`favourited_statuses:${account.id}`}
|
||||
hasMore={hasNextPage}
|
||||
isLoading={isFetching}
|
||||
onLoadMore={() => fetchNextPage({ cancelRefetch: false })}
|
||||
emptyMessage={emptyMessage}
|
||||
/>
|
||||
</Column>
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
import { makePaginatedResponseQuery } from 'pl-fe/queries/utils/make-paginated-response-query';
|
||||
import { minifyStatusList } from 'pl-fe/queries/utils/minify-list';
|
||||
|
||||
const useFavourites = makePaginatedResponseQuery(
|
||||
(accountId?: string) => ['statusLists', 'favourites', accountId],
|
||||
(client, [accountId]) => (accountId ? client.accounts.getAccountFavourites(accountId) : client.myAccount.getFavourites()).then(minifyStatusList),
|
||||
);
|
||||
|
||||
export { useFavourites };
|
||||
@ -3,42 +3,12 @@ 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: [],
|
||||
},
|
||||
joined_events: {
|
||||
next: null,
|
||||
loaded: false,
|
||||
isLoading: null,
|
||||
items: [],
|
||||
},
|
||||
recent_events: {
|
||||
next: null,
|
||||
loaded: false,
|
||||
isLoading: null,
|
||||
items: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,20 +1,5 @@
|
||||
import { create } from 'mutative';
|
||||
|
||||
import {
|
||||
FAVOURITED_STATUSES_FETCH_REQUEST,
|
||||
FAVOURITED_STATUSES_FETCH_SUCCESS,
|
||||
FAVOURITED_STATUSES_FETCH_FAIL,
|
||||
FAVOURITED_STATUSES_EXPAND_REQUEST,
|
||||
FAVOURITED_STATUSES_EXPAND_SUCCESS,
|
||||
FAVOURITED_STATUSES_EXPAND_FAIL,
|
||||
ACCOUNT_FAVOURITED_STATUSES_FETCH_REQUEST,
|
||||
ACCOUNT_FAVOURITED_STATUSES_FETCH_SUCCESS,
|
||||
ACCOUNT_FAVOURITED_STATUSES_FETCH_FAIL,
|
||||
ACCOUNT_FAVOURITED_STATUSES_EXPAND_REQUEST,
|
||||
ACCOUNT_FAVOURITED_STATUSES_EXPAND_SUCCESS,
|
||||
ACCOUNT_FAVOURITED_STATUSES_EXPAND_FAIL,
|
||||
type FavouritesAction,
|
||||
} from 'pl-fe/actions/favourites';
|
||||
import {
|
||||
PIN_SUCCESS,
|
||||
UNPIN_SUCCESS,
|
||||
@ -23,7 +8,6 @@ import {
|
||||
import { PINNED_STATUSES_FETCH_SUCCESS, type PinStatusesAction } from 'pl-fe/actions/pin-statuses';
|
||||
|
||||
import type { PaginatedResponse, Status } from 'pl-api';
|
||||
import type { StatusesAction } from 'pl-fe/actions/statuses';
|
||||
|
||||
interface StatusList {
|
||||
next: (() => Promise<PaginatedResponse<Status>>) | null;
|
||||
@ -50,11 +34,6 @@ const getStatusId = (status: string | Pick<Status, 'id'>) => typeof status === '
|
||||
|
||||
const getStatusIds = (statuses: Array<string | Pick<Status, 'id'>> = []) => statuses.map(getStatusId);
|
||||
|
||||
const setLoading = (state: State, listType: string, loading: boolean) => {
|
||||
const list = state[listType] = state[listType] || newStatusList();
|
||||
list.isLoading = loading;
|
||||
};
|
||||
|
||||
const normalizeList = (state: State, listType: string, statuses: Array<string | Pick<Status, 'id'>>, next: (() => Promise<PaginatedResponse<Status>>) | null) => {
|
||||
const list = state[listType] = state[listType] || newStatusList();
|
||||
|
||||
@ -64,16 +43,6 @@ const normalizeList = (state: State, listType: string, statuses: Array<string |
|
||||
list.items = getStatusIds(statuses);
|
||||
};
|
||||
|
||||
const appendToList = (state: State, listType: string, statuses: Array<string | Pick<Status, 'id'>>, next: (() => Promise<PaginatedResponse<Status>>) | null) => {
|
||||
const newIds = getStatusIds(statuses);
|
||||
|
||||
const list = state[listType] = state[listType] || newStatusList();
|
||||
|
||||
list.next = next;
|
||||
list.isLoading = false;
|
||||
list.items = [...new Set([...list.items, ...newIds])];
|
||||
};
|
||||
|
||||
const prependOneToList = (state: State, listType: string, status: string | Pick<Status, 'id'>) => {
|
||||
const statusId = getStatusId(status);
|
||||
const list = state[listType] = state[listType] || newStatusList();
|
||||
@ -88,28 +57,8 @@ const removeOneFromList = (state: State, listType: string, status: string | Pick
|
||||
list.items = list.items.filter(id => id !== statusId);
|
||||
};
|
||||
|
||||
const statusLists = (state = initialState, action: FavouritesAction | InteractionsAction | PinStatusesAction | StatusesAction): State => {
|
||||
const statusLists = (state = initialState, action: InteractionsAction | PinStatusesAction): State => {
|
||||
switch (action.type) {
|
||||
case FAVOURITED_STATUSES_FETCH_REQUEST:
|
||||
case FAVOURITED_STATUSES_EXPAND_REQUEST:
|
||||
return create(state, draft => setLoading(draft, 'favourites', true));
|
||||
case FAVOURITED_STATUSES_FETCH_FAIL:
|
||||
case FAVOURITED_STATUSES_EXPAND_FAIL:
|
||||
return create(state, draft => setLoading(draft, 'favourites', false));
|
||||
case FAVOURITED_STATUSES_FETCH_SUCCESS:
|
||||
return create(state, draft => normalizeList(draft, 'favourites', action.statuses, action.next));
|
||||
case FAVOURITED_STATUSES_EXPAND_SUCCESS:
|
||||
return create(state, draft => appendToList(draft, 'favourites', action.statuses, action.next));
|
||||
case ACCOUNT_FAVOURITED_STATUSES_FETCH_REQUEST:
|
||||
case ACCOUNT_FAVOURITED_STATUSES_EXPAND_REQUEST:
|
||||
return create(state, draft => setLoading(draft, `favourites:${action.accountId}`, true));
|
||||
case ACCOUNT_FAVOURITED_STATUSES_FETCH_FAIL:
|
||||
case ACCOUNT_FAVOURITED_STATUSES_EXPAND_FAIL:
|
||||
return create(state, draft => setLoading(draft, `favourites:${action.accountId}`, false));
|
||||
case ACCOUNT_FAVOURITED_STATUSES_FETCH_SUCCESS:
|
||||
return create(state, draft => normalizeList(draft, `favourites:${action.accountId}`, action.statuses, action.next));
|
||||
case ACCOUNT_FAVOURITED_STATUSES_EXPAND_SUCCESS:
|
||||
return create(state, draft => appendToList(draft, `favourites:${action.accountId}`, action.statuses, action.next));
|
||||
case PINNED_STATUSES_FETCH_SUCCESS:
|
||||
return create(state, draft => normalizeList(draft, 'pins', action.statuses, action.next));
|
||||
case PIN_SUCCESS:
|
||||
|
||||
Reference in New Issue
Block a user