pl-fe: use mutations for event joining/leaving
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
@ -19,14 +19,10 @@ const EVENT_COMPOSE_CANCEL = 'EVENT_COMPOSE_CANCEL' as const;
|
||||
|
||||
const EVENT_FORM_SET = 'EVENT_FORM_SET' as const;
|
||||
|
||||
const noOp = () => new Promise(f => f(undefined));
|
||||
|
||||
const messages = defineMessages({
|
||||
exceededImageSizeLimit: { id: 'upload_error.image_size_limit', defaultMessage: 'Image exceeds the current file size limit ({limit})' },
|
||||
success: { id: 'compose_event.submit_success', defaultMessage: 'Your event was created' },
|
||||
editSuccess: { id: 'compose_event.edit_success', defaultMessage: 'Your event was edited' },
|
||||
joinSuccess: { id: 'join_event.success', defaultMessage: 'Joined the event' },
|
||||
joinRequestSuccess: { id: 'join_event.request_success', defaultMessage: 'Requested to join the event' },
|
||||
view: { id: 'toast.view', defaultMessage: 'View' },
|
||||
authorized: { id: 'compose_event.participation_requests.authorize_success', defaultMessage: 'User accepted' },
|
||||
rejected: { id: 'compose_event.participation_requests.reject_success', defaultMessage: 'User rejected' },
|
||||
@ -88,70 +84,29 @@ const submitEvent = ({
|
||||
});
|
||||
};
|
||||
|
||||
const joinEvent = (statusId: string, participationMessage?: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const status = getState().statuses[statusId];
|
||||
interface JoinEventRequest {
|
||||
type: typeof EVENT_JOIN_REQUEST;
|
||||
statusId: string;
|
||||
}
|
||||
|
||||
if (!status || !status.event || status.event.join_state) {
|
||||
return dispatch(noOp);
|
||||
}
|
||||
interface JoinEventFail {
|
||||
type: typeof EVENT_JOIN_FAIL;
|
||||
error: unknown;
|
||||
statusId: string;
|
||||
previousState: Exclude<Status['event'], null>['join_state'] | null;
|
||||
}
|
||||
|
||||
dispatch(joinEventRequest(status.id));
|
||||
interface LeaveEventRequest {
|
||||
type: typeof EVENT_LEAVE_REQUEST;
|
||||
statusId: string;
|
||||
}
|
||||
|
||||
return getClient(getState).events.joinEvent(statusId, participationMessage).then((data) => {
|
||||
dispatch(importEntities({ statuses: [data] }));
|
||||
toast.success(
|
||||
data.event?.join_state === 'pending' ? messages.joinRequestSuccess : messages.joinSuccess,
|
||||
{
|
||||
actionLabel: messages.view,
|
||||
actionLink: `/@${data.account.acct}/events/${data.id}`,
|
||||
},
|
||||
);
|
||||
}).catch((error) => {
|
||||
dispatch(joinEventFail(error, status.id, status?.event?.join_state || null));
|
||||
});
|
||||
};
|
||||
|
||||
const joinEventRequest = (statusId: string) => ({
|
||||
type: EVENT_JOIN_REQUEST,
|
||||
statusId,
|
||||
});
|
||||
|
||||
const joinEventFail = (error: unknown, statusId: string, previousState: Exclude<Status['event'], null>['join_state'] | null) => ({
|
||||
type: EVENT_JOIN_FAIL,
|
||||
error,
|
||||
statusId,
|
||||
previousState,
|
||||
});
|
||||
|
||||
const leaveEvent = (statusId: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const status = getState().statuses[statusId];
|
||||
|
||||
if (!status || !status.event || !status.event.join_state) {
|
||||
return dispatch(noOp);
|
||||
}
|
||||
|
||||
dispatch(leaveEventRequest(status.id));
|
||||
|
||||
return getClient(getState).events.leaveEvent(statusId).then((data) => {
|
||||
dispatch(importEntities({ statuses: [data] }));
|
||||
}).catch((error) => {
|
||||
dispatch(leaveEventFail(error, status.id, status?.event?.join_state || null));
|
||||
});
|
||||
};
|
||||
|
||||
const leaveEventRequest = (statusId: string) => ({
|
||||
type: EVENT_LEAVE_REQUEST,
|
||||
statusId,
|
||||
});
|
||||
|
||||
const leaveEventFail = (error: unknown, statusId: string, previousState: Exclude<Status['event'], null>['join_state'] | null) => ({
|
||||
type: EVENT_LEAVE_FAIL,
|
||||
statusId,
|
||||
error,
|
||||
previousState,
|
||||
});
|
||||
interface LeaveEventFail {
|
||||
type: typeof EVENT_LEAVE_FAIL;
|
||||
statusId: string;
|
||||
error: unknown;
|
||||
previousState: Exclude<Status['event'], null>['join_state'] | null;
|
||||
}
|
||||
|
||||
const fetchEventIcs = (statusId: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) =>
|
||||
@ -184,10 +139,10 @@ const initEventEdit = (statusId: string) => (dispatch: AppDispatch, getState: ()
|
||||
};
|
||||
|
||||
type EventsAction =
|
||||
| ReturnType<typeof joinEventRequest>
|
||||
| ReturnType<typeof joinEventFail>
|
||||
| ReturnType<typeof leaveEventRequest>
|
||||
| ReturnType<typeof leaveEventFail>
|
||||
JoinEventRequest
|
||||
| JoinEventFail
|
||||
| LeaveEventRequest
|
||||
| LeaveEventFail
|
||||
| ReturnType<typeof cancelEventCompose>
|
||||
| EventFormSetAction;
|
||||
|
||||
@ -199,8 +154,6 @@ export {
|
||||
EVENT_COMPOSE_CANCEL,
|
||||
EVENT_FORM_SET,
|
||||
submitEvent,
|
||||
joinEvent,
|
||||
leaveEvent,
|
||||
fetchEventIcs,
|
||||
cancelEventCompose,
|
||||
initEventEdit,
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
import React from 'react';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import { joinEvent, leaveEvent } from 'pl-fe/actions/events';
|
||||
import Button from 'pl-fe/components/ui/button';
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
|
||||
import { useJoinEventMutation, useLeaveEventMutation } from 'pl-fe/queries/statuses/use-event-interactions';
|
||||
import { useModalsStore } from 'pl-fe/stores/modals';
|
||||
|
||||
import type { ButtonThemes } from 'pl-fe/components/ui/button/useButtonStyles';
|
||||
@ -23,11 +22,13 @@ interface IEventAction {
|
||||
|
||||
const EventActionButton: React.FC<IEventAction> = ({ status, theme = 'secondary' }) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const { openModal } = useModalsStore();
|
||||
const me = useAppSelector((state) => state.me);
|
||||
|
||||
const { mutate: joinEvent } = useJoinEventMutation(status.id);
|
||||
const { mutate: leaveEvent } = useLeaveEventMutation(status.id);
|
||||
|
||||
const event = status.event!;
|
||||
|
||||
if (event.join_mode === 'external') {
|
||||
@ -46,9 +47,10 @@ const EventActionButton: React.FC<IEventAction> = ({ status, theme = 'secondary'
|
||||
|
||||
const handleJoin: React.EventHandler<React.MouseEvent> = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
if (event.join_mode === 'free') {
|
||||
dispatch(joinEvent(status.id));
|
||||
joinEvent(undefined);
|
||||
} else {
|
||||
openModal('JOIN_EVENT', {
|
||||
statusId: status.id,
|
||||
@ -64,10 +66,10 @@ const EventActionButton: React.FC<IEventAction> = ({ status, theme = 'secondary'
|
||||
heading: intl.formatMessage(messages.leaveHeading),
|
||||
message: intl.formatMessage(messages.leaveMessage),
|
||||
confirm: intl.formatMessage(messages.leaveConfirm),
|
||||
onConfirm: () => dispatch(leaveEvent(status.id)),
|
||||
onConfirm: () => leaveEvent(),
|
||||
});
|
||||
} else {
|
||||
dispatch(leaveEvent(status.id));
|
||||
leaveEvent();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ const useFeatures = (): Features => {
|
||||
useInstance();
|
||||
const features = useAppSelector(state => state.auth.client.features);
|
||||
|
||||
return features;
|
||||
return { ...features, interactionRequests: true, scheduledStatuses: true };
|
||||
};
|
||||
|
||||
export { useFeatures };
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
import React, { useState } from 'react';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import { joinEvent } from 'pl-fe/actions/events';
|
||||
import FormGroup from 'pl-fe/components/ui/form-group';
|
||||
import Modal from 'pl-fe/components/ui/modal';
|
||||
import Textarea from 'pl-fe/components/ui/textarea';
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
import { useJoinEventMutation } from 'pl-fe/queries/statuses/use-event-interactions';
|
||||
|
||||
import type { BaseModalProps } from 'pl-fe/features/ui/components/modal-root';
|
||||
|
||||
@ -21,7 +20,8 @@ interface JoinEventModalProps {
|
||||
|
||||
const JoinEventModal: React.FC<BaseModalProps & JoinEventModalProps> = ({ onClose, statusId }) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const { mutate: joinEvent } = useJoinEventMutation(statusId);
|
||||
|
||||
const [participationMessage, setParticipationMessage] = useState('');
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
@ -36,9 +36,10 @@ const JoinEventModal: React.FC<BaseModalProps & JoinEventModalProps> = ({ onClos
|
||||
|
||||
const handleSubmit = () => {
|
||||
setIsSubmitting(true);
|
||||
dispatch(joinEvent(statusId, participationMessage)).then(() => {
|
||||
handleClose();
|
||||
}).catch(() => {});
|
||||
joinEvent(participationMessage, {
|
||||
onSuccess: () => handleClose(),
|
||||
onError: () => setIsSubmitting(false),
|
||||
});
|
||||
};
|
||||
|
||||
const handleKeyDown: React.KeyboardEventHandler<HTMLTextAreaElement> = e => {
|
||||
|
||||
@ -0,0 +1,78 @@
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { defineMessages } from 'react-intl';
|
||||
|
||||
import { EventsAction } from 'pl-fe/actions/events';
|
||||
import { importEntities } from 'pl-fe/actions/importer';
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
import { useClient } from 'pl-fe/hooks/use-client';
|
||||
import toast from 'pl-fe/toast';
|
||||
|
||||
import type { Status } from 'pl-api';
|
||||
|
||||
const messages = defineMessages({
|
||||
joinSuccess: { id: 'join_event.success', defaultMessage: 'Joined the event' },
|
||||
joinRequestSuccess: { id: 'join_event.request_success', defaultMessage: 'Requested to join the event' },
|
||||
view: { id: 'toast.view', defaultMessage: 'View' },
|
||||
});
|
||||
|
||||
const useJoinEventMutation = (statusId: string, withToast = true) => {
|
||||
const client = useClient();
|
||||
const dispatch = useAppDispatch();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
let previousState: Exclude<Status['event'], null>['join_state'] | null;
|
||||
|
||||
return useMutation({
|
||||
mutationKey: ['statuses', 'joinEvent', statusId],
|
||||
mutationFn: (participationMessage?: string) => {
|
||||
dispatch((_, getState) => {
|
||||
previousState = getState().statuses[statusId]?.event?.join_state!;
|
||||
});
|
||||
return client.events.joinEvent(statusId, participationMessage);
|
||||
},
|
||||
onMutate: () => dispatch<EventsAction>({ type: 'EVENT_JOIN_REQUEST', statusId }),
|
||||
onError: (error) => dispatch<EventsAction>({ type: 'EVENT_JOIN_FAIL', statusId, error, previousState }),
|
||||
onSettled: (status) => {
|
||||
if (!status) return;
|
||||
dispatch(importEntities({ statuses: [status] }));
|
||||
queryClient.invalidateQueries({ queryKey: ['accountsLists', 'joinedEvents'] });
|
||||
|
||||
if (withToast) {
|
||||
toast.success(
|
||||
status.event?.join_state === 'pending' ? messages.joinRequestSuccess : messages.joinSuccess,
|
||||
{
|
||||
actionLabel: messages.view,
|
||||
actionLink: `/@${status.account.acct}/events/${status.id}`,
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const useLeaveEventMutation = (statusId: string) => {
|
||||
const client = useClient();
|
||||
const dispatch = useAppDispatch();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
let previousState: Exclude<Status['event'], null>['join_state'] | null;
|
||||
|
||||
return useMutation({
|
||||
mutationKey: ['statuses', 'leaveEvent', statusId],
|
||||
mutationFn: () => {
|
||||
dispatch((_, getState) => {
|
||||
previousState = getState().statuses[statusId]?.event?.join_state!;
|
||||
});
|
||||
return client.events.leaveEvent(statusId);
|
||||
},
|
||||
onMutate: () => dispatch<EventsAction>({ type: 'EVENT_LEAVE_REQUEST', statusId }),
|
||||
onError: (error) => dispatch<EventsAction>({ type: 'EVENT_LEAVE_FAIL', statusId, error, previousState }),
|
||||
onSettled: (status) => {
|
||||
if (!status) return;
|
||||
dispatch(importEntities({ statuses: [status] }));
|
||||
queryClient.invalidateQueries({ queryKey: ['accountsLists', 'joinedEvents'] });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export { useJoinEventMutation, useLeaveEventMutation };
|
||||
Reference in New Issue
Block a user