@wix/himalaya-cli 0.1323.0 → 0.1325.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/stdlib/flows/checkout/src/index.d.ts +6 -10
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/iap-checkout/src/order-refs.d.ts +1 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/iap-checkout/src/purchase-fields.d.ts +1 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/screens/src/index.d.ts +9 -35
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/comments-v1/src/extensions.d.ts +79 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/groups/src/extensions.d.ts +221 -14
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/groups/src/index.d.ts +0 -2
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/social-feed/src/index.d.ts +38 -21
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/stores-v1/src/display.d.ts +1 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/checkout.mjs +30 -13
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/iap-checkout.mjs +20 -2
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/screens.mjs +2890 -54
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-comments.mjs +80 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-groups.mjs +45 -17
- package/dist/cli.mjs +5 -4
- package/fixtures/wix-canned.json +61 -6
- package/guides/iap-pricing.md +9 -0
- package/guides/iap-screen-adoption.md +77 -0
- package/guides/importable-sdks.md +2 -0
- package/guides/index.json +6 -0
- package/guides/release-notes.md +25 -0
- package/guides/sdk-reference.json +539 -32
- package/guides/wix-api-usage.md +5 -0
- package/package.json +2 -2
- package/templates/beauty-salon/config.ts +1 -1
- package/templates/beauty-salon/dev/index.ts +46 -18
- package/templates/beauty-salon/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/beauty-salon/tier5-src/_shared/collection-service.ts +3 -2
- package/templates/beauty-salon/tier5-src/_shared/himi-store-collection.ts +1 -1
- package/templates/beauty-salon/tier5-src/_shared/home-service.ts +2 -1
- package/templates/beauty-salon/tier5-src/_shared/iap-presentation.ts +53 -0
- package/templates/beauty-salon/tier5-src/_shared/iap-worker.ts +42 -0
- package/templates/beauty-salon/tier5-src/himi-branded-lite-cart/index.ts +48 -1
- package/templates/beauty-salon/tier5-src/himi-branded-lite-home/index.ts +16 -2
- package/templates/beauty-salon/tier5-src/himi-branded-lite-order/index.ts +24 -3
- package/templates/beauty-salon/tier5-src/himi-branded-lite-plans/index.ts +153 -64
- package/templates/beauty-salon/tier5-src/himi-store-product/index.ts +24 -3
- package/templates/beauty-salon/tier5-src/himi-store-saved/index.ts +8 -3
- package/templates/beauty-salon/tier5-src/himi-store-shop/index.ts +10 -0
- package/templates/beauty-salon/tier5-src/wix-spaces-plan-detail/index.ts +8 -3
- package/templates/beauty-salon-owner/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/beauty-salon-owner/tier5-src/_shared/collection-service.ts +3 -2
- package/templates/beauty-salon-owner/tier5-src/_shared/home-service.ts +2 -1
- package/templates/branded-lite/config.ts +1 -8
- package/templates/branded-lite/dev/index.ts +46 -134
- package/templates/branded-lite/himalaya.content.json +0 -3
- package/templates/branded-lite/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/branded-lite/tier5-src/_shared/collection-service.ts +3 -2
- package/templates/branded-lite/tier5-src/_shared/himi-store-collection.ts +1 -1
- package/templates/branded-lite/tier5-src/_shared/home-service.ts +2 -1
- package/templates/branded-lite/tier5-src/_shared/iap-presentation.ts +53 -0
- package/templates/branded-lite/tier5-src/himi-branded-lite-cart/index.ts +48 -1
- package/templates/branded-lite/tier5-src/himi-branded-lite-home/index.ts +16 -2
- package/templates/branded-lite/tier5-src/himi-branded-lite-order/index.ts +24 -3
- package/templates/branded-lite/tier5-src/himi-branded-lite-plans/index.ts +153 -64
- package/templates/branded-lite/tier5-src/himi-store-product/index.ts +24 -3
- package/templates/branded-lite/tier5-src/himi-store-saved/index.ts +8 -3
- package/templates/branded-lite/tier5-src/himi-store-shop/index.ts +10 -0
- package/templates/branded-lite/tier5-src/wix-spaces-plan-detail/index.ts +8 -3
- package/templates/community-club/config.ts +1 -8
- package/templates/community-club/dev/index.ts +3 -117
- package/templates/community-club/himalaya.content.json +0 -3
- package/templates/community-club/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/community-club/tier5-src/_shared/collection-service.ts +3 -2
- package/templates/community-club/tier5-src/_shared/home-service.ts +2 -1
- package/templates/community-club/tier5-src/_shared/iap-presentation.ts +53 -0
- package/templates/community-club/tier5-src/_shared/iap-worker.ts +42 -0
- package/templates/community-club/tier5-src/himi-branded-lite-home/index.ts +16 -2
- package/templates/community-club-owner/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/community-club-owner/tier5-src/_shared/collection-service.ts +3 -2
- package/templates/community-club-owner/tier5-src/_shared/home-service.ts +2 -1
- package/templates/events-meetups/config.ts +1 -8
- package/templates/events-meetups/dev/index.ts +3 -117
- package/templates/events-meetups/himalaya.content.json +0 -3
- package/templates/events-meetups/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/events-meetups/tier5-src/_shared/collection-service.ts +3 -2
- package/templates/events-meetups/tier5-src/_shared/home-service.ts +2 -1
- package/templates/events-meetups/tier5-src/_shared/iap-presentation.ts +53 -0
- package/templates/events-meetups/tier5-src/_shared/iap-worker.ts +42 -0
- package/templates/events-meetups/tier5-src/himi-branded-lite-home/index.ts +16 -2
- package/templates/events-meetups-owner/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/events-meetups-owner/tier5-src/_shared/collection-service.ts +3 -2
- package/templates/events-meetups-owner/tier5-src/_shared/home-service.ts +2 -1
- package/templates/gym-fitness/config.ts +1 -1
- package/templates/gym-fitness/dev/index.ts +46 -18
- package/templates/gym-fitness/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/gym-fitness/tier5-src/_shared/collection-service.ts +3 -2
- package/templates/gym-fitness/tier5-src/_shared/himi-store-collection.ts +1 -1
- package/templates/gym-fitness/tier5-src/_shared/home-service.ts +2 -1
- package/templates/gym-fitness/tier5-src/_shared/iap-presentation.ts +53 -0
- package/templates/gym-fitness/tier5-src/_shared/iap-worker.ts +42 -0
- package/templates/gym-fitness/tier5-src/himi-branded-lite-cart/index.ts +48 -1
- package/templates/gym-fitness/tier5-src/himi-branded-lite-home/index.ts +16 -2
- package/templates/gym-fitness/tier5-src/himi-branded-lite-order/index.ts +24 -3
- package/templates/gym-fitness/tier5-src/himi-branded-lite-plans/index.ts +153 -64
- package/templates/gym-fitness/tier5-src/himi-store-product/index.ts +24 -3
- package/templates/gym-fitness/tier5-src/himi-store-saved/index.ts +8 -3
- package/templates/gym-fitness/tier5-src/himi-store-shop/index.ts +10 -0
- package/templates/gym-fitness/tier5-src/wix-spaces-plan-detail/index.ts +8 -3
- package/templates/gym-fitness-owner/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/gym-fitness-owner/tier5-src/_shared/collection-service.ts +3 -2
- package/templates/gym-fitness-owner/tier5-src/_shared/home-service.ts +2 -1
- package/templates/nonprofit-cause/config.ts +1 -8
- package/templates/nonprofit-cause/dev/index.ts +29 -129
- package/templates/nonprofit-cause/himalaya.content.json +0 -3
- package/templates/nonprofit-cause/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/nonprofit-cause/tier5-src/_shared/collection-service.ts +3 -2
- package/templates/nonprofit-cause/tier5-src/_shared/home-service.ts +2 -1
- package/templates/nonprofit-cause/tier5-src/_shared/iap-presentation.ts +53 -0
- package/templates/nonprofit-cause/tier5-src/_shared/iap-worker.ts +42 -0
- package/templates/nonprofit-cause/tier5-src/himi-branded-lite-home/index.ts +16 -2
- package/templates/nonprofit-cause/tier5-src/himi-branded-lite-plans/index.ts +153 -64
- package/templates/nonprofit-cause/tier5-src/wix-spaces-plan-detail/index.ts +8 -3
- package/templates/nonprofit-cause-owner/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/nonprofit-cause-owner/tier5-src/_shared/collection-service.ts +3 -2
- package/templates/nonprofit-cause-owner/tier5-src/_shared/home-service.ts +2 -1
- package/templates/owner-lite/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/owner-lite/tier5-src/_shared/collection-service.ts +3 -2
- package/templates/owner-lite/tier5-src/_shared/home-service.ts +2 -1
- package/templates/podcast-creator/config.ts +1 -1
- package/templates/podcast-creator/dev/index.ts +29 -13
- package/templates/podcast-creator/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/podcast-creator/tier5-src/_shared/collection-service.ts +3 -2
- package/templates/podcast-creator/tier5-src/_shared/home-service.ts +2 -1
- package/templates/podcast-creator/tier5-src/_shared/iap-presentation.ts +53 -0
- package/templates/podcast-creator/tier5-src/_shared/iap-worker.ts +42 -0
- package/templates/podcast-creator/tier5-src/himi-branded-lite-home/index.ts +16 -2
- package/templates/podcast-creator/tier5-src/himi-branded-lite-plans/index.ts +153 -64
- package/templates/podcast-creator/tier5-src/wix-spaces-plan-detail/index.ts +8 -3
- package/templates/podcast-creator-owner/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/podcast-creator-owner/tier5-src/_shared/collection-service.ts +3 -2
- package/templates/podcast-creator-owner/tier5-src/_shared/home-service.ts +2 -1
- package/templates/portfolio-creative/config.ts +1 -1
- package/templates/portfolio-creative/dev/index.ts +20 -6
- package/templates/portfolio-creative/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/portfolio-creative/tier5-src/_shared/collection-service.ts +3 -2
- package/templates/portfolio-creative/tier5-src/_shared/himi-store-collection.ts +1 -1
- package/templates/portfolio-creative/tier5-src/_shared/home-service.ts +2 -1
- package/templates/portfolio-creative/tier5-src/_shared/iap-presentation.ts +53 -0
- package/templates/portfolio-creative/tier5-src/_shared/iap-worker.ts +42 -0
- package/templates/portfolio-creative/tier5-src/himi-branded-lite-cart/index.ts +48 -1
- package/templates/portfolio-creative/tier5-src/himi-branded-lite-home/index.ts +16 -2
- package/templates/portfolio-creative/tier5-src/himi-branded-lite-order/index.ts +24 -3
- package/templates/portfolio-creative/tier5-src/himi-store-product/index.ts +24 -3
- package/templates/portfolio-creative/tier5-src/himi-store-saved/index.ts +8 -3
- package/templates/portfolio-creative/tier5-src/himi-store-shop/index.ts +10 -0
- package/templates/portfolio-creative-owner/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/portfolio-creative-owner/tier5-src/_shared/collection-service.ts +3 -2
- package/templates/portfolio-creative-owner/tier5-src/_shared/home-service.ts +2 -1
- package/templates/restaurant/config.ts +1 -1
- package/templates/restaurant/dev/index.ts +3 -1
- package/templates/restaurant/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/restaurant/tier5-src/_shared/collection-service.ts +3 -2
- package/templates/restaurant/tier5-src/_shared/home-service.ts +2 -1
- package/templates/restaurant/tier5-src/_shared/iap-presentation.ts +53 -0
- package/templates/restaurant/tier5-src/_shared/iap-worker.ts +42 -0
- package/templates/restaurant/tier5-src/himi-branded-lite-home/index.ts +16 -2
- package/templates/restaurant-owner/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/restaurant-owner/tier5-src/_shared/collection-service.ts +3 -2
- package/templates/restaurant-owner/tier5-src/_shared/home-service.ts +2 -1
- package/templates/retail-store/config.ts +1 -1
- package/templates/retail-store/dev/index.ts +20 -6
- package/templates/retail-store/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/retail-store/tier5-src/_shared/collection-service.ts +3 -2
- package/templates/retail-store/tier5-src/_shared/himi-store-collection.ts +1 -1
- package/templates/retail-store/tier5-src/_shared/home-service.ts +2 -1
- package/templates/retail-store/tier5-src/_shared/iap-presentation.ts +53 -0
- package/templates/retail-store/tier5-src/_shared/iap-worker.ts +42 -0
- package/templates/retail-store/tier5-src/himi-branded-lite-cart/index.ts +48 -1
- package/templates/retail-store/tier5-src/himi-branded-lite-home/index.ts +16 -2
- package/templates/retail-store/tier5-src/himi-branded-lite-order/index.ts +24 -3
- package/templates/retail-store/tier5-src/himi-store-product/index.ts +24 -3
- package/templates/retail-store/tier5-src/himi-store-saved/index.ts +8 -3
- package/templates/retail-store/tier5-src/himi-store-shop/index.ts +10 -0
- package/templates/retail-store-owner/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/retail-store-owner/tier5-src/_shared/collection-service.ts +3 -2
- package/templates/retail-store-owner/tier5-src/_shared/home-service.ts +2 -1
- package/templates/yoga-studio/config.ts +1 -1
- package/templates/yoga-studio/dev/index.ts +29 -13
- package/templates/yoga-studio/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/yoga-studio/tier5-src/_shared/collection-service.ts +3 -2
- package/templates/yoga-studio/tier5-src/_shared/home-service.ts +2 -1
- package/templates/yoga-studio/tier5-src/_shared/iap-presentation.ts +53 -0
- package/templates/yoga-studio/tier5-src/himi-branded-lite-home/index.ts +16 -2
- package/templates/yoga-studio/tier5-src/himi-branded-lite-plans/index.ts +153 -64
- package/templates/yoga-studio/tier5-src/wix-spaces-plan-detail/index.ts +8 -3
- package/templates/yoga-studio-owner/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/yoga-studio-owner/tier5-src/_shared/collection-service.ts +3 -2
- package/templates/yoga-studio-owner/tier5-src/_shared/home-service.ts +2 -1
- package/templates/branded-lite/tier5-src/wix-spaces-create-group/index.ts +0 -29
- package/templates/community-club/tier5-src/wix-spaces-create-group/index.ts +0 -29
- package/templates/events-meetups/tier5-src/wix-spaces-create-group/index.ts +0 -29
- package/templates/nonprofit-cause/tier5-src/wix-spaces-create-group/index.ts +0 -29
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Tier-5 bundle: himi-store — product catalog.
|
|
2
|
+
import { withIapPresentation } from "../_shared/iap-presentation.js";
|
|
2
3
|
import { storeCollectionSaving } from "../_shared/himi-store-collection.js";
|
|
3
4
|
import { defineBackendCollection } from "../_shared/collection-service.js";
|
|
4
5
|
import { getCurrentCart } from "@himalaya/wix-ecom";
|
|
@@ -37,6 +38,7 @@ interface Row {
|
|
|
37
38
|
id: string;
|
|
38
39
|
name: string;
|
|
39
40
|
subtitle: string;
|
|
41
|
+
commercePrice: string;
|
|
40
42
|
image: string;
|
|
41
43
|
initial: string;
|
|
42
44
|
hasImage: boolean;
|
|
@@ -53,6 +55,7 @@ interface S {
|
|
|
53
55
|
fromCache?: boolean;
|
|
54
56
|
query: string;
|
|
55
57
|
all: Row[];
|
|
58
|
+
collection?: { items: Array<Row & { collectionSubtitle: string }> };
|
|
56
59
|
results: Row[];
|
|
57
60
|
/** RFC 0008 cursor paging — non-empty while more pages exist ("" ⇒ exhausted). */
|
|
58
61
|
nextCursor: string;
|
|
@@ -74,6 +77,7 @@ function row(p: StoreProduct): Row {
|
|
|
74
77
|
id,
|
|
75
78
|
name,
|
|
76
79
|
subtitle: productPrice(p),
|
|
80
|
+
commercePrice: productPrice(p),
|
|
77
81
|
image,
|
|
78
82
|
initial: initials(name),
|
|
79
83
|
hasImage: !!image,
|
|
@@ -99,6 +103,12 @@ function derive(s: S): S {
|
|
|
99
103
|
|
|
100
104
|
let cacheWrite = Promise.resolve();
|
|
101
105
|
const svc = defineBackendCollection("himiStoreShop", {
|
|
106
|
+
register: withIapPresentation<S>({
|
|
107
|
+
prefix: "shop", transientKeys: ["all", "results", "collection"], contentActions: ["collectionDataChanged"],
|
|
108
|
+
items: state => state.results.map(row => ({ reference: { kind: "storesProduct", id: row.id }, commerceDisplayPrice: row.commercePrice ?? row.subtitle })),
|
|
109
|
+
present: (state, price) => ({ ...state, results: state.results.map(row => ({ ...row, subtitle: price(row.id) })), all: state.all.map(row => ({ ...row, subtitle: price(row.id) })),
|
|
110
|
+
...(state.collection ? { collection: { ...state.collection, items: state.collection.items.map(row => ({ ...row, collectionSubtitle: price(row.id) })) } } : {}) }),
|
|
111
|
+
}),
|
|
102
112
|
state: derive({ status: "loading", query: "", all: [], results: [], nextCursor: "",
|
|
103
113
|
emptyText: "", countLabel: "", cartCount: 0, isLoading: true, notLoading: false,
|
|
104
114
|
isErrored: false, notErrored: true, noEmpty: true }),
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
// Tier-5 bundle: Wix Spaces clone — Pricing plan detail (ON-DEVICE, site-scoped).
|
|
2
2
|
// himalaya.input.id carries the plan id; fetched via getPlan.
|
|
3
|
-
import {
|
|
3
|
+
import { withIapPresentation } from "../_shared/iap-presentation.js";
|
|
4
4
|
import { getPlan, type WixPlan } from "@himalaya/wix-pricing-plans";
|
|
5
5
|
import { presence, presenceAfterError, detailInputId, type PresenceFlags } from "../_shared/detail-presence.js";
|
|
6
6
|
|
|
7
7
|
interface S extends PresenceFlags { status: "loading" | "ready"; name: string; price: string; description: string; perks: string }
|
|
8
8
|
|
|
9
9
|
function priceOf(p: WixPlan): string {
|
|
10
|
-
const m = p.pricing?.price as unknown as { formattedAmount?: string; amount?: string; currency?: string } | undefined;
|
|
10
|
+
const m = p.pricing?.price as unknown as { formattedAmount?: string; amount?: string; value?: string; currency?: string } | undefined;
|
|
11
11
|
if (!m) return p.pricing?.freeTrialDays ? "Free trial" : "";
|
|
12
|
-
return m.formattedAmount || [m.amount, m.currency].filter(Boolean).join(" ");
|
|
12
|
+
return m.formattedAmount || [m.amount ?? m.value, m.currency].filter(Boolean).join(" ");
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
const defineService = withIapPresentation<S>({
|
|
16
|
+
prefix: "plans", transientKeys: ["price"], contentActions: ["load"],
|
|
17
|
+
items: state => detailInputId() ? [{ reference: { kind: "pricingPlan", id: detailInputId() }, commerceDisplayPrice: state.price }] : [],
|
|
18
|
+
present: (state, price) => ({ ...state, price: price(detailInputId()) }),
|
|
19
|
+
});
|
|
15
20
|
const svc = defineService<S, { load: (s: S) => Promise<S> }>("spacesPlanDetail", {
|
|
16
21
|
state: { status: "loading", name: "", price: "", description: "", perks: "", ...presence("loading") },
|
|
17
22
|
actions: {
|
|
@@ -87,7 +87,7 @@ export const COLLECTIONS: Record<string, CollectionConfig> = {
|
|
|
87
87
|
himiBrandedOrders: { rows: 'rows', title: 'title', subtitle: 'subtitle', view: 'list', backendQuery: true,
|
|
88
88
|
searchPlaceholder: 'Search by order number', countNoun: ['order', 'orders'], filterValues: ['Not fulfilled', 'Partially fulfilled', 'Fulfilled'],
|
|
89
89
|
sortLabels: { default: 'Newest', az: 'Oldest', za: 'Order # ↓' } },
|
|
90
|
-
himiBrandedPlans: { rows: 'rows', title: 'name', subtitle: 'description', metadata: ['badge', 'priceLine', 'periodLine', 'trialBadge', 'perk1', 'perk2', 'perk3'], view: 'grid', gridColumns: 1, metaMaxLines: 8, countNoun: ['plan', 'plans'] },
|
|
90
|
+
himiBrandedPlans: { rows: 'rows', title: 'name', subtitle: 'description', metadata: ['badge', 'priceLine', 'periodLine', 'trialBadge', 'perk1', 'perk2', 'perk3', 'joinNote'], view: 'grid', gridColumns: 1, metaMaxLines: 8, countNoun: ['plan', 'plans'] },
|
|
91
91
|
himiBrandedPrograms: { rows: 'rows', title: 'name', subtitle: 'description', image: 'image', view: 'grid' },
|
|
92
92
|
himiBrandedCoupons: { rows: 'coupons', title: 'name', subtitle: 'subtitle', view: 'list', primaryVerb: { label: 'Get code' } },
|
|
93
93
|
himiBrandedNotifications: { rows: 'items', title: 'name', subtitle: 'subtitle', image: 'photo', metadata: ['unreadDot'], view: 'list' },
|
|
@@ -323,6 +323,7 @@ export function decorateCollection(name: string, def: { state: State; actions?:
|
|
|
323
323
|
/** Domain-owned page source with shared query controls. Publishing uses a normal
|
|
324
324
|
* reducer and reads the private controller snapshot, never an SDUI payload. */
|
|
325
325
|
export function defineBackendCollection<S extends object, Row extends object>(name: string, options: {
|
|
326
|
+
register?: typeof register;
|
|
326
327
|
/** Revalidate already opened pages on navigation return; explicit refresh resets. */
|
|
327
328
|
restorePagesOnLoad?: boolean;
|
|
328
329
|
/** The load action's argument (onAppear params beyond `copy`), before the query runs — for a
|
|
@@ -342,7 +343,7 @@ export function defineBackendCollection<S extends object, Row extends object>(na
|
|
|
342
343
|
controller: controller as unknown as BackendCollection['controller'],
|
|
343
344
|
apply: (state, page) => options.apply(state as S, page as unknown as CollectionQueryState<Row>) as State,
|
|
344
345
|
});
|
|
345
|
-
const handle = register(name, definition);
|
|
346
|
+
const handle = (options.register ?? register)(name, definition);
|
|
346
347
|
publish = () => handle.collectionDataChanged();
|
|
347
348
|
return handle;
|
|
348
349
|
}
|
|
@@ -350,7 +351,7 @@ export function defineBackendCollection<S extends object, Row extends object>(na
|
|
|
350
351
|
/** Keep the domain service's existing TypeScript API. Extra collection verbs are
|
|
351
352
|
* dispatched through SDUI and do not widen the native capability surface. */
|
|
352
353
|
export const defineService: typeof register = ((name: string, def: ServiceDefinition<unknown>) =>
|
|
353
|
-
register(name, decorateCollection(name, def as unknown as { state: State; actions?: Record<string, Reducer> }))) as typeof register;
|
|
354
|
+
register(name, { ...def, ...decorateCollection(name, def as unknown as { state: State; actions?: Record<string, Reducer> }) } as ServiceDefinition<State>)) as typeof register;
|
|
354
355
|
|
|
355
356
|
/** Domains can supply their existing saved-items store without putting domain
|
|
356
357
|
* data or mutations in the shared collection layer. */
|
|
@@ -87,4 +87,5 @@ export function decorateHome(def: { state: State; actions?: Record<string, Actio
|
|
|
87
87
|
actions.homeMoveDown = change(id => { const index = prefs.order.indexOf(id); if (index >= 0 && index < prefs.order.length - 1) [prefs.order[index], prefs.order[index + 1]] = [prefs.order[index + 1], prefs.order[index]]; });
|
|
88
88
|
return { state: derive(def.state), actions };
|
|
89
89
|
}
|
|
90
|
-
export const defineService: typeof register = ((name: string, def: ServiceDefinition<unknown>) =>
|
|
90
|
+
export const defineService: typeof register = ((name: string, def: ServiceDefinition<unknown>) =>
|
|
91
|
+
register(name, { ...def, ...decorateHome(def as unknown as { state: State; actions?: Record<string, Action> }) })) as typeof register;
|
|
@@ -37,7 +37,7 @@ export default defineApp({
|
|
|
37
37
|
actions: "http://localhost:8190/v1/actions",
|
|
38
38
|
sync: "http://localhost:8190/v1/sync",
|
|
39
39
|
},
|
|
40
|
-
capabilities: ["live_activity","push","share","secure_storage","open_url","calendar","local_database","wix_api","wix_platform","app_info","release_control","clipboard"],
|
|
40
|
+
capabilities: ["live_activity","push","share","secure_storage","open_url","calendar","local_database","wix_api","wix_platform","app_info","iap","release_control","clipboard"],
|
|
41
41
|
designTokensPath: "./tokens.json",
|
|
42
42
|
navigation: {
|
|
43
43
|
"navKind": "responsive",
|
|
@@ -210,13 +210,6 @@ export default defineApp({
|
|
|
210
210
|
"parentScreenId": "himi_groups",
|
|
211
211
|
"tabId": ""
|
|
212
212
|
},
|
|
213
|
-
{
|
|
214
|
-
"screenId": "himi_create_group",
|
|
215
|
-
"isPublic": false,
|
|
216
|
-
"paramKeys": [],
|
|
217
|
-
"parentScreenId": "himi_groups",
|
|
218
|
-
"tabId": ""
|
|
219
|
-
},
|
|
220
213
|
{
|
|
221
214
|
"screenId": "himi_account",
|
|
222
215
|
"isPublic": true,
|
|
@@ -2125,7 +2125,9 @@ const SCREENS = runtimeSetting({
|
|
|
2125
2125
|
"integrity": "",
|
|
2126
2126
|
"contentType": "application/javascript+ts-bundle",
|
|
2127
2127
|
"declaredCapabilities": [
|
|
2128
|
-
"wix_api"
|
|
2128
|
+
"wix_api",
|
|
2129
|
+
"iap",
|
|
2130
|
+
"app_info"
|
|
2129
2131
|
],
|
|
2130
2132
|
"minBinaryVersion": "0.2.0",
|
|
2131
2133
|
"bundleVersion": "0.0.1"
|
|
@@ -5616,7 +5618,8 @@ const SCREENS = runtimeSetting({
|
|
|
5616
5618
|
"package": "himalaya",
|
|
5617
5619
|
"type": "Button",
|
|
5618
5620
|
"props": {
|
|
5619
|
-
"title": "
|
|
5621
|
+
"title": "$item.joinLabel",
|
|
5622
|
+
"disabledPath": "$item.joinDisabled",
|
|
5620
5623
|
"style": "primary",
|
|
5621
5624
|
"fullWidth": true,
|
|
5622
5625
|
"action": {
|
|
@@ -5637,7 +5640,8 @@ const SCREENS = runtimeSetting({
|
|
|
5637
5640
|
"package": "himalaya",
|
|
5638
5641
|
"type": "Button",
|
|
5639
5642
|
"props": {
|
|
5640
|
-
"title": "
|
|
5643
|
+
"title": "$item.joinLabel",
|
|
5644
|
+
"disabledPath": "$item.joinDisabled",
|
|
5641
5645
|
"style": "secondary",
|
|
5642
5646
|
"fullWidth": true,
|
|
5643
5647
|
"action": {
|
|
@@ -5818,7 +5822,8 @@ const SCREENS = runtimeSetting({
|
|
|
5818
5822
|
"package": "himalaya",
|
|
5819
5823
|
"type": "Button",
|
|
5820
5824
|
"props": {
|
|
5821
|
-
"title": "
|
|
5825
|
+
"title": "$item.joinLabel",
|
|
5826
|
+
"disabledPath": "$item.joinDisabled",
|
|
5822
5827
|
"style": "primary",
|
|
5823
5828
|
"fullWidth": true,
|
|
5824
5829
|
"action": {
|
|
@@ -5839,7 +5844,8 @@ const SCREENS = runtimeSetting({
|
|
|
5839
5844
|
"package": "himalaya",
|
|
5840
5845
|
"type": "Button",
|
|
5841
5846
|
"props": {
|
|
5842
|
-
"title": "
|
|
5847
|
+
"title": "$item.joinLabel",
|
|
5848
|
+
"disabledPath": "$item.joinDisabled",
|
|
5843
5849
|
"style": "secondary",
|
|
5844
5850
|
"fullWidth": true,
|
|
5845
5851
|
"action": {
|
|
@@ -6244,7 +6250,8 @@ const SCREENS = runtimeSetting({
|
|
|
6244
6250
|
"package": "himalaya",
|
|
6245
6251
|
"type": "Button",
|
|
6246
6252
|
"props": {
|
|
6247
|
-
"title": "
|
|
6253
|
+
"title": "$item.joinLabel",
|
|
6254
|
+
"disabledPath": "$item.joinDisabled",
|
|
6248
6255
|
"style": "primary",
|
|
6249
6256
|
"fullWidth": true,
|
|
6250
6257
|
"action": {
|
|
@@ -6265,7 +6272,8 @@ const SCREENS = runtimeSetting({
|
|
|
6265
6272
|
"package": "himalaya",
|
|
6266
6273
|
"type": "Button",
|
|
6267
6274
|
"props": {
|
|
6268
|
-
"title": "
|
|
6275
|
+
"title": "$item.joinLabel",
|
|
6276
|
+
"disabledPath": "$item.joinDisabled",
|
|
6269
6277
|
"style": "secondary",
|
|
6270
6278
|
"fullWidth": true,
|
|
6271
6279
|
"action": {
|
|
@@ -6446,7 +6454,8 @@ const SCREENS = runtimeSetting({
|
|
|
6446
6454
|
"package": "himalaya",
|
|
6447
6455
|
"type": "Button",
|
|
6448
6456
|
"props": {
|
|
6449
|
-
"title": "
|
|
6457
|
+
"title": "$item.joinLabel",
|
|
6458
|
+
"disabledPath": "$item.joinDisabled",
|
|
6450
6459
|
"style": "primary",
|
|
6451
6460
|
"fullWidth": true,
|
|
6452
6461
|
"action": {
|
|
@@ -6467,7 +6476,8 @@ const SCREENS = runtimeSetting({
|
|
|
6467
6476
|
"package": "himalaya",
|
|
6468
6477
|
"type": "Button",
|
|
6469
6478
|
"props": {
|
|
6470
|
-
"title": "
|
|
6479
|
+
"title": "$item.joinLabel",
|
|
6480
|
+
"disabledPath": "$item.joinDisabled",
|
|
6471
6481
|
"style": "secondary",
|
|
6472
6482
|
"fullWidth": true,
|
|
6473
6483
|
"action": {
|
|
@@ -7045,7 +7055,8 @@ const SCREENS = runtimeSetting({
|
|
|
7045
7055
|
"package": "himalaya",
|
|
7046
7056
|
"type": "Button",
|
|
7047
7057
|
"props": {
|
|
7048
|
-
"title": "
|
|
7058
|
+
"title": "$item.joinLabel",
|
|
7059
|
+
"disabledPath": "$item.joinDisabled",
|
|
7049
7060
|
"style": "primary",
|
|
7050
7061
|
"fullWidth": true,
|
|
7051
7062
|
"action": {
|
|
@@ -7066,7 +7077,8 @@ const SCREENS = runtimeSetting({
|
|
|
7066
7077
|
"package": "himalaya",
|
|
7067
7078
|
"type": "Button",
|
|
7068
7079
|
"props": {
|
|
7069
|
-
"title": "
|
|
7080
|
+
"title": "$item.joinLabel",
|
|
7081
|
+
"disabledPath": "$item.joinDisabled",
|
|
7070
7082
|
"style": "secondary",
|
|
7071
7083
|
"fullWidth": true,
|
|
7072
7084
|
"action": {
|
|
@@ -7344,7 +7356,9 @@ const SCREENS = runtimeSetting({
|
|
|
7344
7356
|
"wix_api",
|
|
7345
7357
|
"wix_cashier",
|
|
7346
7358
|
"open_url",
|
|
7347
|
-
"wix_platform"
|
|
7359
|
+
"wix_platform",
|
|
7360
|
+
"iap",
|
|
7361
|
+
"app_info"
|
|
7348
7362
|
],
|
|
7349
7363
|
"minBinaryVersion": "0.1.0",
|
|
7350
7364
|
"bundleVersion": "0.0.1"
|
|
@@ -7600,7 +7614,9 @@ const SCREENS = runtimeSetting({
|
|
|
7600
7614
|
"integrity": "",
|
|
7601
7615
|
"contentType": "application/javascript+ts-bundle",
|
|
7602
7616
|
"declaredCapabilities": [
|
|
7603
|
-
"wix_api"
|
|
7617
|
+
"wix_api",
|
|
7618
|
+
"iap",
|
|
7619
|
+
"app_info"
|
|
7604
7620
|
],
|
|
7605
7621
|
"minBinaryVersion": "0.1.0",
|
|
7606
7622
|
"bundleVersion": "0.0.1"
|
|
@@ -15799,25 +15815,6 @@ const SCREENS = runtimeSetting({
|
|
|
15799
15815
|
}
|
|
15800
15816
|
}
|
|
15801
15817
|
},
|
|
15802
|
-
{
|
|
15803
|
-
"id": "himi_groups.new_group",
|
|
15804
|
-
"body": {
|
|
15805
|
-
"package": "himalaya",
|
|
15806
|
-
"type": "Button",
|
|
15807
|
-
"props": {
|
|
15808
|
-
"title": "New Group",
|
|
15809
|
-
"style": "secondary",
|
|
15810
|
-
"fullWidth": true,
|
|
15811
|
-
"leadingIcon": "plus",
|
|
15812
|
-
"action": {
|
|
15813
|
-
"actionId": "himi.open",
|
|
15814
|
-
"params": {
|
|
15815
|
-
"screenId": "himi_create_group"
|
|
15816
|
-
}
|
|
15817
|
-
}
|
|
15818
|
-
}
|
|
15819
|
-
}
|
|
15820
|
-
},
|
|
15821
15818
|
{
|
|
15822
15819
|
"id": "himi_groups.search",
|
|
15823
15820
|
"body": {
|
|
@@ -18198,103 +18195,6 @@ const SCREENS = runtimeSetting({
|
|
|
18198
18195
|
"bundleVersion": "0.0.1"
|
|
18199
18196
|
}
|
|
18200
18197
|
},
|
|
18201
|
-
"himi_create_group": {
|
|
18202
|
-
"screenId": "himi_create_group",
|
|
18203
|
-
"renderer": "sdui_intent",
|
|
18204
|
-
"title": "",
|
|
18205
|
-
"schemaVersion": 1,
|
|
18206
|
-
"cacheControl": {
|
|
18207
|
-
"maxAge": 0,
|
|
18208
|
-
"staleWhileRevalidate": 0
|
|
18209
|
-
},
|
|
18210
|
-
"payload": {
|
|
18211
|
-
"id": "himi_create_group.root",
|
|
18212
|
-
"body": {
|
|
18213
|
-
"package": "himalaya",
|
|
18214
|
-
"type": "VStack",
|
|
18215
|
-
"props": {
|
|
18216
|
-
"spacing": 16,
|
|
18217
|
-
"alignment": "leading",
|
|
18218
|
-
"children": [
|
|
18219
|
-
{
|
|
18220
|
-
"id": "himi_create_group.title",
|
|
18221
|
-
"body": {
|
|
18222
|
-
"package": "himalaya",
|
|
18223
|
-
"type": "Text",
|
|
18224
|
-
"props": {
|
|
18225
|
-
"text": "New Group",
|
|
18226
|
-
"style": "title1"
|
|
18227
|
-
}
|
|
18228
|
-
}
|
|
18229
|
-
},
|
|
18230
|
-
{
|
|
18231
|
-
"id": "",
|
|
18232
|
-
"body": {
|
|
18233
|
-
"package": "himalaya",
|
|
18234
|
-
"type": "Text",
|
|
18235
|
-
"props": {
|
|
18236
|
-
"text": "$service:spacesCreateGroup.hint",
|
|
18237
|
-
"style": "footnote",
|
|
18238
|
-
"color": "textSecondary",
|
|
18239
|
-
"collapseWhenEmpty": true
|
|
18240
|
-
}
|
|
18241
|
-
}
|
|
18242
|
-
},
|
|
18243
|
-
{
|
|
18244
|
-
"id": "himi_create_group.name_field",
|
|
18245
|
-
"body": {
|
|
18246
|
-
"package": "himalaya",
|
|
18247
|
-
"type": "TextField",
|
|
18248
|
-
"props": {
|
|
18249
|
-
"placeholder": "Group name",
|
|
18250
|
-
"value": "",
|
|
18251
|
-
"onChange": {
|
|
18252
|
-
"actionId": "tier5.spacesCreateGroup.setName"
|
|
18253
|
-
}
|
|
18254
|
-
}
|
|
18255
|
-
}
|
|
18256
|
-
},
|
|
18257
|
-
{
|
|
18258
|
-
"id": "himi_create_group.submit",
|
|
18259
|
-
"body": {
|
|
18260
|
-
"package": "himalaya",
|
|
18261
|
-
"type": "Button",
|
|
18262
|
-
"props": {
|
|
18263
|
-
"title": "Create",
|
|
18264
|
-
"style": "primary",
|
|
18265
|
-
"fullWidth": true,
|
|
18266
|
-
"action": {
|
|
18267
|
-
"actionId": "tier5.spacesCreateGroup.submit"
|
|
18268
|
-
}
|
|
18269
|
-
}
|
|
18270
|
-
}
|
|
18271
|
-
}
|
|
18272
|
-
]
|
|
18273
|
-
}
|
|
18274
|
-
},
|
|
18275
|
-
"modifiers": {
|
|
18276
|
-
"background": {
|
|
18277
|
-
"token": "surfacePrimary"
|
|
18278
|
-
},
|
|
18279
|
-
"padding": {
|
|
18280
|
-
"leading": 16,
|
|
18281
|
-
"trailing": 16,
|
|
18282
|
-
"top": 18,
|
|
18283
|
-
"bottom": 28
|
|
18284
|
-
}
|
|
18285
|
-
}
|
|
18286
|
-
},
|
|
18287
|
-
"logicBundle": {
|
|
18288
|
-
"url": "http://localhost:8190/v1/bundles/wix-spaces-create-group?integrity=",
|
|
18289
|
-
"integrity": "",
|
|
18290
|
-
"contentType": "application/javascript+ts-bundle",
|
|
18291
|
-
"declaredCapabilities": [
|
|
18292
|
-
"wix_api"
|
|
18293
|
-
],
|
|
18294
|
-
"minBinaryVersion": "0.1.0",
|
|
18295
|
-
"bundleVersion": "0.0.1"
|
|
18296
|
-
}
|
|
18297
|
-
},
|
|
18298
18198
|
}) as Record<string, unknown>;
|
|
18299
18199
|
|
|
18300
18200
|
// The language destination stays registered for deep links and future translations. Keep its
|
|
@@ -87,7 +87,7 @@ export const COLLECTIONS: Record<string, CollectionConfig> = {
|
|
|
87
87
|
himiBrandedOrders: { rows: 'rows', title: 'title', subtitle: 'subtitle', view: 'list', backendQuery: true,
|
|
88
88
|
searchPlaceholder: 'Search by order number', countNoun: ['order', 'orders'], filterValues: ['Not fulfilled', 'Partially fulfilled', 'Fulfilled'],
|
|
89
89
|
sortLabels: { default: 'Newest', az: 'Oldest', za: 'Order # ↓' } },
|
|
90
|
-
himiBrandedPlans: { rows: 'rows', title: 'name', subtitle: 'description', metadata: ['badge', 'priceLine', 'periodLine', 'trialBadge', 'perk1', 'perk2', 'perk3'], view: 'grid', gridColumns: 1, metaMaxLines: 8, countNoun: ['plan', 'plans'] },
|
|
90
|
+
himiBrandedPlans: { rows: 'rows', title: 'name', subtitle: 'description', metadata: ['badge', 'priceLine', 'periodLine', 'trialBadge', 'perk1', 'perk2', 'perk3', 'joinNote'], view: 'grid', gridColumns: 1, metaMaxLines: 8, countNoun: ['plan', 'plans'] },
|
|
91
91
|
himiBrandedPrograms: { rows: 'rows', title: 'name', subtitle: 'description', image: 'image', view: 'grid' },
|
|
92
92
|
himiBrandedCoupons: { rows: 'coupons', title: 'name', subtitle: 'subtitle', view: 'list', primaryVerb: { label: 'Get code' } },
|
|
93
93
|
himiBrandedNotifications: { rows: 'items', title: 'name', subtitle: 'subtitle', image: 'photo', metadata: ['unreadDot'], view: 'list' },
|
|
@@ -323,6 +323,7 @@ export function decorateCollection(name: string, def: { state: State; actions?:
|
|
|
323
323
|
/** Domain-owned page source with shared query controls. Publishing uses a normal
|
|
324
324
|
* reducer and reads the private controller snapshot, never an SDUI payload. */
|
|
325
325
|
export function defineBackendCollection<S extends object, Row extends object>(name: string, options: {
|
|
326
|
+
register?: typeof register;
|
|
326
327
|
/** Revalidate already opened pages on navigation return; explicit refresh resets. */
|
|
327
328
|
restorePagesOnLoad?: boolean;
|
|
328
329
|
/** The load action's argument (onAppear params beyond `copy`), before the query runs — for a
|
|
@@ -342,7 +343,7 @@ export function defineBackendCollection<S extends object, Row extends object>(na
|
|
|
342
343
|
controller: controller as unknown as BackendCollection['controller'],
|
|
343
344
|
apply: (state, page) => options.apply(state as S, page as unknown as CollectionQueryState<Row>) as State,
|
|
344
345
|
});
|
|
345
|
-
const handle = register(name, definition);
|
|
346
|
+
const handle = (options.register ?? register)(name, definition);
|
|
346
347
|
publish = () => handle.collectionDataChanged();
|
|
347
348
|
return handle;
|
|
348
349
|
}
|
|
@@ -350,7 +351,7 @@ export function defineBackendCollection<S extends object, Row extends object>(na
|
|
|
350
351
|
/** Keep the domain service's existing TypeScript API. Extra collection verbs are
|
|
351
352
|
* dispatched through SDUI and do not widen the native capability surface. */
|
|
352
353
|
export const defineService: typeof register = ((name: string, def: ServiceDefinition<unknown>) =>
|
|
353
|
-
register(name, decorateCollection(name, def as unknown as { state: State; actions?: Record<string, Reducer> }))) as typeof register;
|
|
354
|
+
register(name, { ...def, ...decorateCollection(name, def as unknown as { state: State; actions?: Record<string, Reducer> }) } as ServiceDefinition<State>)) as typeof register;
|
|
354
355
|
|
|
355
356
|
/** Domains can supply their existing saved-items store without putting domain
|
|
356
357
|
* data or mutations in the shared collection layer. */
|
|
@@ -87,4 +87,5 @@ export function decorateHome(def: { state: State; actions?: Record<string, Actio
|
|
|
87
87
|
actions.homeMoveDown = change(id => { const index = prefs.order.indexOf(id); if (index >= 0 && index < prefs.order.length - 1) [prefs.order[index], prefs.order[index + 1]] = [prefs.order[index + 1], prefs.order[index]]; });
|
|
88
88
|
return { state: derive(def.state), actions };
|
|
89
89
|
}
|
|
90
|
-
export const defineService: typeof register = ((name: string, def: ServiceDefinition<unknown>) =>
|
|
90
|
+
export const defineService: typeof register = ((name: string, def: ServiceDefinition<unknown>) =>
|
|
91
|
+
register(name, { ...def, ...decorateHome(def as unknown as { state: State; actions?: Record<string, Action> }) })) as typeof register;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { defineService, type ServiceDefinition } from "@himalaya/state";
|
|
2
|
+
import { createIapCommerce, type IapCommerce } from "@himalaya/iap-checkout";
|
|
3
|
+
import { iapPriceFields, type IapCatalogItem } from "@himalaya/iap";
|
|
4
|
+
import { iapPriceCopy, resetUnhydratedKeysOnOlderRuntime } from "./iap-worker.js";
|
|
5
|
+
|
|
6
|
+
// Presentation only. Checkout, mapping, cache, session and refresh policy stay in the shared flow.
|
|
7
|
+
export function withIapPresentation<S extends object>(options: {
|
|
8
|
+
register?: typeof defineService;
|
|
9
|
+
onCreate?(commerce: IapCommerce): void;
|
|
10
|
+
onContent?(state: S): void;
|
|
11
|
+
items(state: S): IapCatalogItem[];
|
|
12
|
+
present(state: S, price: (id: string, commercePrice?: string) => string, allowed: (id: string) => boolean): S;
|
|
13
|
+
contentActions: string[];
|
|
14
|
+
transientKeys: (keyof S & string)[];
|
|
15
|
+
prefix: string;
|
|
16
|
+
}): typeof defineService {
|
|
17
|
+
return ((name: string, definition: ServiceDefinition<S>) => {
|
|
18
|
+
const commerce = createIapCommerce({ onChange: () => { void service.iapPublish(); } });
|
|
19
|
+
options.onCreate?.(commerce);
|
|
20
|
+
let items: IapCatalogItem[] = [];
|
|
21
|
+
let contentVersion = 0;
|
|
22
|
+
const project = (state: S): S => {
|
|
23
|
+
const price = (id: string, commercePrice?: string) => {
|
|
24
|
+
const item = items.find(item => item.reference.id === id);
|
|
25
|
+
if (!item) return "";
|
|
26
|
+
const view = commerce.pricing.view(item.reference);
|
|
27
|
+
return view.route === "disabled" || view.route === "regular" ? commercePrice ?? item.commerceDisplayPrice ?? "" : iapPriceFields(view, iapPriceCopy(options.prefix)).priceText;
|
|
28
|
+
};
|
|
29
|
+
const allowed = (id: string) => commerce.pricing.checkoutGuard({ kind: "storesProduct", id }).allowed;
|
|
30
|
+
const poll = commerce.pollDirective(`${name}.iapPoll`);
|
|
31
|
+
return { ...options.present(state, price, allowed), ...(poll ? { _poll: poll } : {}) };
|
|
32
|
+
};
|
|
33
|
+
const actions = Object.fromEntries(Object.entries(definition.actions ?? {}).map(([action, reduce]) => [action, async (state: S, ...args: never[]) => {
|
|
34
|
+
const content = options.contentActions.includes(action);
|
|
35
|
+
const version = action === "load" ? ++contentVersion : contentVersion;
|
|
36
|
+
const next = await reduce(state, ...args);
|
|
37
|
+
if (!next || (content && version !== contentVersion)) return undefined;
|
|
38
|
+
if (content) {
|
|
39
|
+
options.onContent?.(next);
|
|
40
|
+
items = options.items(next);
|
|
41
|
+
void commerce.loadPrices(items);
|
|
42
|
+
}
|
|
43
|
+
return project(next);
|
|
44
|
+
}]));
|
|
45
|
+
const service = (options.register ?? defineService)(name, {
|
|
46
|
+
...definition, notHydrated: [...(definition.notHydrated ?? []), ...options.transientKeys],
|
|
47
|
+
actions: { ...actions, iapPublish: project, iapPoll: async () => { await commerce.poll(); return undefined; },
|
|
48
|
+
iapRefresh: async () => { await commerce.loadPrices(items); return undefined; } },
|
|
49
|
+
});
|
|
50
|
+
resetUnhydratedKeysOnOlderRuntime(service, definition.state, options.transientKeys);
|
|
51
|
+
return service;
|
|
52
|
+
}) as unknown as typeof defineService;
|
|
53
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Worker-side glue for the shared IAP layer (`@himalaya/iap` catalog pricing and
|
|
2
|
+
// `@himalaya/iap-checkout` → createIapCommerce), shared by every screen that shows or sells an
|
|
3
|
+
// IAP-backed item. Mapping, Apple prices, payment guards, the host scope and the sale live in the
|
|
4
|
+
// SDK; this only supplies the copy and the hydration reset.
|
|
5
|
+
import type { IapPriceCopy } from "@himalaya/iap";
|
|
6
|
+
import type { IapPurchaseCopy } from "@himalaya/iap-checkout";
|
|
7
|
+
import { copyParam } from "./himi-copy.js";
|
|
8
|
+
|
|
9
|
+
/** Price labels from the screen's copy pack (`<prefix>.priceLoadingText`, …). */
|
|
10
|
+
export function iapPriceCopy(prefix: string): IapPriceCopy {
|
|
11
|
+
return {
|
|
12
|
+
loading: copyParam(`${prefix}.priceLoadingText`, "Loading price…"),
|
|
13
|
+
unavailable: copyParam(`${prefix}.priceUnavailableText`, "Price unavailable"),
|
|
14
|
+
oneTimePurchase: copyParam(`${prefix}.oneTimePurchaseLabel`, "One-time purchase"),
|
|
15
|
+
purchaseUnavailable: copyParam(`${prefix}.purchaseUnavailableText`, "Purchasing is not available yet."),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** App Store purchase labels from the screen's copy pack (`<prefix>.buyCta`, …), for
|
|
20
|
+
* `iapPurchaseFields`. Customer copy only: no backend names, ids or diagnostics. */
|
|
21
|
+
export function iapPurchaseCopy(prefix: string): IapPurchaseCopy {
|
|
22
|
+
return {
|
|
23
|
+
buy: copyParam(`${prefix}.buyCta`, "Get access"),
|
|
24
|
+
processing: copyParam(`${prefix}.purchaseProcessingText`, "Processing your purchase…"),
|
|
25
|
+
awaitingApproval: copyParam(`${prefix}.purchaseAwaitingApprovalText`, "Waiting for approval"),
|
|
26
|
+
unavailable: copyParam(`${prefix}.purchaseTemporarilyUnavailableText`, "Purchasing is temporarily unavailable."),
|
|
27
|
+
signInRequired: copyParam(`${prefix}.purchaseSignInText`, "Sign in to buy."),
|
|
28
|
+
retry: copyParam(`${prefix}.purchaseRetryText`, "Something went wrong. Try again."),
|
|
29
|
+
priceChanged: copyParam(`${prefix}.purchasePriceChangedText`, "The price changed. Check it and try again."),
|
|
30
|
+
support: copyParam(`${prefix}.purchaseSupportText`, "We couldn't finish this purchase. Contact support."),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** What `notHydrated` does, for a runtime that ignores the option (a binary older than it, still
|
|
35
|
+
* served new bundles): put the listed keys back to their declared values before any action runs,
|
|
36
|
+
* so a persisted price or an enabled purchase action never paints as this run's. */
|
|
37
|
+
export function resetUnhydratedKeysOnOlderRuntime<S extends object>(svc: { getState(): S }, declared: S, keys: readonly (keyof S & string)[]): void {
|
|
38
|
+
if ((globalThis as { himalaya?: { hydrationOptOut?: unknown } }).himalaya?.hydrationOptOut === true) return;
|
|
39
|
+
const booted = svc.getState();
|
|
40
|
+
if (!booted || typeof booted !== "object") return;
|
|
41
|
+
for (const key of keys) booted[key] = declared[key];
|
|
42
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { fetchMemberBookings, type MemberBookingRow } from "../_shared/member-bookings.js";
|
|
2
2
|
import { memberAccess, assertMemberAccess } from "../_shared/member-access.js";
|
|
3
|
-
import { defineService } from "../_shared/home-service.js";
|
|
3
|
+
import { defineService as registerHome } from "../_shared/home-service.js";
|
|
4
|
+
import { withIapPresentation } from "../_shared/iap-presentation.js";
|
|
4
5
|
import { queryServices, type WixService } from "@himalaya/wix-bookings";
|
|
5
6
|
import { getPost, queryPosts, type WixPost } from "@himalaya/wix-blog";
|
|
6
7
|
import { queryGroups, type WixGroup } from "@himalaya/wix-groups";
|
|
@@ -68,6 +69,7 @@ interface ProductFeature {
|
|
|
68
69
|
/** Device-local catalog saves are useful to visitors too; they are never
|
|
69
70
|
* presented as account-synced data or mixed with member-only bookings. */
|
|
70
71
|
interface SavedProductCard {
|
|
72
|
+
commercePrice: string;
|
|
71
73
|
id: string;
|
|
72
74
|
name: string;
|
|
73
75
|
price: string;
|
|
@@ -573,7 +575,7 @@ function postCard(post: WixPost): PostCard {
|
|
|
573
575
|
function savedProductCard(product: SavedProduct): SavedProductCard {
|
|
574
576
|
const name = cleanTitle(product.name) || copyParam("home.savedProductFallbackName", "Saved item");
|
|
575
577
|
const image = product.image ? wixMediaUrl(product.image) : "";
|
|
576
|
-
return { id: product.id, name, price: product.price, image, initial: initials(name), hasImage: !!image, noImage: !image, detailScreenId: "himi_product" };
|
|
578
|
+
return { id: product.id, name, price: product.price, commercePrice: product.price, image, initial: initials(name), hasImage: !!image, noImage: !image, detailScreenId: "himi_product" };
|
|
577
579
|
}
|
|
578
580
|
|
|
579
581
|
function savedPostCard(post: WixPost): SavedPostCard {
|
|
@@ -1111,6 +1113,18 @@ async function refresh(state: State, arg?: unknown): Promise<State | undefined>
|
|
|
1111
1113
|
weeklyClassCount: current.weeklyClassCount, weeklyClassGoalFraction: current.weeklyClassGoalFraction, weeklyGoalLabel: current.weeklyGoalLabel });
|
|
1112
1114
|
}
|
|
1113
1115
|
|
|
1116
|
+
const defineService = withIapPresentation<State>({
|
|
1117
|
+
register: registerHome, prefix: "home", transientKeys: ["featuredProducts", "savedProducts", "heroPrice"], contentActions: ["refresh"],
|
|
1118
|
+
items: state => [
|
|
1119
|
+
...state.featuredProducts.map(row => ({ reference: { kind: "storesProduct", id: row.id }, commerceDisplayPrice: row.price })),
|
|
1120
|
+
...state.savedProducts.map(row => ({ reference: { kind: "storesProduct", id: row.id }, commerceDisplayPrice: row.commercePrice })),
|
|
1121
|
+
],
|
|
1122
|
+
present: (state, price) => derive({ ...state,
|
|
1123
|
+
featuredProducts: state.featuredProducts.map(row => ({ ...row, price: price(row.id) })),
|
|
1124
|
+
savedProducts: state.savedProducts.map(row => ({ ...row, price: price(row.id, row.commercePrice) })),
|
|
1125
|
+
}),
|
|
1126
|
+
});
|
|
1127
|
+
|
|
1114
1128
|
const svc = defineService<State, {
|
|
1115
1129
|
// `State | undefined`, matching the implementation: `refresh` returns undefined on a
|
|
1116
1130
|
// STALE generation (`if (gen !== refreshGen) return undefined`) so the runtime keeps
|