@scalemule/news-publication 0.0.67 → 0.0.69

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.
@@ -4,6 +4,38 @@
4
4
  var react = require('react');
5
5
  var jsxRuntime = require('react/jsx-runtime');
6
6
 
7
+ // src/article-presentation.ts
8
+ var standard = {
9
+ id: "standard",
10
+ version: 1,
11
+ name: "Standard article",
12
+ renderer: "standard",
13
+ hero_aspect: "landscape",
14
+ headline_position: "before_hero",
15
+ wide_body: false
16
+ };
17
+ var formats = [
18
+ "news",
19
+ "brief",
20
+ "explainer",
21
+ "interview",
22
+ "opinion",
23
+ "photo_essay",
24
+ "video_story"
25
+ ];
26
+ function articlePresentation(value) {
27
+ const l = value?.layout;
28
+ if (!l || !/^[a-z0-9_-]{1,80}$/.test(l.id) || !Number.isInteger(l.version) || l.version < 1 || !["standard", "feature", "portrait"].includes(l.renderer) || !["landscape", "portrait", "square"].includes(l.hero_aspect) || !["before_hero", "after_hero"].includes(l.headline_position) || typeof l.wide_body !== "boolean") {
29
+ return { layout: standard };
30
+ }
31
+ return {
32
+ format: value?.format && formats.includes(value.format) ? value.format : void 0,
33
+ layout: l
34
+ };
35
+ }
36
+ function articleFormatLabel(format) {
37
+ return format === "opinion" ? "Opinion" : format === "explainer" ? "Explainer" : format === "interview" ? "Interview" : format === "photo_essay" ? "Photo essay" : format === "video_story" ? "Video story" : void 0;
38
+ }
7
39
  var stable = (value) => Array.isArray(value) ? value.map(stable) : value && typeof value === "object" ? Object.fromEntries(Object.keys(value).sort().map((k) => [k, stable(value[k])])) : value;
8
40
  var same = (a, b) => JSON.stringify(stable(a)) === JSON.stringify(stable(b));
9
41
  var hasDraft = (data) => !!data && Object.keys(data).length > 0;
@@ -509,6 +541,7 @@ function EditorialNotices({ editorial }) {
509
541
  ] }, i))
510
542
  ] }) : null;
511
543
  }
544
+ var ReviewMediaSession = react.createContext(false);
512
545
  var human = (s) => s.toLowerCase().replaceAll("_", " ");
513
546
  function StoryReview({
514
547
  sessionId,
@@ -518,10 +551,14 @@ function StoryReview({
518
551
  publicationFooter,
519
552
  readerAccountUrl,
520
553
  renderSignIn,
521
- feedbackEmail
554
+ feedbackEmail,
555
+ initialReview,
556
+ onReviewLoaded,
557
+ formatImageCaption = (value) => value || ""
522
558
  }) {
523
559
  const base = `/api/news/review/${encodeURIComponent(sessionId)}`;
524
- const [review, setReview] = react.useState(null), [error, setError] = react.useState(""), [notice, setNotice] = react.useState(""), [busy, setBusy] = react.useState(false), [panel, setPanelState] = react.useState("article"), [verification, setVerification] = react.useState(false), [code, setCode] = react.useState("");
560
+ const [mediaSessionReady, setMediaSessionReady] = react.useState(false);
561
+ const [review, setReview] = react.useState(initialReview ?? null), [error, setError] = react.useState(""), [notice, setNotice] = react.useState(""), [busy, setBusy] = react.useState(false), [panel, setPanelState] = react.useState("article"), [verification, setVerification] = react.useState(false), [code, setCode] = react.useState("");
525
562
  const [shareUrl, setShareUrl] = react.useState(""), [canShare, setCanShare] = react.useState(false);
526
563
  const [contributorName, setContributorName] = react.useState(""), [contributorEmail, setContributorEmail] = react.useState("");
527
564
  const [identityStep, setIdentityStep] = react.useState("name"), [requestedPanel, setRequestedPanel] = react.useState("comment");
@@ -576,6 +613,9 @@ function StoryReview({
576
613
  const responseRef = react.useRef(null);
577
614
  const thanksRef = react.useRef(null);
578
615
  const [moreQuestions, setMoreQuestions] = react.useState(false);
616
+ react.useEffect(() => {
617
+ if (review) onReviewLoaded?.(review);
618
+ }, [review, onReviewLoaded]);
579
619
  const [replacementPhoto, setReplacementPhoto] = react.useState(null);
580
620
  const photoToReplace = pendingPhoto.photo?.replacement || replacementPhoto;
581
621
  function openMedia(photo = null) {
@@ -655,6 +695,7 @@ function StoryReview({
655
695
  setReview(null);
656
696
  } else {
657
697
  setVerification(false);
698
+ setMediaSessionReady(true);
658
699
  setReview(data);
659
700
  }
660
701
  }
@@ -696,7 +737,7 @@ function StoryReview({
696
737
  const token = new URL(window.location.href).searchParams.get("key") || (/^[a-f0-9]{64}$/.test(window.location.hash.slice(1)) ? window.location.hash.slice(1) : void 0);
697
738
  let exchange = {};
698
739
  try {
699
- exchange = await request("/session", token ? { token } : {});
740
+ exchange = await request("/session", { ...token ? { token } : {}, include_review: true });
700
741
  } catch (e) {
701
742
  if (token) throw e;
702
743
  }
@@ -713,13 +754,14 @@ function StoryReview({
713
754
  }
714
755
  }
715
756
  }
716
- const data = await request();
757
+ const data = exchange.review ?? await request();
717
758
  if (active) {
718
759
  if (data.verification_required) {
719
760
  setVerification(true);
720
761
  setReview(null);
721
762
  } else {
722
763
  setVerification(false);
764
+ setMediaSessionReady(true);
723
765
  setReview(data);
724
766
  }
725
767
  }
@@ -880,10 +922,29 @@ function StoryReview({
880
922
  /* @__PURE__ */ jsxRuntime.jsx("a", { href: emailLink, children: "Email the newsroom" }),
881
923
  ". Your email app will open; send your message there."
882
924
  ] });
883
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `sm-review-frame${publicationHeader ? " sm-review-publication-frame" : ""}`, children: [
925
+ const presentation = articlePresentation(review?.presentation);
926
+ const formatLabel = articleFormatLabel(presentation.format);
927
+ const sourceHero = review?.revision.document.media.filter((m) => m.position === "hero").map((m) => /* @__PURE__ */ jsxRuntime.jsx(
928
+ PrivatePhoto,
929
+ {
930
+ media: m,
931
+ base,
932
+ onChange: can("UPLOAD_MEDIA") ? () => openMedia(m) : void 0,
933
+ onSuggestCorrection: can("REQUEST_CHANGES") ? () => openCorrection(m) : void 0,
934
+ disabled: busy
935
+ },
936
+ m.file_id
937
+ ));
938
+ const generated = review?.generated_visual;
939
+ const hero = sourceHero?.length ? sourceHero : generated && /^https:\/\//.test(generated.url) ? /* @__PURE__ */ jsxRuntime.jsxs("figure", { children: [
940
+ /* @__PURE__ */ jsxRuntime.jsx("img", { src: generated.url, alt: formatImageCaption(generated.alt) || review?.revision.document.title || "Article illustration" }),
941
+ formatImageCaption(generated.caption) && /* @__PURE__ */ jsxRuntime.jsx("figcaption", { children: formatImageCaption(generated.caption) })
942
+ ] }) : null;
943
+ const heroBeforeHeadline = presentation.layout.headline_position === "after_hero";
944
+ return /* @__PURE__ */ jsxRuntime.jsx(ReviewMediaSession.Provider, { value: mediaSessionReady, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `sm-review-frame${publicationHeader ? " sm-review-publication-frame" : ""}`, children: [
884
945
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm-review-banner-wrap", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sm-review-private", children: [
885
- /* @__PURE__ */ jsxRuntime.jsx("strong", { children: review?.published ? "Private review" : "Private preview \xB7 Not published" }),
886
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: review?.published ? "This revision has been published. This review link remains private." : `Prepared by ${publicationName} for ${celebration ? "family " : ""}review.` })
946
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: review?.article_published || review?.published ? "Private review" : "Private preview \xB7 Not published" }),
947
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: review?.published ? "This revision has been published. This review link remains private." : review?.article_published ? "The article is published. This private link preserves the version you were invited to review." : `Prepared by ${publicationName} for ${celebration ? "family " : ""}review.` })
887
948
  ] }) }),
888
949
  publicationHeader ?? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm-review-brand-wrap", children: /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "sm-review-masthead", children: [
889
950
  nameplateUrl && /* @__PURE__ */ jsxRuntime.jsx("img", { src: nameplateUrl, alt: "", width: 80, height: 80 }),
@@ -1030,117 +1091,118 @@ function StoryReview({
1030
1091
  "newsroom for final editorial review. It has not been published yet."
1031
1092
  ] })
1032
1093
  ] }),
1033
- /* @__PURE__ */ jsxRuntime.jsxs("article", { className: "sm-review-article", children: [
1034
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "sm-review-category", children: [
1035
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: review.revision.document.section }),
1036
- celebration && /* @__PURE__ */ jsxRuntime.jsx("span", { children: human(review.celebration_type || "") })
1037
- ] }),
1038
- /* @__PURE__ */ jsxRuntime.jsx("h1", { children: review.revision.document.title }),
1039
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "sm-review-dek", children: review.revision.document.subtitle }),
1040
- /* @__PURE__ */ jsxRuntime.jsx(EditorialNotices, { editorial: review.revision.editorial }),
1041
- review.revision.document.media.filter((m) => m.position === "hero").map((m) => /* @__PURE__ */ jsxRuntime.jsx(
1042
- PrivatePhoto,
1043
- {
1044
- media: m,
1045
- base,
1046
- onChange: can("UPLOAD_MEDIA") ? () => openMedia(m) : void 0,
1047
- onSuggestCorrection: can("REQUEST_CHANGES") ? () => openCorrection(m) : void 0,
1048
- disabled: busy
1049
- },
1050
- m.file_id
1051
- )),
1052
- celebration && !review.revision.document.media.some(
1053
- (m) => m.position === "hero"
1054
- ) && can("UPLOAD_MEDIA") && /* @__PURE__ */ jsxRuntime.jsxs(
1055
- "aside",
1056
- {
1057
- className: "sm-review-photo-invitation",
1058
- "aria-label": "Family photograph",
1059
- children: [
1060
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1061
- /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "A photograph makes it yours." }),
1062
- /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Family photograph may be added before publication." })
1063
- ] }),
1064
- /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => openMedia(), children: "+ Add a photograph" })
1065
- ]
1066
- }
1067
- ),
1068
- (() => {
1069
- const blocks = parseDocumentBlocks({
1070
- content_markdown: review.revision.document.content_markdown,
1071
- media: review.revision.document.media,
1072
- blocks: review.revision.document.blocks
1073
- });
1074
- const mediaMap = /* @__PURE__ */ new Map();
1075
- for (const m of review.revision.document.media) {
1076
- mediaMap.set(m.file_id, m);
1077
- if (m.contribution_id) mediaMap.set(m.contribution_id, m);
1078
- }
1079
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm-review-prose", children: blocks.length === 0 && review.revision.content_html ? /* @__PURE__ */ jsxRuntime.jsx("div", { dangerouslySetInnerHTML: { __html: review.revision.content_html } }) : blocks.map((block, idx) => {
1080
- switch (block.type) {
1081
- case "paragraph":
1082
- return /* @__PURE__ */ jsxRuntime.jsx(
1083
- "p",
1084
- {
1085
- dangerouslySetInnerHTML: { __html: block.html }
1086
- },
1087
- idx
1088
- );
1089
- case "heading": {
1090
- if (block.level === 1)
1091
- return /* @__PURE__ */ jsxRuntime.jsx("h1", { dangerouslySetInnerHTML: { __html: block.html } }, idx);
1092
- if (block.level === 2)
1093
- return /* @__PURE__ */ jsxRuntime.jsx("h2", { dangerouslySetInnerHTML: { __html: block.html } }, idx);
1094
- if (block.level === 3)
1095
- return /* @__PURE__ */ jsxRuntime.jsx("h3", { dangerouslySetInnerHTML: { __html: block.html } }, idx);
1096
- if (block.level === 4)
1097
- return /* @__PURE__ */ jsxRuntime.jsx("h4", { dangerouslySetInnerHTML: { __html: block.html } }, idx);
1098
- if (block.level === 5)
1099
- return /* @__PURE__ */ jsxRuntime.jsx("h5", { dangerouslySetInnerHTML: { __html: block.html } }, idx);
1100
- return /* @__PURE__ */ jsxRuntime.jsx("h6", { dangerouslySetInnerHTML: { __html: block.html } }, idx);
1094
+ /* @__PURE__ */ jsxRuntime.jsxs(
1095
+ "article",
1096
+ {
1097
+ className: "sm-review-article",
1098
+ "data-article-layout": presentation.layout.renderer,
1099
+ "data-wide-body": presentation.layout.wide_body,
1100
+ "data-hero-aspect": presentation.layout.hero_aspect,
1101
+ children: [
1102
+ heroBeforeHeadline && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm-review-hero", children: hero }),
1103
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "sm-review-category", children: [
1104
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: review.revision.document.section }),
1105
+ formatLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatLabel }),
1106
+ celebration && /* @__PURE__ */ jsxRuntime.jsx("span", { children: human(review.celebration_type || "") })
1107
+ ] }),
1108
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { children: review.revision.document.title }),
1109
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "sm-review-dek", children: review.revision.document.subtitle }),
1110
+ /* @__PURE__ */ jsxRuntime.jsx(EditorialNotices, { editorial: review.revision.editorial }),
1111
+ !heroBeforeHeadline && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm-review-hero", children: hero }),
1112
+ celebration && !review.revision.document.media.some(
1113
+ (m) => m.position === "hero"
1114
+ ) && can("UPLOAD_MEDIA") && /* @__PURE__ */ jsxRuntime.jsxs(
1115
+ "aside",
1116
+ {
1117
+ className: "sm-review-photo-invitation",
1118
+ "aria-label": "Family photograph",
1119
+ children: [
1120
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1121
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "A photograph makes it yours." }),
1122
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Family photograph may be added before publication." })
1123
+ ] }),
1124
+ /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => openMedia(), children: "+ Add a photograph" })
1125
+ ]
1101
1126
  }
1102
- case "quote":
1103
- return /* @__PURE__ */ jsxRuntime.jsxs("blockquote", { children: [
1104
- /* @__PURE__ */ jsxRuntime.jsx("p", { dangerouslySetInnerHTML: { __html: block.html } }),
1105
- block.attribution && /* @__PURE__ */ jsxRuntime.jsx("cite", { children: block.attribution })
1106
- ] }, idx);
1107
- case "list":
1108
- return block.ordered ? /* @__PURE__ */ jsxRuntime.jsx("ol", { children: block.items.map((it, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { dangerouslySetInnerHTML: { __html: it } }, i)) }, idx) : /* @__PURE__ */ jsxRuntime.jsx("ul", { children: block.items.map((it, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { dangerouslySetInnerHTML: { __html: it } }, i)) }, idx);
1109
- case "divider":
1110
- return /* @__PURE__ */ jsxRuntime.jsx("hr", {}, idx);
1111
- case "inline_media": {
1112
- const m = mediaMap.get(block.media_id);
1113
- if (!m) return null;
1114
- return /* @__PURE__ */ jsxRuntime.jsx(
1115
- PrivatePhoto,
1116
- {
1117
- media: m,
1118
- base,
1119
- onChange: m.kind !== "video" && can("UPLOAD_MEDIA") ? () => openMedia(m) : void 0,
1120
- onSuggestCorrection: can("REQUEST_CHANGES") ? () => openCorrection(m) : void 0,
1121
- disabled: busy,
1122
- isInline: true
1123
- },
1124
- m.file_id
1125
- );
1127
+ ),
1128
+ (() => {
1129
+ const blocks = parseDocumentBlocks({
1130
+ content_markdown: review.revision.document.content_markdown,
1131
+ media: review.revision.document.media,
1132
+ blocks: review.revision.document.blocks
1133
+ });
1134
+ const mediaMap = /* @__PURE__ */ new Map();
1135
+ for (const m of review.revision.document.media) {
1136
+ mediaMap.set(m.file_id, m);
1137
+ if (m.contribution_id) mediaMap.set(m.contribution_id, m);
1126
1138
  }
1127
- default:
1128
- return null;
1129
- }
1130
- }) });
1131
- })(),
1132
- review.revision.document.media.filter((m) => m.position === "gallery").map((m) => /* @__PURE__ */ jsxRuntime.jsx(
1133
- PrivatePhoto,
1134
- {
1135
- media: m,
1136
- base,
1137
- onChange: m.kind !== "video" && can("UPLOAD_MEDIA") ? () => openMedia(m) : void 0,
1138
- onSuggestCorrection: can("REQUEST_CHANGES") ? () => openCorrection(m) : void 0,
1139
- disabled: busy
1140
- },
1141
- m.file_id
1142
- ))
1143
- ] }),
1139
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm-review-prose", children: blocks.length === 0 && review.revision.content_html ? /* @__PURE__ */ jsxRuntime.jsx("div", { dangerouslySetInnerHTML: { __html: review.revision.content_html } }) : blocks.map((block, idx) => {
1140
+ switch (block.type) {
1141
+ case "paragraph":
1142
+ return /* @__PURE__ */ jsxRuntime.jsx(
1143
+ "p",
1144
+ {
1145
+ dangerouslySetInnerHTML: { __html: block.html }
1146
+ },
1147
+ idx
1148
+ );
1149
+ case "heading": {
1150
+ if (block.level === 1)
1151
+ return /* @__PURE__ */ jsxRuntime.jsx("h1", { dangerouslySetInnerHTML: { __html: block.html } }, idx);
1152
+ if (block.level === 2)
1153
+ return /* @__PURE__ */ jsxRuntime.jsx("h2", { dangerouslySetInnerHTML: { __html: block.html } }, idx);
1154
+ if (block.level === 3)
1155
+ return /* @__PURE__ */ jsxRuntime.jsx("h3", { dangerouslySetInnerHTML: { __html: block.html } }, idx);
1156
+ if (block.level === 4)
1157
+ return /* @__PURE__ */ jsxRuntime.jsx("h4", { dangerouslySetInnerHTML: { __html: block.html } }, idx);
1158
+ if (block.level === 5)
1159
+ return /* @__PURE__ */ jsxRuntime.jsx("h5", { dangerouslySetInnerHTML: { __html: block.html } }, idx);
1160
+ return /* @__PURE__ */ jsxRuntime.jsx("h6", { dangerouslySetInnerHTML: { __html: block.html } }, idx);
1161
+ }
1162
+ case "quote":
1163
+ return /* @__PURE__ */ jsxRuntime.jsxs("blockquote", { children: [
1164
+ /* @__PURE__ */ jsxRuntime.jsx("p", { dangerouslySetInnerHTML: { __html: block.html } }),
1165
+ block.attribution && /* @__PURE__ */ jsxRuntime.jsx("cite", { children: block.attribution })
1166
+ ] }, idx);
1167
+ case "list":
1168
+ return block.ordered ? /* @__PURE__ */ jsxRuntime.jsx("ol", { children: block.items.map((it, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { dangerouslySetInnerHTML: { __html: it } }, i)) }, idx) : /* @__PURE__ */ jsxRuntime.jsx("ul", { children: block.items.map((it, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { dangerouslySetInnerHTML: { __html: it } }, i)) }, idx);
1169
+ case "divider":
1170
+ return /* @__PURE__ */ jsxRuntime.jsx("hr", {}, idx);
1171
+ case "inline_media": {
1172
+ const m = mediaMap.get(block.media_id);
1173
+ if (!m) return null;
1174
+ return /* @__PURE__ */ jsxRuntime.jsx(
1175
+ PrivatePhoto,
1176
+ {
1177
+ media: m,
1178
+ base,
1179
+ onChange: m.kind !== "video" && can("UPLOAD_MEDIA") ? () => openMedia(m) : void 0,
1180
+ onSuggestCorrection: can("REQUEST_CHANGES") ? () => openCorrection(m) : void 0,
1181
+ disabled: busy,
1182
+ isInline: true
1183
+ },
1184
+ m.file_id
1185
+ );
1186
+ }
1187
+ default:
1188
+ return null;
1189
+ }
1190
+ }) });
1191
+ })(),
1192
+ review.revision.document.media.filter((m) => m.position === "gallery").map((m) => /* @__PURE__ */ jsxRuntime.jsx(
1193
+ PrivatePhoto,
1194
+ {
1195
+ media: m,
1196
+ base,
1197
+ onChange: m.kind !== "video" && can("UPLOAD_MEDIA") ? () => openMedia(m) : void 0,
1198
+ onSuggestCorrection: can("REQUEST_CHANGES") ? () => openCorrection(m) : void 0,
1199
+ disabled: busy
1200
+ },
1201
+ m.file_id
1202
+ ))
1203
+ ]
1204
+ }
1205
+ ),
1144
1206
  celebration && /* @__PURE__ */ jsxRuntime.jsxs("section", { id: "review-options", className: "sm-review-welcome", children: [
1145
1207
  /* @__PURE__ */ jsxRuntime.jsx("h2", { children: "Help us make this just right" }),
1146
1208
  /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
@@ -1841,7 +1903,7 @@ function StoryReview({
1841
1903
  ] })
1842
1904
  ] }),
1843
1905
  publicationFooter
1844
- ] });
1906
+ ] }) });
1845
1907
  }
1846
1908
  function PrivatePhoto({
1847
1909
  base,
@@ -1851,9 +1913,11 @@ function PrivatePhoto({
1851
1913
  disabled,
1852
1914
  isInline
1853
1915
  }) {
1916
+ const sessionReady = react.useContext(ReviewMediaSession);
1854
1917
  const [url, setUrl] = react.useState("");
1855
1918
  const [video, setVideo] = react.useState(false);
1856
1919
  react.useEffect(() => {
1920
+ if (!sessionReady) return;
1857
1921
  let active = true, object = "";
1858
1922
  void fetch(`${base}/media/${encodeURIComponent(media.file_id)}`, {
1859
1923
  cache: "no-store",
@@ -1872,7 +1936,7 @@ function PrivatePhoto({
1872
1936
  active = false;
1873
1937
  if (object) URL.revokeObjectURL(object);
1874
1938
  };
1875
- }, [base, media.file_id]);
1939
+ }, [base, media.file_id, sessionReady]);
1876
1940
  const captionInfo = formatArtworkCaption(media);
1877
1941
  return /* @__PURE__ */ jsxRuntime.jsxs(
1878
1942
  "figure",
@@ -1896,7 +1960,7 @@ function PrivatePhoto({
1896
1960
  alt: media.alt || captionInfo.title || "Story photograph",
1897
1961
  referrerPolicy: "no-referrer"
1898
1962
  }
1899
- ) : /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Media unavailable." }),
1963
+ ) : /* @__PURE__ */ jsxRuntime.jsx("p", { children: sessionReady ? "Media unavailable." : "Loading photograph\u2026" }),
1900
1964
  /* @__PURE__ */ jsxRuntime.jsxs("figcaption", { className: "sm-review-photo-details", children: [
1901
1965
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sm-review-caption-text", children: [
1902
1966
  captionInfo.title && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm-review-artwork-title", children: /* @__PURE__ */ jsxRuntime.jsxs("strong", { children: [
@@ -1,6 +1,15 @@
1
+ import { A as ArticleFormat, a as ArticlePresentation } from './article-presentation-0d1FiDgA.cjs';
1
2
  import React__default from 'react';
2
3
  import { C as ContentBlock } from './article-blocks-DsabIDmH.cjs';
3
4
 
5
+ type Data = Record<string, any>;
6
+ type Draft = {
7
+ panel: string;
8
+ version: number;
9
+ data: Data;
10
+ updated_at?: string;
11
+ };
12
+
4
13
  type Media = {
5
14
  kind?: "image" | "video";
6
15
  contribution_id: string;
@@ -18,6 +27,11 @@ type Media = {
18
27
  rights_status?: string;
19
28
  };
20
29
  type Document = {
30
+ format?: ArticleFormat;
31
+ layout?: {
32
+ id: string;
33
+ version: number;
34
+ };
21
35
  title: string;
22
36
  subtitle: string;
23
37
  content_markdown: string;
@@ -41,7 +55,59 @@ type Revision = {
41
55
  document: Document;
42
56
  content_html: string;
43
57
  };
44
- declare function StoryReview({ sessionId, publicationName, nameplateUrl, publicationHeader, publicationFooter, readerAccountUrl, renderSignIn, feedbackEmail, }: {
58
+ type StoryReviewData = {
59
+ session_id?: string;
60
+ identity_required?: boolean;
61
+ participant?: boolean;
62
+ response_drafts?: Record<string, Draft>;
63
+ publication: {
64
+ name: string;
65
+ };
66
+ reviewer: string;
67
+ revision: Revision;
68
+ history: Revision[];
69
+ permissions: string[];
70
+ questions: {
71
+ id: string;
72
+ prompt: string;
73
+ required: boolean;
74
+ }[];
75
+ contributions: {
76
+ id: string;
77
+ kind: string;
78
+ state: string;
79
+ payload: {
80
+ message?: string;
81
+ file_id?: string;
82
+ caption?: string;
83
+ credit?: string;
84
+ answers?: Record<string, string>;
85
+ target?: {
86
+ kind: string;
87
+ reference?: string;
88
+ };
89
+ alt?: string;
90
+ };
91
+ }[];
92
+ review_policy: string;
93
+ published: boolean;
94
+ article_published?: boolean;
95
+ presentation?: ArticlePresentation;
96
+ generated_visual?: {
97
+ url: string;
98
+ alt?: string;
99
+ caption?: string;
100
+ label?: string;
101
+ };
102
+ permalink_path?: string;
103
+ public_article?: {
104
+ id: string;
105
+ path: string;
106
+ } | null;
107
+ celebration_type?: string | null;
108
+ decision?: string | null;
109
+ };
110
+ declare function StoryReview({ sessionId, publicationName, nameplateUrl, publicationHeader, publicationFooter, readerAccountUrl, renderSignIn, feedbackEmail, initialReview, onReviewLoaded, formatImageCaption, }: {
45
111
  sessionId: string;
46
112
  publicationName: string;
47
113
  nameplateUrl?: string;
@@ -52,6 +118,10 @@ declare function StoryReview({ sessionId, publicationName, nameplateUrl, publica
52
118
  readerAccountUrl?: string;
53
119
  renderSignIn?: (returnTo: string) => React__default.ReactNode;
54
120
  feedbackEmail?: string;
121
+ /** Only a server-validated private capability may supply this snapshot. Never cache publicly. */
122
+ initialReview?: StoryReviewData;
123
+ onReviewLoaded?: (review: StoryReviewData) => void;
124
+ formatImageCaption?: (value?: string | null) => string;
55
125
  }): React__default.JSX.Element;
56
126
  /** Paragraph comparisons preserve readable prose and identify title/dek and media changes. */
57
127
  declare function ReviewDiff({ before, after, }: {
@@ -59,4 +129,4 @@ declare function ReviewDiff({ before, after, }: {
59
129
  after: Revision;
60
130
  }): React__default.JSX.Element;
61
131
 
62
- export { type Document, type Media, ReviewDiff, StoryReview };
132
+ export { type Document, type Media, ReviewDiff, StoryReview, type StoryReviewData };
@@ -1,6 +1,15 @@
1
+ import { A as ArticleFormat, a as ArticlePresentation } from './article-presentation-0d1FiDgA.js';
1
2
  import React__default from 'react';
2
3
  import { C as ContentBlock } from './article-blocks-DsabIDmH.js';
3
4
 
5
+ type Data = Record<string, any>;
6
+ type Draft = {
7
+ panel: string;
8
+ version: number;
9
+ data: Data;
10
+ updated_at?: string;
11
+ };
12
+
4
13
  type Media = {
5
14
  kind?: "image" | "video";
6
15
  contribution_id: string;
@@ -18,6 +27,11 @@ type Media = {
18
27
  rights_status?: string;
19
28
  };
20
29
  type Document = {
30
+ format?: ArticleFormat;
31
+ layout?: {
32
+ id: string;
33
+ version: number;
34
+ };
21
35
  title: string;
22
36
  subtitle: string;
23
37
  content_markdown: string;
@@ -41,7 +55,59 @@ type Revision = {
41
55
  document: Document;
42
56
  content_html: string;
43
57
  };
44
- declare function StoryReview({ sessionId, publicationName, nameplateUrl, publicationHeader, publicationFooter, readerAccountUrl, renderSignIn, feedbackEmail, }: {
58
+ type StoryReviewData = {
59
+ session_id?: string;
60
+ identity_required?: boolean;
61
+ participant?: boolean;
62
+ response_drafts?: Record<string, Draft>;
63
+ publication: {
64
+ name: string;
65
+ };
66
+ reviewer: string;
67
+ revision: Revision;
68
+ history: Revision[];
69
+ permissions: string[];
70
+ questions: {
71
+ id: string;
72
+ prompt: string;
73
+ required: boolean;
74
+ }[];
75
+ contributions: {
76
+ id: string;
77
+ kind: string;
78
+ state: string;
79
+ payload: {
80
+ message?: string;
81
+ file_id?: string;
82
+ caption?: string;
83
+ credit?: string;
84
+ answers?: Record<string, string>;
85
+ target?: {
86
+ kind: string;
87
+ reference?: string;
88
+ };
89
+ alt?: string;
90
+ };
91
+ }[];
92
+ review_policy: string;
93
+ published: boolean;
94
+ article_published?: boolean;
95
+ presentation?: ArticlePresentation;
96
+ generated_visual?: {
97
+ url: string;
98
+ alt?: string;
99
+ caption?: string;
100
+ label?: string;
101
+ };
102
+ permalink_path?: string;
103
+ public_article?: {
104
+ id: string;
105
+ path: string;
106
+ } | null;
107
+ celebration_type?: string | null;
108
+ decision?: string | null;
109
+ };
110
+ declare function StoryReview({ sessionId, publicationName, nameplateUrl, publicationHeader, publicationFooter, readerAccountUrl, renderSignIn, feedbackEmail, initialReview, onReviewLoaded, formatImageCaption, }: {
45
111
  sessionId: string;
46
112
  publicationName: string;
47
113
  nameplateUrl?: string;
@@ -52,6 +118,10 @@ declare function StoryReview({ sessionId, publicationName, nameplateUrl, publica
52
118
  readerAccountUrl?: string;
53
119
  renderSignIn?: (returnTo: string) => React__default.ReactNode;
54
120
  feedbackEmail?: string;
121
+ /** Only a server-validated private capability may supply this snapshot. Never cache publicly. */
122
+ initialReview?: StoryReviewData;
123
+ onReviewLoaded?: (review: StoryReviewData) => void;
124
+ formatImageCaption?: (value?: string | null) => string;
55
125
  }): React__default.JSX.Element;
56
126
  /** Paragraph comparisons preserve readable prose and identify title/dek and media changes. */
57
127
  declare function ReviewDiff({ before, after, }: {
@@ -59,4 +129,4 @@ declare function ReviewDiff({ before, after, }: {
59
129
  after: Revision;
60
130
  }): React__default.JSX.Element;
61
131
 
62
- export { type Document, type Media, ReviewDiff, StoryReview };
132
+ export { type Document, type Media, ReviewDiff, StoryReview, type StoryReviewData };