@wix/himalaya-cli 0.1583.0 → 0.1584.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/blog-v3/src/extensions.d.ts +2 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/comments-v1/src/extensions.d.ts +2 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/events/src/index.d.ts +3 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/groups/src/extensions.d.ts +15 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/loyalty/src/extensions.d.ts +13 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/members-v1/src/index.d.ts +19 -4
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-comments.mjs +3 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-events.mjs +4 -2
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-groups.mjs +5 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-loyalty.mjs +4 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-members.mjs +10 -3
- package/dist/cli.mjs +247 -186
- package/dist/preview-assets.generated.bin +0 -0
- package/fixtures/wix-canned.json +25 -0
- package/guides/one-app-to-himi-app.md +52 -1
- package/guides/sdk-reference.json +37 -9
- package/guides/wix-api-usage.md +9 -0
- package/package.json +2 -2
- package/templates/beauty-salon/config.ts +7 -0
- package/templates/beauty-salon/dev/index.ts +758 -352
- package/templates/beauty-salon/himalaya.content.json +3 -0
- package/templates/beauty-salon/tier5-src/_shared/branded-lite-hosted.ts +1 -1
- package/templates/beauty-salon/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/beauty-salon/tier5-src/_shared/himi-restaurants.ts +2 -1
- package/templates/beauty-salon/tier5-src/_shared/owner-editor-value.ts +164 -0
- package/templates/beauty-salon/tier5-src/_shared/store-product-info.ts +50 -0
- package/templates/beauty-salon/tier5-src/himi-blog-post/index.ts +21 -8
- package/templates/beauty-salon/tier5-src/himi-branded-lite-category/index.ts +3 -2
- package/templates/beauty-salon/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
- package/templates/beauty-salon/tier5-src/himi-branded-lite-plans/index.ts +2 -1
- package/templates/beauty-salon/tier5-src/himi-store-product/index.ts +14 -3
- package/templates/beauty-salon/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
- package/templates/beauty-salon/tier5-src/wix-spaces-whats-new/index.ts +2 -1
- package/templates/beauty-salon-owner/dev/index.ts +129 -66
- package/templates/beauty-salon-owner/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
- package/templates/branded-lite/config.ts +9 -0
- package/templates/branded-lite/dev/index.ts +4988 -3304
- package/templates/branded-lite/himalaya.content.json +3 -0
- package/templates/branded-lite/tier5-src/_shared/branded-lite-hosted.ts +1 -1
- package/templates/branded-lite/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/branded-lite/tier5-src/_shared/event-registration.ts +86 -0
- package/templates/branded-lite/tier5-src/_shared/himi-restaurants.ts +2 -1
- package/templates/branded-lite/tier5-src/_shared/owner-editor-value.ts +164 -0
- package/templates/branded-lite/tier5-src/_shared/store-product-info.ts +50 -0
- package/templates/branded-lite/tier5-src/himi-blog-post/index.ts +21 -8
- package/templates/branded-lite/tier5-src/himi-branded-lite-badges/index.ts +2 -1
- package/templates/branded-lite/tier5-src/himi-branded-lite-category/index.ts +3 -2
- package/templates/branded-lite/tier5-src/himi-branded-lite-feed/index.ts +4 -3
- package/templates/branded-lite/tier5-src/himi-branded-lite-group-about/index.ts +243 -0
- package/templates/branded-lite/tier5-src/himi-branded-lite-loyalty/index.ts +115 -49
- package/templates/branded-lite/tier5-src/himi-branded-lite-plans/index.ts +2 -1
- package/templates/branded-lite/tier5-src/himi-branded-lite-podcasts/index.ts +2 -1
- package/templates/branded-lite/tier5-src/himi-branded-lite-portfolio/index.ts +2 -1
- package/templates/branded-lite/tier5-src/himi-branded-lite-portfolio-project/index.ts +3 -2
- package/templates/branded-lite/tier5-src/himi-branded-lite-program/facts.ts +52 -0
- package/templates/branded-lite/tier5-src/himi-branded-lite-program/index.ts +21 -11
- package/templates/branded-lite/tier5-src/himi-branded-lite-video/index.ts +2 -1
- package/templates/branded-lite/tier5-src/himi-events-detail/index.ts +12 -86
- package/templates/branded-lite/tier5-src/himi-events-list/index.ts +47 -6
- package/templates/branded-lite/tier5-src/himi-store-product/index.ts +13 -3
- package/templates/branded-lite/tier5-src/wix-spaces-forum-post/index.ts +3 -2
- package/templates/branded-lite/tier5-src/wix-spaces-member-detail/index.ts +53 -37
- package/templates/branded-lite/tier5-src/wix-spaces-members/index.ts +100 -34
- package/templates/branded-lite/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
- package/templates/branded-lite/tier5-src/wix-spaces-whats-new/index.ts +2 -1
- package/templates/community-club/config.ts +16 -0
- package/templates/community-club/dev/index.ts +1930 -401
- package/templates/community-club/himalaya.content.json +6 -0
- package/templates/community-club/tier5-src/_shared/branded-lite-hosted.ts +1 -1
- package/templates/community-club/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/community-club/tier5-src/_shared/event-registration.ts +86 -0
- package/templates/community-club/tier5-src/_shared/himi-restaurants.ts +2 -1
- package/templates/community-club/tier5-src/himi-blog-post/index.ts +21 -8
- package/templates/community-club/tier5-src/himi-branded-lite-feed/index.ts +4 -3
- package/templates/community-club/tier5-src/himi-branded-lite-group-about/index.ts +243 -0
- package/templates/community-club/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
- package/templates/community-club/tier5-src/himi-events-detail/index.ts +12 -86
- package/templates/community-club/tier5-src/himi-events-list/index.ts +47 -6
- package/templates/community-club/tier5-src/wix-spaces-whats-new/index.ts +2 -1
- package/templates/community-club-owner/dev/index.ts +53 -1
- package/templates/community-club-owner/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/community-club-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
- package/templates/community-club-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
- package/templates/community-club-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
- package/templates/community-club-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
- package/templates/community-club-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
- package/templates/community-club-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
- package/templates/community-club-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
- package/templates/events-meetups/config.ts +16 -0
- package/templates/events-meetups/dev/index.ts +1930 -401
- package/templates/events-meetups/himalaya.content.json +6 -0
- package/templates/events-meetups/tier5-src/_shared/branded-lite-hosted.ts +1 -1
- package/templates/events-meetups/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/events-meetups/tier5-src/_shared/event-registration.ts +86 -0
- package/templates/events-meetups/tier5-src/_shared/himi-restaurants.ts +2 -1
- package/templates/events-meetups/tier5-src/himi-blog-post/index.ts +21 -8
- package/templates/events-meetups/tier5-src/himi-branded-lite-feed/index.ts +4 -3
- package/templates/events-meetups/tier5-src/himi-branded-lite-group-about/index.ts +243 -0
- package/templates/events-meetups/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
- package/templates/events-meetups/tier5-src/himi-events-detail/index.ts +12 -86
- package/templates/events-meetups/tier5-src/himi-events-list/index.ts +47 -6
- package/templates/events-meetups/tier5-src/wix-spaces-whats-new/index.ts +2 -1
- package/templates/events-meetups-owner/dev/index.ts +6 -0
- package/templates/events-meetups-owner/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
- package/templates/gym-fitness/config.ts +7 -0
- package/templates/gym-fitness/dev/index.ts +1115 -278
- package/templates/gym-fitness/himalaya.content.json +3 -0
- package/templates/gym-fitness/tier5-src/_shared/branded-lite-hosted.ts +1 -1
- package/templates/gym-fitness/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/gym-fitness/tier5-src/_shared/event-registration.ts +86 -0
- package/templates/gym-fitness/tier5-src/_shared/himi-restaurants.ts +2 -1
- package/templates/gym-fitness/tier5-src/_shared/owner-editor-value.ts +164 -0
- package/templates/gym-fitness/tier5-src/_shared/store-product-info.ts +50 -0
- package/templates/gym-fitness/tier5-src/himi-blog-post/index.ts +21 -8
- package/templates/gym-fitness/tier5-src/himi-branded-lite-category/index.ts +3 -2
- package/templates/gym-fitness/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
- package/templates/gym-fitness/tier5-src/himi-branded-lite-plans/index.ts +2 -1
- package/templates/gym-fitness/tier5-src/himi-events-detail/index.ts +12 -86
- package/templates/gym-fitness/tier5-src/himi-events-list/index.ts +47 -6
- package/templates/gym-fitness/tier5-src/himi-store-product/index.ts +14 -3
- package/templates/gym-fitness/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
- package/templates/gym-fitness/tier5-src/wix-spaces-whats-new/index.ts +2 -1
- package/templates/gym-fitness-owner/dev/index.ts +132 -66
- package/templates/gym-fitness-owner/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
- package/templates/nonprofit-cause/config.ts +16 -0
- package/templates/nonprofit-cause/dev/index.ts +1925 -396
- package/templates/nonprofit-cause/himalaya.content.json +6 -0
- package/templates/nonprofit-cause/tier5-src/_shared/branded-lite-hosted.ts +1 -1
- package/templates/nonprofit-cause/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/nonprofit-cause/tier5-src/_shared/event-registration.ts +86 -0
- package/templates/nonprofit-cause/tier5-src/_shared/himi-restaurants.ts +2 -1
- package/templates/nonprofit-cause/tier5-src/himi-blog-post/index.ts +21 -8
- package/templates/nonprofit-cause/tier5-src/himi-branded-lite-feed/index.ts +4 -3
- package/templates/nonprofit-cause/tier5-src/himi-branded-lite-group-about/index.ts +243 -0
- package/templates/nonprofit-cause/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
- package/templates/nonprofit-cause/tier5-src/himi-branded-lite-plans/index.ts +2 -1
- package/templates/nonprofit-cause/tier5-src/himi-events-detail/index.ts +12 -86
- package/templates/nonprofit-cause/tier5-src/himi-events-list/index.ts +47 -6
- package/templates/nonprofit-cause/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
- package/templates/nonprofit-cause/tier5-src/wix-spaces-whats-new/index.ts +2 -1
- package/templates/nonprofit-cause-owner/dev/index.ts +85 -65
- package/templates/nonprofit-cause-owner/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/nonprofit-cause-owner/tier5-src/_shared/owner-rich-guard.ts +27 -0
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
- package/templates/owner-lite/dev/index.ts +132 -66
- package/templates/owner-lite/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/owner-lite/tier5-src/himi-owner-lite-event/index.ts +16 -8
- package/templates/owner-lite/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
- package/templates/owner-lite/tier5-src/himi-owner-lite-events/index.ts +4 -2
- package/templates/owner-lite/tier5-src/himi-owner-lite-post/index.ts +7 -0
- package/templates/owner-lite/tier5-src/himi-owner-lite-product/index.ts +39 -6
- package/templates/owner-lite/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
- package/templates/owner-lite/tier5-src/himi-owner-lite-products/index.ts +7 -4
- package/templates/owner-lite/tier5-src/himi-owner-lite-service/index.ts +38 -7
- package/templates/owner-lite/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
- package/templates/owner-lite/tier5-src/himi-owner-lite-services/index.ts +4 -2
- package/templates/podcast-creator/config.ts +7 -0
- package/templates/podcast-creator/dev/index.ts +1051 -158
- package/templates/podcast-creator/himalaya.content.json +3 -0
- package/templates/podcast-creator/tier5-src/_shared/branded-lite-hosted.ts +1 -1
- package/templates/podcast-creator/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/podcast-creator/tier5-src/_shared/event-registration.ts +86 -0
- package/templates/podcast-creator/tier5-src/_shared/himi-restaurants.ts +2 -1
- package/templates/podcast-creator/tier5-src/himi-blog-post/index.ts +21 -8
- package/templates/podcast-creator/tier5-src/himi-branded-lite-feed/index.ts +4 -3
- package/templates/podcast-creator/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
- package/templates/podcast-creator/tier5-src/himi-branded-lite-plans/index.ts +2 -1
- package/templates/podcast-creator/tier5-src/himi-branded-lite-podcasts/index.ts +2 -1
- package/templates/podcast-creator/tier5-src/himi-events-detail/index.ts +12 -86
- package/templates/podcast-creator/tier5-src/himi-events-list/index.ts +47 -6
- package/templates/podcast-creator/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
- package/templates/podcast-creator/tier5-src/wix-spaces-whats-new/index.ts +2 -1
- package/templates/podcast-creator-owner/dev/index.ts +53 -1
- package/templates/podcast-creator-owner/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
- package/templates/portfolio-creative/config.ts +7 -0
- package/templates/portfolio-creative/dev/index.ts +1305 -468
- package/templates/portfolio-creative/himalaya.content.json +3 -0
- package/templates/portfolio-creative/tier5-src/_shared/branded-lite-hosted.ts +1 -1
- package/templates/portfolio-creative/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/portfolio-creative/tier5-src/_shared/event-registration.ts +86 -0
- package/templates/portfolio-creative/tier5-src/_shared/himi-restaurants.ts +2 -1
- package/templates/portfolio-creative/tier5-src/_shared/owner-editor-value.ts +164 -0
- package/templates/portfolio-creative/tier5-src/_shared/store-product-info.ts +50 -0
- package/templates/portfolio-creative/tier5-src/himi-blog-post/index.ts +21 -8
- package/templates/portfolio-creative/tier5-src/himi-branded-lite-category/index.ts +3 -2
- package/templates/portfolio-creative/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
- package/templates/portfolio-creative/tier5-src/himi-branded-lite-portfolio/index.ts +2 -1
- package/templates/portfolio-creative/tier5-src/himi-branded-lite-portfolio-project/index.ts +3 -2
- package/templates/portfolio-creative/tier5-src/himi-events-detail/index.ts +12 -86
- package/templates/portfolio-creative/tier5-src/himi-events-list/index.ts +47 -6
- package/templates/portfolio-creative/tier5-src/himi-store-product/index.ts +14 -3
- package/templates/portfolio-creative/tier5-src/wix-spaces-whats-new/index.ts +2 -1
- package/templates/portfolio-creative-owner/dev/index.ts +129 -66
- package/templates/portfolio-creative-owner/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
- package/templates/restaurant/config.ts +7 -0
- package/templates/restaurant/dev/index.ts +1051 -158
- package/templates/restaurant/himalaya.content.json +3 -0
- package/templates/restaurant/tier5-src/_shared/branded-lite-hosted.ts +1 -1
- package/templates/restaurant/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/restaurant/tier5-src/_shared/event-registration.ts +86 -0
- package/templates/restaurant/tier5-src/_shared/himi-restaurants.ts +2 -1
- package/templates/restaurant/tier5-src/himi-blog-post/index.ts +21 -8
- package/templates/restaurant/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
- package/templates/restaurant/tier5-src/himi-events-detail/index.ts +12 -86
- package/templates/restaurant/tier5-src/himi-events-list/index.ts +47 -6
- package/templates/restaurant/tier5-src/wix-spaces-whats-new/index.ts +2 -1
- package/templates/restaurant-owner/dev/index.ts +132 -66
- package/templates/restaurant-owner/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
- package/templates/retail-store/config.ts +7 -0
- package/templates/retail-store/dev/index.ts +597 -191
- package/templates/retail-store/himalaya.content.json +3 -0
- package/templates/retail-store/tier5-src/_shared/branded-lite-hosted.ts +1 -1
- package/templates/retail-store/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/retail-store/tier5-src/_shared/himi-restaurants.ts +2 -1
- package/templates/retail-store/tier5-src/_shared/owner-editor-value.ts +164 -0
- package/templates/retail-store/tier5-src/_shared/store-product-info.ts +50 -0
- package/templates/retail-store/tier5-src/himi-blog-post/index.ts +21 -8
- package/templates/retail-store/tier5-src/himi-branded-lite-category/index.ts +3 -2
- package/templates/retail-store/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
- package/templates/retail-store/tier5-src/himi-store-product/index.ts +13 -3
- package/templates/retail-store/tier5-src/wix-spaces-whats-new/index.ts +2 -1
- package/templates/retail-store-owner/dev/index.ts +50 -1
- package/templates/retail-store-owner/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
- package/templates/yoga-studio/config.ts +7 -0
- package/templates/yoga-studio/dev/index.ts +1107 -110
- package/templates/yoga-studio/himalaya.content.json +3 -0
- package/templates/yoga-studio/tier5-src/_shared/branded-lite-hosted.ts +1 -1
- package/templates/yoga-studio/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/yoga-studio/tier5-src/_shared/event-registration.ts +86 -0
- package/templates/yoga-studio/tier5-src/_shared/himi-restaurants.ts +2 -1
- package/templates/yoga-studio/tier5-src/himi-blog-post/index.ts +21 -8
- package/templates/yoga-studio/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
- package/templates/yoga-studio/tier5-src/himi-branded-lite-plans/index.ts +2 -1
- package/templates/yoga-studio/tier5-src/himi-branded-lite-program/facts.ts +52 -0
- package/templates/yoga-studio/tier5-src/himi-branded-lite-program/index.ts +21 -11
- package/templates/yoga-studio/tier5-src/himi-events-detail/index.ts +12 -86
- package/templates/yoga-studio/tier5-src/himi-events-list/index.ts +47 -6
- package/templates/yoga-studio/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
- package/templates/yoga-studio/tier5-src/wix-spaces-whats-new/index.ts +2 -1
- package/templates/yoga-studio-owner/dev/index.ts +85 -65
- package/templates/yoga-studio-owner/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/yoga-studio-owner/tier5-src/_shared/owner-rich-guard.ts +27 -0
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
|
@@ -49,6 +49,9 @@
|
|
|
49
49
|
"himi_group": {
|
|
50
50
|
"displayName": "Group"
|
|
51
51
|
},
|
|
52
|
+
"himi_group_about": {
|
|
53
|
+
"displayName": "Group About"
|
|
54
|
+
},
|
|
52
55
|
"himi_groups": {
|
|
53
56
|
"displayName": "Groups"
|
|
54
57
|
},
|
|
@@ -58,6 +61,9 @@
|
|
|
58
61
|
"himi_my_subscriptions": {
|
|
59
62
|
"displayName": "My Subscriptions"
|
|
60
63
|
},
|
|
64
|
+
"himi_notif_settings": {
|
|
65
|
+
"displayName": "Notif Settings"
|
|
66
|
+
},
|
|
61
67
|
"himi_place_summary": {
|
|
62
68
|
"displayName": "Place Summary"
|
|
63
69
|
},
|
|
@@ -97,6 +97,7 @@ export const HOSTED_SCREEN_VERTICALS: Readonly<Record<string, readonly BrandedLi
|
|
|
97
97
|
himi_gift_card: ["giftCards"],
|
|
98
98
|
himi_gift_cards: ["giftCards"],
|
|
99
99
|
himi_groups: ["groups"],
|
|
100
|
+
himi_group_about: ["groups"],
|
|
100
101
|
himi_invoice: ["invoices"],
|
|
101
102
|
himi_invoices: ["invoices"],
|
|
102
103
|
himi_loyalty: ["loyalty"],
|
|
@@ -107,7 +108,6 @@ export const HOSTED_SCREEN_VERTICALS: Readonly<Record<string, readonly BrandedLi
|
|
|
107
108
|
himi_my_events: ["events"],
|
|
108
109
|
himi_my_subscriptions: ["plans"],
|
|
109
110
|
himi_new_message: ["inbox", "members"],
|
|
110
|
-
himi_notif_settings: ["notifsettings"],
|
|
111
111
|
himi_notifications: ["notifications"],
|
|
112
112
|
himi_orders: ["store"],
|
|
113
113
|
himi_photo_album: ["galleries"],
|
|
@@ -19,6 +19,8 @@ export interface CollectionConfig {
|
|
|
19
19
|
/** Numeric 0–5 row field rendered as a Rating (accessible "N out of 5") instead of
|
|
20
20
|
* folding a "★★★★☆" glyph string into the metadata line, so screen readers get a value. */
|
|
21
21
|
rating?: string;
|
|
22
|
+
/** Domain-provided calendar text and its unavailable flag, kept separate from media. */
|
|
23
|
+
dateBadge?: { text: string; hidden: string };
|
|
22
24
|
sectioned?: boolean;
|
|
23
25
|
view: 'list' | 'grid';
|
|
24
26
|
backendQuery?: boolean;
|
|
@@ -38,7 +40,13 @@ export interface CollectionConfig {
|
|
|
38
40
|
/** The card's primary verb, shown as a visible button beside the overflow.
|
|
39
41
|
* `screenId` opens that screen with the row id; without it the verb reuses the
|
|
40
42
|
* row's own tap action (the detail screen that owns the verb). */
|
|
41
|
-
primaryVerb?: { label: string; screenId?: string
|
|
43
|
+
primaryVerb?: { label: string; screenId?: string;
|
|
44
|
+
/** A row field holding THIS row's verb label (Register, RSVP, Buy Tickets…); `label` stays the fallback copy. */
|
|
45
|
+
labelPath?: string;
|
|
46
|
+
/** A row boolean that hides the verb on rows that offer no action (an event with no registration). */
|
|
47
|
+
hiddenPath?: string };
|
|
48
|
+
/** The worker action that shares a row (`tier5.<service>.<name>` with `{ id }`): adds Share to the card's overflow menu. */
|
|
49
|
+
shareAction?: string;
|
|
42
50
|
/** A TIME-BOUND entity (an event, a booking, a reservation): the boolean row field the
|
|
43
51
|
* DOMAIN sets once the record has finished — computed by the same rule as the row's own
|
|
44
52
|
* "Past" label, in the same place, so the two cannot disagree. A finished
|
|
@@ -73,7 +81,7 @@ export const COLLECTIONS: Record<string, CollectionConfig> = {
|
|
|
73
81
|
himiBookingBookings: { rows: 'rows', title: 'title', subtitle: 'subtitle', metadata: ['status'], view: 'list', backendQuery: true,
|
|
74
82
|
defaultFilter: 'value:Upcoming', searchPlaceholder: 'Search by booking ID', countNoun: ['booking', 'bookings'], filterValues: ['Upcoming', 'Past', 'Cancelled'],
|
|
75
83
|
sortLabels: { default: 'Soonest', az: 'Oldest', za: 'Latest' } },
|
|
76
|
-
himiEvents: { rows: 'results', title: 'title', subtitle: 'dateLine', image: 'image', view: 'grid', backendQuery: true, defaultFilter: 'value:Upcoming', searchPlaceholder: 'Search events', countNoun: ['event', 'events'], filterValues: ['Upcoming', 'Past'], sortLabels: { default: 'Date', az: 'Title A–Z', za: 'Title Z–A' }, primaryVerb: { label: 'Register' }, endedWhen: 'isPast' },
|
|
84
|
+
himiEvents: { rows: 'results', title: 'title', subtitle: 'dateLine', image: 'image', dateBadge: { text: 'dateBadge', hidden: 'noDateBadge' }, gridColumns: 1, view: 'grid', backendQuery: true, defaultFilter: 'value:Upcoming', searchPlaceholder: 'Search events', countNoun: ['event', 'events'], filterValues: ['Upcoming', 'Past'], sortLabels: { default: 'Date', az: 'Title A–Z', za: 'Title Z–A' }, primaryVerb: { label: 'Register', labelPath: 'actionLabel', hiddenPath: 'noAction' }, shareAction: 'share', endedWhen: 'isPast' },
|
|
77
85
|
himiBlogFeed: { rows: 'posts', title: 'title', subtitle: 'excerpt', image: 'image', filter: 'category', filterValuesFrom: 'categories', view: 'grid', narrowedBy: 'selectedCategoryId' },
|
|
78
86
|
himiOwnerLiteProducts: { rows: 'products', title: 'name', subtitle: 'price', image: 'image', view: 'grid', backendQuery: true, countNoun: ['product', 'products'] },
|
|
79
87
|
himiOwnerModules: { instanceInput: 'module', rows: 'rows', title: 'title', subtitle: 'subtitle', image: 'image', filter: 'statusLabel', view: 'list', paged: true, reviewAction: 'prepareRecord', reviewSelectedAction: 'prepareSelected' },
|
|
@@ -208,7 +216,15 @@ export function projectCollection(source: CollectionRow[], config: CollectionCon
|
|
|
208
216
|
...(config.endedWhen ? (() => {
|
|
209
217
|
const ended = row[config.endedWhen] === true || (config.endedWhen === 'isPast' && row.statusLabel === 'Past');
|
|
210
218
|
return { collectionEnded: ended, collectionNotEnded: !ended };
|
|
211
|
-
})() : {})
|
|
219
|
+
})() : {}),
|
|
220
|
+
// A row restored from state saved by an older build has no badge flag. An unset hidden flag
|
|
221
|
+
// fails open and would paint an empty chip, so it reads as "no badge" until a refresh.
|
|
222
|
+
...(config.dateBadge && typeof row[config.dateBadge.hidden] !== 'boolean'
|
|
223
|
+
? { [config.dateBadge.text]: '', [config.dateBadge.hidden]: true } : {}),
|
|
224
|
+
// Same for a per-row verb: an unset hidden flag fails open and would show a verb with an unresolved
|
|
225
|
+
// label, so a row without the flag offers no action until a refresh establishes one.
|
|
226
|
+
...(config.primaryVerb?.hiddenPath && typeof row[config.primaryVerb.hiddenPath] !== 'boolean'
|
|
227
|
+
? { ...(config.primaryVerb.labelPath ? { [config.primaryVerb.labelPath]: '' } : {}), [config.primaryVerb.hiddenPath]: true } : {}) })),
|
|
212
228
|
filters, loaded: rows.length,
|
|
213
229
|
};
|
|
214
230
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Pure Events registration helpers shared by the event detail and the event list: which action an
|
|
2
|
+
// event offers, its label, and the URLs behind it. One copy, so a card and the detail screen never
|
|
3
|
+
// disagree about whether an event can be registered for.
|
|
4
|
+
import type { WixEvent } from "@himalaya/wix-events";
|
|
5
|
+
import { formatWeekdayDateTime } from "./engine-intl.js";
|
|
6
|
+
import { copyParam } from "./himi-copy.js";
|
|
7
|
+
|
|
8
|
+
function detailCopy(key: string, fallback: string): string { return copyParam(key, fallback); }
|
|
9
|
+
|
|
10
|
+
/** Registration CTA kind derived from the event's registration/eventType. */
|
|
11
|
+
export type RegistrationKind = "rsvp" | "tickets" | "external" | "none";
|
|
12
|
+
|
|
13
|
+
/** Map registration status and decoded eventType onto the CTA variant. The decoded
|
|
14
|
+
* `eventType` already folds registration.type / registration.initialType /
|
|
15
|
+
* the legacy flat `type` (decodeEvent, #478). Without a recognized type or
|
|
16
|
+
* status there is no registration action to offer. */
|
|
17
|
+
export function registrationKindOf(e: WixEvent): RegistrationKind {
|
|
18
|
+
const raw = e as unknown as { registration?: { type?: string; initialType?: string; status?: string } };
|
|
19
|
+
const value = (raw.registration?.type ?? e.eventType ?? raw.registration?.initialType ?? "").toUpperCase();
|
|
20
|
+
if (value === "NONE" || value.includes("NO_REGISTRATION")) return "none";
|
|
21
|
+
// Open statuses select the footer action, so they must also select the
|
|
22
|
+
// inventory read and action guards when type metadata is absent or stale.
|
|
23
|
+
switch (raw.registration?.status?.toUpperCase()) {
|
|
24
|
+
case "OPEN_TICKETS": return "tickets";
|
|
25
|
+
case "OPEN_EXTERNAL": return "external";
|
|
26
|
+
case "OPEN_RSVP":
|
|
27
|
+
case "OPEN_RSVP_WAITLIST_ONLY":
|
|
28
|
+
case "SCHEDULED_RSVP": return "rsvp";
|
|
29
|
+
}
|
|
30
|
+
if (value.includes("EXTERNAL")) return "external";
|
|
31
|
+
if (value.includes("TICKET")) return "tickets";
|
|
32
|
+
if (value.includes("RSVP")) return "rsvp";
|
|
33
|
+
return "none";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function eventStatus(e: WixEvent): string {
|
|
37
|
+
const raw = e as unknown as { status?: string };
|
|
38
|
+
return (raw.status ?? e.eventStatus ?? "").toUpperCase();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function registrationUi(e: WixEvent, now: number): { state: string; label: string; action: string; caption: string; notice: string } {
|
|
42
|
+
const raw = e as unknown as { registration?: { status?: string; registrationPaused?: boolean; registrationDisabled?: boolean; allowedGuestTypes?: string; rsvp?: { startDate?: string }; tickets?: { soldOut?: boolean }; external?: { url?: string } }; form?: { messages?: { registrationClosed?: { message?: string; exploreEventsActionLabel?: string }; ticketsUnavailable?: { message?: string; exploreEventsActionLabel?: string } } } };
|
|
43
|
+
const r = raw.registration ?? {};
|
|
44
|
+
const status = eventStatus(e);
|
|
45
|
+
const type = registrationKindOf(e);
|
|
46
|
+
if (status === "CANCELED") return { state: "notice", label: "", action: "", caption: "", notice: detailCopy("event.canceledNotice", "This event was canceled.") };
|
|
47
|
+
if (status === "ENDED") return { state: "notice", label: "", action: "", caption: "", notice: detailCopy("event.endedNotice", "This event has ended.") };
|
|
48
|
+
if (type === "none" || r.registrationDisabled) return { state: "none", label: "", action: "", caption: "", notice: "" };
|
|
49
|
+
const ownerClosed = raw.form?.messages?.registrationClosed?.message;
|
|
50
|
+
const ownerSold = raw.form?.messages?.ticketsUnavailable?.message;
|
|
51
|
+
if (["CLOSED_AUTOMATICALLY", "CLOSED_MANUALLY"].includes(r.status || "") || r.registrationPaused) return { state: "notice", label: "", action: "", caption: "", notice: ownerClosed || detailCopy("event.closedNotice", "Registration is closed.") };
|
|
52
|
+
if (type === "tickets" && r.tickets?.soldOut) return { state: "notice", label: "", action: "", caption: "", notice: ownerSold || detailCopy("event.soldOutNotice", "Tickets are sold out.") };
|
|
53
|
+
if (r.status === "SCHEDULED_RSVP") return { state: "scheduled", label: detailCopy("event.rsvpCta", "RSVP"), action: "", caption: detailCopy("event.opensCaption", "RSVP opens {date}.").replace("{date}", r.rsvp?.startDate ? formatWeekdayDateTime(new Date(r.rsvp.startDate)) : ""), notice: "" };
|
|
54
|
+
if (r.status === "OPEN_RSVP_WAITLIST_ONLY") return { state: "waitlist", label: detailCopy("event.waitlistCta", "Join Waitlist"), action: "himi.open", caption: detailCopy("event.fullCaption", "This event is full."), notice: "" };
|
|
55
|
+
if (r.status === "OPEN_TICKETS" || type === "tickets") return { state: "tickets", label: detailCopy("event.ticketsCta", "Buy Tickets"), action: "openTickets", caption: "", notice: "" };
|
|
56
|
+
if (r.status === "OPEN_EXTERNAL" || type === "external") return registrationUrlOf(e)
|
|
57
|
+
? { state: "external", label: detailCopy("event.externalCta", "Register"), action: "openRegistration", caption: "", notice: "" }
|
|
58
|
+
: { state: "none", label: "", action: "", caption: "", notice: "" };
|
|
59
|
+
if (type === "rsvp" && r.status === "OPEN_RSVP") return { state: "rsvp", label: detailCopy("event.rsvpCta", "RSVP"), action: "himi.open", caption: "", notice: "" };
|
|
60
|
+
if (r.status === "OPEN_RSVP" || (type === "rsvp" && !r.status)) return { state: "rsvp", label: detailCopy("event.rsvpCta", "RSVP"), action: "himi.open", caption: "", notice: "" };
|
|
61
|
+
void now;
|
|
62
|
+
return { state: "none", label: "", action: "", caption: "", notice: "" };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** External registration URL / event page URL from the raw payload ("" when absent). */
|
|
66
|
+
export function registrationUrlOf(e: WixEvent): string {
|
|
67
|
+
const raw = e as unknown as {
|
|
68
|
+
registration?: { external?: { registration?: string; url?: string } };
|
|
69
|
+
eventPageUrl?: { base?: string; path?: string } | string;
|
|
70
|
+
};
|
|
71
|
+
const external = raw.registration?.external?.registration ?? raw.registration?.external?.url;
|
|
72
|
+
if (typeof external === "string" && /^https?:\/\//i.test(external)) return external;
|
|
73
|
+
return eventPageUrlOf(e);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** The event's public page URL when the payload carries one ("" otherwise). */
|
|
77
|
+
export function eventPageUrlOf(e: WixEvent): string {
|
|
78
|
+
const raw = e as unknown as { eventPageUrl?: { base?: string; path?: string } | string };
|
|
79
|
+
const page = raw.eventPageUrl;
|
|
80
|
+
if (typeof page === "string" && /^https?:\/\//i.test(page)) return page;
|
|
81
|
+
if (page && typeof page === "object" && typeof page.base === "string" && page.base) {
|
|
82
|
+
const path = typeof page.path === "string" ? page.path : "";
|
|
83
|
+
return `${page.base.replace(/\/$/, "")}${path && !path.startsWith("/") ? "/" : ""}${path}`;
|
|
84
|
+
}
|
|
85
|
+
return "";
|
|
86
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { descriptionBody } from "./rich-body.js";
|
|
1
2
|
// Shared restaurants-menu shaping for himi workers (branded-lite `restaurants`
|
|
2
3
|
// vertical): dish cards + menu categories derived from the Wix Restaurants
|
|
3
4
|
// Menus hierarchy (menu → sections → items). Used by BOTH the menus worker
|
|
@@ -166,7 +167,7 @@ export function itemPriceVariants(item: WixItem): Array<{ id: string; variantId:
|
|
|
166
167
|
|
|
167
168
|
/** HTML-stripped, whitespace-collapsed description snippet (≤ 90 chars). */
|
|
168
169
|
export function descriptionSnippet(value: unknown, maxLength = 90): string {
|
|
169
|
-
const text = (
|
|
170
|
+
const text = descriptionBody(value).bodyText.replace(/\s+/g, " ").trim();
|
|
170
171
|
return text.length > maxLength ? `${text.slice(0, maxLength - 1).trimEnd()}…` : text;
|
|
171
172
|
}
|
|
172
173
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Tier-5 bundle: Himi Blog post detail - reader content plus member actions.
|
|
2
2
|
import { defineService } from "@himalaya/state";
|
|
3
3
|
import { share as shareSheet } from "@himalaya/share";
|
|
4
|
-
import { getPost, likePost, unlikePost, type WixPost } from "@himalaya/wix-blog";
|
|
4
|
+
import { getPost, likePost, unlikePost, type WixPost, type WixPostWithContent } from "@himalaya/wix-blog";
|
|
5
5
|
import { comments, BLOG_COMMENTS_APP_ID, blogCommentRow, listBlogComments, type WixBlogCommentRow } from "@himalaya/wix-comments";
|
|
6
6
|
import { get, type DbEntity } from "@himalaya/local-db";
|
|
7
7
|
import { isSavedId, toggleSavedId } from "../_shared/himi-blog-saved";
|
|
@@ -38,6 +38,7 @@ interface S {
|
|
|
38
38
|
signInRequired: boolean; noSignIn: boolean; signInScreenId: string;
|
|
39
39
|
status: "loading" | "ready" | "error";
|
|
40
40
|
id: string;
|
|
41
|
+
commentResourceId: string;
|
|
41
42
|
title: string;
|
|
42
43
|
subtitle: string;
|
|
43
44
|
/** Author byline ("By <name>") — resolved from the post payload's writer-ish
|
|
@@ -80,7 +81,7 @@ interface S {
|
|
|
80
81
|
blogScreenId: string;
|
|
81
82
|
}
|
|
82
83
|
|
|
83
|
-
type PostWithExtras =
|
|
84
|
+
type PostWithExtras = WixPostWithContent & {
|
|
84
85
|
media?: { wixMedia?: { image?: { url?: string } }; embedMedia?: { thumbnail?: { url?: string } } };
|
|
85
86
|
coverImage?: { image?: { url?: string }; url?: string };
|
|
86
87
|
metrics?: { likeCount?: number; likes?: number; totalComments?: number; commentCount?: number; comments?: number };
|
|
@@ -219,6 +220,7 @@ const baseState: S = derive({
|
|
|
219
220
|
...NO_READ_SIGN_IN, signInScreenId: "himi_account",
|
|
220
221
|
status: "loading",
|
|
221
222
|
id: "",
|
|
223
|
+
commentResourceId: "",
|
|
222
224
|
title: "",
|
|
223
225
|
subtitle: "",
|
|
224
226
|
byline: "",
|
|
@@ -265,6 +267,7 @@ async function cachedPostState(s: S, id: string): Promise<S> {
|
|
|
265
267
|
...s,
|
|
266
268
|
status: "ready",
|
|
267
269
|
id,
|
|
270
|
+
commentResourceId: "",
|
|
268
271
|
title,
|
|
269
272
|
subtitle: String(cached.meta ?? ""),
|
|
270
273
|
excerpt: String(cached.excerpt ?? "").trim(),
|
|
@@ -294,19 +297,28 @@ async function loadPostState(s: S): Promise<S | undefined> {
|
|
|
294
297
|
}
|
|
295
298
|
try {
|
|
296
299
|
const access = await readAccess();
|
|
297
|
-
const
|
|
298
|
-
|
|
300
|
+
const response = await getPost(id, { fieldsets: ["RICH_CONTENT", "CONTENT_TEXT", "INTERNAL_ID"] }, { preserveAccessErrors: true });
|
|
301
|
+
await assertReadAccess(access);
|
|
302
|
+
if (version !== loadVersion) return undefined;
|
|
303
|
+
const post = response.post as PostWithExtras;
|
|
304
|
+
// Native Blog v3 Comments scopes by internalId, not the navigation UUID.
|
|
305
|
+
// A missing projection cannot establish that the post has no comments.
|
|
306
|
+
const commentResourceId = post.internalId?.trim() ?? "";
|
|
307
|
+
let commentsUnavailable = !commentResourceId;
|
|
308
|
+
let comments: CommentRow[] = [];
|
|
309
|
+
if (commentResourceId) {
|
|
310
|
+
try { comments = (await listBlogComments(commentResourceId)).map(toCommentRow); }
|
|
311
|
+
catch { commentsUnavailable = true; }
|
|
312
|
+
}
|
|
299
313
|
await assertReadAccess(access);
|
|
300
314
|
if (version !== loadVersion) return undefined;
|
|
301
|
-
const post = (((postResult.value as unknown as { post?: WixPost }).post) ?? postResult.value) as PostWithExtras;
|
|
302
|
-
const commentsUnavailable = commentsResult.status === "rejected";
|
|
303
|
-
const comments = commentsResult.status === "fulfilled" ? commentsResult.value.map(toCommentRow) : [];
|
|
304
315
|
const cover = imageUrl(post);
|
|
305
316
|
const title = post.title || "Untitled post";
|
|
306
317
|
return derive({
|
|
307
318
|
...clean,
|
|
308
319
|
status: "ready",
|
|
309
320
|
id,
|
|
321
|
+
commentResourceId,
|
|
310
322
|
title,
|
|
311
323
|
subtitle: subtitle(post),
|
|
312
324
|
byline: bylineOf(post),
|
|
@@ -398,9 +410,10 @@ const svc = defineService<S, {
|
|
|
398
410
|
submitComment: async (s) => {
|
|
399
411
|
const text = s.commentDraft.trim();
|
|
400
412
|
if (!text) return derive({ ...s, actionMessage: copyParam("blogPost.commentValidation", "Write a comment first.") });
|
|
413
|
+
if (!s.commentResourceId) return derive({ ...s, actionMessage: copyParam("blogPost.commentsUnavailable", "Comments are unavailable right now.") });
|
|
401
414
|
try {
|
|
402
415
|
const { comment } = await comments.createComment({
|
|
403
|
-
comment: { appId: BLOG_COMMENTS_APP_ID, contextId: s.
|
|
416
|
+
comment: { appId: BLOG_COMMENTS_APP_ID, contextId: s.commentResourceId, resourceId: s.commentResourceId, content: { richContent: commentRichContent(text) } },
|
|
404
417
|
});
|
|
405
418
|
const created = blogCommentRow(comment);
|
|
406
419
|
// Named to avoid shadowing the `comments` namespace import in this same block scope.
|
|
@@ -25,7 +25,7 @@ import { initials } from "../_shared/himi-text.js";
|
|
|
25
25
|
import { copyParam, stashCopy } from "../_shared/himi-copy.js";
|
|
26
26
|
import { formatMonthDay } from "../_shared/engine-intl.js";
|
|
27
27
|
import { openRichLink } from "../_shared/rich-content-link.js";
|
|
28
|
-
import {
|
|
28
|
+
import { descriptionBody, ROW_IMAGE_WIDTH } from "../_shared/rich-body.js";
|
|
29
29
|
|
|
30
30
|
interface FeedRow {
|
|
31
31
|
id: string;
|
|
@@ -104,7 +104,7 @@ function feedText(item: WixFeedItem): string {
|
|
|
104
104
|
if (joined) return joined;
|
|
105
105
|
}
|
|
106
106
|
const lean = item.entity?.body?.content;
|
|
107
|
-
return
|
|
107
|
+
return descriptionBody(lean, copyParam("feed.sharedUpdateFallback", "Shared an update.")).bodyText;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
function authorName(item: WixFeedItem, profiles: WixFeedProfile[]): string {
|
|
@@ -172,7 +172,8 @@ function feedRow(item: WixFeedItem, profiles: WixFeedProfile[]): FeedRow {
|
|
|
172
172
|
const count = Array.isArray(item.reactionsList) ? item.reactionsList.length : 0;
|
|
173
173
|
const avatar = authorAvatar(item, profiles);
|
|
174
174
|
// A Ricos post's plain text is the whole document's (every node level), not feedText's one-level read.
|
|
175
|
-
const
|
|
175
|
+
const fallback = item.content == null ? copyParam("feed.sharedUpdateFallback", "Shared an update.") : feedText(item);
|
|
176
|
+
const { bodyJson, bodyText: text } = descriptionBody(item.content || item.entity?.body?.content, fallback, ROW_IMAGE_WIDTH);
|
|
176
177
|
// A Ricos post draws its own images in RichContent, so the separate row image would repeat the first one.
|
|
177
178
|
const image = bodyJson ? "" : contentImage(item);
|
|
178
179
|
return {
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { defineService } from "@himalaya/state";
|
|
2
|
+
import { getGroup, listRules, type WixGroup, type WixGroupRule } from "@himalaya/wix-groups";
|
|
3
|
+
import { WIX_DETAIL_IMAGE } from "@himalaya/wix-media";
|
|
4
|
+
import { copyParam, stashCopy } from "../_shared/himi-copy.js";
|
|
5
|
+
import { groupImage } from "../_shared/himi-media.js";
|
|
6
|
+
import { descriptionBody } from "../_shared/rich-body.js";
|
|
7
|
+
import { openRichLink } from "../_shared/rich-content-link.js";
|
|
8
|
+
import { formatDateMedium } from "../_shared/engine-intl.js";
|
|
9
|
+
|
|
10
|
+
// The About & rules page of ONE group. The group is `input.id`, never a fallback: a missing
|
|
11
|
+
// id is an error, not "the first group". The group read and the rules read are independent —
|
|
12
|
+
// the page renders the group's own facts when only the rules read failed, and says so in the
|
|
13
|
+
// rules region, so a rules outage is never shown as "this group has no rules".
|
|
14
|
+
//
|
|
15
|
+
// Read-only and online-only: no write exists on this page, so there is nothing to queue.
|
|
16
|
+
|
|
17
|
+
interface RuleRow {
|
|
18
|
+
id: string;
|
|
19
|
+
number: string;
|
|
20
|
+
title: string;
|
|
21
|
+
description: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
type Failure = "forbidden" | "missing" | "failed";
|
|
25
|
+
|
|
26
|
+
interface State {
|
|
27
|
+
status: "loading" | "ready" | "error";
|
|
28
|
+
id: string;
|
|
29
|
+
name: string;
|
|
30
|
+
description: string;
|
|
31
|
+
descriptionJson: string;
|
|
32
|
+
hasDescriptionJson: boolean;
|
|
33
|
+
privacy: string;
|
|
34
|
+
createdDate: string;
|
|
35
|
+
memberCount: string;
|
|
36
|
+
image: string;
|
|
37
|
+
initial: string;
|
|
38
|
+
rules: RuleRow[];
|
|
39
|
+
/** The group read failed: the whole page says why. */
|
|
40
|
+
errorText: string;
|
|
41
|
+
/** The rules read alone failed: only the rules region says why. */
|
|
42
|
+
rulesErrorText: string;
|
|
43
|
+
hideLoading: boolean;
|
|
44
|
+
hideError: boolean;
|
|
45
|
+
hideContent: boolean;
|
|
46
|
+
hideImage: boolean;
|
|
47
|
+
hidePrivacy: boolean;
|
|
48
|
+
hideCreated: boolean;
|
|
49
|
+
hideMemberCount: boolean;
|
|
50
|
+
hideRules: boolean;
|
|
51
|
+
hideRulesEmpty: boolean;
|
|
52
|
+
hideRulesError: boolean;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const initial: State = {
|
|
56
|
+
status: "loading",
|
|
57
|
+
id: "",
|
|
58
|
+
name: "",
|
|
59
|
+
description: "",
|
|
60
|
+
descriptionJson: "",
|
|
61
|
+
hasDescriptionJson: false,
|
|
62
|
+
privacy: "",
|
|
63
|
+
createdDate: "",
|
|
64
|
+
memberCount: "",
|
|
65
|
+
image: "",
|
|
66
|
+
initial: "G",
|
|
67
|
+
rules: [],
|
|
68
|
+
errorText: "",
|
|
69
|
+
rulesErrorText: "",
|
|
70
|
+
hideLoading: false,
|
|
71
|
+
hideError: true,
|
|
72
|
+
hideContent: true,
|
|
73
|
+
hideImage: true,
|
|
74
|
+
hidePrivacy: true,
|
|
75
|
+
hideCreated: true,
|
|
76
|
+
hideMemberCount: true,
|
|
77
|
+
hideRules: true,
|
|
78
|
+
hideRulesEmpty: true,
|
|
79
|
+
hideRulesError: true,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/** Bumped by every group load, so a response that arrives after a newer load began is dropped. */
|
|
83
|
+
let loadSeq = 0;
|
|
84
|
+
|
|
85
|
+
function inputId(): string {
|
|
86
|
+
// `himalaya.input.id` is the contract: hosts thread the opener's `id` nav param
|
|
87
|
+
// (himi.open { screenId: "himi_group_about", id }) into worker input.
|
|
88
|
+
const input = (globalThis as { himalaya?: { input?: { id?: string } } }).himalaya?.input;
|
|
89
|
+
return typeof input?.id === "string" ? input.id : "";
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function failureOf(e: unknown): Failure {
|
|
93
|
+
const message = String((e as Error)?.message ?? e);
|
|
94
|
+
if (/HTTP (401|403)\b/.test(message)) return "forbidden";
|
|
95
|
+
if (/HTTP 404\b/.test(message)) return "missing";
|
|
96
|
+
return "failed";
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function groupFailureText(failure: Failure): string {
|
|
100
|
+
if (failure === "forbidden") return copyParam("groupAbout.forbiddenTitle", "You can’t view this group’s details.");
|
|
101
|
+
if (failure === "missing") return copyParam("groupAbout.missingTitle", "This group no longer exists.");
|
|
102
|
+
return copyParam("groupAbout.errorTitle", "This group’s details could not be loaded.");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function rulesFailureText(failure: Failure): string {
|
|
106
|
+
if (failure === "forbidden") return copyParam("groupAbout.rulesForbidden", "You can’t view this group’s rules.");
|
|
107
|
+
return copyParam("groupAbout.rulesError", "The rules could not be loaded.");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** The rules the server sent, numbered from 1. `undefined` when the answer is not a rules list
|
|
111
|
+
* at all — a missing or non-array `rules` is a malformed answer, not an empty group. */
|
|
112
|
+
function ruleRowsOf(response: unknown): RuleRow[] | undefined {
|
|
113
|
+
const rules = (response as { rules?: unknown } | null | undefined)?.rules;
|
|
114
|
+
if (!Array.isArray(rules)) return undefined;
|
|
115
|
+
const rows: RuleRow[] = [];
|
|
116
|
+
for (const entry of rules as WixGroupRule[]) {
|
|
117
|
+
const title = typeof entry?.title === "string" ? entry.title.trim() : "";
|
|
118
|
+
const description = typeof entry?.description === "string" ? entry.description.trim() : "";
|
|
119
|
+
if (!title && !description) continue;
|
|
120
|
+
rows.push({ id: `rule_${rows.length + 1}`, number: String(rows.length + 1), title, description });
|
|
121
|
+
}
|
|
122
|
+
return rows;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function withRules(state: State, rules: RuleRow[] | undefined, failure: Failure | undefined): State {
|
|
126
|
+
if (failure || !rules) {
|
|
127
|
+
return {
|
|
128
|
+
...state,
|
|
129
|
+
rules: [],
|
|
130
|
+
rulesErrorText: rulesFailureText(failure ?? "failed"),
|
|
131
|
+
hideRules: true,
|
|
132
|
+
hideRulesEmpty: true,
|
|
133
|
+
hideRulesError: false,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
...state,
|
|
138
|
+
rules,
|
|
139
|
+
rulesErrorText: "",
|
|
140
|
+
hideRules: rules.length === 0,
|
|
141
|
+
hideRulesEmpty: rules.length > 0,
|
|
142
|
+
hideRulesError: true,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function capitalized(value: string): string {
|
|
147
|
+
const lower = value.trim().toLowerCase();
|
|
148
|
+
return lower ? lower[0].toUpperCase() + lower.slice(1) : "";
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function createdLabel(value: unknown): string {
|
|
152
|
+
if (typeof value !== "string" || !value) return "";
|
|
153
|
+
const date = new Date(value);
|
|
154
|
+
return Number.isNaN(date.getTime()) ? "" : formatDateMedium(date);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function groupState(state: State, id: string, group: WixGroup): State {
|
|
158
|
+
const name = (group.name || (group as { title?: string }).title || copyParam("group.groupFallbackName", "Group")).trim();
|
|
159
|
+
const body = descriptionBody(group.description, descriptionBody(group.teaser, "").bodyText);
|
|
160
|
+
const privacy = capitalized(group.privacyStatus ?? "");
|
|
161
|
+
const created = createdLabel(group.createdDate);
|
|
162
|
+
const count = group.membersCount ?? group.memberCount;
|
|
163
|
+
const image = groupImage(group, WIX_DETAIL_IMAGE);
|
|
164
|
+
return {
|
|
165
|
+
...state,
|
|
166
|
+
status: "ready",
|
|
167
|
+
id,
|
|
168
|
+
name,
|
|
169
|
+
description: body.bodyText,
|
|
170
|
+
descriptionJson: body.bodyJson,
|
|
171
|
+
hasDescriptionJson: body.bodyJson !== "",
|
|
172
|
+
privacy,
|
|
173
|
+
createdDate: created,
|
|
174
|
+
memberCount: typeof count === "number" ? `${count} ${count === 1 ? "member" : "members"}` : "",
|
|
175
|
+
image,
|
|
176
|
+
initial: (name[0] || "G").toUpperCase(),
|
|
177
|
+
errorText: "",
|
|
178
|
+
hideLoading: true,
|
|
179
|
+
hideError: true,
|
|
180
|
+
hideContent: false,
|
|
181
|
+
hideImage: !image,
|
|
182
|
+
hidePrivacy: !privacy,
|
|
183
|
+
hideCreated: !created,
|
|
184
|
+
hideMemberCount: typeof count !== "number",
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function groupErrorState(id: string, failure: Failure): State {
|
|
189
|
+
return {
|
|
190
|
+
...initial,
|
|
191
|
+
id,
|
|
192
|
+
status: "error",
|
|
193
|
+
errorText: groupFailureText(failure),
|
|
194
|
+
hideLoading: true,
|
|
195
|
+
hideError: false,
|
|
196
|
+
hideContent: true,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const svc = defineService<State, {
|
|
201
|
+
load: (state: State, arg?: unknown) => Promise<State | undefined>;
|
|
202
|
+
reloadRules: (state: State, arg?: unknown) => Promise<State | undefined>;
|
|
203
|
+
openLink: (state: State, arg?: unknown) => Promise<undefined>;
|
|
204
|
+
}>("himiBrandedGroupAbout", {
|
|
205
|
+
state: initial,
|
|
206
|
+
actions: {
|
|
207
|
+
load: async (state, arg) => {
|
|
208
|
+
stashCopy(arg);
|
|
209
|
+
const seq = ++loadSeq;
|
|
210
|
+
const id = inputId();
|
|
211
|
+
if (!id) return groupErrorState("", "missing");
|
|
212
|
+
const [groupResult, rulesResult] = await Promise.allSettled([getGroup(id), listRules(id)]);
|
|
213
|
+
// A newer load began while these reads were in flight: its answer owns the state.
|
|
214
|
+
if (seq !== loadSeq) return undefined;
|
|
215
|
+
if (groupResult.status === "rejected") return groupErrorState(id, failureOf(groupResult.reason));
|
|
216
|
+
// Social Groups wraps the entity as `{ group }`; the typed signature claims a bare group.
|
|
217
|
+
const group = ((groupResult.value as unknown as { group?: WixGroup }).group ?? groupResult.value) as WixGroup;
|
|
218
|
+
if (!group || typeof group !== "object" || (group.id && group.id !== id)) return groupErrorState(id, "failed");
|
|
219
|
+
const ready = groupState(state, id, group);
|
|
220
|
+
return rulesResult.status === "rejected"
|
|
221
|
+
? withRules(ready, undefined, failureOf(rulesResult.reason))
|
|
222
|
+
: withRules(ready, ruleRowsOf(rulesResult.value), undefined);
|
|
223
|
+
},
|
|
224
|
+
// Retry for the rules region alone: the group's own facts stay on screen.
|
|
225
|
+
reloadRules: async (state, arg) => {
|
|
226
|
+
stashCopy(arg);
|
|
227
|
+
const id = state.id;
|
|
228
|
+
if (!id) return undefined;
|
|
229
|
+
const seq = loadSeq;
|
|
230
|
+
try {
|
|
231
|
+
const rules = ruleRowsOf(await listRules(id));
|
|
232
|
+
if (seq !== loadSeq || inputId() !== id) return undefined;
|
|
233
|
+
return withRules(state, rules, undefined);
|
|
234
|
+
} catch (e) {
|
|
235
|
+
if (seq !== loadSeq || inputId() !== id) return undefined;
|
|
236
|
+
return withRules(state, undefined, failureOf(e));
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
openLink: async (_state, arg) => { await openRichLink(arg); return undefined; },
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
export { svc };
|