@openrails/contentkit-ui 0.86.0 → 0.87.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 (127) hide show
  1. package/README.md +39 -14
  2. package/dist/client/beacons.js +5 -1
  3. package/dist/client/beacons.js.map +1 -1
  4. package/dist/client/content/types.d.ts +4 -2
  5. package/dist/client/content/types.js.map +1 -1
  6. package/dist/client/contents.d.ts +8 -6
  7. package/dist/client/contents.js +10 -7
  8. package/dist/client/contents.js.map +1 -1
  9. package/dist/client/discovery.js +3 -0
  10. package/dist/client/discovery.js.map +1 -1
  11. package/dist/client/errors.d.ts +21 -3
  12. package/dist/client/errors.js +44 -14
  13. package/dist/client/errors.js.map +1 -1
  14. package/dist/client/gallery.d.ts +3 -5
  15. package/dist/client/gallery.js +16 -20
  16. package/dist/client/gallery.js.map +1 -1
  17. package/dist/client/generated/error-codes.d.ts +1 -1
  18. package/dist/client/generated/error-codes.js +8 -0
  19. package/dist/client/generated/error-codes.js.map +1 -1
  20. package/dist/client/generated/routes.d.ts +16 -3
  21. package/dist/client/generated/routes.js +28 -3
  22. package/dist/client/generated/routes.js.map +1 -1
  23. package/dist/client/generated/wire.d.ts +43 -14
  24. package/dist/client/generated/wire.js.map +1 -1
  25. package/dist/client/http.js +27 -27
  26. package/dist/client/http.js.map +1 -1
  27. package/dist/client/index.d.ts +2 -2
  28. package/dist/client/media/client.d.ts +10 -4
  29. package/dist/client/media/client.js +33 -7
  30. package/dist/client/media/client.js.map +1 -1
  31. package/dist/client/media/rules.d.ts +1 -1
  32. package/dist/client/media/rules.js +6 -6
  33. package/dist/client/media/rules.js.map +1 -1
  34. package/dist/client/media/transport.d.ts +1 -1
  35. package/dist/client/media/transport.js +16 -16
  36. package/dist/client/media/transport.js.map +1 -1
  37. package/dist/client/media/windows.js +1 -1
  38. package/dist/client/media/windows.js.map +1 -1
  39. package/dist/client/public.d.ts +2 -2
  40. package/dist/client/public.js +2 -2
  41. package/dist/client/public.js.map +1 -1
  42. package/dist/client/versions.d.ts +6 -4
  43. package/dist/client/versions.js +24 -26
  44. package/dist/client/versions.js.map +1 -1
  45. package/dist/client.js +25 -25
  46. package/dist/components/admin/content-manager.js +233 -232
  47. package/dist/components/admin/content-manager.js.map +1 -1
  48. package/dist/components/content/card.d.ts +5 -3
  49. package/dist/components/content/card.js +131 -109
  50. package/dist/components/content/card.js.map +1 -1
  51. package/dist/components/content/download-button.d.ts +26 -0
  52. package/dist/components/content/download-button.js +51 -0
  53. package/dist/components/content/download-button.js.map +1 -0
  54. package/dist/components/content/editors.d.ts +8 -0
  55. package/dist/components/content/editors.js +299 -229
  56. package/dist/components/content/editors.js.map +1 -1
  57. package/dist/components/content/grid.d.ts +8 -1
  58. package/dist/components/content/grid.js +125 -76
  59. package/dist/components/content/grid.js.map +1 -1
  60. package/dist/components/content/index.d.ts +3 -2
  61. package/dist/components/content/listing.d.ts +3 -1
  62. package/dist/components/content/listing.js.map +1 -1
  63. package/dist/components/content/paywall.js +61 -61
  64. package/dist/components/content/paywall.js.map +1 -1
  65. package/dist/components/media-gallery.d.ts +1 -4
  66. package/dist/components/media-gallery.js +19 -34
  67. package/dist/components/media-gallery.js.map +1 -1
  68. package/dist/components/social/comments.js +1 -1
  69. package/dist/components/social/comments.js.map +1 -1
  70. package/dist/components/social/engagement.d.ts +2 -1
  71. package/dist/components/social/engagement.js +5 -3
  72. package/dist/components/social/engagement.js.map +1 -1
  73. package/dist/i18n/messages.js +49 -46
  74. package/dist/i18n/messages.js.map +1 -1
  75. package/dist/index.js +12 -11
  76. package/dist/locales/de.js +15 -3
  77. package/dist/locales/de.js.map +1 -1
  78. package/dist/locales/discovery/de.js +3 -0
  79. package/dist/locales/discovery/de.js.map +1 -1
  80. package/dist/locales/discovery/en.d.ts +3 -0
  81. package/dist/locales/discovery/en.js +3 -0
  82. package/dist/locales/discovery/en.js.map +1 -1
  83. package/dist/locales/discovery/es.js +3 -0
  84. package/dist/locales/discovery/es.js.map +1 -1
  85. package/dist/locales/discovery/ja.js +3 -0
  86. package/dist/locales/discovery/ja.js.map +1 -1
  87. package/dist/locales/discovery/ko.js +3 -0
  88. package/dist/locales/discovery/ko.js.map +1 -1
  89. package/dist/locales/discovery/zh.js +3 -0
  90. package/dist/locales/discovery/zh.js.map +1 -1
  91. package/dist/locales/en.d.ts +17 -2
  92. package/dist/locales/en.js +15 -3
  93. package/dist/locales/en.js.map +1 -1
  94. package/dist/locales/es.js +15 -3
  95. package/dist/locales/es.js.map +1 -1
  96. package/dist/locales/ja.js +15 -3
  97. package/dist/locales/ja.js.map +1 -1
  98. package/dist/locales/ko.js +15 -3
  99. package/dist/locales/ko.js.map +1 -1
  100. package/dist/locales/zh.js +15 -3
  101. package/dist/locales/zh.js.map +1 -1
  102. package/dist/react/comments.d.ts +1 -1
  103. package/dist/react/comments.js +3 -2
  104. package/dist/react/comments.js.map +1 -1
  105. package/dist/react/contents.d.ts +13 -5
  106. package/dist/react/contents.js +163 -149
  107. package/dist/react/contents.js.map +1 -1
  108. package/dist/react/context.d.ts +1 -1
  109. package/dist/react/context.js.map +1 -1
  110. package/dist/react/detail.d.ts +7 -1
  111. package/dist/react/detail.js +9 -6
  112. package/dist/react/detail.js.map +1 -1
  113. package/dist/react/discovery.d.ts +11 -0
  114. package/dist/react/discovery.js +49 -30
  115. package/dist/react/discovery.js.map +1 -1
  116. package/dist/react/engagement.d.ts +6 -2
  117. package/dist/react/engagement.js +2 -6
  118. package/dist/react/engagement.js.map +1 -1
  119. package/dist/react/index.d.ts +3 -3
  120. package/dist/react/public.js +17 -16
  121. package/dist/react/public.js.map +1 -1
  122. package/dist/react/versions.d.ts +25 -5
  123. package/dist/react/versions.js +58 -37
  124. package/dist/react/versions.js.map +1 -1
  125. package/dist/react.js +9 -9
  126. package/dist/styles.css +1 -1
  127. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"detail.js","names":[],"sources":["../../src/react/detail.ts"],"sourcesContent":["import { useMemo } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { ContentKitError } from \"../client/errors.js\";\nimport type { Content, Decision, Version } from \"../client/generated/wire.js\";\nimport { parseVersionCode } from \"../urls/index.js\";\nimport { useVersionParam } from \"./location.js\";\nimport { keyOf, useContentScope, useResource } from \"./use-resource.js\";\n\n/** A content's live versions, first first. */\nexport function liveVersions(c: Content | null | undefined): Version[] {\n return (c?.versions ?? []).filter((v) => v.live).sort((a, b) => a.position - b.position);\n}\n\n/** The version a reader of language opens by default: the first live one serving it, else the first live one. */\nexport function defaultVersion(c: Content | null | undefined, language?: string): Version | null {\n const live = liveVersions(c);\n return (language ? live.find((v) => v.languages.includes(language)) : undefined) ?? live[0] ?? null;\n}\n\n/** The version selected names: its code (any spelling, the page's ?v=) or its id; undefined when none. */\nexport function findVersion(versions: readonly Version[], selected: string | null | undefined): Version | undefined {\n if (!selected) return undefined;\n const code = parseVersionCode(selected);\n return versions.find((v) => v.id === selected) ?? (code ? versions.find((v) => v.code === code) : undefined);\n}\n\nexport interface UseContentDetail {\n content: Content | null;\n /** Live versions, first first. */\n versions: Version[];\n /** The version shown: the one asked for (?v=) when live, else the language's default. */\n version: Version | null;\n /** The shown version's access decision: allowed, what is sold, previews. */\n decision: Decision | null;\n /** Every language a live version serves, in version order. */\n languages: string[];\n loading: boolean;\n error?: ContentKitError;\n reload: () => void;\n}\n\nexport interface ContentDetailOptions {\n language?: string;\n /** The version to show, by code or id; default the page's ?v=. Unknown or not live: the language's default. */\n version?: string | null;\n /** The page's location for its ?v=; default window.location. */\n location?: string;\n client?: ContentKitClient;\n}\n\n/**\n * A content page's data: text in the reader's language, live versions with\n * their segments and access decisions, groups, attributes and the version to show.\n */\nexport function useContentDetail(id: string | null | undefined, o: ContentDetailOptions = {}): UseContentDetail {\n const { client, store, scope, language: provided } = useContentScope(o.client);\n const language = o.language ?? provided;\n const [param] = useVersionParam({ location: o.location });\n const selected = o.version === undefined ? param : o.version;\n const key = id ? keyOf(\"content-detail\", scope, id, language) : null;\n const r = useResource<Content>(store, key, { type: \"content-detail\", id: id ?? undefined }, (s) => client.contents.get(id!, language, s));\n const content = r.data ?? null;\n return useMemo(() => {\n const versions = liveVersions(content);\n const version = findVersion(versions, selected) ?? defaultVersion(content, language);\n return {\n content,\n versions,\n version,\n decision: version?.decision ?? content?.decision ?? null,\n languages: [...new Set(versions.flatMap((v) => v.languages))],\n loading: !!key && (r.loading || !r.loaded),\n error: r.error,\n reload: r.reload,\n };\n }, [content, selected, language, key, r.loading, r.loaded, r.error, r.reload]);\n}\n"],"mappings":";;;;;AASA,SAAgB,EAAa,GAA0C;CACrE,QAAQ,GAAG,YAAY,CAAC,EAAA,CAAG,QAAQ,MAAM,EAAE,IAAI,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ;AACzF;AAGA,SAAgB,EAAe,GAA+B,GAAmC;CAC/F,IAAM,IAAO,EAAa,CAAC;CAC3B,QAAQ,IAAW,EAAK,MAAM,MAAM,EAAE,UAAU,SAAS,CAAQ,CAAC,IAAI,KAAA,MAAc,EAAK,MAAM;AACjG;AAGA,SAAgB,EAAY,GAA8B,GAA0D;CAClH,IAAI,CAAC,GAAU;CACf,IAAM,IAAO,EAAiB,CAAQ;CACtC,OAAO,EAAS,MAAM,MAAM,EAAE,OAAO,CAAQ,MAAM,IAAO,EAAS,MAAM,MAAM,EAAE,SAAS,CAAI,IAAI,KAAA;AACpG;AA8BA,SAAgB,EAAiB,GAA+B,IAA0B,CAAC,GAAqB;CAC9G,IAAM,EAAE,WAAQ,UAAO,UAAO,UAAU,MAAa,EAAgB,EAAE,MAAM,GACvE,IAAW,EAAE,YAAY,GACzB,CAAC,KAAS,EAAgB,EAAE,UAAU,EAAE,SAAS,CAAC,GAClD,IAAW,EAAE,YAAY,KAAA,IAAY,IAAQ,EAAE,SAC/C,IAAM,IAAK,EAAM,kBAAkB,GAAO,GAAI,CAAQ,IAAI,MAC1D,IAAI,EAAqB,GAAO,GAAK;EAAE,MAAM;EAAkB,IAAI,KAAM,KAAA;CAAU,IAAI,MAAM,EAAO,SAAS,IAAI,GAAK,GAAU,CAAC,CAAC,GAClI,IAAU,EAAE,QAAQ;CAC1B,OAAO,QAAc;EACnB,IAAM,IAAW,EAAa,CAAO,GAC/B,IAAU,EAAY,GAAU,CAAQ,KAAK,EAAe,GAAS,CAAQ;EACnF,OAAO;GACL;GACA;GACA;GACA,UAAU,GAAS,YAAY,GAAS,YAAY;GACpD,WAAW,CAAC,GAAG,IAAI,IAAI,EAAS,SAAS,MAAM,EAAE,SAAS,CAAC,CAAC;GAC5D,SAAS,CAAC,CAAC,MAAQ,EAAE,WAAW,CAAC,EAAE;GACnC,OAAO,EAAE;GACT,QAAQ,EAAE;EACZ;CACF,GAAG;EAAC;EAAS;EAAU;EAAU;EAAK,EAAE;EAAS,EAAE;EAAQ,EAAE;EAAO,EAAE;CAAM,CAAC;AAC/E"}
1
+ {"version":3,"file":"detail.js","names":[],"sources":["../../src/react/detail.ts"],"sourcesContent":["import { useMemo } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { ContentKitError } from \"../client/errors.js\";\nimport type { Content, Decision, Version } from \"../client/generated/wire.js\";\nimport { parseVersionCode } from \"../urls/index.js\";\nimport { useVersionParam } from \"./location.js\";\nimport { keyOf, useContentScope, useResource } from \"./use-resource.js\";\n\n/** A content's live versions, first first. */\nexport function liveVersions(c: Content | null | undefined): Version[] {\n return (c?.versions ?? []).filter((v) => v.live).sort((a, b) => a.position - b.position);\n}\n\n/**\n * The version a reader of language opens by default: the first live one by\n * position serving it, else the first live one. The server picks cards'\n * versions, covers and meta by the same rule.\n */\nexport function defaultVersion(c: Content | null | undefined, language?: string): Version | null {\n return editionOf(liveVersions(c), language);\n}\n\n/** defaultVersion among versions: the first by position serving language, else the first. */\nexport function editionOf(versions: readonly Version[], language?: string): Version | null {\n const sorted = [...versions].sort((a, b) => a.position - b.position);\n return (language ? sorted.find((v) => v.languages.includes(language)) : undefined) ?? sorted[0] ?? null;\n}\n\n/** The version selected names: its code (any spelling, the page's ?v=) or its id; undefined when none. */\nexport function findVersion(versions: readonly Version[], selected: string | null | undefined): Version | undefined {\n if (!selected) return undefined;\n const code = parseVersionCode(selected);\n return versions.find((v) => v.id === selected) ?? (code ? versions.find((v) => v.code === code) : undefined);\n}\n\nexport interface UseContentDetail {\n content: Content | null;\n /** Live versions, first first. */\n versions: Version[];\n /** The version shown: the one asked for (?v=) when live, else the language's default. */\n version: Version | null;\n /** The shown version's access decision: allowed, what is sold, previews. */\n decision: Decision | null;\n /** Every language a live version serves, in version order. */\n languages: string[];\n loading: boolean;\n error?: ContentKitError;\n reload: () => void;\n}\n\nexport interface ContentDetailOptions {\n language?: string;\n /** The version to show, by code or id; default the page's ?v=. Unknown or not live: the language's default. */\n version?: string | null;\n /** The page's location for its ?v=; default window.location. */\n location?: string;\n client?: ContentKitClient;\n}\n\n/**\n * A content page's data: text in the reader's language, live versions with\n * their segments and access decisions, groups, attributes and the version to show.\n */\nexport function useContentDetail(id: string | null | undefined, o: ContentDetailOptions = {}): UseContentDetail {\n const { client, store, scope, language: provided } = useContentScope(o.client);\n const language = o.language ?? provided;\n const [param] = useVersionParam({ location: o.location });\n const selected = o.version === undefined ? param : o.version;\n const key = id ? keyOf(\"content-detail\", scope, id, language) : null;\n const r = useResource<Content>(store, key, { type: \"content-detail\", id: id ?? undefined }, (s) => client.contents.get(id!, language, s));\n const content = r.data ?? null;\n return useMemo(() => {\n const versions = liveVersions(content);\n const version = findVersion(versions, selected) ?? defaultVersion(content, language);\n return {\n content,\n versions,\n version,\n decision: version?.decision ?? content?.decision ?? null,\n languages: [...new Set(versions.flatMap((v) => v.languages))],\n loading: !!key && (r.loading || !r.loaded),\n error: r.error,\n reload: r.reload,\n };\n }, [content, selected, language, key, r.loading, r.loaded, r.error, r.reload]);\n}\n"],"mappings":";;;;;AASA,SAAgB,EAAa,GAA0C;CACrE,QAAQ,GAAG,YAAY,CAAC,EAAA,CAAG,QAAQ,MAAM,EAAE,IAAI,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ;AACzF;AAOA,SAAgB,EAAe,GAA+B,GAAmC;CAC/F,OAAO,EAAU,EAAa,CAAC,GAAG,CAAQ;AAC5C;AAGA,SAAgB,EAAU,GAA8B,GAAmC;CACzF,IAAM,IAAS,CAAC,GAAG,CAAQ,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ;CACnE,QAAQ,IAAW,EAAO,MAAM,MAAM,EAAE,UAAU,SAAS,CAAQ,CAAC,IAAI,KAAA,MAAc,EAAO,MAAM;AACrG;AAGA,SAAgB,EAAY,GAA8B,GAA0D;CAClH,IAAI,CAAC,GAAU;CACf,IAAM,IAAO,EAAiB,CAAQ;CACtC,OAAO,EAAS,MAAM,MAAM,EAAE,OAAO,CAAQ,MAAM,IAAO,EAAS,MAAM,MAAM,EAAE,SAAS,CAAI,IAAI,KAAA;AACpG;AA8BA,SAAgB,EAAiB,GAA+B,IAA0B,CAAC,GAAqB;CAC9G,IAAM,EAAE,WAAQ,UAAO,UAAO,UAAU,MAAa,EAAgB,EAAE,MAAM,GACvE,IAAW,EAAE,YAAY,GACzB,CAAC,KAAS,EAAgB,EAAE,UAAU,EAAE,SAAS,CAAC,GAClD,IAAW,EAAE,YAAY,KAAA,IAAY,IAAQ,EAAE,SAC/C,IAAM,IAAK,EAAM,kBAAkB,GAAO,GAAI,CAAQ,IAAI,MAC1D,IAAI,EAAqB,GAAO,GAAK;EAAE,MAAM;EAAkB,IAAI,KAAM,KAAA;CAAU,IAAI,MAAM,EAAO,SAAS,IAAI,GAAK,GAAU,CAAC,CAAC,GAClI,IAAU,EAAE,QAAQ;CAC1B,OAAO,QAAc;EACnB,IAAM,IAAW,EAAa,CAAO,GAC/B,IAAU,EAAY,GAAU,CAAQ,KAAK,EAAe,GAAS,CAAQ;EACnF,OAAO;GACL;GACA;GACA;GACA,UAAU,GAAS,YAAY,GAAS,YAAY;GACpD,WAAW,CAAC,GAAG,IAAI,IAAI,EAAS,SAAS,MAAM,EAAE,SAAS,CAAC,CAAC;GAC5D,SAAS,CAAC,CAAC,MAAQ,EAAE,WAAW,CAAC,EAAE;GACnC,OAAO,EAAE;GACT,QAAQ,EAAE;EACZ;CACF,GAAG;EAAC;EAAS;EAAU;EAAU;EAAK,EAAE;EAAS,EAAE;EAAQ,EAAE;EAAO,EAAE;CAAM,CAAC;AAC/E"}
@@ -9,6 +9,17 @@ import { UseList } from './use-resource.js';
9
9
  export declare function useContents(q: ContentQuery | null, o?: {
10
10
  client?: ContentKitClient;
11
11
  }): UseList<Card>;
12
+ /**
13
+ * How many contents of a list the reader doesn't open: its filters' count
14
+ * with locked cards less without them (sort and paging aside); null reads
15
+ * nothing.
16
+ */
17
+ export declare function useLockedCount(q: ContentQuery | null, o?: {
18
+ client?: ContentKitClient;
19
+ }): {
20
+ count?: number;
21
+ loading: boolean;
22
+ };
12
23
  /** Keyword search results as cards; nothing while q.q is empty. */
13
24
  export declare function useSearch(q: SearchQuery | null, o?: {
14
25
  client?: ContentKitClient;
@@ -23,13 +23,32 @@ function _(e, t = {}) {
23
23
  }, t)));
24
24
  }
25
25
  function v(e, t = {}) {
26
+ let { client: n, store: a, scope: s } = i(t.client), c = g(f(() => {
27
+ if (!e) return null;
28
+ let { sort: t, cursor: n, limit: r, card_groups: i, mode: a, ...o } = e;
29
+ return o;
30
+ }, [e])), l = o(a, c ? r("release-dates", s, "locked", c) : null, { type: "release-dates" }, async (e) => {
31
+ let t = async (t) => (await n.discovery.releaseDates({
32
+ ...c,
33
+ mode: t,
34
+ by: "month",
35
+ limit: 1e3
36
+ }, e)).dates.reduce((e, t) => e + t.count, 0), [r, i] = await Promise.all([t("lock"), t("hide")]);
37
+ return Math.max(0, r - i);
38
+ });
39
+ return {
40
+ count: l.data,
41
+ loading: l.loading
42
+ };
43
+ }
44
+ function y(e, t = {}) {
26
45
  let { client: n, store: o, scope: s } = i(t.client), c = g(e?.q.trim() ? e : null);
27
46
  return a(o, c ? r("cards", s, "search", c) : null, { type: "cards" }, async (e, t) => h(await n.discovery.search({
28
47
  ...c,
29
48
  cursor: e
30
49
  }, t)));
31
50
  }
32
- function y(e, t, n) {
51
+ function b(e, t, n) {
33
52
  let { store: r } = i(n.client), a = o(r, e, { type: "cards" }, async (e) => (await t(e)).contents), s = a.error?.code === "not_configured";
34
53
  return {
35
54
  items: a.data ?? [],
@@ -39,15 +58,15 @@ function y(e, t, n) {
39
58
  reload: a.reload
40
59
  };
41
60
  }
42
- function b(e, t = {}, n = {}) {
61
+ function x(e, t = {}, n = {}) {
43
62
  let { client: a, scope: o } = i(n.client), s = g(t);
44
- return y(e ? r("cards", o, "similar", e, s) : null, (t) => a.discovery.similar(e, s, t), n);
63
+ return b(e ? r("cards", o, "similar", e, s) : null, (t) => a.discovery.similar(e, s, t), n);
45
64
  }
46
- function x(e = {}, t = {}) {
65
+ function S(e = {}, t = {}) {
47
66
  let { client: n, scope: a, viewer: o } = i(t.client), s = g(e);
48
- return y(o === null || !s ? null : r("cards", a, "recommended", s), (e) => n.discovery.recommendations(s, e), t);
67
+ return b(o === null || !s ? null : r("cards", a, "recommended", s), (e) => n.discovery.recommendations(s, e), t);
49
68
  }
50
- function S(e = {}, t = {}) {
69
+ function C(e = {}, t = {}) {
51
70
  let { client: n, store: o, scope: s } = i(t.client), c = g(e);
52
71
  return a(o, r("release-dates", s, c), { type: "release-dates" }, async (e, t) => {
53
72
  let r = await n.discovery.releaseDates({
@@ -63,7 +82,7 @@ function S(e = {}, t = {}) {
63
82
  };
64
83
  });
65
84
  }
66
- function C(e = {}, t = {}) {
85
+ function w(e = {}, t = {}) {
67
86
  let { client: o, store: s, scope: c, viewer: u } = i(t.client), d = g(e), f = n(), p = a(s, u === null || !d ? null : r("history", c, d), { type: "history" }, async (e, t) => h(await o.discovery.history({
68
87
  ...d,
69
88
  cursor: e
@@ -73,7 +92,7 @@ function C(e = {}, t = {}) {
73
92
  forget: m
74
93
  };
75
94
  }
76
- function w(e = {}) {
95
+ function T(e = {}) {
77
96
  let { client: t, store: a, scope: s } = i(e.client), c = n(), u = e.enabled === !1 ? null : r("searches", s, e.limit ?? 10), d = o(a, u, { type: "searches" }, async (n) => (await t.discovery.recentSearches({ limit: e.limit }, n)).searches), f = l((e) => c(() => t.discovery.clearSearches(e ? { query: e } : {})), [t, c]);
78
97
  return {
79
98
  searches: d.data ?? [],
@@ -82,28 +101,28 @@ function w(e = {}) {
82
101
  clear: f
83
102
  };
84
103
  }
85
- var T = (e) => ({
104
+ var E = (e) => ({
86
105
  items: e.items.map((e) => ({
87
106
  ...e,
88
107
  id: e.content?.id ?? e.group?.id ?? ""
89
108
  })),
90
109
  next: e.next_cursor ?? null
91
110
  });
92
- function E(e = {}, t = {}) {
111
+ function D(e = {}, t = {}) {
93
112
  let { client: n, store: o, scope: s, viewer: c } = i(t.client), l = g(e);
94
- return a(o, c === null ? null : r("favorites", s, l), { type: "favorites" }, async (e, t) => T(await n.favorites.mine({
113
+ return a(o, c === null ? null : r("favorites", s, l), { type: "favorites" }, async (e, t) => E(await n.favorites.mine({
95
114
  ...l,
96
115
  cursor: e
97
116
  }, t)));
98
117
  }
99
- function D(e = {}, t = {}) {
118
+ function O(e = {}, t = {}) {
100
119
  let { client: n, store: o, scope: s, viewer: c } = i(t.client), l = g(e);
101
- return a(o, c === null ? null : r("reactions", s, l), { type: "reactions" }, async (e, t) => T(await n.reactions.mine({
120
+ return a(o, c === null ? null : r("reactions", s, l), { type: "reactions" }, async (e, t) => E(await n.reactions.mine({
102
121
  ...l,
103
122
  cursor: e
104
123
  }, t)));
105
124
  }
106
- function O(e = {}) {
125
+ function k(e = {}) {
107
126
  let { client: t, store: a, scope: s, viewer: c, language: u } = i(e.client), d = e.language ?? u, f = c === null ? null : r("content-filters", s, d), h = o(a, f, { type: "content-filters" }, (e) => t.discovery.contentFilters(d, e)), g = n(), [_, v] = m(0), y = p(h.data);
108
127
  y.current = h.data;
109
128
  let b = l(async (e) => {
@@ -149,11 +168,11 @@ function O(e = {}) {
149
168
  setIncludeDeleted: C
150
169
  };
151
170
  }
152
- var k = {
171
+ var A = {
153
172
  contents: [],
154
173
  groups: []
155
174
  };
156
- function A(t, n = {}) {
175
+ function j(t, n = {}) {
157
176
  let { client: r, language: a } = i(n.client), { debounce: o = 150, minLength: s = 1, client: c, ...l } = n, u = t.trim(), p = f(() => ({
158
177
  display_language: a,
159
178
  ...l,
@@ -163,14 +182,14 @@ function A(t, n = {}) {
163
182
  u,
164
183
  JSON.stringify(l)
165
184
  ]), [h, g] = m({
166
- ...k,
185
+ ...A,
167
186
  query: "",
168
187
  loading: !1
169
188
  });
170
189
  return d(() => {
171
190
  if (u.length < s) {
172
191
  g({
173
- ...k,
192
+ ...A,
174
193
  query: "",
175
194
  loading: !1
176
195
  });
@@ -189,7 +208,7 @@ function A(t, n = {}) {
189
208
  }), (t) => {
190
209
  let n = e(t);
191
210
  n.code !== "aborted" && g({
192
- ...k,
211
+ ...A,
193
212
  query: u,
194
213
  loading: !1,
195
214
  error: n
@@ -207,25 +226,25 @@ function A(t, n = {}) {
207
226
  s
208
227
  ]), h;
209
228
  }
210
- var j = /* @__PURE__ */ new WeakMap();
211
- function M(e, t) {
212
- let n = j.get(e);
213
- return n || j.set(e, n = new s(e, t)), n;
229
+ var M = /* @__PURE__ */ new WeakMap();
230
+ function N(e, t) {
231
+ let n = M.get(e);
232
+ return n || M.set(e, n = new s(e, t)), n;
214
233
  }
215
- function N(e = {}) {
234
+ function P(e = {}) {
216
235
  let { client: t } = i(e.client);
217
- return M(t);
236
+ return N(t);
218
237
  }
219
- function P(e, t, n = {}) {
220
- let r = N(n), { client: i, ...a } = n, o = f(() => e ? r.view(e, t, a) : null, [
238
+ function F(e, t, n = {}) {
239
+ let r = P(n), { client: i, ...a } = n, o = f(() => e ? r.view(e, t, a) : null, [
221
240
  r,
222
241
  e,
223
242
  t
224
243
  ]);
225
244
  return l((e) => o?.update(e), [o]);
226
245
  }
227
- function F(e, t) {
228
- let n = N(t), r = f(() => c(), [t.render]), i = f(() => ({
246
+ function I(e, t) {
247
+ let n = P(t), r = f(() => c(), [t.render]), i = f(() => ({
229
248
  render_id: r,
230
249
  surface: t.surface,
231
250
  ranker: t.ranker,
@@ -294,6 +313,6 @@ function F(e, t) {
294
313
  };
295
314
  }
296
315
  //#endregion
297
- export { M as beaconsFor, N as useBeacons, O as useContentFilters, _ as useContents, F as useExposure, C as useHistory, E as useMyFavorites, D as useMyReactions, w as useRecentSearches, x as useRecommendations, S as useReleaseDates, v as useSearch, b as useSimilar, A as useTypeahead, P as useViewSignal };
316
+ export { N as beaconsFor, P as useBeacons, k as useContentFilters, _ as useContents, I as useExposure, w as useHistory, v as useLockedCount, D as useMyFavorites, O as useMyReactions, T as useRecentSearches, S as useRecommendations, C as useReleaseDates, y as useSearch, x as useSimilar, j as useTypeahead, F as useViewSignal };
298
317
 
299
318
  //# sourceMappingURL=discovery.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"discovery.js","names":[],"sources":["../../src/react/discovery.ts"],"sourcesContent":["import { useCallback, useContext, useEffect, useMemo, useRef, useState } from \"react\";\nimport { Beacons, eventId, type Attribution, type BeaconOptions, type ViewSession } from \"../client/beacons.js\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { MineQuery } from \"../client/content/favorites.js\";\nimport type { ContentQuery, HistoryQuery, RailQuery, ReleaseDatesQuery, SearchQuery, TypeaheadQuery } from \"../client/discovery.js\";\nimport { toContentKitError, type ContentKitError } from \"../client/errors.js\";\nimport type { Card, ContentFilters, ContentFiltersUpdate, ContentPage, DateCount, GroupCard, Interaction, InteractionPage, RecentSearch } from \"../client/generated/wire.js\";\nimport { ContentKitContext, useGuarded } from \"./context.js\";\nimport { keyOf, useContentScope, useList, useResource, type UseList } from \"./use-resource.js\";\n\nconst page = (p: ContentPage) => ({ items: p.contents, next: p.next_cursor ?? null });\n\n/** Titles, names and paths in the provider's language unless the query says otherwise. */\nfunction useDisplay<T extends { display_language?: string }>(q: T | null): T | null {\n const language = useContext(ContentKitContext)?.language;\n return useMemo(() => (q && !q.display_language && language ? { ...q, display_language: language } : q), [q, language]);\n}\n\n/** Contents as cards, a cursor page at a time; null reads nothing. */\nexport function useContents(q: ContentQuery | null, o: { client?: ContentKitClient } = {}): UseList<Card> {\n const { client, store, scope } = useContentScope(o.client);\n const query = useDisplay(q);\n return useList(store, query ? keyOf(\"cards\", scope, \"list\", query) : null, { type: \"cards\" }, async (cursor, signal) =>\n page(await client.discovery.list({ ...query, cursor: cursor as string | undefined }, signal)),\n );\n}\n\n/** Keyword search results as cards; nothing while q.q is empty. */\nexport function useSearch(q: SearchQuery | null, o: { client?: ContentKitClient } = {}): UseList<Card> {\n const { client, store, scope } = useContentScope(o.client);\n const query = useDisplay(q?.q.trim() ? q : null);\n return useList(store, query ? keyOf(\"cards\", scope, \"search\", query) : null, { type: \"cards\" }, async (cursor, signal) =>\n page(await client.discovery.search({ ...query!, cursor: cursor as string | undefined }, signal)),\n );\n}\n\nexport interface UseRail {\n items: Card[];\n loading: boolean;\n error?: ContentKitError;\n /** The site runs without the signal plane (not_configured): leave the rail out. */\n unavailable: boolean;\n reload: () => void;\n}\n\nfunction useRail(key: string | null, fetch: (signal: AbortSignal) => Promise<ContentPage>, o: { client?: ContentKitClient }): UseRail {\n const { store } = useContentScope(o.client);\n const r = useResource<Card[]>(store, key, { type: \"cards\" }, async (signal) => (await fetch(signal)).contents);\n const unavailable = r.error?.code === \"not_configured\";\n return { items: r.data ?? [], loading: !!key && (r.loading || !r.loaded), error: unavailable ? undefined : r.error, unavailable, reload: r.reload };\n}\n\n/** Contents like one: co-engagement, or the site's configured source. */\nexport function useSimilar(id: string | null | undefined, q: RailQuery = {}, o: { client?: ContentKitClient } = {}): UseRail {\n const { client, scope } = useContentScope(o.client);\n const query = useDisplay(q)!;\n return useRail(id ? keyOf(\"cards\", scope, \"similar\", id, query) : null, (s) => client.discovery.similar(id!, query, s), o);\n}\n\n/** Contents for the signed-in reader; nothing signed out, or when q is null. */\nexport function useRecommendations(q: RailQuery | null = {}, o: { client?: ContentKitClient } = {}): UseRail {\n const { client, scope, viewer } = useContentScope(o.client);\n const query = useDisplay(q);\n return useRail(viewer === null || !query ? null : keyOf(\"cards\", scope, \"recommended\", query), (s) => client.discovery.recommendations(query!, s), o);\n}\n\n/** A release date bucket, keyed by its date. */\nexport type ReleaseDate = DateCount & { id: string };\n\n/** The release date index: per month or day, newest first. */\nexport function useReleaseDates(q: ReleaseDatesQuery = {}, o: { client?: ContentKitClient } = {}): UseList<ReleaseDate> {\n const { client, store, scope } = useContentScope(o.client);\n const query = useDisplay(q)!;\n return useList(store, keyOf(\"release-dates\", scope, query), { type: \"release-dates\" }, async (cursor, signal) => {\n const r = await client.discovery.releaseDates({ ...query, cursor: cursor as string | undefined }, signal);\n return { items: r.dates.map((d) => ({ ...d, id: d.date })), next: r.next_cursor ?? null };\n });\n}\n\nexport interface UseHistory extends UseList<Card> {\n /** Erases the reader's history: of one content item, one kind, or (no argument) everything. */\n forget: (o?: { kind?: string; content_id?: string }) => Promise<void>;\n}\n\n/** What the signed-in reader engaged with, most recent first; nothing signed out, or when q is null. */\nexport function useHistory(q: HistoryQuery | null = {}, o: { client?: ContentKitClient } = {}): UseHistory {\n const { client, store, scope, viewer } = useContentScope(o.client);\n const query = useDisplay(q);\n const guarded = useGuarded();\n const list = useList(store, viewer === null || !query ? null : keyOf(\"history\", scope, query), { type: \"history\" }, async (cursor, signal) =>\n page(await client.discovery.history({ ...query, cursor: cursor as string | undefined }, signal)),\n );\n const forget = useCallback((f: { kind?: string; content_id?: string } = {}) => guarded(() => client.discovery.forgetHistory(f)), [client, guarded]);\n return { ...list, forget };\n}\n\nexport interface UseRecentSearches {\n searches: RecentSearch[];\n loading: boolean;\n error?: ContentKitError;\n /** Clears one query, or every recent search. */\n clear: (query?: string) => Promise<void>;\n}\n\n/** The reader's recent searches, newest first (signed out, of this tab's session); enabled false reads nothing yet. */\nexport function useRecentSearches(o: { limit?: number; enabled?: boolean; client?: ContentKitClient } = {}): UseRecentSearches {\n const { client, store, scope } = useContentScope(o.client);\n const guarded = useGuarded();\n const key = o.enabled === false ? null : keyOf(\"searches\", scope, o.limit ?? 10);\n const r = useResource<RecentSearch[]>(store, key, { type: \"searches\" }, async (s) => (await client.discovery.recentSearches({ limit: o.limit }, s)).searches);\n const clear = useCallback((query?: string) => guarded(() => client.discovery.clearSearches(query ? { query } : {})), [client, guarded]);\n return { searches: r.data ?? [], loading: !!key && (r.loading || !r.loaded), error: r.error, clear };\n}\n\n/** A favorite or reaction with its content's or group's id. */\nexport type MyInteraction = Interaction & { id: string };\n\nconst interactions = (p: InteractionPage) => ({ items: p.items.map((x) => ({ ...x, id: x.content?.id ?? x.group?.id ?? \"\" })), next: p.next_cursor ?? null });\n\n/** The signed-in reader's favorites, most recent first: content cards or groups. */\nexport function useMyFavorites(q: MineQuery = {}, o: { client?: ContentKitClient } = {}): UseList<MyInteraction> {\n const { client, store, scope, viewer } = useContentScope(o.client);\n const query = useDisplay(q)!;\n return useList(store, viewer === null ? null : keyOf(\"favorites\", scope, query), { type: \"favorites\" }, async (cursor, signal) =>\n interactions(await client.favorites.mine({ ...query, cursor: cursor as string | undefined }, signal)),\n );\n}\n\n/** What the signed-in reader liked (or, with value, disliked), most recent first. */\nexport function useMyReactions(q: MineQuery & { value?: \"like\" | \"dislike\" } = {}, o: { client?: ContentKitClient } = {}): UseList<MyInteraction> {\n const { client, store, scope, viewer } = useContentScope(o.client);\n const query = useDisplay(q)!;\n return useList(store, viewer === null ? null : keyOf(\"reactions\", scope, query), { type: \"reactions\" }, async (cursor, signal) =>\n interactions(await client.reactions.mine({ ...query, cursor: cursor as string | undefined }, signal)),\n );\n}\n\nexport interface UseContentFilters {\n /** null until read (and signed out). */\n filters: ContentFilters | null;\n loading: boolean;\n error?: ContentKitError;\n saving: boolean;\n /** Replaces every setting; the lists showing re-read. */\n save: (update: ContentFiltersUpdate) => Promise<ContentFilters>;\n /** Shows or hides one filter's group, keeping the other settings. */\n setVisible: (groupId: string, visible: boolean) => Promise<ContentFilters>;\n setIncludeDeleted: (include: boolean) => Promise<ContentFilters>;\n}\n\n/** The signed-in reader's content filters: each configured group, its default and their setting. */\nexport function useContentFilters(o: { language?: string; client?: ContentKitClient } = {}): UseContentFilters {\n const { client, store, scope, viewer, language: provided } = useContentScope(o.client);\n const language = o.language ?? provided;\n const key = viewer === null ? null : keyOf(\"content-filters\", scope, language);\n const r = useResource<ContentFilters>(store, key, { type: \"content-filters\" }, (s) => client.discovery.contentFilters(language, s));\n const guarded = useGuarded();\n const [inflight, setInflight] = useState(0);\n const current = useRef(r.data);\n current.current = r.data;\n const save = useCallback(\n async (update: ContentFiltersUpdate) => {\n setInflight((n) => n + 1);\n try {\n const out = await guarded(() => client.discovery.setContentFilters(update, language));\n if (key) store.set(key, out);\n return out;\n } finally {\n setInflight((n) => n - 1);\n }\n },\n [client, guarded, language, key, store],\n );\n const settings = (f: ContentFilters) => f.filters.filter((x) => x.overridden).map((x) => ({ group_id: x.group.id, visible: x.visible }));\n const setVisible = useCallback(\n (groupId: string, visible: boolean) => {\n const f = current.current;\n if (!f) return Promise.reject(new Error(\"contentkit-ui: the content filters have not loaded\"));\n return save({ filters: [...settings(f).filter((x) => x.group_id !== groupId), { group_id: groupId, visible }], include_deleted: f.include_deleted });\n },\n [save],\n );\n const setIncludeDeleted = useCallback(\n (include: boolean) => {\n const f = current.current;\n if (!f) return Promise.reject(new Error(\"contentkit-ui: the content filters have not loaded\"));\n return save({ filters: settings(f), include_deleted: include });\n },\n [save],\n );\n return { filters: r.data ?? null, loading: !!key && (r.loading || !r.loaded), error: r.error, saving: inflight > 0, save, setVisible, setIncludeDeleted };\n}\n\nexport interface UseTypeahead {\n contents: Card[];\n groups: GroupCard[];\n /** The query the suggestions answer (trimmed). */\n query: string;\n loading: boolean;\n error?: ContentKitError;\n}\n\nconst NONE: Pick<UseTypeahead, \"contents\" | \"groups\"> = { contents: [], groups: [] };\n\n/** Suggestions while the reader types: debounced, the stale ones abandoned. */\nexport function useTypeahead(\n text: string,\n o: Omit<TypeaheadQuery, \"q\"> & { debounce?: number; minLength?: number; client?: ContentKitClient } = {},\n): UseTypeahead {\n const { client, language } = useContentScope(o.client);\n const { debounce = 150, minLength = 1, client: _own, ...rest } = o;\n const q = text.trim();\n const query = useMemo(() => ({ display_language: language, ...rest, q }), [language, q, JSON.stringify(rest)]); // eslint-disable-line react-hooks/exhaustive-deps\n const [state, setState] = useState<UseTypeahead>({ ...NONE, query: \"\", loading: false });\n useEffect(() => {\n if (q.length < minLength) {\n setState({ ...NONE, query: \"\", loading: false });\n return;\n }\n setState((s) => ({ ...s, loading: true }));\n const ctl = new AbortController();\n const timer = setTimeout(() => {\n client.discovery.typeahead(query, ctl.signal).then(\n (r) => setState({ contents: r.contents, groups: r.groups, query: q, loading: false }),\n (e) => {\n const error = toContentKitError(e);\n if (error.code !== \"aborted\") setState({ ...NONE, query: q, loading: false, error });\n },\n );\n }, debounce);\n return () => {\n clearTimeout(timer);\n ctl.abort();\n };\n }, [client, query, q, debounce, minLength]);\n return state;\n}\n\nconst beacons = new WeakMap<ContentKitClient, Beacons>();\n\n/** The client's signal and exposure batcher: one per client. */\nexport function beaconsFor(client: ContentKitClient, o?: BeaconOptions): Beacons {\n let b = beacons.get(client);\n if (!b) beacons.set(client, (b = new Beacons(client, o)));\n return b;\n}\n\nexport function useBeacons(o: { client?: ContentKitClient } = {}): Beacons {\n const { client } = useContentScope(o.client);\n return beaconsFor(client);\n}\n\n/** A view of a content (or one version) while it is shown: report its progress as it changes. */\nexport function useViewSignal(content: string | null | undefined, version?: string, o: Attribution & { client?: ContentKitClient } = {}): ViewSession[\"update\"] {\n const b = useBeacons(o);\n const { client: _c, ...attribution } = o;\n const session = useMemo(() => (content ? b.view(content, version, attribution) : null), [b, content, version]); // eslint-disable-line react-hooks/exhaustive-deps\n return useCallback((u) => session?.update(u), [session]);\n}\n\nexport interface ExposureOptions {\n /** Where the list is: \"home\", \"search\", \"similar\". */\n surface: string;\n /** Changes with what the list shows: a new render id. */\n render: string;\n ranker?: string;\n query_id?: string;\n language?: string;\n client?: ContentKitClient;\n}\n\nexport interface UseExposure {\n render_id: string;\n /** A card's ref: reports it shown once at least half of it is in view. */\n track: (content: string, position: number) => (el: Element | null) => void;\n /** Reports a click on the card at position. */\n click: (content: string, position: number) => void;\n}\n\n/** A rendered result list: each page rendered, each card shown, clicks attributed to the render. */\nexport function useExposure(items: readonly { id: string }[], o: ExposureOptions): UseExposure {\n const b = useBeacons(o);\n const renderId = useMemo(() => eventId(), [o.render]); // eslint-disable-line react-hooks/exhaustive-deps\n const base = useMemo(() => ({ render_id: renderId, surface: o.surface, ranker: o.ranker, query_id: o.query_id, language: o.language }), [renderId, o.surface, o.ranker, o.query_id, o.language]);\n const rendered = useRef(new Set<string>());\n const visible = useRef(new Set<string>());\n useEffect(() => {\n rendered.current = new Set();\n visible.current = new Set();\n }, [renderId]);\n useEffect(() => {\n const shown = items.flatMap((it, i) => (rendered.current.has(it.id) ? [] : [{ content_id: it.id, position: i + 1 }]));\n for (const s of shown) rendered.current.add(s.content_id);\n if (shown.length) b.exposure({ ...base, stage: \"rendered\", occurred_at: new Date().toISOString(), shown });\n }, [b, base, items]);\n const pending = useRef<{ content_id: string; position: number }[]>([]);\n const timer = useRef<ReturnType<typeof setTimeout>>(undefined);\n const observer = useMemo(() => {\n if (typeof IntersectionObserver !== \"function\") return null;\n return new IntersectionObserver(\n (entries) => {\n for (const e of entries) {\n if (!e.isIntersecting) continue;\n const el = e.target as HTMLElement;\n const content = el.dataset.exposureContent!;\n if (visible.current.has(content)) continue;\n visible.current.add(content);\n pending.current.push({ content_id: content, position: Number(el.dataset.exposurePosition) });\n }\n timer.current ??= setTimeout(() => {\n timer.current = undefined;\n const shown = pending.current.splice(0);\n if (shown.length) b.exposure({ ...base, stage: \"visible\", occurred_at: new Date().toISOString(), shown });\n }, 300);\n },\n { threshold: 0.5 },\n );\n }, [b, base]);\n useEffect(() => () => observer?.disconnect(), [observer]);\n const track = useCallback(\n (content: string, position: number) => (el: Element | null) => {\n if (!el || !observer) return;\n (el as HTMLElement).dataset.exposureContent = content;\n (el as HTMLElement).dataset.exposurePosition = String(position);\n observer.observe(el);\n },\n [observer],\n );\n const click = useCallback((content: string, position: number) => b.click(content, { render_id: renderId, surface: o.surface, position }), [b, renderId, o.surface]);\n return { render_id: renderId, track, click };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"discovery.js","names":[],"sources":["../../src/react/discovery.ts"],"sourcesContent":["import { useCallback, useContext, useEffect, useMemo, useRef, useState } from \"react\";\nimport { Beacons, eventId, type Attribution, type BeaconOptions, type ViewSession } from \"../client/beacons.js\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { MineQuery } from \"../client/content/favorites.js\";\nimport type { AccessMode, ContentQuery, HistoryQuery, RailQuery, ReleaseDatesQuery, SearchQuery, TypeaheadQuery } from \"../client/discovery.js\";\nimport { toContentKitError, type ContentKitError } from \"../client/errors.js\";\nimport type { Card, ContentFilters, ContentFiltersUpdate, ContentPage, DateCount, GroupCard, Interaction, InteractionPage, RecentSearch } from \"../client/generated/wire.js\";\nimport { ContentKitContext, useGuarded } from \"./context.js\";\nimport { keyOf, useContentScope, useList, useResource, type UseList } from \"./use-resource.js\";\n\nconst page = (p: ContentPage) => ({ items: p.contents, next: p.next_cursor ?? null });\n\n/** Titles, names and paths in the provider's language unless the query says otherwise. */\nfunction useDisplay<T extends { display_language?: string }>(q: T | null): T | null {\n const language = useContext(ContentKitContext)?.language;\n return useMemo(() => (q && !q.display_language && language ? { ...q, display_language: language } : q), [q, language]);\n}\n\n/** Contents as cards, a cursor page at a time; null reads nothing. */\nexport function useContents(q: ContentQuery | null, o: { client?: ContentKitClient } = {}): UseList<Card> {\n const { client, store, scope } = useContentScope(o.client);\n const query = useDisplay(q);\n return useList(store, query ? keyOf(\"cards\", scope, \"list\", query) : null, { type: \"cards\" }, async (cursor, signal) =>\n page(await client.discovery.list({ ...query, cursor: cursor as string | undefined }, signal)),\n );\n}\n\n/**\n * How many contents of a list the reader doesn't open: its filters' count\n * with locked cards less without them (sort and paging aside); null reads\n * nothing.\n */\nexport function useLockedCount(q: ContentQuery | null, o: { client?: ContentKitClient } = {}): { count?: number; loading: boolean } {\n const { client, store, scope } = useContentScope(o.client);\n const filter = useMemo(() => {\n if (!q) return null;\n const { sort: _s, cursor: _c, limit: _l, card_groups: _g, mode: _m, ...f } = q;\n return f;\n }, [q]);\n const query = useDisplay(filter);\n const r = useResource<number>(store, query ? keyOf(\"release-dates\", scope, \"locked\", query) : null, { type: \"release-dates\" }, async (signal) => {\n const count = async (mode: AccessMode) =>\n (await client.discovery.releaseDates({ ...query, mode, by: \"month\", limit: 1000 }, signal)).dates.reduce((n, d) => n + d.count, 0);\n const [all, open] = await Promise.all([count(\"lock\"), count(\"hide\")]);\n return Math.max(0, all - open);\n });\n return { count: r.data, loading: r.loading };\n}\n\n/** Keyword search results as cards; nothing while q.q is empty. */\nexport function useSearch(q: SearchQuery | null, o: { client?: ContentKitClient } = {}): UseList<Card> {\n const { client, store, scope } = useContentScope(o.client);\n const query = useDisplay(q?.q.trim() ? q : null);\n return useList(store, query ? keyOf(\"cards\", scope, \"search\", query) : null, { type: \"cards\" }, async (cursor, signal) =>\n page(await client.discovery.search({ ...query!, cursor: cursor as string | undefined }, signal)),\n );\n}\n\nexport interface UseRail {\n items: Card[];\n loading: boolean;\n error?: ContentKitError;\n /** The site runs without the signal plane (not_configured): leave the rail out. */\n unavailable: boolean;\n reload: () => void;\n}\n\nfunction useRail(key: string | null, fetch: (signal: AbortSignal) => Promise<ContentPage>, o: { client?: ContentKitClient }): UseRail {\n const { store } = useContentScope(o.client);\n const r = useResource<Card[]>(store, key, { type: \"cards\" }, async (signal) => (await fetch(signal)).contents);\n const unavailable = r.error?.code === \"not_configured\";\n return { items: r.data ?? [], loading: !!key && (r.loading || !r.loaded), error: unavailable ? undefined : r.error, unavailable, reload: r.reload };\n}\n\n/** Contents like one: co-engagement, or the site's configured source. */\nexport function useSimilar(id: string | null | undefined, q: RailQuery = {}, o: { client?: ContentKitClient } = {}): UseRail {\n const { client, scope } = useContentScope(o.client);\n const query = useDisplay(q)!;\n return useRail(id ? keyOf(\"cards\", scope, \"similar\", id, query) : null, (s) => client.discovery.similar(id!, query, s), o);\n}\n\n/** Contents for the signed-in reader; nothing signed out, or when q is null. */\nexport function useRecommendations(q: RailQuery | null = {}, o: { client?: ContentKitClient } = {}): UseRail {\n const { client, scope, viewer } = useContentScope(o.client);\n const query = useDisplay(q);\n return useRail(viewer === null || !query ? null : keyOf(\"cards\", scope, \"recommended\", query), (s) => client.discovery.recommendations(query!, s), o);\n}\n\n/** A release date bucket, keyed by its date. */\nexport type ReleaseDate = DateCount & { id: string };\n\n/** The release date index: per month or day, newest first. */\nexport function useReleaseDates(q: ReleaseDatesQuery = {}, o: { client?: ContentKitClient } = {}): UseList<ReleaseDate> {\n const { client, store, scope } = useContentScope(o.client);\n const query = useDisplay(q)!;\n return useList(store, keyOf(\"release-dates\", scope, query), { type: \"release-dates\" }, async (cursor, signal) => {\n const r = await client.discovery.releaseDates({ ...query, cursor: cursor as string | undefined }, signal);\n return { items: r.dates.map((d) => ({ ...d, id: d.date })), next: r.next_cursor ?? null };\n });\n}\n\nexport interface UseHistory extends UseList<Card> {\n /** Erases the reader's history: of one content item, one kind, or (no argument) everything. */\n forget: (o?: { kind?: string; content_id?: string }) => Promise<void>;\n}\n\n/** What the signed-in reader engaged with, most recent first; nothing signed out, or when q is null. */\nexport function useHistory(q: HistoryQuery | null = {}, o: { client?: ContentKitClient } = {}): UseHistory {\n const { client, store, scope, viewer } = useContentScope(o.client);\n const query = useDisplay(q);\n const guarded = useGuarded();\n const list = useList(store, viewer === null || !query ? null : keyOf(\"history\", scope, query), { type: \"history\" }, async (cursor, signal) =>\n page(await client.discovery.history({ ...query, cursor: cursor as string | undefined }, signal)),\n );\n const forget = useCallback((f: { kind?: string; content_id?: string } = {}) => guarded(() => client.discovery.forgetHistory(f)), [client, guarded]);\n return { ...list, forget };\n}\n\nexport interface UseRecentSearches {\n searches: RecentSearch[];\n loading: boolean;\n error?: ContentKitError;\n /** Clears one query, or every recent search. */\n clear: (query?: string) => Promise<void>;\n}\n\n/** The reader's recent searches, newest first (signed out, of this tab's session); enabled false reads nothing yet. */\nexport function useRecentSearches(o: { limit?: number; enabled?: boolean; client?: ContentKitClient } = {}): UseRecentSearches {\n const { client, store, scope } = useContentScope(o.client);\n const guarded = useGuarded();\n const key = o.enabled === false ? null : keyOf(\"searches\", scope, o.limit ?? 10);\n const r = useResource<RecentSearch[]>(store, key, { type: \"searches\" }, async (s) => (await client.discovery.recentSearches({ limit: o.limit }, s)).searches);\n const clear = useCallback((query?: string) => guarded(() => client.discovery.clearSearches(query ? { query } : {})), [client, guarded]);\n return { searches: r.data ?? [], loading: !!key && (r.loading || !r.loaded), error: r.error, clear };\n}\n\n/** A favorite or reaction with its content's or group's id. */\nexport type MyInteraction = Interaction & { id: string };\n\nconst interactions = (p: InteractionPage) => ({ items: p.items.map((x) => ({ ...x, id: x.content?.id ?? x.group?.id ?? \"\" })), next: p.next_cursor ?? null });\n\n/** The signed-in reader's favorites, most recent first: content cards or groups. */\nexport function useMyFavorites(q: MineQuery = {}, o: { client?: ContentKitClient } = {}): UseList<MyInteraction> {\n const { client, store, scope, viewer } = useContentScope(o.client);\n const query = useDisplay(q)!;\n return useList(store, viewer === null ? null : keyOf(\"favorites\", scope, query), { type: \"favorites\" }, async (cursor, signal) =>\n interactions(await client.favorites.mine({ ...query, cursor: cursor as string | undefined }, signal)),\n );\n}\n\n/** What the signed-in reader liked (or, with value, disliked), most recent first. */\nexport function useMyReactions(q: MineQuery & { value?: \"like\" | \"dislike\" } = {}, o: { client?: ContentKitClient } = {}): UseList<MyInteraction> {\n const { client, store, scope, viewer } = useContentScope(o.client);\n const query = useDisplay(q)!;\n return useList(store, viewer === null ? null : keyOf(\"reactions\", scope, query), { type: \"reactions\" }, async (cursor, signal) =>\n interactions(await client.reactions.mine({ ...query, cursor: cursor as string | undefined }, signal)),\n );\n}\n\nexport interface UseContentFilters {\n /** null until read (and signed out). */\n filters: ContentFilters | null;\n loading: boolean;\n error?: ContentKitError;\n saving: boolean;\n /** Replaces every setting; the lists showing re-read. */\n save: (update: ContentFiltersUpdate) => Promise<ContentFilters>;\n /** Shows or hides one filter's group, keeping the other settings. */\n setVisible: (groupId: string, visible: boolean) => Promise<ContentFilters>;\n setIncludeDeleted: (include: boolean) => Promise<ContentFilters>;\n}\n\n/** The signed-in reader's content filters: each configured group, its default and their setting. */\nexport function useContentFilters(o: { language?: string; client?: ContentKitClient } = {}): UseContentFilters {\n const { client, store, scope, viewer, language: provided } = useContentScope(o.client);\n const language = o.language ?? provided;\n const key = viewer === null ? null : keyOf(\"content-filters\", scope, language);\n const r = useResource<ContentFilters>(store, key, { type: \"content-filters\" }, (s) => client.discovery.contentFilters(language, s));\n const guarded = useGuarded();\n const [inflight, setInflight] = useState(0);\n const current = useRef(r.data);\n current.current = r.data;\n const save = useCallback(\n async (update: ContentFiltersUpdate) => {\n setInflight((n) => n + 1);\n try {\n const out = await guarded(() => client.discovery.setContentFilters(update, language));\n if (key) store.set(key, out);\n return out;\n } finally {\n setInflight((n) => n - 1);\n }\n },\n [client, guarded, language, key, store],\n );\n const settings = (f: ContentFilters) => f.filters.filter((x) => x.overridden).map((x) => ({ group_id: x.group.id, visible: x.visible }));\n const setVisible = useCallback(\n (groupId: string, visible: boolean) => {\n const f = current.current;\n if (!f) return Promise.reject(new Error(\"contentkit-ui: the content filters have not loaded\"));\n return save({ filters: [...settings(f).filter((x) => x.group_id !== groupId), { group_id: groupId, visible }], include_deleted: f.include_deleted });\n },\n [save],\n );\n const setIncludeDeleted = useCallback(\n (include: boolean) => {\n const f = current.current;\n if (!f) return Promise.reject(new Error(\"contentkit-ui: the content filters have not loaded\"));\n return save({ filters: settings(f), include_deleted: include });\n },\n [save],\n );\n return { filters: r.data ?? null, loading: !!key && (r.loading || !r.loaded), error: r.error, saving: inflight > 0, save, setVisible, setIncludeDeleted };\n}\n\nexport interface UseTypeahead {\n contents: Card[];\n groups: GroupCard[];\n /** The query the suggestions answer (trimmed). */\n query: string;\n loading: boolean;\n error?: ContentKitError;\n}\n\nconst NONE: Pick<UseTypeahead, \"contents\" | \"groups\"> = { contents: [], groups: [] };\n\n/** Suggestions while the reader types: debounced, the stale ones abandoned. */\nexport function useTypeahead(\n text: string,\n o: Omit<TypeaheadQuery, \"q\"> & { debounce?: number; minLength?: number; client?: ContentKitClient } = {},\n): UseTypeahead {\n const { client, language } = useContentScope(o.client);\n const { debounce = 150, minLength = 1, client: _own, ...rest } = o;\n const q = text.trim();\n const query = useMemo(() => ({ display_language: language, ...rest, q }), [language, q, JSON.stringify(rest)]); // eslint-disable-line react-hooks/exhaustive-deps\n const [state, setState] = useState<UseTypeahead>({ ...NONE, query: \"\", loading: false });\n useEffect(() => {\n if (q.length < minLength) {\n setState({ ...NONE, query: \"\", loading: false });\n return;\n }\n setState((s) => ({ ...s, loading: true }));\n const ctl = new AbortController();\n const timer = setTimeout(() => {\n client.discovery.typeahead(query, ctl.signal).then(\n (r) => setState({ contents: r.contents, groups: r.groups, query: q, loading: false }),\n (e) => {\n const error = toContentKitError(e);\n if (error.code !== \"aborted\") setState({ ...NONE, query: q, loading: false, error });\n },\n );\n }, debounce);\n return () => {\n clearTimeout(timer);\n ctl.abort();\n };\n }, [client, query, q, debounce, minLength]);\n return state;\n}\n\nconst beacons = new WeakMap<ContentKitClient, Beacons>();\n\n/** The client's signal and exposure batcher: one per client. */\nexport function beaconsFor(client: ContentKitClient, o?: BeaconOptions): Beacons {\n let b = beacons.get(client);\n if (!b) beacons.set(client, (b = new Beacons(client, o)));\n return b;\n}\n\nexport function useBeacons(o: { client?: ContentKitClient } = {}): Beacons {\n const { client } = useContentScope(o.client);\n return beaconsFor(client);\n}\n\n/** A view of a content (or one version) while it is shown: report its progress as it changes. */\nexport function useViewSignal(content: string | null | undefined, version?: string, o: Attribution & { client?: ContentKitClient } = {}): ViewSession[\"update\"] {\n const b = useBeacons(o);\n const { client: _c, ...attribution } = o;\n const session = useMemo(() => (content ? b.view(content, version, attribution) : null), [b, content, version]); // eslint-disable-line react-hooks/exhaustive-deps\n return useCallback((u) => session?.update(u), [session]);\n}\n\nexport interface ExposureOptions {\n /** Where the list is: \"home\", \"search\", \"similar\". */\n surface: string;\n /** Changes with what the list shows: a new render id. */\n render: string;\n ranker?: string;\n query_id?: string;\n language?: string;\n client?: ContentKitClient;\n}\n\nexport interface UseExposure {\n render_id: string;\n /** A card's ref: reports it shown once at least half of it is in view. */\n track: (content: string, position: number) => (el: Element | null) => void;\n /** Reports a click on the card at position. */\n click: (content: string, position: number) => void;\n}\n\n/** A rendered result list: each page rendered, each card shown, clicks attributed to the render. */\nexport function useExposure(items: readonly { id: string }[], o: ExposureOptions): UseExposure {\n const b = useBeacons(o);\n const renderId = useMemo(() => eventId(), [o.render]); // eslint-disable-line react-hooks/exhaustive-deps\n const base = useMemo(() => ({ render_id: renderId, surface: o.surface, ranker: o.ranker, query_id: o.query_id, language: o.language }), [renderId, o.surface, o.ranker, o.query_id, o.language]);\n const rendered = useRef(new Set<string>());\n const visible = useRef(new Set<string>());\n useEffect(() => {\n rendered.current = new Set();\n visible.current = new Set();\n }, [renderId]);\n useEffect(() => {\n const shown = items.flatMap((it, i) => (rendered.current.has(it.id) ? [] : [{ content_id: it.id, position: i + 1 }]));\n for (const s of shown) rendered.current.add(s.content_id);\n if (shown.length) b.exposure({ ...base, stage: \"rendered\", occurred_at: new Date().toISOString(), shown });\n }, [b, base, items]);\n const pending = useRef<{ content_id: string; position: number }[]>([]);\n const timer = useRef<ReturnType<typeof setTimeout>>(undefined);\n const observer = useMemo(() => {\n if (typeof IntersectionObserver !== \"function\") return null;\n return new IntersectionObserver(\n (entries) => {\n for (const e of entries) {\n if (!e.isIntersecting) continue;\n const el = e.target as HTMLElement;\n const content = el.dataset.exposureContent!;\n if (visible.current.has(content)) continue;\n visible.current.add(content);\n pending.current.push({ content_id: content, position: Number(el.dataset.exposurePosition) });\n }\n timer.current ??= setTimeout(() => {\n timer.current = undefined;\n const shown = pending.current.splice(0);\n if (shown.length) b.exposure({ ...base, stage: \"visible\", occurred_at: new Date().toISOString(), shown });\n }, 300);\n },\n { threshold: 0.5 },\n );\n }, [b, base]);\n useEffect(() => () => observer?.disconnect(), [observer]);\n const track = useCallback(\n (content: string, position: number) => (el: Element | null) => {\n if (!el || !observer) return;\n (el as HTMLElement).dataset.exposureContent = content;\n (el as HTMLElement).dataset.exposurePosition = String(position);\n observer.observe(el);\n },\n [observer],\n );\n const click = useCallback((content: string, position: number) => b.click(content, { render_id: renderId, surface: o.surface, position }), [b, renderId, o.surface]);\n return { render_id: renderId, track, click };\n}\n"],"mappings":"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"}
@@ -11,14 +11,18 @@ export interface UseReaction {
11
11
  /** Sets value, or clears it when it is the current one. */
12
12
  toggle: (value: 1 | -1) => Promise<void>;
13
13
  }
14
- /** A target's likes and dislikes and the caller's own reaction (initial: counts the host already has). */
14
+ /**
15
+ * A target's likes and dislikes and the caller's own reaction (initial:
16
+ * counts the host already has). The counts are absent where the target's
17
+ * kind's locked view hides them.
18
+ */
15
19
  export declare function useReaction(target: Target, o?: {
16
20
  initial?: ReactionCounts;
17
21
  client?: ContentKitClient;
18
22
  }): UseReaction;
19
23
  export interface UseFavorite {
20
24
  favorited: boolean;
21
- /** How many favorited the item; undefined until read. */
25
+ /** How many favorited the item; undefined until read, or where its kind's locked view hides it. */
22
26
  count?: number;
23
27
  loading: boolean;
24
28
  /** A write is in flight. */
@@ -3,11 +3,7 @@ import { keyOf as t, useContentScope as n, useResource as r } from "./use-resour
3
3
  import { withReaction as i } from "./comments.js";
4
4
  import { useCallback as a, useState as o } from "react";
5
5
  //#region src/react/engagement.ts
6
- var s = {
7
- likes: 0,
8
- dislikes: 0,
9
- mine: 0
10
- };
6
+ var s = { mine: 0 };
11
7
  function c(o, c = {}) {
12
8
  let { client: l, store: u, scope: d } = n(c.client), { type: f, id: p, version: m } = o, h = t("reaction", d, f, p, m), g = r(u, h, {
13
9
  type: "reaction",
@@ -65,7 +61,7 @@ function l(i, s = {}) {
65
61
  let n = l.snapshot(m).data, r = l.mark(m);
66
62
  n && n.favorited !== t && l.set(m, {
67
63
  favorited: t,
68
- count: Math.max(0, n.count + (t ? 1 : -1))
64
+ count: n.count === void 0 ? void 0 : Math.max(0, n.count + (t ? 1 : -1))
69
65
  }), v(!0);
70
66
  try {
71
67
  await c.favorites.set({
@@ -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, 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
+ {"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 = { mine: 0 };\n\n/**\n * A target's likes and dislikes and the caller's own reaction (initial:\n * counts the host already has). The counts are absent where the target's\n * kind's locked view hides them.\n */\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, or where its kind's locked view hides it. */\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)\n store.set<FavoriteState>(key, { favorited: next, count: prev.count === undefined ? undefined : 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,EAAE,MAAM,EAAE;AAOvC,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;EAG5B,AAFI,KAAQ,EAAK,cAAc,KAC7B,EAAM,IAAmB,GAAK;GAAE,WAAW;GAAM,OAAO,EAAK,UAAU,KAAA,IAAY,KAAA,IAAY,KAAK,IAAI,GAAG,EAAK,SAAS,IAAO,IAAI,GAAG;EAAE,CAAC,GAC5I,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"}
@@ -13,11 +13,11 @@ export { useFavorite, useReaction, type UseFavorite, type UseReaction } from './
13
13
  export { usePoll, usePollEditor, usePolls, withVote, type PollFilter, type PollImages, type UsePoll, type UsePollEditor } from './polls.js';
14
14
  export { useContent, useManagedContents, type ManageFilter, type UseContent } from './contents.js';
15
15
  export { useActivity, useMyStats, useTopContents, type UseStats } from './stats.js';
16
- export { defaultVersion, findVersion, liveVersions, useContentDetail, type ContentDetailOptions, type UseContentDetail } from './detail.js';
16
+ export { defaultVersion, editionOf, findVersion, liveVersions, useContentDetail, type ContentDetailOptions, type UseContentDetail } from './detail.js';
17
17
  export { useVersionParam } from './location.js';
18
18
  export { memberId, useContentGroups, useGroup, useGroupMembers, useGroups, useKinds, useMyGroups, type GroupFilter, type GroupSort, type Member, type UseContentGroups, type UseGroup, type UseGroupMembers, type UseKinds, } from './groups.js';
19
- export { beaconsFor, useBeacons, useContentFilters, useContents, useExposure, useHistory, useMyFavorites, useMyReactions, useRecentSearches, useRecommendations, useReleaseDates, useSearch, useSimilar, useTypeahead, useViewSignal, type ExposureOptions, type MyInteraction, type ReleaseDate, type UseContentFilters, type UseExposure, type UseHistory, type UseRail, type UseRecentSearches, type UseTypeahead, } from './discovery.js';
20
- export { nextRelease, useReaderPlan, useSegmentPages, useVersionDownload, type ReaderPage, type UseReaderPlan, type UseSegmentPages, type UseVersionDownload, } from './versions.js';
19
+ export { beaconsFor, useBeacons, useContentFilters, useContents, useExposure, useHistory, useLockedCount, useMyFavorites, useMyReactions, useRecentSearches, useRecommendations, useReleaseDates, useSearch, useSimilar, useTypeahead, useViewSignal, type ExposureOptions, type MyInteraction, type ReleaseDate, type UseContentFilters, type UseExposure, type UseHistory, type UseRail, type UseRecentSearches, type UseTypeahead, } from './discovery.js';
20
+ export { downloadMessage, nextRelease, startDownload, useReaderPlan, useSegmentPages, useVersionDownload, type ReaderPage, type UseReaderPlan, type UseSegmentPages, type UseVersionDownload, } from './versions.js';
21
21
  export { useContentScope, usePaged, type UseList, type UsePaged } from './use-resource.js';
22
22
  export { ResourceStore, resourcesFor, type Page, type Resource } from './resources.js';
23
23
  export { useCrop, useEncodeProgress, useUpload, useUploadQueue, type UploadQueueOptions, type UploadStatus, type UseCrop, type UseCropOptions, type UseEncodeProgress, type UseUpload, type UseUploadQueue, } from './upload.js';
@@ -23,27 +23,27 @@ function f(e = {}) {
23
23
  };
24
24
  }
25
25
  function p(r, a, s = {}) {
26
- let { kind: l, id: u } = r, d = i(s.client), { presets: p, loaded: m } = f({ client: d }), h = p.find((e) => e.kind === l && e.name === a), g = s.image !== void 0, _ = h ? t(h.from) ? n(h.from) : h.from : void 0, v = o(r, {
27
- prefix: _,
28
- read: g || !m ? null : s.read,
29
- client: d
26
+ let { kind: l, id: u, version: d } = r, p = i(s.client), { presets: m, loaded: h } = f({ client: p }), g = m.find((e) => e.kind === l && e.name === a), _ = s.image !== void 0, v = g ? t(g.from) ? n(g.from) : g.from : void 0, y = o(r, {
27
+ prefix: v,
28
+ read: _ || !h ? null : s.read,
29
+ client: p
30
30
  });
31
31
  return {
32
32
  ...c(() => {
33
- let t = g ? s.image : v.read?.public?.find((e) => e.preset === a);
33
+ let t = _ ? s.image : y.read?.public?.find((e) => e.preset === a);
34
34
  if (t && t.renditions.length) return {
35
35
  image: {
36
36
  preset: a,
37
37
  renditions: t.renditions,
38
- aspect: "aspect" in t && t.aspect || h?.aspect
38
+ aspect: "aspect" in t && t.aspect || g?.aspect
39
39
  },
40
40
  isDefault: !1
41
41
  };
42
- if (!g && !v.read) return {
42
+ if (!_ && !y.read) return {
43
43
  image: null,
44
44
  isDefault: !1
45
45
  };
46
- let n = h && e(h, u);
46
+ let n = g && e(g, u, d);
47
47
  return n ? {
48
48
  image: n,
49
49
  isDefault: !0
@@ -52,17 +52,18 @@ function p(r, a, s = {}) {
52
52
  isDefault: !1
53
53
  };
54
54
  }, [
55
- g,
55
+ _,
56
56
  s.image,
57
- v.read,
57
+ y.read,
58
58
  a,
59
- h,
60
- u
59
+ g,
60
+ u,
61
+ d
61
62
  ]),
62
- rule: h,
63
- loading: !m || v.loading,
64
- error: v.error,
65
- reload: v.reload
63
+ rule: g,
64
+ loading: !h || y.loading,
65
+ error: y.error,
66
+ reload: y.reload
66
67
  };
67
68
  }
68
69
  function m(e, t = {}) {
@@ -1 +1 @@
1
- {"version":3,"file":"public.js","names":[],"sources":["../../src/react/public.ts"],"sourcesContent":["import { useEffect, useMemo, useState } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { ContentKitError } from \"../client/errors.js\";\nimport type { ReadResult, RefBody } from \"../client/generated/wire.js\";\nimport type { PresetRule } from \"../client/generated/wire.js\";\nimport { defaultImage, isPattern, ruleDir, stableImage } from \"../client/media/rules.js\";\nimport type { PublicPreset } from \"../client/public.js\";\nimport { useOptionalContentKitClient } from \"./context.js\";\nimport { useKinds } from \"./groups.js\";\nimport { useMediaRead } from \"./read.js\";\n\nexport interface UsePresets {\n /** Every kind's public presets; empty until they load (or when the server has none). */\n presets: PresetRule[];\n loaded: boolean;\n error?: ContentKitError;\n}\n\nconst settled = new WeakMap<ContentKitClient, { presets: PresetRule[]; error?: ContentKitError }>();\nconst inflight = new WeakMap<ContentKitClient, Promise<void>>();\n\n/** The public presets (`GET /media/presets`), fetched once per client. */\nexport function usePresets(o: { client?: ContentKitClient | null } = {}): UsePresets {\n const client = useOptionalContentKitClient(o.client);\n const [, rerender] = useState(0);\n const done = client ? settled.get(client) : undefined;\n useEffect(() => {\n if (!client || settled.has(client)) return;\n let live = true;\n let load = inflight.get(client);\n if (!load) {\n load = client.media.presets().then(\n (presets) => void settled.set(client, { presets }),\n (error: ContentKitError) => void settled.set(client, { presets: [], error }),\n ).finally(() => inflight.delete(client));\n inflight.set(client, load);\n }\n void load.then(() => live && rerender((n) => n + 1));\n return () => {\n live = false;\n };\n }, [client]);\n return { presets: done?.presets ?? [], loaded: !!done || !client, error: done?.error };\n}\n\nexport interface PublicImageOptions {\n /** The host's listing of the item's image (its API's): shown without a read. */\n image?: PublicPreset | null;\n /** A read of the item the host already has. */\n read?: ReadResult | null;\n /** Overrides the provider's client. */\n client?: ContentKitClient | null;\n}\n\nexport interface UsePublicImage {\n /** The item's current renditions; else the kind's default image (when the preset has one); null while unknown or when there is neither. */\n image: PublicPreset | null;\n /** The preset's rules (aspect, widths, min_width) once the presets load. */\n rule?: PresetRule;\n /** image is the kind's default, not the item's own. */\n isDefault: boolean;\n loading: boolean;\n error?: ContentKitError;\n reload: () => void;\n}\n\n/**\n * An item's public image for a preset (\"avatar\", \"cover\", a version's\n * \"poster\"): the exact renditions from a read's `public` (or the host's\n * listing), else the kind's default image, else none. Reads refresh after\n * commits through the same client.\n */\nexport function usePublicImage(item: RefBody, preset: string, o: PublicImageOptions = {}): UsePublicImage {\n const { kind, id } = item;\n const client = useOptionalContentKitClient(o.client);\n const { presets, loaded } = usePresets({ client });\n const rule = presets.find((p) => p.kind === kind && p.name === preset);\n const listed = o.image !== undefined;\n // The read waits for the presets, so it lists only the preset's upload.\n const prefix = rule ? (isPattern(rule.from) ? ruleDir(rule.from) : rule.from) : undefined;\n const r = useMediaRead(item, { prefix, read: listed || !loaded ? null : o.read, client });\n const image = useMemo((): { image: PublicPreset | null; isDefault: boolean } => {\n const own = listed ? o.image : r.read?.public?.find((p) => p.preset === preset);\n if (own && own.renditions.length) return { image: { preset, renditions: own.renditions, aspect: (\"aspect\" in own && own.aspect) || rule?.aspect }, isDefault: false };\n if (!listed && !r.read) return { image: null, isDefault: false };\n const def = rule && defaultImage(rule, id);\n return def ? { image: def, isDefault: true } : { image: null, isDefault: false };\n }, [listed, o.image, r.read, preset, rule, id]);\n return { ...image, rule, loading: !loaded || r.loading, error: r.error, reload: r.reload };\n}\n\n/**\n * A person's avatar (the content model's `avatars`, a Stable preset): URLs\n * built from their id on this site's media host, showing their image or\n * the default. null while the model loads or when the site shows none.\n */\nexport function useAvatar(userId: string | null | undefined, o: { client?: ContentKitClient } = {}): PublicPreset | null {\n const { kinds } = useKinds(o);\n const rule = kinds?.avatars;\n return useMemo(() => (rule && userId ? stableImage(rule, userId) : null), [rule, userId]);\n}\n"],"mappings":";;;;;;AAkBA,IAAM,oBAAU,IAAI,QAA8E,GAC5F,oBAAW,IAAI,QAAyC;AAG9D,SAAgB,EAAW,IAA0C,CAAC,GAAe;CACnF,IAAM,IAAS,EAA4B,EAAE,MAAM,GAC7C,GAAG,KAAY,EAAS,CAAC,GACzB,IAAO,IAAS,EAAQ,IAAI,CAAM,IAAI,KAAA;CAiB5C,OAhBA,QAAgB;EACd,IAAI,CAAC,KAAU,EAAQ,IAAI,CAAM,GAAG;EACpC,IAAI,IAAO,IACP,IAAO,EAAS,IAAI,CAAM;EAS9B,OARK,MACH,IAAO,EAAO,MAAM,QAAQ,CAAC,CAAC,MAC3B,MAAY,KAAK,EAAQ,IAAI,GAAQ,EAAE,WAAQ,CAAC,IAChD,MAA2B,KAAK,EAAQ,IAAI,GAAQ;GAAE,SAAS,CAAC;GAAG;EAAM,CAAC,CAC7E,CAAC,CAAC,cAAc,EAAS,OAAO,CAAM,CAAC,GACvC,EAAS,IAAI,GAAQ,CAAI,IAE3B,EAAU,WAAW,KAAQ,GAAU,MAAM,IAAI,CAAC,CAAC,SACtC;GACX,IAAO;EACT;CACF,GAAG,CAAC,CAAM,CAAC,GACJ;EAAE,SAAS,GAAM,WAAW,CAAC;EAAG,QAAQ,CAAC,CAAC,KAAQ,CAAC;EAAQ,OAAO,GAAM;CAAM;AACvF;AA6BA,SAAgB,EAAe,GAAe,GAAgB,IAAwB,CAAC,GAAmB;CACxG,IAAM,EAAE,SAAM,UAAO,GACf,IAAS,EAA4B,EAAE,MAAM,GAC7C,EAAE,YAAS,cAAW,EAAW,EAAE,UAAO,CAAC,GAC3C,IAAO,EAAQ,MAAM,MAAM,EAAE,SAAS,KAAQ,EAAE,SAAS,CAAM,GAC/D,IAAS,EAAE,UAAU,KAAA,GAErB,IAAS,IAAQ,EAAU,EAAK,IAAI,IAAI,EAAQ,EAAK,IAAI,IAAI,EAAK,OAAQ,KAAA,GAC1E,IAAI,EAAa,GAAM;EAAE;EAAQ,MAAM,KAAU,CAAC,IAAS,OAAO,EAAE;EAAM;CAAO,CAAC;CAQxF,OAAO;EAAE,GAPK,QAAkE;GAC9E,IAAM,IAAM,IAAS,EAAE,QAAQ,EAAE,MAAM,QAAQ,MAAM,MAAM,EAAE,WAAW,CAAM;GAC9E,IAAI,KAAO,EAAI,WAAW,QAAQ,OAAO;IAAE,OAAO;KAAE;KAAQ,YAAY,EAAI;KAAY,QAAS,YAAY,KAAO,EAAI,UAAW,GAAM;IAAO;IAAG,WAAW;GAAM;GACpK,IAAI,CAAC,KAAU,CAAC,EAAE,MAAM,OAAO;IAAE,OAAO;IAAM,WAAW;GAAM;GAC/D,IAAM,IAAM,KAAQ,EAAa,GAAM,CAAE;GACzC,OAAO,IAAM;IAAE,OAAO;IAAK,WAAW;GAAK,IAAI;IAAE,OAAO;IAAM,WAAW;GAAM;EACjF,GAAG;GAAC;GAAQ,EAAE;GAAO,EAAE;GAAM;GAAQ;GAAM;EAAE,CACjC;EAAO;EAAM,SAAS,CAAC,KAAU,EAAE;EAAS,OAAO,EAAE;EAAO,QAAQ,EAAE;CAAO;AAC3F;AAOA,SAAgB,EAAU,GAAmC,IAAmC,CAAC,GAAwB;CACvH,IAAM,EAAE,aAAU,EAAS,CAAC,GACtB,IAAO,GAAO;CACpB,OAAO,QAAe,KAAQ,IAAS,EAAY,GAAM,CAAM,IAAI,MAAO,CAAC,GAAM,CAAM,CAAC;AAC1F"}
1
+ {"version":3,"file":"public.js","names":[],"sources":["../../src/react/public.ts"],"sourcesContent":["import { useEffect, useMemo, useState } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { ContentKitError } from \"../client/errors.js\";\nimport type { ReadResult, RefBody } from \"../client/generated/wire.js\";\nimport type { PresetRule } from \"../client/generated/wire.js\";\nimport { defaultImage, isPattern, ruleDir, stableImage } from \"../client/media/rules.js\";\nimport type { PublicPreset } from \"../client/public.js\";\nimport { useOptionalContentKitClient } from \"./context.js\";\nimport { useKinds } from \"./groups.js\";\nimport { useMediaRead } from \"./read.js\";\n\nexport interface UsePresets {\n /** Every kind's public presets; empty until they load (or when the server has none). */\n presets: PresetRule[];\n loaded: boolean;\n error?: ContentKitError;\n}\n\nconst settled = new WeakMap<ContentKitClient, { presets: PresetRule[]; error?: ContentKitError }>();\nconst inflight = new WeakMap<ContentKitClient, Promise<void>>();\n\n/** The public presets (`GET /media/presets`), fetched once per client. */\nexport function usePresets(o: { client?: ContentKitClient | null } = {}): UsePresets {\n const client = useOptionalContentKitClient(o.client);\n const [, rerender] = useState(0);\n const done = client ? settled.get(client) : undefined;\n useEffect(() => {\n if (!client || settled.has(client)) return;\n let live = true;\n let load = inflight.get(client);\n if (!load) {\n load = client.media.presets().then(\n (presets) => void settled.set(client, { presets }),\n (error: ContentKitError) => void settled.set(client, { presets: [], error }),\n ).finally(() => inflight.delete(client));\n inflight.set(client, load);\n }\n void load.then(() => live && rerender((n) => n + 1));\n return () => {\n live = false;\n };\n }, [client]);\n return { presets: done?.presets ?? [], loaded: !!done || !client, error: done?.error };\n}\n\nexport interface PublicImageOptions {\n /** The host's listing of the item's image (its API's): shown without a read. */\n image?: PublicPreset | null;\n /** A read of the item the host already has. */\n read?: ReadResult | null;\n /** Overrides the provider's client. */\n client?: ContentKitClient | null;\n}\n\nexport interface UsePublicImage {\n /** The item's current renditions; else the kind's default image (when the preset has one); null while unknown or when there is neither. */\n image: PublicPreset | null;\n /** The preset's rules (aspect, widths, min_width) once the presets load. */\n rule?: PresetRule;\n /** image is the kind's default, not the item's own. */\n isDefault: boolean;\n loading: boolean;\n error?: ContentKitError;\n reload: () => void;\n}\n\n/**\n * An item's public image for a preset (\"avatar\", \"cover\", a version's\n * \"poster\"): the exact renditions from a read's `public` (or the host's\n * listing), else the kind's default image, else none. Reads refresh after\n * commits through the same client.\n */\nexport function usePublicImage(item: RefBody, preset: string, o: PublicImageOptions = {}): UsePublicImage {\n const { kind, id, version } = item;\n const client = useOptionalContentKitClient(o.client);\n const { presets, loaded } = usePresets({ client });\n const rule = presets.find((p) => p.kind === kind && p.name === preset);\n const listed = o.image !== undefined;\n // The read waits for the presets, so it lists only the preset's upload.\n const prefix = rule ? (isPattern(rule.from) ? ruleDir(rule.from) : rule.from) : undefined;\n const r = useMediaRead(item, { prefix, read: listed || !loaded ? null : o.read, client });\n const image = useMemo((): { image: PublicPreset | null; isDefault: boolean } => {\n const own = listed ? o.image : r.read?.public?.find((p) => p.preset === preset);\n if (own && own.renditions.length) return { image: { preset, renditions: own.renditions, aspect: (\"aspect\" in own && own.aspect) || rule?.aspect }, isDefault: false };\n if (!listed && !r.read) return { image: null, isDefault: false };\n const def = rule && defaultImage(rule, id, version);\n return def ? { image: def, isDefault: true } : { image: null, isDefault: false };\n }, [listed, o.image, r.read, preset, rule, id, version]);\n return { ...image, rule, loading: !loaded || r.loading, error: r.error, reload: r.reload };\n}\n\n/**\n * A person's avatar (the content model's `avatars`, a Stable preset): URLs\n * built from their id on this site's media host, showing their image or\n * the default. null while the model loads or when the site shows none.\n */\nexport function useAvatar(userId: string | null | undefined, o: { client?: ContentKitClient } = {}): PublicPreset | null {\n const { kinds } = useKinds(o);\n const rule = kinds?.avatars;\n return useMemo(() => (rule && userId ? stableImage(rule, userId) : null), [rule, userId]);\n}\n"],"mappings":";;;;;;AAkBA,IAAM,oBAAU,IAAI,QAA8E,GAC5F,oBAAW,IAAI,QAAyC;AAG9D,SAAgB,EAAW,IAA0C,CAAC,GAAe;CACnF,IAAM,IAAS,EAA4B,EAAE,MAAM,GAC7C,GAAG,KAAY,EAAS,CAAC,GACzB,IAAO,IAAS,EAAQ,IAAI,CAAM,IAAI,KAAA;CAiB5C,OAhBA,QAAgB;EACd,IAAI,CAAC,KAAU,EAAQ,IAAI,CAAM,GAAG;EACpC,IAAI,IAAO,IACP,IAAO,EAAS,IAAI,CAAM;EAS9B,OARK,MACH,IAAO,EAAO,MAAM,QAAQ,CAAC,CAAC,MAC3B,MAAY,KAAK,EAAQ,IAAI,GAAQ,EAAE,WAAQ,CAAC,IAChD,MAA2B,KAAK,EAAQ,IAAI,GAAQ;GAAE,SAAS,CAAC;GAAG;EAAM,CAAC,CAC7E,CAAC,CAAC,cAAc,EAAS,OAAO,CAAM,CAAC,GACvC,EAAS,IAAI,GAAQ,CAAI,IAE3B,EAAU,WAAW,KAAQ,GAAU,MAAM,IAAI,CAAC,CAAC,SACtC;GACX,IAAO;EACT;CACF,GAAG,CAAC,CAAM,CAAC,GACJ;EAAE,SAAS,GAAM,WAAW,CAAC;EAAG,QAAQ,CAAC,CAAC,KAAQ,CAAC;EAAQ,OAAO,GAAM;CAAM;AACvF;AA6BA,SAAgB,EAAe,GAAe,GAAgB,IAAwB,CAAC,GAAmB;CACxG,IAAM,EAAE,SAAM,OAAI,eAAY,GACxB,IAAS,EAA4B,EAAE,MAAM,GAC7C,EAAE,YAAS,cAAW,EAAW,EAAE,UAAO,CAAC,GAC3C,IAAO,EAAQ,MAAM,MAAM,EAAE,SAAS,KAAQ,EAAE,SAAS,CAAM,GAC/D,IAAS,EAAE,UAAU,KAAA,GAErB,IAAS,IAAQ,EAAU,EAAK,IAAI,IAAI,EAAQ,EAAK,IAAI,IAAI,EAAK,OAAQ,KAAA,GAC1E,IAAI,EAAa,GAAM;EAAE;EAAQ,MAAM,KAAU,CAAC,IAAS,OAAO,EAAE;EAAM;CAAO,CAAC;CAQxF,OAAO;EAAE,GAPK,QAAkE;GAC9E,IAAM,IAAM,IAAS,EAAE,QAAQ,EAAE,MAAM,QAAQ,MAAM,MAAM,EAAE,WAAW,CAAM;GAC9E,IAAI,KAAO,EAAI,WAAW,QAAQ,OAAO;IAAE,OAAO;KAAE;KAAQ,YAAY,EAAI;KAAY,QAAS,YAAY,KAAO,EAAI,UAAW,GAAM;IAAO;IAAG,WAAW;GAAM;GACpK,IAAI,CAAC,KAAU,CAAC,EAAE,MAAM,OAAO;IAAE,OAAO;IAAM,WAAW;GAAM;GAC/D,IAAM,IAAM,KAAQ,EAAa,GAAM,GAAI,CAAO;GAClD,OAAO,IAAM;IAAE,OAAO;IAAK,WAAW;GAAK,IAAI;IAAE,OAAO;IAAM,WAAW;GAAM;EACjF,GAAG;GAAC;GAAQ,EAAE;GAAO,EAAE;GAAM;GAAQ;GAAM;GAAI;EAAO,CAC1C;EAAO;EAAM,SAAS,CAAC,KAAU,EAAE;EAAS,OAAO,EAAE;EAAO,QAAQ,EAAE;CAAO;AAC3F;AAOA,SAAgB,EAAU,GAAmC,IAAmC,CAAC,GAAwB;CACvH,IAAM,EAAE,aAAU,EAAS,CAAC,GACtB,IAAO,GAAO;CACpB,OAAO,QAAe,KAAQ,IAAS,EAAY,GAAM,CAAM,IAAI,MAAO,CAAC,GAAM,CAAM,CAAC;AAC1F"}
@@ -1,6 +1,7 @@
1
1
  import { ContentKitClient } from '../client/client.js';
2
2
  import { ContentKitError } from '../client/errors.js';
3
- import { PlanSegment, ReaderPlan } from '../client/generated/wire.js';
3
+ import { FileDownload, PlanSegment, ReaderPlan } from '../client/generated/wire.js';
4
+ import { Translator } from '../i18n/messages.js';
4
5
  import { ReadWindow } from './read.js';
5
6
  export interface UseReaderPlan {
6
7
  /** null while loading, pending or failed. */
@@ -44,14 +45,33 @@ export declare function useSegmentPages(segment: PlanSegment | null | undefined,
44
45
  client?: ContentKitClient;
45
46
  }): UseSegmentPages;
46
47
  export interface UseVersionDownload {
47
- /** Prepares the zip (asking again while it is prepared) and resolves with it. */
48
- download: () => Promise<Blob>;
49
- /** Prepared by the server, waiting. */
48
+ /**
49
+ * Grants the download (asking again while it is prepared), starts it in the
50
+ * browser and resolves with the grant.
51
+ */
52
+ download: () => Promise<FileDownload>;
53
+ /** Being prepared by the server, or granted and starting. */
50
54
  preparing: boolean;
51
55
  error?: ContentKitError;
56
+ /** While a quota refused: when the viewer may download again (ms). */
57
+ retryAt?: number;
58
+ /**
59
+ * What to tell the viewer about the last refusal: when a quota frees, when
60
+ * a download the site holds back opens ("available to download in N
61
+ * days"), a locked version, nothing to download, or a failure.
62
+ */
63
+ message?: string;
52
64
  }
53
- /** A version's pages as a zip, charged to the viewer's download quota. */
65
+ /** Starts a granted download: the browser fetches its URL, an attachment. */
66
+ export declare function startDownload(d: FileDownload): void;
67
+ /** The viewer's line for a download refusal at now. */
68
+ export declare function downloadMessage(m: Translator, err: ContentKitError | undefined, now?: number): string | undefined;
69
+ /**
70
+ * A version's download, charged to the viewer's download quota: the
71
+ * original or rendition its kind serves, or its pages as a zip.
72
+ */
54
73
  export declare function useVersionDownload(content: string, version: string, o?: {
55
74
  poll?: number;
56
75
  client?: ContentKitClient;
76
+ start?: (d: FileDownload) => void;
57
77
  }): UseVersionDownload;