Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | 13x 13x 13x 12x 12x 13x 13x 55x 55x 55x 55x 55x 36x 36x 36x 55x 6x 5x 5x 1x 1x 1x 55x 10x 8x 6x 6x 55x | import { useEffect, useState } from "react";
import { useQueryClient } from "react-query";
import NiceModal from "@ebay/nice-modal-react";
import { showMessage } from "@common/Toast";
import { QKEY_EVENT_TICKETS } from "@constants/queryKeys";
import { useCheckInEventTickets } from "@services/api/products/tickets";
import { useCustomThemeV2 } from "@theme/hooks/useCustomThemeV2";
import { EVENT_TICKET_CHECK_IN_MODAL } from "modals/modal_names";
import { CHECK_IN_SUCCESS_TOAST } from "./EventTicketCheckInModal";
import { EventTicketRow } from "./ticket.types";
// ONE lock for the whole surface, not one per hook instance: the table row and
// the detail panel each mount this hook (the panel's modal args are fixed by
// `useTable`, so it cannot borrow the tab's instance), and a per-instance lock
// left the row's "Check In" clickable beside the panel while the panel's
// request was in flight. Check-in is one-way on the API — a duplicate PATCH is
// not undoable — so the guard must be a plain variable read synchronously:
// React state re-renders too late for two clicks landing in the same frame.
let activeCheckInId: EventTicketRow["id"] | null = null;
const lockListeners = new Set<(id: EventTicketRow["id"] | null) => void>();
const setActiveCheckInId = (id: EventTicketRow["id"] | null) => {
activeCheckInId = id;
lockListeners.forEach((notify) => notify(id));
};
/** Test-only: clears the shared lock a case may have left held. */
export const resetTicketCheckInLock = () => setActiveCheckInId(null);
/**
* Manual check-in, confirmation and all (frames 7412-195380 / 7412-197026).
*
* Shared by the table's row button and the detail panel so both go through the
* same confirm → mutate → toast sequence and neither can skip the confirmation.
* The mutation lives here rather than in the modal because the modal is dismissed
* the moment it is confirmed — the caller's own row/panel shows the pending state.
*/
export const useTicketCheckIn = (eventId?: string) => {
const queryClient = useQueryClient();
const { isMobileView } = useCustomThemeV2();
const mutation = useCheckInEventTickets(eventId);
// The shared lock mirrored into state so every mounted caller re-renders its
// disabled/pending UI when any of them starts or finishes a check-in.
const [checkingInId, setCheckingInId] = useState(activeCheckInId);
useEffect(() => {
lockListeners.add(setCheckingInId);
return () => {
lockListeners.delete(setCheckingInId);
};
}, []);
// Bulk endpoint with a single-element array (see tickets.ts). The list is
// refetched on settle either way — on failure (e.g. the ticket was scanned
// elsewhere in the meantime) the refetch resyncs the row's real state, and the
// axios interceptor already surfaces the error toast.
const checkIn = (row: EventTicketRow) => {
if (activeCheckInId !== null) return;
setActiveCheckInId(row.id);
mutation.mutate([row.id], {
onSuccess: () => {
showMessage(
"Success",
CHECK_IN_SUCCESS_TOAST,
!isMobileView,
"",
undefined,
undefined,
// Frame 7412-197026 is a single line: the message itself, no "Success"
// heading above it.
{ showTitle: false },
);
},
onSettled: async () => {
// Hold the lock through the refetch, not just the mutation: released on
// settle, "Check In" came back for the beat before `checkedInAt` landed
// on the row — long enough for a second, duplicate submit.
await queryClient.invalidateQueries(QKEY_EVENT_TICKETS);
setActiveCheckInId(null);
},
});
};
const requestCheckIn = (row: EventTicketRow) => {
if (activeCheckInId !== null) return;
NiceModal.show(EVENT_TICKET_CHECK_IN_MODAL, {
row,
onConfirm: (confirmed: EventTicketRow) => {
NiceModal.remove(EVENT_TICKET_CHECK_IN_MODAL);
checkIn(confirmed);
},
});
};
return { requestCheckIn, checkingInId };
};
|