@stamprally/ui 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +229 -124
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +51 -12
- package/dist/index.d.ts +51 -12
- package/dist/index.js +229 -124
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -6,11 +6,17 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
|
6
6
|
|
|
7
7
|
// src/index.tsx
|
|
8
8
|
var label = (dictionary, locale, key, fallback) => dictionary?.[locale]?.[key] ?? fallback;
|
|
9
|
+
var join = (...names) => {
|
|
10
|
+
const value = names.filter((name) => name !== void 0 && name !== "").join(" ");
|
|
11
|
+
return value === "" ? void 0 : value;
|
|
12
|
+
};
|
|
9
13
|
function DefaultCondition({
|
|
10
14
|
condition,
|
|
11
15
|
dictionary,
|
|
12
16
|
locale,
|
|
13
17
|
disabled,
|
|
18
|
+
classNames,
|
|
19
|
+
styles,
|
|
14
20
|
onSubmit
|
|
15
21
|
}) {
|
|
16
22
|
const [value, setValue] = react.useState("");
|
|
@@ -32,17 +38,9 @@ function DefaultCondition({
|
|
|
32
38
|
setError(text("verificationFailed", "Verification failed."));
|
|
33
39
|
}
|
|
34
40
|
};
|
|
35
|
-
const
|
|
36
|
-
event.preventDefault();
|
|
37
|
-
if (condition.type === "gps") return;
|
|
38
|
-
if (condition.type === "nfc") return;
|
|
39
|
-
void verify(condition.type === "passcode" ? value : value);
|
|
40
|
-
};
|
|
41
|
-
const scanQr = () => {
|
|
42
|
-
if (videoRef.current === null) return;
|
|
41
|
+
const readLocation = () => {
|
|
43
42
|
setStatus("loading");
|
|
44
|
-
|
|
45
|
-
void core.readQrContext(videoRef.current).then((result) => {
|
|
43
|
+
void core.getCurrentGeoContext().then((result) => {
|
|
46
44
|
if (result.ok) void verify(result.value);
|
|
47
45
|
else {
|
|
48
46
|
setStatus("error");
|
|
@@ -50,10 +48,9 @@ function DefaultCondition({
|
|
|
50
48
|
}
|
|
51
49
|
});
|
|
52
50
|
};
|
|
53
|
-
const
|
|
51
|
+
const readNfc = () => {
|
|
54
52
|
setStatus("loading");
|
|
55
|
-
|
|
56
|
-
void core.getCurrentGeoContext().then((result) => {
|
|
53
|
+
void core.readNfcContext().then((result) => {
|
|
57
54
|
if (result.ok) void verify(result.value);
|
|
58
55
|
else {
|
|
59
56
|
setStatus("error");
|
|
@@ -61,10 +58,10 @@ function DefaultCondition({
|
|
|
61
58
|
}
|
|
62
59
|
});
|
|
63
60
|
};
|
|
64
|
-
const
|
|
61
|
+
const scanQr = () => {
|
|
62
|
+
if (videoRef.current === null) return;
|
|
65
63
|
setStatus("loading");
|
|
66
|
-
|
|
67
|
-
void core.readNfcContext().then((result) => {
|
|
64
|
+
void core.readQrContext(videoRef.current).then((result) => {
|
|
68
65
|
if (result.ok) void verify(result.value);
|
|
69
66
|
else {
|
|
70
67
|
setStatus("error");
|
|
@@ -99,45 +96,76 @@ function DefaultCondition({
|
|
|
99
96
|
"button",
|
|
100
97
|
{
|
|
101
98
|
type: "button",
|
|
102
|
-
className: "sry-action",
|
|
99
|
+
className: join("sry-action", classNames?.action, classNames?.button),
|
|
100
|
+
style: styles?.button ?? styles?.action,
|
|
103
101
|
disabled: disabled || !core.isQrSupported(),
|
|
104
102
|
onClick: scanQr,
|
|
105
103
|
children: text("scanQr", "Scan QR")
|
|
106
104
|
}
|
|
107
105
|
)
|
|
108
106
|
] }),
|
|
109
|
-
condition.type === "gps" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
107
|
+
condition.type === "gps" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
108
|
+
"button",
|
|
109
|
+
{
|
|
110
|
+
type: "button",
|
|
111
|
+
className: join("sry-action", classNames?.action, classNames?.button),
|
|
112
|
+
style: styles?.button ?? styles?.action,
|
|
113
|
+
disabled,
|
|
114
|
+
onClick: readLocation,
|
|
115
|
+
children: text("checkLocation", "Check location")
|
|
116
|
+
}
|
|
117
|
+
),
|
|
110
118
|
condition.type === "nfc" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
111
119
|
"button",
|
|
112
120
|
{
|
|
113
121
|
type: "button",
|
|
114
|
-
className: "sry-action",
|
|
122
|
+
className: join("sry-action", classNames?.action, classNames?.button),
|
|
123
|
+
style: styles?.button ?? styles?.action,
|
|
115
124
|
disabled: disabled || !core.isNfcSupported(),
|
|
116
125
|
onClick: readNfc,
|
|
117
126
|
children: text("readNfc", "Read NFC")
|
|
118
127
|
}
|
|
119
128
|
),
|
|
120
|
-
(condition.type === "passcode" || condition.type === "custom" || condition.type === "qr") && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
{
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
129
|
+
(condition.type === "passcode" || condition.type === "custom" || condition.type === "qr") && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
130
|
+
"form",
|
|
131
|
+
{
|
|
132
|
+
onSubmit: (event) => {
|
|
133
|
+
event.preventDefault();
|
|
134
|
+
void verify(value);
|
|
135
|
+
},
|
|
136
|
+
children: [
|
|
137
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
138
|
+
text(
|
|
139
|
+
condition.type === "passcode" ? "passcode" : "proof",
|
|
140
|
+
condition.type === "passcode" ? "Passcode" : "Proof"
|
|
141
|
+
),
|
|
142
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
143
|
+
"input",
|
|
144
|
+
{
|
|
145
|
+
"aria-label": text("proof", "Proof"),
|
|
146
|
+
value,
|
|
147
|
+
onChange: (event) => setValue(event.target.value)
|
|
148
|
+
}
|
|
149
|
+
)
|
|
150
|
+
] }),
|
|
151
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
152
|
+
"button",
|
|
153
|
+
{
|
|
154
|
+
type: "submit",
|
|
155
|
+
className: join("sry-action", classNames?.action, classNames?.button),
|
|
156
|
+
style: styles?.button ?? styles?.action,
|
|
157
|
+
disabled: disabled || value.trim() === "",
|
|
158
|
+
children: text("checkIn", "Check in")
|
|
159
|
+
}
|
|
160
|
+
)
|
|
161
|
+
]
|
|
162
|
+
}
|
|
163
|
+
),
|
|
137
164
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
138
165
|
"div",
|
|
139
166
|
{
|
|
140
|
-
className: "sry-feedback",
|
|
167
|
+
className: join("sry-feedback", classNames?.feedback),
|
|
168
|
+
style: styles?.feedback,
|
|
141
169
|
"aria-live": "polite",
|
|
142
170
|
role: status === "error" ? "alert" : void 0,
|
|
143
171
|
children: [
|
|
@@ -149,6 +177,35 @@ function DefaultCondition({
|
|
|
149
177
|
)
|
|
150
178
|
] });
|
|
151
179
|
}
|
|
180
|
+
function DefaultRewardCard({
|
|
181
|
+
reward,
|
|
182
|
+
state,
|
|
183
|
+
locale,
|
|
184
|
+
dictionary,
|
|
185
|
+
onClaim,
|
|
186
|
+
classNames,
|
|
187
|
+
styles
|
|
188
|
+
}) {
|
|
189
|
+
const status = state?.status ?? "LOCKED";
|
|
190
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
191
|
+
"button",
|
|
192
|
+
{
|
|
193
|
+
type: "button",
|
|
194
|
+
className: join(classNames?.reward, classNames?.button),
|
|
195
|
+
style: styles?.reward ?? styles?.button,
|
|
196
|
+
disabled: status !== "AVAILABLE" || onClaim === void 0,
|
|
197
|
+
onClick: () => {
|
|
198
|
+
if (onClaim !== void 0) void onClaim(reward.id);
|
|
199
|
+
},
|
|
200
|
+
children: [
|
|
201
|
+
core.resolveLocalizedText(reward.title, locale),
|
|
202
|
+
" (",
|
|
203
|
+
label(dictionary, locale, `status.${status.toLowerCase()}`, status),
|
|
204
|
+
")"
|
|
205
|
+
]
|
|
206
|
+
}
|
|
207
|
+
);
|
|
208
|
+
}
|
|
152
209
|
function RallyViewer({
|
|
153
210
|
config: providedConfig,
|
|
154
211
|
client,
|
|
@@ -156,12 +213,22 @@ function RallyViewer({
|
|
|
156
213
|
locale,
|
|
157
214
|
dictionary,
|
|
158
215
|
classNames = {},
|
|
216
|
+
styles = {},
|
|
159
217
|
style,
|
|
160
|
-
customConditionRenderers = {}
|
|
218
|
+
customConditionRenderers = {},
|
|
219
|
+
headerSlot,
|
|
220
|
+
footerSlot,
|
|
221
|
+
renderSpotCard,
|
|
222
|
+
renderRewardCard,
|
|
223
|
+
renderStatusBadge,
|
|
224
|
+
renderVerifyingState,
|
|
225
|
+
renderSuccessFeedback,
|
|
226
|
+
renderErrorFeedback
|
|
161
227
|
}) {
|
|
162
228
|
const config = providedConfig ?? client?.getConfig() ?? adapter?.config;
|
|
163
229
|
const [state, setState] = react.useState(() => client?.getState() ?? null);
|
|
164
230
|
const [busy, setBusy] = react.useState(null);
|
|
231
|
+
const [feedback, setFeedback] = react.useState(null);
|
|
165
232
|
react.useEffect(() => {
|
|
166
233
|
if (client === void 0) return;
|
|
167
234
|
const unsubscribe = client.subscribe(setState);
|
|
@@ -169,20 +236,26 @@ function RallyViewer({
|
|
|
169
236
|
return unsubscribe;
|
|
170
237
|
}, [client]);
|
|
171
238
|
if (config === void 0) throw new Error("RallyViewer requires config, client, or adapter.");
|
|
239
|
+
const currentState = state ?? {
|
|
240
|
+
rallyId: config.id,
|
|
241
|
+
userId: null,
|
|
242
|
+
records: [],
|
|
243
|
+
rewards: [],
|
|
244
|
+
updatedAt: ""
|
|
245
|
+
};
|
|
172
246
|
const progress = react.useMemo(
|
|
173
|
-
() => core.calculateProgress(
|
|
174
|
-
|
|
175
|
-
config
|
|
176
|
-
),
|
|
177
|
-
[config, state]
|
|
247
|
+
() => core.calculateProgress(currentState, config),
|
|
248
|
+
[config, currentState]
|
|
178
249
|
);
|
|
179
250
|
const checkIn = adapter?.onCheckIn ?? (client === void 0 ? void 0 : (spotId, proof, options) => client.checkIn(spotId, proof, options));
|
|
180
251
|
const claim = adapter?.onClaimReward ?? (client === void 0 ? void 0 : (rewardId, options) => client.claimReward(rewardId, options));
|
|
181
252
|
const submit = react.useCallback(
|
|
182
|
-
(spotId, proof) => {
|
|
183
|
-
if (checkIn === void 0) return
|
|
253
|
+
async (spotId, proof) => {
|
|
254
|
+
if (checkIn === void 0) return void 0;
|
|
184
255
|
setBusy(spotId);
|
|
185
|
-
|
|
256
|
+
const result = await checkIn(spotId, proof).finally(() => setBusy(null));
|
|
257
|
+
setFeedback(result);
|
|
258
|
+
return result;
|
|
186
259
|
},
|
|
187
260
|
[checkIn]
|
|
188
261
|
);
|
|
@@ -190,111 +263,143 @@ function RallyViewer({
|
|
|
190
263
|
"section",
|
|
191
264
|
{
|
|
192
265
|
className: classNames.root,
|
|
193
|
-
style,
|
|
266
|
+
style: styles.root ?? style,
|
|
194
267
|
"aria-label": label(dictionary, locale, "viewer", "Stamp rally"),
|
|
195
268
|
children: [
|
|
196
|
-
/* @__PURE__ */ jsxRuntime.
|
|
197
|
-
|
|
198
|
-
"
|
|
199
|
-
|
|
200
|
-
"
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
269
|
+
/* @__PURE__ */ jsxRuntime.jsxs("header", { className: classNames.header, style: styles.header, children: [
|
|
270
|
+
typeof headerSlot === "function" ? headerSlot({ config, state: currentState }) : headerSlot,
|
|
271
|
+
headerSlot === void 0 && /* @__PURE__ */ jsxRuntime.jsx("h1", { children: core.resolveLocalizedText(config.title, locale) || config.id }),
|
|
272
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
273
|
+
"progress",
|
|
274
|
+
{
|
|
275
|
+
"aria-label": label(dictionary, locale, "progress", "Progress"),
|
|
276
|
+
max: 100,
|
|
277
|
+
value: progress.percentage
|
|
278
|
+
}
|
|
279
|
+
),
|
|
280
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
281
|
+
progress.acquired,
|
|
282
|
+
"/",
|
|
283
|
+
progress.total
|
|
284
|
+
] })
|
|
210
285
|
] }),
|
|
211
|
-
|
|
212
|
-
|
|
286
|
+
busy !== null && renderVerifyingState?.(),
|
|
287
|
+
feedback?.ok === true && renderSuccessFeedback?.(feedback),
|
|
288
|
+
feedback?.ok === false && renderErrorFeedback?.(
|
|
289
|
+
"message" in feedback.error ? feedback.error.message : feedback.error.code
|
|
290
|
+
),
|
|
291
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { children: config.spots.map((spot) => {
|
|
292
|
+
const claimed = currentState.records.some((record) => record.stampId === spot.id);
|
|
293
|
+
const status = busy === spot.id ? "verifying" : claimed ? "claimed" : "available";
|
|
294
|
+
const children = spot.conditions.map((condition) => {
|
|
213
295
|
const Renderer = customConditionRenderers[condition.type] ?? DefaultCondition;
|
|
214
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
215
|
-
"
|
|
296
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
297
|
+
"div",
|
|
216
298
|
{
|
|
217
299
|
className: classNames.condition,
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
300
|
+
style: styles.condition,
|
|
301
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
302
|
+
Renderer,
|
|
303
|
+
{
|
|
304
|
+
spot,
|
|
305
|
+
condition,
|
|
306
|
+
locale,
|
|
307
|
+
...dictionary === void 0 ? {} : { dictionary },
|
|
308
|
+
disabled: busy !== null || claimed,
|
|
309
|
+
...Object.keys(classNames).length === 0 ? {} : { classNames },
|
|
310
|
+
...Object.keys(styles).length === 0 ? {} : { styles },
|
|
311
|
+
onSubmit: (proof) => submit(spot.id, proof)
|
|
312
|
+
}
|
|
313
|
+
)
|
|
232
314
|
},
|
|
233
315
|
`${spot.id}-${condition.type}-${JSON.stringify(condition)}`
|
|
234
316
|
);
|
|
235
|
-
})
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
317
|
+
});
|
|
318
|
+
const props = {
|
|
319
|
+
spot,
|
|
320
|
+
state: currentState,
|
|
321
|
+
status,
|
|
322
|
+
locale,
|
|
323
|
+
...dictionary === void 0 ? {} : { dictionary },
|
|
324
|
+
children
|
|
325
|
+
};
|
|
326
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: classNames.card, style: styles.card, children: renderSpotCard?.(props) ?? /* @__PURE__ */ jsxRuntime.jsxs("article", { children: [
|
|
327
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { children: core.resolveLocalizedText(spot.name, locale) }),
|
|
328
|
+
renderStatusBadge?.({ status }) ?? /* @__PURE__ */ jsxRuntime.jsx("span", { className: classNames.badge, style: styles.badge, children: status }),
|
|
329
|
+
children
|
|
330
|
+
] }) }, spot.id);
|
|
331
|
+
}) }),
|
|
332
|
+
/* @__PURE__ */ jsxRuntime.jsx("section", { "aria-label": label(dictionary, locale, "rewards", "Rewards"), children: config.rewards.map((reward) => {
|
|
333
|
+
const props = {
|
|
240
334
|
reward,
|
|
241
|
-
state:
|
|
335
|
+
state: currentState.rewards.find((item) => item.rewardId === reward.id),
|
|
242
336
|
locale,
|
|
243
337
|
...dictionary === void 0 ? {} : { dictionary },
|
|
244
|
-
...classNames.reward === void 0 ? {} : { className: classNames.reward },
|
|
245
338
|
onClaim: claim
|
|
246
|
-
}
|
|
247
|
-
reward.id
|
|
248
|
-
)
|
|
339
|
+
};
|
|
340
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: classNames.reward, style: styles.reward, children: renderRewardCard?.(props) ?? /* @__PURE__ */ jsxRuntime.jsx(DefaultRewardCard, { ...props, classNames, styles }) }, reward.id);
|
|
341
|
+
}) }),
|
|
342
|
+
/* @__PURE__ */ jsxRuntime.jsx("footer", { className: classNames.footer, style: styles.footer, children: footerSlot })
|
|
249
343
|
]
|
|
250
344
|
}
|
|
251
345
|
);
|
|
252
346
|
}
|
|
253
|
-
function
|
|
254
|
-
|
|
347
|
+
function StampSheet({
|
|
348
|
+
config,
|
|
255
349
|
state,
|
|
256
|
-
|
|
350
|
+
title,
|
|
351
|
+
progress,
|
|
352
|
+
locale = "en",
|
|
257
353
|
dictionary,
|
|
258
|
-
|
|
259
|
-
|
|
354
|
+
classNames = {},
|
|
355
|
+
styles = {},
|
|
356
|
+
style,
|
|
357
|
+
headerSlot,
|
|
358
|
+
footerSlot,
|
|
359
|
+
renderSpotCard,
|
|
360
|
+
renderStatusBadge
|
|
260
361
|
}) {
|
|
261
|
-
const
|
|
362
|
+
const currentState = state ?? {
|
|
363
|
+
rallyId: config.id,
|
|
364
|
+
userId: null,
|
|
365
|
+
records: [],
|
|
366
|
+
rewards: [],
|
|
367
|
+
updatedAt: ""
|
|
368
|
+
};
|
|
369
|
+
const current = progress ?? core.calculateProgress(currentState, config);
|
|
262
370
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
263
|
-
"
|
|
371
|
+
"section",
|
|
264
372
|
{
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
onClick: () => {
|
|
269
|
-
if (onClaim !== void 0) void onClaim(reward.id);
|
|
270
|
-
},
|
|
373
|
+
className: classNames.root,
|
|
374
|
+
style: styles.root ?? style,
|
|
375
|
+
"aria-label": title ?? label(dictionary, locale, "stampSheet", "Stamp sheet"),
|
|
271
376
|
children: [
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
377
|
+
/* @__PURE__ */ jsxRuntime.jsxs("header", { className: classNames.header, style: styles.header, children: [
|
|
378
|
+
typeof headerSlot === "function" ? headerSlot({ config, state: currentState }) : headerSlot ?? /* @__PURE__ */ jsxRuntime.jsx("h2", { children: title ?? core.resolveLocalizedText(config.title, locale) }),
|
|
379
|
+
/* @__PURE__ */ jsxRuntime.jsx("progress", { max: 100, value: current.percentage })
|
|
380
|
+
] }),
|
|
381
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { children: config.spots.map((spot) => {
|
|
382
|
+
const claimed = currentState.records.some((record) => record.stampId === spot.id);
|
|
383
|
+
const status = claimed ? "claimed" : "available";
|
|
384
|
+
const props = {
|
|
385
|
+
spot,
|
|
386
|
+
state: currentState,
|
|
387
|
+
status,
|
|
388
|
+
locale,
|
|
389
|
+
...dictionary === void 0 ? {} : { dictionary },
|
|
390
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: classNames.slot, style: styles.slot, children: claimed ? "\u2713" : "\u25CB" })
|
|
391
|
+
};
|
|
392
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: classNames.card, style: styles.card, children: renderSpotCard?.(props) ?? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: classNames.slot, style: styles.slot, children: [
|
|
393
|
+
core.resolveLocalizedText(spot.name, locale),
|
|
394
|
+
" ",
|
|
395
|
+
renderStatusBadge?.({ status }) ?? (claimed ? "\u2713" : "\u25CB")
|
|
396
|
+
] }) }, spot.id);
|
|
397
|
+
}) }),
|
|
398
|
+
/* @__PURE__ */ jsxRuntime.jsx("footer", { className: classNames.footer, style: styles.footer, children: footerSlot })
|
|
276
399
|
]
|
|
277
400
|
}
|
|
278
401
|
);
|
|
279
402
|
}
|
|
280
|
-
function StampSheet({
|
|
281
|
-
config,
|
|
282
|
-
state,
|
|
283
|
-
title,
|
|
284
|
-
progress,
|
|
285
|
-
locale = "en",
|
|
286
|
-
dictionary
|
|
287
|
-
}) {
|
|
288
|
-
const current = progress ?? core.calculateProgress(
|
|
289
|
-
state ?? { rallyId: config.id, userId: null, records: [], rewards: [], updatedAt: "" },
|
|
290
|
-
config
|
|
291
|
-
);
|
|
292
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("section", { "aria-label": title ?? label(dictionary, locale, "stampSheet", "Stamp sheet"), children: [
|
|
293
|
-
/* @__PURE__ */ jsxRuntime.jsx("h2", { children: title ?? core.resolveLocalizedText(config.title, locale) }),
|
|
294
|
-
/* @__PURE__ */ jsxRuntime.jsx("progress", { max: 100, value: current.percentage }),
|
|
295
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { children: config.spots.map((spot) => /* @__PURE__ */ jsxRuntime.jsx("span", { children: state?.records.some((record) => record.stampId === spot.id) ? "\u2713" : "\u25CB" }, spot.id)) })
|
|
296
|
-
] });
|
|
297
|
-
}
|
|
298
403
|
|
|
299
404
|
exports.RallyViewer = RallyViewer;
|
|
300
405
|
exports.StampSheet = StampSheet;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.tsx"],"names":["useState","useRef","readQrContext","getCurrentGeoContext","readNfcContext","jsxs","Fragment","jsx","isQrSupported","isNfcSupported","useEffect","useMemo","calculateProgress","useCallback","resolveLocalizedText"],"mappings":"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","file":"index.cjs","sourcesContent":["import type {\n CheckInOptions,\n CheckInResult,\n ClaimOptions,\n ClaimResult,\n LocaleDictionary,\n PublicCheckInCondition,\n PublicRallyConfig,\n PublicSpotItem,\n RallyConfig,\n Reward,\n RewardState,\n StampRallyClient,\n StampRallyProgress,\n StampRallyState,\n UserRallyState,\n} from \"@stamprally/core\";\nimport {\n calculateProgress,\n getCurrentGeoContext,\n isNfcSupported,\n isQrSupported,\n readNfcContext,\n readQrContext,\n resolveLocalizedText,\n} from \"@stamprally/core\";\nimport {\n type ComponentType,\n type CSSProperties,\n type FormEvent,\n type ReactElement,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nexport interface ConditionRendererProps<TLocale extends string = string> {\n readonly spot: PublicSpotItem<TLocale>;\n readonly condition: PublicCheckInCondition;\n readonly locale: TLocale;\n readonly dictionary?: LocaleDictionary<TLocale>;\n readonly disabled: boolean;\n // biome-ignore lint/suspicious/noConfusingVoidType: custom renderers may fire-and-forget while built-ins await feedback.\n readonly onSubmit: (proof: unknown) => void | Promise<unknown>;\n}\nexport type ConditionRenderer<TLocale extends string = string> = ComponentType<\n ConditionRendererProps<TLocale>\n>;\nexport type ViewerClassName = \"root\" | \"condition\" | \"action\" | \"feedback\" | \"reward\";\nexport type ViewerStyle = CSSProperties & {\n readonly [key: `--${string}`]: string | number | undefined;\n};\n\nexport interface RallyViewerAdapter<TLocale extends string = string> {\n readonly config: PublicRallyConfig<TLocale>;\n readonly onCheckIn: (\n spotId: string,\n proof: unknown,\n options?: CheckInOptions,\n ) => Promise<CheckInResult>;\n readonly onClaimReward?: (rewardId: string, options?: ClaimOptions) => Promise<ClaimResult>;\n}\nexport interface RallyViewerProps<TLocale extends string = string> {\n readonly config?: PublicRallyConfig<TLocale>;\n readonly client?: StampRallyClient;\n readonly adapter?: RallyViewerAdapter<TLocale>;\n readonly locale: TLocale;\n readonly dictionary?: LocaleDictionary<TLocale>;\n readonly classNames?: Partial<Record<ViewerClassName, string>>;\n readonly style?: ViewerStyle;\n readonly customConditionRenderers?: Partial<\n Record<PublicCheckInCondition[\"type\"], ConditionRenderer<TLocale>>\n >;\n}\n\nconst label = <TLocale extends string>(\n dictionary: LocaleDictionary<TLocale> | undefined,\n locale: TLocale,\n key: string,\n fallback: string,\n): string => dictionary?.[locale]?.[key] ?? fallback;\n\ntype VerificationStatus = \"idle\" | \"loading\" | \"success\" | \"error\";\n\nfunction DefaultCondition<TLocale extends string>({\n condition,\n dictionary,\n locale,\n disabled,\n onSubmit,\n}: ConditionRendererProps<TLocale>): ReactElement {\n const [value, setValue] = useState(\"\");\n const [status, setStatus] = useState<VerificationStatus>(\"idle\");\n const [error, setError] = useState<string | null>(null);\n const videoRef = useRef<HTMLVideoElement>(null);\n const text = (key: string, fallback: string): string => label(dictionary, locale, key, fallback);\n const verify = async (proof: unknown): Promise<void> => {\n setStatus(\"loading\");\n setError(null);\n try {\n const result = await onSubmit(proof);\n if (\n result !== undefined &&\n typeof result === \"object\" &&\n result !== null &&\n \"ok\" in result &&\n result.ok === false\n ) {\n setStatus(\"error\");\n setError(text(\"verificationFailed\", \"Verification failed.\"));\n } else setStatus(\"success\");\n } catch {\n setStatus(\"error\");\n setError(text(\"verificationFailed\", \"Verification failed.\"));\n }\n };\n const submit = (event: FormEvent): void => {\n event.preventDefault();\n if (condition.type === \"gps\") return;\n if (condition.type === \"nfc\") return;\n void verify(condition.type === \"passcode\" ? value : value);\n };\n const scanQr = (): void => {\n if (videoRef.current === null) return;\n setStatus(\"loading\");\n setError(null);\n void readQrContext(videoRef.current).then((result) => {\n if (result.ok) void verify(result.value);\n else {\n setStatus(\"error\");\n setError(result.error.message);\n }\n });\n };\n const readLocation = (): void => {\n setStatus(\"loading\");\n setError(null);\n void getCurrentGeoContext().then((result) => {\n if (result.ok) void verify(result.value);\n else {\n setStatus(\"error\");\n setError(result.error.message);\n }\n });\n };\n const readNfc = (): void => {\n setStatus(\"loading\");\n setError(null);\n void readNfcContext().then((result) => {\n if (result.ok) void verify(result.value);\n else {\n setStatus(\"error\");\n setError(result.error.message);\n }\n });\n };\n return (\n <div>\n {condition.type === \"qr\" && (\n <>\n <label>\n {text(\"qrValue\", \"QR value\")}\n <input\n aria-label={text(\"qrValue\", \"QR value\")}\n placeholder={condition.qrEntryUrl ?? text(\"qrPlaceholder\", \"Paste a QR value\")}\n value={value}\n onChange={(event) => setValue(event.target.value)}\n />\n </label>\n <video\n ref={videoRef}\n aria-label={text(\"qrCamera\", \"QR camera\")}\n hidden={!isQrSupported()}\n >\n <track kind=\"captions\" />\n </video>\n <button\n type=\"button\"\n className=\"sry-action\"\n disabled={disabled || !isQrSupported()}\n onClick={scanQr}\n >\n {text(\"scanQr\", \"Scan QR\")}\n </button>\n </>\n )}\n {condition.type === \"gps\" && (\n <button type=\"button\" className=\"sry-action\" disabled={disabled} onClick={readLocation}>\n {text(\"checkLocation\", \"Check location\")}\n </button>\n )}\n {condition.type === \"nfc\" && (\n <button\n type=\"button\"\n className=\"sry-action\"\n disabled={disabled || !isNfcSupported()}\n onClick={readNfc}\n >\n {text(\"readNfc\", \"Read NFC\")}\n </button>\n )}\n {(condition.type === \"passcode\" ||\n condition.type === \"custom\" ||\n condition.type === \"qr\") && (\n <form onSubmit={submit}>\n <label>\n {text(\n condition.type === \"passcode\" ? \"passcode\" : \"proof\",\n condition.type === \"passcode\" ? \"Passcode\" : \"Proof\",\n )}\n <input\n aria-label={text(\"proof\", \"Proof\")}\n value={value}\n onChange={(event) => setValue(event.target.value)}\n />\n </label>\n <button type=\"submit\" className=\"sry-action\" disabled={disabled || value.trim() === \"\"}>\n {text(\"checkIn\", \"Check in\")}\n </button>\n </form>\n )}\n <div\n className=\"sry-feedback\"\n aria-live=\"polite\"\n role={status === \"error\" ? \"alert\" : undefined}\n >\n {status === \"loading\" && text(\"verifying\", \"Verifying…\")}\n {status === \"success\" && text(\"verified\", \"Verified\")}\n {status === \"error\" && (error ?? text(\"verificationFailed\", \"Verification failed.\"))}\n </div>\n </div>\n );\n}\n\nexport function RallyViewer<TLocale extends string = string>({\n config: providedConfig,\n client,\n adapter,\n locale,\n dictionary,\n classNames = {},\n style,\n customConditionRenderers = {},\n}: RallyViewerProps<TLocale>): ReactElement {\n const config = providedConfig ?? client?.getConfig() ?? adapter?.config;\n const [state, setState] = useState<UserRallyState | null>(() => client?.getState() ?? null);\n const [busy, setBusy] = useState<string | null>(null);\n useEffect(() => {\n if (client === undefined) return;\n const unsubscribe = client.subscribe(setState);\n void client.init().then(setState);\n return unsubscribe;\n }, [client]);\n if (config === undefined) throw new Error(\"RallyViewer requires config, client, or adapter.\");\n const progress = useMemo<StampRallyProgress>(\n () =>\n calculateProgress(\n state ?? { rallyId: config.id, userId: null, records: [], rewards: [], updatedAt: \"\" },\n config,\n ),\n [config, state],\n );\n const checkIn =\n adapter?.onCheckIn ??\n (client === undefined\n ? undefined\n : (spotId: string, proof: unknown, options?: CheckInOptions) =>\n client.checkIn(spotId, proof, options));\n const claim =\n adapter?.onClaimReward ??\n (client === undefined\n ? undefined\n : (rewardId: string, options?: ClaimOptions) => client.claimReward(rewardId, options));\n const submit = useCallback(\n (spotId: string, proof: unknown): Promise<CheckInResult | undefined> => {\n if (checkIn === undefined) return Promise.resolve(undefined);\n setBusy(spotId);\n return checkIn(spotId, proof).finally(() => setBusy(null));\n },\n [checkIn],\n );\n return (\n <section\n className={classNames.root}\n style={style}\n aria-label={label(dictionary, locale, \"viewer\", \"Stamp rally\")}\n >\n <h1>{resolveLocalizedText(config.title, locale) || config.id}</h1>\n <progress\n aria-label={label(dictionary, locale, \"progress\", \"Progress\")}\n max={100}\n value={progress.percentage}\n />{\" \"}\n <span>\n {progress.acquired}/{progress.total}\n </span>\n <div>\n {config.spots.map((spot) =>\n spot.conditions.map((condition) => {\n const Renderer = customConditionRenderers[condition.type] ?? DefaultCondition;\n return (\n <article\n className={classNames.condition}\n key={`${spot.id}-${condition.type}-${JSON.stringify(condition)}`}\n >\n <h2>{resolveLocalizedText(spot.name, locale)}</h2>\n <Renderer\n spot={spot}\n condition={condition}\n locale={locale}\n {...(dictionary === undefined ? {} : { dictionary })}\n disabled={busy !== null}\n onSubmit={(proof) => submit(spot.id, proof)}\n />\n </article>\n );\n }),\n )}\n </div>\n <section aria-label={label(dictionary, locale, \"rewards\", \"Rewards\")}>\n {config.rewards.map((reward) => (\n <RewardButton\n key={reward.id}\n reward={reward}\n state={state?.rewards.find((item) => item.rewardId === reward.id)}\n locale={locale}\n {...(dictionary === undefined ? {} : { dictionary })}\n {...(classNames.reward === undefined ? {} : { className: classNames.reward })}\n onClaim={claim}\n />\n ))}\n </section>\n </section>\n );\n}\n\nfunction RewardButton<TLocale extends string>({\n reward,\n state,\n locale,\n dictionary,\n className,\n onClaim,\n}: {\n readonly reward: Reward<TLocale>;\n readonly state: RewardState | undefined;\n readonly locale: TLocale;\n readonly dictionary?: LocaleDictionary<TLocale>;\n readonly className?: string;\n readonly onClaim:\n | ((rewardId: string, options?: ClaimOptions) => Promise<ClaimResult>)\n | undefined;\n}): ReactElement {\n const status = state?.status ?? \"LOCKED\";\n return (\n <button\n type=\"button\"\n className={className}\n disabled={status !== \"AVAILABLE\" || onClaim === undefined}\n onClick={() => {\n if (onClaim !== undefined) void onClaim(reward.id);\n }}\n >\n {resolveLocalizedText(reward.title, locale)} (\n {label(dictionary, locale, `status.${status.toLowerCase()}`, status)})\n </button>\n );\n}\n\nexport interface StampSheetProps<TLocale extends string = string> {\n readonly config: RallyConfig<TLocale>;\n readonly state?: StampRallyState | null;\n readonly title?: string;\n readonly progress?: StampRallyProgress;\n readonly locale?: TLocale;\n readonly dictionary?: LocaleDictionary<TLocale>;\n}\nexport function StampSheet<TLocale extends string = string>({\n config,\n state,\n title,\n progress,\n locale = \"en\" as TLocale,\n dictionary,\n}: StampSheetProps<TLocale>): ReactElement {\n const current =\n progress ??\n calculateProgress(\n state ?? { rallyId: config.id, userId: null, records: [], rewards: [], updatedAt: \"\" },\n config,\n );\n return (\n <section aria-label={title ?? label(dictionary, locale, \"stampSheet\", \"Stamp sheet\")}>\n <h2>{title ?? resolveLocalizedText(config.title, locale)}</h2>\n <progress max={100} value={current.percentage} />\n <div>\n {config.spots.map((spot) => (\n <span key={spot.id}>\n {state?.records.some((record) => record.stampId === spot.id) ? \"✓\" : \"○\"}\n </span>\n ))}\n </div>\n </section>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.tsx"],"names":["useState","useRef","getCurrentGeoContext","readNfcContext","readQrContext","jsxs","Fragment","jsx","isQrSupported","isNfcSupported","resolveLocalizedText","useEffect","useMemo","calculateProgress","useCallback"],"mappings":"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","file":"index.cjs","sourcesContent":["import type {\n CheckInOptions,\n CheckInResult,\n ClaimOptions,\n ClaimResult,\n LocaleDictionary,\n PublicCheckInCondition,\n PublicRallyConfig,\n PublicReward,\n PublicSpotItem,\n RallyConfig,\n RewardState,\n StampRallyClient,\n StampRallyProgress,\n StampRallyState,\n UserRallyState,\n} from \"@stamprally/core\";\nimport {\n calculateProgress,\n getCurrentGeoContext,\n isNfcSupported,\n isQrSupported,\n readNfcContext,\n readQrContext,\n resolveLocalizedText,\n} from \"@stamprally/core\";\nimport {\n type ComponentType,\n type CSSProperties,\n type ReactElement,\n type ReactNode,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nexport type ViewerClassName =\n | \"root\"\n | \"header\"\n | \"card\"\n | \"condition\"\n | \"slot\"\n | \"badge\"\n | \"modal\"\n | \"button\"\n | \"action\"\n | \"feedback\"\n | \"reward\"\n | \"footer\";\nexport type ViewerStyle = CSSProperties & {\n readonly [key: `--${string}`]: string | number | undefined;\n};\nexport type ViewerStyles = Partial<Record<ViewerClassName, ViewerStyle>>;\nexport type ViewerClassNames = Partial<Record<ViewerClassName, string>>;\nexport type SpotStatus = \"locked\" | \"available\" | \"claimed\" | \"verifying\" | \"error\";\n\nexport interface ConditionRendererProps<TLocale extends string = string> {\n readonly spot: PublicSpotItem<TLocale>;\n readonly condition: PublicCheckInCondition;\n readonly locale: TLocale;\n readonly dictionary?: LocaleDictionary<TLocale>;\n readonly disabled: boolean;\n readonly classNames?: ViewerClassNames;\n readonly styles?: ViewerStyles;\n // biome-ignore lint/suspicious/noConfusingVoidType: custom renderers may fire-and-forget while built-ins await feedback.\n readonly onSubmit: (proof: unknown) => void | Promise<unknown>;\n}\nexport type ConditionRenderer<TLocale extends string = string> = ComponentType<\n ConditionRendererProps<TLocale>\n>;\n\nexport interface SpotCardProps<TLocale extends string = string> {\n readonly spot: PublicSpotItem<TLocale>;\n readonly state: UserRallyState;\n readonly status: SpotStatus;\n readonly locale: TLocale;\n readonly dictionary?: LocaleDictionary<TLocale>;\n readonly children: ReactNode;\n}\nexport interface RewardCardProps<TLocale extends string = string> {\n readonly reward: PublicReward<TLocale>;\n readonly state: RewardState | undefined;\n readonly locale: TLocale;\n readonly dictionary?: LocaleDictionary<TLocale>;\n readonly onClaim:\n | ((rewardId: string, options?: ClaimOptions) => Promise<ClaimResult>)\n | undefined;\n}\nexport interface RallyViewerSlots<TLocale extends string = string> {\n readonly headerSlot?:\n | ReactNode\n | ((props: {\n readonly config: PublicRallyConfig<TLocale>;\n readonly state: UserRallyState;\n }) => ReactNode);\n readonly footerSlot?: ReactNode;\n readonly renderSpotCard?: (props: SpotCardProps<TLocale>) => ReactNode;\n readonly renderRewardCard?: (props: RewardCardProps<TLocale>) => ReactNode;\n readonly renderStatusBadge?: (props: { readonly status: SpotStatus }) => ReactNode;\n readonly renderVerifyingState?: () => ReactNode;\n readonly renderSuccessFeedback?: (result: CheckInResult) => ReactNode;\n readonly renderErrorFeedback?: (error: string) => ReactNode;\n}\n\nexport interface RallyViewerAdapter<TLocale extends string = string> {\n readonly config: PublicRallyConfig<TLocale>;\n readonly onCheckIn: (\n spotId: string,\n proof: unknown,\n options?: CheckInOptions,\n ) => Promise<CheckInResult>;\n readonly onClaimReward?: (rewardId: string, options?: ClaimOptions) => Promise<ClaimResult>;\n}\nexport interface RallyViewerProps<TLocale extends string = string>\n extends RallyViewerSlots<TLocale> {\n readonly config?: PublicRallyConfig<TLocale>;\n readonly client?: StampRallyClient;\n readonly adapter?: RallyViewerAdapter<TLocale>;\n readonly locale: TLocale;\n readonly dictionary?: LocaleDictionary<TLocale>;\n readonly classNames?: ViewerClassNames;\n readonly styles?: ViewerStyles;\n readonly style?: ViewerStyle;\n readonly customConditionRenderers?: Partial<\n Record<PublicCheckInCondition[\"type\"], ConditionRenderer<TLocale>>\n >;\n}\n\nconst label = <TLocale extends string>(\n dictionary: LocaleDictionary<TLocale> | undefined,\n locale: TLocale,\n key: string,\n fallback: string,\n): string => dictionary?.[locale]?.[key] ?? fallback;\nconst join = (...names: ReadonlyArray<string | undefined>): string | undefined => {\n const value = names.filter((name): name is string => name !== undefined && name !== \"\").join(\" \");\n return value === \"\" ? undefined : value;\n};\ntype VerificationStatus = \"idle\" | \"loading\" | \"success\" | \"error\";\n\nfunction DefaultCondition<TLocale extends string>({\n condition,\n dictionary,\n locale,\n disabled,\n classNames,\n styles,\n onSubmit,\n}: ConditionRendererProps<TLocale>): ReactElement {\n const [value, setValue] = useState(\"\");\n const [status, setStatus] = useState<VerificationStatus>(\"idle\");\n const [error, setError] = useState<string | null>(null);\n const videoRef = useRef<HTMLVideoElement>(null);\n const text = (key: string, fallback: string): string => label(dictionary, locale, key, fallback);\n const verify = async (proof: unknown): Promise<void> => {\n setStatus(\"loading\");\n setError(null);\n try {\n const result = await onSubmit(proof);\n if (\n result !== undefined &&\n typeof result === \"object\" &&\n result !== null &&\n \"ok\" in result &&\n result.ok === false\n ) {\n setStatus(\"error\");\n setError(text(\"verificationFailed\", \"Verification failed.\"));\n } else setStatus(\"success\");\n } catch {\n setStatus(\"error\");\n setError(text(\"verificationFailed\", \"Verification failed.\"));\n }\n };\n const readLocation = (): void => {\n setStatus(\"loading\");\n void getCurrentGeoContext().then((result) => {\n if (result.ok) void verify(result.value);\n else {\n setStatus(\"error\");\n setError(result.error.message);\n }\n });\n };\n const readNfc = (): void => {\n setStatus(\"loading\");\n void readNfcContext().then((result) => {\n if (result.ok) void verify(result.value);\n else {\n setStatus(\"error\");\n setError(result.error.message);\n }\n });\n };\n const scanQr = (): void => {\n if (videoRef.current === null) return;\n setStatus(\"loading\");\n void readQrContext(videoRef.current).then((result) => {\n if (result.ok) void verify(result.value);\n else {\n setStatus(\"error\");\n setError(result.error.message);\n }\n });\n };\n return (\n <div>\n {condition.type === \"qr\" && (\n <>\n <label>\n {text(\"qrValue\", \"QR value\")}\n <input\n aria-label={text(\"qrValue\", \"QR value\")}\n placeholder={condition.qrEntryUrl ?? text(\"qrPlaceholder\", \"Paste a QR value\")}\n value={value}\n onChange={(event) => setValue(event.target.value)}\n />\n </label>\n <video\n ref={videoRef}\n aria-label={text(\"qrCamera\", \"QR camera\")}\n hidden={!isQrSupported()}\n >\n <track kind=\"captions\" />\n </video>\n <button\n type=\"button\"\n className={join(\"sry-action\", classNames?.action, classNames?.button)}\n style={styles?.button ?? styles?.action}\n disabled={disabled || !isQrSupported()}\n onClick={scanQr}\n >\n {text(\"scanQr\", \"Scan QR\")}\n </button>\n </>\n )}\n {condition.type === \"gps\" && (\n <button\n type=\"button\"\n className={join(\"sry-action\", classNames?.action, classNames?.button)}\n style={styles?.button ?? styles?.action}\n disabled={disabled}\n onClick={readLocation}\n >\n {text(\"checkLocation\", \"Check location\")}\n </button>\n )}\n {condition.type === \"nfc\" && (\n <button\n type=\"button\"\n className={join(\"sry-action\", classNames?.action, classNames?.button)}\n style={styles?.button ?? styles?.action}\n disabled={disabled || !isNfcSupported()}\n onClick={readNfc}\n >\n {text(\"readNfc\", \"Read NFC\")}\n </button>\n )}\n {(condition.type === \"passcode\" ||\n condition.type === \"custom\" ||\n condition.type === \"qr\") && (\n <form\n onSubmit={(event) => {\n event.preventDefault();\n void verify(value);\n }}\n >\n <label>\n {text(\n condition.type === \"passcode\" ? \"passcode\" : \"proof\",\n condition.type === \"passcode\" ? \"Passcode\" : \"Proof\",\n )}\n <input\n aria-label={text(\"proof\", \"Proof\")}\n value={value}\n onChange={(event) => setValue(event.target.value)}\n />\n </label>\n <button\n type=\"submit\"\n className={join(\"sry-action\", classNames?.action, classNames?.button)}\n style={styles?.button ?? styles?.action}\n disabled={disabled || value.trim() === \"\"}\n >\n {text(\"checkIn\", \"Check in\")}\n </button>\n </form>\n )}\n <div\n className={join(\"sry-feedback\", classNames?.feedback)}\n style={styles?.feedback}\n aria-live=\"polite\"\n role={status === \"error\" ? \"alert\" : undefined}\n >\n {status === \"loading\" && text(\"verifying\", \"Verifying…\")}\n {status === \"success\" && text(\"verified\", \"Verified\")}\n {status === \"error\" && (error ?? text(\"verificationFailed\", \"Verification failed.\"))}\n </div>\n </div>\n );\n}\n\nfunction DefaultRewardCard<TLocale extends string>({\n reward,\n state,\n locale,\n dictionary,\n onClaim,\n classNames,\n styles,\n}: RewardCardProps<TLocale> & {\n readonly classNames?: ViewerClassNames;\n readonly styles?: ViewerStyles;\n}): ReactElement {\n const status = state?.status ?? \"LOCKED\";\n return (\n <button\n type=\"button\"\n className={join(classNames?.reward, classNames?.button)}\n style={styles?.reward ?? styles?.button}\n disabled={status !== \"AVAILABLE\" || onClaim === undefined}\n onClick={() => {\n if (onClaim !== undefined) void onClaim(reward.id);\n }}\n >\n {resolveLocalizedText(reward.title, locale)} (\n {label(dictionary, locale, `status.${status.toLowerCase()}`, status)})\n </button>\n );\n}\n\nexport function RallyViewer<TLocale extends string = string>({\n config: providedConfig,\n client,\n adapter,\n locale,\n dictionary,\n classNames = {},\n styles = {},\n style,\n customConditionRenderers = {},\n headerSlot,\n footerSlot,\n renderSpotCard,\n renderRewardCard,\n renderStatusBadge,\n renderVerifyingState,\n renderSuccessFeedback,\n renderErrorFeedback,\n}: RallyViewerProps<TLocale>): ReactElement {\n const config = providedConfig ?? client?.getConfig() ?? adapter?.config;\n const [state, setState] = useState<UserRallyState | null>(() => client?.getState() ?? null);\n const [busy, setBusy] = useState<string | null>(null);\n const [feedback, setFeedback] = useState<CheckInResult | null>(null);\n useEffect(() => {\n if (client === undefined) return;\n const unsubscribe = client.subscribe(setState);\n void client.init().then(setState);\n return unsubscribe;\n }, [client]);\n if (config === undefined) throw new Error(\"RallyViewer requires config, client, or adapter.\");\n const currentState = state ?? {\n rallyId: config.id,\n userId: null,\n records: [],\n rewards: [],\n updatedAt: \"\",\n };\n const progress = useMemo<StampRallyProgress>(\n () => calculateProgress(currentState, config),\n [config, currentState],\n );\n const checkIn =\n adapter?.onCheckIn ??\n (client === undefined\n ? undefined\n : (spotId: string, proof: unknown, options?: CheckInOptions) =>\n client.checkIn(spotId, proof, options));\n const claim =\n adapter?.onClaimReward ??\n (client === undefined\n ? undefined\n : (rewardId: string, options?: ClaimOptions) => client.claimReward(rewardId, options));\n const submit = useCallback(\n async (spotId: string, proof: unknown): Promise<CheckInResult | undefined> => {\n if (checkIn === undefined) return undefined;\n setBusy(spotId);\n const result = await checkIn(spotId, proof).finally(() => setBusy(null));\n setFeedback(result);\n return result;\n },\n [checkIn],\n );\n return (\n <section\n className={classNames.root}\n style={styles.root ?? style}\n aria-label={label(dictionary, locale, \"viewer\", \"Stamp rally\")}\n >\n <header className={classNames.header} style={styles.header}>\n {typeof headerSlot === \"function\"\n ? headerSlot({ config, state: currentState })\n : headerSlot}\n {headerSlot === undefined && (\n <h1>{resolveLocalizedText(config.title, locale) || config.id}</h1>\n )}\n <progress\n aria-label={label(dictionary, locale, \"progress\", \"Progress\")}\n max={100}\n value={progress.percentage}\n />\n <span>\n {progress.acquired}/{progress.total}\n </span>\n </header>\n {busy !== null && renderVerifyingState?.()}\n {feedback?.ok === true && renderSuccessFeedback?.(feedback)}\n {feedback?.ok === false &&\n renderErrorFeedback?.(\n \"message\" in feedback.error ? feedback.error.message : feedback.error.code,\n )}\n <div>\n {config.spots.map((spot) => {\n const claimed = currentState.records.some((record) => record.stampId === spot.id);\n const status: SpotStatus =\n busy === spot.id ? \"verifying\" : claimed ? \"claimed\" : \"available\";\n const children = spot.conditions.map((condition) => {\n const Renderer = customConditionRenderers[condition.type] ?? DefaultCondition;\n return (\n <div\n className={classNames.condition}\n style={styles.condition}\n key={`${spot.id}-${condition.type}-${JSON.stringify(condition)}`}\n >\n <Renderer\n spot={spot}\n condition={condition}\n locale={locale}\n {...(dictionary === undefined ? {} : { dictionary })}\n disabled={busy !== null || claimed}\n {...(Object.keys(classNames).length === 0 ? {} : { classNames })}\n {...(Object.keys(styles).length === 0 ? {} : { styles })}\n onSubmit={(proof) => submit(spot.id, proof)}\n />\n </div>\n );\n });\n const props: SpotCardProps<TLocale> = {\n spot,\n state: currentState,\n status,\n locale,\n ...(dictionary === undefined ? {} : { dictionary }),\n children,\n };\n return (\n <div key={spot.id} className={classNames.card} style={styles.card}>\n {renderSpotCard?.(props) ?? (\n <article>\n <h2>{resolveLocalizedText(spot.name, locale)}</h2>\n {renderStatusBadge?.({ status }) ?? (\n <span className={classNames.badge} style={styles.badge}>\n {status}\n </span>\n )}\n {children}\n </article>\n )}\n </div>\n );\n })}\n </div>\n <section aria-label={label(dictionary, locale, \"rewards\", \"Rewards\")}>\n {config.rewards.map((reward) => {\n const props: RewardCardProps<TLocale> = {\n reward,\n state: currentState.rewards.find((item) => item.rewardId === reward.id),\n locale,\n ...(dictionary === undefined ? {} : { dictionary }),\n onClaim: claim,\n };\n return (\n <div key={reward.id} className={classNames.reward} style={styles.reward}>\n {renderRewardCard?.(props) ?? (\n <DefaultRewardCard {...props} classNames={classNames} styles={styles} />\n )}\n </div>\n );\n })}\n </section>\n <footer className={classNames.footer} style={styles.footer}>\n {footerSlot}\n </footer>\n </section>\n );\n}\n\nexport interface StampSheetProps<TLocale extends string = string>\n extends RallyViewerSlots<TLocale> {\n readonly config: RallyConfig<TLocale>;\n readonly state?: StampRallyState | null;\n readonly title?: string;\n readonly progress?: StampRallyProgress;\n readonly locale?: TLocale;\n readonly dictionary?: LocaleDictionary<TLocale>;\n readonly classNames?: ViewerClassNames;\n readonly styles?: ViewerStyles;\n readonly style?: ViewerStyle;\n}\nexport function StampSheet<TLocale extends string = string>({\n config,\n state,\n title,\n progress,\n locale = \"en\" as TLocale,\n dictionary,\n classNames = {},\n styles = {},\n style,\n headerSlot,\n footerSlot,\n renderSpotCard,\n renderStatusBadge,\n}: StampSheetProps<TLocale>): ReactElement {\n const currentState = state ?? {\n rallyId: config.id,\n userId: null,\n records: [],\n rewards: [],\n updatedAt: \"\",\n };\n const current = progress ?? calculateProgress(currentState, config);\n return (\n <section\n className={classNames.root}\n style={styles.root ?? style}\n aria-label={title ?? label(dictionary, locale, \"stampSheet\", \"Stamp sheet\")}\n >\n <header className={classNames.header} style={styles.header}>\n {typeof headerSlot === \"function\"\n ? headerSlot({ config, state: currentState })\n : (headerSlot ?? <h2>{title ?? resolveLocalizedText(config.title, locale)}</h2>)}\n <progress max={100} value={current.percentage} />\n </header>\n <div>\n {config.spots.map((spot) => {\n const claimed = currentState.records.some((record) => record.stampId === spot.id);\n const status: SpotStatus = claimed ? \"claimed\" : \"available\";\n const props: SpotCardProps<TLocale> = {\n spot,\n state: currentState,\n status,\n locale,\n ...(dictionary === undefined ? {} : { dictionary }),\n children: (\n <span className={classNames.slot} style={styles.slot}>\n {claimed ? \"✓\" : \"○\"}\n </span>\n ),\n };\n return (\n <div key={spot.id} className={classNames.card} style={styles.card}>\n {renderSpotCard?.(props) ?? (\n <span className={classNames.slot} style={styles.slot}>\n {resolveLocalizedText(spot.name, locale)}{\" \"}\n {renderStatusBadge?.({ status }) ?? (claimed ? \"✓\" : \"○\")}\n </span>\n )}\n </div>\n );\n })}\n </div>\n <footer className={classNames.footer} style={styles.footer}>\n {footerSlot}\n </footer>\n </section>\n );\n}\n"]}
|