@wix/himalaya-cli 0.1378.0 → 0.1379.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/sdk/app-info/src/index.d.ts +4 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/ecom/src/extensions.d.ts +113 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/app-info.mjs +2 -1
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/iap-checkout.mjs +2 -1
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/screens.mjs +2 -1
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-ecom.mjs +77 -0
- package/dist/cli.mjs +23 -23
- package/dist/preview-assets.generated.bin +0 -0
- package/guides/importable-sdks.md +1 -1
- package/guides/sdk-reference.json +128 -2
- package/guides/wix-api-usage.md +2 -0
- package/package.json +2 -2
- package/templates/beauty-salon/config.ts +23 -0
- package/templates/beauty-salon/dev/index.ts +2611 -1
- package/templates/beauty-salon/himalaya.content.json +9 -0
- package/templates/beauty-salon/tier5-src/_shared/branded-lite-hosted.ts +3 -0
- package/templates/beauty-salon/tier5-src/_shared/member-access.ts +14 -0
- package/templates/beauty-salon/tier5-src/_shared/member-account-routes.ts +43 -0
- package/templates/beauty-salon/tier5-src/_shared/member-address.ts +129 -0
- package/templates/beauty-salon/tier5-src/_shared/saved-card.ts +20 -0
- package/templates/beauty-salon/tier5-src/himi-branded-lite-address/index.ts +223 -0
- package/templates/beauty-salon/tier5-src/himi-branded-lite-addresses/index.ts +202 -0
- package/templates/beauty-salon/tier5-src/himi-branded-lite-wallet/index.ts +217 -0
- package/templates/beauty-salon/tier5-src/himi-store-account/index.ts +17 -0
- package/templates/beauty-salon-owner/dev/index.ts +5 -27
- package/templates/beauty-salon-owner/tier5-src/_shared/aria-chat.ts +99 -40
- package/templates/beauty-salon-owner/tier5-src/_shared/aria-widget-oauth.ts +42 -0
- package/templates/beauty-salon-owner/tier5-src/_shared/aria-widget-view.ts +6 -0
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-aria/index.ts +12 -1
- package/templates/branded-lite/config.ts +23 -0
- package/templates/branded-lite/dev/index.ts +2614 -2
- package/templates/branded-lite/himalaya.content.json +9 -0
- package/templates/branded-lite/tier5-src/_shared/branded-lite-hosted.ts +3 -0
- package/templates/branded-lite/tier5-src/_shared/member-access.ts +14 -0
- package/templates/branded-lite/tier5-src/_shared/member-account-routes.ts +43 -0
- package/templates/branded-lite/tier5-src/_shared/member-address.ts +129 -0
- package/templates/branded-lite/tier5-src/_shared/saved-card.ts +20 -0
- package/templates/branded-lite/tier5-src/himi-branded-lite-address/index.ts +223 -0
- package/templates/branded-lite/tier5-src/himi-branded-lite-addresses/index.ts +202 -0
- package/templates/branded-lite/tier5-src/himi-branded-lite-wallet/index.ts +217 -0
- package/templates/branded-lite/tier5-src/himi-store-account/index.ts +17 -0
- package/templates/community-club/dev/index.ts +3 -1
- package/templates/community-club/tier5-src/_shared/branded-lite-hosted.ts +3 -0
- package/templates/community-club/tier5-src/_shared/member-access.ts +14 -0
- package/templates/community-club/tier5-src/_shared/member-account-routes.ts +43 -0
- package/templates/community-club/tier5-src/himi-store-account/index.ts +17 -0
- package/templates/community-club-owner/dev/index.ts +5 -27
- package/templates/community-club-owner/tier5-src/_shared/aria-chat.ts +99 -40
- package/templates/community-club-owner/tier5-src/_shared/aria-widget-oauth.ts +42 -0
- package/templates/community-club-owner/tier5-src/_shared/aria-widget-view.ts +6 -0
- package/templates/community-club-owner/tier5-src/himi-owner-lite-aria/index.ts +12 -1
- package/templates/events-meetups/dev/index.ts +3 -1
- package/templates/events-meetups/tier5-src/_shared/branded-lite-hosted.ts +3 -0
- package/templates/events-meetups/tier5-src/_shared/member-access.ts +14 -0
- package/templates/events-meetups/tier5-src/_shared/member-account-routes.ts +43 -0
- package/templates/events-meetups/tier5-src/himi-store-account/index.ts +17 -0
- package/templates/events-meetups-owner/dev/index.ts +5 -27
- package/templates/events-meetups-owner/tier5-src/_shared/aria-chat.ts +99 -40
- package/templates/events-meetups-owner/tier5-src/_shared/aria-widget-oauth.ts +42 -0
- package/templates/events-meetups-owner/tier5-src/_shared/aria-widget-view.ts +6 -0
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-aria/index.ts +12 -1
- package/templates/gym-fitness/config.ts +23 -0
- package/templates/gym-fitness/dev/index.ts +2611 -1
- package/templates/gym-fitness/himalaya.content.json +9 -0
- package/templates/gym-fitness/tier5-src/_shared/branded-lite-hosted.ts +3 -0
- package/templates/gym-fitness/tier5-src/_shared/member-access.ts +14 -0
- package/templates/gym-fitness/tier5-src/_shared/member-account-routes.ts +43 -0
- package/templates/gym-fitness/tier5-src/_shared/member-address.ts +129 -0
- package/templates/gym-fitness/tier5-src/_shared/saved-card.ts +20 -0
- package/templates/gym-fitness/tier5-src/himi-branded-lite-address/index.ts +223 -0
- package/templates/gym-fitness/tier5-src/himi-branded-lite-addresses/index.ts +202 -0
- package/templates/gym-fitness/tier5-src/himi-branded-lite-wallet/index.ts +217 -0
- package/templates/gym-fitness/tier5-src/himi-store-account/index.ts +17 -0
- package/templates/gym-fitness-owner/dev/index.ts +5 -27
- package/templates/gym-fitness-owner/tier5-src/_shared/aria-chat.ts +99 -40
- package/templates/gym-fitness-owner/tier5-src/_shared/aria-widget-oauth.ts +42 -0
- package/templates/gym-fitness-owner/tier5-src/_shared/aria-widget-view.ts +6 -0
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-aria/index.ts +12 -1
- package/templates/nonprofit-cause/config.ts +7 -0
- package/templates/nonprofit-cause/dev/index.ts +762 -1
- package/templates/nonprofit-cause/himalaya.content.json +3 -0
- package/templates/nonprofit-cause/tier5-src/_shared/branded-lite-hosted.ts +3 -0
- package/templates/nonprofit-cause/tier5-src/_shared/member-access.ts +14 -0
- package/templates/nonprofit-cause/tier5-src/_shared/member-account-routes.ts +43 -0
- package/templates/nonprofit-cause/tier5-src/_shared/saved-card.ts +20 -0
- package/templates/nonprofit-cause/tier5-src/himi-branded-lite-wallet/index.ts +217 -0
- package/templates/nonprofit-cause/tier5-src/himi-store-account/index.ts +17 -0
- package/templates/nonprofit-cause-owner/dev/index.ts +5 -27
- package/templates/nonprofit-cause-owner/tier5-src/_shared/aria-chat.ts +99 -40
- package/templates/nonprofit-cause-owner/tier5-src/_shared/aria-widget-oauth.ts +42 -0
- package/templates/nonprofit-cause-owner/tier5-src/_shared/aria-widget-view.ts +6 -0
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-aria/index.ts +12 -1
- package/templates/owner-lite/dev/index.ts +5 -27
- package/templates/owner-lite/tier5-src/_shared/aria-chat.ts +99 -40
- package/templates/owner-lite/tier5-src/_shared/aria-widget-oauth.ts +42 -0
- package/templates/owner-lite/tier5-src/_shared/aria-widget-view.ts +6 -0
- package/templates/owner-lite/tier5-src/himi-owner-lite-aria/index.ts +12 -1
- package/templates/podcast-creator/config.ts +7 -0
- package/templates/podcast-creator/dev/index.ts +762 -1
- package/templates/podcast-creator/himalaya.content.json +3 -0
- package/templates/podcast-creator/tier5-src/_shared/branded-lite-hosted.ts +3 -0
- package/templates/podcast-creator/tier5-src/_shared/member-access.ts +14 -0
- package/templates/podcast-creator/tier5-src/_shared/member-account-routes.ts +43 -0
- package/templates/podcast-creator/tier5-src/_shared/saved-card.ts +20 -0
- package/templates/podcast-creator/tier5-src/himi-branded-lite-wallet/index.ts +217 -0
- package/templates/podcast-creator/tier5-src/himi-store-account/index.ts +17 -0
- package/templates/podcast-creator-owner/dev/index.ts +5 -27
- package/templates/podcast-creator-owner/tier5-src/_shared/aria-chat.ts +99 -40
- package/templates/podcast-creator-owner/tier5-src/_shared/aria-widget-oauth.ts +42 -0
- package/templates/podcast-creator-owner/tier5-src/_shared/aria-widget-view.ts +6 -0
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-aria/index.ts +12 -1
- package/templates/portfolio-creative/config.ts +23 -0
- package/templates/portfolio-creative/dev/index.ts +2611 -1
- package/templates/portfolio-creative/himalaya.content.json +9 -0
- package/templates/portfolio-creative/tier5-src/_shared/branded-lite-hosted.ts +3 -0
- package/templates/portfolio-creative/tier5-src/_shared/member-access.ts +14 -0
- package/templates/portfolio-creative/tier5-src/_shared/member-account-routes.ts +43 -0
- package/templates/portfolio-creative/tier5-src/_shared/member-address.ts +129 -0
- package/templates/portfolio-creative/tier5-src/_shared/saved-card.ts +20 -0
- package/templates/portfolio-creative/tier5-src/himi-branded-lite-address/index.ts +223 -0
- package/templates/portfolio-creative/tier5-src/himi-branded-lite-addresses/index.ts +202 -0
- package/templates/portfolio-creative/tier5-src/himi-branded-lite-wallet/index.ts +217 -0
- package/templates/portfolio-creative/tier5-src/himi-store-account/index.ts +17 -0
- package/templates/portfolio-creative-owner/dev/index.ts +5 -27
- package/templates/portfolio-creative-owner/tier5-src/_shared/aria-chat.ts +99 -40
- package/templates/portfolio-creative-owner/tier5-src/_shared/aria-widget-oauth.ts +42 -0
- package/templates/portfolio-creative-owner/tier5-src/_shared/aria-widget-view.ts +6 -0
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-aria/index.ts +12 -1
- package/templates/restaurant/config.ts +23 -0
- package/templates/restaurant/dev/index.ts +2611 -1
- package/templates/restaurant/himalaya.content.json +9 -0
- package/templates/restaurant/tier5-src/_shared/branded-lite-hosted.ts +3 -0
- package/templates/restaurant/tier5-src/_shared/member-access.ts +14 -0
- package/templates/restaurant/tier5-src/_shared/member-account-routes.ts +43 -0
- package/templates/restaurant/tier5-src/_shared/member-address.ts +129 -0
- package/templates/restaurant/tier5-src/_shared/saved-card.ts +20 -0
- package/templates/restaurant/tier5-src/himi-branded-lite-address/index.ts +223 -0
- package/templates/restaurant/tier5-src/himi-branded-lite-addresses/index.ts +202 -0
- package/templates/restaurant/tier5-src/himi-branded-lite-wallet/index.ts +217 -0
- package/templates/restaurant/tier5-src/himi-store-account/index.ts +17 -0
- package/templates/restaurant-owner/dev/index.ts +5 -27
- package/templates/restaurant-owner/tier5-src/_shared/aria-chat.ts +99 -40
- package/templates/restaurant-owner/tier5-src/_shared/aria-widget-oauth.ts +42 -0
- package/templates/restaurant-owner/tier5-src/_shared/aria-widget-view.ts +6 -0
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-aria/index.ts +12 -1
- package/templates/retail-store/config.ts +23 -0
- package/templates/retail-store/dev/index.ts +2611 -1
- package/templates/retail-store/himalaya.content.json +9 -0
- package/templates/retail-store/tier5-src/_shared/branded-lite-hosted.ts +3 -0
- package/templates/retail-store/tier5-src/_shared/member-access.ts +14 -0
- package/templates/retail-store/tier5-src/_shared/member-account-routes.ts +43 -0
- package/templates/retail-store/tier5-src/_shared/member-address.ts +129 -0
- package/templates/retail-store/tier5-src/_shared/saved-card.ts +20 -0
- package/templates/retail-store/tier5-src/himi-branded-lite-address/index.ts +223 -0
- package/templates/retail-store/tier5-src/himi-branded-lite-addresses/index.ts +202 -0
- package/templates/retail-store/tier5-src/himi-branded-lite-wallet/index.ts +217 -0
- package/templates/retail-store/tier5-src/himi-store-account/index.ts +17 -0
- package/templates/retail-store-owner/dev/index.ts +5 -27
- package/templates/retail-store-owner/tier5-src/_shared/aria-chat.ts +99 -40
- package/templates/retail-store-owner/tier5-src/_shared/aria-widget-oauth.ts +42 -0
- package/templates/retail-store-owner/tier5-src/_shared/aria-widget-view.ts +6 -0
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-aria/index.ts +12 -1
- package/templates/yoga-studio/config.ts +7 -0
- package/templates/yoga-studio/dev/index.ts +762 -1
- package/templates/yoga-studio/himalaya.content.json +3 -0
- package/templates/yoga-studio/tier5-src/_shared/branded-lite-hosted.ts +3 -0
- package/templates/yoga-studio/tier5-src/_shared/member-access.ts +14 -0
- package/templates/yoga-studio/tier5-src/_shared/member-account-routes.ts +43 -0
- package/templates/yoga-studio/tier5-src/_shared/saved-card.ts +20 -0
- package/templates/yoga-studio/tier5-src/himi-branded-lite-wallet/index.ts +217 -0
- package/templates/yoga-studio/tier5-src/himi-store-account/index.ts +17 -0
- package/templates/yoga-studio-owner/dev/index.ts +5 -27
- package/templates/yoga-studio-owner/tier5-src/_shared/aria-chat.ts +99 -40
- package/templates/yoga-studio-owner/tier5-src/_shared/aria-widget-oauth.ts +42 -0
- package/templates/yoga-studio-owner/tier5-src/_shared/aria-widget-view.ts +6 -0
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-aria/index.ts +12 -1
|
@@ -65,6 +65,8 @@ export const VERTICAL_ROOT_SCREENS: Readonly<Record<string, readonly string[]>>
|
|
|
65
65
|
* (`brandedLiteScreenOwners`, checked by the app's hosted-mode tests), so a screen added
|
|
66
66
|
* to a vertical without being listed here fails the build. */
|
|
67
67
|
export const HOSTED_SCREEN_VERTICALS: Readonly<Record<string, readonly BrandedLiteVertical[]>> = {
|
|
68
|
+
himi_address: ["restaurants", "store"],
|
|
69
|
+
himi_addresses: ["restaurants", "store"],
|
|
68
70
|
himi_badge: ["badges"],
|
|
69
71
|
himi_badges: ["badges"],
|
|
70
72
|
himi_blog: ["blog"],
|
|
@@ -127,6 +129,7 @@ export const HOSTED_SCREEN_VERTICALS: Readonly<Record<string, readonly BrandedLi
|
|
|
127
129
|
himi_video: ["videos"],
|
|
128
130
|
himi_video_channel: ["videos"],
|
|
129
131
|
himi_videos: ["videos"],
|
|
132
|
+
himi_wallet: ["bookings", "plans", "restaurants", "store"],
|
|
130
133
|
};
|
|
131
134
|
|
|
132
135
|
/** The templated (`<prefix>[_/]<id>`) detail routes each vertical owns, pinned the same way. */
|
|
@@ -36,3 +36,17 @@ export async function assertMemberAccess(member: MemberAccess): Promise<void> {
|
|
|
36
36
|
export function memberAccessDenied(error: unknown): boolean {
|
|
37
37
|
return (error as { name?: string } | null)?.name === "MemberAccessChanged" || /(?:HTTP\s*(?:401|403)|READ_ORDER_FORBIDDEN|permission denied|forbidden)/i.test(String(error));
|
|
38
38
|
}
|
|
39
|
+
|
|
40
|
+
/** Did this write fail because the member changed (signed out, or another account)? Then
|
|
41
|
+
* the screen reloads for whoever is signed in now. A 401/403 for the SAME member is only a
|
|
42
|
+
* failed write: the caller keeps what the member typed and says so. */
|
|
43
|
+
export async function memberChanged(member: MemberAccess | undefined, error: unknown): Promise<boolean> {
|
|
44
|
+
if ((error as { name?: string } | null)?.name === "MemberAccessChanged") return true;
|
|
45
|
+
if (!member || !memberAccessDenied(error)) return false;
|
|
46
|
+
try {
|
|
47
|
+
await assertMemberAccess(member);
|
|
48
|
+
return false;
|
|
49
|
+
} catch {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Can this host reach the member account routes My Addresses and My Wallet need?
|
|
2
|
+
//
|
|
3
|
+
// Both are served OFF www.wixapis.com (the address book on ecom.wix.com, saved cards on
|
|
4
|
+
// the member's own site), so only a native runtime whose WixApiCapability pins them can
|
|
5
|
+
// call them: HimalayaCore 1.4.0 and later. The web bridges refuse absolute URLs, and a
|
|
6
|
+
// hosted app (Spaces) signs its calls with a capability pinned to www.wixapis.com. Saved
|
|
7
|
+
// cards also need the shell to have bound its own site (app_info `memberSiteBound`): a
|
|
8
|
+
// multi-app host such as Himi Player binds none, so its wallet route has no host. The
|
|
9
|
+
// Account screen hides a link whose route is unreachable, and the screen says so instead
|
|
10
|
+
// of failing a load.
|
|
11
|
+
import { get as getAppInfo } from "@himalaya/app-info";
|
|
12
|
+
import { isHostedWorker } from "./hosted-checkout.js";
|
|
13
|
+
|
|
14
|
+
/** The HimalayaCore version whose WixApiCapability pins these routes. Checked here, at run
|
|
15
|
+
* time: the screens declare no `minBinaryVersion`, because a release takes its highest
|
|
16
|
+
* screen floor as its own and would then refuse every older binary. */
|
|
17
|
+
export const MEMBER_ACCOUNT_ROUTES_MIN_BINARY = "1.4.0";
|
|
18
|
+
|
|
19
|
+
function atLeast(version: string, floor: string): boolean {
|
|
20
|
+
const parts = (v: string) => v.split("-")[0].split(".").map((p) => Number.parseInt(p, 10) || 0);
|
|
21
|
+
const a = parts(version), b = parts(floor);
|
|
22
|
+
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
23
|
+
const x = a[i] ?? 0, y = b[i] ?? 0;
|
|
24
|
+
if (x !== y) return x > y;
|
|
25
|
+
}
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface MemberAccountRoutes {
|
|
30
|
+
/** The address book on ecom.wix.com (My Addresses). */
|
|
31
|
+
addresses: boolean;
|
|
32
|
+
/** Saved cards on the member's own site (My Wallet). */
|
|
33
|
+
wallet: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function memberAccountRoutes(): Promise<MemberAccountRoutes> {
|
|
37
|
+
if (isHostedWorker()) return { addresses: false, wallet: false };
|
|
38
|
+
const info = await getAppInfo().catch(() => undefined);
|
|
39
|
+
if (!info) return { addresses: false, wallet: false };
|
|
40
|
+
const native = info.platform === "ios" || info.platform === "android" || info.platform === "macos";
|
|
41
|
+
const addresses = native && atLeast(info.binaryVersion ?? "", MEMBER_ACCOUNT_ROUTES_MIN_BINARY);
|
|
42
|
+
return { addresses, wallet: addresses && info.memberSiteBound === true };
|
|
43
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// A saved card's network from its BIN (the first 4-6 digits Wix returns with a saved
|
|
2
|
+
// payment method), for My Wallet's row titles. Wix returns the network only as a brand
|
|
3
|
+
// GUID, so this is the readable name. Brand names are not copy.
|
|
4
|
+
//
|
|
5
|
+
// Pure + Hermes-safe: no host globals, no Node APIs.
|
|
6
|
+
|
|
7
|
+
/** The card network from its BIN, for the row title. Brand names are not copy. */
|
|
8
|
+
export function cardBrand(bin: string): string {
|
|
9
|
+
const d = bin.replace(/\D/g, "");
|
|
10
|
+
const n = (len: number) => Number(d.slice(0, len));
|
|
11
|
+
if (!d) return "";
|
|
12
|
+
if (d.startsWith("4")) return "Visa";
|
|
13
|
+
if ((n(2) >= 51 && n(2) <= 55) || (n(4) >= 2221 && n(4) <= 2720)) return "Mastercard";
|
|
14
|
+
if (d.startsWith("34") || d.startsWith("37")) return "American Express";
|
|
15
|
+
if (d.startsWith("6011") || d.startsWith("65") || (n(3) >= 644 && n(3) <= 649)) return "Discover";
|
|
16
|
+
if (d.startsWith("35")) return "JCB";
|
|
17
|
+
if (d.startsWith("36") || d.startsWith("38") || (n(3) >= 300 && n(3) <= 305)) return "Diners Club";
|
|
18
|
+
if (d.startsWith("62")) return "UnionPay";
|
|
19
|
+
return "";
|
|
20
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
// Tier-5 bundle: Branded Lite — My Wallet (the member's saved payment methods).
|
|
2
|
+
//
|
|
3
|
+
// The cards a member chose to save at checkout, read from Wix Payments on the member's
|
|
4
|
+
// own site (@himalaya/wix-ecom listMySavedPaymentMethods — the native transport routes it there). Like the site's My Wallet
|
|
5
|
+
// page, a member can make one the default for checkout or remove one — there is no
|
|
6
|
+
// "add a card" here: a card is saved while paying. Only the last four digits, the
|
|
7
|
+
// expiry and the cardholder name ever reach the app.
|
|
8
|
+
import { defineService } from "@himalaya/state";
|
|
9
|
+
import {
|
|
10
|
+
deleteSavedPaymentMethod,
|
|
11
|
+
listMySavedPaymentMethods,
|
|
12
|
+
markSavedPaymentMethodPrimary,
|
|
13
|
+
type WixSavedPaymentMethod,
|
|
14
|
+
} from "@himalaya/wix-ecom";
|
|
15
|
+
import { copyParam, copyTemplate, stashCopy } from "../_shared/himi-copy.js";
|
|
16
|
+
import { assertMemberAccess, memberAccess, memberAccessDenied, memberChanged, type MemberAccess } from "../_shared/member-access.js";
|
|
17
|
+
import { memberAccountRoutes } from "../_shared/member-account-routes.js";
|
|
18
|
+
import { cardBrand } from "../_shared/saved-card.js";
|
|
19
|
+
|
|
20
|
+
interface CardRow {
|
|
21
|
+
id: string;
|
|
22
|
+
title: string;
|
|
23
|
+
detail: string;
|
|
24
|
+
isPrimary: boolean;
|
|
25
|
+
notPrimary: boolean;
|
|
26
|
+
confirming: boolean;
|
|
27
|
+
notConfirming: boolean;
|
|
28
|
+
hideActions: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface State {
|
|
32
|
+
status: "loading" | "ready" | "error";
|
|
33
|
+
rows: CardRow[];
|
|
34
|
+
message: string;
|
|
35
|
+
errorText: string;
|
|
36
|
+
needsSignIn: boolean;
|
|
37
|
+
unavailable: boolean;
|
|
38
|
+
noUnavailable: boolean;
|
|
39
|
+
isLoading: boolean;
|
|
40
|
+
notLoading: boolean;
|
|
41
|
+
notErrored: boolean;
|
|
42
|
+
noSignIn: boolean;
|
|
43
|
+
hideList: boolean;
|
|
44
|
+
noEmpty: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let member: MemberAccess | undefined;
|
|
48
|
+
let methods: WixSavedPaymentMethod[] = [];
|
|
49
|
+
let confirmId = "";
|
|
50
|
+
let busy = false;
|
|
51
|
+
|
|
52
|
+
function rowOf(method: WixSavedPaymentMethod, now: Date): CardRow {
|
|
53
|
+
const id = method.id ?? "";
|
|
54
|
+
const card = method.paymentMethod?.cardInfo;
|
|
55
|
+
const last4 = card?.lastFourDigits?.trim() ?? "";
|
|
56
|
+
const isPayPal = (method.paymentMethod?.typeId ?? "").toLowerCase() === "paypal";
|
|
57
|
+
const name = isPayPal ? "PayPal" : cardBrand(card?.bin ?? "") || copyParam("wallet.cardLabel", "Card");
|
|
58
|
+
const title = last4 ? `${name} •••• ${last4}` : name;
|
|
59
|
+
const month = card?.expirationMonth, year = card?.expirationYear;
|
|
60
|
+
let expiry = "";
|
|
61
|
+
if (month && year) {
|
|
62
|
+
const mmyy = `${String(month).padStart(2, "0")}/${String(year).slice(-2)}`;
|
|
63
|
+
// A card is good through the last day of its expiry month.
|
|
64
|
+
const expired = year < now.getFullYear() || (year === now.getFullYear() && month < now.getMonth() + 1);
|
|
65
|
+
expiry = expired
|
|
66
|
+
? copyTemplate("wallet.expiredLabel", "Expired {date}", { date: mmyy })
|
|
67
|
+
: copyTemplate("wallet.expiresLabel", "Expires {date}", { date: mmyy });
|
|
68
|
+
}
|
|
69
|
+
const isPrimary = method.primary === true;
|
|
70
|
+
const confirming = id === confirmId;
|
|
71
|
+
return {
|
|
72
|
+
id,
|
|
73
|
+
title,
|
|
74
|
+
detail: [expiry, card?.cardholderName?.trim()].filter(Boolean).join(" · "),
|
|
75
|
+
isPrimary,
|
|
76
|
+
notPrimary: !isPrimary,
|
|
77
|
+
confirming,
|
|
78
|
+
notConfirming: !confirming,
|
|
79
|
+
hideActions: confirming,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function derive(state: State): State {
|
|
84
|
+
const ready = state.status === "ready" && !state.unavailable;
|
|
85
|
+
const now = new Date();
|
|
86
|
+
// The default card leads, like the site's own page.
|
|
87
|
+
const ordered = [...methods].sort((a, b) => Number(b.primary === true) - Number(a.primary === true));
|
|
88
|
+
const rows = ready ? ordered.filter((m) => !!m.id).map((m) => rowOf(m, now)) : [];
|
|
89
|
+
return {
|
|
90
|
+
...state,
|
|
91
|
+
rows,
|
|
92
|
+
noUnavailable: !state.unavailable,
|
|
93
|
+
isLoading: state.status === "loading",
|
|
94
|
+
notLoading: state.status !== "loading",
|
|
95
|
+
notErrored: state.status !== "error",
|
|
96
|
+
noSignIn: !ready || !state.needsSignIn,
|
|
97
|
+
hideList: !ready || state.needsSignIn || rows.length === 0,
|
|
98
|
+
noEmpty: !ready || state.needsSignIn || rows.length > 0,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const INITIAL: State = derive({
|
|
103
|
+
status: "loading", rows: [], message: "", errorText: "", needsSignIn: false, unavailable: false, noUnavailable: true,
|
|
104
|
+
isLoading: true, notLoading: false, notErrored: true, noSignIn: true, hideList: true, noEmpty: true,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// 100 per page is the service maximum; the page cap only stops a cursor that never ends.
|
|
108
|
+
const MAX_PAGES = 10;
|
|
109
|
+
|
|
110
|
+
async function allSavedPaymentMethods(): Promise<WixSavedPaymentMethod[]> {
|
|
111
|
+
const all: WixSavedPaymentMethod[] = [];
|
|
112
|
+
let cursor = "";
|
|
113
|
+
for (let page = 0; page < MAX_PAGES; page++) {
|
|
114
|
+
const response = await listMySavedPaymentMethods({ limit: 100, ...(cursor ? { cursor } : {}) });
|
|
115
|
+
all.push(...(response.savedPaymentMethods ?? []));
|
|
116
|
+
const next = response.pagingMetadata?.cursors?.next ?? "";
|
|
117
|
+
if (response.pagingMetadata?.hasNext === false || !next || next === cursor) break;
|
|
118
|
+
cursor = next;
|
|
119
|
+
}
|
|
120
|
+
return all;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function load(state: State, arg?: unknown): Promise<State> {
|
|
124
|
+
stashCopy(arg);
|
|
125
|
+
confirmId = "";
|
|
126
|
+
if (!(await memberAccountRoutes()).wallet) {
|
|
127
|
+
methods = [];
|
|
128
|
+
return derive({ ...state, status: "ready", unavailable: true, needsSignIn: false, message: "", errorText: "" });
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
member = await memberAccess();
|
|
132
|
+
if (!member.email) {
|
|
133
|
+
methods = [];
|
|
134
|
+
return derive({ ...state, status: "ready", unavailable: false, needsSignIn: true, message: "", errorText: "" });
|
|
135
|
+
}
|
|
136
|
+
const all = await allSavedPaymentMethods();
|
|
137
|
+
await assertMemberAccess(member);
|
|
138
|
+
methods = all;
|
|
139
|
+
// A fresh load drops the last write's confirmation; write() sets it again after this.
|
|
140
|
+
return derive({ ...state, status: "ready", unavailable: false, needsSignIn: false, message: "", errorText: "" });
|
|
141
|
+
} catch (error) {
|
|
142
|
+
methods = [];
|
|
143
|
+
return derive({
|
|
144
|
+
...state,
|
|
145
|
+
status: "error",
|
|
146
|
+
unavailable: false,
|
|
147
|
+
message: "",
|
|
148
|
+
errorText: memberAccessDenied(error)
|
|
149
|
+
? copyParam("wallet.accessErrorText", "Your wallet is unavailable for this account. Try signing in again.")
|
|
150
|
+
: copyParam("wallet.loadErrorText", "Your saved cards could not be loaded."),
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function argId(arg: unknown): string {
|
|
156
|
+
const id = (arg as { id?: unknown } | null)?.id;
|
|
157
|
+
return typeof id === "string" ? id : "";
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async function write(state: State, run: () => Promise<unknown>, done: string, failed: string): Promise<State> {
|
|
161
|
+
if (busy || !member?.email) return state;
|
|
162
|
+
busy = true;
|
|
163
|
+
try {
|
|
164
|
+
await assertMemberAccess(member);
|
|
165
|
+
await run();
|
|
166
|
+
const next = await load(state);
|
|
167
|
+
return next.status === "ready" ? { ...next, message: done } : next;
|
|
168
|
+
} catch (error) {
|
|
169
|
+
if (await memberChanged(member, error)) return load(state);
|
|
170
|
+
return derive({ ...state, message: failed });
|
|
171
|
+
} finally {
|
|
172
|
+
busy = false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const svc = defineService<State, {
|
|
177
|
+
load: (state: State, arg?: unknown) => Promise<State>;
|
|
178
|
+
retry: (state: State) => Promise<State>;
|
|
179
|
+
setPrimary: (state: State, arg?: unknown) => Promise<State>;
|
|
180
|
+
askRemove: (state: State, arg?: unknown) => State;
|
|
181
|
+
keep: (state: State) => State;
|
|
182
|
+
remove: (state: State, arg?: unknown) => Promise<State>;
|
|
183
|
+
}>("himiBrandedWallet", {
|
|
184
|
+
state: INITIAL,
|
|
185
|
+
actions: {
|
|
186
|
+
load,
|
|
187
|
+
retry: (state) => load(derive({ ...state, status: "loading", message: "" })),
|
|
188
|
+
setPrimary: (state, arg) => {
|
|
189
|
+
const id = argId(arg);
|
|
190
|
+
const method = methods.find((m) => m.id === id);
|
|
191
|
+
if (!id || !method || method.primary) return Promise.resolve(state);
|
|
192
|
+
return write(state, () => markSavedPaymentMethodPrimary(id),
|
|
193
|
+
copyParam("wallet.primaryDone", "Default card updated."),
|
|
194
|
+
copyParam("wallet.primaryFailed", "The default card could not be changed. Try again."));
|
|
195
|
+
},
|
|
196
|
+
askRemove: (state, arg) => {
|
|
197
|
+
const id = argId(arg);
|
|
198
|
+
if (!methods.some((m) => m.id === id)) return state;
|
|
199
|
+
confirmId = id;
|
|
200
|
+
return derive({ ...state, message: "" });
|
|
201
|
+
},
|
|
202
|
+
keep: (state) => {
|
|
203
|
+
confirmId = "";
|
|
204
|
+
return derive(state);
|
|
205
|
+
},
|
|
206
|
+
remove: (state, arg) => {
|
|
207
|
+
// Only the card the member is confirming — a stale row tap removes nothing.
|
|
208
|
+
const id = argId(arg);
|
|
209
|
+
if (!id || id !== confirmId) return Promise.resolve(state);
|
|
210
|
+
return write(state, () => deleteSavedPaymentMethod(id),
|
|
211
|
+
copyParam("wallet.removeDone", "Card removed."),
|
|
212
|
+
copyParam("wallet.removeFailed", "The card could not be removed. Try again."));
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
export { svc };
|
|
@@ -13,6 +13,7 @@ import { dispatch } from "@himalaya/dispatch";
|
|
|
13
13
|
import { searchOrders, type WixOrder } from "@himalaya/wix-ecom";
|
|
14
14
|
import { guestProfile, loadMemberProfile, strOf, fmtDate, titleCase, type AccountActivityRow } from "../_shared/himi-account";
|
|
15
15
|
import { stashCopy, copyParam, copyTemplate } from "../_shared/himi-copy.js";
|
|
16
|
+
import { memberAccountRoutes } from "../_shared/member-account-routes.js";
|
|
16
17
|
|
|
17
18
|
interface AuthRouteInput {
|
|
18
19
|
intent?: string;
|
|
@@ -205,6 +206,14 @@ interface S {
|
|
|
205
206
|
activityEmptyText: string;
|
|
206
207
|
hasActivity: boolean;
|
|
207
208
|
noActivity: boolean;
|
|
209
|
+
/** Set once a descriptor asks for the member-account links (appear param
|
|
210
|
+
* `memberAccountLinks: "1"`). Only then does the probe read app_info, so a descriptor
|
|
211
|
+
* that renders no such link need not declare that capability. */
|
|
212
|
+
memberAccountLinks: boolean;
|
|
213
|
+
/** Hides My Addresses where this runtime cannot reach the address book. */
|
|
214
|
+
noMemberAddresses: boolean;
|
|
215
|
+
/** Hides My Wallet where this runtime cannot reach saved cards. */
|
|
216
|
+
noMemberWallet: boolean;
|
|
208
217
|
}
|
|
209
218
|
|
|
210
219
|
function derive(s: S): S {
|
|
@@ -278,6 +287,8 @@ async function orderRows(memberEmail: string): Promise<AccountActivityRow[]> {
|
|
|
278
287
|
async function probe(s: S, arg?: unknown): Promise<S> {
|
|
279
288
|
const intent = authIntentFromArg(arg, s);
|
|
280
289
|
const profile = await loadMemberProfile(copyParam("storeAccount.guestDetail", "Sign in to keep your cart and orders connected."));
|
|
290
|
+
const memberAccountLinks = s.memberAccountLinks || strField(arg, "memberAccountLinks") === "1";
|
|
291
|
+
const routes = memberAccountLinks ? await memberAccountRoutes() : { addresses: false, wallet: false };
|
|
281
292
|
let activityRows: AccountActivityRow[] = [];
|
|
282
293
|
if (profile.isMember) {
|
|
283
294
|
try {
|
|
@@ -301,6 +312,9 @@ async function probe(s: S, arg?: unknown): Promise<S> {
|
|
|
301
312
|
noMemberImage: profile.noMemberImage,
|
|
302
313
|
email: profile.memberEmail || s.email,
|
|
303
314
|
message: "",
|
|
315
|
+
memberAccountLinks,
|
|
316
|
+
noMemberAddresses: !routes.addresses,
|
|
317
|
+
noMemberWallet: !routes.wallet,
|
|
304
318
|
activityRows,
|
|
305
319
|
activityTitle: copyParam("storeAccount.activityTitle", "Previous orders"),
|
|
306
320
|
activitySummary: activityRows.length
|
|
@@ -394,6 +408,9 @@ const svc = defineService<S, {
|
|
|
394
408
|
activityEmptyText: "No previous orders for this member yet.",
|
|
395
409
|
hasActivity: false,
|
|
396
410
|
noActivity: true,
|
|
411
|
+
memberAccountLinks: false,
|
|
412
|
+
noMemberAddresses: true,
|
|
413
|
+
noMemberWallet: true,
|
|
397
414
|
}),
|
|
398
415
|
actions: {
|
|
399
416
|
load: (s, arg) => { stashCopy(arg); return probe(s, arg); },
|
|
@@ -60653,22 +60653,6 @@ const SCREENS = runtimeSetting({
|
|
|
60653
60653
|
"props": {
|
|
60654
60654
|
"spacing": 8,
|
|
60655
60655
|
"children": [
|
|
60656
|
-
{
|
|
60657
|
-
"id": "",
|
|
60658
|
-
"body": {
|
|
60659
|
-
"package": "himalaya",
|
|
60660
|
-
"type": "Text",
|
|
60661
|
-
"props": {
|
|
60662
|
-
"text": "$service:himiOwnerLiteAria.widget.badge",
|
|
60663
|
-
"style": "footnote",
|
|
60664
|
-
"color": "textSecondary",
|
|
60665
|
-
"maxLines": 12
|
|
60666
|
-
}
|
|
60667
|
-
},
|
|
60668
|
-
"modifiers": {
|
|
60669
|
-
"hiddenPath": "$service:himiOwnerLiteAria.widget.badgeHidden"
|
|
60670
|
-
}
|
|
60671
|
-
},
|
|
60672
60656
|
{
|
|
60673
60657
|
"id": "",
|
|
60674
60658
|
"body": {
|
|
@@ -60705,21 +60689,21 @@ const SCREENS = runtimeSetting({
|
|
|
60705
60689
|
"package": "himalaya",
|
|
60706
60690
|
"type": "Text",
|
|
60707
60691
|
"props": {
|
|
60708
|
-
"text": "$service:himiOwnerLiteAria.widget.
|
|
60692
|
+
"text": "$service:himiOwnerLiteAria.widget.notice",
|
|
60709
60693
|
"style": "footnote",
|
|
60710
60694
|
"color": "textSecondary",
|
|
60711
60695
|
"maxLines": 12
|
|
60712
60696
|
}
|
|
60713
60697
|
},
|
|
60714
60698
|
"modifiers": {
|
|
60715
|
-
"hiddenPath": "$service:himiOwnerLiteAria.widget.
|
|
60699
|
+
"hiddenPath": "$service:himiOwnerLiteAria.widget.noticeHidden"
|
|
60716
60700
|
}
|
|
60717
60701
|
},
|
|
60718
60702
|
{
|
|
60719
60703
|
"id": "himi_owner_lite_aria.widget.OAUTH_REQUIRED.footer",
|
|
60720
60704
|
"body": {
|
|
60721
60705
|
"package": "himalaya",
|
|
60722
|
-
"type": "
|
|
60706
|
+
"type": "VStack",
|
|
60723
60707
|
"props": {
|
|
60724
60708
|
"spacing": 8,
|
|
60725
60709
|
"children": [
|
|
@@ -60731,6 +60715,7 @@ const SCREENS = runtimeSetting({
|
|
|
60731
60715
|
"props": {
|
|
60732
60716
|
"title": "$service:himiOwnerLiteAria.widget.primaryLabel",
|
|
60733
60717
|
"style": "primary",
|
|
60718
|
+
"fullWidth": true,
|
|
60734
60719
|
"action": {
|
|
60735
60720
|
"actionId": "tier5.himiOwnerLiteAria.widgetPrimary"
|
|
60736
60721
|
}
|
|
@@ -60748,6 +60733,7 @@ const SCREENS = runtimeSetting({
|
|
|
60748
60733
|
"props": {
|
|
60749
60734
|
"title": "$service:himiOwnerLiteAria.widget.secondaryLabel",
|
|
60750
60735
|
"style": "secondary",
|
|
60736
|
+
"fullWidth": true,
|
|
60751
60737
|
"action": {
|
|
60752
60738
|
"actionId": "tier5.himiOwnerLiteAria.widgetSecondary"
|
|
60753
60739
|
}
|
|
@@ -60756,14 +60742,6 @@ const SCREENS = runtimeSetting({
|
|
|
60756
60742
|
"modifiers": {
|
|
60757
60743
|
"hiddenPath": "$service:himiOwnerLiteAria.widget.secondaryHidden"
|
|
60758
60744
|
}
|
|
60759
|
-
},
|
|
60760
|
-
{
|
|
60761
|
-
"id": "",
|
|
60762
|
-
"body": {
|
|
60763
|
-
"package": "himalaya",
|
|
60764
|
-
"type": "Spacer",
|
|
60765
|
-
"props": {}
|
|
60766
|
-
}
|
|
60767
60745
|
}
|
|
60768
60746
|
]
|
|
60769
60747
|
}
|