@openrails/contentkit-ui 0.79.0 → 0.81.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 (295) hide show
  1. package/README.md +82 -54
  2. package/dist/client/beacons.d.ts +53 -0
  3. package/dist/client/beacons.js +72 -0
  4. package/dist/client/beacons.js.map +1 -0
  5. package/dist/client/catalog.d.ts +2 -2
  6. package/dist/client/catalog.js +1 -1
  7. package/dist/client/catalog.js.map +1 -1
  8. package/dist/client/client.d.ts +24 -11
  9. package/dist/client/client.js +37 -29
  10. package/dist/client/client.js.map +1 -1
  11. package/dist/client/content/comments.d.ts +12 -8
  12. package/dist/client/content/comments.js +33 -20
  13. package/dist/client/content/comments.js.map +1 -1
  14. package/dist/client/content/favorites.d.ts +24 -9
  15. package/dist/client/content/favorites.js +21 -22
  16. package/dist/client/content/favorites.js.map +1 -1
  17. package/dist/client/content/moderation.d.ts +1 -1
  18. package/dist/client/content/moderation.js.map +1 -1
  19. package/dist/client/content/reactions.d.ts +11 -6
  20. package/dist/client/content/reactions.js +23 -18
  21. package/dist/client/content/reactions.js.map +1 -1
  22. package/dist/client/content/types.d.ts +40 -21
  23. package/dist/client/content/types.js +5 -2
  24. package/dist/client/content/types.js.map +1 -1
  25. package/dist/client/contents.d.ts +55 -0
  26. package/dist/client/contents.js +144 -0
  27. package/dist/client/contents.js.map +1 -0
  28. package/dist/client/discovery.d.ts +126 -0
  29. package/dist/client/discovery.js +125 -0
  30. package/dist/client/discovery.js.map +1 -0
  31. package/dist/client/generated/error-codes.d.ts +1 -1
  32. package/dist/client/generated/error-codes.js +12 -0
  33. package/dist/client/generated/error-codes.js.map +1 -1
  34. package/dist/client/generated/routes.d.ts +1244 -370
  35. package/dist/client/generated/routes.js +1870 -554
  36. package/dist/client/generated/routes.js.map +1 -1
  37. package/dist/client/generated/wire.d.ts +960 -134
  38. package/dist/client/generated/wire.js.map +1 -1
  39. package/dist/client/group-fields.d.ts +25 -0
  40. package/dist/client/group-fields.js +63 -0
  41. package/dist/client/group-fields.js.map +1 -0
  42. package/dist/client/groups.d.ts +58 -0
  43. package/dist/client/groups.js +119 -0
  44. package/dist/client/groups.js.map +1 -0
  45. package/dist/client/http.d.ts +24 -12
  46. package/dist/client/http.js +20 -24
  47. package/dist/client/http.js.map +1 -1
  48. package/dist/client/index.d.ts +11 -6
  49. package/dist/client/media/api.d.ts +1 -1
  50. package/dist/client/media/api.js +4 -2
  51. package/dist/client/media/api.js.map +1 -1
  52. package/dist/client/media/client.d.ts +8 -9
  53. package/dist/client/media/client.js +58 -71
  54. package/dist/client/media/client.js.map +1 -1
  55. package/dist/client/media/ref.d.ts +30 -0
  56. package/dist/client/media/ref.js +10 -3
  57. package/dist/client/media/ref.js.map +1 -1
  58. package/dist/client/offers.d.ts +41 -0
  59. package/dist/client/offers.js +66 -0
  60. package/dist/client/offers.js.map +1 -0
  61. package/dist/client/route.d.ts +2 -0
  62. package/dist/client/route.js +2 -1
  63. package/dist/client/route.js.map +1 -1
  64. package/dist/client/versions.d.ts +20 -0
  65. package/dist/client/versions.js +53 -0
  66. package/dist/client/versions.js.map +1 -0
  67. package/dist/client.js +35 -30
  68. package/dist/components/admin/content-manager.d.ts +80 -0
  69. package/dist/components/admin/content-manager.js +591 -0
  70. package/dist/components/admin/content-manager.js.map +1 -0
  71. package/dist/components/admin/group-manager.d.ts +24 -0
  72. package/dist/components/admin/group-manager.js +244 -0
  73. package/dist/components/admin/group-manager.js.map +1 -0
  74. package/dist/components/admin/media-item-panel.js +1 -1
  75. package/dist/components/admin/media-item-panel.js.map +1 -1
  76. package/dist/components/content/card.d.ts +44 -0
  77. package/dist/components/content/card.js +285 -0
  78. package/dist/components/content/card.js.map +1 -0
  79. package/dist/components/content/editors.d.ts +40 -0
  80. package/dist/components/content/editors.js +764 -0
  81. package/dist/components/content/editors.js.map +1 -0
  82. package/dist/components/content/filters.d.ts +28 -0
  83. package/dist/components/content/filters.js +134 -0
  84. package/dist/components/content/filters.js.map +1 -0
  85. package/dist/components/content/grid.d.ts +62 -0
  86. package/dist/components/content/grid.js +233 -0
  87. package/dist/components/content/grid.js.map +1 -0
  88. package/dist/components/content/index.d.ts +8 -0
  89. package/dist/components/content/listing.d.ts +20 -0
  90. package/dist/components/content/listing.js +150 -0
  91. package/dist/components/content/listing.js.map +1 -0
  92. package/dist/components/content/offer-fields.d.ts +8 -0
  93. package/dist/components/content/offer-fields.js +127 -0
  94. package/dist/components/content/offer-fields.js.map +1 -0
  95. package/dist/components/content/paywall.d.ts +53 -0
  96. package/dist/components/content/paywall.js +221 -0
  97. package/dist/components/content/paywall.js.map +1 -0
  98. package/dist/components/content/search.d.ts +27 -0
  99. package/dist/components/content/search.js +164 -0
  100. package/dist/components/content/search.js.map +1 -0
  101. package/dist/components/groups/banner.d.ts +51 -0
  102. package/dist/components/groups/banner.js +194 -0
  103. package/dist/components/groups/banner.js.map +1 -0
  104. package/dist/components/groups/directory.d.ts +37 -0
  105. package/dist/components/groups/directory.js +117 -0
  106. package/dist/components/groups/directory.js.map +1 -0
  107. package/dist/components/groups/editor.d.ts +44 -0
  108. package/dist/components/groups/editor.js +860 -0
  109. package/dist/components/groups/editor.js.map +1 -0
  110. package/dist/components/groups/fields.d.ts +13 -0
  111. package/dist/components/groups/fields.js +68 -0
  112. package/dist/components/groups/fields.js.map +1 -0
  113. package/dist/components/groups/header.d.ts +25 -0
  114. package/dist/components/groups/header.js +131 -0
  115. package/dist/components/groups/header.js.map +1 -0
  116. package/dist/components/groups/index.d.ts +6 -0
  117. package/dist/components/groups/memberships.d.ts +55 -0
  118. package/dist/components/groups/memberships.js +527 -0
  119. package/dist/components/groups/memberships.js.map +1 -0
  120. package/dist/components/groups/parts.d.ts +49 -0
  121. package/dist/components/groups/parts.js +172 -0
  122. package/dist/components/groups/parts.js.map +1 -0
  123. package/dist/components/media-folder-editor.js +153 -152
  124. package/dist/components/media-folder-editor.js.map +1 -1
  125. package/dist/components/social/comments.d.ts +7 -5
  126. package/dist/components/social/comments.js +6 -2
  127. package/dist/components/social/comments.js.map +1 -1
  128. package/dist/components/social/engagement.d.ts +4 -3
  129. package/dist/components/social/engagement.js.map +1 -1
  130. package/dist/components/social/moderation.d.ts +1 -1
  131. package/dist/components/social/moderation.js +3 -3
  132. package/dist/components/social/moderation.js.map +1 -1
  133. package/dist/editor/rich-text-editor.d.ts +4 -4
  134. package/dist/editor/rich-text-editor.js.map +1 -1
  135. package/dist/index.d.ts +4 -2
  136. package/dist/index.js +23 -9
  137. package/dist/locales/admin/de.js +5 -66
  138. package/dist/locales/admin/de.js.map +1 -1
  139. package/dist/locales/admin/en.d.ts +5 -66
  140. package/dist/locales/admin/en.js +19 -80
  141. package/dist/locales/admin/en.js.map +1 -1
  142. package/dist/locales/admin/es.js +5 -66
  143. package/dist/locales/admin/es.js.map +1 -1
  144. package/dist/locales/admin/ja.js +5 -66
  145. package/dist/locales/admin/ja.js.map +1 -1
  146. package/dist/locales/admin/ko.js +5 -66
  147. package/dist/locales/admin/ko.js.map +1 -1
  148. package/dist/locales/admin/zh.js +5 -66
  149. package/dist/locales/admin/zh.js.map +1 -1
  150. package/dist/locales/content/de.d.ts +2 -0
  151. package/dist/locales/content/de.js +98 -0
  152. package/dist/locales/content/de.js.map +1 -0
  153. package/dist/locales/content/en.d.ts +93 -0
  154. package/dist/locales/content/en.js +98 -0
  155. package/dist/locales/content/en.js.map +1 -0
  156. package/dist/locales/content/es.d.ts +2 -0
  157. package/dist/locales/content/es.js +98 -0
  158. package/dist/locales/content/es.js.map +1 -0
  159. package/dist/locales/content/ja.d.ts +2 -0
  160. package/dist/locales/content/ja.js +98 -0
  161. package/dist/locales/content/ja.js.map +1 -0
  162. package/dist/locales/content/ko.d.ts +2 -0
  163. package/dist/locales/content/ko.js +98 -0
  164. package/dist/locales/content/ko.js.map +1 -0
  165. package/dist/locales/content/zh.d.ts +2 -0
  166. package/dist/locales/content/zh.js +98 -0
  167. package/dist/locales/content/zh.js.map +1 -0
  168. package/dist/locales/de.js +11 -2
  169. package/dist/locales/de.js.map +1 -1
  170. package/dist/locales/discovery/de.d.ts +2 -0
  171. package/dist/locales/discovery/de.js +68 -0
  172. package/dist/locales/discovery/de.js.map +1 -0
  173. package/dist/locales/discovery/en.d.ts +63 -0
  174. package/dist/locales/discovery/en.js +68 -0
  175. package/dist/locales/discovery/en.js.map +1 -0
  176. package/dist/locales/discovery/es.d.ts +2 -0
  177. package/dist/locales/discovery/es.js +68 -0
  178. package/dist/locales/discovery/es.js.map +1 -0
  179. package/dist/locales/discovery/ja.d.ts +2 -0
  180. package/dist/locales/discovery/ja.js +68 -0
  181. package/dist/locales/discovery/ja.js.map +1 -0
  182. package/dist/locales/discovery/ko.d.ts +2 -0
  183. package/dist/locales/discovery/ko.js +68 -0
  184. package/dist/locales/discovery/ko.js.map +1 -0
  185. package/dist/locales/discovery/zh.d.ts +2 -0
  186. package/dist/locales/discovery/zh.js +68 -0
  187. package/dist/locales/discovery/zh.js.map +1 -0
  188. package/dist/locales/en.d.ts +288 -67
  189. package/dist/locales/en.js +11 -2
  190. package/dist/locales/en.js.map +1 -1
  191. package/dist/locales/es.js +11 -2
  192. package/dist/locales/es.js.map +1 -1
  193. package/dist/locales/groups/de.d.ts +2 -0
  194. package/dist/locales/groups/de.js +133 -0
  195. package/dist/locales/groups/de.js.map +1 -0
  196. package/dist/locales/groups/en.d.ts +128 -0
  197. package/dist/locales/groups/en.js +133 -0
  198. package/dist/locales/groups/en.js.map +1 -0
  199. package/dist/locales/groups/es.d.ts +2 -0
  200. package/dist/locales/groups/es.js +133 -0
  201. package/dist/locales/groups/es.js.map +1 -0
  202. package/dist/locales/groups/ja.d.ts +2 -0
  203. package/dist/locales/groups/ja.js +133 -0
  204. package/dist/locales/groups/ja.js.map +1 -0
  205. package/dist/locales/groups/ko.d.ts +2 -0
  206. package/dist/locales/groups/ko.js +133 -0
  207. package/dist/locales/groups/ko.js.map +1 -0
  208. package/dist/locales/groups/zh.d.ts +2 -0
  209. package/dist/locales/groups/zh.js +133 -0
  210. package/dist/locales/groups/zh.js.map +1 -0
  211. package/dist/locales/ja.js +11 -2
  212. package/dist/locales/ja.js.map +1 -1
  213. package/dist/locales/ko.js +11 -2
  214. package/dist/locales/ko.js.map +1 -1
  215. package/dist/locales/social/de.js +2 -1
  216. package/dist/locales/social/de.js.map +1 -1
  217. package/dist/locales/social/en.d.ts +2 -1
  218. package/dist/locales/social/en.js +2 -1
  219. package/dist/locales/social/en.js.map +1 -1
  220. package/dist/locales/social/es.js +2 -1
  221. package/dist/locales/social/es.js.map +1 -1
  222. package/dist/locales/social/ja.js +2 -1
  223. package/dist/locales/social/ja.js.map +1 -1
  224. package/dist/locales/social/ko.js +2 -1
  225. package/dist/locales/social/ko.js.map +1 -1
  226. package/dist/locales/social/zh.js +2 -1
  227. package/dist/locales/social/zh.js.map +1 -1
  228. package/dist/locales/zh.js +11 -2
  229. package/dist/locales/zh.js.map +1 -1
  230. package/dist/react/admin.d.ts +1 -1
  231. package/dist/react/admin.js +8 -6
  232. package/dist/react/admin.js.map +1 -1
  233. package/dist/react/comments.d.ts +9 -7
  234. package/dist/react/comments.js +42 -27
  235. package/dist/react/comments.js.map +1 -1
  236. package/dist/react/contents.d.ts +57 -0
  237. package/dist/react/contents.js +214 -0
  238. package/dist/react/contents.js.map +1 -0
  239. package/dist/react/context.d.ts +1 -1
  240. package/dist/react/context.js.map +1 -1
  241. package/dist/react/crop.js +45 -44
  242. package/dist/react/crop.js.map +1 -1
  243. package/dist/react/detail.d.ts +30 -0
  244. package/dist/react/detail.js +42 -0
  245. package/dist/react/detail.js.map +1 -0
  246. package/dist/react/discovery.d.ts +138 -0
  247. package/dist/react/discovery.js +299 -0
  248. package/dist/react/discovery.js.map +1 -0
  249. package/dist/react/engagement.d.ts +5 -5
  250. package/dist/react/engagement.js +55 -41
  251. package/dist/react/engagement.js.map +1 -1
  252. package/dist/react/folder.js +54 -53
  253. package/dist/react/folder.js.map +1 -1
  254. package/dist/react/groups.d.ts +115 -0
  255. package/dist/react/groups.js +229 -0
  256. package/dist/react/groups.js.map +1 -0
  257. package/dist/react/index.d.ts +5 -1
  258. package/dist/react/public.d.ts +6 -5
  259. package/dist/react/public.js +19 -22
  260. package/dist/react/public.js.map +1 -1
  261. package/dist/react/read.js +105 -106
  262. package/dist/react/read.js.map +1 -1
  263. package/dist/react/resources.d.ts +2 -2
  264. package/dist/react/resources.js +69 -40
  265. package/dist/react/resources.js.map +1 -1
  266. package/dist/react/store.js +41 -39
  267. package/dist/react/store.js.map +1 -1
  268. package/dist/react/upload.js +54 -53
  269. package/dist/react/upload.js.map +1 -1
  270. package/dist/react/use-resource.d.ts +2 -1
  271. package/dist/react/use-resource.js +1 -0
  272. package/dist/react/use-resource.js.map +1 -1
  273. package/dist/react/versions.d.ts +57 -0
  274. package/dist/react/versions.js +94 -0
  275. package/dist/react/versions.js.map +1 -0
  276. package/dist/react/video.js +87 -86
  277. package/dist/react/video.js.map +1 -1
  278. package/dist/react.js +9 -5
  279. package/dist/styles.css +1 -1
  280. package/package.json +1 -1
  281. package/dist/client/content/posts.d.ts +0 -52
  282. package/dist/client/content/posts.js +0 -84
  283. package/dist/client/content/posts.js.map +0 -1
  284. package/dist/client/taxonomy.d.ts +0 -34
  285. package/dist/client/taxonomy.js +0 -84
  286. package/dist/client/taxonomy.js.map +0 -1
  287. package/dist/components/admin/post-manager.d.ts +0 -45
  288. package/dist/components/admin/post-manager.js +0 -602
  289. package/dist/components/admin/post-manager.js.map +0 -1
  290. package/dist/components/admin/taxonomy-manager.d.ts +0 -54
  291. package/dist/components/admin/taxonomy-manager.js +0 -976
  292. package/dist/components/admin/taxonomy-manager.js.map +0 -1
  293. package/dist/react/posts.d.ts +0 -61
  294. package/dist/react/posts.js +0 -183
  295. package/dist/react/posts.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"engagement.js","names":[],"sources":["../../src/react/engagement.ts"],"sourcesContent":["import { useCallback, useState } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { ContentKitError } from \"../client/errors.js\";\nimport { toContentKitError } from \"../client/errors.js\";\nimport type { Reaction } from \"../client/content/types.js\";\nimport type { FavoriteState, ReactionCounts, RefBody } from \"../client/generated/wire.js\";\nimport { withReaction } from \"./comments.js\";\nimport { keyOf, useContentScope, useResource } from \"./use-resource.js\";\n\nexport interface UseReaction {\n counts: ReactionCounts;\n loading: boolean;\n error?: ContentKitError;\n /** Sets the caller's reaction at once; rolled back if the server refuses it. */\n set: (value: Reaction) => Promise<void>;\n /** Sets value, or clears it when it is the current one. */\n toggle: (value: 1 | -1) => Promise<void>;\n}\n\nconst ZERO: ReactionCounts = { likes: 0, dislikes: 0, mine: 0 };\n\n/** An item's likes and dislikes and the caller's own reaction (initial: counts the host already has). */\nexport function useReaction(ref: RefBody, o: { initial?: ReactionCounts; client?: ContentKitClient } = {}): UseReaction {\n const { client, store, scope } = useContentScope(o.client);\n const key = keyOf(\"reaction\", scope, ref.kind, ref.id);\n const r = useResource<ReactionCounts>(store, key, { type: \"reaction\", ref: { kind: ref.kind, id: ref.id } }, (s) => client.reactions.get(ref, s), { initial: o.initial });\n const counts = r.data ?? ZERO;\n const { kind, id } = ref;\n const set = useCallback(\n async (value: Reaction) => {\n const prev = store.snapshot<ReactionCounts>(key).data ?? counts;\n const token = store.mark(key);\n store.set(key, withReaction(prev, value));\n try {\n await client.reactions.set({ kind, id }, value);\n } catch (e) {\n store.rollback(key, token, prev);\n throw toContentKitError(e);\n }\n },\n [client, store, key, kind, id, counts],\n );\n const toggle = useCallback((value: 1 | -1) => set(counts.mine === value ? 0 : value), [set, counts.mine]);\n return { counts, loading: r.loading || (!r.loaded && o.initial === undefined), error: r.error, set, toggle };\n}\n\nexport interface UseFavorite {\n favorited: boolean;\n /** How many favorited the item; undefined until read. */\n count?: number;\n loading: boolean;\n /** A write is in flight. */\n pending: boolean;\n error?: ContentKitError;\n /** Sets it at once (count moved by one); rolled back if the server refuses it. */\n set: (favorited: boolean) => Promise<void>;\n toggle: () => Promise<void>;\n}\n\n/**\n * An item's favorite count and whether the signed-in caller favorited it\n * (initial: what the host already has). Signed out, favorited stays false.\n */\nexport function useFavorite(ref: RefBody, o: { initial?: FavoriteState; client?: ContentKitClient } = {}): UseFavorite {\n const { client, store, scope } = useContentScope(o.client);\n const key = keyOf(\"favorite\", scope, ref.kind, ref.id);\n const r = useResource<FavoriteState>(store, key, { type: \"favorite\", ref: { kind: ref.kind, id: ref.id } }, (s) => client.favorites.get(ref, s), { initial: o.initial });\n const favorited = !!r.data?.favorited;\n const [pending, setPending] = useState(false);\n const { kind, id } = ref;\n const set = useCallback(\n async (next: boolean) => {\n const prev = store.snapshot<FavoriteState>(key).data;\n const token = store.mark(key);\n if (prev && prev.favorited !== next) store.set<FavoriteState>(key, { favorited: next, count: Math.max(0, prev.count + (next ? 1 : -1)) });\n setPending(true);\n try {\n await client.favorites.set({ kind, id }, next);\n } catch (e) {\n if (prev) store.rollback(key, token, prev);\n throw toContentKitError(e);\n } finally {\n setPending(false);\n }\n },\n [client, store, key, kind, id],\n );\n const toggle = useCallback(() => set(!favorited), [set, favorited]);\n return { favorited, count: r.data?.count, loading: r.loading || (!r.loaded && o.initial === undefined), pending, error: r.error, set, toggle };\n}\n"],"mappings":";;;;;AAmBA,IAAM,IAAuB;CAAE,OAAO;CAAG,UAAU;CAAG,MAAM;AAAE;AAG9D,SAAgB,EAAY,GAAc,IAA6D,CAAC,GAAgB;CACtH,IAAM,EAAE,WAAQ,UAAO,aAAU,EAAgB,EAAE,MAAM,GACnD,IAAM,EAAM,YAAY,GAAO,EAAI,MAAM,EAAI,EAAE,GAC/C,IAAI,EAA4B,GAAO,GAAK;EAAE,MAAM;EAAY,KAAK;GAAE,MAAM,EAAI;GAAM,IAAI,EAAI;EAAG;CAAE,IAAI,MAAM,EAAO,UAAU,IAAI,GAAK,CAAC,GAAG,EAAE,SAAS,EAAE,QAAQ,CAAC,GAClK,IAAS,EAAE,QAAQ,GACnB,EAAE,SAAM,UAAO,GACf,IAAM,EACV,OAAO,MAAoB;EACzB,IAAM,IAAO,EAAM,SAAyB,CAAG,CAAC,CAAC,QAAQ,GACnD,IAAQ,EAAM,KAAK,CAAG;EAC5B,EAAM,IAAI,GAAK,EAAa,GAAM,CAAK,CAAC;EACxC,IAAI;GACF,MAAM,EAAO,UAAU,IAAI;IAAE;IAAM;GAAG,GAAG,CAAK;EAChD,SAAS,GAAG;GAEV,MADA,EAAM,SAAS,GAAK,GAAO,CAAI,GACzB,EAAkB,CAAC;EAC3B;CACF,GACA;EAAC;EAAQ;EAAO;EAAK;EAAM;EAAI;CAAM,CACvC,GACM,IAAS,GAAa,MAAkB,EAAI,EAAO,SAAS,IAAQ,IAAI,CAAK,GAAG,CAAC,GAAK,EAAO,IAAI,CAAC;CACxG,OAAO;EAAE;EAAQ,SAAS,EAAE,WAAY,CAAC,EAAE,UAAU,EAAE,YAAY,KAAA;EAAY,OAAO,EAAE;EAAO;EAAK;CAAO;AAC7G;AAmBA,SAAgB,EAAY,GAAc,IAA4D,CAAC,GAAgB;CACrH,IAAM,EAAE,WAAQ,UAAO,aAAU,EAAgB,EAAE,MAAM,GACnD,IAAM,EAAM,YAAY,GAAO,EAAI,MAAM,EAAI,EAAE,GAC/C,IAAI,EAA2B,GAAO,GAAK;EAAE,MAAM;EAAY,KAAK;GAAE,MAAM,EAAI;GAAM,IAAI,EAAI;EAAG;CAAE,IAAI,MAAM,EAAO,UAAU,IAAI,GAAK,CAAC,GAAG,EAAE,SAAS,EAAE,QAAQ,CAAC,GACjK,IAAY,CAAC,CAAC,EAAE,MAAM,WACtB,CAAC,GAAS,KAAc,EAAS,EAAK,GACtC,EAAE,SAAM,UAAO,GACf,IAAM,EACV,OAAO,MAAkB;EACvB,IAAM,IAAO,EAAM,SAAwB,CAAG,CAAC,CAAC,MAC1C,IAAQ,EAAM,KAAK,CAAG;EAE5B,AADI,KAAQ,EAAK,cAAc,KAAM,EAAM,IAAmB,GAAK;GAAE,WAAW;GAAM,OAAO,KAAK,IAAI,GAAG,EAAK,SAAS,IAAO,IAAI,GAAG;EAAE,CAAC,GACxI,EAAW,EAAI;EACf,IAAI;GACF,MAAM,EAAO,UAAU,IAAI;IAAE;IAAM;GAAG,GAAG,CAAI;EAC/C,SAAS,GAAG;GAEV,MADI,KAAM,EAAM,SAAS,GAAK,GAAO,CAAI,GACnC,EAAkB,CAAC;EAC3B,UAAU;GACR,EAAW,EAAK;EAClB;CACF,GACA;EAAC;EAAQ;EAAO;EAAK;EAAM;CAAE,CAC/B,GACM,IAAS,QAAkB,EAAI,CAAC,CAAS,GAAG,CAAC,GAAK,CAAS,CAAC;CAClE,OAAO;EAAE;EAAW,OAAO,EAAE,MAAM;EAAO,SAAS,EAAE,WAAY,CAAC,EAAE,UAAU,EAAE,YAAY,KAAA;EAAY;EAAS,OAAO,EAAE;EAAO;EAAK;CAAO;AAC/I"}
1
+ {"version":3,"file":"engagement.js","names":[],"sources":["../../src/react/engagement.ts"],"sourcesContent":["import { useCallback, useState } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { ContentKitError } from \"../client/errors.js\";\nimport { toContentKitError } from \"../client/errors.js\";\nimport type { Reaction, Target } from \"../client/content/types.js\";\nimport type { FavoriteState, ReactionCounts } from \"../client/generated/wire.js\";\nimport { withReaction } from \"./comments.js\";\nimport { keyOf, useContentScope, useResource } from \"./use-resource.js\";\n\nexport interface UseReaction {\n counts: ReactionCounts;\n loading: boolean;\n error?: ContentKitError;\n /** Sets the caller's reaction at once; rolled back if the server refuses it. */\n set: (value: Reaction) => Promise<void>;\n /** Sets value, or clears it when it is the current one. */\n toggle: (value: 1 | -1) => Promise<void>;\n}\n\nconst ZERO: ReactionCounts = { likes: 0, dislikes: 0, mine: 0 };\n\n/** A target's likes and dislikes and the caller's own reaction (initial: counts the host already has). */\nexport function useReaction(target: Target, o: { initial?: ReactionCounts; client?: ContentKitClient } = {}): UseReaction {\n const { client, store, scope } = useContentScope(o.client);\n const { type, id, version } = target;\n const key = keyOf(\"reaction\", scope, type, id, version);\n const r = useResource<ReactionCounts>(store, key, { type: \"reaction\", target: { type, id, version } }, (s) => client.reactions.get({ type, id, version }, s), {\n initial: o.initial,\n });\n const counts = r.data ?? ZERO;\n const set = useCallback(\n async (value: Reaction) => {\n const prev = store.snapshot<ReactionCounts>(key).data ?? counts;\n const token = store.mark(key);\n store.set(key, withReaction(prev, value));\n try {\n await client.reactions.set({ type, id, version }, value);\n } catch (e) {\n store.rollback(key, token, prev);\n throw toContentKitError(e);\n }\n },\n [client, store, key, type, id, version, counts],\n );\n const toggle = useCallback((value: 1 | -1) => set(counts.mine === value ? 0 : value), [set, counts.mine]);\n return { counts, loading: r.loading || (!r.loaded && o.initial === undefined), error: r.error, set, toggle };\n}\n\nexport interface UseFavorite {\n favorited: boolean;\n /** How many favorited the item; undefined until read. */\n count?: number;\n loading: boolean;\n /** A write is in flight. */\n pending: boolean;\n error?: ContentKitError;\n /** Sets it at once (count moved by one); rolled back if the server refuses it. */\n set: (favorited: boolean) => Promise<void>;\n toggle: () => Promise<void>;\n}\n\n/**\n * An item's favorite count and whether the signed-in caller favorited it\n * (initial: what the host already has). Signed out, favorited stays false.\n */\nexport function useFavorite(target: Target, o: { initial?: FavoriteState; client?: ContentKitClient } = {}): UseFavorite {\n const { client, store, scope } = useContentScope(o.client);\n const { type, id, version } = target;\n const key = keyOf(\"favorite\", scope, type, id, version);\n const r = useResource<FavoriteState>(store, key, { type: \"favorite\", target: { type, id, version } }, (s) => client.favorites.get({ type, id, version }, s), {\n initial: o.initial,\n });\n const favorited = !!r.data?.favorited;\n const [pending, setPending] = useState(false);\n const set = useCallback(\n async (next: boolean) => {\n const prev = store.snapshot<FavoriteState>(key).data;\n const token = store.mark(key);\n if (prev && prev.favorited !== next) store.set<FavoriteState>(key, { favorited: next, count: Math.max(0, prev.count + (next ? 1 : -1)) });\n setPending(true);\n try {\n await client.favorites.set({ type, id, version }, next);\n } catch (e) {\n if (prev) store.rollback(key, token, prev);\n throw toContentKitError(e);\n } finally {\n setPending(false);\n }\n },\n [client, store, key, type, id, version],\n );\n const toggle = useCallback(() => set(!favorited), [set, favorited]);\n return { favorited, count: r.data?.count, loading: r.loading || (!r.loaded && o.initial === undefined), pending, error: r.error, set, toggle };\n}\n"],"mappings":";;;;;AAmBA,IAAM,IAAuB;CAAE,OAAO;CAAG,UAAU;CAAG,MAAM;AAAE;AAG9D,SAAgB,EAAY,GAAgB,IAA6D,CAAC,GAAgB;CACxH,IAAM,EAAE,WAAQ,UAAO,aAAU,EAAgB,EAAE,MAAM,GACnD,EAAE,SAAM,OAAI,eAAY,GACxB,IAAM,EAAM,YAAY,GAAO,GAAM,GAAI,CAAO,GAChD,IAAI,EAA4B,GAAO,GAAK;EAAE,MAAM;EAAY,QAAQ;GAAE;GAAM;GAAI;EAAQ;CAAE,IAAI,MAAM,EAAO,UAAU,IAAI;EAAE;EAAM;EAAI;CAAQ,GAAG,CAAC,GAAG,EAC5J,SAAS,EAAE,QACb,CAAC,GACK,IAAS,EAAE,QAAQ,GACnB,IAAM,EACV,OAAO,MAAoB;EACzB,IAAM,IAAO,EAAM,SAAyB,CAAG,CAAC,CAAC,QAAQ,GACnD,IAAQ,EAAM,KAAK,CAAG;EAC5B,EAAM,IAAI,GAAK,EAAa,GAAM,CAAK,CAAC;EACxC,IAAI;GACF,MAAM,EAAO,UAAU,IAAI;IAAE;IAAM;IAAI;GAAQ,GAAG,CAAK;EACzD,SAAS,GAAG;GAEV,MADA,EAAM,SAAS,GAAK,GAAO,CAAI,GACzB,EAAkB,CAAC;EAC3B;CACF,GACA;EAAC;EAAQ;EAAO;EAAK;EAAM;EAAI;EAAS;CAAM,CAChD,GACM,IAAS,GAAa,MAAkB,EAAI,EAAO,SAAS,IAAQ,IAAI,CAAK,GAAG,CAAC,GAAK,EAAO,IAAI,CAAC;CACxG,OAAO;EAAE;EAAQ,SAAS,EAAE,WAAY,CAAC,EAAE,UAAU,EAAE,YAAY,KAAA;EAAY,OAAO,EAAE;EAAO;EAAK;CAAO;AAC7G;AAmBA,SAAgB,EAAY,GAAgB,IAA4D,CAAC,GAAgB;CACvH,IAAM,EAAE,WAAQ,UAAO,aAAU,EAAgB,EAAE,MAAM,GACnD,EAAE,SAAM,OAAI,eAAY,GACxB,IAAM,EAAM,YAAY,GAAO,GAAM,GAAI,CAAO,GAChD,IAAI,EAA2B,GAAO,GAAK;EAAE,MAAM;EAAY,QAAQ;GAAE;GAAM;GAAI;EAAQ;CAAE,IAAI,MAAM,EAAO,UAAU,IAAI;EAAE;EAAM;EAAI;CAAQ,GAAG,CAAC,GAAG,EAC3J,SAAS,EAAE,QACb,CAAC,GACK,IAAY,CAAC,CAAC,EAAE,MAAM,WACtB,CAAC,GAAS,KAAc,EAAS,EAAK,GACtC,IAAM,EACV,OAAO,MAAkB;EACvB,IAAM,IAAO,EAAM,SAAwB,CAAG,CAAC,CAAC,MAC1C,IAAQ,EAAM,KAAK,CAAG;EAE5B,AADI,KAAQ,EAAK,cAAc,KAAM,EAAM,IAAmB,GAAK;GAAE,WAAW;GAAM,OAAO,KAAK,IAAI,GAAG,EAAK,SAAS,IAAO,IAAI,GAAG;EAAE,CAAC,GACxI,EAAW,EAAI;EACf,IAAI;GACF,MAAM,EAAO,UAAU,IAAI;IAAE;IAAM;IAAI;GAAQ,GAAG,CAAI;EACxD,SAAS,GAAG;GAEV,MADI,KAAM,EAAM,SAAS,GAAK,GAAO,CAAI,GACnC,EAAkB,CAAC;EAC3B,UAAU;GACR,EAAW,EAAK;EAClB;CACF,GACA;EAAC;EAAQ;EAAO;EAAK;EAAM;EAAI;CAAO,CACxC,GACM,IAAS,QAAkB,EAAI,CAAC,CAAS,GAAG,CAAC,GAAK,CAAS,CAAC;CAClE,OAAO;EAAE;EAAW,OAAO,EAAE,MAAM;EAAO,SAAS,EAAE,WAAY,CAAC,EAAE,UAAU,EAAE,YAAY,KAAA;EAAY;EAAS,OAAO,EAAE;EAAO;EAAK;CAAO;AAC/I"}
@@ -1,17 +1,18 @@
1
1
  import { ContentKitError as e, failureError as t, toContentKitError as n } from "../client/errors.js";
2
- import { stem as r } from "../client/media/client.js";
3
- import { filesFor as i, isPattern as a, ruleDir as o, ruleFor as s, screenFiles as ee, uniqueName as te, uploadRules as c } from "../client/media/rules.js";
4
- import { useContentKitClient as l, useErrorReporter as ne } from "./context.js";
5
- import { useUploadQueue as u } from "./upload.js";
2
+ import { itemKey as r } from "../client/media/ref.js";
3
+ import { stem as i } from "../client/media/client.js";
4
+ import { filesFor as a, isPattern as o, ruleDir as s, ruleFor as c, screenFiles as ee, uniqueName as te, uploadRules as l } from "../client/media/rules.js";
5
+ import { useContentKitClient as u, useErrorReporter as d } from "./context.js";
6
+ import { useUploadQueue as ne } from "./upload.js";
6
7
  import { useMediaRead as re } from "./read.js";
7
- import { useCallback as d, useEffect as f, useMemo as p, useRef as m, useState as h } from "react";
8
+ import { useCallback as f, useEffect as p, useMemo as m, useRef as h, useState as g } from "react";
8
9
  //#region src/react/folder.ts
9
10
  var ie = {
10
11
  start: 0,
11
12
  end: 2 ** 53 - 1,
12
13
  chunk: 200
13
- }, g = (e, t) => t.slice(o(e.path).length);
14
- function _(e, t) {
14
+ }, _ = (e, t) => t.slice(s(e.path).length);
15
+ function v(e, t) {
15
16
  let n;
16
17
  for (let r of e?.files ?? []) !r.upload && r.from === t.path && r.url && r.type.startsWith("image/") && (!n || (r.w ?? Infinity) < (n.w ?? Infinity)) && (n = r);
17
18
  return n;
@@ -19,43 +20,43 @@ function _(e, t) {
19
20
  var ae = {
20
21
  kind: "",
21
22
  id: ""
22
- }, oe = (e) => e ? `${e.kind}/${e.id}` : "", v = /* @__PURE__ */ new WeakMap();
23
+ }, oe = (e) => e ? r(e) : "", y = /* @__PURE__ */ new WeakMap();
23
24
  function se(t) {
24
- let n = v.get(t);
25
- return n || v.set(t, n = new e("forbidden", "the editor read states no upload rules", { status: 403 })), n;
25
+ let n = y.get(t);
26
+ return n || y.set(t, n = new e("forbidden", "the editor read states no upload rules", { status: 403 })), n;
26
27
  }
27
28
  function ce(e, t) {
28
29
  return e.filter((e) => e.status !== "failed" && !(e.status === "committed" && t.has(e.path)));
29
30
  }
30
- function y(_, v = {}) {
31
- let y = l(v.client), b = ne(v.onError), x = _ ?? null, S = oe(x), C = x ?? ae, w = re(C, {
31
+ function b(r, v = {}) {
32
+ let y = u(v.client), b = d(v.onError), x = r ?? null, S = oe(x), C = x ?? ae, w = re(C, {
32
33
  editor: !0,
33
34
  window: ie,
34
35
  poll: v.poll,
35
36
  client: y,
36
37
  read: x ? void 0 : null
37
- }), T = c(w.read), E = v.paths?.join("\n"), D = p(() => E === void 0 ? T.filter((e) => a(e.path) && !e.named).map((e) => e.path) : E.split("\n").filter(Boolean), [E, T]), O = p(() => {
38
+ }), T = l(w.read), E = v.paths?.join("\n"), D = m(() => E === void 0 ? T.filter((e) => o(e.path) && !e.named).map((e) => e.path) : E.split("\n").filter(Boolean), [E, T]), O = m(() => {
38
39
  let e = T.length ? T : D.map((e) => ({ path: e }));
39
40
  return D.map((t) => ({
40
41
  path: t,
41
42
  rule: T.find((e) => e.path === t),
42
- files: i(w.read, { path: t }, e)
43
+ files: a(w.read, { path: t }, e)
43
44
  }));
44
45
  }, [
45
46
  D,
46
47
  T,
47
48
  w.read
48
- ]), k = p(() => O.flatMap((e) => e.files), [O]), A = p(() => new Set(k.map((e) => e.path)), [k]), j = u({
49
+ ]), k = m(() => O.flatMap((e) => e.files), [O]), A = m(() => new Set(k.map((e) => e.path)), [k]), j = ne({
49
50
  client: y,
50
51
  ref: C,
51
- path: `${o(D[0] ?? "{name}")}{name}`,
52
+ path: `${s(D[0] ?? "{name}")}{name}`,
52
53
  concurrency: v.concurrency
53
- }), M = j.queue, N = p(() => j.items.filter((e) => !(e.status === "committed" && A.has(e.path))), [j.items, A]), le = {
54
+ }), M = j.queue, N = m(() => j.items.filter((e) => !(e.status === "committed" && A.has(e.path))), [j.items, A]), le = {
54
55
  ...j,
55
56
  items: N
56
- }, [ue, P] = h([]), [F, I] = h([]), L = m([]), [R, de] = h(0), [z, fe] = h(!1), [pe, B] = h(), [me, V] = h(), H = me?.key === S ? me.error : void 0, U = m(v);
57
+ }, [ue, P] = g([]), [F, I] = g([]), L = h([]), [R, de] = g(0), [z, fe] = g(!1), [pe, B] = g(), [me, V] = g(), H = me?.key === S ? me.error : void 0, U = h(v);
57
58
  U.current = v;
58
- let W = !!x && !!w.read?.uploads, G = x ? w.read ? w.read.uploads ? void 0 : se(w.read) : w.error : void 0, K = m({
59
+ let W = !!x && !!w.read?.uploads, G = x ? w.read ? w.read.uploads ? void 0 : se(w.read) : w.error : void 0, K = h({
59
60
  groups: O,
60
61
  ref: C,
61
62
  listed: A,
@@ -69,41 +70,41 @@ function y(_, v = {}) {
69
70
  ready: W,
70
71
  loadError: G
71
72
  };
72
- let he = d((e) => {
73
+ let he = f((e) => {
73
74
  let { groups: t, listed: n } = K.current, r = t.map((e) => e.rule ?? {
74
75
  path: e.path,
75
76
  types: [],
76
77
  max_bytes: 0
77
- }), i = ce(M.getSnapshot().items, n), a = (e, t) => s(r, e)?.path === t.path, c = ee(e, r, (e) => t.find((t) => t.path === e.path).files.length + i.filter((t) => a(t.path, e)).length), l = /* @__PURE__ */ new Map(), ne = (e) => {
78
+ }), i = ce(M.getSnapshot().items, n), a = (e, t) => c(r, e)?.path === t.path, o = ee(e, r, (e) => t.find((t) => t.path === e.path).files.length + i.filter((t) => a(t.path, e)).length), l = /* @__PURE__ */ new Map(), u = (e) => {
78
79
  let n = l.get(e.path);
79
80
  if (!n) {
80
81
  let r = t.find((t) => t.path === e.path);
81
- n = [...r.files.map((e) => g(r, e.path)), ...i.filter((t) => a(t.path, e)).map((e) => g(r, e.path))], l.set(e.path, n);
82
+ n = [...r.files.map((e) => _(r, e.path)), ...i.filter((t) => a(t.path, e)).map((e) => _(r, e.path))], l.set(e.path, n);
82
83
  }
83
84
  return n;
84
- }, u = /* @__PURE__ */ new Map();
85
- for (let { file: e, rule: t } of c.accepted) {
86
- let n = ne(t), r = U.current.name?.(e) ?? te(e.name, n);
87
- n.push(r), u.set(e, o(t.path) + r);
85
+ }, d = /* @__PURE__ */ new Map();
86
+ for (let { file: e, rule: t } of o.accepted) {
87
+ let n = u(t), r = U.current.name?.(e) ?? te(e.name, n);
88
+ n.push(r), d.set(e, s(t.path) + r);
88
89
  }
89
- u.size && (M.add(u.keys(), { path: (e) => u.get(e) }), M.start(), V(void 0)), P(c.refused);
90
- for (let e of c.refused) b(e.error, "upload", e.file.name);
91
- }, [M, b]), q = d(() => {
90
+ d.size && (M.add(d.keys(), { path: (e) => d.get(e) }), M.start(), V(void 0)), P(o.refused);
91
+ for (let e of o.refused) b(e.error, "upload", e.file.name);
92
+ }, [M, b]), q = f(() => {
92
93
  let e = L.current, { ready: t, loadError: n } = K.current;
93
94
  return !e.length || !t && !n ? !1 : (L.current = [], I([]), t ? he(e) : P(e.map((e) => ({
94
95
  file: e,
95
96
  error: n
96
97
  }))), !0);
97
- }, [he]), ge = d((e) => {
98
+ }, [he]), ge = f((e) => {
98
99
  let t = [...e];
99
100
  t.length && (L.current = [...L.current, ...t], q() || I(L.current));
100
101
  }, [q]);
101
- f(() => void q(), [
102
+ p(() => void q(), [
102
103
  q,
103
104
  W,
104
105
  G
105
106
  ]);
106
- let J = d(async (e, t) => {
107
+ let J = f(async (e, t) => {
107
108
  de((e) => e + 1);
108
109
  try {
109
110
  t ? await t() : await y.media.commit(K.current.ref, e), B(void 0);
@@ -113,7 +114,7 @@ function y(_, v = {}) {
113
114
  } finally {
114
115
  de((e) => e - 1);
115
116
  }
116
- }, [y, b]), Y = d(async () => {
117
+ }, [y, b]), Y = f(async () => {
117
118
  fe(!0);
118
119
  try {
119
120
  let e = await M.commit(void 0, { head: U.current.commit === "auto" });
@@ -125,7 +126,7 @@ function y(_, v = {}) {
125
126
  fe(!1);
126
127
  }
127
128
  }, [M, b]);
128
- f(() => {
129
+ p(() => {
129
130
  for (let e of j.items) e.status === "committed" && (A.has(e.path) || !w.loading) && M.remove(e.id);
130
131
  }, [
131
132
  j.items,
@@ -134,7 +135,7 @@ function y(_, v = {}) {
134
135
  M
135
136
  ]);
136
137
  let X = v.commit === "auto", Z = N.find((e) => e.status !== "committed")?.status === "uploaded";
137
- f(() => {
138
+ p(() => {
138
139
  X && Z && !z && !H && Y().catch((e) => V({
139
140
  key: S,
140
141
  error: n(e)
@@ -147,8 +148,8 @@ function y(_, v = {}) {
147
148
  Y,
148
149
  S
149
150
  ]);
150
- let _e = d(async () => (V(void 0), Y()), [Y]), Q = m(/* @__PURE__ */ new Set());
151
- f(() => {
151
+ let _e = f(async () => (V(void 0), Y()), [Y]), Q = h(/* @__PURE__ */ new Set());
152
+ p(() => {
152
153
  for (let e of j.items) e.status === "failed" && e.error && !Q.current.has(e.error) && (Q.current.add(e.error), b(e.error, "upload", e.file.name));
153
154
  j.blocked && !Q.current.has(j.blocked) && (Q.current.add(j.blocked), b(j.blocked, "upload"));
154
155
  }, [
@@ -156,8 +157,8 @@ function y(_, v = {}) {
156
157
  j.blocked,
157
158
  b
158
159
  ]);
159
- let $ = m(null);
160
- f(() => {
160
+ let $ = h(null);
161
+ p(() => {
161
162
  if (!w.read) return;
162
163
  let e = k.filter((e) => e.failed);
163
164
  if ($.current?.key !== S) {
@@ -176,8 +177,8 @@ function y(_, v = {}) {
176
177
  k,
177
178
  b,
178
179
  S
179
- ]), f(() => void (w.error && b(w.error, "folder.load")), [w.error, b]);
180
- let ve = F.length > 0 || z || R > 0 || X && Z && !H || N.some((e) => e.status === "uploading" || e.status === "queued" && !j.blocked), ye = k.length + F.length + ce(N, A).length, be = (e) => K.current.groups.find((t) => s([{ path: t.path }], e));
180
+ ]), p(() => void (w.error && b(w.error, "folder.load")), [w.error, b]);
181
+ let ve = F.length > 0 || z || R > 0 || X && Z && !H || N.some((e) => e.status === "uploading" || e.status === "queued" && !j.blocked), ye = k.length + F.length + ce(N, A).length, be = (e) => K.current.groups.find((t) => c([{ path: t.path }], e));
181
182
  return {
182
183
  ref: x,
183
184
  options: v,
@@ -188,20 +189,20 @@ function y(_, v = {}) {
188
189
  waiting: F,
189
190
  add: ge,
190
191
  refused: ue,
191
- dismiss: d(() => P([]), []),
192
- move: d((e, t) => J([{
192
+ dismiss: f(() => P([]), []),
193
+ move: f((e, t) => J([{
193
194
  op: "move",
194
195
  path: e,
195
196
  index: t
196
197
  }]), [J]),
197
- remove: d((e) => J(e.map((e) => ({
198
+ remove: f((e) => J(e.map((e) => ({
198
199
  op: "remove",
199
200
  path: e
200
201
  }))), [J]),
201
- rename: d((t, n) => {
202
- let i = be(t), a = (i ? o(i.path) : t.slice(0, t.lastIndexOf("/") + 1)) + n.trim();
203
- if (r(a) === r(t)) return Promise.resolve();
204
- if (i?.files.some((e) => r(e.path) === r(a))) {
202
+ rename: f((t, n) => {
203
+ let r = be(t), a = (r ? s(r.path) : t.slice(0, t.lastIndexOf("/") + 1)) + n.trim();
204
+ if (i(a) === i(t)) return Promise.resolve();
205
+ if (r?.files.some((e) => i(e.path) === i(a))) {
205
206
  let t = new e("conflict", `${n} exists`, { status: 409 });
206
207
  return B(t), Promise.reject(t);
207
208
  }
@@ -211,18 +212,18 @@ function y(_, v = {}) {
211
212
  to: a
212
213
  }]);
213
214
  }, [J]),
214
- edit: d((e, t) => J([{
215
+ edit: f((e, t) => J([{
215
216
  op: "edit",
216
217
  path: e,
217
218
  ...t ? { edit: t } : {}
218
219
  }]), [J]),
219
- replace: d((e, t) => J([], () => y.media.put(t, {
220
+ replace: f((e, t) => J([], () => y.media.put(t, {
220
221
  ref: K.current.ref,
221
- path: r(e),
222
+ path: i(e),
222
223
  wait: !1
223
224
  })), [J, y]),
224
225
  commit: _e,
225
- discard: d(() => {
226
+ discard: f(() => {
226
227
  L.current = [], I([]);
227
228
  for (let e of M.getSnapshot().items) M.remove(e.id);
228
229
  M.pause();
@@ -235,6 +236,6 @@ function y(_, v = {}) {
235
236
  };
236
237
  }
237
238
  //#endregion
238
- export { g as nameIn, _ as thumbnailOf, y as useMediaFolder };
239
+ export { _ as nameIn, v as thumbnailOf, b as useMediaFolder };
239
240
 
240
241
  //# sourceMappingURL=folder.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"folder.js","names":[],"sources":["../../src/react/folder.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport { ContentKitError, failureError, toContentKitError } from \"../client/errors.js\";\nimport type { Edit, FileInfo, Op, ReadResult, RefBody, UploadRule } from \"../client/generated/wire.js\";\nimport { stem } from \"../client/media/client.js\";\nimport type { QueueItem } from \"../client/media/queue.js\";\nimport { filesFor, isPattern, ruleDir, ruleFor, screenFiles, uniqueName, uploadRules, type Screened } from \"../client/media/rules.js\";\nimport { useContentKitClient, useErrorReporter, type ContentKitErrorHandler } from \"./context.js\";\nimport { useMediaRead, type UseMediaRead } from \"./read.js\";\nimport { useUploadQueue, type UseUploadQueue } from \"./upload.js\";\n\nexport interface MediaFolderOptions {\n /**\n * The upload paths shown and added to, e.g. [\"images/{name}\",\n * \"videos/{name}\"]: one group each. Default every pattern path of the kind\n * the server does not name. A file goes to the first path whose rule takes\n * its type.\n */\n paths?: readonly string[];\n /**\n * \"manual\" (default): commit() adds the uploaded files. \"auto\": each\n * upload commits once those before it have, in the order added (a draft).\n */\n commit?: \"manual\" | \"auto\";\n /** A file's name under its path; default its own, numbered around names taken. */\n name?: (file: File) => string;\n /** Files uploading at once. Default 2. */\n concurrency?: number;\n /** Poll interval (ms) while uploads process. Default 2500. */\n poll?: number | false;\n /** Every failure (uploads, commits, updates, processing); default the provider's. */\n onError?: ContentKitErrorHandler;\n /** Overrides the provider's client. */\n client?: ContentKitClient;\n}\n\nexport interface FolderGroup {\n /** The upload path, e.g. \"images/{name}\". */\n path: string;\n /** Its rules, once an editor read states them. */\n rule?: UploadRule;\n /** Its uploads in manifest order. */\n files: FileInfo[];\n}\n\nexport interface UseMediaFolder {\n /** The item; null until the host has one (files added meanwhile wait for it). */\n ref: RefBody | null;\n /** The options the folder was opened with. */\n options: MediaFolderOptions;\n /** The editor read of the item (every window, polled while processing). */\n read: UseMediaRead;\n groups: FolderGroup[];\n /** Every group's uploads. */\n uploads: FileInfo[];\n /** The upload queue; a file leaves it once the editor read lists it. */\n queue: UseUploadQueue;\n /**\n * Files added before the item and its editor read (the upload rules) were\n * known. They are screened and queued when the rules arrive, or refused\n * when the read fails or states no rules (the actor may not edit the item).\n */\n waiting: readonly File[];\n /** Screens files against the rules and queues the rest (once the rules are known); refusals land in refused. */\n add: (files: Iterable<File>) => void;\n /** The last screening's refusals. */\n refused: Screened[\"refused\"];\n dismiss: () => void;\n /** Moves an upload to index among its group's uploads. */\n move: (path: string, index: number) => Promise<void>;\n remove: (paths: readonly string[]) => Promise<void>;\n /** Renames an upload within its path; the extension stays unless name has one. */\n rename: (path: string, name: string) => Promise<void>;\n edit: (path: string, edit: Edit | null) => Promise<void>;\n /** Uploads file in place of the upload at path (same position and meta, no edit). */\n replace: (path: string, file: File) => Promise<void>;\n /** Commits the uploaded files in queue order. */\n commit: () => Promise<FileInfo[]>;\n /** Stops every upload, aborts its multipart upload and empties the queue (before deleting a draft). */\n discard: () => void;\n /** An update (move, remove, rename, edit, replace) is running. */\n updating: boolean;\n committing: boolean;\n /**\n * Work is in flight: files wait for the rules, uploads are queued or\n * running, an automatic commit is due, or a commit or update runs.\n * Leaving the page now interrupts it.\n */\n busy: boolean;\n /** The item's uploads plus the files on their way in (waiting, queued, uploading, uploaded, being added); failed uploads are not counted. */\n fileCount: number;\n /** The last update's or commit's failure; cleared by the next success. */\n error?: ContentKitError;\n}\n\nconst ALL = { start: 0, end: Number.MAX_SAFE_INTEGER, chunk: 200 };\n\n/** The upload's name under its group (\"images/a.png\" in \"images/{name}\" is \"a.png\"). */\nexport const nameIn = (group: { path: string }, path: string) => path.slice(ruleDir(group.path).length);\n\n/** Its smallest derived image with a URL: a thumbnail for an editor. */\nexport function thumbnailOf(read: ReadResult | null | undefined, upload: FileInfo): FileInfo | undefined {\n let best: FileInfo | undefined;\n for (const f of read?.files ?? []) {\n if (f.upload || f.from !== upload.path || !f.url || !f.type.startsWith(\"image/\")) continue;\n if (!best || (f.w ?? Infinity) < (best.w ?? Infinity)) best = f;\n }\n return best;\n}\n\nconst NO_ITEM: RefBody = { kind: \"\", id: \"\" };\nconst keyOf = (ref: RefBody | null) => (ref ? `${ref.kind}/${ref.id}` : \"\");\n\nconst noRules = new WeakMap<ReadResult, ContentKitError>();\n/** The refusal for files added to an item whose read carries no upload rules (one per read). */\nfunction notEditor(read: ReadResult): ContentKitError {\n let e = noRules.get(read);\n if (!e) noRules.set(read, (e = new ContentKitError(\"forbidden\", \"the editor read states no upload rules\", { status: 403 })));\n return e;\n}\n\n/** Queue items still on their way in: not failed, and not yet listed by the read. */\nfunction inbound(items: readonly QueueItem[], listed: ReadonlySet<string>) {\n return items.filter((i) => i.status !== \"failed\" && !(i.status === \"committed\" && listed.has(i.path)));\n}\n\n/**\n * An item's folder for its editors: the editor read, an upload queue, the\n * kind's upload rules screening files before they upload, and the updates\n * (move, remove, rename, edit, replace). Files added before the editor read\n * arrives wait for its rules; with a null ref they wait for the item too (a\n * draft created on the first drop). Uploads the worker fails after the read\n * are reported once (\"folder.process\"). Another ref gets a fresh queue.\n */\nexport function useMediaFolder(ref: RefBody | null | undefined, o: MediaFolderOptions = {}): UseMediaFolder {\n const client = useContentKitClient(o.client);\n const report = useErrorReporter(o.onError);\n const item = ref ?? null;\n const key = keyOf(item);\n const target = item ?? NO_ITEM;\n // Without an item there is nothing to read: a supplied null read never fetches.\n const read = useMediaRead(target, { editor: true, window: ALL, poll: o.poll, client, read: item ? undefined : null });\n const rules = uploadRules(read.read);\n const pathsKey = o.paths?.join(\"\\n\");\n const groupPaths = useMemo(\n () => (pathsKey !== undefined ? pathsKey.split(\"\\n\").filter(Boolean) : rules.filter((r) => isPattern(r.path) && !r.named).map((r) => r.path)),\n [pathsKey, rules],\n );\n const groups = useMemo((): FolderGroup[] => {\n const known: { path: string }[] = rules.length ? rules : groupPaths.map((path) => ({ path }));\n return groupPaths.map((path) => ({ path, rule: rules.find((r) => r.path === path), files: filesFor(read.read, { path }, known) }));\n }, [groupPaths, rules, read.read]);\n const uploads = useMemo(() => groups.flatMap((g) => g.files), [groups]);\n const listed = useMemo(() => new Set(uploads.map((f) => f.path)), [uploads]);\n\n const raw = useUploadQueue({ client, ref: target, path: `${ruleDir(groupPaths[0] ?? \"{name}\")}{name}`, concurrency: o.concurrency });\n // The ItemUploadQueue itself is stable per item; the hook's wrapper is new every render.\n const q = raw.queue;\n // A committed file stays in the queue until the read lists it, so it never drops out of view.\n const items = useMemo(() => raw.items.filter((i) => !(i.status === \"committed\" && listed.has(i.path))), [raw.items, listed]);\n const queue = { ...raw, items };\n const [refused, setRefused] = useState<Screened[\"refused\"]>([]);\n const [waiting, setWaiting] = useState<readonly File[]>([]);\n const pending = useRef<File[]>([]);\n const [updating, setUpdating] = useState(0);\n const [committing, setCommitting] = useState(false);\n const [error, setError] = useState<ContentKitError>();\n // An automatic commit that failed: no more until commit() or new files.\n const [stall, setStall] = useState<{ key: string; error: ContentKitError }>();\n const stalled = stall?.key === key ? stall.error : undefined;\n const opts = useRef(o);\n opts.current = o;\n // The rules are known once the item's editor read states them. A failed read\n // refuses what waits, and so does one without rules: the server answered a\n // viewer's read, so nothing uploads here.\n const ready = !!item && !!read.read?.uploads;\n const loadError = !item ? undefined : !read.read ? read.error : !read.read.uploads ? notEditor(read.read) : undefined;\n const live = useRef({ groups, ref: target, listed, ready, loadError });\n live.current = { groups, ref: target, listed, ready, loadError };\n\n const place = useCallback(\n (files: File[]) => {\n const { groups, listed } = live.current;\n const gs = groups.map((g) => g.rule ?? { path: g.path, types: [], max_bytes: 0 });\n const queued = inbound(q.getSnapshot().items, listed);\n const inGroup = (path: string, rule: UploadRule) => ruleFor(gs, path)?.path === rule.path;\n const s = screenFiles(files, gs, (rule) => groups.find((g) => g.path === rule.path)!.files.length + queued.filter((i) => inGroup(i.path, rule)).length);\n const taken = new Map<string, string[]>();\n const takenIn = (rule: UploadRule) => {\n let t = taken.get(rule.path);\n if (!t) {\n const g = groups.find((x) => x.path === rule.path)!;\n t = [...g.files.map((f) => nameIn(g, f.path)), ...queued.filter((i) => inGroup(i.path, rule)).map((i) => nameIn(g, i.path))];\n taken.set(rule.path, t);\n }\n return t;\n };\n const paths = new Map<File, string>();\n for (const { file, rule } of s.accepted) {\n const t = takenIn(rule);\n const name = opts.current.name?.(file) ?? uniqueName(file.name, t);\n t.push(name);\n paths.set(file, ruleDir(rule.path) + name);\n }\n if (paths.size) {\n q.add(paths.keys(), { path: (f) => paths.get(f)! });\n // New files lift a block and restart a queue discard() paused.\n q.start();\n setStall(undefined);\n }\n setRefused(s.refused);\n for (const r of s.refused) report(r.error, \"upload\", r.file.name);\n },\n [q, report],\n );\n\n // Screens what waits once the rules are known; a failed read refuses it (the read failure is reported once, as folder.load).\n const flush = useCallback(() => {\n const files = pending.current;\n const { ready, loadError } = live.current;\n if (!files.length || (!ready && !loadError)) return false;\n pending.current = [];\n setWaiting([]);\n if (ready) place(files);\n else setRefused(files.map((file) => ({ file, error: loadError! })));\n return true;\n }, [place]);\n\n const add = useCallback(\n (files: Iterable<File>) => {\n const list = [...files];\n if (!list.length) return;\n pending.current = [...pending.current, ...list];\n if (!flush()) setWaiting(pending.current);\n },\n [flush],\n );\n useEffect(() => void flush(), [flush, ready, loadError]);\n\n const update = useCallback(\n async (ops: Op[], run?: () => Promise<unknown>) => {\n setUpdating((n) => n + 1);\n try {\n if (run) await run();\n else await client.media.commit(live.current.ref, ops);\n setError(undefined);\n } catch (e) {\n const err = toContentKitError(e);\n if (err.code !== \"aborted\") setError(err);\n report(err, \"folder.update\");\n throw err;\n } finally {\n setUpdating((n) => n - 1);\n }\n },\n [client, report],\n );\n\n const commit = useCallback(async () => {\n setCommitting(true);\n try {\n const files = await q.commit(undefined, { head: opts.current.commit === \"auto\" });\n setError(undefined);\n return files;\n } catch (e) {\n const err = toContentKitError(e);\n setError(err);\n report(err, \"folder.commit\");\n throw err;\n } finally {\n setCommitting(false);\n }\n }, [q, report]);\n\n // Committed files leave the queue once the read lists them, or once a read lands without them.\n useEffect(() => {\n for (const i of raw.items) if (i.status === \"committed\" && (listed.has(i.path) || !read.loading)) q.remove(i.id);\n }, [raw.items, listed, read.loading, q]);\n\n // A draft commits the uploaded head of the queue as it finishes; a failed commit waits for commit().\n const auto = o.commit === \"auto\";\n const headReady = items.find((i) => i.status !== \"committed\")?.status === \"uploaded\";\n useEffect(() => {\n if (!auto || !headReady || committing || stalled) return;\n commit().catch((e: unknown) => setStall({ key, error: toContentKitError(e) }));\n }, [auto, headReady, committing, stalled, commit, key]);\n const manualCommit = useCallback(async () => {\n setStall(undefined);\n return commit();\n }, [commit]);\n\n // Each failed upload and queue-wide refusal is reported once; rows keep showing it.\n const seen = useRef(new Set<unknown>());\n useEffect(() => {\n for (const i of raw.items) {\n if (i.status !== \"failed\" || !i.error || seen.current.has(i.error)) continue;\n seen.current.add(i.error);\n report(i.error, \"upload\", i.file.name);\n }\n if (raw.blocked && !seen.current.has(raw.blocked)) {\n seen.current.add(raw.blocked);\n report(raw.blocked, \"upload\");\n }\n }, [raw.items, raw.blocked, report]);\n\n // Processing failures that appear after the item's first read.\n const failures = useRef<{ key: string; seen: Set<string> } | null>(null);\n useEffect(() => {\n if (!read.read) return;\n const failed = uploads.filter((f) => f.failed);\n if (failures.current?.key !== key) {\n failures.current = { key, seen: new Set(failed.map((f) => `${f.path}:${f.failed!.of}`)) };\n return;\n }\n for (const f of failed) {\n const id = `${f.path}:${f.failed!.of}`;\n if (failures.current.seen.has(id)) continue;\n failures.current.seen.add(id);\n report(failureError(f.failed!), \"folder.process\", f.path.split(\"/\").pop());\n }\n }, [read.read, uploads, report, key]);\n\n useEffect(() => void (read.error && report(read.error, \"folder.load\")), [read.error, report]);\n\n const busy =\n waiting.length > 0 ||\n committing ||\n updating > 0 ||\n (auto && headReady && !stalled) ||\n items.some((i) => i.status === \"uploading\" || (i.status === \"queued\" && !raw.blocked));\n const fileCount = uploads.length + waiting.length + inbound(items, listed).length;\n\n // Reads the latest groups, so callbacks stay stable.\n const groupOf = (path: string) => live.current.groups.find((g) => ruleFor([{ path: g.path }], path));\n return {\n ref: item,\n options: o,\n read,\n groups,\n uploads,\n queue,\n waiting,\n add,\n refused,\n dismiss: useCallback(() => setRefused([]), []),\n move: useCallback((path, index) => update([{ op: \"move\", path, index }]), [update]),\n remove: useCallback((paths) => update(paths.map((path): Op => ({ op: \"remove\", path }))), [update]),\n rename: useCallback(\n (path, name) => {\n const g = groupOf(path);\n const to = (g ? ruleDir(g.path) : path.slice(0, path.lastIndexOf(\"/\") + 1)) + name.trim();\n if (stem(to) === stem(path)) return Promise.resolve();\n if (g?.files.some((f) => stem(f.path) === stem(to))) {\n const err = new ContentKitError(\"conflict\", `${name} exists`, { status: 409 });\n setError(err);\n return Promise.reject(err);\n }\n return update([{ op: \"rename\", path, to }]);\n },\n [update],\n ),\n edit: useCallback((path, edit) => update([{ op: \"edit\", path, ...(edit ? { edit } : {}) }]), [update]),\n replace: useCallback(\n (path, file) => update([], () => client.media.put(file, { ref: live.current.ref, path: stem(path), wait: false })),\n [update, client],\n ),\n commit: manualCommit,\n discard: useCallback(() => {\n pending.current = [];\n setWaiting([]);\n for (const i of q.getSnapshot().items) q.remove(i.id);\n q.pause();\n }, [q]),\n updating: updating > 0,\n committing,\n busy,\n fileCount,\n error: error ?? stalled,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"folder.js","names":[],"sources":["../../src/react/folder.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { itemKey } from \"../client/media/ref.js\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport { ContentKitError, failureError, toContentKitError } from \"../client/errors.js\";\nimport type { Edit, FileInfo, Op, ReadResult, RefBody, UploadRule } from \"../client/generated/wire.js\";\nimport { stem } from \"../client/media/client.js\";\nimport type { QueueItem } from \"../client/media/queue.js\";\nimport { filesFor, isPattern, ruleDir, ruleFor, screenFiles, uniqueName, uploadRules, type Screened } from \"../client/media/rules.js\";\nimport { useContentKitClient, useErrorReporter, type ContentKitErrorHandler } from \"./context.js\";\nimport { useMediaRead, type UseMediaRead } from \"./read.js\";\nimport { useUploadQueue, type UseUploadQueue } from \"./upload.js\";\n\nexport interface MediaFolderOptions {\n /**\n * The upload paths shown and added to, e.g. [\"images/{name}\",\n * \"videos/{name}\"]: one group each. Default every pattern path of the kind\n * the server does not name. A file goes to the first path whose rule takes\n * its type.\n */\n paths?: readonly string[];\n /**\n * \"manual\" (default): commit() adds the uploaded files. \"auto\": each\n * upload commits once those before it have, in the order added (a draft).\n */\n commit?: \"manual\" | \"auto\";\n /** A file's name under its path; default its own, numbered around names taken. */\n name?: (file: File) => string;\n /** Files uploading at once. Default 2. */\n concurrency?: number;\n /** Poll interval (ms) while uploads process. Default 2500. */\n poll?: number | false;\n /** Every failure (uploads, commits, updates, processing); default the provider's. */\n onError?: ContentKitErrorHandler;\n /** Overrides the provider's client. */\n client?: ContentKitClient;\n}\n\nexport interface FolderGroup {\n /** The upload path, e.g. \"images/{name}\". */\n path: string;\n /** Its rules, once an editor read states them. */\n rule?: UploadRule;\n /** Its uploads in manifest order. */\n files: FileInfo[];\n}\n\nexport interface UseMediaFolder {\n /** The item; null until the host has one (files added meanwhile wait for it). */\n ref: RefBody | null;\n /** The options the folder was opened with. */\n options: MediaFolderOptions;\n /** The editor read of the item (every window, polled while processing). */\n read: UseMediaRead;\n groups: FolderGroup[];\n /** Every group's uploads. */\n uploads: FileInfo[];\n /** The upload queue; a file leaves it once the editor read lists it. */\n queue: UseUploadQueue;\n /**\n * Files added before the item and its editor read (the upload rules) were\n * known. They are screened and queued when the rules arrive, or refused\n * when the read fails or states no rules (the actor may not edit the item).\n */\n waiting: readonly File[];\n /** Screens files against the rules and queues the rest (once the rules are known); refusals land in refused. */\n add: (files: Iterable<File>) => void;\n /** The last screening's refusals. */\n refused: Screened[\"refused\"];\n dismiss: () => void;\n /** Moves an upload to index among its group's uploads. */\n move: (path: string, index: number) => Promise<void>;\n remove: (paths: readonly string[]) => Promise<void>;\n /** Renames an upload within its path; the extension stays unless name has one. */\n rename: (path: string, name: string) => Promise<void>;\n edit: (path: string, edit: Edit | null) => Promise<void>;\n /** Uploads file in place of the upload at path (same position and meta, no edit). */\n replace: (path: string, file: File) => Promise<void>;\n /** Commits the uploaded files in queue order. */\n commit: () => Promise<FileInfo[]>;\n /** Stops every upload, aborts its multipart upload and empties the queue (before deleting a draft). */\n discard: () => void;\n /** An update (move, remove, rename, edit, replace) is running. */\n updating: boolean;\n committing: boolean;\n /**\n * Work is in flight: files wait for the rules, uploads are queued or\n * running, an automatic commit is due, or a commit or update runs.\n * Leaving the page now interrupts it.\n */\n busy: boolean;\n /** The item's uploads plus the files on their way in (waiting, queued, uploading, uploaded, being added); failed uploads are not counted. */\n fileCount: number;\n /** The last update's or commit's failure; cleared by the next success. */\n error?: ContentKitError;\n}\n\nconst ALL = { start: 0, end: Number.MAX_SAFE_INTEGER, chunk: 200 };\n\n/** The upload's name under its group (\"images/a.png\" in \"images/{name}\" is \"a.png\"). */\nexport const nameIn = (group: { path: string }, path: string) => path.slice(ruleDir(group.path).length);\n\n/** Its smallest derived image with a URL: a thumbnail for an editor. */\nexport function thumbnailOf(read: ReadResult | null | undefined, upload: FileInfo): FileInfo | undefined {\n let best: FileInfo | undefined;\n for (const f of read?.files ?? []) {\n if (f.upload || f.from !== upload.path || !f.url || !f.type.startsWith(\"image/\")) continue;\n if (!best || (f.w ?? Infinity) < (best.w ?? Infinity)) best = f;\n }\n return best;\n}\n\nconst NO_ITEM: RefBody = { kind: \"\", id: \"\" };\nconst keyOf = (ref: RefBody | null) => (ref ? itemKey(ref) : \"\");\n\nconst noRules = new WeakMap<ReadResult, ContentKitError>();\n/** The refusal for files added to an item whose read carries no upload rules (one per read). */\nfunction notEditor(read: ReadResult): ContentKitError {\n let e = noRules.get(read);\n if (!e) noRules.set(read, (e = new ContentKitError(\"forbidden\", \"the editor read states no upload rules\", { status: 403 })));\n return e;\n}\n\n/** Queue items still on their way in: not failed, and not yet listed by the read. */\nfunction inbound(items: readonly QueueItem[], listed: ReadonlySet<string>) {\n return items.filter((i) => i.status !== \"failed\" && !(i.status === \"committed\" && listed.has(i.path)));\n}\n\n/**\n * An item's folder for its editors: the editor read, an upload queue, the\n * kind's upload rules screening files before they upload, and the updates\n * (move, remove, rename, edit, replace). Files added before the editor read\n * arrives wait for its rules; with a null ref they wait for the item too (a\n * draft created on the first drop). Uploads the worker fails after the read\n * are reported once (\"folder.process\"). Another ref gets a fresh queue.\n */\nexport function useMediaFolder(ref: RefBody | null | undefined, o: MediaFolderOptions = {}): UseMediaFolder {\n const client = useContentKitClient(o.client);\n const report = useErrorReporter(o.onError);\n const item = ref ?? null;\n const key = keyOf(item);\n const target = item ?? NO_ITEM;\n // Without an item there is nothing to read: a supplied null read never fetches.\n const read = useMediaRead(target, { editor: true, window: ALL, poll: o.poll, client, read: item ? undefined : null });\n const rules = uploadRules(read.read);\n const pathsKey = o.paths?.join(\"\\n\");\n const groupPaths = useMemo(\n () => (pathsKey !== undefined ? pathsKey.split(\"\\n\").filter(Boolean) : rules.filter((r) => isPattern(r.path) && !r.named).map((r) => r.path)),\n [pathsKey, rules],\n );\n const groups = useMemo((): FolderGroup[] => {\n const known: { path: string }[] = rules.length ? rules : groupPaths.map((path) => ({ path }));\n return groupPaths.map((path) => ({ path, rule: rules.find((r) => r.path === path), files: filesFor(read.read, { path }, known) }));\n }, [groupPaths, rules, read.read]);\n const uploads = useMemo(() => groups.flatMap((g) => g.files), [groups]);\n const listed = useMemo(() => new Set(uploads.map((f) => f.path)), [uploads]);\n\n const raw = useUploadQueue({ client, ref: target, path: `${ruleDir(groupPaths[0] ?? \"{name}\")}{name}`, concurrency: o.concurrency });\n // The ItemUploadQueue itself is stable per item; the hook's wrapper is new every render.\n const q = raw.queue;\n // A committed file stays in the queue until the read lists it, so it never drops out of view.\n const items = useMemo(() => raw.items.filter((i) => !(i.status === \"committed\" && listed.has(i.path))), [raw.items, listed]);\n const queue = { ...raw, items };\n const [refused, setRefused] = useState<Screened[\"refused\"]>([]);\n const [waiting, setWaiting] = useState<readonly File[]>([]);\n const pending = useRef<File[]>([]);\n const [updating, setUpdating] = useState(0);\n const [committing, setCommitting] = useState(false);\n const [error, setError] = useState<ContentKitError>();\n // An automatic commit that failed: no more until commit() or new files.\n const [stall, setStall] = useState<{ key: string; error: ContentKitError }>();\n const stalled = stall?.key === key ? stall.error : undefined;\n const opts = useRef(o);\n opts.current = o;\n // The rules are known once the item's editor read states them. A failed read\n // refuses what waits, and so does one without rules: the server answered a\n // viewer's read, so nothing uploads here.\n const ready = !!item && !!read.read?.uploads;\n const loadError = !item ? undefined : !read.read ? read.error : !read.read.uploads ? notEditor(read.read) : undefined;\n const live = useRef({ groups, ref: target, listed, ready, loadError });\n live.current = { groups, ref: target, listed, ready, loadError };\n\n const place = useCallback(\n (files: File[]) => {\n const { groups, listed } = live.current;\n const gs = groups.map((g) => g.rule ?? { path: g.path, types: [], max_bytes: 0 });\n const queued = inbound(q.getSnapshot().items, listed);\n const inGroup = (path: string, rule: UploadRule) => ruleFor(gs, path)?.path === rule.path;\n const s = screenFiles(files, gs, (rule) => groups.find((g) => g.path === rule.path)!.files.length + queued.filter((i) => inGroup(i.path, rule)).length);\n const taken = new Map<string, string[]>();\n const takenIn = (rule: UploadRule) => {\n let t = taken.get(rule.path);\n if (!t) {\n const g = groups.find((x) => x.path === rule.path)!;\n t = [...g.files.map((f) => nameIn(g, f.path)), ...queued.filter((i) => inGroup(i.path, rule)).map((i) => nameIn(g, i.path))];\n taken.set(rule.path, t);\n }\n return t;\n };\n const paths = new Map<File, string>();\n for (const { file, rule } of s.accepted) {\n const t = takenIn(rule);\n const name = opts.current.name?.(file) ?? uniqueName(file.name, t);\n t.push(name);\n paths.set(file, ruleDir(rule.path) + name);\n }\n if (paths.size) {\n q.add(paths.keys(), { path: (f) => paths.get(f)! });\n // New files lift a block and restart a queue discard() paused.\n q.start();\n setStall(undefined);\n }\n setRefused(s.refused);\n for (const r of s.refused) report(r.error, \"upload\", r.file.name);\n },\n [q, report],\n );\n\n // Screens what waits once the rules are known; a failed read refuses it (the read failure is reported once, as folder.load).\n const flush = useCallback(() => {\n const files = pending.current;\n const { ready, loadError } = live.current;\n if (!files.length || (!ready && !loadError)) return false;\n pending.current = [];\n setWaiting([]);\n if (ready) place(files);\n else setRefused(files.map((file) => ({ file, error: loadError! })));\n return true;\n }, [place]);\n\n const add = useCallback(\n (files: Iterable<File>) => {\n const list = [...files];\n if (!list.length) return;\n pending.current = [...pending.current, ...list];\n if (!flush()) setWaiting(pending.current);\n },\n [flush],\n );\n useEffect(() => void flush(), [flush, ready, loadError]);\n\n const update = useCallback(\n async (ops: Op[], run?: () => Promise<unknown>) => {\n setUpdating((n) => n + 1);\n try {\n if (run) await run();\n else await client.media.commit(live.current.ref, ops);\n setError(undefined);\n } catch (e) {\n const err = toContentKitError(e);\n if (err.code !== \"aborted\") setError(err);\n report(err, \"folder.update\");\n throw err;\n } finally {\n setUpdating((n) => n - 1);\n }\n },\n [client, report],\n );\n\n const commit = useCallback(async () => {\n setCommitting(true);\n try {\n const files = await q.commit(undefined, { head: opts.current.commit === \"auto\" });\n setError(undefined);\n return files;\n } catch (e) {\n const err = toContentKitError(e);\n setError(err);\n report(err, \"folder.commit\");\n throw err;\n } finally {\n setCommitting(false);\n }\n }, [q, report]);\n\n // Committed files leave the queue once the read lists them, or once a read lands without them.\n useEffect(() => {\n for (const i of raw.items) if (i.status === \"committed\" && (listed.has(i.path) || !read.loading)) q.remove(i.id);\n }, [raw.items, listed, read.loading, q]);\n\n // A draft commits the uploaded head of the queue as it finishes; a failed commit waits for commit().\n const auto = o.commit === \"auto\";\n const headReady = items.find((i) => i.status !== \"committed\")?.status === \"uploaded\";\n useEffect(() => {\n if (!auto || !headReady || committing || stalled) return;\n commit().catch((e: unknown) => setStall({ key, error: toContentKitError(e) }));\n }, [auto, headReady, committing, stalled, commit, key]);\n const manualCommit = useCallback(async () => {\n setStall(undefined);\n return commit();\n }, [commit]);\n\n // Each failed upload and queue-wide refusal is reported once; rows keep showing it.\n const seen = useRef(new Set<unknown>());\n useEffect(() => {\n for (const i of raw.items) {\n if (i.status !== \"failed\" || !i.error || seen.current.has(i.error)) continue;\n seen.current.add(i.error);\n report(i.error, \"upload\", i.file.name);\n }\n if (raw.blocked && !seen.current.has(raw.blocked)) {\n seen.current.add(raw.blocked);\n report(raw.blocked, \"upload\");\n }\n }, [raw.items, raw.blocked, report]);\n\n // Processing failures that appear after the item's first read.\n const failures = useRef<{ key: string; seen: Set<string> } | null>(null);\n useEffect(() => {\n if (!read.read) return;\n const failed = uploads.filter((f) => f.failed);\n if (failures.current?.key !== key) {\n failures.current = { key, seen: new Set(failed.map((f) => `${f.path}:${f.failed!.of}`)) };\n return;\n }\n for (const f of failed) {\n const id = `${f.path}:${f.failed!.of}`;\n if (failures.current.seen.has(id)) continue;\n failures.current.seen.add(id);\n report(failureError(f.failed!), \"folder.process\", f.path.split(\"/\").pop());\n }\n }, [read.read, uploads, report, key]);\n\n useEffect(() => void (read.error && report(read.error, \"folder.load\")), [read.error, report]);\n\n const busy =\n waiting.length > 0 ||\n committing ||\n updating > 0 ||\n (auto && headReady && !stalled) ||\n items.some((i) => i.status === \"uploading\" || (i.status === \"queued\" && !raw.blocked));\n const fileCount = uploads.length + waiting.length + inbound(items, listed).length;\n\n // Reads the latest groups, so callbacks stay stable.\n const groupOf = (path: string) => live.current.groups.find((g) => ruleFor([{ path: g.path }], path));\n return {\n ref: item,\n options: o,\n read,\n groups,\n uploads,\n queue,\n waiting,\n add,\n refused,\n dismiss: useCallback(() => setRefused([]), []),\n move: useCallback((path, index) => update([{ op: \"move\", path, index }]), [update]),\n remove: useCallback((paths) => update(paths.map((path): Op => ({ op: \"remove\", path }))), [update]),\n rename: useCallback(\n (path, name) => {\n const g = groupOf(path);\n const to = (g ? ruleDir(g.path) : path.slice(0, path.lastIndexOf(\"/\") + 1)) + name.trim();\n if (stem(to) === stem(path)) return Promise.resolve();\n if (g?.files.some((f) => stem(f.path) === stem(to))) {\n const err = new ContentKitError(\"conflict\", `${name} exists`, { status: 409 });\n setError(err);\n return Promise.reject(err);\n }\n return update([{ op: \"rename\", path, to }]);\n },\n [update],\n ),\n edit: useCallback((path, edit) => update([{ op: \"edit\", path, ...(edit ? { edit } : {}) }]), [update]),\n replace: useCallback(\n (path, file) => update([], () => client.media.put(file, { ref: live.current.ref, path: stem(path), wait: false })),\n [update, client],\n ),\n commit: manualCommit,\n discard: useCallback(() => {\n pending.current = [];\n setWaiting([]);\n for (const i of q.getSnapshot().items) q.remove(i.id);\n q.pause();\n }, [q]),\n updating: updating > 0,\n committing,\n busy,\n fileCount,\n error: error ?? stalled,\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,115 @@
1
+ import { ContentKitClient } from '../client/client.js';
2
+ import { ContentKitError } from '../client/errors.js';
3
+ import { ContentMemberships, ContentModel, Edge, EdgeInput, Group, GroupCard, GroupDetail, GroupInput, GroupUpdate, MemberRef, Membership, MergeReport, MyGroup, Name, Edit } from '../client/generated/wire.js';
4
+ import { GroupImageSlot, MyGroupQuery } from '../client/groups.js';
5
+ import { UseList } from './use-resource.js';
6
+ export interface UseKinds {
7
+ /** null until it loads (or when it fails). */
8
+ kinds: ContentModel | null;
9
+ loading: boolean;
10
+ error?: ContentKitError;
11
+ reload: () => void;
12
+ }
13
+ /** The site's content model (GET /v1/kinds): languages, tiers, kinds, group kinds and content filters; one read per client. */
14
+ export declare function useKinds(o?: {
15
+ client?: ContentKitClient;
16
+ }): UseKinds;
17
+ export type GroupSort = "name" | "count" | "newest";
18
+ export interface GroupFilter {
19
+ kind?: string;
20
+ /** Children of this group. */
21
+ parent?: string;
22
+ /** Groups scoped by this group (characters of a series). */
23
+ scope?: string;
24
+ slug?: string;
25
+ /** Sort names starting with this letter (a-z), or with anything else ("#"). */
26
+ letter?: string;
27
+ /** Names, counts and paths in this language; default the provider's. */
28
+ language?: string;
29
+ /** Count contents of this kind only. */
30
+ contentKind?: string;
31
+ /** Default "name". */
32
+ sort?: GroupSort;
33
+ /** Default 50, at most 200. */
34
+ pageSize?: number;
35
+ }
36
+ /** Active groups with name, path, cover and count, a cursor page at a time. */
37
+ export declare function useGroups(f?: GroupFilter, o?: {
38
+ client?: ContentKitClient;
39
+ }): UseList<GroupCard>;
40
+ export interface UseGroup {
41
+ /** The group as its page shows it; null before create and while loading. A merged group reads as its survivor. */
42
+ group: GroupDetail | null;
43
+ /** What this hook deleted, until restored: reads no longer answer for it. */
44
+ deleted: Group | null;
45
+ loading: boolean;
46
+ error?: ContentKitError;
47
+ /** Writes in flight. */
48
+ saving: boolean;
49
+ reload: () => void;
50
+ /** Creates the group; the hook then shows it. */
51
+ create: (input: GroupInput) => Promise<Group>;
52
+ update: (patch: GroupUpdate) => Promise<Group>;
53
+ remove: () => Promise<Group>;
54
+ restore: () => Promise<Group>;
55
+ /** Replaces ("set"), adds or removes names and aliases; a new canonical name demotes the old one to an alias. */
56
+ names: (op: "set" | "add" | "remove", names: Name[]) => Promise<Name[]>;
57
+ edges: (op: "add" | "remove", edges: EdgeInput[]) => Promise<Edge[]>;
58
+ /** Merges the group into another of its kind; the hook then shows the survivor. */
59
+ merge: (into: string) => Promise<MergeReport>;
60
+ /** Uploads an image (cropped by edit) and selects it as the cover or avatar. */
61
+ uploadImage: (slot: GroupImageSlot, file: Blob & {
62
+ name?: string;
63
+ }, o?: {
64
+ wait?: boolean;
65
+ edit?: Edit;
66
+ }) => Promise<Group>;
67
+ /** Shows the images uploaded here until their renditions exist. */
68
+ previews: Partial<Record<GroupImageSlot, string>>;
69
+ }
70
+ /** One group: its page for anyone; create, edit, names, edges, cover, merge, delete and restore for staff (root:groups:write). null creates. */
71
+ export declare function useGroup(id: string | null | undefined, o?: {
72
+ language?: string;
73
+ initial?: GroupDetail;
74
+ client?: ContentKitClient;
75
+ }): UseGroup;
76
+ /** Groups the signed-in person owns, edits or belongs to, a cursor page at a time; nothing signed out. */
77
+ export declare function useMyGroups(q?: Omit<MyGroupQuery, "cursor" | "limit"> & {
78
+ pageSize?: number;
79
+ }, o?: {
80
+ client?: ContentKitClient;
81
+ }): UseList<MyGroup>;
82
+ /** A membership with a stable id: (group, content, version, role) is its identity. */
83
+ export type Member = Membership & {
84
+ id: string;
85
+ };
86
+ export declare const memberId: (m: Membership) => string;
87
+ export interface UseGroupMembers extends UseList<Member> {
88
+ saving: boolean;
89
+ /** Adds members, or updates their role's position, window and state. */
90
+ add: (members: Membership[]) => Promise<number>;
91
+ remove: (members: Membership[]) => Promise<number>;
92
+ /** Orders an ordered group: the listed members first, in list order. */
93
+ reorder: (members: MemberRef[]) => Promise<number>;
94
+ }
95
+ /** Staff: a group's memberships in member order, with writes. */
96
+ export declare function useGroupMembers(id: string | null | undefined, o?: {
97
+ pageSize?: number;
98
+ client?: ContentKitClient;
99
+ }): UseGroupMembers;
100
+ export interface UseContentGroups {
101
+ memberships: Membership[];
102
+ /** The groups the memberships name. */
103
+ groups: ReadonlyMap<string, GroupCard>;
104
+ loading: boolean;
105
+ error?: ContentKitError;
106
+ saving: boolean;
107
+ reload: () => void;
108
+ /** Replaces the content's memberships, its versions' included, in these group kinds (every kind its kind joins when omitted). */
109
+ set: (memberships: Membership[], kinds?: string[]) => Promise<ContentMemberships>;
110
+ }
111
+ /** Staff: one content's memberships, its versions' included, and their replacement. */
112
+ export declare function useContentGroups(contentId: string | null | undefined, o?: {
113
+ language?: string;
114
+ client?: ContentKitClient;
115
+ }): UseContentGroups;