@wix/himalaya-cli 0.1222.0 → 0.1224.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.
Files changed (58) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/events/src/display.d.ts +3 -0
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-events.mjs +3 -1
  3. package/dist/cli.mjs +33 -24
  4. package/dist/preview-assets.generated.bin +0 -0
  5. package/guides/sdk-reference.json +1 -1
  6. package/package.json +2 -2
  7. package/templates/beauty-salon/dev/index.ts +5 -1
  8. package/templates/beauty-salon/tier5-src/_shared/collection-contract.ts +15 -2
  9. package/templates/beauty-salon/tier5-src/wix-spaces-create-post/index.ts +14 -4
  10. package/templates/beauty-salon-owner/tier5-src/_shared/collection-contract.ts +15 -2
  11. package/templates/branded-lite/dev/index.ts +520 -274
  12. package/templates/branded-lite/tier5-src/_shared/collection-contract.ts +15 -2
  13. package/templates/branded-lite/tier5-src/wix-spaces-create-post/index.ts +14 -4
  14. package/templates/community-club/dev/index.ts +518 -272
  15. package/templates/community-club/tier5-src/_shared/collection-contract.ts +15 -2
  16. package/templates/community-club/tier5-src/wix-spaces-create-post/index.ts +14 -4
  17. package/templates/community-club-owner/tier5-src/_shared/collection-contract.ts +15 -2
  18. package/templates/community-club-owner/tier5-src/himi-owner-lite-events/index.ts +1 -0
  19. package/templates/events-meetups/dev/index.ts +518 -272
  20. package/templates/events-meetups/tier5-src/_shared/collection-contract.ts +15 -2
  21. package/templates/events-meetups/tier5-src/wix-spaces-create-post/index.ts +14 -4
  22. package/templates/events-meetups-owner/tier5-src/_shared/collection-contract.ts +15 -2
  23. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-events/index.ts +1 -0
  24. package/templates/gym-fitness/dev/index.ts +520 -274
  25. package/templates/gym-fitness/tier5-src/_shared/collection-contract.ts +15 -2
  26. package/templates/gym-fitness/tier5-src/wix-spaces-create-post/index.ts +14 -4
  27. package/templates/gym-fitness-owner/tier5-src/_shared/collection-contract.ts +15 -2
  28. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-events/index.ts +1 -0
  29. package/templates/nonprofit-cause/dev/index.ts +518 -272
  30. package/templates/nonprofit-cause/tier5-src/_shared/collection-contract.ts +15 -2
  31. package/templates/nonprofit-cause/tier5-src/wix-spaces-create-post/index.ts +14 -4
  32. package/templates/nonprofit-cause-owner/tier5-src/_shared/collection-contract.ts +15 -2
  33. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-events/index.ts +1 -0
  34. package/templates/owner-lite/tier5-src/_shared/collection-contract.ts +15 -2
  35. package/templates/owner-lite/tier5-src/himi-owner-lite-events/index.ts +1 -0
  36. package/templates/podcast-creator/dev/index.ts +518 -272
  37. package/templates/podcast-creator/tier5-src/_shared/collection-contract.ts +15 -2
  38. package/templates/podcast-creator/tier5-src/wix-spaces-create-post/index.ts +14 -4
  39. package/templates/podcast-creator-owner/tier5-src/_shared/collection-contract.ts +15 -2
  40. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-events/index.ts +1 -0
  41. package/templates/portfolio-creative/dev/index.ts +520 -274
  42. package/templates/portfolio-creative/tier5-src/_shared/collection-contract.ts +15 -2
  43. package/templates/portfolio-creative/tier5-src/wix-spaces-create-post/index.ts +14 -4
  44. package/templates/portfolio-creative-owner/tier5-src/_shared/collection-contract.ts +15 -2
  45. package/templates/restaurant/dev/index.ts +518 -272
  46. package/templates/restaurant/tier5-src/_shared/collection-contract.ts +15 -2
  47. package/templates/restaurant/tier5-src/wix-spaces-create-post/index.ts +14 -4
  48. package/templates/restaurant-owner/tier5-src/_shared/collection-contract.ts +15 -2
  49. package/templates/restaurant-owner/tier5-src/himi-owner-lite-events/index.ts +1 -0
  50. package/templates/retail-store/dev/index.ts +5 -1
  51. package/templates/retail-store/tier5-src/_shared/collection-contract.ts +15 -2
  52. package/templates/retail-store/tier5-src/wix-spaces-create-post/index.ts +14 -4
  53. package/templates/retail-store-owner/tier5-src/_shared/collection-contract.ts +15 -2
  54. package/templates/yoga-studio/dev/index.ts +518 -272
  55. package/templates/yoga-studio/tier5-src/_shared/collection-contract.ts +15 -2
  56. package/templates/yoga-studio/tier5-src/wix-spaces-create-post/index.ts +14 -4
  57. package/templates/yoga-studio-owner/tier5-src/_shared/collection-contract.ts +15 -2
  58. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-events/index.ts +1 -0
@@ -8,6 +8,9 @@ export interface EventRow {
8
8
  dateLine: string;
9
9
  /** "Past" once the event has finished, else "". */
10
10
  statusLabel: string;
11
+ /** Has the event finished? The SAME `eventIsPast` answer `statusLabel` reads — one call,
12
+ * both fields — so a card can never say "Past" and still offer Register. */
13
+ isPast: boolean;
11
14
  location: string;
12
15
  status: string;
13
16
  image: string;
@@ -222,6 +222,7 @@ function normalizeEventAt(e, now) {
222
222
  const start = startDateOf(e);
223
223
  const date = start ? new Date(start) : null;
224
224
  const image = eventImage(e);
225
+ const past = eventIsPast(e, now);
225
226
  return {
226
227
  id,
227
228
  title,
@@ -230,7 +231,8 @@ function normalizeEventAt(e, now) {
230
231
  dateLine: eventWhen(e, now),
231
232
  // "Past" for a finished event, "" otherwise — the shared collection row prints it in the
232
233
  // meta line, so an event from last month no longer reads as current.
233
- statusLabel: eventIsPast(e, now) ? "Past" : "",
234
+ statusLabel: past ? "Past" : "",
235
+ isPast: past,
234
236
  location: eventLocation(e),
235
237
  status: titleCase(e.eventStatus) || titleCase(e.status),
236
238
  image,
package/dist/cli.mjs CHANGED
@@ -16814,9 +16814,10 @@ var init_native_requirements = __esm({
16814
16814
  // Silent / background push wakes the app for a content-available payload.
16815
16815
  infoPlist: { UIBackgroundModes: ["remote-notification"] }
16816
16816
  },
16817
- // macOS also uses APNs via the aps-environment entitlement (no UIBackgroundModes —
16818
- // background delivery on macOS doesn't use the iOS remote-notification background mode).
16819
- macos: { entitlements: { "aps-environment": "development" } },
16817
+ // macOS also uses APNs, but its entitlement key is `com.apple.developer.aps-environment`
16818
+ // (iOS's is the bare `aps-environment`). No UIBackgroundModes — background delivery on macOS
16819
+ // doesn't use the iOS remote-notification background mode.
16820
+ macos: { entitlements: { "com.apple.developer.aps-environment": "development" } },
16820
16821
  android: { permissions: ["android.permission.POST_NOTIFICATIONS"] },
16821
16822
  privacy: {
16822
16823
  collectedDataTypes: [
@@ -20362,7 +20363,7 @@ var PLAYER_HOST_MANIFEST;
20362
20363
  var init_player_host_manifest = __esm({
20363
20364
  "../../apps/himi-player/player-host-manifest.ts"() {
20364
20365
  PLAYER_HOST_MANIFEST = {
20365
- "version": "1.2.0",
20366
+ "version": "1.3.0",
20366
20367
  "capabilities": [
20367
20368
  "app_info",
20368
20369
  "audio_record",
@@ -20405,19 +20406,27 @@ var init_player_host_manifest = __esm({
20405
20406
  "wix_platform"
20406
20407
  ],
20407
20408
  "unlinked": {
20408
- "web": [],
20409
20409
  "ios": [
20410
20410
  "base44",
20411
20411
  "godot"
20412
20412
  ],
20413
20413
  "macos": [
20414
+ "audio_record_background",
20414
20415
  "base44",
20415
- "godot"
20416
+ "browser_auth",
20417
+ "godot",
20418
+ "iap",
20419
+ "live_activity",
20420
+ "motion",
20421
+ "push",
20422
+ "speech",
20423
+ "wix_cashier"
20416
20424
  ],
20417
20425
  "android": [
20418
20426
  "base44",
20419
20427
  "godot"
20420
- ]
20428
+ ],
20429
+ "web": []
20421
20430
  },
20422
20431
  "nativeComponents": [
20423
20432
  "template.HimiPlayerCard",
@@ -143304,23 +143313,23 @@ var init_preview_assets_generated = __esm({
143304
143313
  "../../packages/serve/src/preview-assets.generated.ts"() {
143305
143314
  init_preview_assets_format();
143306
143315
  PREVIEW_ASSET_MANIFEST = [
143307
- { key: "assets/index-DGtdfh3l.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1186860, sha256: "2cdbf6bcc03e3792c8c1ae50ed1f71ac89b2e53caebe38dce19cb9446572e02f" },
143308
- { key: "assets/pdf.worker.min-BmVo14Nb.mjs", contentType: "application/octet-stream", offset: 1186868, length: 1317034, sha256: "a33cfe728c584fdba4fcc1fd54bcdc2f9f2f13889ddbb5b2bd1d0f8cbe49b84e" },
143309
- { key: "assets/pdfDocument-BbC8s1V3.js", contentType: "application/javascript; charset=utf-8", offset: 2503902, length: 489166, sha256: "cf3002d973e8aa1eaf3c727ee818567b257d6a424fa6a63ea1fc16e5da272d44" },
143310
- { key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 2993068, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
143311
- { key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 2997678, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
143312
- { key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 3486262, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
143313
- { key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 3577186, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
143314
- { key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 3668170, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
143315
- { key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 3759150, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
143316
- { key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 3850022, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
143317
- { key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 3941002, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
143318
- { key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 4049638, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
143319
- { key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 4158722, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
143320
- { key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 4267770, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
143321
- { key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 4375470, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
143322
- { key: "index.html", contentType: "text/html; charset=utf-8", offset: 4463578, length: 4947, sha256: "4b2d8f3664db552d9b52b738019ee1a42c60acb713984c49a290b1903c115f5f" },
143323
- { key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 4468525, length: 17344, sha256: "d057298d75e7373e5e2c4d11b7d1bcbea88e03af555ba63d97641c78509c58e4" }
143316
+ { key: "assets/index-DiC0HNa3.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1187655, sha256: "08fb74d8752e2f7ae5dcffd520eb03d2eb3def8db9d8fa4b9d5de1a7ae293100" },
143317
+ { key: "assets/pdf.worker.min-BmVo14Nb.mjs", contentType: "application/octet-stream", offset: 1187663, length: 1317034, sha256: "a33cfe728c584fdba4fcc1fd54bcdc2f9f2f13889ddbb5b2bd1d0f8cbe49b84e" },
143318
+ { key: "assets/pdfDocument-CXSifjpc.js", contentType: "application/javascript; charset=utf-8", offset: 2504697, length: 489166, sha256: "7dd04afebfbeeede2222a8beb5d87e58391ed36493b57a190a3e2eed4da35356" },
143319
+ { key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 2993863, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
143320
+ { key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 2998473, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
143321
+ { key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 3487057, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
143322
+ { key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 3577981, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
143323
+ { key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 3668965, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
143324
+ { key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 3759945, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
143325
+ { key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 3850817, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
143326
+ { key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 3941797, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
143327
+ { key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 4050433, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
143328
+ { key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 4159517, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
143329
+ { key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 4268565, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
143330
+ { key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 4376265, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
143331
+ { key: "index.html", contentType: "text/html; charset=utf-8", offset: 4464373, length: 4947, sha256: "e46144d08f1a73203c2313ca0ed8b607fd383f4889a82701d6c37f00e4720c95" },
143332
+ { key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 4469320, length: 17344, sha256: "d057298d75e7373e5e2c4d11b7d1bcbea88e03af555ba63d97641c78509c58e4" }
143324
143333
  ];
143325
143334
  }
143326
143335
  });
Binary file
@@ -15665,7 +15665,7 @@
15665
15665
  "name": "EventRow",
15666
15666
  "kind": "interface",
15667
15667
  "jsdoc": "",
15668
- "decl": "export interface EventRow {\n id: string;\n title: string;\n day: string;\n month: string;\n dateLine: string;\n /** \"Past\" once the event has finished, else \"\". */\n statusLabel: string;\n location: string;\n status: string;\n image: string;\n hasImage: boolean;\n noImage: boolean;\n detailScreenId: string;\n}"
15668
+ "decl": "export interface EventRow {\n id: string;\n title: string;\n day: string;\n month: string;\n dateLine: string;\n /** \"Past\" once the event has finished, else \"\". */\n statusLabel: string;\n /** Has the event finished? The SAME `eventIsPast` answer `statusLabel` reads — one call,\n * both fields — so a card can never say \"Past\" and still offer Register. */\n isPast: boolean;\n location: string;\n status: string;\n image: string;\n hasImage: boolean;\n noImage: boolean;\n detailScreenId: string;\n}"
15669
15669
  },
15670
15670
  {
15671
15671
  "name": "titleCase",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.1222.0",
3
+ "version": "0.1224.0",
4
4
  "type": "module",
5
5
  "description": "himi — the Himalaya authoring CLI. Build a Tier-5 content package on your own machine, push it to the production serve as a draft, preview it on web + Himi Player, and read worker logs back — the remote dev loop. Wraps @himalaya/serve-cli for transport.",
6
6
  "keywords": [
@@ -67,5 +67,5 @@
67
67
  "artifactId": "himalaya-cli"
68
68
  }
69
69
  },
70
- "falconPackageHash": "aca6dc6e29031b15d2bbed0009cded2e1e6a6c974fc432b8aced8d13"
70
+ "falconPackageHash": "6b76728b30d33797b71068367615815eb886dc05bb87adbcc4577a32"
71
71
  }
@@ -21370,6 +21370,7 @@ const SCREENS = runtimeSetting({
21370
21370
  "props": {
21371
21371
  "id": "title",
21372
21372
  "label": "Title",
21373
+ "required": true,
21373
21374
  "kind": "text",
21374
21375
  "text": {
21375
21376
  "placeholder": "Add a title",
@@ -21417,7 +21418,10 @@ const SCREENS = runtimeSetting({
21417
21418
  "style": "primary",
21418
21419
  "fullWidth": true,
21419
21420
  "action": {
21420
- "actionId": "tier5.spacesCreatePost.submit"
21421
+ "actionId": "tier5.spacesCreatePost.submit",
21422
+ "params": {
21423
+ "postScreenId": "himi_blog_post"
21424
+ }
21421
21425
  }
21422
21426
  }
21423
21427
  }
@@ -37,6 +37,13 @@ export interface CollectionConfig {
37
37
  * `screenId` opens that screen with the row id; without it the verb reuses the
38
38
  * row's own tap action (the detail screen that owns the verb). */
39
39
  primaryVerb?: { label: string; screenId?: string };
40
+ /** A TIME-BOUND entity (an event, a booking, a reservation): the boolean row field the
41
+ * DOMAIN sets once the record has finished — computed by the same rule as the row's own
42
+ * "Past" label, in the same place, so the two cannot disagree. A finished
43
+ * record's `primaryVerb` (Register, Book…) is transactional and would act on something
44
+ * that is over, so it is swapped for a non-transactional "View details" that opens the
45
+ * row's own detail. Without a row tap action the verb is simply hidden. */
46
+ endedWhen?: string;
40
47
  /** A catalog card whose one expected per-item action is "favorite".
41
48
  * The ellipsis overflow is replaced by an always-visible heart toggle wired to
42
49
  * the same `collectionSave` reducer. A dropdown `Menu` anchored inside a
@@ -57,7 +64,7 @@ export const COLLECTIONS: Record<string, CollectionConfig> = {
57
64
  himiBookingBookings: { rows: 'rows', title: 'title', subtitle: 'subtitle', metadata: ['status'], view: 'list', backendQuery: true,
58
65
  defaultFilter: 'value:Upcoming', searchPlaceholder: 'Search by booking ID', countNoun: ['booking', 'bookings'], filterValues: ['Upcoming', 'Past', 'Cancelled'],
59
66
  sortLabels: { default: 'Soonest', az: 'Oldest', za: 'Latest' } },
60
- himiEvents: { rows: 'results', title: 'title', subtitle: 'dateLine', image: 'image', view: 'grid', backendQuery: true, defaultFilter: 'value:Upcoming', searchPlaceholder: 'Search events', countNoun: ['event', 'events'], filterValues: ['Upcoming', 'Past'], sortLabels: { default: 'Date', az: 'Title A–Z', za: 'Title Z–A' }, primaryVerb: { label: 'Register' } },
67
+ himiEvents: { rows: 'results', title: 'title', subtitle: 'dateLine', image: 'image', view: 'grid', backendQuery: true, defaultFilter: 'value:Upcoming', searchPlaceholder: 'Search events', countNoun: ['event', 'events'], filterValues: ['Upcoming', 'Past'], sortLabels: { default: 'Date', az: 'Title A–Z', za: 'Title Z–A' }, primaryVerb: { label: 'Register' }, endedWhen: 'isPast' },
61
68
  himiBlogFeed: { rows: 'posts', title: 'title', subtitle: 'excerpt', image: 'image', filter: 'category', filterValuesFrom: 'categories', view: 'grid', narrowedBy: 'selectedCategoryId' },
62
69
  himiOwnerLiteProducts: { rows: 'products', title: 'name', subtitle: 'price', image: 'image', view: 'grid', backendQuery: true, countNoun: ['product', 'products'] },
63
70
  himiOwnerModules: { instanceInput: 'module', rows: 'rows', title: 'title', subtitle: 'subtitle', image: 'image', filter: 'statusLabel', view: 'list', paged: true, reviewAction: 'prepareRecord', reviewSelectedAction: 'prepareSelected' },
@@ -180,7 +187,13 @@ export function projectCollection(source: CollectionRow[], config: CollectionCon
180
187
  collectionSubtitle: scalar(row[config.subtitle ?? '']) || scalar(row.subtitle) || scalar(row.description) || scalar(row.meta),
181
188
  collectionImage: scalar(row[config.image ?? '']), collectionNoImage: !scalar(row[config.image ?? '']), collectionHasImage: !!scalar(row[config.image ?? '']),
182
189
  collectionSaved: prefs.savedIds.includes(collectionId(row)), collectionNotSaved: !prefs.savedIds.includes(collectionId(row)), collectionSelected: prefs.selectedIds.includes(collectionId(row)),
183
- collectionNoIdentity: !collectionId(row) })),
190
+ collectionNoIdentity: !collectionId(row),
191
+ // Older cached EventRows predate `isPast` but already stored `statusLabel: "Past"`.
192
+ // Keep their ended state through an offline render until the row is refreshed.
193
+ ...(config.endedWhen ? (() => {
194
+ const ended = row[config.endedWhen] === true || (config.endedWhen === 'isPast' && row.statusLabel === 'Past');
195
+ return { collectionEnded: ended, collectionNotEnded: !ended };
196
+ })() : {}) })),
184
197
  filters, loaded: rows.length,
185
198
  };
186
199
  }
@@ -2,7 +2,7 @@
2
2
  // Holds the typed title + body; submit creates AND publishes a real blog post via
3
3
  // the Wix Blog API, then the new post shows up in ws_blog. (Forum has no member
4
4
  // create endpoint in the SDK; blog is the available authored-content write.)
5
- import { defineService } from "@himalaya/state";
5
+ import { defineService, navigate } from "@himalaya/state";
6
6
  import { createDraftPost, publishDraftPost } from "@himalaya/wix-blog";
7
7
  import { memberAccess } from "../_shared/member-access.js";
8
8
 
@@ -38,7 +38,7 @@ function val(arg?: unknown): string {
38
38
  return typeof arg === "string" ? arg : ((arg as { value?: string; text?: string })?.value ?? (arg as { text?: string })?.text ?? "");
39
39
  }
40
40
 
41
- const svc = defineService<S, { load: (s: S) => Promise<S>; setTitle: (s: S, a?: unknown) => S; setBody: (s: S, a?: unknown) => S; focusBody: (s: S) => S; submit: (s: S) => Promise<S> }>("spacesCreatePost", {
41
+ const svc = defineService<S, { load: (s: S) => Promise<S>; setTitle: (s: S, a?: unknown) => S; setBody: (s: S, a?: unknown) => S; focusBody: (s: S) => S; submit: (s: S, a?: unknown) => Promise<S> }>("spacesCreatePost", {
42
42
  state: { status: "ready", title: "", body: "", hint: "Share something with the community.", bodyFocus: "", needsSignIn: false, noSignIn: true, hideForm: true },
43
43
  actions: {
44
44
  load: async (s) => {
@@ -50,7 +50,11 @@ const svc = defineService<S, { load: (s: S) => Promise<S>; setTitle: (s: S, a?:
50
50
  // Title's Next key lands here. The keyboard stays up (that is the "next"
51
51
  // contract) and the body field takes focus.
52
52
  focusBody: (s) => ({ ...s, bodyFocus: bumpToken(s.bodyFocus) }),
53
- submit: async (s) => {
53
+ // `postScreenId` (a submit param) names the screen that shows ONE post, so a host
54
+ // with one lands the author on what they just posted. Several apps share this
55
+ // worker and each owns its own post screen, so the descriptor names it; without
56
+ // it the composer clears and confirms in place.
57
+ submit: async (s, a) => {
54
58
  if (s.needsSignIn) return { ...s, hint: "Sign in to post." };
55
59
  const title = s.title.trim();
56
60
  if (!title) return { ...s, hint: "Please enter a title." };
@@ -73,7 +77,13 @@ const svc = defineService<S, { load: (s: S) => Promise<S>; setTitle: (s: S, a?:
73
77
  // steals focus, so those transitions must clear it (see himi-store-account's
74
78
  // NO_FOCUS on its mode switches). Same token, opposite fix; the deciding
75
79
  // question is always whether the field stays mounted.
76
- return { ...s, status: "ready", title: "", body: "", bodyFocus: s.bodyFocus, hint: `Posted “${title}” ✓ — it now appears in Blog.` };
80
+ const posted: S = { ...s, status: "ready", title: "", body: "", bodyFocus: s.bodyFocus, hint: `Posted “${title}” ✓ — it now appears in Blog.` };
81
+ // Only after the worker has the post's id — a failure takes the catch below and
82
+ // keeps every character the user typed.
83
+ const postScreenId = (a as { postScreenId?: unknown } | undefined)?.postScreenId;
84
+ return id && typeof postScreenId === "string" && postScreenId
85
+ ? navigate(posted, { screenId: postScreenId, args: { id } })
86
+ : posted;
77
87
  } catch {
78
88
  return { ...s, status: "ready", hint: "Couldn't post — check your connection and try again." };
79
89
  } finally {
@@ -37,6 +37,13 @@ export interface CollectionConfig {
37
37
  * `screenId` opens that screen with the row id; without it the verb reuses the
38
38
  * row's own tap action (the detail screen that owns the verb). */
39
39
  primaryVerb?: { label: string; screenId?: string };
40
+ /** A TIME-BOUND entity (an event, a booking, a reservation): the boolean row field the
41
+ * DOMAIN sets once the record has finished — computed by the same rule as the row's own
42
+ * "Past" label, in the same place, so the two cannot disagree. A finished
43
+ * record's `primaryVerb` (Register, Book…) is transactional and would act on something
44
+ * that is over, so it is swapped for a non-transactional "View details" that opens the
45
+ * row's own detail. Without a row tap action the verb is simply hidden. */
46
+ endedWhen?: string;
40
47
  /** A catalog card whose one expected per-item action is "favorite".
41
48
  * The ellipsis overflow is replaced by an always-visible heart toggle wired to
42
49
  * the same `collectionSave` reducer. A dropdown `Menu` anchored inside a
@@ -57,7 +64,7 @@ export const COLLECTIONS: Record<string, CollectionConfig> = {
57
64
  himiBookingBookings: { rows: 'rows', title: 'title', subtitle: 'subtitle', metadata: ['status'], view: 'list', backendQuery: true,
58
65
  defaultFilter: 'value:Upcoming', searchPlaceholder: 'Search by booking ID', countNoun: ['booking', 'bookings'], filterValues: ['Upcoming', 'Past', 'Cancelled'],
59
66
  sortLabels: { default: 'Soonest', az: 'Oldest', za: 'Latest' } },
60
- himiEvents: { rows: 'results', title: 'title', subtitle: 'dateLine', image: 'image', view: 'grid', backendQuery: true, defaultFilter: 'value:Upcoming', searchPlaceholder: 'Search events', countNoun: ['event', 'events'], filterValues: ['Upcoming', 'Past'], sortLabels: { default: 'Date', az: 'Title A–Z', za: 'Title Z–A' }, primaryVerb: { label: 'Register' } },
67
+ himiEvents: { rows: 'results', title: 'title', subtitle: 'dateLine', image: 'image', view: 'grid', backendQuery: true, defaultFilter: 'value:Upcoming', searchPlaceholder: 'Search events', countNoun: ['event', 'events'], filterValues: ['Upcoming', 'Past'], sortLabels: { default: 'Date', az: 'Title A–Z', za: 'Title Z–A' }, primaryVerb: { label: 'Register' }, endedWhen: 'isPast' },
61
68
  himiBlogFeed: { rows: 'posts', title: 'title', subtitle: 'excerpt', image: 'image', filter: 'category', filterValuesFrom: 'categories', view: 'grid', narrowedBy: 'selectedCategoryId' },
62
69
  himiOwnerLiteProducts: { rows: 'products', title: 'name', subtitle: 'price', image: 'image', view: 'grid', backendQuery: true, countNoun: ['product', 'products'] },
63
70
  himiOwnerModules: { instanceInput: 'module', rows: 'rows', title: 'title', subtitle: 'subtitle', image: 'image', filter: 'statusLabel', view: 'list', paged: true, reviewAction: 'prepareRecord', reviewSelectedAction: 'prepareSelected' },
@@ -180,7 +187,13 @@ export function projectCollection(source: CollectionRow[], config: CollectionCon
180
187
  collectionSubtitle: scalar(row[config.subtitle ?? '']) || scalar(row.subtitle) || scalar(row.description) || scalar(row.meta),
181
188
  collectionImage: scalar(row[config.image ?? '']), collectionNoImage: !scalar(row[config.image ?? '']), collectionHasImage: !!scalar(row[config.image ?? '']),
182
189
  collectionSaved: prefs.savedIds.includes(collectionId(row)), collectionNotSaved: !prefs.savedIds.includes(collectionId(row)), collectionSelected: prefs.selectedIds.includes(collectionId(row)),
183
- collectionNoIdentity: !collectionId(row) })),
190
+ collectionNoIdentity: !collectionId(row),
191
+ // Older cached EventRows predate `isPast` but already stored `statusLabel: "Past"`.
192
+ // Keep their ended state through an offline render until the row is refreshed.
193
+ ...(config.endedWhen ? (() => {
194
+ const ended = row[config.endedWhen] === true || (config.endedWhen === 'isPast' && row.statusLabel === 'Past');
195
+ return { collectionEnded: ended, collectionNotEnded: !ended };
196
+ })() : {}) })),
184
197
  filters, loaded: rows.length,
185
198
  };
186
199
  }