@wix/himalaya-cli 0.1387.0 → 0.1388.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/wix-sdk/video/src/display.d.ts +66 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/video/src/index.d.ts +185 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-video.mjs +324 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/package.json +4 -0
- package/dist/cli.mjs +5 -2
- package/guides/importable-sdks.md +1 -0
- package/guides/sdk-reference.json +253 -0
- package/package.json +2 -2
- package/templates/beauty-salon/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/beauty-salon/tier5-src/_shared/video-caption.ts +21 -0
- package/templates/beauty-salon/tier5-src/himi-branded-lite-home/index.ts +31 -1
- package/templates/beauty-salon-owner/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/branded-lite/dev/index.ts +982 -16
- package/templates/branded-lite/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/branded-lite/tier5-src/_shared/video-caption.ts +21 -0
- package/templates/branded-lite/tier5-src/himi-branded-lite-home/index.ts +31 -1
- package/templates/branded-lite/tier5-src/himi-branded-lite-video/index.ts +23 -32
- package/templates/branded-lite/tier5-src/himi-branded-lite-videos/index.ts +44 -38
- package/templates/community-club/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/community-club/tier5-src/_shared/video-caption.ts +21 -0
- package/templates/community-club/tier5-src/himi-branded-lite-home/index.ts +31 -1
- package/templates/community-club-owner/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/events-meetups/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/events-meetups/tier5-src/_shared/video-caption.ts +21 -0
- package/templates/events-meetups/tier5-src/himi-branded-lite-home/index.ts +31 -1
- package/templates/events-meetups-owner/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/gym-fitness/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/gym-fitness/tier5-src/_shared/video-caption.ts +21 -0
- package/templates/gym-fitness/tier5-src/himi-branded-lite-home/index.ts +31 -1
- package/templates/gym-fitness-owner/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/nonprofit-cause/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/nonprofit-cause/tier5-src/_shared/video-caption.ts +21 -0
- package/templates/nonprofit-cause/tier5-src/himi-branded-lite-home/index.ts +31 -1
- package/templates/nonprofit-cause-owner/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/owner-lite/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/podcast-creator/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/podcast-creator/tier5-src/_shared/video-caption.ts +21 -0
- package/templates/podcast-creator/tier5-src/himi-branded-lite-home/index.ts +31 -1
- package/templates/podcast-creator-owner/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/portfolio-creative/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/portfolio-creative/tier5-src/_shared/video-caption.ts +21 -0
- package/templates/portfolio-creative/tier5-src/himi-branded-lite-home/index.ts +31 -1
- package/templates/portfolio-creative-owner/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/restaurant/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/restaurant/tier5-src/_shared/video-caption.ts +21 -0
- package/templates/restaurant/tier5-src/himi-branded-lite-home/index.ts +31 -1
- package/templates/restaurant-owner/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/retail-store/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/retail-store/tier5-src/_shared/video-caption.ts +21 -0
- package/templates/retail-store/tier5-src/himi-branded-lite-home/index.ts +31 -1
- package/templates/retail-store-owner/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/yoga-studio/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/yoga-studio/tier5-src/_shared/video-caption.ts +21 -0
- package/templates/yoga-studio/tier5-src/himi-branded-lite-home/index.ts +31 -1
- package/templates/yoga-studio-owner/tier5-src/_shared/collection-contract.ts +1 -1
- package/templates/branded-lite/tier5-src/_shared/vod.ts +0 -71
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// The video carousel's two looks: the caption ON the cover (a brand label chip over the
|
|
2
|
+
// image) or UNDER it. Every slide carries both placements; the descriptor's
|
|
3
|
+
// `videoCaption` appear param picks one, and these flags hide the other
|
|
4
|
+
// (`hiddenWhen` hides on TRUE). So a screen switches look by changing ONE param,
|
|
5
|
+
// with no second carousel and no change to the slide tree.
|
|
6
|
+
export type VideoCaption = "overlay" | "below";
|
|
7
|
+
|
|
8
|
+
/** The placement a param asks for. Anything but "below" is the default, "overlay". */
|
|
9
|
+
export function videoCaptionOf(value: unknown): VideoCaption {
|
|
10
|
+
return value === "below" ? "below" : "overlay";
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface CaptionFlags {
|
|
14
|
+
captionOnImage: boolean;
|
|
15
|
+
captionBelow: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Stamp each card with the gates its slide reads (`$item.captionOnImage` / `$item.captionBelow`). */
|
|
19
|
+
export function withCaption<T extends object>(cards: T[], caption: VideoCaption): (T & CaptionFlags)[] {
|
|
20
|
+
return cards.map((card) => ({ ...card, captionOnImage: caption === "overlay", captionBelow: caption === "below" }));
|
|
21
|
+
}
|
|
@@ -22,12 +22,18 @@ import { formatDateMedium } from "../_shared/engine-intl.js";
|
|
|
22
22
|
import { readSavedProducts, type SavedProduct } from "../_shared/himi-store-saved.js";
|
|
23
23
|
import { readSavedIds } from "../_shared/himi-blog-saved.js";
|
|
24
24
|
import { wixMediaUrl } from "@himalaya/wix-media";
|
|
25
|
+
import { isWixVideoWebRefusal, listLatestVideos, videoCard, type WixVideoCard } from "@himalaya/wix-video";
|
|
26
|
+
import { videoCaptionOf, withCaption, type CaptionFlags } from "../_shared/video-caption.js";
|
|
25
27
|
|
|
26
28
|
/** Latched once the member feed is refused because of WHERE the app is running rather
|
|
27
29
|
* than because of the request — see the catch in `loadExtras`. Module scope, so it
|
|
28
30
|
* survives every refresh of this worker and is reset only by a fresh boot. */
|
|
29
31
|
let feedSurfaceRefused = false;
|
|
30
32
|
|
|
33
|
+
/** The same latch for Wix Video: its gateway is reachable from the native apps only, so a
|
|
34
|
+
* web bridge's refusal is permanent for this boot and the rail stays collapsed. */
|
|
35
|
+
let videoSurfaceRefused = false;
|
|
36
|
+
|
|
31
37
|
/** True for the one refusal that can never change while the app keeps running: the web
|
|
32
38
|
* bridge reporting that this host is not reachable from a browser at all. Deliberately
|
|
33
39
|
* narrower than `isBridgeUnavailable` — a 502/503 is an outage worth retrying, and only
|
|
@@ -292,6 +298,10 @@ interface State {
|
|
|
292
298
|
noNextEvent: boolean;
|
|
293
299
|
homePrograms: ProgramCard[];
|
|
294
300
|
noHomePrograms: boolean;
|
|
301
|
+
/** The videos module's carousel — the newest ready Wix Video videos, the channels
|
|
302
|
+
* taking turns. Guarded on `videos`; same silent-fail contract. */
|
|
303
|
+
homeVideos: (WixVideoCard & CaptionFlags)[];
|
|
304
|
+
noHomeVideos: boolean;
|
|
295
305
|
feedHighlights: FeedHighlight[];
|
|
296
306
|
noFeedHighlights: boolean;
|
|
297
307
|
/** communityPulse member strip — the author directory of the SAME feed read
|
|
@@ -529,6 +539,7 @@ interface ExtrasSignal {
|
|
|
529
539
|
upcomingEvents: EventEntry[];
|
|
530
540
|
nextEventCards: NextEventCard[];
|
|
531
541
|
homePrograms: ProgramCard[];
|
|
542
|
+
homeVideos: (WixVideoCard & CaptionFlags)[];
|
|
532
543
|
feedHighlights: FeedHighlight[];
|
|
533
544
|
memberAvatars: CoachAvatar[];
|
|
534
545
|
groupCards: GroupCard[];
|
|
@@ -539,7 +550,7 @@ interface ExtrasSignal {
|
|
|
539
550
|
}
|
|
540
551
|
|
|
541
552
|
function emptyExtrasSignal(): ExtrasSignal {
|
|
542
|
-
return { upcomingEvents: [], nextEventCards: [], homePrograms: [], feedHighlights: [], memberAvatars: [], groupCards: [], announcementTitle: "", postCards: [], popularDishes: [], menuCategories: [] };
|
|
553
|
+
return { upcomingEvents: [], nextEventCards: [], homePrograms: [], homeVideos: [], feedHighlights: [], memberAvatars: [], groupCards: [], announcementTitle: "", postCards: [], popularDishes: [], menuCategories: [] };
|
|
543
554
|
}
|
|
544
555
|
|
|
545
556
|
/** Cover URL probe for a blog post — the himi-blog-feed imageUrl fallbacks
|
|
@@ -679,6 +690,18 @@ async function fetchHomePrograms(): Promise<ProgramCard[]> {
|
|
|
679
690
|
}
|
|
680
691
|
}
|
|
681
692
|
|
|
693
|
+
/** ONE Wix Video read for the videos module. A failure collapses the module; the web
|
|
694
|
+
* bridges' refusal of the gateway also latches it off for the rest of this boot. */
|
|
695
|
+
async function fetchHomeVideos(): Promise<WixVideoCard[]> {
|
|
696
|
+
try {
|
|
697
|
+
const untitled = copyParam("home.videoFallbackTitle", "Video");
|
|
698
|
+
return (await listLatestVideos({ limit: 8 })).map((entry) => videoCard(entry, untitled));
|
|
699
|
+
} catch (error) {
|
|
700
|
+
if (isWixVideoWebRefusal(error)) videoSurfaceRefused = true;
|
|
701
|
+
return [];
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
682
705
|
function homeFeedText(item: WixFeedItem): string {
|
|
683
706
|
const blocks = item.content?.blocks;
|
|
684
707
|
if (Array.isArray(blocks)) {
|
|
@@ -731,6 +754,10 @@ async function fetchExtrasSignal(arg: unknown): Promise<ExtrasSignal> {
|
|
|
731
754
|
if (verticals.includes("programs")) {
|
|
732
755
|
jobs.push(fetchHomePrograms().then((cards) => { extras.homePrograms = cards; }));
|
|
733
756
|
}
|
|
757
|
+
if (verticals.includes("videos") && !videoSurfaceRefused) {
|
|
758
|
+
const caption = videoCaptionOf(paramValue(arg, "videoCaption"));
|
|
759
|
+
jobs.push(fetchHomeVideos().then((cards) => { extras.homeVideos = withCaption(cards, caption); }));
|
|
760
|
+
}
|
|
734
761
|
if (verticals.includes("blog")) {
|
|
735
762
|
// ONE limit-8 posts query feeds BOTH the communityPulse announcement
|
|
736
763
|
// (posts[0].title — the frozen contract) and the showcaseFirst/episodeFirst
|
|
@@ -847,6 +874,7 @@ function derive(state: State): State {
|
|
|
847
874
|
noUpcomingEvents: state.upcomingEvents.length === 0,
|
|
848
875
|
noNextEvent: state.nextEventCards.length === 0,
|
|
849
876
|
noHomePrograms: state.homePrograms.length === 0,
|
|
877
|
+
noHomeVideos: state.homeVideos.length === 0,
|
|
850
878
|
noFeedHighlights: state.feedHighlights.length === 0,
|
|
851
879
|
noMemberAvatars: state.memberAvatars.length === 0,
|
|
852
880
|
noGroupCards: state.groupCards.length === 0,
|
|
@@ -938,6 +966,8 @@ function baseState(): State {
|
|
|
938
966
|
noNextEvent: true,
|
|
939
967
|
homePrograms: [],
|
|
940
968
|
noHomePrograms: true,
|
|
969
|
+
homeVideos: [],
|
|
970
|
+
noHomeVideos: true,
|
|
941
971
|
feedHighlights: [],
|
|
942
972
|
noFeedHighlights: true,
|
|
943
973
|
memberAvatars: [],
|
|
@@ -98,7 +98,7 @@ export const COLLECTIONS: Record<string, CollectionConfig> = {
|
|
|
98
98
|
himiBrandedBadges: { rows: 'badges', title: 'name', subtitle: 'description', view: 'grid' },
|
|
99
99
|
himiBrandedDonations: { rows: 'campaigns', title: 'name', subtitle: 'description', view: 'grid', primaryVerb: { label: 'Donate' } },
|
|
100
100
|
himiBrandedGiftCards: { rows: 'cards', title: 'name', subtitle: 'description', view: 'grid' },
|
|
101
|
-
himiBrandedVideos: { rows: 'channels', title: 'title', subtitle: '
|
|
101
|
+
himiBrandedVideos: { rows: 'channels', title: 'title', subtitle: 'subtitle', image: 'coverUrl', view: 'grid', searchPlaceholder: 'Search channels', countNoun: ['channel', 'channels'] },
|
|
102
102
|
himiBrandedPodcasts: { rows: 'episodes', title: 'title', subtitle: 'subtitle', image: 'image', metadata: ['progressLabel'], view: 'list' },
|
|
103
103
|
himiBrandedForms: { rows: 'forms', title: 'name', subtitle: 'description', view: 'list' },
|
|
104
104
|
himiBrandedGalleries: { rows: 'galleries', title: 'name', subtitle: 'description', image: 'image', view: 'grid' },
|
|
@@ -98,7 +98,7 @@ export const COLLECTIONS: Record<string, CollectionConfig> = {
|
|
|
98
98
|
himiBrandedBadges: { rows: 'badges', title: 'name', subtitle: 'description', view: 'grid' },
|
|
99
99
|
himiBrandedDonations: { rows: 'campaigns', title: 'name', subtitle: 'description', view: 'grid', primaryVerb: { label: 'Donate' } },
|
|
100
100
|
himiBrandedGiftCards: { rows: 'cards', title: 'name', subtitle: 'description', view: 'grid' },
|
|
101
|
-
himiBrandedVideos: { rows: 'channels', title: 'title', subtitle: '
|
|
101
|
+
himiBrandedVideos: { rows: 'channels', title: 'title', subtitle: 'subtitle', image: 'coverUrl', view: 'grid', searchPlaceholder: 'Search channels', countNoun: ['channel', 'channels'] },
|
|
102
102
|
himiBrandedPodcasts: { rows: 'episodes', title: 'title', subtitle: 'subtitle', image: 'image', metadata: ['progressLabel'], view: 'list' },
|
|
103
103
|
himiBrandedForms: { rows: 'forms', title: 'name', subtitle: 'description', view: 'list' },
|
|
104
104
|
himiBrandedGalleries: { rows: 'galleries', title: 'name', subtitle: 'description', image: 'image', view: 'grid' },
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// The video carousel's two looks: the caption ON the cover (a brand label chip over the
|
|
2
|
+
// image) or UNDER it. Every slide carries both placements; the descriptor's
|
|
3
|
+
// `videoCaption` appear param picks one, and these flags hide the other
|
|
4
|
+
// (`hiddenWhen` hides on TRUE). So a screen switches look by changing ONE param,
|
|
5
|
+
// with no second carousel and no change to the slide tree.
|
|
6
|
+
export type VideoCaption = "overlay" | "below";
|
|
7
|
+
|
|
8
|
+
/** The placement a param asks for. Anything but "below" is the default, "overlay". */
|
|
9
|
+
export function videoCaptionOf(value: unknown): VideoCaption {
|
|
10
|
+
return value === "below" ? "below" : "overlay";
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface CaptionFlags {
|
|
14
|
+
captionOnImage: boolean;
|
|
15
|
+
captionBelow: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Stamp each card with the gates its slide reads (`$item.captionOnImage` / `$item.captionBelow`). */
|
|
19
|
+
export function withCaption<T extends object>(cards: T[], caption: VideoCaption): (T & CaptionFlags)[] {
|
|
20
|
+
return cards.map((card) => ({ ...card, captionOnImage: caption === "overlay", captionBelow: caption === "below" }));
|
|
21
|
+
}
|
|
@@ -22,12 +22,18 @@ import { formatDateMedium } from "../_shared/engine-intl.js";
|
|
|
22
22
|
import { readSavedProducts, type SavedProduct } from "../_shared/himi-store-saved.js";
|
|
23
23
|
import { readSavedIds } from "../_shared/himi-blog-saved.js";
|
|
24
24
|
import { wixMediaUrl } from "@himalaya/wix-media";
|
|
25
|
+
import { isWixVideoWebRefusal, listLatestVideos, videoCard, type WixVideoCard } from "@himalaya/wix-video";
|
|
26
|
+
import { videoCaptionOf, withCaption, type CaptionFlags } from "../_shared/video-caption.js";
|
|
25
27
|
|
|
26
28
|
/** Latched once the member feed is refused because of WHERE the app is running rather
|
|
27
29
|
* than because of the request — see the catch in `loadExtras`. Module scope, so it
|
|
28
30
|
* survives every refresh of this worker and is reset only by a fresh boot. */
|
|
29
31
|
let feedSurfaceRefused = false;
|
|
30
32
|
|
|
33
|
+
/** The same latch for Wix Video: its gateway is reachable from the native apps only, so a
|
|
34
|
+
* web bridge's refusal is permanent for this boot and the rail stays collapsed. */
|
|
35
|
+
let videoSurfaceRefused = false;
|
|
36
|
+
|
|
31
37
|
/** True for the one refusal that can never change while the app keeps running: the web
|
|
32
38
|
* bridge reporting that this host is not reachable from a browser at all. Deliberately
|
|
33
39
|
* narrower than `isBridgeUnavailable` — a 502/503 is an outage worth retrying, and only
|
|
@@ -292,6 +298,10 @@ interface State {
|
|
|
292
298
|
noNextEvent: boolean;
|
|
293
299
|
homePrograms: ProgramCard[];
|
|
294
300
|
noHomePrograms: boolean;
|
|
301
|
+
/** The videos module's carousel — the newest ready Wix Video videos, the channels
|
|
302
|
+
* taking turns. Guarded on `videos`; same silent-fail contract. */
|
|
303
|
+
homeVideos: (WixVideoCard & CaptionFlags)[];
|
|
304
|
+
noHomeVideos: boolean;
|
|
295
305
|
feedHighlights: FeedHighlight[];
|
|
296
306
|
noFeedHighlights: boolean;
|
|
297
307
|
/** communityPulse member strip — the author directory of the SAME feed read
|
|
@@ -529,6 +539,7 @@ interface ExtrasSignal {
|
|
|
529
539
|
upcomingEvents: EventEntry[];
|
|
530
540
|
nextEventCards: NextEventCard[];
|
|
531
541
|
homePrograms: ProgramCard[];
|
|
542
|
+
homeVideos: (WixVideoCard & CaptionFlags)[];
|
|
532
543
|
feedHighlights: FeedHighlight[];
|
|
533
544
|
memberAvatars: CoachAvatar[];
|
|
534
545
|
groupCards: GroupCard[];
|
|
@@ -539,7 +550,7 @@ interface ExtrasSignal {
|
|
|
539
550
|
}
|
|
540
551
|
|
|
541
552
|
function emptyExtrasSignal(): ExtrasSignal {
|
|
542
|
-
return { upcomingEvents: [], nextEventCards: [], homePrograms: [], feedHighlights: [], memberAvatars: [], groupCards: [], announcementTitle: "", postCards: [], popularDishes: [], menuCategories: [] };
|
|
553
|
+
return { upcomingEvents: [], nextEventCards: [], homePrograms: [], homeVideos: [], feedHighlights: [], memberAvatars: [], groupCards: [], announcementTitle: "", postCards: [], popularDishes: [], menuCategories: [] };
|
|
543
554
|
}
|
|
544
555
|
|
|
545
556
|
/** Cover URL probe for a blog post — the himi-blog-feed imageUrl fallbacks
|
|
@@ -679,6 +690,18 @@ async function fetchHomePrograms(): Promise<ProgramCard[]> {
|
|
|
679
690
|
}
|
|
680
691
|
}
|
|
681
692
|
|
|
693
|
+
/** ONE Wix Video read for the videos module. A failure collapses the module; the web
|
|
694
|
+
* bridges' refusal of the gateway also latches it off for the rest of this boot. */
|
|
695
|
+
async function fetchHomeVideos(): Promise<WixVideoCard[]> {
|
|
696
|
+
try {
|
|
697
|
+
const untitled = copyParam("home.videoFallbackTitle", "Video");
|
|
698
|
+
return (await listLatestVideos({ limit: 8 })).map((entry) => videoCard(entry, untitled));
|
|
699
|
+
} catch (error) {
|
|
700
|
+
if (isWixVideoWebRefusal(error)) videoSurfaceRefused = true;
|
|
701
|
+
return [];
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
682
705
|
function homeFeedText(item: WixFeedItem): string {
|
|
683
706
|
const blocks = item.content?.blocks;
|
|
684
707
|
if (Array.isArray(blocks)) {
|
|
@@ -731,6 +754,10 @@ async function fetchExtrasSignal(arg: unknown): Promise<ExtrasSignal> {
|
|
|
731
754
|
if (verticals.includes("programs")) {
|
|
732
755
|
jobs.push(fetchHomePrograms().then((cards) => { extras.homePrograms = cards; }));
|
|
733
756
|
}
|
|
757
|
+
if (verticals.includes("videos") && !videoSurfaceRefused) {
|
|
758
|
+
const caption = videoCaptionOf(paramValue(arg, "videoCaption"));
|
|
759
|
+
jobs.push(fetchHomeVideos().then((cards) => { extras.homeVideos = withCaption(cards, caption); }));
|
|
760
|
+
}
|
|
734
761
|
if (verticals.includes("blog")) {
|
|
735
762
|
// ONE limit-8 posts query feeds BOTH the communityPulse announcement
|
|
736
763
|
// (posts[0].title — the frozen contract) and the showcaseFirst/episodeFirst
|
|
@@ -847,6 +874,7 @@ function derive(state: State): State {
|
|
|
847
874
|
noUpcomingEvents: state.upcomingEvents.length === 0,
|
|
848
875
|
noNextEvent: state.nextEventCards.length === 0,
|
|
849
876
|
noHomePrograms: state.homePrograms.length === 0,
|
|
877
|
+
noHomeVideos: state.homeVideos.length === 0,
|
|
850
878
|
noFeedHighlights: state.feedHighlights.length === 0,
|
|
851
879
|
noMemberAvatars: state.memberAvatars.length === 0,
|
|
852
880
|
noGroupCards: state.groupCards.length === 0,
|
|
@@ -938,6 +966,8 @@ function baseState(): State {
|
|
|
938
966
|
noNextEvent: true,
|
|
939
967
|
homePrograms: [],
|
|
940
968
|
noHomePrograms: true,
|
|
969
|
+
homeVideos: [],
|
|
970
|
+
noHomeVideos: true,
|
|
941
971
|
feedHighlights: [],
|
|
942
972
|
noFeedHighlights: true,
|
|
943
973
|
memberAvatars: [],
|
|
@@ -98,7 +98,7 @@ export const COLLECTIONS: Record<string, CollectionConfig> = {
|
|
|
98
98
|
himiBrandedBadges: { rows: 'badges', title: 'name', subtitle: 'description', view: 'grid' },
|
|
99
99
|
himiBrandedDonations: { rows: 'campaigns', title: 'name', subtitle: 'description', view: 'grid', primaryVerb: { label: 'Donate' } },
|
|
100
100
|
himiBrandedGiftCards: { rows: 'cards', title: 'name', subtitle: 'description', view: 'grid' },
|
|
101
|
-
himiBrandedVideos: { rows: 'channels', title: 'title', subtitle: '
|
|
101
|
+
himiBrandedVideos: { rows: 'channels', title: 'title', subtitle: 'subtitle', image: 'coverUrl', view: 'grid', searchPlaceholder: 'Search channels', countNoun: ['channel', 'channels'] },
|
|
102
102
|
himiBrandedPodcasts: { rows: 'episodes', title: 'title', subtitle: 'subtitle', image: 'image', metadata: ['progressLabel'], view: 'list' },
|
|
103
103
|
himiBrandedForms: { rows: 'forms', title: 'name', subtitle: 'description', view: 'list' },
|
|
104
104
|
himiBrandedGalleries: { rows: 'galleries', title: 'name', subtitle: 'description', image: 'image', view: 'grid' },
|
|
@@ -98,7 +98,7 @@ export const COLLECTIONS: Record<string, CollectionConfig> = {
|
|
|
98
98
|
himiBrandedBadges: { rows: 'badges', title: 'name', subtitle: 'description', view: 'grid' },
|
|
99
99
|
himiBrandedDonations: { rows: 'campaigns', title: 'name', subtitle: 'description', view: 'grid', primaryVerb: { label: 'Donate' } },
|
|
100
100
|
himiBrandedGiftCards: { rows: 'cards', title: 'name', subtitle: 'description', view: 'grid' },
|
|
101
|
-
himiBrandedVideos: { rows: 'channels', title: 'title', subtitle: '
|
|
101
|
+
himiBrandedVideos: { rows: 'channels', title: 'title', subtitle: 'subtitle', image: 'coverUrl', view: 'grid', searchPlaceholder: 'Search channels', countNoun: ['channel', 'channels'] },
|
|
102
102
|
himiBrandedPodcasts: { rows: 'episodes', title: 'title', subtitle: 'subtitle', image: 'image', metadata: ['progressLabel'], view: 'list' },
|
|
103
103
|
himiBrandedForms: { rows: 'forms', title: 'name', subtitle: 'description', view: 'list' },
|
|
104
104
|
himiBrandedGalleries: { rows: 'galleries', title: 'name', subtitle: 'description', image: 'image', view: 'grid' },
|
|
@@ -98,7 +98,7 @@ export const COLLECTIONS: Record<string, CollectionConfig> = {
|
|
|
98
98
|
himiBrandedBadges: { rows: 'badges', title: 'name', subtitle: 'description', view: 'grid' },
|
|
99
99
|
himiBrandedDonations: { rows: 'campaigns', title: 'name', subtitle: 'description', view: 'grid', primaryVerb: { label: 'Donate' } },
|
|
100
100
|
himiBrandedGiftCards: { rows: 'cards', title: 'name', subtitle: 'description', view: 'grid' },
|
|
101
|
-
himiBrandedVideos: { rows: 'channels', title: 'title', subtitle: '
|
|
101
|
+
himiBrandedVideos: { rows: 'channels', title: 'title', subtitle: 'subtitle', image: 'coverUrl', view: 'grid', searchPlaceholder: 'Search channels', countNoun: ['channel', 'channels'] },
|
|
102
102
|
himiBrandedPodcasts: { rows: 'episodes', title: 'title', subtitle: 'subtitle', image: 'image', metadata: ['progressLabel'], view: 'list' },
|
|
103
103
|
himiBrandedForms: { rows: 'forms', title: 'name', subtitle: 'description', view: 'list' },
|
|
104
104
|
himiBrandedGalleries: { rows: 'galleries', title: 'name', subtitle: 'description', image: 'image', view: 'grid' },
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// The video carousel's two looks: the caption ON the cover (a brand label chip over the
|
|
2
|
+
// image) or UNDER it. Every slide carries both placements; the descriptor's
|
|
3
|
+
// `videoCaption` appear param picks one, and these flags hide the other
|
|
4
|
+
// (`hiddenWhen` hides on TRUE). So a screen switches look by changing ONE param,
|
|
5
|
+
// with no second carousel and no change to the slide tree.
|
|
6
|
+
export type VideoCaption = "overlay" | "below";
|
|
7
|
+
|
|
8
|
+
/** The placement a param asks for. Anything but "below" is the default, "overlay". */
|
|
9
|
+
export function videoCaptionOf(value: unknown): VideoCaption {
|
|
10
|
+
return value === "below" ? "below" : "overlay";
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface CaptionFlags {
|
|
14
|
+
captionOnImage: boolean;
|
|
15
|
+
captionBelow: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Stamp each card with the gates its slide reads (`$item.captionOnImage` / `$item.captionBelow`). */
|
|
19
|
+
export function withCaption<T extends object>(cards: T[], caption: VideoCaption): (T & CaptionFlags)[] {
|
|
20
|
+
return cards.map((card) => ({ ...card, captionOnImage: caption === "overlay", captionBelow: caption === "below" }));
|
|
21
|
+
}
|
|
@@ -22,12 +22,18 @@ import { formatDateMedium } from "../_shared/engine-intl.js";
|
|
|
22
22
|
import { readSavedProducts, type SavedProduct } from "../_shared/himi-store-saved.js";
|
|
23
23
|
import { readSavedIds } from "../_shared/himi-blog-saved.js";
|
|
24
24
|
import { wixMediaUrl } from "@himalaya/wix-media";
|
|
25
|
+
import { isWixVideoWebRefusal, listLatestVideos, videoCard, type WixVideoCard } from "@himalaya/wix-video";
|
|
26
|
+
import { videoCaptionOf, withCaption, type CaptionFlags } from "../_shared/video-caption.js";
|
|
25
27
|
|
|
26
28
|
/** Latched once the member feed is refused because of WHERE the app is running rather
|
|
27
29
|
* than because of the request — see the catch in `loadExtras`. Module scope, so it
|
|
28
30
|
* survives every refresh of this worker and is reset only by a fresh boot. */
|
|
29
31
|
let feedSurfaceRefused = false;
|
|
30
32
|
|
|
33
|
+
/** The same latch for Wix Video: its gateway is reachable from the native apps only, so a
|
|
34
|
+
* web bridge's refusal is permanent for this boot and the rail stays collapsed. */
|
|
35
|
+
let videoSurfaceRefused = false;
|
|
36
|
+
|
|
31
37
|
/** True for the one refusal that can never change while the app keeps running: the web
|
|
32
38
|
* bridge reporting that this host is not reachable from a browser at all. Deliberately
|
|
33
39
|
* narrower than `isBridgeUnavailable` — a 502/503 is an outage worth retrying, and only
|
|
@@ -292,6 +298,10 @@ interface State {
|
|
|
292
298
|
noNextEvent: boolean;
|
|
293
299
|
homePrograms: ProgramCard[];
|
|
294
300
|
noHomePrograms: boolean;
|
|
301
|
+
/** The videos module's carousel — the newest ready Wix Video videos, the channels
|
|
302
|
+
* taking turns. Guarded on `videos`; same silent-fail contract. */
|
|
303
|
+
homeVideos: (WixVideoCard & CaptionFlags)[];
|
|
304
|
+
noHomeVideos: boolean;
|
|
295
305
|
feedHighlights: FeedHighlight[];
|
|
296
306
|
noFeedHighlights: boolean;
|
|
297
307
|
/** communityPulse member strip — the author directory of the SAME feed read
|
|
@@ -529,6 +539,7 @@ interface ExtrasSignal {
|
|
|
529
539
|
upcomingEvents: EventEntry[];
|
|
530
540
|
nextEventCards: NextEventCard[];
|
|
531
541
|
homePrograms: ProgramCard[];
|
|
542
|
+
homeVideos: (WixVideoCard & CaptionFlags)[];
|
|
532
543
|
feedHighlights: FeedHighlight[];
|
|
533
544
|
memberAvatars: CoachAvatar[];
|
|
534
545
|
groupCards: GroupCard[];
|
|
@@ -539,7 +550,7 @@ interface ExtrasSignal {
|
|
|
539
550
|
}
|
|
540
551
|
|
|
541
552
|
function emptyExtrasSignal(): ExtrasSignal {
|
|
542
|
-
return { upcomingEvents: [], nextEventCards: [], homePrograms: [], feedHighlights: [], memberAvatars: [], groupCards: [], announcementTitle: "", postCards: [], popularDishes: [], menuCategories: [] };
|
|
553
|
+
return { upcomingEvents: [], nextEventCards: [], homePrograms: [], homeVideos: [], feedHighlights: [], memberAvatars: [], groupCards: [], announcementTitle: "", postCards: [], popularDishes: [], menuCategories: [] };
|
|
543
554
|
}
|
|
544
555
|
|
|
545
556
|
/** Cover URL probe for a blog post — the himi-blog-feed imageUrl fallbacks
|
|
@@ -679,6 +690,18 @@ async function fetchHomePrograms(): Promise<ProgramCard[]> {
|
|
|
679
690
|
}
|
|
680
691
|
}
|
|
681
692
|
|
|
693
|
+
/** ONE Wix Video read for the videos module. A failure collapses the module; the web
|
|
694
|
+
* bridges' refusal of the gateway also latches it off for the rest of this boot. */
|
|
695
|
+
async function fetchHomeVideos(): Promise<WixVideoCard[]> {
|
|
696
|
+
try {
|
|
697
|
+
const untitled = copyParam("home.videoFallbackTitle", "Video");
|
|
698
|
+
return (await listLatestVideos({ limit: 8 })).map((entry) => videoCard(entry, untitled));
|
|
699
|
+
} catch (error) {
|
|
700
|
+
if (isWixVideoWebRefusal(error)) videoSurfaceRefused = true;
|
|
701
|
+
return [];
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
682
705
|
function homeFeedText(item: WixFeedItem): string {
|
|
683
706
|
const blocks = item.content?.blocks;
|
|
684
707
|
if (Array.isArray(blocks)) {
|
|
@@ -731,6 +754,10 @@ async function fetchExtrasSignal(arg: unknown): Promise<ExtrasSignal> {
|
|
|
731
754
|
if (verticals.includes("programs")) {
|
|
732
755
|
jobs.push(fetchHomePrograms().then((cards) => { extras.homePrograms = cards; }));
|
|
733
756
|
}
|
|
757
|
+
if (verticals.includes("videos") && !videoSurfaceRefused) {
|
|
758
|
+
const caption = videoCaptionOf(paramValue(arg, "videoCaption"));
|
|
759
|
+
jobs.push(fetchHomeVideos().then((cards) => { extras.homeVideos = withCaption(cards, caption); }));
|
|
760
|
+
}
|
|
734
761
|
if (verticals.includes("blog")) {
|
|
735
762
|
// ONE limit-8 posts query feeds BOTH the communityPulse announcement
|
|
736
763
|
// (posts[0].title — the frozen contract) and the showcaseFirst/episodeFirst
|
|
@@ -847,6 +874,7 @@ function derive(state: State): State {
|
|
|
847
874
|
noUpcomingEvents: state.upcomingEvents.length === 0,
|
|
848
875
|
noNextEvent: state.nextEventCards.length === 0,
|
|
849
876
|
noHomePrograms: state.homePrograms.length === 0,
|
|
877
|
+
noHomeVideos: state.homeVideos.length === 0,
|
|
850
878
|
noFeedHighlights: state.feedHighlights.length === 0,
|
|
851
879
|
noMemberAvatars: state.memberAvatars.length === 0,
|
|
852
880
|
noGroupCards: state.groupCards.length === 0,
|
|
@@ -938,6 +966,8 @@ function baseState(): State {
|
|
|
938
966
|
noNextEvent: true,
|
|
939
967
|
homePrograms: [],
|
|
940
968
|
noHomePrograms: true,
|
|
969
|
+
homeVideos: [],
|
|
970
|
+
noHomeVideos: true,
|
|
941
971
|
feedHighlights: [],
|
|
942
972
|
noFeedHighlights: true,
|
|
943
973
|
memberAvatars: [],
|
|
@@ -98,7 +98,7 @@ export const COLLECTIONS: Record<string, CollectionConfig> = {
|
|
|
98
98
|
himiBrandedBadges: { rows: 'badges', title: 'name', subtitle: 'description', view: 'grid' },
|
|
99
99
|
himiBrandedDonations: { rows: 'campaigns', title: 'name', subtitle: 'description', view: 'grid', primaryVerb: { label: 'Donate' } },
|
|
100
100
|
himiBrandedGiftCards: { rows: 'cards', title: 'name', subtitle: 'description', view: 'grid' },
|
|
101
|
-
himiBrandedVideos: { rows: 'channels', title: 'title', subtitle: '
|
|
101
|
+
himiBrandedVideos: { rows: 'channels', title: 'title', subtitle: 'subtitle', image: 'coverUrl', view: 'grid', searchPlaceholder: 'Search channels', countNoun: ['channel', 'channels'] },
|
|
102
102
|
himiBrandedPodcasts: { rows: 'episodes', title: 'title', subtitle: 'subtitle', image: 'image', metadata: ['progressLabel'], view: 'list' },
|
|
103
103
|
himiBrandedForms: { rows: 'forms', title: 'name', subtitle: 'description', view: 'list' },
|
|
104
104
|
himiBrandedGalleries: { rows: 'galleries', title: 'name', subtitle: 'description', image: 'image', view: 'grid' },
|
|
@@ -98,7 +98,7 @@ export const COLLECTIONS: Record<string, CollectionConfig> = {
|
|
|
98
98
|
himiBrandedBadges: { rows: 'badges', title: 'name', subtitle: 'description', view: 'grid' },
|
|
99
99
|
himiBrandedDonations: { rows: 'campaigns', title: 'name', subtitle: 'description', view: 'grid', primaryVerb: { label: 'Donate' } },
|
|
100
100
|
himiBrandedGiftCards: { rows: 'cards', title: 'name', subtitle: 'description', view: 'grid' },
|
|
101
|
-
himiBrandedVideos: { rows: 'channels', title: 'title', subtitle: '
|
|
101
|
+
himiBrandedVideos: { rows: 'channels', title: 'title', subtitle: 'subtitle', image: 'coverUrl', view: 'grid', searchPlaceholder: 'Search channels', countNoun: ['channel', 'channels'] },
|
|
102
102
|
himiBrandedPodcasts: { rows: 'episodes', title: 'title', subtitle: 'subtitle', image: 'image', metadata: ['progressLabel'], view: 'list' },
|
|
103
103
|
himiBrandedForms: { rows: 'forms', title: 'name', subtitle: 'description', view: 'list' },
|
|
104
104
|
himiBrandedGalleries: { rows: 'galleries', title: 'name', subtitle: 'description', image: 'image', view: 'grid' },
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// The video carousel's two looks: the caption ON the cover (a brand label chip over the
|
|
2
|
+
// image) or UNDER it. Every slide carries both placements; the descriptor's
|
|
3
|
+
// `videoCaption` appear param picks one, and these flags hide the other
|
|
4
|
+
// (`hiddenWhen` hides on TRUE). So a screen switches look by changing ONE param,
|
|
5
|
+
// with no second carousel and no change to the slide tree.
|
|
6
|
+
export type VideoCaption = "overlay" | "below";
|
|
7
|
+
|
|
8
|
+
/** The placement a param asks for. Anything but "below" is the default, "overlay". */
|
|
9
|
+
export function videoCaptionOf(value: unknown): VideoCaption {
|
|
10
|
+
return value === "below" ? "below" : "overlay";
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface CaptionFlags {
|
|
14
|
+
captionOnImage: boolean;
|
|
15
|
+
captionBelow: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Stamp each card with the gates its slide reads (`$item.captionOnImage` / `$item.captionBelow`). */
|
|
19
|
+
export function withCaption<T extends object>(cards: T[], caption: VideoCaption): (T & CaptionFlags)[] {
|
|
20
|
+
return cards.map((card) => ({ ...card, captionOnImage: caption === "overlay", captionBelow: caption === "below" }));
|
|
21
|
+
}
|
|
@@ -22,12 +22,18 @@ import { formatDateMedium } from "../_shared/engine-intl.js";
|
|
|
22
22
|
import { readSavedProducts, type SavedProduct } from "../_shared/himi-store-saved.js";
|
|
23
23
|
import { readSavedIds } from "../_shared/himi-blog-saved.js";
|
|
24
24
|
import { wixMediaUrl } from "@himalaya/wix-media";
|
|
25
|
+
import { isWixVideoWebRefusal, listLatestVideos, videoCard, type WixVideoCard } from "@himalaya/wix-video";
|
|
26
|
+
import { videoCaptionOf, withCaption, type CaptionFlags } from "../_shared/video-caption.js";
|
|
25
27
|
|
|
26
28
|
/** Latched once the member feed is refused because of WHERE the app is running rather
|
|
27
29
|
* than because of the request — see the catch in `loadExtras`. Module scope, so it
|
|
28
30
|
* survives every refresh of this worker and is reset only by a fresh boot. */
|
|
29
31
|
let feedSurfaceRefused = false;
|
|
30
32
|
|
|
33
|
+
/** The same latch for Wix Video: its gateway is reachable from the native apps only, so a
|
|
34
|
+
* web bridge's refusal is permanent for this boot and the rail stays collapsed. */
|
|
35
|
+
let videoSurfaceRefused = false;
|
|
36
|
+
|
|
31
37
|
/** True for the one refusal that can never change while the app keeps running: the web
|
|
32
38
|
* bridge reporting that this host is not reachable from a browser at all. Deliberately
|
|
33
39
|
* narrower than `isBridgeUnavailable` — a 502/503 is an outage worth retrying, and only
|
|
@@ -292,6 +298,10 @@ interface State {
|
|
|
292
298
|
noNextEvent: boolean;
|
|
293
299
|
homePrograms: ProgramCard[];
|
|
294
300
|
noHomePrograms: boolean;
|
|
301
|
+
/** The videos module's carousel — the newest ready Wix Video videos, the channels
|
|
302
|
+
* taking turns. Guarded on `videos`; same silent-fail contract. */
|
|
303
|
+
homeVideos: (WixVideoCard & CaptionFlags)[];
|
|
304
|
+
noHomeVideos: boolean;
|
|
295
305
|
feedHighlights: FeedHighlight[];
|
|
296
306
|
noFeedHighlights: boolean;
|
|
297
307
|
/** communityPulse member strip — the author directory of the SAME feed read
|
|
@@ -529,6 +539,7 @@ interface ExtrasSignal {
|
|
|
529
539
|
upcomingEvents: EventEntry[];
|
|
530
540
|
nextEventCards: NextEventCard[];
|
|
531
541
|
homePrograms: ProgramCard[];
|
|
542
|
+
homeVideos: (WixVideoCard & CaptionFlags)[];
|
|
532
543
|
feedHighlights: FeedHighlight[];
|
|
533
544
|
memberAvatars: CoachAvatar[];
|
|
534
545
|
groupCards: GroupCard[];
|
|
@@ -539,7 +550,7 @@ interface ExtrasSignal {
|
|
|
539
550
|
}
|
|
540
551
|
|
|
541
552
|
function emptyExtrasSignal(): ExtrasSignal {
|
|
542
|
-
return { upcomingEvents: [], nextEventCards: [], homePrograms: [], feedHighlights: [], memberAvatars: [], groupCards: [], announcementTitle: "", postCards: [], popularDishes: [], menuCategories: [] };
|
|
553
|
+
return { upcomingEvents: [], nextEventCards: [], homePrograms: [], homeVideos: [], feedHighlights: [], memberAvatars: [], groupCards: [], announcementTitle: "", postCards: [], popularDishes: [], menuCategories: [] };
|
|
543
554
|
}
|
|
544
555
|
|
|
545
556
|
/** Cover URL probe for a blog post — the himi-blog-feed imageUrl fallbacks
|
|
@@ -679,6 +690,18 @@ async function fetchHomePrograms(): Promise<ProgramCard[]> {
|
|
|
679
690
|
}
|
|
680
691
|
}
|
|
681
692
|
|
|
693
|
+
/** ONE Wix Video read for the videos module. A failure collapses the module; the web
|
|
694
|
+
* bridges' refusal of the gateway also latches it off for the rest of this boot. */
|
|
695
|
+
async function fetchHomeVideos(): Promise<WixVideoCard[]> {
|
|
696
|
+
try {
|
|
697
|
+
const untitled = copyParam("home.videoFallbackTitle", "Video");
|
|
698
|
+
return (await listLatestVideos({ limit: 8 })).map((entry) => videoCard(entry, untitled));
|
|
699
|
+
} catch (error) {
|
|
700
|
+
if (isWixVideoWebRefusal(error)) videoSurfaceRefused = true;
|
|
701
|
+
return [];
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
682
705
|
function homeFeedText(item: WixFeedItem): string {
|
|
683
706
|
const blocks = item.content?.blocks;
|
|
684
707
|
if (Array.isArray(blocks)) {
|
|
@@ -731,6 +754,10 @@ async function fetchExtrasSignal(arg: unknown): Promise<ExtrasSignal> {
|
|
|
731
754
|
if (verticals.includes("programs")) {
|
|
732
755
|
jobs.push(fetchHomePrograms().then((cards) => { extras.homePrograms = cards; }));
|
|
733
756
|
}
|
|
757
|
+
if (verticals.includes("videos") && !videoSurfaceRefused) {
|
|
758
|
+
const caption = videoCaptionOf(paramValue(arg, "videoCaption"));
|
|
759
|
+
jobs.push(fetchHomeVideos().then((cards) => { extras.homeVideos = withCaption(cards, caption); }));
|
|
760
|
+
}
|
|
734
761
|
if (verticals.includes("blog")) {
|
|
735
762
|
// ONE limit-8 posts query feeds BOTH the communityPulse announcement
|
|
736
763
|
// (posts[0].title — the frozen contract) and the showcaseFirst/episodeFirst
|
|
@@ -847,6 +874,7 @@ function derive(state: State): State {
|
|
|
847
874
|
noUpcomingEvents: state.upcomingEvents.length === 0,
|
|
848
875
|
noNextEvent: state.nextEventCards.length === 0,
|
|
849
876
|
noHomePrograms: state.homePrograms.length === 0,
|
|
877
|
+
noHomeVideos: state.homeVideos.length === 0,
|
|
850
878
|
noFeedHighlights: state.feedHighlights.length === 0,
|
|
851
879
|
noMemberAvatars: state.memberAvatars.length === 0,
|
|
852
880
|
noGroupCards: state.groupCards.length === 0,
|
|
@@ -938,6 +966,8 @@ function baseState(): State {
|
|
|
938
966
|
noNextEvent: true,
|
|
939
967
|
homePrograms: [],
|
|
940
968
|
noHomePrograms: true,
|
|
969
|
+
homeVideos: [],
|
|
970
|
+
noHomeVideos: true,
|
|
941
971
|
feedHighlights: [],
|
|
942
972
|
noFeedHighlights: true,
|
|
943
973
|
memberAvatars: [],
|
|
@@ -98,7 +98,7 @@ export const COLLECTIONS: Record<string, CollectionConfig> = {
|
|
|
98
98
|
himiBrandedBadges: { rows: 'badges', title: 'name', subtitle: 'description', view: 'grid' },
|
|
99
99
|
himiBrandedDonations: { rows: 'campaigns', title: 'name', subtitle: 'description', view: 'grid', primaryVerb: { label: 'Donate' } },
|
|
100
100
|
himiBrandedGiftCards: { rows: 'cards', title: 'name', subtitle: 'description', view: 'grid' },
|
|
101
|
-
himiBrandedVideos: { rows: 'channels', title: 'title', subtitle: '
|
|
101
|
+
himiBrandedVideos: { rows: 'channels', title: 'title', subtitle: 'subtitle', image: 'coverUrl', view: 'grid', searchPlaceholder: 'Search channels', countNoun: ['channel', 'channels'] },
|
|
102
102
|
himiBrandedPodcasts: { rows: 'episodes', title: 'title', subtitle: 'subtitle', image: 'image', metadata: ['progressLabel'], view: 'list' },
|
|
103
103
|
himiBrandedForms: { rows: 'forms', title: 'name', subtitle: 'description', view: 'list' },
|
|
104
104
|
himiBrandedGalleries: { rows: 'galleries', title: 'name', subtitle: 'description', image: 'image', view: 'grid' },
|
|
@@ -98,7 +98,7 @@ export const COLLECTIONS: Record<string, CollectionConfig> = {
|
|
|
98
98
|
himiBrandedBadges: { rows: 'badges', title: 'name', subtitle: 'description', view: 'grid' },
|
|
99
99
|
himiBrandedDonations: { rows: 'campaigns', title: 'name', subtitle: 'description', view: 'grid', primaryVerb: { label: 'Donate' } },
|
|
100
100
|
himiBrandedGiftCards: { rows: 'cards', title: 'name', subtitle: 'description', view: 'grid' },
|
|
101
|
-
himiBrandedVideos: { rows: 'channels', title: 'title', subtitle: '
|
|
101
|
+
himiBrandedVideos: { rows: 'channels', title: 'title', subtitle: 'subtitle', image: 'coverUrl', view: 'grid', searchPlaceholder: 'Search channels', countNoun: ['channel', 'channels'] },
|
|
102
102
|
himiBrandedPodcasts: { rows: 'episodes', title: 'title', subtitle: 'subtitle', image: 'image', metadata: ['progressLabel'], view: 'list' },
|
|
103
103
|
himiBrandedForms: { rows: 'forms', title: 'name', subtitle: 'description', view: 'list' },
|
|
104
104
|
himiBrandedGalleries: { rows: 'galleries', title: 'name', subtitle: 'description', image: 'image', view: 'grid' },
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// The video carousel's two looks: the caption ON the cover (a brand label chip over the
|
|
2
|
+
// image) or UNDER it. Every slide carries both placements; the descriptor's
|
|
3
|
+
// `videoCaption` appear param picks one, and these flags hide the other
|
|
4
|
+
// (`hiddenWhen` hides on TRUE). So a screen switches look by changing ONE param,
|
|
5
|
+
// with no second carousel and no change to the slide tree.
|
|
6
|
+
export type VideoCaption = "overlay" | "below";
|
|
7
|
+
|
|
8
|
+
/** The placement a param asks for. Anything but "below" is the default, "overlay". */
|
|
9
|
+
export function videoCaptionOf(value: unknown): VideoCaption {
|
|
10
|
+
return value === "below" ? "below" : "overlay";
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface CaptionFlags {
|
|
14
|
+
captionOnImage: boolean;
|
|
15
|
+
captionBelow: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Stamp each card with the gates its slide reads (`$item.captionOnImage` / `$item.captionBelow`). */
|
|
19
|
+
export function withCaption<T extends object>(cards: T[], caption: VideoCaption): (T & CaptionFlags)[] {
|
|
20
|
+
return cards.map((card) => ({ ...card, captionOnImage: caption === "overlay", captionBelow: caption === "below" }));
|
|
21
|
+
}
|