@wix/himalaya-cli 0.1211.0 → 0.1213.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/cli.mjs +20 -21
- package/dist/preview-assets.generated.bin +0 -0
- package/package.json +2 -2
- package/templates/beauty-salon-owner/dev/index.ts +218 -0
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-post/index.ts +63 -16
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-product/index.ts +61 -16
- package/templates/community-club-owner/dev/index.ts +327 -0
- package/templates/community-club-owner/tier5-src/himi-owner-lite-event/index.ts +59 -14
- package/templates/community-club-owner/tier5-src/himi-owner-lite-post/index.ts +63 -16
- package/templates/community-club-owner/tier5-src/himi-owner-lite-product/index.ts +61 -16
- package/templates/events-meetups-owner/dev/index.ts +218 -0
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-event/index.ts +59 -14
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-post/index.ts +63 -16
- package/templates/gym-fitness-owner/dev/index.ts +327 -0
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-event/index.ts +59 -14
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-post/index.ts +63 -16
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-product/index.ts +61 -16
- package/templates/nonprofit-cause-owner/dev/index.ts +218 -0
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-event/index.ts +59 -14
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-post/index.ts +63 -16
- package/templates/owner-lite/dev/index.ts +327 -0
- package/templates/owner-lite/tier5-src/himi-owner-lite-event/index.ts +59 -14
- package/templates/owner-lite/tier5-src/himi-owner-lite-post/index.ts +63 -16
- package/templates/owner-lite/tier5-src/himi-owner-lite-product/index.ts +61 -16
- package/templates/podcast-creator-owner/dev/index.ts +327 -0
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-event/index.ts +59 -14
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-post/index.ts +63 -16
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-product/index.ts +61 -16
- package/templates/portfolio-creative-owner/dev/index.ts +218 -0
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-post/index.ts +63 -16
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-product/index.ts +61 -16
- package/templates/restaurant-owner/dev/index.ts +327 -0
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-event/index.ts +59 -14
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-post/index.ts +63 -16
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-product/index.ts +61 -16
- package/templates/retail-store-owner/dev/index.ts +218 -0
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-post/index.ts +63 -16
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-product/index.ts +61 -16
- package/templates/yoga-studio-owner/dev/index.ts +218 -0
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-event/index.ts +59 -14
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-post/index.ts +63 -16
|
@@ -59,6 +59,9 @@ interface ProductState {
|
|
|
59
59
|
/** Non-empty when showing owner-preview-demo sample data (web preview, no owner bridge). */
|
|
60
60
|
demoNote: string;
|
|
61
61
|
notDemo: boolean;
|
|
62
|
+
/** #3158 — Delete ARMS a confirmation; only confirmDelete reaches the API. */
|
|
63
|
+
deletePending: boolean;
|
|
64
|
+
noDeleteConfirmation: boolean;
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
@@ -143,6 +146,9 @@ function shape(s: Partial<ProductState>): ProductState {
|
|
|
143
146
|
hideHide: !visible || status !== "ready",
|
|
144
147
|
demoNote: s.demoNote ?? "",
|
|
145
148
|
notDemo: !s.demoNote,
|
|
149
|
+
// Only a loaded entity can be pending deletion; every other action disarms it (`disarm`).
|
|
150
|
+
deletePending: !!s.deletePending && hasEntity,
|
|
151
|
+
noDeleteConfirmation: !(s.deletePending && hasEntity),
|
|
146
152
|
};
|
|
147
153
|
}
|
|
148
154
|
|
|
@@ -265,16 +271,33 @@ async function setVisibility(s: ProductState, visible: boolean, msg: string): Pr
|
|
|
265
271
|
}
|
|
266
272
|
}
|
|
267
273
|
|
|
274
|
+
/** #3158 — a confirmed delete in flight. Synchronous and module-level on purpose: two
|
|
275
|
+
* confirmDelete dispatches (a double tap) both start from a state that says "armed", and only a
|
|
276
|
+
* flag set BEFORE the first await stops the second from issuing its own DELETE — whose late 404
|
|
277
|
+
* would otherwise land after, and overwrite, the first one's "deleted". */
|
|
278
|
+
let deleteInFlight = false;
|
|
279
|
+
|
|
280
|
+
/** #3158 — the confirmation describes THIS visit. Never take it from a persisted snapshot, or a
|
|
281
|
+
* reopened record comes back with "Delete this …?" already armed. */
|
|
282
|
+
const DELETE_RUN_KEYS = ["deletePending", "noDeleteConfirmation"] as const;
|
|
283
|
+
|
|
284
|
+
/** #3158 — clear an armed delete confirmation. */
|
|
285
|
+
const disarm = <T extends { deletePending: boolean }>(s: T): T => ({ ...s, deletePending: false });
|
|
286
|
+
|
|
268
287
|
const svc = defineService<ProductState, {
|
|
269
288
|
load: (s: ProductState, arg?: unknown) => Promise<ProductState>;
|
|
270
289
|
retry: (s: ProductState) => Promise<ProductState>;
|
|
271
290
|
show: (s: ProductState) => Promise<ProductState>;
|
|
272
291
|
hide: (s: ProductState) => Promise<ProductState>;
|
|
273
292
|
delete: (s: ProductState) => Promise<ProductState>;
|
|
293
|
+
cancelDelete: (s: ProductState) => Promise<ProductState>;
|
|
294
|
+
confirmDelete: (s: ProductState) => Promise<ProductState | undefined>;
|
|
274
295
|
}>("himiOwnerLiteProduct", {
|
|
275
296
|
state: shape({ status: "loading" }),
|
|
297
|
+
notHydrated: [...DELETE_RUN_KEYS],
|
|
276
298
|
actions: {
|
|
277
|
-
load: async (
|
|
299
|
+
load: async (s0, arg) => {
|
|
300
|
+
const s = disarm(s0);
|
|
278
301
|
stashCopy(arg); // absorb the descriptor's onAppear copy-pack param
|
|
279
302
|
const id = idFrom(arg, s.id) || inputId();
|
|
280
303
|
if (!id) return shape({ ...s, status: "error", error: copyParam("ownerProduct.errorNoId", "no product id") });
|
|
@@ -297,7 +320,8 @@ const svc = defineService<ProductState, {
|
|
|
297
320
|
return shape({ ...s, id, status: "error", error: ownerLoadError(e) });
|
|
298
321
|
}
|
|
299
322
|
},
|
|
300
|
-
retry: async (
|
|
323
|
+
retry: async (s0) => {
|
|
324
|
+
const s = disarm(s0);
|
|
301
325
|
if (!s.id) return shape({ ...s, status: "error", error: copyParam("ownerProduct.errorNoId", "no product id") });
|
|
302
326
|
if (isOwnerPreviewDemo()) {
|
|
303
327
|
const demo = demoDetail(s, s.id);
|
|
@@ -317,25 +341,46 @@ const svc = defineService<ProductState, {
|
|
|
317
341
|
return shape({ ...s, status: "error", error: ownerLoadError(e) });
|
|
318
342
|
}
|
|
319
343
|
},
|
|
320
|
-
show: async (
|
|
321
|
-
hide: async (
|
|
322
|
-
delete
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
344
|
+
show: async (s0) => { const s = disarm(s0); return setVisibility(s, true, copyParam("ownerProduct.shownMessage", "Product is now visible in your store.")); },
|
|
345
|
+
hide: async (s0) => { const s = disarm(s0); return setVisibility(s, false, copyParam("ownerProduct.hiddenMessage", "Product hidden from your store.")); },
|
|
346
|
+
// #3158 — destructive, so two steps. `delete` (the Manage card's Delete button)
|
|
347
|
+
// only ARMS the inline confirmation, which names the record and says it cannot
|
|
348
|
+
// be undone; `cancelDelete` disarms it; only `confirmDelete` deletes. Anything
|
|
349
|
+
// that dispatches `delete` directly therefore still asks first.
|
|
350
|
+
delete: async (s) => shape({ ...s, deletePending: true, actionMessage: "" }),
|
|
351
|
+
cancelDelete: async (s) => shape({ ...s, deletePending: false }),
|
|
352
|
+
confirmDelete: async (s0) => {
|
|
353
|
+
// Nothing armed (a stray dispatch, a rehydrated snapshot) or a delete already running
|
|
354
|
+
// (a second tap): ignore it — `undefined` leaves the state exactly as it is.
|
|
355
|
+
if (!s0.deletePending || deleteInFlight) return undefined;
|
|
356
|
+
deleteInFlight = true;
|
|
327
357
|
try {
|
|
328
|
-
|
|
329
|
-
return shape({ ...s,
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
358
|
+
const s = disarm(s0);
|
|
359
|
+
if (isOwnerPreviewDemo() || s.demoNote) return shape({ ...s, actionMessage: copyParam("ownerCommon.demoSaveNote", DEMO_SAVE_NOTE) });
|
|
360
|
+
if (!s.id) return shape({ ...s, actionMessage: copyParam("ownerProduct.noIdMessage", "No product id.") });
|
|
361
|
+
// Optimistic local delete — the list drops the row immediately.
|
|
362
|
+
await cacheDrop(s.id);
|
|
363
|
+
try {
|
|
364
|
+
await deleteProduct(s.id);
|
|
365
|
+
return shape({ ...s, status: "deleted", title: "", emptyText: copyParam("ownerProduct.deletedEmpty", "Product deleted."), actionMessage: "" });
|
|
366
|
+
} catch (e) {
|
|
367
|
+
if (shouldQueueOwnerWrite(e)) {
|
|
368
|
+
await queueWrite("tier5.himiOwnerLiteProduct.delete", { id: s.id });
|
|
369
|
+
return shape({ ...s, status: "deleted", title: "", emptyText: copyParam("ownerProduct.deletedEmpty", "Product deleted."), actionMessage: copyParam("ownerCommon.demoSaveNote", DEMO_SAVE_NOTE) });
|
|
370
|
+
}
|
|
371
|
+
return shape({ ...s, actionMessage: ownerLoadError(e) });
|
|
334
372
|
}
|
|
335
|
-
|
|
373
|
+
} finally {
|
|
374
|
+
deleteInFlight = false;
|
|
336
375
|
}
|
|
337
376
|
},
|
|
338
377
|
},
|
|
339
378
|
});
|
|
340
379
|
|
|
380
|
+
// The same reset on a runtime from before `notHydrated` (an older binary on a new bundle).
|
|
381
|
+
if ((globalThis as { himalaya?: { hydrationOptOut?: unknown } }).himalaya?.hydrationOptOut !== true) {
|
|
382
|
+
const booted = svc.getState?.() as unknown as Record<string, unknown> | undefined;
|
|
383
|
+
if (booted && typeof booted === "object") Object.assign(booted, { deletePending: false, noDeleteConfirmation: true });
|
|
384
|
+
}
|
|
385
|
+
|
|
341
386
|
export { svc };
|
|
@@ -80445,6 +80445,115 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
|
|
|
80445
80445
|
}
|
|
80446
80446
|
}
|
|
80447
80447
|
},
|
|
80448
|
+
{
|
|
80449
|
+
"id": "product_delete_confirmation",
|
|
80450
|
+
"body": {
|
|
80451
|
+
"package": "himalaya",
|
|
80452
|
+
"type": "VStack",
|
|
80453
|
+
"props": {
|
|
80454
|
+
"spacing": 10,
|
|
80455
|
+
"alignment": "leading",
|
|
80456
|
+
"children": [
|
|
80457
|
+
{
|
|
80458
|
+
"id": "product_delete_confirmation.title",
|
|
80459
|
+
"body": {
|
|
80460
|
+
"package": "himalaya",
|
|
80461
|
+
"type": "Text",
|
|
80462
|
+
"props": {
|
|
80463
|
+
"text": "Delete this dish?",
|
|
80464
|
+
"style": "bodyEmphasis"
|
|
80465
|
+
}
|
|
80466
|
+
}
|
|
80467
|
+
},
|
|
80468
|
+
{
|
|
80469
|
+
"id": "product_delete_confirmation.record",
|
|
80470
|
+
"body": {
|
|
80471
|
+
"package": "himalaya",
|
|
80472
|
+
"type": "Text",
|
|
80473
|
+
"props": {
|
|
80474
|
+
"text": "$service:himiOwnerLiteProduct.title",
|
|
80475
|
+
"style": "body",
|
|
80476
|
+
"color": "textSecondary",
|
|
80477
|
+
"maxLines": 2
|
|
80478
|
+
}
|
|
80479
|
+
}
|
|
80480
|
+
},
|
|
80481
|
+
{
|
|
80482
|
+
"id": "",
|
|
80483
|
+
"body": {
|
|
80484
|
+
"package": "himalaya",
|
|
80485
|
+
"type": "Text",
|
|
80486
|
+
"props": {
|
|
80487
|
+
"text": "This cannot be undone.",
|
|
80488
|
+
"style": "footnote",
|
|
80489
|
+
"color": "textSecondary"
|
|
80490
|
+
}
|
|
80491
|
+
}
|
|
80492
|
+
},
|
|
80493
|
+
{
|
|
80494
|
+
"id": "",
|
|
80495
|
+
"body": {
|
|
80496
|
+
"package": "himalaya",
|
|
80497
|
+
"type": "HStack",
|
|
80498
|
+
"props": {
|
|
80499
|
+
"spacing": 8,
|
|
80500
|
+
"children": [
|
|
80501
|
+
{
|
|
80502
|
+
"id": "product_delete_cancel",
|
|
80503
|
+
"body": {
|
|
80504
|
+
"package": "himalaya",
|
|
80505
|
+
"type": "Button",
|
|
80506
|
+
"props": {
|
|
80507
|
+
"title": "Cancel",
|
|
80508
|
+
"style": "secondary",
|
|
80509
|
+
"action": {
|
|
80510
|
+
"actionId": "tier5.himiOwnerLiteProduct.cancelDelete"
|
|
80511
|
+
}
|
|
80512
|
+
}
|
|
80513
|
+
}
|
|
80514
|
+
},
|
|
80515
|
+
{
|
|
80516
|
+
"id": "product_delete_confirm",
|
|
80517
|
+
"body": {
|
|
80518
|
+
"package": "himalaya",
|
|
80519
|
+
"type": "Button",
|
|
80520
|
+
"props": {
|
|
80521
|
+
"title": "Delete",
|
|
80522
|
+
"style": "destructive",
|
|
80523
|
+
"action": {
|
|
80524
|
+
"actionId": "tier5.himiOwnerLiteProduct.confirmDelete"
|
|
80525
|
+
}
|
|
80526
|
+
}
|
|
80527
|
+
}
|
|
80528
|
+
}
|
|
80529
|
+
]
|
|
80530
|
+
}
|
|
80531
|
+
}
|
|
80532
|
+
}
|
|
80533
|
+
]
|
|
80534
|
+
}
|
|
80535
|
+
},
|
|
80536
|
+
"modifiers": {
|
|
80537
|
+
"background": {
|
|
80538
|
+
"token": "surfaceElevated"
|
|
80539
|
+
},
|
|
80540
|
+
"border": {
|
|
80541
|
+
"width": 1,
|
|
80542
|
+
"color": {
|
|
80543
|
+
"token": "danger"
|
|
80544
|
+
}
|
|
80545
|
+
},
|
|
80546
|
+
"cornerRadius": 12,
|
|
80547
|
+
"padding": {
|
|
80548
|
+
"leading": 14,
|
|
80549
|
+
"trailing": 14,
|
|
80550
|
+
"top": 14,
|
|
80551
|
+
"bottom": 14
|
|
80552
|
+
},
|
|
80553
|
+
"fillMaxWidth": true,
|
|
80554
|
+
"hiddenPath": "$service:himiOwnerLiteProduct.noDeleteConfirmation"
|
|
80555
|
+
}
|
|
80556
|
+
},
|
|
80448
80557
|
{
|
|
80449
80558
|
"id": "",
|
|
80450
80559
|
"body": {
|
|
@@ -82036,6 +82145,115 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
|
|
|
82036
82145
|
}
|
|
82037
82146
|
}
|
|
82038
82147
|
},
|
|
82148
|
+
{
|
|
82149
|
+
"id": "event_delete_confirmation",
|
|
82150
|
+
"body": {
|
|
82151
|
+
"package": "himalaya",
|
|
82152
|
+
"type": "VStack",
|
|
82153
|
+
"props": {
|
|
82154
|
+
"spacing": 10,
|
|
82155
|
+
"alignment": "leading",
|
|
82156
|
+
"children": [
|
|
82157
|
+
{
|
|
82158
|
+
"id": "event_delete_confirmation.title",
|
|
82159
|
+
"body": {
|
|
82160
|
+
"package": "himalaya",
|
|
82161
|
+
"type": "Text",
|
|
82162
|
+
"props": {
|
|
82163
|
+
"text": "Delete this wine night?",
|
|
82164
|
+
"style": "bodyEmphasis"
|
|
82165
|
+
}
|
|
82166
|
+
}
|
|
82167
|
+
},
|
|
82168
|
+
{
|
|
82169
|
+
"id": "event_delete_confirmation.record",
|
|
82170
|
+
"body": {
|
|
82171
|
+
"package": "himalaya",
|
|
82172
|
+
"type": "Text",
|
|
82173
|
+
"props": {
|
|
82174
|
+
"text": "$service:himiOwnerLiteEvent.title",
|
|
82175
|
+
"style": "body",
|
|
82176
|
+
"color": "textSecondary",
|
|
82177
|
+
"maxLines": 2
|
|
82178
|
+
}
|
|
82179
|
+
}
|
|
82180
|
+
},
|
|
82181
|
+
{
|
|
82182
|
+
"id": "",
|
|
82183
|
+
"body": {
|
|
82184
|
+
"package": "himalaya",
|
|
82185
|
+
"type": "Text",
|
|
82186
|
+
"props": {
|
|
82187
|
+
"text": "This cannot be undone.",
|
|
82188
|
+
"style": "footnote",
|
|
82189
|
+
"color": "textSecondary"
|
|
82190
|
+
}
|
|
82191
|
+
}
|
|
82192
|
+
},
|
|
82193
|
+
{
|
|
82194
|
+
"id": "",
|
|
82195
|
+
"body": {
|
|
82196
|
+
"package": "himalaya",
|
|
82197
|
+
"type": "HStack",
|
|
82198
|
+
"props": {
|
|
82199
|
+
"spacing": 8,
|
|
82200
|
+
"children": [
|
|
82201
|
+
{
|
|
82202
|
+
"id": "event_delete_cancel",
|
|
82203
|
+
"body": {
|
|
82204
|
+
"package": "himalaya",
|
|
82205
|
+
"type": "Button",
|
|
82206
|
+
"props": {
|
|
82207
|
+
"title": "Cancel",
|
|
82208
|
+
"style": "secondary",
|
|
82209
|
+
"action": {
|
|
82210
|
+
"actionId": "tier5.himiOwnerLiteEvent.cancelDelete"
|
|
82211
|
+
}
|
|
82212
|
+
}
|
|
82213
|
+
}
|
|
82214
|
+
},
|
|
82215
|
+
{
|
|
82216
|
+
"id": "event_delete_confirm",
|
|
82217
|
+
"body": {
|
|
82218
|
+
"package": "himalaya",
|
|
82219
|
+
"type": "Button",
|
|
82220
|
+
"props": {
|
|
82221
|
+
"title": "Delete",
|
|
82222
|
+
"style": "destructive",
|
|
82223
|
+
"action": {
|
|
82224
|
+
"actionId": "tier5.himiOwnerLiteEvent.confirmDelete"
|
|
82225
|
+
}
|
|
82226
|
+
}
|
|
82227
|
+
}
|
|
82228
|
+
}
|
|
82229
|
+
]
|
|
82230
|
+
}
|
|
82231
|
+
}
|
|
82232
|
+
}
|
|
82233
|
+
]
|
|
82234
|
+
}
|
|
82235
|
+
},
|
|
82236
|
+
"modifiers": {
|
|
82237
|
+
"background": {
|
|
82238
|
+
"token": "surfaceElevated"
|
|
82239
|
+
},
|
|
82240
|
+
"border": {
|
|
82241
|
+
"width": 1,
|
|
82242
|
+
"color": {
|
|
82243
|
+
"token": "danger"
|
|
82244
|
+
}
|
|
82245
|
+
},
|
|
82246
|
+
"cornerRadius": 12,
|
|
82247
|
+
"padding": {
|
|
82248
|
+
"leading": 14,
|
|
82249
|
+
"trailing": 14,
|
|
82250
|
+
"top": 14,
|
|
82251
|
+
"bottom": 14
|
|
82252
|
+
},
|
|
82253
|
+
"fillMaxWidth": true,
|
|
82254
|
+
"hiddenPath": "$service:himiOwnerLiteEvent.noDeleteConfirmation"
|
|
82255
|
+
}
|
|
82256
|
+
},
|
|
82039
82257
|
{
|
|
82040
82258
|
"id": "",
|
|
82041
82259
|
"body": {
|
|
@@ -82897,6 +83115,115 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
|
|
|
82897
83115
|
}
|
|
82898
83116
|
}
|
|
82899
83117
|
},
|
|
83118
|
+
{
|
|
83119
|
+
"id": "post_delete_confirmation",
|
|
83120
|
+
"body": {
|
|
83121
|
+
"package": "himalaya",
|
|
83122
|
+
"type": "VStack",
|
|
83123
|
+
"props": {
|
|
83124
|
+
"spacing": 10,
|
|
83125
|
+
"alignment": "leading",
|
|
83126
|
+
"children": [
|
|
83127
|
+
{
|
|
83128
|
+
"id": "post_delete_confirmation.title",
|
|
83129
|
+
"body": {
|
|
83130
|
+
"package": "himalaya",
|
|
83131
|
+
"type": "Text",
|
|
83132
|
+
"props": {
|
|
83133
|
+
"text": "Delete this post?",
|
|
83134
|
+
"style": "bodyEmphasis"
|
|
83135
|
+
}
|
|
83136
|
+
}
|
|
83137
|
+
},
|
|
83138
|
+
{
|
|
83139
|
+
"id": "post_delete_confirmation.record",
|
|
83140
|
+
"body": {
|
|
83141
|
+
"package": "himalaya",
|
|
83142
|
+
"type": "Text",
|
|
83143
|
+
"props": {
|
|
83144
|
+
"text": "$service:himiOwnerLitePost.title",
|
|
83145
|
+
"style": "body",
|
|
83146
|
+
"color": "textSecondary",
|
|
83147
|
+
"maxLines": 2
|
|
83148
|
+
}
|
|
83149
|
+
}
|
|
83150
|
+
},
|
|
83151
|
+
{
|
|
83152
|
+
"id": "",
|
|
83153
|
+
"body": {
|
|
83154
|
+
"package": "himalaya",
|
|
83155
|
+
"type": "Text",
|
|
83156
|
+
"props": {
|
|
83157
|
+
"text": "This cannot be undone.",
|
|
83158
|
+
"style": "footnote",
|
|
83159
|
+
"color": "textSecondary"
|
|
83160
|
+
}
|
|
83161
|
+
}
|
|
83162
|
+
},
|
|
83163
|
+
{
|
|
83164
|
+
"id": "",
|
|
83165
|
+
"body": {
|
|
83166
|
+
"package": "himalaya",
|
|
83167
|
+
"type": "HStack",
|
|
83168
|
+
"props": {
|
|
83169
|
+
"spacing": 8,
|
|
83170
|
+
"children": [
|
|
83171
|
+
{
|
|
83172
|
+
"id": "post_delete_cancel",
|
|
83173
|
+
"body": {
|
|
83174
|
+
"package": "himalaya",
|
|
83175
|
+
"type": "Button",
|
|
83176
|
+
"props": {
|
|
83177
|
+
"title": "Cancel",
|
|
83178
|
+
"style": "secondary",
|
|
83179
|
+
"action": {
|
|
83180
|
+
"actionId": "tier5.himiOwnerLitePost.cancelDelete"
|
|
83181
|
+
}
|
|
83182
|
+
}
|
|
83183
|
+
}
|
|
83184
|
+
},
|
|
83185
|
+
{
|
|
83186
|
+
"id": "post_delete_confirm",
|
|
83187
|
+
"body": {
|
|
83188
|
+
"package": "himalaya",
|
|
83189
|
+
"type": "Button",
|
|
83190
|
+
"props": {
|
|
83191
|
+
"title": "Delete",
|
|
83192
|
+
"style": "destructive",
|
|
83193
|
+
"action": {
|
|
83194
|
+
"actionId": "tier5.himiOwnerLitePost.confirmDelete"
|
|
83195
|
+
}
|
|
83196
|
+
}
|
|
83197
|
+
}
|
|
83198
|
+
}
|
|
83199
|
+
]
|
|
83200
|
+
}
|
|
83201
|
+
}
|
|
83202
|
+
}
|
|
83203
|
+
]
|
|
83204
|
+
}
|
|
83205
|
+
},
|
|
83206
|
+
"modifiers": {
|
|
83207
|
+
"background": {
|
|
83208
|
+
"token": "surfaceElevated"
|
|
83209
|
+
},
|
|
83210
|
+
"border": {
|
|
83211
|
+
"width": 1,
|
|
83212
|
+
"color": {
|
|
83213
|
+
"token": "danger"
|
|
83214
|
+
}
|
|
83215
|
+
},
|
|
83216
|
+
"cornerRadius": 12,
|
|
83217
|
+
"padding": {
|
|
83218
|
+
"leading": 14,
|
|
83219
|
+
"trailing": 14,
|
|
83220
|
+
"top": 14,
|
|
83221
|
+
"bottom": 14
|
|
83222
|
+
},
|
|
83223
|
+
"fillMaxWidth": true,
|
|
83224
|
+
"hiddenPath": "$service:himiOwnerLitePost.noDeleteConfirmation"
|
|
83225
|
+
}
|
|
83226
|
+
},
|
|
82900
83227
|
{
|
|
82901
83228
|
"id": "",
|
|
82902
83229
|
"body": {
|
|
@@ -62,6 +62,9 @@ interface EventState {
|
|
|
62
62
|
/** Non-empty when showing owner-preview-demo sample data (web preview, no owner bridge). */
|
|
63
63
|
demoNote: string;
|
|
64
64
|
notDemo: boolean;
|
|
65
|
+
/** #3158 — Delete ARMS a confirmation; only confirmDelete reaches the API. */
|
|
66
|
+
deletePending: boolean;
|
|
67
|
+
noDeleteConfirmation: boolean;
|
|
65
68
|
}
|
|
66
69
|
|
|
67
70
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
@@ -139,6 +142,9 @@ function shape(s: Partial<EventState>): EventState {
|
|
|
139
142
|
noCover: !image,
|
|
140
143
|
demoNote: s.demoNote ?? "",
|
|
141
144
|
notDemo: !s.demoNote,
|
|
145
|
+
// Only a loaded entity can be pending deletion; every other action disarms it (`disarm`).
|
|
146
|
+
deletePending: !!s.deletePending && hasEntity,
|
|
147
|
+
noDeleteConfirmation: !(s.deletePending && hasEntity),
|
|
142
148
|
};
|
|
143
149
|
}
|
|
144
150
|
|
|
@@ -224,15 +230,32 @@ async function refresh(prev: EventState, id: string): Promise<EventState> {
|
|
|
224
230
|
});
|
|
225
231
|
}
|
|
226
232
|
|
|
233
|
+
/** #3158 — a confirmed delete in flight. Synchronous and module-level on purpose: two
|
|
234
|
+
* confirmDelete dispatches (a double tap) both start from a state that says "armed", and only a
|
|
235
|
+
* flag set BEFORE the first await stops the second from issuing its own DELETE — whose late 404
|
|
236
|
+
* would otherwise land after, and overwrite, the first one's "deleted". */
|
|
237
|
+
let deleteInFlight = false;
|
|
238
|
+
|
|
239
|
+
/** #3158 — the confirmation describes THIS visit. Never take it from a persisted snapshot, or a
|
|
240
|
+
* reopened record comes back with "Delete this …?" already armed. */
|
|
241
|
+
const DELETE_RUN_KEYS = ["deletePending", "noDeleteConfirmation"] as const;
|
|
242
|
+
|
|
243
|
+
/** #3158 — clear an armed delete confirmation. */
|
|
244
|
+
const disarm = <T extends { deletePending: boolean }>(s: T): T => ({ ...s, deletePending: false });
|
|
245
|
+
|
|
227
246
|
const svc = defineService<EventState, {
|
|
228
247
|
load: (s: EventState, arg?: unknown) => Promise<EventState>;
|
|
229
248
|
retry: (s: EventState) => Promise<EventState>;
|
|
230
249
|
delete: (s: EventState) => Promise<EventState>;
|
|
250
|
+
cancelDelete: (s: EventState) => Promise<EventState>;
|
|
251
|
+
confirmDelete: (s: EventState) => Promise<EventState | undefined>;
|
|
231
252
|
openLink: (s: EventState, arg?: unknown) => Promise<EventState>;
|
|
232
253
|
}>("himiOwnerLiteEvent", {
|
|
233
254
|
state: shape({ status: "loading" }),
|
|
255
|
+
notHydrated: [...DELETE_RUN_KEYS],
|
|
234
256
|
actions: {
|
|
235
|
-
load: async (
|
|
257
|
+
load: async (s0, arg) => {
|
|
258
|
+
const s = disarm(s0);
|
|
236
259
|
stashCopy(arg); // absorb the descriptor's onAppear copy-pack param
|
|
237
260
|
const id = idFrom(arg, s.id) || inputId();
|
|
238
261
|
if (!id) return shape({ ...s, status: "error", error: copyParam("ownerEvent.errorNoId", "no event id") });
|
|
@@ -257,7 +280,8 @@ const svc = defineService<EventState, {
|
|
|
257
280
|
return shape({ ...s, id, status: "error", error: ownerLoadError(e) });
|
|
258
281
|
}
|
|
259
282
|
},
|
|
260
|
-
retry: async (
|
|
283
|
+
retry: async (s0) => {
|
|
284
|
+
const s = disarm(s0);
|
|
261
285
|
if (!s.id) return shape({ ...s, status: "error", error: copyParam("ownerEvent.errorNoId", "no event id") });
|
|
262
286
|
if (isOwnerPreviewDemo()) {
|
|
263
287
|
const demo = demoDetail(s, s.id);
|
|
@@ -279,20 +303,35 @@ const svc = defineService<EventState, {
|
|
|
279
303
|
return shape({ ...s, status: "error", error: ownerLoadError(e) });
|
|
280
304
|
}
|
|
281
305
|
},
|
|
282
|
-
delete
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
306
|
+
// #3158 — destructive, so two steps. `delete` (the Manage card's Delete button)
|
|
307
|
+
// only ARMS the inline confirmation, which names the record and says it cannot
|
|
308
|
+
// be undone; `cancelDelete` disarms it; only `confirmDelete` deletes. Anything
|
|
309
|
+
// that dispatches `delete` directly therefore still asks first.
|
|
310
|
+
delete: async (s) => shape({ ...s, deletePending: true, actionMessage: "" }),
|
|
311
|
+
cancelDelete: async (s) => shape({ ...s, deletePending: false }),
|
|
312
|
+
confirmDelete: async (s0) => {
|
|
313
|
+
// Nothing armed (a stray dispatch, a rehydrated snapshot) or a delete already running
|
|
314
|
+
// (a second tap): ignore it — `undefined` leaves the state exactly as it is.
|
|
315
|
+
if (!s0.deletePending || deleteInFlight) return undefined;
|
|
316
|
+
deleteInFlight = true;
|
|
287
317
|
try {
|
|
288
|
-
|
|
289
|
-
return shape({ ...s,
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
318
|
+
const s = disarm(s0);
|
|
319
|
+
if (isOwnerPreviewDemo() || s.demoNote) return shape({ ...s, actionMessage: copyParam("ownerCommon.demoSaveNote", DEMO_SAVE_NOTE) });
|
|
320
|
+
if (!s.id) return shape({ ...s, actionMessage: copyParam("ownerEvent.noIdMessage", "No event id.") });
|
|
321
|
+
// Optimistic local delete — the list drops the row immediately.
|
|
322
|
+
await cacheDrop(s.id);
|
|
323
|
+
try {
|
|
324
|
+
await deleteEvent(s.id);
|
|
325
|
+
return shape({ ...s, status: "deleted", title: "", emptyText: copyParam("ownerEvent.deletedEmpty", "Event deleted."), actionMessage: "" });
|
|
326
|
+
} catch (e) {
|
|
327
|
+
if (shouldQueueOwnerWrite(e)) {
|
|
328
|
+
await queueWrite("tier5.himiOwnerLiteEvent.delete", { id: s.id });
|
|
329
|
+
return shape({ ...s, status: "deleted", title: "", emptyText: copyParam("ownerEvent.deletedEmpty", "Event deleted."), actionMessage: copyParam("ownerCommon.demoSaveNote", DEMO_SAVE_NOTE) });
|
|
330
|
+
}
|
|
331
|
+
return shape({ ...s, actionMessage: ownerLoadError(e) });
|
|
294
332
|
}
|
|
295
|
-
|
|
333
|
+
} finally {
|
|
334
|
+
deleteInFlight = false;
|
|
296
335
|
}
|
|
297
336
|
},
|
|
298
337
|
// [rico] #3499 — a link tapped in the RichContent description.
|
|
@@ -303,4 +342,10 @@ const svc = defineService<EventState, {
|
|
|
303
342
|
},
|
|
304
343
|
});
|
|
305
344
|
|
|
345
|
+
// The same reset on a runtime from before `notHydrated` (an older binary on a new bundle).
|
|
346
|
+
if ((globalThis as { himalaya?: { hydrationOptOut?: unknown } }).himalaya?.hydrationOptOut !== true) {
|
|
347
|
+
const booted = svc.getState?.() as unknown as Record<string, unknown> | undefined;
|
|
348
|
+
if (booted && typeof booted === "object") Object.assign(booted, { deletePending: false, noDeleteConfirmation: true });
|
|
349
|
+
}
|
|
350
|
+
|
|
306
351
|
export { svc };
|