@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.
Files changed (304) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/blog-v3/src/extensions.d.ts +2 -0
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/comments-v1/src/extensions.d.ts +2 -0
  3. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/events/src/index.d.ts +3 -0
  4. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/groups/src/extensions.d.ts +15 -0
  5. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/loyalty/src/extensions.d.ts +13 -0
  6. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/members-v1/src/index.d.ts +19 -4
  7. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-comments.mjs +3 -0
  8. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-events.mjs +4 -2
  9. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-groups.mjs +5 -0
  10. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-loyalty.mjs +4 -0
  11. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-members.mjs +10 -3
  12. package/dist/cli.mjs +247 -186
  13. package/dist/preview-assets.generated.bin +0 -0
  14. package/fixtures/wix-canned.json +25 -0
  15. package/guides/one-app-to-himi-app.md +52 -1
  16. package/guides/sdk-reference.json +37 -9
  17. package/guides/wix-api-usage.md +9 -0
  18. package/package.json +2 -2
  19. package/templates/beauty-salon/config.ts +7 -0
  20. package/templates/beauty-salon/dev/index.ts +758 -352
  21. package/templates/beauty-salon/himalaya.content.json +3 -0
  22. package/templates/beauty-salon/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  23. package/templates/beauty-salon/tier5-src/_shared/collection-contract.ts +19 -3
  24. package/templates/beauty-salon/tier5-src/_shared/himi-restaurants.ts +2 -1
  25. package/templates/beauty-salon/tier5-src/_shared/owner-editor-value.ts +164 -0
  26. package/templates/beauty-salon/tier5-src/_shared/store-product-info.ts +50 -0
  27. package/templates/beauty-salon/tier5-src/himi-blog-post/index.ts +21 -8
  28. package/templates/beauty-salon/tier5-src/himi-branded-lite-category/index.ts +3 -2
  29. package/templates/beauty-salon/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  30. package/templates/beauty-salon/tier5-src/himi-branded-lite-plans/index.ts +2 -1
  31. package/templates/beauty-salon/tier5-src/himi-store-product/index.ts +14 -3
  32. package/templates/beauty-salon/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
  33. package/templates/beauty-salon/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  34. package/templates/beauty-salon-owner/dev/index.ts +129 -66
  35. package/templates/beauty-salon-owner/tier5-src/_shared/collection-contract.ts +19 -3
  36. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  37. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
  38. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  39. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
  40. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
  41. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
  42. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
  43. package/templates/branded-lite/config.ts +9 -0
  44. package/templates/branded-lite/dev/index.ts +4988 -3304
  45. package/templates/branded-lite/himalaya.content.json +3 -0
  46. package/templates/branded-lite/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  47. package/templates/branded-lite/tier5-src/_shared/collection-contract.ts +19 -3
  48. package/templates/branded-lite/tier5-src/_shared/event-registration.ts +86 -0
  49. package/templates/branded-lite/tier5-src/_shared/himi-restaurants.ts +2 -1
  50. package/templates/branded-lite/tier5-src/_shared/owner-editor-value.ts +164 -0
  51. package/templates/branded-lite/tier5-src/_shared/store-product-info.ts +50 -0
  52. package/templates/branded-lite/tier5-src/himi-blog-post/index.ts +21 -8
  53. package/templates/branded-lite/tier5-src/himi-branded-lite-badges/index.ts +2 -1
  54. package/templates/branded-lite/tier5-src/himi-branded-lite-category/index.ts +3 -2
  55. package/templates/branded-lite/tier5-src/himi-branded-lite-feed/index.ts +4 -3
  56. package/templates/branded-lite/tier5-src/himi-branded-lite-group-about/index.ts +243 -0
  57. package/templates/branded-lite/tier5-src/himi-branded-lite-loyalty/index.ts +115 -49
  58. package/templates/branded-lite/tier5-src/himi-branded-lite-plans/index.ts +2 -1
  59. package/templates/branded-lite/tier5-src/himi-branded-lite-podcasts/index.ts +2 -1
  60. package/templates/branded-lite/tier5-src/himi-branded-lite-portfolio/index.ts +2 -1
  61. package/templates/branded-lite/tier5-src/himi-branded-lite-portfolio-project/index.ts +3 -2
  62. package/templates/branded-lite/tier5-src/himi-branded-lite-program/facts.ts +52 -0
  63. package/templates/branded-lite/tier5-src/himi-branded-lite-program/index.ts +21 -11
  64. package/templates/branded-lite/tier5-src/himi-branded-lite-video/index.ts +2 -1
  65. package/templates/branded-lite/tier5-src/himi-events-detail/index.ts +12 -86
  66. package/templates/branded-lite/tier5-src/himi-events-list/index.ts +47 -6
  67. package/templates/branded-lite/tier5-src/himi-store-product/index.ts +13 -3
  68. package/templates/branded-lite/tier5-src/wix-spaces-forum-post/index.ts +3 -2
  69. package/templates/branded-lite/tier5-src/wix-spaces-member-detail/index.ts +53 -37
  70. package/templates/branded-lite/tier5-src/wix-spaces-members/index.ts +100 -34
  71. package/templates/branded-lite/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
  72. package/templates/branded-lite/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  73. package/templates/community-club/config.ts +16 -0
  74. package/templates/community-club/dev/index.ts +1930 -401
  75. package/templates/community-club/himalaya.content.json +6 -0
  76. package/templates/community-club/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  77. package/templates/community-club/tier5-src/_shared/collection-contract.ts +19 -3
  78. package/templates/community-club/tier5-src/_shared/event-registration.ts +86 -0
  79. package/templates/community-club/tier5-src/_shared/himi-restaurants.ts +2 -1
  80. package/templates/community-club/tier5-src/himi-blog-post/index.ts +21 -8
  81. package/templates/community-club/tier5-src/himi-branded-lite-feed/index.ts +4 -3
  82. package/templates/community-club/tier5-src/himi-branded-lite-group-about/index.ts +243 -0
  83. package/templates/community-club/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  84. package/templates/community-club/tier5-src/himi-events-detail/index.ts +12 -86
  85. package/templates/community-club/tier5-src/himi-events-list/index.ts +47 -6
  86. package/templates/community-club/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  87. package/templates/community-club-owner/dev/index.ts +53 -1
  88. package/templates/community-club-owner/tier5-src/_shared/collection-contract.ts +19 -3
  89. package/templates/community-club-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
  90. package/templates/community-club-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  91. package/templates/community-club-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
  92. package/templates/community-club-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  93. package/templates/community-club-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
  94. package/templates/community-club-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  95. package/templates/community-club-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
  96. package/templates/events-meetups/config.ts +16 -0
  97. package/templates/events-meetups/dev/index.ts +1930 -401
  98. package/templates/events-meetups/himalaya.content.json +6 -0
  99. package/templates/events-meetups/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  100. package/templates/events-meetups/tier5-src/_shared/collection-contract.ts +19 -3
  101. package/templates/events-meetups/tier5-src/_shared/event-registration.ts +86 -0
  102. package/templates/events-meetups/tier5-src/_shared/himi-restaurants.ts +2 -1
  103. package/templates/events-meetups/tier5-src/himi-blog-post/index.ts +21 -8
  104. package/templates/events-meetups/tier5-src/himi-branded-lite-feed/index.ts +4 -3
  105. package/templates/events-meetups/tier5-src/himi-branded-lite-group-about/index.ts +243 -0
  106. package/templates/events-meetups/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  107. package/templates/events-meetups/tier5-src/himi-events-detail/index.ts +12 -86
  108. package/templates/events-meetups/tier5-src/himi-events-list/index.ts +47 -6
  109. package/templates/events-meetups/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  110. package/templates/events-meetups-owner/dev/index.ts +6 -0
  111. package/templates/events-meetups-owner/tier5-src/_shared/collection-contract.ts +19 -3
  112. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
  113. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  114. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
  115. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  116. package/templates/gym-fitness/config.ts +7 -0
  117. package/templates/gym-fitness/dev/index.ts +1115 -278
  118. package/templates/gym-fitness/himalaya.content.json +3 -0
  119. package/templates/gym-fitness/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  120. package/templates/gym-fitness/tier5-src/_shared/collection-contract.ts +19 -3
  121. package/templates/gym-fitness/tier5-src/_shared/event-registration.ts +86 -0
  122. package/templates/gym-fitness/tier5-src/_shared/himi-restaurants.ts +2 -1
  123. package/templates/gym-fitness/tier5-src/_shared/owner-editor-value.ts +164 -0
  124. package/templates/gym-fitness/tier5-src/_shared/store-product-info.ts +50 -0
  125. package/templates/gym-fitness/tier5-src/himi-blog-post/index.ts +21 -8
  126. package/templates/gym-fitness/tier5-src/himi-branded-lite-category/index.ts +3 -2
  127. package/templates/gym-fitness/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  128. package/templates/gym-fitness/tier5-src/himi-branded-lite-plans/index.ts +2 -1
  129. package/templates/gym-fitness/tier5-src/himi-events-detail/index.ts +12 -86
  130. package/templates/gym-fitness/tier5-src/himi-events-list/index.ts +47 -6
  131. package/templates/gym-fitness/tier5-src/himi-store-product/index.ts +14 -3
  132. package/templates/gym-fitness/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
  133. package/templates/gym-fitness/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  134. package/templates/gym-fitness-owner/dev/index.ts +132 -66
  135. package/templates/gym-fitness-owner/tier5-src/_shared/collection-contract.ts +19 -3
  136. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
  137. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  138. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
  139. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  140. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
  141. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  142. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
  143. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
  144. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
  145. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
  146. package/templates/nonprofit-cause/config.ts +16 -0
  147. package/templates/nonprofit-cause/dev/index.ts +1925 -396
  148. package/templates/nonprofit-cause/himalaya.content.json +6 -0
  149. package/templates/nonprofit-cause/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  150. package/templates/nonprofit-cause/tier5-src/_shared/collection-contract.ts +19 -3
  151. package/templates/nonprofit-cause/tier5-src/_shared/event-registration.ts +86 -0
  152. package/templates/nonprofit-cause/tier5-src/_shared/himi-restaurants.ts +2 -1
  153. package/templates/nonprofit-cause/tier5-src/himi-blog-post/index.ts +21 -8
  154. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-feed/index.ts +4 -3
  155. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-group-about/index.ts +243 -0
  156. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  157. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-plans/index.ts +2 -1
  158. package/templates/nonprofit-cause/tier5-src/himi-events-detail/index.ts +12 -86
  159. package/templates/nonprofit-cause/tier5-src/himi-events-list/index.ts +47 -6
  160. package/templates/nonprofit-cause/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
  161. package/templates/nonprofit-cause/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  162. package/templates/nonprofit-cause-owner/dev/index.ts +85 -65
  163. package/templates/nonprofit-cause-owner/tier5-src/_shared/collection-contract.ts +19 -3
  164. package/templates/nonprofit-cause-owner/tier5-src/_shared/owner-rich-guard.ts +27 -0
  165. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
  166. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  167. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
  168. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  169. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
  170. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
  171. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
  172. package/templates/owner-lite/dev/index.ts +132 -66
  173. package/templates/owner-lite/tier5-src/_shared/collection-contract.ts +19 -3
  174. package/templates/owner-lite/tier5-src/himi-owner-lite-event/index.ts +16 -8
  175. package/templates/owner-lite/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  176. package/templates/owner-lite/tier5-src/himi-owner-lite-events/index.ts +4 -2
  177. package/templates/owner-lite/tier5-src/himi-owner-lite-post/index.ts +7 -0
  178. package/templates/owner-lite/tier5-src/himi-owner-lite-product/index.ts +39 -6
  179. package/templates/owner-lite/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  180. package/templates/owner-lite/tier5-src/himi-owner-lite-products/index.ts +7 -4
  181. package/templates/owner-lite/tier5-src/himi-owner-lite-service/index.ts +38 -7
  182. package/templates/owner-lite/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
  183. package/templates/owner-lite/tier5-src/himi-owner-lite-services/index.ts +4 -2
  184. package/templates/podcast-creator/config.ts +7 -0
  185. package/templates/podcast-creator/dev/index.ts +1051 -158
  186. package/templates/podcast-creator/himalaya.content.json +3 -0
  187. package/templates/podcast-creator/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  188. package/templates/podcast-creator/tier5-src/_shared/collection-contract.ts +19 -3
  189. package/templates/podcast-creator/tier5-src/_shared/event-registration.ts +86 -0
  190. package/templates/podcast-creator/tier5-src/_shared/himi-restaurants.ts +2 -1
  191. package/templates/podcast-creator/tier5-src/himi-blog-post/index.ts +21 -8
  192. package/templates/podcast-creator/tier5-src/himi-branded-lite-feed/index.ts +4 -3
  193. package/templates/podcast-creator/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  194. package/templates/podcast-creator/tier5-src/himi-branded-lite-plans/index.ts +2 -1
  195. package/templates/podcast-creator/tier5-src/himi-branded-lite-podcasts/index.ts +2 -1
  196. package/templates/podcast-creator/tier5-src/himi-events-detail/index.ts +12 -86
  197. package/templates/podcast-creator/tier5-src/himi-events-list/index.ts +47 -6
  198. package/templates/podcast-creator/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
  199. package/templates/podcast-creator/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  200. package/templates/podcast-creator-owner/dev/index.ts +53 -1
  201. package/templates/podcast-creator-owner/tier5-src/_shared/collection-contract.ts +19 -3
  202. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
  203. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  204. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
  205. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  206. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
  207. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  208. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
  209. package/templates/portfolio-creative/config.ts +7 -0
  210. package/templates/portfolio-creative/dev/index.ts +1305 -468
  211. package/templates/portfolio-creative/himalaya.content.json +3 -0
  212. package/templates/portfolio-creative/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  213. package/templates/portfolio-creative/tier5-src/_shared/collection-contract.ts +19 -3
  214. package/templates/portfolio-creative/tier5-src/_shared/event-registration.ts +86 -0
  215. package/templates/portfolio-creative/tier5-src/_shared/himi-restaurants.ts +2 -1
  216. package/templates/portfolio-creative/tier5-src/_shared/owner-editor-value.ts +164 -0
  217. package/templates/portfolio-creative/tier5-src/_shared/store-product-info.ts +50 -0
  218. package/templates/portfolio-creative/tier5-src/himi-blog-post/index.ts +21 -8
  219. package/templates/portfolio-creative/tier5-src/himi-branded-lite-category/index.ts +3 -2
  220. package/templates/portfolio-creative/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  221. package/templates/portfolio-creative/tier5-src/himi-branded-lite-portfolio/index.ts +2 -1
  222. package/templates/portfolio-creative/tier5-src/himi-branded-lite-portfolio-project/index.ts +3 -2
  223. package/templates/portfolio-creative/tier5-src/himi-events-detail/index.ts +12 -86
  224. package/templates/portfolio-creative/tier5-src/himi-events-list/index.ts +47 -6
  225. package/templates/portfolio-creative/tier5-src/himi-store-product/index.ts +14 -3
  226. package/templates/portfolio-creative/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  227. package/templates/portfolio-creative-owner/dev/index.ts +129 -66
  228. package/templates/portfolio-creative-owner/tier5-src/_shared/collection-contract.ts +19 -3
  229. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  230. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
  231. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  232. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
  233. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
  234. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
  235. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
  236. package/templates/restaurant/config.ts +7 -0
  237. package/templates/restaurant/dev/index.ts +1051 -158
  238. package/templates/restaurant/himalaya.content.json +3 -0
  239. package/templates/restaurant/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  240. package/templates/restaurant/tier5-src/_shared/collection-contract.ts +19 -3
  241. package/templates/restaurant/tier5-src/_shared/event-registration.ts +86 -0
  242. package/templates/restaurant/tier5-src/_shared/himi-restaurants.ts +2 -1
  243. package/templates/restaurant/tier5-src/himi-blog-post/index.ts +21 -8
  244. package/templates/restaurant/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  245. package/templates/restaurant/tier5-src/himi-events-detail/index.ts +12 -86
  246. package/templates/restaurant/tier5-src/himi-events-list/index.ts +47 -6
  247. package/templates/restaurant/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  248. package/templates/restaurant-owner/dev/index.ts +132 -66
  249. package/templates/restaurant-owner/tier5-src/_shared/collection-contract.ts +19 -3
  250. package/templates/restaurant-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
  251. package/templates/restaurant-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  252. package/templates/restaurant-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
  253. package/templates/restaurant-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  254. package/templates/restaurant-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
  255. package/templates/restaurant-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  256. package/templates/restaurant-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
  257. package/templates/restaurant-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
  258. package/templates/restaurant-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
  259. package/templates/restaurant-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
  260. package/templates/retail-store/config.ts +7 -0
  261. package/templates/retail-store/dev/index.ts +597 -191
  262. package/templates/retail-store/himalaya.content.json +3 -0
  263. package/templates/retail-store/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  264. package/templates/retail-store/tier5-src/_shared/collection-contract.ts +19 -3
  265. package/templates/retail-store/tier5-src/_shared/himi-restaurants.ts +2 -1
  266. package/templates/retail-store/tier5-src/_shared/owner-editor-value.ts +164 -0
  267. package/templates/retail-store/tier5-src/_shared/store-product-info.ts +50 -0
  268. package/templates/retail-store/tier5-src/himi-blog-post/index.ts +21 -8
  269. package/templates/retail-store/tier5-src/himi-branded-lite-category/index.ts +3 -2
  270. package/templates/retail-store/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  271. package/templates/retail-store/tier5-src/himi-store-product/index.ts +13 -3
  272. package/templates/retail-store/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  273. package/templates/retail-store-owner/dev/index.ts +50 -1
  274. package/templates/retail-store-owner/tier5-src/_shared/collection-contract.ts +19 -3
  275. package/templates/retail-store-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  276. package/templates/retail-store-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
  277. package/templates/retail-store-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  278. package/templates/retail-store-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
  279. package/templates/yoga-studio/config.ts +7 -0
  280. package/templates/yoga-studio/dev/index.ts +1107 -110
  281. package/templates/yoga-studio/himalaya.content.json +3 -0
  282. package/templates/yoga-studio/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  283. package/templates/yoga-studio/tier5-src/_shared/collection-contract.ts +19 -3
  284. package/templates/yoga-studio/tier5-src/_shared/event-registration.ts +86 -0
  285. package/templates/yoga-studio/tier5-src/_shared/himi-restaurants.ts +2 -1
  286. package/templates/yoga-studio/tier5-src/himi-blog-post/index.ts +21 -8
  287. package/templates/yoga-studio/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  288. package/templates/yoga-studio/tier5-src/himi-branded-lite-plans/index.ts +2 -1
  289. package/templates/yoga-studio/tier5-src/himi-branded-lite-program/facts.ts +52 -0
  290. package/templates/yoga-studio/tier5-src/himi-branded-lite-program/index.ts +21 -11
  291. package/templates/yoga-studio/tier5-src/himi-events-detail/index.ts +12 -86
  292. package/templates/yoga-studio/tier5-src/himi-events-list/index.ts +47 -6
  293. package/templates/yoga-studio/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
  294. package/templates/yoga-studio/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  295. package/templates/yoga-studio-owner/dev/index.ts +85 -65
  296. package/templates/yoga-studio-owner/tier5-src/_shared/collection-contract.ts +19 -3
  297. package/templates/yoga-studio-owner/tier5-src/_shared/owner-rich-guard.ts +27 -0
  298. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
  299. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  300. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
  301. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  302. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
  303. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
  304. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
@@ -52,6 +52,9 @@
52
52
  "himi_my_subscriptions": {
53
53
  "displayName": "My Subscriptions"
54
54
  },
55
+ "himi_notif_settings": {
56
+ "displayName": "Notif Settings"
57
+ },
55
58
  "himi_place_summary": {
56
59
  "displayName": "Place Summary"
57
60
  },
@@ -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 = (typeof value === "string" ? value : "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
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 = WixPost & {
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 [postResult, commentsResult] = await Promise.allSettled([getPost(id, {}, { preserveAccessErrors: true }), listBlogComments(id)]);
298
- if (postResult.status === "rejected") throw postResult.reason;
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.id, resourceId: s.id, content: { richContent: commentRichContent(text) } },
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 { richBody, ROW_IMAGE_WIDTH } from "../_shared/rich-body.js";
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 typeof lean === "string" && lean.trim() ? lean.trim() : copyParam("feed.sharedUpdateFallback", "Shared an update.");
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 { bodyJson, bodyText: text } = richBody(item.content, feedText(item), ROW_IMAGE_WIDTH);
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,173 @@
1
+ import { withOnlineWrites } from "../_shared/online-actions.js";
2
+ // himi-branded-lite-notifsettings — reads the site's notification topics + the member's preferences.
3
+ // a failed query is an error with Retry (or Sign in when Wix refuses the
4
+ // visitor) and an empty one is a real empty state — never sample rows presented as
5
+ // the customer's own. Samples appear only in the host's explicit sample preview
6
+ // (_shared/branded-load-state.ts).
7
+ // any exist. INTERACTIVE: tapping a row toggles that topic On/Off — optimistic +
8
+ // best-effort upsertMyNotificationPreferences (a no-op on sample rows).
9
+ import { defineService as registerService } from "@himalaya/state";
10
+ import { withEmptyFlags } from "../_shared/empty-flags.js";
11
+ import { stashCopy } from "../_shared/himi-copy.js";
12
+ import { memberAccess, assertMemberAccess, type MemberAccess } from "../_shared/member-access.js";
13
+ import { LOADING, loaded, loadFailed, type LoadCopy, type LoadFlags } from "../_shared/branded-load-state.js";
14
+ import {
15
+ getNotificationTopics,
16
+ getMyNotificationPreferences,
17
+ upsertMyNotificationPreferences,
18
+ muteStatusFor,
19
+ readTopics,
20
+ readPreferences,
21
+ isDeliveredNow,
22
+ isLocked,
23
+ preferenceFor,
24
+ type NotificationTopicDefinition,
25
+ type PreferenceInfo,
26
+ } from "@himalaya/wix-notification-preferences";
27
+
28
+ /** The channel this surface delivers on — read for the row state, written on toggle.
29
+ * One constant, so the read and the write cannot drift apart. */
30
+ const CHANNEL = "MOBILE";
31
+
32
+ /** `note` is the line under a row's switch — only a locked topic has one. */
33
+ interface Row { id: string; name: string; subtitle: string; note: string; on: boolean; locked: boolean;
34
+ /** Inverse of `locked` (hiddenWhen hides on TRUE) and the plain On/Off a locked row shows. */
35
+ notLocked: boolean; stateLabel: string }
36
+ interface S extends LoadFlags {
37
+ preferencesMember: MemberAccess | null;
38
+ topics: Row[]; emptyText: string; actionMessage: string;
39
+ isMock: boolean; notMock: boolean; /** #2849 — hiddenWhen() hides on TRUE, so these are inverted: the empty-state banner
40
+ * shows only when the site really has none, and the unavailable one only when the
41
+ * query failed. A failed query must never render the sentence that claims the
42
+ * customer has no content. */
43
+ notMockEmpty: boolean; notMockUnavailable: boolean;
44
+ }
45
+
46
+ /** Whether this topic is actually being DELIVERED — not merely what the recipient
47
+ * asked for. `isDeliveredNow` prefers the server's `granted` verdict, so a topic the
48
+ * member unmuted under a channel-wide mute renders OFF instead of promising a
49
+ * notification that will never arrive. No preference row at all ⇒ the topic's default,
50
+ * which is delivery. */
51
+ function isOn(topicId: string, prefs: PreferenceInfo[]): boolean {
52
+ const p = preferenceFor(topicId, prefs, CHANNEL);
53
+ if (!p) return true;
54
+ return isDeliveredNow(p.stateData);
55
+ }
56
+
57
+ /** A row the server says cannot be changed here (a broader rule overrides it). */
58
+ function locked(topicId: string, prefs: PreferenceInfo[]): boolean {
59
+ return isLocked(preferenceFor(topicId, prefs, CHANNEL)?.stateData);
60
+ }
61
+
62
+ /** Row subtitle doubles as an affordance: current state + how to change it. */
63
+ function subtitleFor(on: boolean): string {
64
+ return on ? "On · tap to mute" : "Off · tap to enable";
65
+ }
66
+
67
+ function row(id: string, name: string, on: boolean): Row {
68
+ return { id, name, subtitle: subtitleFor(on), note: "", on, locked: false, notLocked: true, stateLabel: on ? "On" : "Off" };
69
+ }
70
+
71
+ function norm(t: NotificationTopicDefinition, prefs: PreferenceInfo[]): Row {
72
+ const name = (t.displayName || "Notification").trim() || "Notification";
73
+ const id = t.topicId || name;
74
+ const r = row(id, name, isOn(id, prefs));
75
+ // An overridden row says so instead of offering a toggle that cannot take effect.
76
+ return locked(id, prefs)
77
+ ? { ...r, locked: true, notLocked: false, subtitle: "Managed by your channel settings", note: "Managed by your channel settings" }
78
+ : r;
79
+ }
80
+
81
+ const MOCK: Row[] = [
82
+ row("sample-orders", "Order updates", true),
83
+ row("sample-bookings", "Bookings & reminders", true),
84
+ row("sample-promos", "Promotions & offers", false),
85
+ row("sample-community", "Community activity", true),
86
+ ];
87
+
88
+ /** sample rows reach the screen ONLY in the host's explicit sample preview;
89
+ * a failed or empty query is an honest error / empty state (branded-load-state.ts). */
90
+ const COPY: LoadCopy<Row> = {
91
+ prefix: "notifsettings",
92
+ empty: "No notification topics to manage yet.",
93
+ error: "Notification settings couldn't be loaded.",
94
+ signIn: "Sign in to manage your notifications.",
95
+ sample: () => MOCK.map((r) => ({ ...r })),
96
+ };
97
+
98
+ const defineService = withOnlineWrites(registerService, ["toggle"], { messageFields: ["actionMessage"] });
99
+ const INITIAL = { ...LOADING, topics: [] as Row[], emptyText: "", actionMessage: "", preferencesMember: null };
100
+ const signInRequired = () => new Error("Unauthenticated: sign in to manage notifications");
101
+ let loadVersion = 0;
102
+ const svc = defineService<S, {
103
+ prepare: (s: S) => S;
104
+ load: (s: S, arg?: unknown) => Promise<S | undefined>;
105
+ toggle: (s: S, p?: { id?: string; value?: unknown }) => Promise<S | undefined>;
106
+ }>("himiBrandedNotifSettings", withEmptyFlags({
107
+ state: INITIAL,
108
+ actions: {
109
+ prepare: (s) => ({ ...s, ...INITIAL }),
110
+ load: async (s, arg): Promise<S | undefined> => {
111
+ stashCopy(arg);
112
+ const version = ++loadVersion;
113
+ await svc.prepare();
114
+ try {
115
+ const member = await memberAccess();
116
+ if (!member.email) throw signInRequired();
117
+ const [topicsRes, prefsRes] = await Promise.all([
118
+ getNotificationTopics(),
119
+ getMyNotificationPreferences(),
120
+ ]);
121
+ await assertMemberAccess(member);
122
+ const prefs = readPreferences(prefsRes);
123
+ const topics = readTopics(topicsRes).map((t) => norm(t, prefs));
124
+ return version === loadVersion ? { ...s, ...loaded("topics", topics, COPY), preferencesMember: member, actionMessage: "" } : undefined;
125
+ } catch {
126
+ const member = await memberAccess();
127
+ // An authenticated denial is not repaired by an endless sign-in loop.
128
+ const failure = member.email ? new Error(COPY.error) : signInRequired();
129
+ return version === loadVersion ? { ...s, ...loadFailed("topics", failure, COPY), preferencesMember: null, actionMessage: "" } : undefined;
130
+ }
131
+ },
132
+ // Flip one topic's On/Off. Optimistic (updates the row immediately); the real
133
+ // upsert is best-effort and a no-op on sample rows.
134
+ toggle: async (s, p): Promise<S | undefined> => {
135
+ const id = p?.id;
136
+ const target = s.topics.find((r) => r.id === id);
137
+ if (s.busy || !id || !target || target.locked) return undefined;
138
+ if (!s.isMock) {
139
+ try {
140
+ if (!s.preferencesMember) return undefined;
141
+ await assertMemberAccess(s.preferencesMember);
142
+ if (svc.getState().preferencesMember !== s.preferencesMember) return undefined;
143
+ } catch {
144
+ await svc.load();
145
+ return undefined;
146
+ }
147
+ }
148
+ let nextOn = true;
149
+ const topics = s.topics.map((r) => {
150
+ if (r.id !== id) return r;
151
+ // A Toggle sends the NEXT state as `value` (action-payload contract); honour it so
152
+ // a repeated event can never flip the row the wrong way. A bare tap still flips.
153
+ nextOn = typeof p?.value === "boolean" ? p.value : !r.on;
154
+ return { ...r, on: nextOn, subtitle: subtitleFor(nextOn), stateLabel: nextOn ? "On" : "Off" };
155
+ });
156
+ if (!s.isMock && !id.startsWith("sample-")) {
157
+ try {
158
+ // `stateData.state` is a SCALAR enum — the old nested `{ muteStatus }` was a
159
+ // shape this service does not have, so every write was malformed.
160
+ await upsertMyNotificationPreferences([
161
+ { topicId: id, channel: CHANNEL, stateData: { state: muteStatusFor(nextOn) } },
162
+ ]);
163
+ } catch { /* keep the optimistic state */ }
164
+ try { await assertMemberAccess(s.preferencesMember!); }
165
+ catch { await svc.load(); return undefined; }
166
+ }
167
+ return svc.getState().preferencesMember === s.preferencesMember ? { ...svc.getState(), topics } : undefined;
168
+ },
169
+ },
170
+ }, { topics: "noTopics" }));
171
+ svc.prepare();
172
+ svc.load();
173
+ export { svc };
@@ -1,3 +1,4 @@
1
+ import { descriptionBody } from "../_shared/rich-body.js";
1
2
  import { paymentSetupMessage } from "../_shared/payment-setup.js";
2
3
  import { offlineWriteMessage, offlineWritesEnabled, withOnlineWrites } from "../_shared/online-actions.js";
3
4
  import { connectivity } from "@himalaya/state";
@@ -140,7 +141,7 @@ function planRow(plan: WixPlan): PlanRow {
140
141
  return {
141
142
  id: plan.id || "",
142
143
  name,
143
- description: (plan.description ?? "").trim(),
144
+ description: descriptionBody(plan.description).bodyText,
144
145
  trialBadge,
145
146
  noTrialBadge: !trialBadge,
146
147
  priceLine: money(plan),
@@ -1,3 +1,4 @@
1
+ import { descriptionBody } from "../_shared/rich-body.js";
1
2
  // himi-branded-lite-podcasts — live RSS episodes with a persistent queue and
2
3
  // the shared native/web media playback transport.
3
4
  import { defineService } from "../_shared/collection-service.js";
@@ -99,7 +100,7 @@ function norm(episode: Episode): Row | null {
99
100
  title: (episode.title || "Episode").trim() || "Episode",
100
101
  subtitle: [dateLabel(episode.date), duration ? clock(duration) : ""].filter(Boolean).join(" · "),
101
102
  image: (episode.image || "").trim(),
102
- description: (episode.descriptionExcerpt || "").trim(),
103
+ description: descriptionBody(episode.descriptionExcerpt).bodyText,
103
104
  audioUrl,
104
105
  durationMs: duration,
105
106
  progressLabel: "",