pl-fe: migrate status interaction lists to tanstack query
Signed-off-by: mkljczk <git@mkljczk.pl>
This commit is contained in:
parent
fac30c2ea9
commit
16ea9f13e2
10 changed files with 83 additions and 356 deletions
|
@ -2214,11 +2214,8 @@ class PlApiClient {
|
|||
* Requires features{@link Features['statusDislikes']}.
|
||||
* @see {@link https://github.com/friendica/friendica/blob/2024.06-rc/doc/API-Friendica.md#get-apifriendicastatusesiddisliked_by}
|
||||
*/
|
||||
getDislikedBy: async (statusId: string) => {
|
||||
const response = await this.request(`/api/friendica/statuses/${statusId}/disliked_by`);
|
||||
|
||||
return v.parse(filteredArray(accountSchema), response.json);
|
||||
},
|
||||
getDislikedBy: async (statusId: string) =>
|
||||
this.#paginatedGet(`/api/v1/statuses/${statusId}/disliked_by`, {}, accountSchema),
|
||||
|
||||
/**
|
||||
* Marks the given status as disliked by this user
|
||||
|
|
|
@ -343,7 +343,6 @@ const submitCompose = (composeId: string, opts: SubmitComposeOpts = {}) =>
|
|||
|
||||
const compose = state.compose[composeId]!;
|
||||
|
||||
|
||||
const status = compose.text;
|
||||
const media = compose.media_attachments;
|
||||
const statusId = compose.id;
|
||||
|
@ -782,7 +781,6 @@ const deleteComposeLanguage = (composeId: string, value: Language) => ({
|
|||
value,
|
||||
});
|
||||
|
||||
|
||||
const addPoll = (composeId: string) => ({
|
||||
type: COMPOSE_POLL_ADD,
|
||||
composeId,
|
||||
|
|
|
@ -6,10 +6,9 @@ import { isLoggedIn } from 'pl-fe/utils/auth';
|
|||
|
||||
import { getClient } from '../api';
|
||||
|
||||
import { fetchRelationships } from './accounts';
|
||||
import { importEntities } from './importer';
|
||||
|
||||
import type { Account, EmojiReaction, PaginatedResponse, Status } from 'pl-api';
|
||||
import type { Status } from 'pl-api';
|
||||
import type { AppDispatch, RootState } from 'pl-fe/store';
|
||||
|
||||
const REBLOG_REQUEST = 'REBLOG_REQUEST' as const;
|
||||
|
@ -36,22 +35,6 @@ const UNDISLIKE_REQUEST = 'UNDISLIKE_REQUEST' as const;
|
|||
const UNDISLIKE_SUCCESS = 'UNDISLIKE_SUCCESS' as const;
|
||||
const UNDISLIKE_FAIL = 'UNDISLIKE_FAIL' as const;
|
||||
|
||||
const REBLOGS_FETCH_REQUEST = 'REBLOGS_FETCH_REQUEST' as const;
|
||||
const REBLOGS_FETCH_SUCCESS = 'REBLOGS_FETCH_SUCCESS' as const;
|
||||
const REBLOGS_FETCH_FAIL = 'REBLOGS_FETCH_FAIL' as const;
|
||||
|
||||
const FAVOURITES_FETCH_REQUEST = 'FAVOURITES_FETCH_REQUEST' as const;
|
||||
const FAVOURITES_FETCH_SUCCESS = 'FAVOURITES_FETCH_SUCCESS' as const;
|
||||
const FAVOURITES_FETCH_FAIL = 'FAVOURITES_FETCH_FAIL' as const;
|
||||
|
||||
const DISLIKES_FETCH_REQUEST = 'DISLIKES_FETCH_REQUEST' as const;
|
||||
const DISLIKES_FETCH_SUCCESS = 'DISLIKES_FETCH_SUCCESS' as const;
|
||||
const DISLIKES_FETCH_FAIL = 'DISLIKES_FETCH_FAIL' as const;
|
||||
|
||||
const REACTIONS_FETCH_REQUEST = 'REACTIONS_FETCH_REQUEST' as const;
|
||||
const REACTIONS_FETCH_SUCCESS = 'REACTIONS_FETCH_SUCCESS' as const;
|
||||
const REACTIONS_FETCH_FAIL = 'REACTIONS_FETCH_FAIL' as const;
|
||||
|
||||
const PIN_REQUEST = 'PIN_REQUEST' as const;
|
||||
const PIN_SUCCESS = 'PIN_SUCCESS' as const;
|
||||
const PIN_FAIL = 'PIN_FAIL' as const;
|
||||
|
@ -80,8 +63,6 @@ const REBLOGS_EXPAND_FAIL = 'REBLOGS_EXPAND_FAIL' as const;
|
|||
|
||||
const noOp = () => new Promise(f => f(undefined));
|
||||
|
||||
type AccountListLink = () => Promise<PaginatedResponse<Account>>;
|
||||
|
||||
const messages = defineMessages({
|
||||
bookmarkAdded: { id: 'status.bookmarked', defaultMessage: 'Bookmark added.' },
|
||||
bookmarkRemoved: { id: 'status.unbookmarked', defaultMessage: 'Bookmark removed.' },
|
||||
|
@ -386,175 +367,6 @@ const unbookmarkFail = (statusId: string, error: unknown) => ({
|
|||
error,
|
||||
});
|
||||
|
||||
const fetchReblogs = (statusId: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
dispatch(fetchReblogsRequest(statusId));
|
||||
|
||||
return getClient(getState()).statuses.getRebloggedBy(statusId).then(response => {
|
||||
dispatch(importEntities({ accounts: response.items }));
|
||||
dispatch(fetchRelationships(response.items.map((item) => item.id)));
|
||||
dispatch(fetchReblogsSuccess(statusId, response.items, response.next));
|
||||
}).catch(error => {
|
||||
dispatch(fetchReblogsFail(statusId, error));
|
||||
});
|
||||
};
|
||||
|
||||
const fetchReblogsRequest = (statusId: string) => ({
|
||||
type: REBLOGS_FETCH_REQUEST,
|
||||
statusId,
|
||||
});
|
||||
|
||||
const fetchReblogsSuccess = (statusId: string, accounts: Array<Account>, next: AccountListLink | null) => ({
|
||||
type: REBLOGS_FETCH_SUCCESS,
|
||||
statusId,
|
||||
accounts,
|
||||
next,
|
||||
});
|
||||
|
||||
const fetchReblogsFail = (statusId: string, error: unknown) => ({
|
||||
type: REBLOGS_FETCH_FAIL,
|
||||
statusId,
|
||||
error,
|
||||
});
|
||||
|
||||
const expandReblogs = (statusId: string, next: AccountListLink) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
next().then(response => {
|
||||
dispatch(importEntities({ accounts: response.items }));
|
||||
dispatch(fetchRelationships(response.items.map((item) => item.id)));
|
||||
dispatch(expandReblogsSuccess(statusId, response.items, response.next));
|
||||
}).catch(error => {
|
||||
dispatch(expandReblogsFail(statusId, error));
|
||||
});
|
||||
};
|
||||
|
||||
const expandReblogsSuccess = (statusId: string, accounts: Array<Account>, next: AccountListLink | null) => ({
|
||||
type: REBLOGS_EXPAND_SUCCESS,
|
||||
statusId,
|
||||
accounts,
|
||||
next,
|
||||
});
|
||||
|
||||
const expandReblogsFail = (statusId: string, error: unknown) => ({
|
||||
type: REBLOGS_EXPAND_FAIL,
|
||||
statusId,
|
||||
error,
|
||||
});
|
||||
|
||||
const fetchFavourites = (statusId: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
dispatch(fetchFavouritesRequest(statusId));
|
||||
|
||||
return getClient(getState()).statuses.getFavouritedBy(statusId).then(response => {
|
||||
dispatch(importEntities({ accounts: response.items }));
|
||||
dispatch(fetchRelationships(response.items.map((item) => item.id)));
|
||||
dispatch(fetchFavouritesSuccess(statusId, response.items, response.next));
|
||||
}).catch(error => {
|
||||
dispatch(fetchFavouritesFail(statusId, error));
|
||||
});
|
||||
};
|
||||
|
||||
const fetchFavouritesRequest = (statusId: string) => ({
|
||||
type: FAVOURITES_FETCH_REQUEST,
|
||||
statusId,
|
||||
});
|
||||
|
||||
const fetchFavouritesSuccess = (statusId: string, accounts: Array<Account>, next: AccountListLink | null) => ({
|
||||
type: FAVOURITES_FETCH_SUCCESS,
|
||||
statusId,
|
||||
accounts,
|
||||
next,
|
||||
});
|
||||
|
||||
const fetchFavouritesFail = (statusId: string, error: unknown) => ({
|
||||
type: FAVOURITES_FETCH_FAIL,
|
||||
statusId,
|
||||
error,
|
||||
});
|
||||
|
||||
const expandFavourites = (statusId: string, next: AccountListLink) =>
|
||||
(dispatch: AppDispatch) => {
|
||||
next().then(response => {
|
||||
dispatch(importEntities({ accounts: response.items }));
|
||||
dispatch(fetchRelationships(response.items.map((item) => item.id)));
|
||||
dispatch(expandFavouritesSuccess(statusId, response.items, response.next));
|
||||
}).catch(error => {
|
||||
dispatch(expandFavouritesFail(statusId, error));
|
||||
});
|
||||
};
|
||||
|
||||
const expandFavouritesSuccess = (statusId: string, accounts: Array<Account>, next: AccountListLink | null) => ({
|
||||
type: FAVOURITES_EXPAND_SUCCESS,
|
||||
statusId,
|
||||
accounts,
|
||||
next,
|
||||
});
|
||||
|
||||
const expandFavouritesFail = (statusId: string, error: unknown) => ({
|
||||
type: FAVOURITES_EXPAND_FAIL,
|
||||
statusId,
|
||||
error,
|
||||
});
|
||||
|
||||
const fetchDislikes = (statusId: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
dispatch(fetchDislikesRequest(statusId));
|
||||
|
||||
return getClient(getState).statuses.getDislikedBy(statusId).then(response => {
|
||||
dispatch(importEntities({ accounts: response }));
|
||||
dispatch(fetchRelationships(response.map((item) => item.id)));
|
||||
dispatch(fetchDislikesSuccess(statusId, response));
|
||||
}).catch(error => {
|
||||
dispatch(fetchDislikesFail(statusId, error));
|
||||
});
|
||||
};
|
||||
|
||||
const fetchDislikesRequest = (statusId: string) => ({
|
||||
type: DISLIKES_FETCH_REQUEST,
|
||||
statusId,
|
||||
});
|
||||
|
||||
const fetchDislikesSuccess = (statusId: string, accounts: Array<Account>) => ({
|
||||
type: DISLIKES_FETCH_SUCCESS,
|
||||
statusId,
|
||||
accounts,
|
||||
});
|
||||
|
||||
const fetchDislikesFail = (statusId: string, error: unknown) => ({
|
||||
type: DISLIKES_FETCH_FAIL,
|
||||
statusId,
|
||||
error,
|
||||
});
|
||||
|
||||
const fetchReactions = (statusId: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
dispatch(fetchReactionsRequest(statusId));
|
||||
|
||||
return getClient(getState).statuses.getStatusReactions(statusId).then(response => {
|
||||
dispatch(importEntities({ accounts: (response).map(({ accounts }) => accounts).flat() }));
|
||||
dispatch(fetchReactionsSuccess(statusId, response));
|
||||
}).catch(error => {
|
||||
dispatch(fetchReactionsFail(statusId, error));
|
||||
});
|
||||
};
|
||||
|
||||
const fetchReactionsRequest = (statusId: string) => ({
|
||||
type: REACTIONS_FETCH_REQUEST,
|
||||
statusId,
|
||||
});
|
||||
|
||||
const fetchReactionsSuccess = (statusId: string, reactions: EmojiReaction[]) => ({
|
||||
type: REACTIONS_FETCH_SUCCESS,
|
||||
statusId,
|
||||
reactions,
|
||||
});
|
||||
|
||||
const fetchReactionsFail = (statusId: string, error: unknown) => ({
|
||||
type: REACTIONS_FETCH_FAIL,
|
||||
statusId,
|
||||
error,
|
||||
});
|
||||
|
||||
const pin = (status: Pick<Status, 'id'>, accountId: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (!isLoggedIn(getState)) return;
|
||||
|
@ -695,22 +507,6 @@ type InteractionsAction =
|
|||
| ReturnType<typeof unbookmarkRequest>
|
||||
| ReturnType<typeof unbookmarkSuccess>
|
||||
| ReturnType<typeof unbookmarkFail>
|
||||
| ReturnType<typeof fetchReblogsRequest>
|
||||
| ReturnType<typeof fetchReblogsSuccess>
|
||||
| ReturnType<typeof fetchReblogsFail>
|
||||
| ReturnType<typeof expandReblogsSuccess>
|
||||
| ReturnType<typeof expandReblogsFail>
|
||||
| ReturnType<typeof fetchFavouritesRequest>
|
||||
| ReturnType<typeof fetchFavouritesSuccess>
|
||||
| ReturnType<typeof fetchFavouritesFail>
|
||||
| ReturnType<typeof expandFavouritesSuccess>
|
||||
| ReturnType<typeof expandFavouritesFail>
|
||||
| ReturnType<typeof fetchDislikesRequest>
|
||||
| ReturnType<typeof fetchDislikesSuccess>
|
||||
| ReturnType<typeof fetchDislikesFail>
|
||||
| ReturnType<typeof fetchReactionsRequest>
|
||||
| ReturnType<typeof fetchReactionsSuccess>
|
||||
| ReturnType<typeof fetchReactionsFail>
|
||||
| ReturnType<typeof pinRequest>
|
||||
| ReturnType<typeof pinSuccess>
|
||||
| ReturnType<typeof pinFail>
|
||||
|
@ -740,18 +536,6 @@ export {
|
|||
UNDISLIKE_REQUEST,
|
||||
UNDISLIKE_SUCCESS,
|
||||
UNDISLIKE_FAIL,
|
||||
REBLOGS_FETCH_REQUEST,
|
||||
REBLOGS_FETCH_SUCCESS,
|
||||
REBLOGS_FETCH_FAIL,
|
||||
FAVOURITES_FETCH_REQUEST,
|
||||
FAVOURITES_FETCH_SUCCESS,
|
||||
FAVOURITES_FETCH_FAIL,
|
||||
DISLIKES_FETCH_REQUEST,
|
||||
DISLIKES_FETCH_SUCCESS,
|
||||
DISLIKES_FETCH_FAIL,
|
||||
REACTIONS_FETCH_REQUEST,
|
||||
REACTIONS_FETCH_SUCCESS,
|
||||
REACTIONS_FETCH_FAIL,
|
||||
PIN_REQUEST,
|
||||
PIN_SUCCESS,
|
||||
PIN_FAIL,
|
||||
|
@ -783,12 +567,6 @@ export {
|
|||
bookmark,
|
||||
unbookmark,
|
||||
toggleBookmark,
|
||||
fetchReblogs,
|
||||
expandReblogs,
|
||||
fetchFavourites,
|
||||
expandFavourites,
|
||||
fetchDislikes,
|
||||
fetchReactions,
|
||||
pin,
|
||||
unpin,
|
||||
togglePin,
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
import { useInfiniteQuery, useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { importEntities } from 'pl-fe/actions/importer';
|
||||
import { minifyAccountList } from 'pl-fe/api/normalizers/minify-list';
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
import { useClient } from 'pl-fe/hooks/use-client';
|
||||
|
||||
import type { PaginatedResponse } from 'pl-api';
|
||||
|
||||
const useStatusInteractions = (statusId: string, method: 'getDislikedBy' | 'getFavouritedBy' | 'getRebloggedBy') => {
|
||||
const client = useClient();
|
||||
|
||||
const queryKey = {
|
||||
getDislikedBy: 'statusDislikes',
|
||||
getFavouritedBy: 'statusFavourites',
|
||||
getRebloggedBy: 'statusReblogs',
|
||||
}[method];
|
||||
|
||||
return useInfiniteQuery({
|
||||
queryKey: ['accountsLists', queryKey, statusId],
|
||||
queryFn: ({ pageParam }) => pageParam.next?.() || client.statuses[method](statusId).then(minifyAccountList),
|
||||
initialPageParam: { previous: null, next: null, items: [], partial: false } as PaginatedResponse<string>,
|
||||
getNextPageParam: (page) => page.next ? page : undefined,
|
||||
select: (data) => data.pages.map(page => page.items).flat(),
|
||||
});
|
||||
};
|
||||
|
||||
const useStatusDislikes = (statusId: string) => useStatusInteractions(statusId, 'getDislikedBy');
|
||||
const useStatusFavourites = (statusId: string) => useStatusInteractions(statusId, 'getFavouritedBy');
|
||||
const useStatusReblogs = (statusId: string) => useStatusInteractions(statusId, 'getRebloggedBy');
|
||||
|
||||
const useStatusReactions = (statusId: string, emoji?: string) => {
|
||||
const client = useClient();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
return useQuery({
|
||||
queryKey: ['accountsLists', 'statusReactions', statusId, emoji],
|
||||
queryFn: () => client.statuses.getStatusReactions(statusId, emoji).then((reactions) => {
|
||||
dispatch(importEntities({ accounts: reactions.map(({ accounts }) => accounts).flat() }));
|
||||
|
||||
return reactions.map(({ accounts, ...reactions }) => reactions);
|
||||
}),
|
||||
placeholderData: (previousData) => previousData?.filter(({ name }) => name === emoji),
|
||||
});
|
||||
};
|
||||
|
||||
export { useStatusDislikes, useStatusFavourites, useStatusReactions, useStatusReblogs };
|
|
@ -1,13 +1,11 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import React, { useRef } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { fetchDislikes } from 'pl-fe/actions/interactions';
|
||||
import { useStatusDislikes } from 'pl-fe/api/hooks/account-lists/use-status-interactions';
|
||||
import ScrollableList from 'pl-fe/components/scrollable-list';
|
||||
import Modal from 'pl-fe/components/ui/modal';
|
||||
import Spinner from 'pl-fe/components/ui/spinner';
|
||||
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 type { BaseModalProps } from '../modal-root';
|
||||
|
||||
|
@ -16,17 +14,9 @@ interface DislikesModalProps {
|
|||
}
|
||||
|
||||
const DislikesModal: React.FC<BaseModalProps & DislikesModalProps> = ({ onClose, statusId }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const modalRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const accountIds = useAppSelector((state) => state.user_lists.disliked_by[statusId]?.items);
|
||||
|
||||
const fetchData = () => {
|
||||
dispatch(fetchDislikes(statusId));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
const { data: accountIds, isLoading, hasNextPage, fetchNextPage } = useStatusDislikes(statusId);
|
||||
|
||||
const onClickClose = () => {
|
||||
onClose('DISLIKES');
|
||||
|
@ -44,7 +34,12 @@ const DislikesModal: React.FC<BaseModalProps & DislikesModalProps> = ({ onClose,
|
|||
emptyMessage={emptyMessage}
|
||||
listClassName='max-w-full'
|
||||
itemClassName='pb-3'
|
||||
style={{ height: 'calc(80vh - 88px)' }}
|
||||
hasMore={hasNextPage}
|
||||
isLoading={typeof isLoading === 'boolean' ? isLoading : true}
|
||||
onLoadMore={() => fetchNextPage({ cancelRefetch: false })}
|
||||
estimatedSize={42}
|
||||
parentRef={modalRef}
|
||||
>
|
||||
{accountIds.map(id =>
|
||||
<AccountContainer key={id} id={id} />,
|
||||
|
@ -57,6 +52,7 @@ const DislikesModal: React.FC<BaseModalProps & DislikesModalProps> = ({ onClose,
|
|||
<Modal
|
||||
title={<FormattedMessage id='column.dislikes' defaultMessage='Dislikes' />}
|
||||
onClose={onClickClose}
|
||||
ref={modalRef}
|
||||
>
|
||||
{body}
|
||||
</Modal>
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
import React, { useEffect, useRef } from 'react';
|
||||
import React, { useRef } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { fetchFavourites, expandFavourites } from 'pl-fe/actions/interactions';
|
||||
import { useStatusFavourites } from 'pl-fe/api/hooks/account-lists/use-status-interactions';
|
||||
import ScrollableList from 'pl-fe/components/scrollable-list';
|
||||
import Modal from 'pl-fe/components/ui/modal';
|
||||
import Spinner from 'pl-fe/components/ui/spinner';
|
||||
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 type { BaseModalProps } from '../modal-root';
|
||||
|
||||
|
@ -17,29 +15,13 @@ interface FavouritesModalProps {
|
|||
|
||||
const FavouritesModal: React.FC<BaseModalProps & FavouritesModalProps> = ({ onClose, statusId }) => {
|
||||
const modalRef = useRef<HTMLDivElement>(null);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const accountIds = useAppSelector((state) => state.user_lists.favourited_by[statusId]?.items);
|
||||
const next = useAppSelector((state) => state.user_lists.favourited_by[statusId]?.next);
|
||||
|
||||
const fetchData = () => {
|
||||
dispatch(fetchFavourites(statusId));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
const { data: accountIds, isLoading, hasNextPage, fetchNextPage } = useStatusFavourites(statusId);
|
||||
|
||||
const onClickClose = () => {
|
||||
onClose('FAVOURITES');
|
||||
};
|
||||
|
||||
const handleLoadMore = () => {
|
||||
if (next) {
|
||||
dispatch(expandFavourites(statusId, next!));
|
||||
}
|
||||
};
|
||||
|
||||
let body;
|
||||
|
||||
if (!accountIds) {
|
||||
|
@ -53,8 +35,9 @@ const FavouritesModal: React.FC<BaseModalProps & FavouritesModalProps> = ({ onCl
|
|||
listClassName='max-w-full'
|
||||
itemClassName='pb-3'
|
||||
style={{ height: 'calc(80vh - 88px)' }}
|
||||
onLoadMore={handleLoadMore}
|
||||
hasMore={!!next}
|
||||
hasMore={hasNextPage}
|
||||
isLoading={typeof isLoading === 'boolean' ? isLoading : true}
|
||||
onLoadMore={() => fetchNextPage({ cancelRefetch: false })}
|
||||
estimatedSize={42}
|
||||
parentRef={modalRef}
|
||||
>
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
import clsx from 'clsx';
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import React, { useMemo, useRef, useState } from 'react';
|
||||
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { fetchReactions } from 'pl-fe/actions/interactions';
|
||||
import { useStatusReactions } from 'pl-fe/api/hooks/account-lists/use-status-interactions';
|
||||
import ScrollableList from 'pl-fe/components/scrollable-list';
|
||||
import Emoji from 'pl-fe/components/ui/emoji';
|
||||
import Modal from 'pl-fe/components/ui/modal';
|
||||
import Spinner from 'pl-fe/components/ui/spinner';
|
||||
import Tabs from 'pl-fe/components/ui/tabs';
|
||||
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 type { BaseModalProps } from '../modal-root';
|
||||
import type { Item } from 'pl-fe/components/ui/tabs';
|
||||
|
@ -32,10 +30,10 @@ interface ReactionsModalProps {
|
|||
|
||||
const ReactionsModal: React.FC<BaseModalProps & ReactionsModalProps> = ({ onClose, statusId, reaction: initialReaction }) => {
|
||||
const modalRef = useRef<HTMLDivElement>(null);
|
||||
const dispatch = useAppDispatch();
|
||||
const intl = useIntl();
|
||||
const [reaction, setReaction] = useState(initialReaction);
|
||||
const reactions = useAppSelector((state) => state.user_lists.reactions[statusId]?.items);
|
||||
|
||||
const { data: reactions, isLoading } = useStatusReactions(statusId);
|
||||
|
||||
const onClickClose = () => {
|
||||
onClose('REACTIONS');
|
||||
|
@ -70,16 +68,12 @@ const ReactionsModal: React.FC<BaseModalProps & ReactionsModalProps> = ({ onClos
|
|||
if (reaction) {
|
||||
const reactionRecord = reactions.find(({ name }) => name === reaction);
|
||||
|
||||
if (reactionRecord) return reactionRecord.accounts.map(account => ({ id: account, reaction: reaction, reactionUrl: reactionRecord.url || undefined }));
|
||||
if (reactionRecord) return reactionRecord.account_ids.map(account => ({ id: account, reaction: reaction, reactionUrl: reactionRecord.url || undefined }));
|
||||
} else {
|
||||
return reactions.map(({ accounts, name, url }) => accounts.map(account => ({ id: account, reaction: name, reactionUrl: url || undefined }))).flat();
|
||||
return reactions.map(({ account_ids, name, url }) => account_ids.map(account => ({ id: account, reaction: name, reactionUrl: url || undefined }))).flat();
|
||||
}
|
||||
}, [reactions, reaction]);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchReactions(statusId));
|
||||
}, []);
|
||||
|
||||
let body;
|
||||
|
||||
if (!accounts || !reactions) {
|
||||
|
@ -96,6 +90,7 @@ const ReactionsModal: React.FC<BaseModalProps & ReactionsModalProps> = ({ onClos
|
|||
})}
|
||||
itemClassName='pb-3'
|
||||
style={{ height: 'calc(80vh - 88px)' }}
|
||||
isLoading={typeof isLoading === 'boolean' ? isLoading : true}
|
||||
estimatedSize={42}
|
||||
parentRef={modalRef}
|
||||
>
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
import React, { useEffect, useRef } from 'react';
|
||||
import { FormattedMessage, useIntl } from 'react-intl';
|
||||
import React, { useRef } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { fetchReblogs, expandReblogs } from 'pl-fe/actions/interactions';
|
||||
import { fetchStatus } from 'pl-fe/actions/statuses';
|
||||
import { useStatusReblogs } from 'pl-fe/api/hooks/account-lists/use-status-interactions';
|
||||
import ScrollableList from 'pl-fe/components/scrollable-list';
|
||||
import Modal from 'pl-fe/components/ui/modal';
|
||||
import Spinner from 'pl-fe/components/ui/spinner';
|
||||
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 type { BaseModalProps } from '../modal-root';
|
||||
|
||||
|
@ -17,31 +14,14 @@ interface ReblogsModalProps {
|
|||
}
|
||||
|
||||
const ReblogsModal: React.FC<BaseModalProps & ReblogsModalProps> = ({ onClose, statusId }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const intl = useIntl();
|
||||
const accountIds = useAppSelector((state) => state.user_lists.reblogged_by[statusId]?.items);
|
||||
const next = useAppSelector((state) => state.user_lists.reblogged_by[statusId]?.next);
|
||||
const modalRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const fetchData = () => {
|
||||
dispatch(fetchReblogs(statusId));
|
||||
dispatch(fetchStatus(statusId, intl));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
const { data: accountIds, isLoading, hasNextPage, fetchNextPage } = useStatusReblogs(statusId);
|
||||
|
||||
const onClickClose = () => {
|
||||
onClose('REBLOGS');
|
||||
};
|
||||
|
||||
const handleLoadMore = () => {
|
||||
if (next) {
|
||||
dispatch(expandReblogs(statusId, next!));
|
||||
}
|
||||
};
|
||||
|
||||
let body;
|
||||
|
||||
if (!accountIds) {
|
||||
|
@ -55,8 +35,9 @@ const ReblogsModal: React.FC<BaseModalProps & ReblogsModalProps> = ({ onClose, s
|
|||
listClassName='max-w-full'
|
||||
itemClassName='pb-3'
|
||||
style={{ height: 'calc(80vh - 88px)' }}
|
||||
onLoadMore={handleLoadMore}
|
||||
hasMore={!!next}
|
||||
hasMore={hasNextPage}
|
||||
isLoading={typeof isLoading === 'boolean' ? isLoading : true}
|
||||
onLoadMore={() => fetchNextPage({ cancelRefetch: false })}
|
||||
estimatedSize={42}
|
||||
parentRef={modalRef}
|
||||
>
|
||||
|
|
|
@ -13,9 +13,7 @@ describe('user_lists reducer', () => {
|
|||
follow_requests: { next: null, items: ImmutableOrderedSet(), isLoading: false },
|
||||
blocks: { next: null, items: ImmutableOrderedSet(), isLoading: false },
|
||||
mutes: { next: null, items: ImmutableOrderedSet(), isLoading: false },
|
||||
directory: { next: null, items: ImmutableOrderedSet(), isLoading: true },
|
||||
pinned: {},
|
||||
birthday_reminders: {},
|
||||
familiar_followers: {},
|
||||
});
|
||||
});
|
||||
|
|
|
@ -16,15 +16,6 @@ import {
|
|||
GROUP_UNBLOCK_SUCCESS,
|
||||
type GroupsAction,
|
||||
} from 'pl-fe/actions/groups';
|
||||
import {
|
||||
REBLOGS_FETCH_SUCCESS,
|
||||
REBLOGS_EXPAND_SUCCESS,
|
||||
FAVOURITES_FETCH_SUCCESS,
|
||||
FAVOURITES_EXPAND_SUCCESS,
|
||||
DISLIKES_FETCH_SUCCESS,
|
||||
REACTIONS_FETCH_SUCCESS,
|
||||
type InteractionsAction,
|
||||
} from 'pl-fe/actions/interactions';
|
||||
import { NOTIFICATIONS_UPDATE, type NotificationsAction } from 'pl-fe/actions/notifications';
|
||||
|
||||
import type { Account, NotificationGroup, PaginatedResponse } from 'pl-api';
|
||||
|
@ -35,31 +26,12 @@ interface List {
|
|||
isLoading: boolean;
|
||||
}
|
||||
|
||||
interface Reaction {
|
||||
accounts: Array<string>;
|
||||
count: number | null;
|
||||
name: string;
|
||||
url: string | undefined;
|
||||
}
|
||||
|
||||
interface ReactionList {
|
||||
next: (() => Promise<PaginatedResponse<Reaction>>) | null;
|
||||
items: Array<Reaction>;
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
type ListKey = 'follow_requests';
|
||||
type NestedListKey = 'reblogged_by' | 'favourited_by' | 'disliked_by' | 'pinned' | 'familiar_followers' | 'membership_requests' | 'group_blocks';
|
||||
type NestedListKey = 'pinned' | 'familiar_followers' | 'membership_requests' | 'group_blocks';
|
||||
|
||||
type State = Record<ListKey, List> & Record<NestedListKey, Record<string, List>> & {
|
||||
reactions: Record<string, ReactionList>;
|
||||
};
|
||||
type State = Record<ListKey, List> & Record<NestedListKey, Record<string, List>>;
|
||||
|
||||
const initialState: State = {
|
||||
reblogged_by: {},
|
||||
favourited_by: {},
|
||||
disliked_by: {},
|
||||
reactions: {},
|
||||
follow_requests: { next: null, items: [], isLoading: false },
|
||||
pinned: {},
|
||||
familiar_followers: {},
|
||||
|
@ -122,26 +94,8 @@ const normalizeFollowRequest = (state: State, notification: NotificationGroup) =
|
|||
draft.follow_requests.items = [...new Set([...notification.sample_account_ids, ...draft.follow_requests.items])];
|
||||
});
|
||||
|
||||
const userLists = (state = initialState, action: AccountsAction | FamiliarFollowersAction | GroupsAction | InteractionsAction | NotificationsAction): State => {
|
||||
const userLists = (state = initialState, action: AccountsAction | FamiliarFollowersAction | GroupsAction | NotificationsAction): State => {
|
||||
switch (action.type) {
|
||||
case REBLOGS_FETCH_SUCCESS:
|
||||
return normalizeList(state, ['reblogged_by', action.statusId], action.accounts, action.next);
|
||||
case REBLOGS_EXPAND_SUCCESS:
|
||||
return appendToList(state, ['reblogged_by', action.statusId], action.accounts, action.next);
|
||||
case FAVOURITES_FETCH_SUCCESS:
|
||||
return normalizeList(state, ['favourited_by', action.statusId], action.accounts, action.next);
|
||||
case FAVOURITES_EXPAND_SUCCESS:
|
||||
return appendToList(state, ['favourited_by', action.statusId], action.accounts, action.next);
|
||||
case DISLIKES_FETCH_SUCCESS:
|
||||
return normalizeList(state, ['disliked_by', action.statusId], action.accounts);
|
||||
case REACTIONS_FETCH_SUCCESS:
|
||||
return create(state, (draft) => {
|
||||
draft.reactions[action.statusId] = {
|
||||
items: action.reactions.map((reaction) => ({ ...reaction, accounts: reaction.accounts.map(({ id }) => id) })),
|
||||
next: null,
|
||||
isLoading: false,
|
||||
};
|
||||
});
|
||||
case NOTIFICATIONS_UPDATE:
|
||||
return action.notification.type === 'follow_request' ? normalizeFollowRequest(state, action.notification) : state;
|
||||
case FOLLOW_REQUESTS_FETCH_SUCCESS:
|
||||
|
|
Loading…
Reference in a new issue