pl-fe: migrate status interactions to tanstack query

Signed-off-by: Nicole Mikołajczyk <git@mkljczk.pl>
This commit is contained in:
Nicole Mikołajczyk
2025-05-29 00:55:23 +02:00
parent 88dabae10d
commit 92be17d0e2
8 changed files with 256 additions and 213 deletions

View File

@ -15,7 +15,6 @@ const REBLOG_REQUEST = 'REBLOG_REQUEST' as const;
const REBLOG_FAIL = 'REBLOG_FAIL' as const;
const FAVOURITE_REQUEST = 'FAVOURITE_REQUEST' as const;
const FAVOURITE_SUCCESS = 'FAVOURITE_SUCCESS' as const;
const FAVOURITE_FAIL = 'FAVOURITE_FAIL' as const;
const DISLIKE_REQUEST = 'DISLIKE_REQUEST' as const;
@ -25,7 +24,6 @@ const UNREBLOG_REQUEST = 'UNREBLOG_REQUEST' as const;
const UNREBLOG_FAIL = 'UNREBLOG_FAIL' as const;
const UNFAVOURITE_REQUEST = 'UNFAVOURITE_REQUEST' as const;
const UNFAVOURITE_SUCCESS = 'UNFAVOURITE_SUCCESS' as const;
const UNDISLIKE_REQUEST = 'UNDISLIKE_REQUEST' as const;
@ -37,8 +35,6 @@ const BOOKMARK_SUCCESS = 'BOOKMARKED_SUCCESS' as const;
const UNBOOKMARK_SUCCESS = 'UNBOOKMARKED_SUCCESS' as const;
const noOp = () => new Promise(f => f(undefined));
const messages = defineMessages({
bookmarkAdded: { id: 'status.bookmarked', defaultMessage: 'Bookmark added.' },
bookmarkRemoved: { id: 'status.unbookmarked', defaultMessage: 'Bookmark removed.' },
@ -47,166 +43,59 @@ const messages = defineMessages({
selectFolder: { id: 'status.bookmark.select_folder', defaultMessage: 'Select folder' },
});
const reblog = (status: Pick<Status, 'id'>, visibility?: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
if (!isLoggedIn(getState)) return noOp();
interface ReblogRequest {
type: typeof REBLOG_REQUEST;
statusId: string;
}
dispatch(reblogRequest(status.id));
interface ReblogFail {
type: typeof REBLOG_FAIL;
statusId: string;
error: unknown;
}
return getClient(getState()).statuses.reblogStatus(status.id, visibility).then((response) => {
// The reblog API method returns a new status wrapped around the original. In this case we are only
// interested in how the original is modified, hence passing it skipping the wrapper
if (response.reblog) dispatch(importEntities({ statuses: [response.reblog] }));
}).catch(error => {
dispatch(reblogFail(status.id, error));
});
};
interface UnreblogRequest {
type: typeof UNREBLOG_REQUEST;
statusId: string;
}
const unreblog = (status: Pick<Status, 'id'>) =>
(dispatch: AppDispatch, getState: () => RootState) => {
if (!isLoggedIn(getState)) return noOp();
interface UnreblogFail {
type: typeof UNREBLOG_FAIL;
statusId: string;
error: unknown;
}
dispatch(unreblogRequest(status.id));
interface FavouriteRequest {
type: typeof FAVOURITE_REQUEST;
statusId: string;
}
return getClient(getState()).statuses.unreblogStatus(status.id).catch(error => {
dispatch(unreblogFail(status.id, error));
});
};
interface FavouriteFail {
type: typeof FAVOURITE_FAIL;
statusId: string;
error: unknown;
}
const toggleReblog = (status: Pick<Status, 'id' | 'reblogged'>, visibility?: string) => {
if (status.reblogged) {
return unreblog(status);
} else {
return reblog(status, visibility);
}
};
interface UnfavouriteRequest {
type: typeof UNFAVOURITE_REQUEST;
statusId: string;
}
const reblogRequest = (statusId: string) => ({
type: REBLOG_REQUEST,
statusId,
});
interface DislikeRequest {
type: typeof DISLIKE_REQUEST;
statusId: string;
}
const reblogFail = (statusId: string, error: unknown) => ({
type: REBLOG_FAIL,
statusId,
error,
});
interface DislikeFail {
type: typeof DISLIKE_FAIL;
statusId: string;
error: unknown;
}
const unreblogRequest = (statusId: string) => ({
type: UNREBLOG_REQUEST,
statusId,
});
const unreblogFail = (statusId: string, error: unknown) => ({
type: UNREBLOG_FAIL,
statusId,
error,
});
const favourite = (status: Pick<Status, 'id'>) =>
(dispatch: AppDispatch, getState: () => RootState) => {
if (!isLoggedIn(getState)) return noOp();
dispatch(favouriteRequest(status.id));
return getClient(getState()).statuses.favouriteStatus(status.id).then((response) => {
dispatch(favouriteSuccess(response));
}).catch((error) => {
dispatch(favouriteFail(status.id, error));
});
};
const unfavourite = (status: Pick<Status, 'id'>) =>
(dispatch: AppDispatch, getState: () => RootState) => {
if (!isLoggedIn(getState)) return noOp();
dispatch(unfavouriteRequest(status.id));
return getClient(getState()).statuses.unfavouriteStatus(status.id).then((response) => {
dispatch(unfavouriteSuccess(response));
});
};
const toggleFavourite = (status: Pick<Status, 'id' | 'favourited'>) => {
if (status.favourited) {
return unfavourite(status);
} else {
return favourite(status);
}
};
const favouriteRequest = (statusId: string) => ({
type: FAVOURITE_REQUEST,
statusId,
});
const favouriteSuccess = (status: Status) => ({
type: FAVOURITE_SUCCESS,
status,
statusId: status.id,
});
const favouriteFail = (statusId: string, error: unknown) => ({
type: FAVOURITE_FAIL,
statusId,
error,
});
const unfavouriteRequest = (statusId: string) => ({
type: UNFAVOURITE_REQUEST,
statusId,
});
const unfavouriteSuccess = (status: Status) => ({
type: UNFAVOURITE_SUCCESS,
status,
statusId: status.id,
});
const dislike = (status: Pick<Status, 'id'>) =>
(dispatch: AppDispatch, getState: () => RootState) => {
if (!isLoggedIn(getState)) return;
dispatch(dislikeRequest(status.id));
return getClient(getState).statuses.dislikeStatus(status.id).catch((error) => {
dispatch(dislikeFail(status.id, error));
});
};
const undislike = (status: Pick<Status, 'id'>) =>
(dispatch: AppDispatch, getState: () => RootState) => {
if (!isLoggedIn(getState)) return;
dispatch(undislikeRequest(status.id));
return getClient(getState).statuses.undislikeStatus(status.id);
};
const toggleDislike = (status: Pick<Status, 'id' | 'disliked'>) =>
(dispatch: AppDispatch) => {
if (status.disliked) {
dispatch(undislike(status));
} else {
dispatch(dislike(status));
}
};
const dislikeRequest = (statusId: string) => ({
type: DISLIKE_REQUEST,
statusId,
});
const dislikeFail = (statusId: string, error: unknown) => ({
type: DISLIKE_FAIL,
statusId,
error,
});
const undislikeRequest = (statusId: string) => ({
type: UNDISLIKE_REQUEST,
statusId,
});
interface UndislikeRequest {
type: typeof UNDISLIKE_REQUEST;
statusId: string;
}
const bookmark = (status: Pick<Status, 'id'>, folderId?: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
@ -318,18 +207,16 @@ const remoteInteraction = (ap_id: string, profile: string) =>
getClient(getState).accounts.remoteInteraction(ap_id, profile).then((data) => data.url);
type InteractionsAction =
| ReturnType<typeof reblogRequest>
| ReturnType<typeof reblogFail>
| ReturnType<typeof unreblogRequest>
| ReturnType<typeof unreblogFail>
| ReturnType<typeof favouriteRequest>
| ReturnType<typeof favouriteSuccess>
| ReturnType<typeof favouriteFail>
| ReturnType<typeof unfavouriteRequest>
| ReturnType<typeof unfavouriteSuccess>
| ReturnType<typeof dislikeRequest>
| ReturnType<typeof dislikeFail>
| ReturnType<typeof undislikeRequest>
| ReblogRequest
| ReblogFail
| UnreblogRequest
| UnreblogFail
| FavouriteRequest
| FavouriteFail
| UnfavouriteRequest
| DislikeRequest
| DislikeFail
| UndislikeRequest
| ReturnType<typeof bookmarkSuccess>
| ReturnType<typeof unbookmarkSuccess>
| ReturnType<typeof pinSuccess>
@ -339,26 +226,17 @@ export {
REBLOG_REQUEST,
REBLOG_FAIL,
FAVOURITE_REQUEST,
FAVOURITE_SUCCESS,
FAVOURITE_FAIL,
DISLIKE_REQUEST,
DISLIKE_FAIL,
UNREBLOG_REQUEST,
UNREBLOG_FAIL,
UNFAVOURITE_REQUEST,
UNFAVOURITE_SUCCESS,
UNDISLIKE_REQUEST,
PIN_SUCCESS,
UNPIN_SUCCESS,
BOOKMARK_SUCCESS,
UNBOOKMARK_SUCCESS,
reblog,
unreblog,
toggleReblog,
favourite,
unfavourite,
toggleFavourite,
toggleDislike,
bookmark,
toggleBookmark,
togglePin,

View File

@ -6,7 +6,7 @@ import { useHistory, useRouteMatch } from 'react-router-dom';
import { blockAccount } from 'pl-fe/actions/accounts';
import { directCompose, mentionCompose, quoteCompose, replyCompose } from 'pl-fe/actions/compose';
import { emojiReact, unEmojiReact } from 'pl-fe/actions/emoji-reacts';
import { toggleBookmark, toggleDislike, toggleFavourite, togglePin, toggleReblog } from 'pl-fe/actions/interactions';
import { toggleBookmark, togglePin } from 'pl-fe/actions/interactions';
import { deleteStatusModal, toggleStatusSensitivityModal } from 'pl-fe/actions/moderation';
import { initReport, ReportableEntities } from 'pl-fe/actions/reports';
import { changeSetting } from 'pl-fe/actions/settings';
@ -30,6 +30,7 @@ import { useChats } from 'pl-fe/queries/chats';
import { useBlockGroupUserMutation } from 'pl-fe/queries/groups/use-group-blocks';
import { useCustomEmojis } from 'pl-fe/queries/instance/use-custom-emojis';
import { useTranslationLanguages } from 'pl-fe/queries/instance/use-translation-languages';
import { useDislikeStatus, useFavouriteStatus, useReblogStatus, useUndislikeStatus, useUnfavouriteStatus, useUnreblogStatus } from 'pl-fe/queries/statuses/use-status-interactions';
import { useModalsStore } from 'pl-fe/stores/modals';
import { useStatusMetaStore } from 'pl-fe/stores/status-meta';
import toast from 'pl-fe/toast';
@ -288,6 +289,9 @@ const ReblogButton: React.FC<IReblogButton> = ({
const { openModal } = useModalsStore();
const canReblog = useCanInteract(status, 'can_reblog');
const { mutate: reblogStatus } = useReblogStatus(status.id);
const { mutate: unreblogStatus } = useUnreblogStatus(status.id);
let reblogIcon = require('@tabler/icons/outline/repeat.svg');
if (status.visibility === 'direct') {
@ -298,9 +302,17 @@ const ReblogButton: React.FC<IReblogButton> = ({
const handleReblogClick: React.EventHandler<React.MouseEvent> = e => {
if (me) {
const modalReblog = () => dispatch(toggleReblog(status)).then(() => {
if (canReblog.approvalRequired) toast.info(messages.reblogApprovalRequired);
});
const modalReblog = () => {
if (status.reblogged) {
unreblogStatus();
} else {
reblogStatus(undefined, {
onSuccess: () => {
if (canReblog.approvalRequired) toast.info(messages.reblogApprovalRequired);
},
});
}
};
if ((e && e.shiftKey) || !boostModal) {
modalReblog();
} else {
@ -377,18 +389,26 @@ const FavouriteButton: React.FC<IActionButton> = ({
withLabels,
onOpenUnauthorizedModal,
}) => {
const dispatch = useAppDispatch();
const features = useFeatures();
const intl = useIntl();
const { openModal } = useModalsStore();
const canFavourite = useCanInteract(status, 'can_favourite');
const { mutate: favouriteStatus } = useFavouriteStatus(status.id);
const { mutate: unfavouriteStatus } = useUnfavouriteStatus(status.id);
const handleFavouriteClick: React.EventHandler<React.MouseEvent> = (e) => {
if (me) {
dispatch(toggleFavourite(status)).then(() => {
if (canFavourite.approvalRequired) toast.info(messages.favouriteApprovalRequired);
}).catch(() => {});
if (status.favourited) {
unfavouriteStatus();
} else {
favouriteStatus(undefined, {
onSuccess: () => {
if (canFavourite.approvalRequired) toast.info(messages.favouriteApprovalRequired);
},
});
}
} else {
onOpenUnauthorizedModal('FAVOURITE');
}
@ -431,17 +451,23 @@ const DislikeButton: React.FC<IActionButton> = ({
me,
onOpenUnauthorizedModal,
}) => {
const dispatch = useAppDispatch();
const features = useFeatures();
const intl = useIntl();
const { openModal } = useModalsStore();
const { mutate: dislikeStatus } = useDislikeStatus(status.id);
const { mutate: undislikeStatus } = useUndislikeStatus(status.id);
if (!features.statusDislikes) return;
const handleDislikeClick: React.EventHandler<React.MouseEvent> = (e) => {
if (me) {
dispatch(toggleDislike(status));
if (status.disliked) {
undislikeStatus();
} else {
dislikeStatus();
}
} else {
onOpenUnauthorizedModal('DISLIKE');
}
@ -603,6 +629,8 @@ const MenuButton: React.FC<IMenuButton> = ({
const { autoTranslate, deleteModal, knownLanguages } = useSettings();
const { translationLanguages } = useTranslationLanguages();
const { mutate: reblogStatus } = useReblogStatus(status.id);
const { mutate: unreblogStatus } = useUnreblogStatus(status.id);
const autoTranslating = useMemo(() => {
const {
@ -666,7 +694,10 @@ const MenuButton: React.FC<IMenuButton> = ({
};
const handleReblogClick = (e: React.MouseEvent | React.KeyboardEvent, visibility?: string) => {
const modalReblog = () => dispatch(toggleReblog(status, visibility));
const modalReblog = () => {
if (status.reblogged) unreblogStatus();
else reblogStatus(visibility);
};
if ((e && e.shiftKey) || !boostModal) {
modalReblog();
} else {

View File

@ -4,7 +4,6 @@ import { defineMessages, useIntl, FormattedList, FormattedMessage } from 'react-
import { Link, useHistory } from 'react-router-dom';
import { mentionCompose, replyCompose } from 'pl-fe/actions/compose';
import { toggleFavourite, toggleReblog } from 'pl-fe/actions/interactions';
import { unfilterStatus } from 'pl-fe/actions/statuses';
import Card from 'pl-fe/components/ui/card';
import Icon from 'pl-fe/components/ui/icon';
@ -17,6 +16,7 @@ import { HotKeys } from 'pl-fe/features/ui/components/hotkeys';
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
import { useSettings } from 'pl-fe/hooks/use-settings';
import { useFavouriteStatus, useReblogStatus, useUnfavouriteStatus, useUnreblogStatus } from 'pl-fe/queries/statuses/use-status-interactions';
import { makeGetStatus, type SelectedStatus } from 'pl-fe/selectors';
import { useModalsStore } from 'pl-fe/stores/modals';
import { useStatusMetaStore } from 'pl-fe/stores/status-meta';
@ -88,6 +88,11 @@ const Status: React.FC<IStatus> = (props) => {
const getStatus = useMemo(makeGetStatus, []);
const actualStatus = useAppSelector(state => status.reblog_id && getStatus(state, { id: status.reblog_id }) || status)!;
const { mutate: favouriteStatus } = useFavouriteStatus(actualStatus.id);
const { mutate: unfavouriteStatus } = useUnfavouriteStatus(actualStatus.id);
const { mutate: reblogStatus } = useReblogStatus(actualStatus.id);
const { mutate: unreblogStatus } = useUnreblogStatus(actualStatus.id);
const isReblog = status.reblog_id;
const statusUrl = `/@${actualStatus.account.acct}/posts/${actualStatus.id}`;
const group = actualStatus.group;
@ -140,11 +145,15 @@ const Status: React.FC<IStatus> = (props) => {
const handleHotkeyFavourite = (e?: KeyboardEvent) => {
e?.preventDefault();
dispatch(toggleFavourite(actualStatus));
if (status.favourited) unfavouriteStatus();
else favouriteStatus();
};
const handleHotkeyBoost = (e?: KeyboardEvent) => {
const modalReblog = () => dispatch(toggleReblog(actualStatus));
const modalReblog = () => {
if (status.reblogged) unreblogStatus();
else reblogStatus(undefined);
};
if ((e && e.shiftKey) || !boostModal) {
modalReblog();
} else {

View File

@ -5,7 +5,7 @@ import { Link, useHistory } from 'react-router-dom';
import { blockAccount } from 'pl-fe/actions/accounts';
import { directCompose, mentionCompose, quoteCompose } from 'pl-fe/actions/compose';
import { fetchEventIcs } from 'pl-fe/actions/events';
import { toggleBookmark, togglePin, toggleReblog } from 'pl-fe/actions/interactions';
import { toggleBookmark, togglePin } from 'pl-fe/actions/interactions';
import { deleteStatusModal, toggleStatusSensitivityModal } from 'pl-fe/actions/moderation';
import { initReport, ReportableEntities } from 'pl-fe/actions/reports';
import { deleteStatus } from 'pl-fe/actions/statuses';
@ -24,6 +24,7 @@ import { useFeatures } from 'pl-fe/hooks/use-features';
import { useOwnAccount } from 'pl-fe/hooks/use-own-account';
import { useSettings } from 'pl-fe/hooks/use-settings';
import { useChats } from 'pl-fe/queries/chats';
import { useReblogStatus, useUnreblogStatus } from 'pl-fe/queries/statuses/use-status-interactions';
import { useModalsStore } from 'pl-fe/stores/modals';
import copy from 'pl-fe/utils/copy';
import { download } from 'pl-fe/utils/download';
@ -89,6 +90,9 @@ const EventHeader: React.FC<IEventHeader> = ({ status }) => {
const isStaff = ownAccount ? ownAccount.is_admin || ownAccount.is_moderator : false;
const isAdmin = ownAccount ? ownAccount.is_admin : false;
const { mutate: reblogStatus } = useReblogStatus(status?.id!);
const { mutate: unreblogStatus } = useUnreblogStatus(status?.id!);
if (!status || !status.event) {
return (
<>
@ -131,7 +135,10 @@ const EventHeader: React.FC<IEventHeader> = ({ status }) => {
};
const handleReblogClick = (visibility?: string) => {
const modalReblog = () => dispatch(toggleReblog(status, visibility));
const modalReblog = () => {
if (status.reblogged) unreblogStatus();
else reblogStatus(visibility);
};
if (!boostModal) {
modalReblog();
} else {

View File

@ -3,7 +3,6 @@ import { defineMessages, useIntl, FormattedList, FormattedMessage, IntlShape, Me
import { Link, useHistory } from 'react-router-dom';
import { mentionCompose, replyCompose } from 'pl-fe/actions/compose';
import { reblog, favourite, unreblog, unfavourite } from 'pl-fe/actions/interactions';
import HoverAccountWrapper from 'pl-fe/components/hover-account-wrapper';
import Icon from 'pl-fe/components/icon';
import RelativeTimestamp from 'pl-fe/components/relative-timestamp';
@ -18,6 +17,7 @@ import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
import { useInstance } from 'pl-fe/hooks/use-instance';
import { useLoggedIn } from 'pl-fe/hooks/use-logged-in';
import { useFavouriteStatus, useUnfavouriteStatus, useReblogStatus, useUnreblogStatus } from 'pl-fe/queries/statuses/use-status-interactions';
import { makeGetNotification } from 'pl-fe/selectors';
import { useModalsStore } from 'pl-fe/stores/modals';
import { useSettingsStore } from 'pl-fe/stores/settings';
@ -211,6 +211,11 @@ const Notification: React.FC<INotification> = (props) => {
const notification = useAppSelector((state) => getNotification(state, props.notification));
const status = getNotificationStatus(notification);
const { mutate: favouriteStatus } = useFavouriteStatus(status?.id!);
const { mutate: unfavouriteStatus } = useUnfavouriteStatus(status?.id!);
const { mutate: reblogStatus } = useReblogStatus(status?.id!);
const { mutate: unreblogStatus } = useUnreblogStatus(status?.id!);
const history = useHistory();
const intl = useIntl();
const instance = useInstance();
@ -252,9 +257,9 @@ const Notification: React.FC<INotification> = (props) => {
const handleHotkeyFavourite = useCallback((e?: KeyboardEvent) => {
if (status && typeof status === 'object') {
if (status.favourited) {
dispatch(unfavourite(status));
unfavouriteStatus();
} else {
dispatch(favourite(status));
favouriteStatus();
}
}
}, [status]);
@ -263,15 +268,15 @@ const Notification: React.FC<INotification> = (props) => {
if (status && typeof status === 'object') {
const boostModal = settings.boostModal;
if (status.reblogged) {
dispatch(unreblog(status));
unreblogStatus();
} else {
if (e?.shiftKey || !boostModal) {
dispatch(reblog(status));
reblogStatus(undefined);
} else {
openModal('BOOST', {
statusId: status.id,
onReblog: () => {
dispatch(reblog(status));
reblogStatus(undefined);
},
});
}

View File

@ -6,7 +6,6 @@ import { useIntl } from 'react-intl';
import { useHistory } from 'react-router-dom';
import { type ComposeReplyAction, mentionCompose, replyCompose } from 'pl-fe/actions/compose';
import { reblog, toggleFavourite, unreblog } from 'pl-fe/actions/interactions';
import ScrollableList from 'pl-fe/components/scrollable-list';
import StatusActionBar from 'pl-fe/components/status-action-bar';
import Tombstone from 'pl-fe/components/tombstone';
@ -16,6 +15,7 @@ import { HotKeys } from 'pl-fe/features/ui/components/hotkeys';
import PendingStatus from 'pl-fe/features/ui/components/pending-status';
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
import { useFavouriteStatus, useReblogStatus, useUnfavouriteStatus, useUnreblogStatus } from 'pl-fe/queries/statuses/use-status-interactions';
import { RootState } from 'pl-fe/store';
import { useModalsStore } from 'pl-fe/stores/modals';
import { useSettingsStore } from 'pl-fe/stores/settings';
@ -117,6 +117,11 @@ const Thread: React.FC<IThread> = ({
const { openModal } = useModalsStore();
const { settings } = useSettingsStore();
const { mutate: favouriteStatus } = useFavouriteStatus(status.id);
const { mutate: unfavouriteStatus } = useUnfavouriteStatus(status.id);
const { mutate: reblogStatus } = useReblogStatus(status.id);
const { mutate: unreblogStatus } = useUnreblogStatus(status.id);
const getThread = useCallback(makeGetThread(), []);
const { ancestorsIds, descendantsIds } = useAppSelector((state) => getThread(state, status.id));
@ -135,22 +140,21 @@ const Thread: React.FC<IThread> = ({
};
const handleFavouriteClick = (status: SelectedStatus) => {
dispatch(toggleFavourite(status));
if (status.favourited) unfavouriteStatus();
else favouriteStatus();
};
const handleReplyClick = (status: ComposeReplyAction['status']) => dispatch(replyCompose(status));
const handleModalReblog = (status: Pick<SelectedStatus, 'id'>) => dispatch(reblog(status));
const handleReblogClick = (status: SelectedStatus, e?: React.MouseEvent) => {
const boostModal = settings.boostModal;
if (status.reblogged) {
dispatch(unreblog(status));
unreblogStatus();
} else {
if ((e && e.shiftKey) || !boostModal) {
handleModalReblog(status);
reblogStatus(undefined);
} else {
openModal('BOOST', { statusId: status.id, onReblog: () => handleModalReblog(status) });
openModal('BOOST', { statusId: status.id, onReblog: () => reblogStatus(undefined) });
}
}
};

View File

@ -1,4 +1,4 @@
import { useQuery } from '@tanstack/react-query';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { importEntities } from 'pl-fe/actions/importer';
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
@ -6,6 +6,8 @@ import { useClient } from 'pl-fe/hooks/use-client';
import { makePaginatedResponseQuery } from 'pl-fe/queries/utils/make-paginated-response-query';
import { minifyAccountList } from 'pl-fe/queries/utils/minify-list';
import type { InteractionsAction } from 'pl-fe/actions/interactions';
const queryKey = {
getDislikedBy: 'statusDislikes',
getFavouritedBy: 'statusFavourites',
@ -36,4 +38,117 @@ const useStatusReactions = (statusId: string, emoji?: string) => {
});
};
export { useStatusDislikes, useStatusFavourites, useStatusReactions, useStatusReblogs };
const useFavouriteStatus = (statusId: string) => {
const client = useClient();
const dispatch = useAppDispatch();
const queryClient = useQueryClient();
return useMutation({
mutationKey: ['statuses', 'favourite', statusId],
mutationFn: () => client.statuses.favouriteStatus(statusId),
onMutate: () => dispatch<InteractionsAction>({ type: 'FAVOURITE_REQUEST', statusId }),
onError: () => dispatch<InteractionsAction>({ type: 'UNFAVOURITE_REQUEST', statusId }),
onSettled: (status) => {
dispatch(importEntities({ statuses: [status] }));
queryClient.invalidateQueries({ queryKey: ['accountsLists', 'statusFavourites', statusId] });
},
});
};
const useUnfavouriteStatus = (statusId: string) => {
const client = useClient();
const dispatch = useAppDispatch();
const queryClient = useQueryClient();
return useMutation({
mutationKey: ['statuses', 'favourite', statusId],
mutationFn: () => client.statuses.unfavouriteStatus(statusId),
onMutate: () => dispatch<InteractionsAction>({ type: 'UNFAVOURITE_REQUEST', statusId }),
onError: () => dispatch<InteractionsAction>({ type: 'FAVOURITE_REQUEST', statusId }),
onSettled: (status) => {
dispatch(importEntities({ statuses: [status] }));
queryClient.invalidateQueries({ queryKey: ['accountsLists', 'statusFavourites', statusId] });
},
});
};
const useDislikeStatus = (statusId: string) => {
const client = useClient();
const dispatch = useAppDispatch();
const queryClient = useQueryClient();
return useMutation({
mutationKey: ['statuses', 'dislike', statusId],
mutationFn: () => client.statuses.dislikeStatus(statusId),
onMutate: () => dispatch<InteractionsAction>({ type: 'DISLIKE_REQUEST', statusId }),
onError: () => dispatch<InteractionsAction>({ type: 'UNDISLIKE_REQUEST', statusId }),
onSettled: (status) => {
dispatch(importEntities({ statuses: [status] }));
queryClient.invalidateQueries({ queryKey: ['accountsLists', 'statusDislikes', statusId] });
},
});
};
const useUndislikeStatus = (statusId: string) => {
const client = useClient();
const dispatch = useAppDispatch();
const queryClient = useQueryClient();
return useMutation({
mutationKey: ['statuses', 'dislike', statusId],
mutationFn: () => client.statuses.undislikeStatus(statusId),
onMutate: () => dispatch<InteractionsAction>({ type: 'UNDISLIKE_REQUEST', statusId }),
onError: () => dispatch<InteractionsAction>({ type: 'DISLIKE_REQUEST', statusId }),
onSettled: (status) => {
dispatch(importEntities({ statuses: [status] }));
queryClient.invalidateQueries({ queryKey: ['accountsLists', 'statusDislikes', statusId] });
},
});
};
const useReblogStatus = (statusId: string) => {
const client = useClient();
const dispatch = useAppDispatch();
const queryClient = useQueryClient();
return useMutation({
mutationKey: ['statuses', 'reblog', statusId],
mutationFn: (visibility?: string) => client.statuses.reblogStatus(statusId, visibility),
onMutate: () => dispatch<InteractionsAction>({ type: 'REBLOG_REQUEST', statusId }),
onError: (error) => dispatch<InteractionsAction>({ type: 'REBLOG_FAIL', statusId, error }),
onSettled: (status) => {
dispatch(importEntities({ statuses: [status] }));
queryClient.invalidateQueries({ queryKey: ['accountsLists', 'statusReblogs', statusId] });
},
});
};
const useUnreblogStatus = (statusId: string) => {
const client = useClient();
const dispatch = useAppDispatch();
const queryClient = useQueryClient();
return useMutation({
mutationKey: ['statuses', 'reblog', statusId],
mutationFn: () => client.statuses.unreblogStatus(statusId),
onMutate: () => dispatch<InteractionsAction>({ type: 'UNREBLOG_REQUEST', statusId }),
onError: (error) => dispatch<InteractionsAction>({ type: 'UNREBLOG_FAIL', statusId, error }),
onSettled: (status) => {
dispatch(importEntities({ statuses: [status] }));
queryClient.invalidateQueries({ queryKey: ['accountsLists', 'statusReblogs', statusId] });
},
});
};
export {
useStatusDislikes,
useStatusFavourites,
useStatusReactions,
useStatusReblogs,
useFavouriteStatus,
useUnfavouriteStatus,
useDislikeStatus,
useUndislikeStatus,
useReblogStatus,
useUnreblogStatus,
};

View File

@ -34,8 +34,6 @@ import {
type FavouritesAction,
} from 'pl-fe/actions/favourites';
import {
FAVOURITE_SUCCESS,
UNFAVOURITE_SUCCESS,
BOOKMARK_SUCCESS,
UNBOOKMARK_SUCCESS,
PIN_SUCCESS,
@ -161,10 +159,6 @@ const statusLists = (state = initialState, action: BookmarksAction | EventsActio
return create(state, draft => normalizeList(draft, action.folderId ? `bookmarks:${action.folderId}` : 'bookmarks', action.statuses, action.next));
case BOOKMARKED_STATUSES_EXPAND_SUCCESS:
return create(state, draft => appendToList(draft, action.folderId ? `bookmarks:${action.folderId}` : 'bookmarks', action.statuses, action.next));
case FAVOURITE_SUCCESS:
return create(state, draft => prependOneToList(draft, 'favourites', action.status));
case UNFAVOURITE_SUCCESS:
return create(state, draft => removeOneFromList(draft, 'favourites', action.status));
case BOOKMARK_SUCCESS:
return create(state, draft => addBookmarkToLists(draft, action.status));
case UNBOOKMARK_SUCCESS: