Setting the file. One moment.
Events Order Store · Wix Headless Templates · wix/skills · Skills Docs
ContentsBack to the top of the page events/app/wix/events/ events-order-store.ts
TypeScript · 144 lines · 5 KB
"./events"
;
13 import { fetchOrder } from "./orders" ;
14 import { ORDER_POLL_INTERVAL_MS, ORDER_POLL_TOTAL_MS } from "./orders-core" ;
15 import type { EventDetail, OrderSummary } from "./types" ;
16
17 export interface OrderStoreOptions {
18 /** From the thank-you URL. Empty → the honest "no order" state, nothing is fetched. */
19 orderNumber : string ;
20 eventId : string ;
21 }
22
23 export interface OrderState {
24 orderNumber : string ;
25 eventId : string ;
26 /** The event for context (title, date, calendar link); null until loaded or when unavailable. */
27 event : EventDetail | null ;
28 /** The order with invoice and tickets; null while loading or when unavailable. */
29 order : OrderSummary | null ;
30 /** The order read was refused or found nothing — render the confirmation without details. */
31 orderUnavailable : boolean ;
32 /** Still waiting for Wix to generate the tickets (the PDF link appears when `order.ticketsReady`). */
33 polling : boolean ;
34 loading : boolean ;
35 }
36
37 export interface OrderStore {
38 getState () : OrderState ;
39 subscribe ( listener : ( state : OrderState ) => void ) : () => void ;
40 /** Fetch the event and the order, then poll the order until its tickets exist. Call once when mounted (a browser). */
41 start () : void ;
42 /** Stop polling; drop late responses. */
43 stop () : void ;
44 }
45
46 export function createOrderStore ({ orderNumber , eventId } : OrderStoreOptions ) : OrderStore {
47 let event : EventDetail | null = null ;
48 let order : OrderSummary | null = null ;
49 let orderUnavailable = false ;
50 let polling = false ;
51 let loading = Boolean (orderNumber && eventId);
52 let started = false ;
53 let timer : ReturnType < typeof setTimeout> | null = null ;
54 const listeners = new Set <() => void >();
55 let snapshot : OrderState | null = null ;
56 const emit = () => {
57 snapshot = null ;
58 for ( const fn of listeners) fn ();
59 };
60
61 function getState () : OrderState {
62 if (snapshot) return snapshot;
63 snapshot = { orderNumber, eventId, event, order, orderUnavailable, polling, loading };
64 return snapshot;
65 }
66
67 async function readOrder () : Promise < boolean > {
68 const next = await fetchOrder (eventId, orderNumber);
69 if ( ! started) return true ;
70 if ( ! next) {
71 orderUnavailable = order === null ;
72 return true ; // nothing to poll for
73 }
74 order = next;
75 return next.ticketsReady;
76 }
77
78 function schedule ( elapsed : number ) {
79 if ( ! started || elapsed >= ORDER_POLL_TOTAL_MS ) {
80 polling = false ;
81 emit ();
82 return ;
83 }
84 polling = true ;
85 emit ();
86 timer = setTimeout ( async () => {
87 timer = null ;
88 let ready = true ;
89 try {
90 ready = await readOrder ();
91 } catch {
92 /* a transient failure: keep the last order, try again until the budget is spent */
93 ready = false ;
94 }
95 if ( ! started) return ;
96 if (ready) {
97 polling = false ;
98 emit ();
99 } else {
100 schedule (elapsed + ORDER_POLL_INTERVAL_MS );
101 }
102 }, ORDER_POLL_INTERVAL_MS );
103 }
104
105 return {
106 getState,
107 subscribe ( fn ) {
108 const listener = () : void => { try { fn ( getState ()); } catch (e) { console. error ( "events-order-store listener failed" , e); } };
109 listeners. add (listener);
110 return () => listeners. delete (listener);
111 },
112 start () {
113 if (started) return ;
114 started = true ;
115 if ( ! orderNumber || ! eventId) return ;
116 fetchEventById (eventId)
117 . then (( e ) => {
118 if ( ! started) return ;
119 event = e;
120 emit ();
121 })
122 . catch (() => {});
123 readOrder ()
124 . then (( ready ) => {
125 if ( ! started) return ;
126 loading = false ;
127 emit ();
128 if ( ! ready) schedule ( 0 );
129 })
130 . catch (() => {
131 if ( ! started) return ;
132 // Refused for this visitor scope (or a transient failure) — degrade, never fake details.
133 orderUnavailable = true ;
134 loading = false ;
135 emit ();
136 });
137 },
138 stop () {
139 started = false ;
140 if (timer) clearTimeout (timer);
141 timer = null ;
142 },
143 };
144 }