diff --git a/packages/pl-fe/src/actions/favourites.ts b/packages/pl-fe/src/actions/favourites.ts deleted file mode 100644 index 81f9f9dd8..000000000 --- a/packages/pl-fe/src/actions/favourites.ts +++ /dev/null @@ -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, next: (() => Promise>) | 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, next: (() => Promise>) | 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, next: (() => Promise>) | 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, next: (() => Promise>) | 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 - | ReturnType - | ReturnType - | ReturnType - | ReturnType - | ReturnType - | ReturnType - | ReturnType - | ReturnType - | ReturnType - | ReturnType - | ReturnType; - -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, -}; diff --git a/packages/pl-fe/src/pages/status-lists/favourited-statuses.tsx b/packages/pl-fe/src/pages/status-lists/favourited-statuses.tsx index 4b1cae6f6..a1b4f67d1 100644 --- a/packages/pl-fe/src/pages/status-lists/favourited-statuses.tsx +++ b/packages/pl-fe/src/pages/status-lists/favourited-statuses.tsx @@ -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 = ({ 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 = ({ params }) => { fetchNextPage({ cancelRefetch: false })} emptyMessage={emptyMessage} /> diff --git a/packages/pl-fe/src/queries/status-lists/use-favourites.ts b/packages/pl-fe/src/queries/status-lists/use-favourites.ts new file mode 100644 index 000000000..a44f4a8e0 --- /dev/null +++ b/packages/pl-fe/src/queries/status-lists/use-favourites.ts @@ -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 }; diff --git a/packages/pl-fe/src/reducers/status-lists.test.ts b/packages/pl-fe/src/reducers/status-lists.test.ts index e87c42e76..d3b6aa2af 100644 --- a/packages/pl-fe/src/reducers/status-lists.test.ts +++ b/packages/pl-fe/src/reducers/status-lists.test.ts @@ -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: [], - }, }); }); }); diff --git a/packages/pl-fe/src/reducers/status-lists.ts b/packages/pl-fe/src/reducers/status-lists.ts index 55b6bec4b..e7ef542f5 100644 --- a/packages/pl-fe/src/reducers/status-lists.ts +++ b/packages/pl-fe/src/reducers/status-lists.ts @@ -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>) | null; @@ -50,11 +34,6 @@ const getStatusId = (status: string | Pick) => typeof status === ' const getStatusIds = (statuses: Array> = []) => 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>, next: (() => Promise>) | null) => { const list = state[listType] = state[listType] || newStatusList(); @@ -64,16 +43,6 @@ const normalizeList = (state: State, listType: string, statuses: Array>, next: (() => Promise>) | 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) => { 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: