@wix/himalaya-cli 0.1241.0 → 0.1242.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +0 -1
- package/package.json +2 -2
- package/templates/beauty-salon-owner/dev/index.ts +2 -2
- package/templates/beauty-salon-owner/tier5-src/_shared/owner-selected-site.ts +9 -3
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-overview/index.ts +14 -9
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-sites/index.ts +35 -4
- package/templates/community-club-owner/dev/index.ts +2 -2
- package/templates/community-club-owner/tier5-src/_shared/owner-selected-site.ts +9 -3
- package/templates/community-club-owner/tier5-src/himi-owner-lite-overview/index.ts +14 -9
- package/templates/community-club-owner/tier5-src/himi-owner-lite-sites/index.ts +35 -4
- package/templates/events-meetups-owner/dev/index.ts +2 -2
- package/templates/events-meetups-owner/tier5-src/_shared/owner-selected-site.ts +9 -3
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-overview/index.ts +14 -9
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-sites/index.ts +35 -4
- package/templates/gym-fitness-owner/dev/index.ts +2 -2
- package/templates/gym-fitness-owner/tier5-src/_shared/owner-selected-site.ts +9 -3
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-overview/index.ts +14 -9
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-sites/index.ts +35 -4
- package/templates/nonprofit-cause-owner/dev/index.ts +2 -2
- package/templates/nonprofit-cause-owner/tier5-src/_shared/owner-selected-site.ts +9 -3
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-overview/index.ts +14 -9
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-sites/index.ts +35 -4
- package/templates/owner-lite/dev/index.ts +2 -2
- package/templates/owner-lite/tier5-src/_shared/owner-selected-site.ts +9 -3
- package/templates/owner-lite/tier5-src/himi-owner-lite-overview/index.ts +14 -9
- package/templates/owner-lite/tier5-src/himi-owner-lite-sites/index.ts +35 -4
- package/templates/podcast-creator-owner/dev/index.ts +2 -2
- package/templates/podcast-creator-owner/tier5-src/_shared/owner-selected-site.ts +9 -3
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-overview/index.ts +14 -9
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-sites/index.ts +35 -4
- package/templates/portfolio-creative-owner/dev/index.ts +2 -2
- package/templates/portfolio-creative-owner/tier5-src/_shared/owner-selected-site.ts +9 -3
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-overview/index.ts +14 -9
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-sites/index.ts +35 -4
- package/templates/restaurant-owner/dev/index.ts +2 -2
- package/templates/restaurant-owner/tier5-src/_shared/owner-selected-site.ts +9 -3
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-overview/index.ts +14 -9
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-sites/index.ts +35 -4
- package/templates/retail-store-owner/dev/index.ts +2 -2
- package/templates/retail-store-owner/tier5-src/_shared/owner-selected-site.ts +9 -3
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-overview/index.ts +14 -9
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-sites/index.ts +35 -4
- package/templates/yoga-studio-owner/dev/index.ts +2 -2
- package/templates/yoga-studio-owner/tier5-src/_shared/owner-selected-site.ts +9 -3
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-overview/index.ts +14 -9
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-sites/index.ts +35 -4
|
@@ -13,6 +13,7 @@ import { DEMO_SITES, DEMO_NOTE } from "../_shared/owner-preview-demo.js";
|
|
|
13
13
|
import { stashCopy, copyParam } from "../_shared/himi-copy.js";
|
|
14
14
|
import { getAll, upsert, type DbEntity } from "../_shared/owner-local-db.js";
|
|
15
15
|
import { pickSite, pickedSite } from "../_shared/owner-site-pick.js";
|
|
16
|
+
import { readSelectedSite } from "../_shared/owner-selected-site.js";
|
|
16
17
|
|
|
17
18
|
// Offline-first (data-offline): the owner's last-synced businesses are the
|
|
18
19
|
// READ-THROUGH cache for the `sites` entity — paint them instantly (works
|
|
@@ -33,6 +34,8 @@ interface SiteRow {
|
|
|
33
34
|
id: string; // metaSiteId
|
|
34
35
|
name: string;
|
|
35
36
|
meta: string;
|
|
37
|
+
/** A complete, useful spoken name for the row's tap target. */
|
|
38
|
+
spokenLabel: string;
|
|
36
39
|
initial: string;
|
|
37
40
|
}
|
|
38
41
|
|
|
@@ -55,12 +58,13 @@ interface SitesState {
|
|
|
55
58
|
}
|
|
56
59
|
|
|
57
60
|
function normalize(raw: WixSite): SiteRow {
|
|
58
|
-
const name = raw.displayName || (raw as { name?: string }).name ||
|
|
61
|
+
const name = raw.displayName || (raw as { name?: string }).name || copyParam("ownerSites.unnamedBusiness", "Business");
|
|
59
62
|
const studio = (raw as { namespace?: string }).namespace === "WIX_STUDIO";
|
|
60
63
|
return {
|
|
61
64
|
id: raw.id || "",
|
|
62
65
|
name,
|
|
63
66
|
meta: studio ? "Wix Studio site" : "Wix site",
|
|
67
|
+
spokenLabel: [name, studio ? "Wix Studio site" : "Wix site"].join(", "),
|
|
64
68
|
initial: (name.trim()[0] || "S").toUpperCase(),
|
|
65
69
|
};
|
|
66
70
|
}
|
|
@@ -70,6 +74,30 @@ function applySearch(rows: SiteRow[], query: string): SiteRow[] {
|
|
|
70
74
|
return q ? rows.filter((r) => r.name.toLowerCase().includes(q)) : rows;
|
|
71
75
|
}
|
|
72
76
|
|
|
77
|
+
/** Upgrade rows cached by older bundles, which used the private site id as a fallback name. */
|
|
78
|
+
function repairCachedRow(row: SiteRow, selected: ReturnType<typeof readSelectedSite>): SiteRow {
|
|
79
|
+
// Rows written by this bundle already carry a spoken name, so preserve valid
|
|
80
|
+
// Wix display names even when a business name happens to equal its site ID.
|
|
81
|
+
if (typeof row.spokenLabel === "string" && row.spokenLabel) return row;
|
|
82
|
+
// A legacy cache has no provenance bit. For the currently selected business,
|
|
83
|
+
// the picker kept the Wix display name separately; use it to distinguish a
|
|
84
|
+
// legitimate same-as-ID name from the old private-ID fallback.
|
|
85
|
+
const selectedName = selected?.id === row.id
|
|
86
|
+
&& (selected.name !== row.id || selected.nameFromWix === true)
|
|
87
|
+
? selected.name : "";
|
|
88
|
+
const name = selectedName || (row.name && row.name !== row.id
|
|
89
|
+
? row.name
|
|
90
|
+
: copyParam("ownerSites.unnamedBusiness", "Business"));
|
|
91
|
+
const meta = typeof row.meta === "string" ? row.meta : "";
|
|
92
|
+
return {
|
|
93
|
+
...row,
|
|
94
|
+
name,
|
|
95
|
+
meta,
|
|
96
|
+
spokenLabel: [name, ...meta.split(" · ")].filter(Boolean).join(", "),
|
|
97
|
+
initial: (name.trim()[0] || "S").toUpperCase(),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
73
101
|
function emptyFor(query = ""): string {
|
|
74
102
|
return query.trim()
|
|
75
103
|
? copyParam("ownerSites.emptySearch", "No businesses match that search.")
|
|
@@ -106,6 +134,7 @@ function demoList(s: SitesState): SitesState {
|
|
|
106
134
|
id: d.id,
|
|
107
135
|
name: d.name,
|
|
108
136
|
meta: [d.domain, d.plan].filter(Boolean).join(" · "),
|
|
137
|
+
spokenLabel: [d.name, d.domain, d.plan].filter(Boolean).join(", "),
|
|
109
138
|
initial: (d.name[0] || "S").toUpperCase(),
|
|
110
139
|
}));
|
|
111
140
|
return shape({
|
|
@@ -122,8 +151,10 @@ async function loadList(s: SitesState): Promise<SitesState> {
|
|
|
122
151
|
if (isOwnerPreviewDemo()) return demoList(s);
|
|
123
152
|
// 1) Instant paint from the on-device cache (offline-safe; empty on first-ever run).
|
|
124
153
|
const cached = await readCachedRows();
|
|
125
|
-
const
|
|
126
|
-
|
|
154
|
+
const selected = readSelectedSite();
|
|
155
|
+
const repairedCache = cached.map((row) => repairCachedRow(row, selected));
|
|
156
|
+
const base = repairedCache.length
|
|
157
|
+
? shape({ ...s, status: "ready", allSites: repairedCache, sites: applySearch(repairedCache, s.searchQuery), error: "", demoNote: "" })
|
|
127
158
|
: s;
|
|
128
159
|
// 2) Revalidate from Wix and refresh the cache.
|
|
129
160
|
try {
|
|
@@ -133,7 +164,7 @@ async function loadList(s: SitesState): Promise<SitesState> {
|
|
|
133
164
|
return shape({ ...s, status: "ready", allSites: rows, sites: applySearch(rows, s.searchQuery), resultSummary: `${rows.length} ${rows.length === 1 ? "business" : "businesses"}`, error: "", demoNote: "" });
|
|
134
165
|
} catch (e) {
|
|
135
166
|
// Offline / fetch failure: keep the last-synced cache BEFORE the demo/error fallbacks.
|
|
136
|
-
if (
|
|
167
|
+
if (repairedCache.length) return base;
|
|
137
168
|
if (isBridgeUnavailable(e)) return demoList(s);
|
|
138
169
|
return shape({ ...s, status: "error", allSites: [], sites: [], error: ownerLoadError(e) });
|
|
139
170
|
}
|
|
@@ -61588,7 +61588,7 @@ const SCREENS = runtimeSetting({
|
|
|
61588
61588
|
},
|
|
61589
61589
|
"pressFeedback": "scale",
|
|
61590
61590
|
"haptic": "light",
|
|
61591
|
-
"accessibilityLabel": "
|
|
61591
|
+
"accessibilityLabel": "$item.spokenLabel"
|
|
61592
61592
|
}
|
|
61593
61593
|
}
|
|
61594
61594
|
]
|
|
@@ -62148,7 +62148,7 @@ const SCREENS = runtimeSetting({
|
|
|
62148
62148
|
},
|
|
62149
62149
|
"pressFeedback": "scale",
|
|
62150
62150
|
"haptic": "light",
|
|
62151
|
-
"accessibilityLabel": "
|
|
62151
|
+
"accessibilityLabel": "$item.spokenLabel"
|
|
62152
62152
|
}
|
|
62153
62153
|
}
|
|
62154
62154
|
]
|
|
@@ -27,6 +27,8 @@ type Host = typeof globalThis & { himalaya?: { storage?: StorageLike; input?: Re
|
|
|
27
27
|
export interface SelectedSite {
|
|
28
28
|
id: string;
|
|
29
29
|
name: string;
|
|
30
|
+
/** True when the picker wrote this from a current, repaired Wix site row. */
|
|
31
|
+
nameFromWix?: boolean;
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
const KEY = "himi.ownerLite.selectedSite.v1";
|
|
@@ -60,13 +62,15 @@ export function readSelectedSite(): SelectedSite | null {
|
|
|
60
62
|
if (account && account !== me) return null;
|
|
61
63
|
// An entry the picker wrote before the host knew the owner is CLAIMED by the first
|
|
62
64
|
// worker that does know it, so from then on no other owner on the device reads it.
|
|
63
|
-
if (!account && me) host()?.storage?.setItem(KEY, JSON.stringify({
|
|
65
|
+
if (!account && me) host()?.storage?.setItem(KEY, JSON.stringify({
|
|
66
|
+
id, name, ...(parsed.nameFromWix === true ? { nameFromWix: true } : {}), account: me,
|
|
67
|
+
}));
|
|
64
68
|
// A screen opened FOR a specific business (a deep link or push carrying
|
|
65
69
|
// `business`) names that business's data; a name recorded for another one would
|
|
66
70
|
// sit over the wrong numbers, so it falls back instead.
|
|
67
71
|
const opened = inputString("business");
|
|
68
72
|
if (opened && opened !== id) return null;
|
|
69
|
-
return { id, name };
|
|
73
|
+
return { id, name, ...(parsed.nameFromWix === true ? { nameFromWix: true } : {}) };
|
|
70
74
|
} catch {
|
|
71
75
|
return null; // a corrupt entry reads as "nothing picked", never as a crash
|
|
72
76
|
}
|
|
@@ -80,7 +84,9 @@ export function rememberSelectedSite(site: SelectedSite): void {
|
|
|
80
84
|
if (!id || !name) return;
|
|
81
85
|
const account = inputString("__himiPreferenceAccount");
|
|
82
86
|
try {
|
|
83
|
-
host()?.storage?.setItem(KEY, JSON.stringify(account
|
|
87
|
+
host()?.storage?.setItem(KEY, JSON.stringify(account
|
|
88
|
+
? { id, name, nameFromWix: true, account }
|
|
89
|
+
: { id, name, nameFromWix: true }));
|
|
84
90
|
} catch {
|
|
85
91
|
// Quota / storage disabled: the chrome falls back to the build-time name.
|
|
86
92
|
}
|
|
@@ -44,6 +44,8 @@ const PER_SITE_LIMIT = 50;
|
|
|
44
44
|
interface OverviewRow {
|
|
45
45
|
id: string;
|
|
46
46
|
name: string;
|
|
47
|
+
/** A complete, useful spoken name for the row's tap target. */
|
|
48
|
+
spokenLabel: string;
|
|
47
49
|
initial: string;
|
|
48
50
|
/** "3", "50+", or "—" when that business could not be read. */
|
|
49
51
|
attention: string;
|
|
@@ -79,21 +81,24 @@ function countLabel(n: number | null): string {
|
|
|
79
81
|
|
|
80
82
|
function rowFor(id: string, name: string, count: number | null): OverviewRow {
|
|
81
83
|
const needs = count !== null && count > 0;
|
|
84
|
+
const attention = countLabel(count);
|
|
85
|
+
const attentionLabel = count === null
|
|
86
|
+
? copyParam("ownerOverview.unavailable", "Couldn’t read this business")
|
|
87
|
+
: count === 0
|
|
88
|
+
? copyParam("ownerOverview.allClear", "All clear")
|
|
89
|
+
: count === 1
|
|
90
|
+
? copyParam("ownerOverview.oneNeedsAttention", "order needs attention")
|
|
91
|
+
: copyParam("ownerOverview.manyNeedAttention", "orders need attention");
|
|
82
92
|
return {
|
|
83
93
|
id,
|
|
84
94
|
name,
|
|
95
|
+
spokenLabel: [name, attention, attentionLabel].filter(Boolean).join(", "),
|
|
85
96
|
initial: initialOf(name),
|
|
86
|
-
attention
|
|
97
|
+
attention,
|
|
87
98
|
// Reads as ONE phrase with the count rendered beside it — "3" + "orders need
|
|
88
99
|
// attention". So the label never repeats the number, or the row would say "1 · 1 order
|
|
89
100
|
// needs attention".
|
|
90
|
-
attentionLabel
|
|
91
|
-
? copyParam("ownerOverview.unavailable", "Couldn’t read this business")
|
|
92
|
-
: count === 0
|
|
93
|
-
? copyParam("ownerOverview.allClear", "All clear")
|
|
94
|
-
: count === 1
|
|
95
|
-
? copyParam("ownerOverview.oneNeedsAttention", "order needs attention")
|
|
96
|
-
: copyParam("ownerOverview.manyNeedAttention", "orders need attention"),
|
|
101
|
+
attentionLabel,
|
|
97
102
|
needsAttention: needs,
|
|
98
103
|
calm: !needs,
|
|
99
104
|
};
|
|
@@ -179,7 +184,7 @@ async function loadRollup(): Promise<OverviewState> {
|
|
|
179
184
|
.filter((r: WixSite) => r?.id)
|
|
180
185
|
.map((r: WixSite) => ({
|
|
181
186
|
id: r.id || "",
|
|
182
|
-
name: r.displayName || (r as { name?: string }).name ||
|
|
187
|
+
name: r.displayName || (r as { name?: string }).name || copyParam("ownerOverview.unnamedBusiness", "Business"),
|
|
183
188
|
}));
|
|
184
189
|
if (businesses.length === 0) return shape({ status: "ready", rows: [] });
|
|
185
190
|
|
|
@@ -13,6 +13,7 @@ import { DEMO_SITES, DEMO_NOTE } from "../_shared/owner-preview-demo.js";
|
|
|
13
13
|
import { stashCopy, copyParam } from "../_shared/himi-copy.js";
|
|
14
14
|
import { getAll, upsert, type DbEntity } from "../_shared/owner-local-db.js";
|
|
15
15
|
import { pickSite, pickedSite } from "../_shared/owner-site-pick.js";
|
|
16
|
+
import { readSelectedSite } from "../_shared/owner-selected-site.js";
|
|
16
17
|
|
|
17
18
|
// Offline-first (data-offline): the owner's last-synced businesses are the
|
|
18
19
|
// READ-THROUGH cache for the `sites` entity — paint them instantly (works
|
|
@@ -33,6 +34,8 @@ interface SiteRow {
|
|
|
33
34
|
id: string; // metaSiteId
|
|
34
35
|
name: string;
|
|
35
36
|
meta: string;
|
|
37
|
+
/** A complete, useful spoken name for the row's tap target. */
|
|
38
|
+
spokenLabel: string;
|
|
36
39
|
initial: string;
|
|
37
40
|
}
|
|
38
41
|
|
|
@@ -55,12 +58,13 @@ interface SitesState {
|
|
|
55
58
|
}
|
|
56
59
|
|
|
57
60
|
function normalize(raw: WixSite): SiteRow {
|
|
58
|
-
const name = raw.displayName || (raw as { name?: string }).name ||
|
|
61
|
+
const name = raw.displayName || (raw as { name?: string }).name || copyParam("ownerSites.unnamedBusiness", "Business");
|
|
59
62
|
const studio = (raw as { namespace?: string }).namespace === "WIX_STUDIO";
|
|
60
63
|
return {
|
|
61
64
|
id: raw.id || "",
|
|
62
65
|
name,
|
|
63
66
|
meta: studio ? "Wix Studio site" : "Wix site",
|
|
67
|
+
spokenLabel: [name, studio ? "Wix Studio site" : "Wix site"].join(", "),
|
|
64
68
|
initial: (name.trim()[0] || "S").toUpperCase(),
|
|
65
69
|
};
|
|
66
70
|
}
|
|
@@ -70,6 +74,30 @@ function applySearch(rows: SiteRow[], query: string): SiteRow[] {
|
|
|
70
74
|
return q ? rows.filter((r) => r.name.toLowerCase().includes(q)) : rows;
|
|
71
75
|
}
|
|
72
76
|
|
|
77
|
+
/** Upgrade rows cached by older bundles, which used the private site id as a fallback name. */
|
|
78
|
+
function repairCachedRow(row: SiteRow, selected: ReturnType<typeof readSelectedSite>): SiteRow {
|
|
79
|
+
// Rows written by this bundle already carry a spoken name, so preserve valid
|
|
80
|
+
// Wix display names even when a business name happens to equal its site ID.
|
|
81
|
+
if (typeof row.spokenLabel === "string" && row.spokenLabel) return row;
|
|
82
|
+
// A legacy cache has no provenance bit. For the currently selected business,
|
|
83
|
+
// the picker kept the Wix display name separately; use it to distinguish a
|
|
84
|
+
// legitimate same-as-ID name from the old private-ID fallback.
|
|
85
|
+
const selectedName = selected?.id === row.id
|
|
86
|
+
&& (selected.name !== row.id || selected.nameFromWix === true)
|
|
87
|
+
? selected.name : "";
|
|
88
|
+
const name = selectedName || (row.name && row.name !== row.id
|
|
89
|
+
? row.name
|
|
90
|
+
: copyParam("ownerSites.unnamedBusiness", "Business"));
|
|
91
|
+
const meta = typeof row.meta === "string" ? row.meta : "";
|
|
92
|
+
return {
|
|
93
|
+
...row,
|
|
94
|
+
name,
|
|
95
|
+
meta,
|
|
96
|
+
spokenLabel: [name, ...meta.split(" · ")].filter(Boolean).join(", "),
|
|
97
|
+
initial: (name.trim()[0] || "S").toUpperCase(),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
73
101
|
function emptyFor(query = ""): string {
|
|
74
102
|
return query.trim()
|
|
75
103
|
? copyParam("ownerSites.emptySearch", "No businesses match that search.")
|
|
@@ -106,6 +134,7 @@ function demoList(s: SitesState): SitesState {
|
|
|
106
134
|
id: d.id,
|
|
107
135
|
name: d.name,
|
|
108
136
|
meta: [d.domain, d.plan].filter(Boolean).join(" · "),
|
|
137
|
+
spokenLabel: [d.name, d.domain, d.plan].filter(Boolean).join(", "),
|
|
109
138
|
initial: (d.name[0] || "S").toUpperCase(),
|
|
110
139
|
}));
|
|
111
140
|
return shape({
|
|
@@ -122,8 +151,10 @@ async function loadList(s: SitesState): Promise<SitesState> {
|
|
|
122
151
|
if (isOwnerPreviewDemo()) return demoList(s);
|
|
123
152
|
// 1) Instant paint from the on-device cache (offline-safe; empty on first-ever run).
|
|
124
153
|
const cached = await readCachedRows();
|
|
125
|
-
const
|
|
126
|
-
|
|
154
|
+
const selected = readSelectedSite();
|
|
155
|
+
const repairedCache = cached.map((row) => repairCachedRow(row, selected));
|
|
156
|
+
const base = repairedCache.length
|
|
157
|
+
? shape({ ...s, status: "ready", allSites: repairedCache, sites: applySearch(repairedCache, s.searchQuery), error: "", demoNote: "" })
|
|
127
158
|
: s;
|
|
128
159
|
// 2) Revalidate from Wix and refresh the cache.
|
|
129
160
|
try {
|
|
@@ -133,7 +164,7 @@ async function loadList(s: SitesState): Promise<SitesState> {
|
|
|
133
164
|
return shape({ ...s, status: "ready", allSites: rows, sites: applySearch(rows, s.searchQuery), resultSummary: `${rows.length} ${rows.length === 1 ? "business" : "businesses"}`, error: "", demoNote: "" });
|
|
134
165
|
} catch (e) {
|
|
135
166
|
// Offline / fetch failure: keep the last-synced cache BEFORE the demo/error fallbacks.
|
|
136
|
-
if (
|
|
167
|
+
if (repairedCache.length) return base;
|
|
137
168
|
if (isBridgeUnavailable(e)) return demoList(s);
|
|
138
169
|
return shape({ ...s, status: "error", allSites: [], sites: [], error: ownerLoadError(e) });
|
|
139
170
|
}
|