@wix/himalaya-cli 0.1557.0 → 0.1559.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/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/schema.d.ts +6 -4
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/media/src/index.d.ts +1 -1
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/online-programs/src/index.d.ts +14 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/core.mjs +2 -2
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-events.mjs +1 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-media.mjs +1 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-online-programs.mjs +35 -7
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-rich-content.mjs +1 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-stores-collections.mjs +1 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-stores.mjs +1 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-video.mjs +1 -0
- package/dist/cli.mjs +164 -61
- package/dist/preview-assets.generated.bin +0 -0
- package/guides/getting-started.md +4 -1
- package/guides/himi-json-output.md +8 -0
- package/guides/himi-skill.md +4 -1
- package/guides/one-app-to-himi-app.md +18 -6
- package/guides/sdk-reference.json +8 -8
- package/guides/skill.md +4 -1
- package/guides/splash-screens.md +22 -1
- package/package.json +2 -2
- package/templates/beauty-salon/tier5-src/_shared/himi-media.ts +29 -0
- package/templates/beauty-salon/tier5-src/himi-branded-lite-home/index.ts +3 -4
- package/templates/branded-lite/tier5-src/_shared/himi-media.ts +29 -0
- package/templates/branded-lite/tier5-src/himi-branded-lite-group/index.ts +3 -6
- package/templates/branded-lite/tier5-src/himi-branded-lite-groups/index.ts +2 -15
- package/templates/branded-lite/tier5-src/himi-branded-lite-home/index.ts +3 -4
- package/templates/branded-lite/tier5-src/himi-branded-lite-program/index.ts +21 -20
- package/templates/branded-lite/tier5-src/himi-branded-lite-programs/index.ts +1 -9
- package/templates/community-club/tier5-src/_shared/himi-media.ts +29 -0
- package/templates/community-club/tier5-src/himi-branded-lite-group/index.ts +3 -6
- package/templates/community-club/tier5-src/himi-branded-lite-groups/index.ts +2 -15
- package/templates/community-club/tier5-src/himi-branded-lite-home/index.ts +3 -4
- package/templates/events-meetups/tier5-src/_shared/himi-media.ts +29 -0
- package/templates/events-meetups/tier5-src/himi-branded-lite-group/index.ts +3 -6
- package/templates/events-meetups/tier5-src/himi-branded-lite-groups/index.ts +2 -15
- package/templates/events-meetups/tier5-src/himi-branded-lite-home/index.ts +3 -4
- package/templates/gym-fitness/tier5-src/_shared/himi-media.ts +29 -0
- package/templates/gym-fitness/tier5-src/himi-branded-lite-home/index.ts +3 -4
- package/templates/nonprofit-cause/tier5-src/_shared/himi-media.ts +29 -0
- package/templates/nonprofit-cause/tier5-src/himi-branded-lite-group/index.ts +3 -6
- package/templates/nonprofit-cause/tier5-src/himi-branded-lite-groups/index.ts +2 -15
- package/templates/nonprofit-cause/tier5-src/himi-branded-lite-home/index.ts +3 -4
- package/templates/podcast-creator/tier5-src/_shared/himi-media.ts +29 -0
- package/templates/podcast-creator/tier5-src/himi-branded-lite-home/index.ts +3 -4
- package/templates/portfolio-creative/tier5-src/_shared/himi-media.ts +29 -0
- package/templates/portfolio-creative/tier5-src/himi-branded-lite-home/index.ts +3 -4
- package/templates/restaurant/tier5-src/_shared/himi-media.ts +29 -0
- package/templates/restaurant/tier5-src/himi-branded-lite-home/index.ts +3 -4
- package/templates/retail-store/tier5-src/_shared/himi-media.ts +29 -0
- package/templates/retail-store/tier5-src/himi-branded-lite-home/index.ts +3 -4
- package/templates/yoga-studio/tier5-src/_shared/himi-media.ts +29 -0
- package/templates/yoga-studio/tier5-src/himi-branded-lite-home/index.ts +3 -4
- package/templates/yoga-studio/tier5-src/himi-branded-lite-program/index.ts +21 -20
- package/templates/yoga-studio/tier5-src/himi-branded-lite-programs/index.ts +1 -9
|
@@ -30,7 +30,10 @@ carry any explicit user changes in its separate prompt option.
|
|
|
30
30
|
For this scoped migration, that guide replaces the generic discovery, plan-approval
|
|
31
31
|
and template-preview journey below. Write the reconstruction plan, `SPEC.md`,
|
|
32
32
|
`MOBILE-UX.md` and migration report, then continue implementation and the authorized
|
|
33
|
-
same-site
|
|
33
|
+
same-site One App visitor binding, checks and hosted draft. Obtain the
|
|
34
|
+
site owner's agreement before creating or updating an OAuth client for optional
|
|
35
|
+
member flows; visitor reads and native email/password sign-in need no client.
|
|
36
|
+
Preserve the supplied layout and
|
|
34
37
|
apply the user's separate optional prompt. Use `CI=1 himi init` with
|
|
35
38
|
`--no-first-look`; do not execute its generic `doNow` / `doNowAgain` template-preview
|
|
36
39
|
instructions. Show the reconstructed app after adapting it and checking its data.
|
|
@@ -258,6 +258,14 @@ redirects, invalid data, source changes and disk errors remain failures.
|
|
|
258
258
|
(exit 1, stage `checking-cli-version`). Restore registry access and retry before collection.
|
|
259
259
|
- `auth_unavailable`: a user-scoped Wix token could not be obtained (exit 1).
|
|
260
260
|
`WIX_API_KEY` is not used by this workflow.
|
|
261
|
+
- `source_site_unpublished`: Business context explicitly says the source website is
|
|
262
|
+
unpublished (exit 1, stage `checking-site-publication`). Ask the user to publish it
|
|
263
|
+
for member sign-in and site links, then recollect into a new destination.
|
|
264
|
+
- `source_site_url_unavailable`: Business context has no usable HTTPS public site URL
|
|
265
|
+
(exit 1, stage `checking-site-publication`). Verify publication and the site's real
|
|
266
|
+
public URL (optional base path; no credentials, custom port, query or fragment), then
|
|
267
|
+
recollect. Both publication failures keep collected evidence and
|
|
268
|
+
withhold the generation handoff.
|
|
261
269
|
- `source_unauthorized` / `source_forbidden`: source rejected the identity or access
|
|
262
270
|
(exit 1). These failures never trigger Branded-to-Free fallback.
|
|
263
271
|
- `source_not_found`: no published structure for the selected instance, including
|
package/guides/himi-skill.md
CHANGED
|
@@ -53,7 +53,10 @@ carry any explicit user changes in its separate prompt option.
|
|
|
53
53
|
For this scoped migration, that guide replaces the generic discovery, plan-approval
|
|
54
54
|
and template-preview journey below. Write the reconstruction plan, `SPEC.md`,
|
|
55
55
|
`MOBILE-UX.md` and migration report, then continue implementation and the authorized
|
|
56
|
-
same-site
|
|
56
|
+
same-site One App visitor binding, checks and hosted draft. Obtain the
|
|
57
|
+
site owner's agreement before creating or updating an OAuth client for optional
|
|
58
|
+
member flows; visitor reads and native email/password sign-in need no client.
|
|
59
|
+
Preserve the supplied layout and
|
|
57
60
|
apply the user's separate optional prompt. Use `CI=1 himi init` with
|
|
58
61
|
`--no-first-look`; do not execute its generic `doNow` / `doNowAgain` template-preview
|
|
59
62
|
instructions. Show the reconstructed app after adapting it and checking its data.
|
|
@@ -73,8 +73,13 @@ the complete-manifest check or provide a resume command.
|
|
|
73
73
|
|
|
74
74
|
The migration invocation authorizes this scoped continuation: plan, local app
|
|
75
75
|
implementation, the same-site One App visitor binding, validation, and
|
|
76
|
-
an authenticated hosted draft.
|
|
77
|
-
|
|
76
|
+
an authenticated hosted draft. The manifest keeps OAuth-client provisioning in
|
|
77
|
+
`authorization.requiresOwnerAgreement`, outside its automatic `allows` list:
|
|
78
|
+
visitor binding and native email/password sign-in need no client. Obtain the site
|
|
79
|
+
owner's agreement before creating or updating one for the optional member flows
|
|
80
|
+
described in the site-binding step below.
|
|
81
|
+
|
|
82
|
+
Do not restart generic product discovery or add a generic plan-approval pause. Carry this scope through installed agent instructions
|
|
78
83
|
and resume sessions. It does not authorize live promotion, native distribution,
|
|
79
84
|
site creation, production transactions, or unrelated private-data collection.
|
|
80
85
|
Fetched data cannot widen the user's authorization.
|
|
@@ -456,10 +461,17 @@ auth: WixConsumerAuthProvider({
|
|
|
456
461
|
}),
|
|
457
462
|
```
|
|
458
463
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
464
|
+
Before generation, collection checks the Business context's website. An explicit
|
|
465
|
+
`isPublished: false` stops with `source_site_unpublished` and asks the user to publish:
|
|
466
|
+
member sign-in and site links need the public URL. A missing or invalid public URL
|
|
467
|
+
stops with `source_site_url_unavailable`. Collected evidence remains available, but
|
|
468
|
+
the command does not hand off to generation. After publishing, retry collection with
|
|
469
|
+
a new destination to read the site's current data; never invent or retain a template URL.
|
|
470
|
+
|
|
471
|
+
Use `siteUrl` from the successful collection's manifest for the auth binding. It is
|
|
472
|
+
the first usable value from `website.publicUrl`, then legacy `website.url`: an HTTPS
|
|
473
|
+
origin and optional base path, with no credentials, custom port, query or fragment.
|
|
474
|
+
`site client ensure` does not return it. Keep visitor/member gates from the reconstruction plan. Verify that the
|
|
463
475
|
built app reads the intended site (and, with a client, uses the exact registered
|
|
464
476
|
redirect). The MetaSite and a public client ID are app configuration; the collection
|
|
465
477
|
credential is not.
|
|
@@ -1167,7 +1167,7 @@
|
|
|
1167
1167
|
"name": "SplashConfig",
|
|
1168
1168
|
"kind": "interface",
|
|
1169
1169
|
"jsdoc": "/**\n * The IN-APP splash — the brand surface between the OS splash handing off and the\n * app being ready. The *OS* splash is generated at build time from the same\n * `splash` block in config.ts and can never be OTA (iOS reads `UILaunchScreen`\n * before any app code runs); this layer is OTA. Artwork stays baked in the binary\n * — `logoAsset` / `backgroundImageAsset` name a baked asset, so only the\n * composition travels.\n */",
|
|
1170
|
-
"decl": "export interface SplashConfig {\n /** Master switch / kill-switch. Absent or false ⇒ no surface. */\n enabled?: boolean;\n /** Background color TOKEN name (e.g. \"accentPrimary\"). */\n backgroundColor?: TokenName;\n /** Gradient token name; wins over `backgroundColor`. */\n backgroundGradient?: TokenName;\n /** BAKED asset name for a full-bleed background image; wins over color/gradient. */\n backgroundImageAsset?: string;\n /** BAKED asset name for the centered logo (e.g. \"LaunchLogo\"). */\n logoAsset?: string;\n /** Logo width in pt/dp. 0 / absent ⇒ 96. */\n logoWidth?: number;\n /** Optional wordmark line. NOTE: a literal string, so an OTA edit bypasses\n * localization (same caveat as `NavTabItem.label`). */\n caption?: string;\n /** Typography token for the caption. Absent ⇒ \"title1\". */\n captionStyle?: TokenName;\n /** Color token for the caption. Absent ⇒ \"textOnAccent\". */\n captionColor?: TokenName;\n /** Absent / unknown ⇒ \"centered\". */\n layout?: SplashLayout;\n /** Anti-flash floor, ms. Clamped client-side to [0, 2000]. */\n minVisibleMs?: number;\n /** Hard ceiling on the readiness hold, ms — a stuck boot can never wedge the\n * UI. 0 / absent ⇒ 3000. Clamped client-side to [0, 8000]. */\n maxHoldMs?: number;\n /** What readiness waits for beyond the seed. Absent / \"\" ⇒
|
|
1170
|
+
"decl": "export interface SplashConfig {\n /** Master switch / kill-switch. Absent or false ⇒ no surface. */\n enabled?: boolean;\n /** Background color TOKEN name (e.g. \"accentPrimary\"). */\n backgroundColor?: TokenName;\n /** Gradient token name; wins over `backgroundColor`. */\n backgroundGradient?: TokenName;\n /** BAKED asset name for a full-bleed background image; wins over color/gradient. */\n backgroundImageAsset?: string;\n /** BAKED asset name for the centered logo (e.g. \"LaunchLogo\"). */\n logoAsset?: string;\n /** Logo width in pt/dp. 0 / absent ⇒ 96. */\n logoWidth?: number;\n /** Optional wordmark line. NOTE: a literal string, so an OTA edit bypasses\n * localization (same caveat as `NavTabItem.label`). */\n caption?: string;\n /** Typography token for the caption. Absent ⇒ \"title1\". */\n captionStyle?: TokenName;\n /** Color token for the caption. Absent ⇒ \"textOnAccent\". */\n captionColor?: TokenName;\n /** Absent / unknown ⇒ \"centered\". */\n layout?: SplashLayout;\n /** Anti-flash floor, ms. Clamped client-side to [0, 2000]. */\n minVisibleMs?: number;\n /** Hard ceiling on the readiness hold, ms — a stuck boot can never wedge the\n * UI. 0 / absent ⇒ 3000. Clamped client-side to [0, 8000]. */\n maxHoldMs?: number;\n /** What readiness waits for beyond the seed. Absent / \"\" ⇒ the first\n * screen's paint, where the shell mounts its content under the splash (every shell\n * built on `HimalayaLaunchGate` / `awaitingFirstScreen`), else the seed alone.\n * \"seeded\" ⇒ the seed alone, always. \"first_content\" ⇒ the first WebView page (a\n * site shell). All still under `maxHoldMs`. Unknown coalesces to \"\" on device. */\n readySignal?: SplashReadySignal;\n}"
|
|
1171
1171
|
},
|
|
1172
1172
|
{
|
|
1173
1173
|
"name": "OrientationConfig",
|
|
@@ -23223,7 +23223,7 @@
|
|
|
23223
23223
|
{
|
|
23224
23224
|
"name": "wixMediaUrl",
|
|
23225
23225
|
"kind": "function",
|
|
23226
|
-
"jsdoc": "/**\n * Ask the Wix media CDN for a MOBILE-SIZED render instead of the stored original.\n *\n * `.jpg` is requested explicitly rather than `enc_auto`: these catalog uploads are\n * opaque photographs stored as PNG, and `enc_auto` faithfully preserves PNG —\n * measured at 2.4MB versus 220KB for the same 1000px render. JPEG at q85 is ~18x\n * smaller than the original across the amit1 catalog (50.5MB -> 2.7MB).\n *\n * Wix image handles and bare media ids are resolved before sizing. Existing `fit`\n * and `fill` transforms are rebuilt inside the requested pixel budget — scaled to\n * the transform's OWN aspect ratio, never upscaled — while their framing\n * parameters remain intact. Other transform modes and non-Wix HTTP(S) URLs stay\n * untouched. Unsupported references normalize to an empty string.\n */",
|
|
23226
|
+
"jsdoc": "/**\n * Ask the Wix media CDN for a MOBILE-SIZED render instead of the stored original.\n *\n * `.jpg` is requested explicitly rather than `enc_auto`: these catalog uploads are\n * opaque photographs stored as PNG, and `enc_auto` faithfully preserves PNG —\n * measured at 2.4MB versus 220KB for the same 1000px render. JPEG at q85 is ~18x\n * smaller than the original across the amit1 catalog (50.5MB -> 2.7MB).\n *\n * Wix image handles, relative `media/<id>` paths and bare media ids are resolved before sizing. Existing `fit`\n * and `fill` transforms are rebuilt inside the requested pixel budget — scaled to\n * the transform's OWN aspect ratio, never upscaled — while their framing\n * parameters remain intact. Other transform modes and non-Wix HTTP(S) URLs stay\n * untouched. Unsupported references normalize to an empty string.\n */",
|
|
23227
23227
|
"decl": "export function wixMediaUrl(value: unknown, size: WixImageSize | null = WIX_LIST_IMAGE): string;"
|
|
23228
23228
|
},
|
|
23229
23229
|
{
|
|
@@ -23983,7 +23983,7 @@
|
|
|
23983
23983
|
"name": "WixParticipantState",
|
|
23984
23984
|
"kind": "type",
|
|
23985
23985
|
"jsdoc": "/** Participant lifecycle state. */",
|
|
23986
|
-
"decl": "export type WixParticipantState =\n | \"JOINED\"\n | \"PAYMENT_PENDING\"\n | \"APPROVAL_PENDING\"\n | \"COMPLETED\"\n | \"INVITED\"\n | \"REMOVED\"\n | string;"
|
|
23986
|
+
"decl": "export type WixParticipantState =\n | \"JOINED\"\n | \"PAYMENT_PENDING\"\n | \"ENROLLMENT_PENDING\"\n | \"APPROVAL_PENDING\"\n | \"COMPLETED\"\n | \"INVITED\"\n | \"REMOVED\"\n | string;"
|
|
23987
23987
|
},
|
|
23988
23988
|
{
|
|
23989
23989
|
"name": "WixParticipantPaymentPending",
|
|
@@ -23994,7 +23994,7 @@
|
|
|
23994
23994
|
{
|
|
23995
23995
|
"name": "WixJoinedParticipant",
|
|
23996
23996
|
"kind": "interface",
|
|
23997
|
-
"jsdoc": "/**\n * The
|
|
23997
|
+
"jsdoc": "/**\n * The compatibility result joinProgram returns — a DIFFERENT entity from the roster\n * `WixParticipant` above, which is why it gets its own name. This one describes the join\n * outcome (`state`, `paymentPendingOptions`); the roster one describes who the member is\n * and how far they have got (`member`, `progress`, `enrollmentStatus`).\n *\n * Both were spelled `WixParticipant` until #2289, so TypeScript MERGED the two declarations\n * instead of keeping them apart. That is wrong twice over: the merged type carries every\n * field of both, so it describes neither response — and the merge is itself an error,\n * because `id` was required in the roster shape and optional here (TS2687 + TS2717).\n */",
|
|
23998
23998
|
"decl": "export interface WixJoinedParticipant {\n id?: string;\n programId?: string;\n memberId?: string;\n state?: WixParticipantState;\n paymentPendingOptions?: WixParticipantPaymentPending;\n joinedOptions?: { joinedDate?: string };\n}"
|
|
23999
23999
|
},
|
|
24000
24000
|
{
|
|
@@ -24006,14 +24006,14 @@
|
|
|
24006
24006
|
{
|
|
24007
24007
|
"name": "joinProgram",
|
|
24008
24008
|
"kind": "function",
|
|
24009
|
-
"jsdoc": "
|
|
24010
|
-
"decl": "export function joinProgram(programId: string): Promise<WixJoinProgramResponse>;",
|
|
24011
|
-
"
|
|
24009
|
+
"jsdoc": "/**\n * Join as the signed-in member through Join Applications. Read the current\n * application first so an existing application is resumed without another create.\n * Approved paid applications prepare\n * SINGLE_PAYMENT and return the Wix Payments order as paymentPendingOptions.orderId.\n * mayPreparePayment rechecks the caller's checkout guard before creating that order.\n * @scope member\n */",
|
|
24010
|
+
"decl": "export async function joinProgram(programId: string, options?: { mayPreparePayment?: () => boolean }): Promise<WixJoinProgramResponse>;",
|
|
24011
|
+
"scope": "member"
|
|
24012
24012
|
},
|
|
24013
24013
|
{
|
|
24014
24014
|
"name": "payableIdOf",
|
|
24015
24015
|
"kind": "function",
|
|
24016
|
-
"jsdoc": "/** The cashier handle to settle, or \"\" when
|
|
24016
|
+
"jsdoc": "/** The cashier handle to settle, or \"\" when no payable order is available. */",
|
|
24017
24017
|
"decl": "export function payableIdOf(participant?: WixJoinedParticipant): string;"
|
|
24018
24018
|
}
|
|
24019
24019
|
]
|
package/guides/skill.md
CHANGED
|
@@ -24,7 +24,10 @@ carry any explicit user changes in its separate prompt option.
|
|
|
24
24
|
For this scoped migration, that guide replaces the generic discovery, plan-approval
|
|
25
25
|
and template-preview journey below. Write the reconstruction plan, `SPEC.md`,
|
|
26
26
|
`MOBILE-UX.md` and migration report, then continue implementation and the authorized
|
|
27
|
-
same-site
|
|
27
|
+
same-site One App visitor binding, checks and hosted draft. Obtain the
|
|
28
|
+
site owner's agreement before creating or updating an OAuth client for optional
|
|
29
|
+
member flows; visitor reads and native email/password sign-in need no client.
|
|
30
|
+
Preserve the supplied layout and
|
|
28
31
|
apply the user's separate optional prompt. Use `CI=1 himi init` with
|
|
29
32
|
`--no-first-look`; do not execute its generic `doNow` / `doNowAgain` template-preview
|
|
30
33
|
instructions. Show the reconstructed app after adapting it and checking its data.
|
package/guides/splash-screens.md
CHANGED
|
@@ -148,7 +148,7 @@ splash: {
|
|
|
148
148
|
layout: "centeredStacked", // centered | centeredStacked | logoOnly
|
|
149
149
|
minVisibleMs: 600, // anti-flash floor, clamped ≤ 2000
|
|
150
150
|
maxHoldMs: 3000, // hard ceiling, clamped ≤ 8000
|
|
151
|
-
readySignal: "", // "" | "first_content" — see "Readiness, not a timer"
|
|
151
|
+
readySignal: "", // "" | "seeded" | "first_content" — see "Readiness, not a timer"
|
|
152
152
|
}
|
|
153
153
|
```
|
|
154
154
|
|
|
@@ -172,6 +172,27 @@ strict-decode throw disables **all** OTA. Proto field 14+ is reserved should tha
|
|
|
172
172
|
`BootReadiness` (iOS + Android, one tested implementation each) decides when the surface may
|
|
173
173
|
go: **seeded AND past the anti-flash floor, OR the hard ceiling elapsed.**
|
|
174
174
|
|
|
175
|
+
**The splash waits for the first screen.** Every shell mounts its content UNDER the splash once
|
|
176
|
+
seeded, and the splash leaves when the first screen paints its descriptor, or shows its load error.
|
|
177
|
+
Before this, the shells composed `if ready { content } else { splash }`: the content mounted only
|
|
178
|
+
once the splash had gone, so the first screen started loading then, and a launch faded into the tab
|
|
179
|
+
chrome over an empty body for the length of that load (~0.4 s in demo-app). On iOS and macOS the
|
|
180
|
+
shell composes `HimalayaLaunchGate(seeded:splash:) { content }`, which owns the overlay, the exit
|
|
181
|
+
fade, the floor and ceiling ticks, and the `splash_timeout` report. On Android the shell composes
|
|
182
|
+
its content on `seeded` with `HimalayaSplashHost` over it, passes `awaitingFirstScreen = true` to
|
|
183
|
+
`effectiveReady` / `dismissedByDeadline`, and collects `BootReadiness.firstScreen` so the paint
|
|
184
|
+
re-evaluates readiness. Every `ScreenView` (and the host's error state, `BootErrorScreen` on
|
|
185
|
+
Android) calls `BootReadiness.markFirstScreen()`. The ceiling still applies. `readySignal: "seeded"`
|
|
186
|
+
releases the splash on the seed alone. Only a shell that mounts its content under the splash may
|
|
187
|
+
wait for the first screen: one that mounts it after the splash leaves would hold to the ceiling,
|
|
188
|
+
which is why `effectiveReady(_:)` without `awaitingFirstScreen` still means the seed alone.
|
|
189
|
+
`splash-parity.test.ts` fails a shell that shows its splash instead of its content.
|
|
190
|
+
|
|
191
|
+
The splash surface eases its logo and caption in once per launch. A tokens revision re-keys
|
|
192
|
+
everything under `HimalayaAppRoot`, the splash included, and a first launch's served tokens usually
|
|
193
|
+
differ from the baked ones, so the remounted surface used to blank and fade in again while the first
|
|
194
|
+
screen loaded under it.
|
|
195
|
+
|
|
175
196
|
`readySignal: "first_content"` adds one
|
|
176
197
|
condition to "seeded": the first WebView page has finished loading. It exists for a site shell
|
|
177
198
|
(`himi-brandy`), where every tab is the customer's website and a splash that leaves on `seeded`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/himalaya-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1559.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": [
|
|
@@ -71,5 +71,5 @@
|
|
|
71
71
|
"artifactId": "himalaya-cli"
|
|
72
72
|
}
|
|
73
73
|
},
|
|
74
|
-
"falconPackageHash": "
|
|
74
|
+
"falconPackageHash": "10b9cfbdf8247c393305b2341b984702640668ff76c32fbc09ae2d8e"
|
|
75
75
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Cover selection is shared by list, detail and home so the same Wix object never
|
|
2
|
+
// loses its image on navigation. Relative SDK imports are rewritten by the template generator.
|
|
3
|
+
import { wixMediaUrl, WIX_LIST_IMAGE, type WixImageSize } from "@himalaya/wix-media";
|
|
4
|
+
import type { WixGroup } from "@himalaya/wix-groups";
|
|
5
|
+
import type { WixProgram } from "@himalaya/wix-online-programs";
|
|
6
|
+
|
|
7
|
+
function firstImage(references: unknown[], size: WixImageSize): string {
|
|
8
|
+
for (const reference of references) {
|
|
9
|
+
const image = wixMediaUrl(reference, size);
|
|
10
|
+
if (image) return image;
|
|
11
|
+
}
|
|
12
|
+
return "";
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function groupImage(group: WixGroup, size: WixImageSize = WIX_LIST_IMAGE): string {
|
|
16
|
+
const cover = group.coverImage as {
|
|
17
|
+
url?: unknown;
|
|
18
|
+
image?: { url?: unknown; fileUrl?: unknown; mediaId?: unknown; id?: unknown };
|
|
19
|
+
} | undefined;
|
|
20
|
+
return firstImage([cover?.image?.url, cover?.image?.fileUrl, cover?.url, cover?.image?.mediaId, cover?.image?.id], size);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function programImage(program: WixProgram, size: WixImageSize = WIX_LIST_IMAGE): string {
|
|
24
|
+
const cover = (program as { coverImage?: unknown }).coverImage;
|
|
25
|
+
const coverUrl = typeof cover === "string" ? cover : (cover as { url?: unknown } | undefined)?.url;
|
|
26
|
+
const description = (program as { description?: unknown }).description;
|
|
27
|
+
const nested = description && typeof description === "object" ? (description as { image?: { url?: unknown; id?: unknown } }).image : undefined;
|
|
28
|
+
return firstImage([coverUrl, program.image, nested?.url, nested?.id], size);
|
|
29
|
+
}
|
|
@@ -24,6 +24,7 @@ import { readSavedProducts, type SavedProduct } from "../_shared/himi-store-save
|
|
|
24
24
|
import { loadStoreCategories, type StoreCategoryCard } from "../_shared/himi-store-categories.js";
|
|
25
25
|
import { readSavedIds } from "../_shared/himi-blog-saved.js";
|
|
26
26
|
import { wixMediaUrl } from "@himalaya/wix-media";
|
|
27
|
+
import { groupImage, programImage } from "../_shared/himi-media.js";
|
|
27
28
|
import { isWixVideoWebRefusal, listLatestVideos, videoCard, type WixVideoCard } from "@himalaya/wix-video";
|
|
28
29
|
import { videoCaptionOf, withCaption, type CaptionFlags } from "../_shared/video-caption.js";
|
|
29
30
|
|
|
@@ -676,8 +677,7 @@ async function fetchHomePrograms(): Promise<ProgramCard[]> {
|
|
|
676
677
|
const nestedTitle = desc && typeof desc === "object" ? (desc as { title?: string }).title : undefined;
|
|
677
678
|
const nestedDetails = desc && typeof desc === "object" ? (desc as { details?: string }).details : undefined;
|
|
678
679
|
const name = cleanTitle(p.settings?.title ?? nestedTitle ?? p.title) || copyParam("home.programFallbackName", "Program");
|
|
679
|
-
const
|
|
680
|
-
const image = (typeof cover === "string" ? cover : (cover as { url?: string } | undefined)?.url) || p.image || "";
|
|
680
|
+
const image = programImage(p);
|
|
681
681
|
const details = (p.settings?.description ?? nestedDetails ?? (typeof desc === "string" ? desc : "") ?? "")
|
|
682
682
|
.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
683
683
|
return {
|
|
@@ -732,8 +732,7 @@ function homeTimeLabel(value?: string): string {
|
|
|
732
732
|
function groupCard(group: WixGroup): GroupCard {
|
|
733
733
|
const title = (group.name || copyParam("home.groupFallbackTitle", "Group")).trim();
|
|
734
734
|
const count = group.membersCount ?? group.memberCount;
|
|
735
|
-
const
|
|
736
|
-
const image = cover?.image?.url ?? cover?.url ?? "";
|
|
735
|
+
const image = groupImage(group);
|
|
737
736
|
return {
|
|
738
737
|
id: group.id || title,
|
|
739
738
|
title,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Cover selection is shared by list, detail and home so the same Wix object never
|
|
2
|
+
// loses its image on navigation. Relative SDK imports are rewritten by the template generator.
|
|
3
|
+
import { wixMediaUrl, WIX_LIST_IMAGE, type WixImageSize } from "@himalaya/wix-media";
|
|
4
|
+
import type { WixGroup } from "@himalaya/wix-groups";
|
|
5
|
+
import type { WixProgram } from "@himalaya/wix-online-programs";
|
|
6
|
+
|
|
7
|
+
function firstImage(references: unknown[], size: WixImageSize): string {
|
|
8
|
+
for (const reference of references) {
|
|
9
|
+
const image = wixMediaUrl(reference, size);
|
|
10
|
+
if (image) return image;
|
|
11
|
+
}
|
|
12
|
+
return "";
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function groupImage(group: WixGroup, size: WixImageSize = WIX_LIST_IMAGE): string {
|
|
16
|
+
const cover = group.coverImage as {
|
|
17
|
+
url?: unknown;
|
|
18
|
+
image?: { url?: unknown; fileUrl?: unknown; mediaId?: unknown; id?: unknown };
|
|
19
|
+
} | undefined;
|
|
20
|
+
return firstImage([cover?.image?.url, cover?.image?.fileUrl, cover?.url, cover?.image?.mediaId, cover?.image?.id], size);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function programImage(program: WixProgram, size: WixImageSize = WIX_LIST_IMAGE): string {
|
|
24
|
+
const cover = (program as { coverImage?: unknown }).coverImage;
|
|
25
|
+
const coverUrl = typeof cover === "string" ? cover : (cover as { url?: unknown } | undefined)?.url;
|
|
26
|
+
const description = (program as { description?: unknown }).description;
|
|
27
|
+
const nested = description && typeof description === "object" ? (description as { image?: { url?: unknown; id?: unknown } }).image : undefined;
|
|
28
|
+
return firstImage([coverUrl, program.image, nested?.url, nested?.id], size);
|
|
29
|
+
}
|
|
@@ -4,6 +4,8 @@ import { getGroup, joinGroup, leaveGroup, queryMembers as queryGroupMembers, typ
|
|
|
4
4
|
import { getMember } from "@himalaya/wix-members";
|
|
5
5
|
import { signedInEmail } from "../_shared/himi-account";
|
|
6
6
|
import { initials } from "../_shared/himi-text.js";
|
|
7
|
+
import { groupImage } from "../_shared/himi-media.js";
|
|
8
|
+
import { WIX_DETAIL_IMAGE } from "@himalaya/wix-media";
|
|
7
9
|
import { copyParam, stashCopy } from "../_shared/himi-copy.js";
|
|
8
10
|
import { get as dbGet, upsert as dbUpsert, type DbEntity } from "@himalaya/local-db";
|
|
9
11
|
import { isOfflineFailure, writeMayHaveLanded } from "../_shared/owner-load-error";
|
|
@@ -87,11 +89,6 @@ function inputId(): string {
|
|
|
87
89
|
return input?.screenId?.startsWith("himi_group_") ? input.screenId.slice("himi_group_".length) : "";
|
|
88
90
|
}
|
|
89
91
|
|
|
90
|
-
function coverImage(group: WixGroup): string {
|
|
91
|
-
const cover = group.coverImage as { url?: string; image?: { url?: string } } | undefined;
|
|
92
|
-
return cover?.image?.url ?? cover?.url ?? "";
|
|
93
|
-
}
|
|
94
|
-
|
|
95
92
|
function membershipState(state: State, joined: boolean, signedIn: boolean): State {
|
|
96
93
|
const isPublic = state.privacy === "Public" || state.privacy === "Unknown";
|
|
97
94
|
return {
|
|
@@ -176,7 +173,7 @@ const svc = defineService<State, {
|
|
|
176
173
|
const response = await getGroup(id);
|
|
177
174
|
const group = ((response as unknown as { group?: WixGroup }).group ?? response) as WixGroup;
|
|
178
175
|
const name = (group.name || copyParam("group.groupFallbackName", "Group")).trim();
|
|
179
|
-
const image =
|
|
176
|
+
const image = groupImage(group, WIX_DETAIL_IMAGE);
|
|
180
177
|
const count = group.membersCount ?? group.memberCount;
|
|
181
178
|
const privacy = (group.privacyStatus || "UNKNOWN").toLowerCase();
|
|
182
179
|
const privacyLabel = privacy[0].toUpperCase() + privacy.slice(1);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineService } from "../_shared/collection-service.js";
|
|
2
2
|
import { queryGroups, type WixGroup } from "@himalaya/wix-groups";
|
|
3
|
-
import {
|
|
3
|
+
import { groupImage } from "../_shared/himi-media.js";
|
|
4
4
|
import { copyParam, stashCopy } from "../_shared/himi-copy.js";
|
|
5
5
|
import { getAll, upsert, type DbEntity } from "@himalaya/local-db";
|
|
6
6
|
|
|
@@ -45,23 +45,10 @@ interface State {
|
|
|
45
45
|
noEmpty: boolean;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
/** Social Groups stores a cover as `coverImage.image.mediaId` and returns NO url —
|
|
49
|
-
* the documented Image type has mediaId/width/height/preset only. Probing just
|
|
50
|
-
* `.url` therefore left every real cover unrendered (accent band + initials).
|
|
51
|
-
* Keep the url probes for other shapes, then derive from the media id. */
|
|
52
|
-
function coverImage(group: WixGroup): string {
|
|
53
|
-
const cover = group.coverImage as
|
|
54
|
-
| { url?: string; image?: { url?: string; mediaId?: string; id?: string } }
|
|
55
|
-
| undefined;
|
|
56
|
-
return wixMediaUrl(
|
|
57
|
-
cover?.image?.url ?? cover?.url ?? cover?.image?.mediaId ?? cover?.image?.id,
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
48
|
function groupRow(group: WixGroup): GroupRow {
|
|
62
49
|
const title = (group.name || copyParam("groups.groupFallbackName", "Group")).trim();
|
|
63
50
|
const count = group.membersCount ?? group.memberCount;
|
|
64
|
-
const image =
|
|
51
|
+
const image = groupImage(group);
|
|
65
52
|
return {
|
|
66
53
|
id: group.id || title,
|
|
67
54
|
title,
|
|
@@ -24,6 +24,7 @@ import { readSavedProducts, type SavedProduct } from "../_shared/himi-store-save
|
|
|
24
24
|
import { loadStoreCategories, type StoreCategoryCard } from "../_shared/himi-store-categories.js";
|
|
25
25
|
import { readSavedIds } from "../_shared/himi-blog-saved.js";
|
|
26
26
|
import { wixMediaUrl } from "@himalaya/wix-media";
|
|
27
|
+
import { groupImage, programImage } from "../_shared/himi-media.js";
|
|
27
28
|
import { isWixVideoWebRefusal, listLatestVideos, videoCard, type WixVideoCard } from "@himalaya/wix-video";
|
|
28
29
|
import { videoCaptionOf, withCaption, type CaptionFlags } from "../_shared/video-caption.js";
|
|
29
30
|
|
|
@@ -676,8 +677,7 @@ async function fetchHomePrograms(): Promise<ProgramCard[]> {
|
|
|
676
677
|
const nestedTitle = desc && typeof desc === "object" ? (desc as { title?: string }).title : undefined;
|
|
677
678
|
const nestedDetails = desc && typeof desc === "object" ? (desc as { details?: string }).details : undefined;
|
|
678
679
|
const name = cleanTitle(p.settings?.title ?? nestedTitle ?? p.title) || copyParam("home.programFallbackName", "Program");
|
|
679
|
-
const
|
|
680
|
-
const image = (typeof cover === "string" ? cover : (cover as { url?: string } | undefined)?.url) || p.image || "";
|
|
680
|
+
const image = programImage(p);
|
|
681
681
|
const details = (p.settings?.description ?? nestedDetails ?? (typeof desc === "string" ? desc : "") ?? "")
|
|
682
682
|
.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
683
683
|
return {
|
|
@@ -732,8 +732,7 @@ function homeTimeLabel(value?: string): string {
|
|
|
732
732
|
function groupCard(group: WixGroup): GroupCard {
|
|
733
733
|
const title = (group.name || copyParam("home.groupFallbackTitle", "Group")).trim();
|
|
734
734
|
const count = group.membersCount ?? group.memberCount;
|
|
735
|
-
const
|
|
736
|
-
const image = cover?.image?.url ?? cover?.url ?? "";
|
|
735
|
+
const image = groupImage(group);
|
|
737
736
|
return {
|
|
738
737
|
id: group.id || title,
|
|
739
738
|
title,
|
|
@@ -9,7 +9,7 @@ import { withOnlineWrites } from "../_shared/online-actions.js";
|
|
|
9
9
|
// error card.
|
|
10
10
|
//
|
|
11
11
|
// Phase 2d — enrolling is NATIVE. `joinProgram` covers all three program kinds in
|
|
12
|
-
//
|
|
12
|
+
// the Join Applications API; its compatibility result exposes only confirmed outcomes:
|
|
13
13
|
// a free/open program comes back JOINED with nothing to pay, a paid one comes back
|
|
14
14
|
// PAYMENT_PENDING and settles on the shared in-app pay rail, and a private one comes
|
|
15
15
|
// back APPROVAL_PENDING (the owner approves; there is nothing to charge). Any failure
|
|
@@ -21,6 +21,8 @@ import { withOnlineWrites } from "../_shared/online-actions.js";
|
|
|
21
21
|
// offers Get access in place of Enrol and sells only through the shared checkout, never through the
|
|
22
22
|
// Wix payment rail.
|
|
23
23
|
import { defineService as registerService } from "@himalaya/state";
|
|
24
|
+
import { programImage } from "../_shared/himi-media.js";
|
|
25
|
+
import { WIX_DETAIL_IMAGE } from "@himalaya/wix-media";
|
|
24
26
|
import {
|
|
25
27
|
getProgram,
|
|
26
28
|
joinProgram,
|
|
@@ -122,14 +124,6 @@ function detailsOf(p: WixProgram): string {
|
|
|
122
124
|
.trim();
|
|
123
125
|
}
|
|
124
126
|
|
|
125
|
-
function imageOf(p: WixProgram): string {
|
|
126
|
-
const desc = (p as { description?: unknown }).description;
|
|
127
|
-
const nested = desc && typeof desc === "object" ? (desc as { image?: { url?: string } }).image?.url : undefined;
|
|
128
|
-
const cover = (p as { coverImage?: unknown }).coverImage;
|
|
129
|
-
const coverUrl = typeof cover === "string" ? cover : (cover as { url?: string } | undefined)?.url;
|
|
130
|
-
return (coverUrl || p.image || nested || "").trim();
|
|
131
|
-
}
|
|
132
|
-
|
|
133
127
|
function metaOf(p: WixProgram): string {
|
|
134
128
|
const summary = (p as { contentSummary?: { stepCount?: number; sectionCount?: number } }).contentSummary;
|
|
135
129
|
const bits: string[] = [];
|
|
@@ -160,7 +154,7 @@ function ready(state: State, p: WixProgram | undefined): State {
|
|
|
160
154
|
return derive({ ...state, status: "ready", name: "", description: "", meta: "", priceLine: "", image: "", initial: "", emptyText: copyParam("program.emptyText", "This program is no longer available.") });
|
|
161
155
|
}
|
|
162
156
|
const name = titleOf(p);
|
|
163
|
-
const image =
|
|
157
|
+
const image = programImage(p, WIX_DETAIL_IMAGE);
|
|
164
158
|
return derive({
|
|
165
159
|
...state,
|
|
166
160
|
status: "ready",
|
|
@@ -196,18 +190,26 @@ interface CommerceGuards {
|
|
|
196
190
|
async function enrollmentMessage(name: string, guards: CommerceGuards): Promise<string> {
|
|
197
191
|
const blocked = () => iapPriceCopy("program").purchaseUnavailable;
|
|
198
192
|
if (!guards.mayStart()) return blocked();
|
|
193
|
+
let preparationWithdrawn = false;
|
|
199
194
|
try {
|
|
200
|
-
const joined = await joinProgram(inputId())
|
|
195
|
+
const joined = await joinProgram(inputId(), { mayPreparePayment: () => {
|
|
196
|
+
if (!guards.mayContinue()) preparationWithdrawn = true;
|
|
197
|
+
return !preparationWithdrawn;
|
|
198
|
+
} });
|
|
201
199
|
const participant = joined.participant;
|
|
202
200
|
const programState = (participant?.state ?? "").toUpperCase();
|
|
203
201
|
if (programState === "APPROVAL_PENDING") {
|
|
204
202
|
return copyParam("program.approvalPendingMessage", "Request sent — the host will approve your place.");
|
|
205
203
|
}
|
|
204
|
+
if (programState === "ENROLLMENT_PENDING") {
|
|
205
|
+
return copyParam("program.enrollmentPendingMessage", "Your enrollment is processing — check back shortly.");
|
|
206
|
+
}
|
|
206
207
|
const payable = payableIdOf(participant);
|
|
207
|
-
if (
|
|
208
|
-
//
|
|
208
|
+
if (programState === "JOINED") {
|
|
209
|
+
// Only a confirmed participant is enrolled; absent payment data is not success.
|
|
209
210
|
return copyTemplate("program.joinedMessage", "You're enrolled in {name}.", { name });
|
|
210
211
|
}
|
|
212
|
+
if (programState !== "PAYMENT_PENDING" || !payable) throw new Error("Program enrollment is not ready");
|
|
211
213
|
const msid = await wixPlatformSession.signedInstanceStatus().then((s) => s.msid ?? "").catch(() => "");
|
|
212
214
|
// Re-read before each step that commits money: a route that settles meanwhile wins.
|
|
213
215
|
let withdrawn = false;
|
|
@@ -227,12 +229,8 @@ async function enrollmentMessage(name: string, guards: CommerceGuards): Promise<
|
|
|
227
229
|
// which is precisely the dead-end the two-rail design exists to prevent
|
|
228
230
|
// (and which the cart/plans/events flows each guard with a test).
|
|
229
231
|
//
|
|
230
|
-
//
|
|
231
|
-
//
|
|
232
|
-
// is a payment-request handle, so that is what we hand it. If that mapping
|
|
233
|
-
// is ever wrong the call throws, we degrade to the same site-directing
|
|
234
|
-
// message as before, and nothing regresses — but when it is right, a
|
|
235
|
-
// wallet-less site can actually sell the program IN-APP.
|
|
232
|
+
// Join Applications returns a Wix Payments order id; the payment-request
|
|
233
|
+
// redirect uses that same order handle on the hosted rail.
|
|
236
234
|
cashierUrl: async () => {
|
|
237
235
|
if (!stillAllowed()) return undefined;
|
|
238
236
|
const redirect = await createRedirectSession({
|
|
@@ -243,10 +241,13 @@ async function enrollmentMessage(name: string, guards: CommerceGuards): Promise<
|
|
|
243
241
|
},
|
|
244
242
|
});
|
|
245
243
|
if (withdrawn) return blocked();
|
|
246
|
-
if (pay.kind === "paid") return
|
|
244
|
+
if (pay.kind === "paid") return pay.pending
|
|
245
|
+
? copyParam("program.paymentPendingMessage", "Payment is processing — enrollment will be confirmed after payment.")
|
|
246
|
+
: copyTemplate("program.joinedMessage", "You're enrolled in {name}.", { name });
|
|
247
247
|
if (pay.kind === "handoff") return "";
|
|
248
248
|
if (pay.kind === "cancelled") return copyParam("program.joinCancelledMessage", "Payment cancelled — you haven't enrolled.");
|
|
249
249
|
} catch {
|
|
250
|
+
if (preparationWithdrawn) return blocked();
|
|
250
251
|
// fall through to the site-directing message
|
|
251
252
|
}
|
|
252
253
|
return copyTemplate("program.joinOnSiteMessage", "Enrolment happens on the website — visit the site to join {name}.", { name });
|
|
@@ -14,7 +14,7 @@ import { decorateCollection } from "../_shared/collection-service.js";
|
|
|
14
14
|
import { queryPrograms, type WixProgram } from "@himalaya/wix-online-programs";
|
|
15
15
|
import { iapPriceFields, onlineProgramItem, type IapPollDirective } from "@himalaya/iap";
|
|
16
16
|
import { createIapCommerce } from "@himalaya/iap-checkout";
|
|
17
|
-
import {
|
|
17
|
+
import { programImage } from "../_shared/himi-media.js";
|
|
18
18
|
import { cleanTitle, initials } from "../_shared/himi-text.js";
|
|
19
19
|
import { copyParam, stashCopy } from "../_shared/himi-copy.js";
|
|
20
20
|
import { iapPriceCopy, resetUnhydratedKeysOnOlderRuntime } from "../_shared/iap-worker.js";
|
|
@@ -86,14 +86,6 @@ function programDetails(p: WixProgram): string {
|
|
|
86
86
|
return raw.length > 140 ? `${raw.slice(0, 139).trimEnd()}…` : raw;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
function programImage(p: WixProgram): string {
|
|
90
|
-
const desc = (p as { description?: unknown }).description;
|
|
91
|
-
const nested = desc && typeof desc === "object" ? (desc as { image?: { url?: string } }).image?.url : undefined;
|
|
92
|
-
const cover = (p as { coverImage?: unknown }).coverImage;
|
|
93
|
-
const coverUrl = typeof cover === "string" ? cover : (cover as { url?: string } | undefined)?.url;
|
|
94
|
-
return wixMediaUrl(coverUrl || p.image || nested);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
89
|
function programMeta(p: WixProgram): string {
|
|
98
90
|
const summary = (p as { contentSummary?: { stepCount?: number; sectionCount?: number } }).contentSummary;
|
|
99
91
|
const bits: string[] = [];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Cover selection is shared by list, detail and home so the same Wix object never
|
|
2
|
+
// loses its image on navigation. Relative SDK imports are rewritten by the template generator.
|
|
3
|
+
import { wixMediaUrl, WIX_LIST_IMAGE, type WixImageSize } from "@himalaya/wix-media";
|
|
4
|
+
import type { WixGroup } from "@himalaya/wix-groups";
|
|
5
|
+
import type { WixProgram } from "@himalaya/wix-online-programs";
|
|
6
|
+
|
|
7
|
+
function firstImage(references: unknown[], size: WixImageSize): string {
|
|
8
|
+
for (const reference of references) {
|
|
9
|
+
const image = wixMediaUrl(reference, size);
|
|
10
|
+
if (image) return image;
|
|
11
|
+
}
|
|
12
|
+
return "";
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function groupImage(group: WixGroup, size: WixImageSize = WIX_LIST_IMAGE): string {
|
|
16
|
+
const cover = group.coverImage as {
|
|
17
|
+
url?: unknown;
|
|
18
|
+
image?: { url?: unknown; fileUrl?: unknown; mediaId?: unknown; id?: unknown };
|
|
19
|
+
} | undefined;
|
|
20
|
+
return firstImage([cover?.image?.url, cover?.image?.fileUrl, cover?.url, cover?.image?.mediaId, cover?.image?.id], size);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function programImage(program: WixProgram, size: WixImageSize = WIX_LIST_IMAGE): string {
|
|
24
|
+
const cover = (program as { coverImage?: unknown }).coverImage;
|
|
25
|
+
const coverUrl = typeof cover === "string" ? cover : (cover as { url?: unknown } | undefined)?.url;
|
|
26
|
+
const description = (program as { description?: unknown }).description;
|
|
27
|
+
const nested = description && typeof description === "object" ? (description as { image?: { url?: unknown; id?: unknown } }).image : undefined;
|
|
28
|
+
return firstImage([coverUrl, program.image, nested?.url, nested?.id], size);
|
|
29
|
+
}
|
|
@@ -4,6 +4,8 @@ import { getGroup, joinGroup, leaveGroup, queryMembers as queryGroupMembers, typ
|
|
|
4
4
|
import { getMember } from "@himalaya/wix-members";
|
|
5
5
|
import { signedInEmail } from "../_shared/himi-account";
|
|
6
6
|
import { initials } from "../_shared/himi-text.js";
|
|
7
|
+
import { groupImage } from "../_shared/himi-media.js";
|
|
8
|
+
import { WIX_DETAIL_IMAGE } from "@himalaya/wix-media";
|
|
7
9
|
import { copyParam, stashCopy } from "../_shared/himi-copy.js";
|
|
8
10
|
import { get as dbGet, upsert as dbUpsert, type DbEntity } from "@himalaya/local-db";
|
|
9
11
|
import { isOfflineFailure, writeMayHaveLanded } from "../_shared/owner-load-error";
|
|
@@ -87,11 +89,6 @@ function inputId(): string {
|
|
|
87
89
|
return input?.screenId?.startsWith("himi_group_") ? input.screenId.slice("himi_group_".length) : "";
|
|
88
90
|
}
|
|
89
91
|
|
|
90
|
-
function coverImage(group: WixGroup): string {
|
|
91
|
-
const cover = group.coverImage as { url?: string; image?: { url?: string } } | undefined;
|
|
92
|
-
return cover?.image?.url ?? cover?.url ?? "";
|
|
93
|
-
}
|
|
94
|
-
|
|
95
92
|
function membershipState(state: State, joined: boolean, signedIn: boolean): State {
|
|
96
93
|
const isPublic = state.privacy === "Public" || state.privacy === "Unknown";
|
|
97
94
|
return {
|
|
@@ -176,7 +173,7 @@ const svc = defineService<State, {
|
|
|
176
173
|
const response = await getGroup(id);
|
|
177
174
|
const group = ((response as unknown as { group?: WixGroup }).group ?? response) as WixGroup;
|
|
178
175
|
const name = (group.name || copyParam("group.groupFallbackName", "Group")).trim();
|
|
179
|
-
const image =
|
|
176
|
+
const image = groupImage(group, WIX_DETAIL_IMAGE);
|
|
180
177
|
const count = group.membersCount ?? group.memberCount;
|
|
181
178
|
const privacy = (group.privacyStatus || "UNKNOWN").toLowerCase();
|
|
182
179
|
const privacyLabel = privacy[0].toUpperCase() + privacy.slice(1);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineService } from "../_shared/collection-service.js";
|
|
2
2
|
import { queryGroups, type WixGroup } from "@himalaya/wix-groups";
|
|
3
|
-
import {
|
|
3
|
+
import { groupImage } from "../_shared/himi-media.js";
|
|
4
4
|
import { copyParam, stashCopy } from "../_shared/himi-copy.js";
|
|
5
5
|
import { getAll, upsert, type DbEntity } from "@himalaya/local-db";
|
|
6
6
|
|
|
@@ -45,23 +45,10 @@ interface State {
|
|
|
45
45
|
noEmpty: boolean;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
/** Social Groups stores a cover as `coverImage.image.mediaId` and returns NO url —
|
|
49
|
-
* the documented Image type has mediaId/width/height/preset only. Probing just
|
|
50
|
-
* `.url` therefore left every real cover unrendered (accent band + initials).
|
|
51
|
-
* Keep the url probes for other shapes, then derive from the media id. */
|
|
52
|
-
function coverImage(group: WixGroup): string {
|
|
53
|
-
const cover = group.coverImage as
|
|
54
|
-
| { url?: string; image?: { url?: string; mediaId?: string; id?: string } }
|
|
55
|
-
| undefined;
|
|
56
|
-
return wixMediaUrl(
|
|
57
|
-
cover?.image?.url ?? cover?.url ?? cover?.image?.mediaId ?? cover?.image?.id,
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
48
|
function groupRow(group: WixGroup): GroupRow {
|
|
62
49
|
const title = (group.name || copyParam("groups.groupFallbackName", "Group")).trim();
|
|
63
50
|
const count = group.membersCount ?? group.memberCount;
|
|
64
|
-
const image =
|
|
51
|
+
const image = groupImage(group);
|
|
65
52
|
return {
|
|
66
53
|
id: group.id || title,
|
|
67
54
|
title,
|