@scalemule/news-publication 0.0.42 → 0.0.44

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.
@@ -1,4 +1,5 @@
1
1
  "use client";
2
+ import { parseDocumentBlocks, formatArtworkCaption } from './chunk-OISLCYER.js';
2
3
  import { useState, useRef, useEffect } from 'react';
3
4
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
5
 
@@ -397,15 +398,27 @@ function StoryReview({
397
398
  setRights(false);
398
399
  setPanel("media");
399
400
  }
401
+ function openCorrection(photo) {
402
+ const title = photo.artwork_title || photo.caption || "Photograph";
403
+ setPanel("changes");
404
+ drafts.set("changes", {
405
+ ...drafts.entries.changes?.data,
406
+ correctionKind: "Correct a detail",
407
+ message: drafts.entries.changes?.data.message || `Correction regarding \u201C${title}\u201D: `
408
+ });
409
+ }
400
410
  useEffect(() => {
401
411
  if (panel !== "article") {
402
- responseRef.current?.scrollIntoView({
403
- block: "start",
404
- behavior: window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ? "auto" : "smooth"
405
- });
406
- responseRef.current?.focus({ preventScroll: true });
412
+ responseRef.current?.focus();
413
+ const handleKeyDown = (e) => {
414
+ if (e.key === "Escape" && !busy) {
415
+ setPanel("article");
416
+ }
417
+ };
418
+ window.addEventListener("keydown", handleKeyDown);
419
+ return () => window.removeEventListener("keydown", handleKeyDown);
407
420
  }
408
- }, [panel]);
421
+ }, [panel, busy]);
409
422
  const emptyMediaInfo = { caption: "", who_is_pictured: "", creator: "", when_taken: "", where_taken: "", credit: "", alt: "" };
410
423
  const drafts = useReviewDrafts(identity, review?.identity_required ? void 0 : review?.revision.id, review?.response_drafts, request);
411
424
  const currentDraft = drafts.entries[panel];
@@ -774,11 +787,25 @@ function StoryReview({
774
787
  ] }),
775
788
  /* @__PURE__ */ jsx("button", { disabled: busy || pending.length > 0 || !!file, onClick: async () => {
776
789
  setBusy(true);
790
+ setError("");
777
791
  try {
792
+ const prevReviewer = review.reviewer;
778
793
  await request("/leave", {});
794
+ setReader(null);
779
795
  setPanelState("article");
780
796
  setIdentityStep("name");
781
797
  await load();
798
+ if (readerAccountUrl) {
799
+ void fetch(readerAccountUrl, { credentials: "same-origin", cache: "no-store" }).then(async (res) => {
800
+ if (res.ok) {
801
+ const d = await res.json();
802
+ setReader(d.user || null);
803
+ } else {
804
+ setReader(null);
805
+ }
806
+ }).catch(() => setReader(null));
807
+ }
808
+ setNotice(`You are no longer contributing as ${prevReviewer}. You can review the draft or sign in with another account.`);
782
809
  } catch (e) {
783
810
  setError(e.message);
784
811
  } finally {
@@ -828,6 +855,7 @@ function StoryReview({
828
855
  media: m,
829
856
  base,
830
857
  onChange: can("UPLOAD_MEDIA") ? () => openMedia(m) : void 0,
858
+ onSuggestCorrection: can("REQUEST_CHANGES") ? () => openCorrection(m) : void 0,
831
859
  disabled: busy
832
860
  },
833
861
  m.file_id
@@ -848,19 +876,77 @@ function StoryReview({
848
876
  ]
849
877
  }
850
878
  ),
851
- /* @__PURE__ */ jsx(
852
- "div",
853
- {
854
- className: "sm-review-prose",
855
- dangerouslySetInnerHTML: { __html: review.revision.content_html }
879
+ (() => {
880
+ const blocks = parseDocumentBlocks({
881
+ content_markdown: review.revision.document.content_markdown,
882
+ media: review.revision.document.media,
883
+ blocks: review.revision.document.blocks
884
+ });
885
+ const mediaMap = /* @__PURE__ */ new Map();
886
+ for (const m of review.revision.document.media) {
887
+ mediaMap.set(m.file_id, m);
888
+ if (m.contribution_id) mediaMap.set(m.contribution_id, m);
856
889
  }
857
- ),
858
- review.revision.document.media.filter((m) => m.position !== "hero").map((m) => /* @__PURE__ */ jsx(
890
+ return /* @__PURE__ */ jsx("div", { className: "sm-review-prose", children: blocks.length === 0 && review.revision.content_html ? /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: review.revision.content_html } }) : blocks.map((block, idx) => {
891
+ switch (block.type) {
892
+ case "paragraph":
893
+ return /* @__PURE__ */ jsx(
894
+ "p",
895
+ {
896
+ dangerouslySetInnerHTML: { __html: block.html }
897
+ },
898
+ idx
899
+ );
900
+ case "heading": {
901
+ if (block.level === 1)
902
+ return /* @__PURE__ */ jsx("h1", { dangerouslySetInnerHTML: { __html: block.html } }, idx);
903
+ if (block.level === 2)
904
+ return /* @__PURE__ */ jsx("h2", { dangerouslySetInnerHTML: { __html: block.html } }, idx);
905
+ if (block.level === 3)
906
+ return /* @__PURE__ */ jsx("h3", { dangerouslySetInnerHTML: { __html: block.html } }, idx);
907
+ if (block.level === 4)
908
+ return /* @__PURE__ */ jsx("h4", { dangerouslySetInnerHTML: { __html: block.html } }, idx);
909
+ if (block.level === 5)
910
+ return /* @__PURE__ */ jsx("h5", { dangerouslySetInnerHTML: { __html: block.html } }, idx);
911
+ return /* @__PURE__ */ jsx("h6", { dangerouslySetInnerHTML: { __html: block.html } }, idx);
912
+ }
913
+ case "quote":
914
+ return /* @__PURE__ */ jsxs("blockquote", { children: [
915
+ /* @__PURE__ */ jsx("p", { dangerouslySetInnerHTML: { __html: block.html } }),
916
+ block.attribution && /* @__PURE__ */ jsx("cite", { children: block.attribution })
917
+ ] }, idx);
918
+ case "list":
919
+ return block.ordered ? /* @__PURE__ */ jsx("ol", { children: block.items.map((it, i) => /* @__PURE__ */ jsx("li", { dangerouslySetInnerHTML: { __html: it } }, i)) }, idx) : /* @__PURE__ */ jsx("ul", { children: block.items.map((it, i) => /* @__PURE__ */ jsx("li", { dangerouslySetInnerHTML: { __html: it } }, i)) }, idx);
920
+ case "divider":
921
+ return /* @__PURE__ */ jsx("hr", {}, idx);
922
+ case "inline_media": {
923
+ const m = mediaMap.get(block.media_id);
924
+ if (!m) return null;
925
+ return /* @__PURE__ */ jsx(
926
+ PrivatePhoto,
927
+ {
928
+ media: m,
929
+ base,
930
+ onChange: m.kind !== "video" && can("UPLOAD_MEDIA") ? () => openMedia(m) : void 0,
931
+ onSuggestCorrection: can("REQUEST_CHANGES") ? () => openCorrection(m) : void 0,
932
+ disabled: busy,
933
+ isInline: true
934
+ },
935
+ m.file_id
936
+ );
937
+ }
938
+ default:
939
+ return null;
940
+ }
941
+ }) });
942
+ })(),
943
+ review.revision.document.media.filter((m) => m.position === "gallery").map((m) => /* @__PURE__ */ jsx(
859
944
  PrivatePhoto,
860
945
  {
861
946
  media: m,
862
947
  base,
863
948
  onChange: m.kind !== "video" && can("UPLOAD_MEDIA") ? () => openMedia(m) : void 0,
949
+ onSuggestCorrection: can("REQUEST_CHANGES") ? () => openCorrection(m) : void 0,
864
950
  disabled: busy
865
951
  },
866
952
  m.file_id
@@ -934,544 +1020,573 @@ function StoryReview({
934
1020
  ] }, name)),
935
1021
  file && !pending.some(([name]) => name === "media") && /* @__PURE__ */ jsx("button", { onClick: () => setPanel("media"), children: "Continue your photo upload" })
936
1022
  ] }),
937
- panel === "share" && /* @__PURE__ */ jsxs("section", { ref: responseRef, tabIndex: -1, className: "sm-review-form", "aria-label": "Share private preview", children: [
938
- /* @__PURE__ */ jsx("h2", { children: "Share with your family" }),
939
- errorMessage,
940
- /* @__PURE__ */ jsxs("p", { children: [
941
- "Anyone with this link can read the private preview. ",
942
- renderSignIn ? "To contribute, they can continue with Google or use an email code." : "To send feedback or photos, they will enter their own name and verify their email. No account is needed."
943
- ] }),
944
- /* @__PURE__ */ jsx("label", { htmlFor: "review-share-url", children: "Private preview link" }),
945
- /* @__PURE__ */ jsx("input", { id: "review-share-url", readOnly: true, value: shareUrl, onFocus: (e) => e.currentTarget.select() }),
946
- /* @__PURE__ */ jsx("button", { onClick: async () => {
947
- try {
948
- await navigator.clipboard.writeText(shareUrl);
949
- setNotice("Private link copied. Paste it into your message to family.");
950
- } catch {
951
- setNotice("Select the link above, then choose Copy on your device.");
952
- }
953
- }, children: "Copy private link" }),
954
- /* @__PURE__ */ jsx("button", { onClick: () => setPanel("article"), children: "Back to the story" })
955
- ] }),
956
- panel === "question-list" && /* @__PURE__ */ jsxs("section", { ref: responseRef, tabIndex: -1, className: "sm-review-form", "aria-label": "Optional story questions", children: [
957
- /* @__PURE__ */ jsxs("div", { className: "sm-review-form-heading", children: [
958
- /* @__PURE__ */ jsx("h2", { children: "Tell us a little more" }),
959
- /* @__PURE__ */ jsx("button", { onClick: () => setPanel("article"), children: "Close" })
960
- ] }),
961
- /* @__PURE__ */ jsx("p", { children: "Completely optional. Read the questions first, then answer online or copy them into an email. Even one answer helps." }),
962
- /* @__PURE__ */ jsx("ol", { className: "sm-review-question-list", children: review.questions.filter((q, i) => moreQuestions || i < 5 || q.required).map((q) => /* @__PURE__ */ jsx("li", { children: q.prompt }, q.id)) }),
963
- !moreQuestions && review.questions.length > 5 && /* @__PURE__ */ jsx("button", { onClick: () => setMoreQuestions(true), children: "See all questions" }),
964
- /* @__PURE__ */ jsx("button", { className: "sm-review-primary", onClick: () => setPanel("questions"), children: "Answer online" }),
965
- /* @__PURE__ */ jsx("p", { children: "Sign in to save your answers as you go. You can return whenever it suits you." }),
966
- /* @__PURE__ */ jsx("button", { onClick: async () => {
967
- try {
968
- await navigator.clipboard.writeText(questionText);
969
- setNotice("Questions copied. Paste them into an email and answer as many as you like.");
970
- } catch {
971
- setCopiedQuestions(true);
972
- }
973
- }, children: "Copy all questions" }),
974
- copiedQuestions && /* @__PURE__ */ jsxs("label", { children: [
975
- "Copy these questions",
976
- /* @__PURE__ */ jsx("textarea", { readOnly: true, rows: 10, value: questionText, onFocus: (e) => e.currentTarget.select() })
977
- ] }),
978
- questionsEmail && /* @__PURE__ */ jsxs("p", { children: [
979
- /* @__PURE__ */ jsx("a", { href: questionsEmail, children: "Answer by email" }),
980
- " \u2014 opens your email app with the questions ready."
981
- ] })
982
- ] }),
983
- panel === "identity" && /* @__PURE__ */ jsxs("section", { ref: responseRef, tabIndex: -1, className: "sm-review-form", "aria-label": "Introduce yourself", children: [
984
- /* @__PURE__ */ jsx("h2", { children: identityStep === "code" ? "Check your email" : "Continue to help with the story" }),
985
- errorMessage,
986
- /* @__PURE__ */ jsx("p", { children: "Your name accompanies your feedback. Your email stays private." }),
987
- identityStep === "name" && renderSignIn && /* @__PURE__ */ jsxs(Fragment, { children: [
988
- readerLoading ? /* @__PURE__ */ jsx("p", { role: "status", children: "Checking your sign-in\u2026" }) : reader?.email_verified ? /* @__PURE__ */ jsxs(Fragment, { children: [
989
- /* @__PURE__ */ jsxs("button", { className: "sm-review-primary", disabled: busy, onClick: () => void connectReader(), children: [
990
- "Continue as ",
991
- reader.full_name || reader.email
992
- ] }),
993
- /* @__PURE__ */ jsx("p", { children: "Your reader account is ready. No email code is needed." })
994
- ] }) : renderSignIn(signInReturn),
995
- !useEmailCode && /* @__PURE__ */ jsx("button", { className: "sm-review-quiet", onClick: () => setUseEmailCode(true), children: "Use an email code instead" })
996
- ] }),
997
- identityStep === "name" && useEmailCode && /* @__PURE__ */ jsxs("form", { onSubmit: async (e) => {
998
- e.preventDefault();
999
- setBusy(true);
1000
- setError("");
1001
- try {
1002
- await request("/join", { name: contributorName, email: contributorEmail });
1003
- await request("/verify", {});
1004
- setIdentityStep("code");
1005
- setCode("");
1006
- } catch (e2) {
1007
- setError(e2.message);
1008
- } finally {
1009
- setBusy(false);
1010
- }
1011
- }, children: [
1012
- /* @__PURE__ */ jsx("label", { htmlFor: "contributor-name", children: "Your name" }),
1013
- /* @__PURE__ */ jsx("input", { id: "contributor-name", autoComplete: "name", value: contributorName, maxLength: 150, required: true, onChange: (e) => setContributorName(e.target.value) }),
1014
- /* @__PURE__ */ jsx("label", { htmlFor: "contributor-email", children: "Your email" }),
1015
- /* @__PURE__ */ jsx("input", { id: "contributor-email", type: "email", autoComplete: "email", value: contributorEmail, maxLength: 320, required: true, onChange: (e) => setContributorEmail(e.target.value) }),
1016
- /* @__PURE__ */ jsx("p", { children: "We'll email you a code to confirm it's you." }),
1017
- /* @__PURE__ */ jsx("p", { children: "No password or account is needed for this option." }),
1018
- /* @__PURE__ */ jsx("button", { disabled: busy, children: busy ? "Sending your code\u2026" : "Email me a code" })
1019
- ] }),
1020
- identityStep === "code" && /* @__PURE__ */ jsxs("form", { onSubmit: async (e) => {
1021
- e.preventDefault();
1022
- setBusy(true);
1023
- setError("");
1024
- try {
1025
- await request("/verify", { code });
1026
- await load();
1027
- setCode("");
1028
- setPanelState(requestedPanel);
1029
- } catch (e2) {
1030
- setError(e2.message);
1031
- } finally {
1032
- setBusy(false);
1033
- }
1034
- }, children: [
1035
- /* @__PURE__ */ jsxs("p", { children: [
1036
- "Enter the eight-digit code sent to ",
1037
- /* @__PURE__ */ jsx("strong", { children: contributorEmail }),
1038
- ". It expires in ten minutes."
1039
- ] }),
1040
- /* @__PURE__ */ jsx("label", { htmlFor: "contributor-code", children: "Email verification code" }),
1041
- /* @__PURE__ */ jsx("input", { id: "contributor-code", inputMode: "numeric", autoComplete: "one-time-code", pattern: "[0-9]{8}", maxLength: 8, value: code, required: true, onChange: (e) => setCode(e.target.value.replace(/\s/g, "")) }),
1042
- /* @__PURE__ */ jsx("button", { disabled: busy, children: busy ? "Verifying\u2026" : "Verify and continue" }),
1043
- /* @__PURE__ */ jsx("button", { type: "button", disabled: busy, onClick: async () => {
1044
- setBusy(true);
1045
- try {
1046
- await request("/verify", {});
1047
- setNotice("A new code was sent. Check your inbox and spam folder.");
1048
- } catch (e) {
1049
- setError(e.message);
1050
- } finally {
1051
- setBusy(false);
1052
- }
1053
- }, children: "Send a new code" }),
1054
- /* @__PURE__ */ jsx("button", { type: "button", disabled: busy, onClick: () => setIdentityStep("name"), children: "Use a different email" })
1055
- ] }),
1056
- emailAlternative,
1057
- /* @__PURE__ */ jsx("button", { disabled: busy, onClick: () => setPanel("article"), children: "Just read the story" })
1058
- ] }),
1059
- panel === "feedback" && /* @__PURE__ */ jsxs("section", { ref: responseRef, tabIndex: -1, className: "sm-review-form", "aria-label": "Family feedback", children: [
1060
- /* @__PURE__ */ jsx("h2", { children: "Looks good to you?" }),
1061
- errorMessage,
1062
- /* @__PURE__ */ jsxs("p", { children: [
1063
- "We'll send your feedback as ",
1064
- review.reviewer,
1065
- ". The newsroom will request approval from the designated family reviewer separately. This will not publish the story."
1066
- ] }),
1067
- /* @__PURE__ */ jsx("button", { className: "sm-review-primary", disabled: busy, onClick: () => void send({ action: "COMMENT", message: "This draft looks good to me.", target: { kind: "STORY" } }, "Thank you. Your feedback has been sent to the newsroom. The story is still private.", false), children: "Send my feedback" }),
1068
- /* @__PURE__ */ jsx("button", { disabled: busy, onClick: () => setPanel("article"), children: "Back to the story" })
1069
- ] }),
1070
- !["article", "identity", "share", "feedback", "question-list"].includes(panel) && /* @__PURE__ */ jsxs(
1071
- "section",
1023
+ panel !== "article" && /* @__PURE__ */ jsx(
1024
+ "div",
1072
1025
  {
1073
- ref: responseRef,
1074
- tabIndex: -1,
1075
- className: "sm-review-form",
1076
- "aria-label": "Your response",
1077
- children: [
1078
- /* @__PURE__ */ jsxs("div", { className: "sm-review-form-heading", children: [
1079
- /* @__PURE__ */ jsx("h2", { children: panel === "approve" ? "Approve this exact draft" : panel === "edits" ? "Propose changes" : panel === "media" ? photoToReplace ? "Change photo" : "Contribute a photograph" : panel === "questions" ? "Tell us a little more" : panel === "history" ? "Selected revision history" : panel === "decline" ? "Prefer not to publish" : panel === "changes" ? "Suggest a change" : "Message the newsroom" }),
1080
- /* @__PURE__ */ jsx("button", { disabled: busy, onClick: () => setPanel("article"), children: "Close" })
1026
+ className: "sm-review-modal-backdrop",
1027
+ role: "dialog",
1028
+ "aria-modal": "true",
1029
+ onClick: (e) => {
1030
+ if (e.target === e.currentTarget && !busy) setPanel("article");
1031
+ },
1032
+ children: /* @__PURE__ */ jsxs("div", { className: "sm-review-modal-container", children: [
1033
+ panel === "share" && /* @__PURE__ */ jsxs("section", { ref: responseRef, tabIndex: -1, className: "sm-review-form", "aria-label": "Share private preview", children: [
1034
+ /* @__PURE__ */ jsxs("div", { className: "sm-review-form-heading", children: [
1035
+ /* @__PURE__ */ jsx("h2", { children: "Share with your family" }),
1036
+ /* @__PURE__ */ jsx("button", { disabled: busy, type: "button", onClick: () => setPanel("article"), "aria-label": "Close", children: "Close" })
1037
+ ] }),
1038
+ errorMessage,
1039
+ /* @__PURE__ */ jsxs("p", { children: [
1040
+ "Anyone with this link can read the private preview. ",
1041
+ renderSignIn ? "To contribute, they can continue with Google or use an email code." : "To send feedback or photos, they will enter their own name and verify their email. No account is needed."
1042
+ ] }),
1043
+ /* @__PURE__ */ jsx("label", { htmlFor: "review-share-url", children: "Private preview link" }),
1044
+ /* @__PURE__ */ jsx("input", { id: "review-share-url", readOnly: true, value: shareUrl, onFocus: (e) => e.currentTarget.select() }),
1045
+ /* @__PURE__ */ jsx("button", { onClick: async () => {
1046
+ try {
1047
+ await navigator.clipboard.writeText(shareUrl);
1048
+ setNotice("Private link copied. Paste it into your message to family.");
1049
+ } catch {
1050
+ setNotice("Select the link above, then choose Copy on your device.");
1051
+ }
1052
+ }, children: "Copy private link" }),
1053
+ /* @__PURE__ */ jsx("button", { onClick: () => setPanel("article"), children: "Back to the story" })
1081
1054
  ] }),
1082
- errorMessage,
1083
- panel !== "history" && /* @__PURE__ */ jsxs("div", { className: "sm-review-save-state", role: "status", "aria-live": "polite", children: [
1084
- currentDraft?.status === "conflict" ? /* @__PURE__ */ jsxs(Fragment, { children: [
1085
- /* @__PURE__ */ jsx("p", { children: "A response was saved in another tab or device. Your writing here is still available. Choose which version to keep." }),
1086
- /* @__PURE__ */ jsx("button", { disabled: busy, onClick: () => void drafts.resolve(panel, true).catch((e) => setError(e.message)), children: "Keep what I wrote here" }),
1087
- /* @__PURE__ */ jsx("button", { disabled: busy, onClick: () => void drafts.resolve(panel, false).catch((e) => setError(e.message)), children: "Use the saved response" })
1088
- ] }) : currentDraft?.status === "offline" ? /* @__PURE__ */ jsxs(Fragment, { children: [
1089
- /* @__PURE__ */ jsx("p", { children: currentDraft.local ? "Saved on this device. Waiting for a connection to save online." : "Not saved yet. Keep this page open and try again." }),
1090
- /* @__PURE__ */ jsx("button", { disabled: busy, onClick: () => void drafts.flush(panel).catch((e) => setError(e.message)), children: "Try saving again" })
1091
- ] }) : /* @__PURE__ */ jsx("p", { children: currentDraft?.status === "saving" ? "Saving your writing\u2026" : hasDraft(currentDraft?.data) ? "Saved \u2014 not sent yet. You can safely come back later." : "Your writing saves automatically. Use the Send button below when you're ready to share it with the newsroom." }),
1092
- hasDraft(currentDraft?.data) && /* @__PURE__ */ jsx("button", { className: "sm-review-quiet", disabled: busy, onClick: () => {
1093
- if (window.confirm("Discard this unsent response? Responses already sent will be kept.")) void drafts.discard(panel).catch((e) => setError(e.message));
1094
- }, children: "Discard this unsent response" })
1055
+ panel === "question-list" && /* @__PURE__ */ jsxs("section", { ref: responseRef, tabIndex: -1, className: "sm-review-form", "aria-label": "Optional story questions", children: [
1056
+ /* @__PURE__ */ jsxs("div", { className: "sm-review-form-heading", children: [
1057
+ /* @__PURE__ */ jsx("h2", { children: "Tell us a little more" }),
1058
+ /* @__PURE__ */ jsx("button", { onClick: () => setPanel("article"), children: "Close" })
1059
+ ] }),
1060
+ /* @__PURE__ */ jsx("p", { children: "Completely optional. Read the questions first, then answer online or copy them into an email. Even one answer helps." }),
1061
+ /* @__PURE__ */ jsx("ol", { className: "sm-review-question-list", children: review.questions.filter((q, i) => moreQuestions || i < 5 || q.required).map((q) => /* @__PURE__ */ jsx("li", { children: q.prompt }, q.id)) }),
1062
+ !moreQuestions && review.questions.length > 5 && /* @__PURE__ */ jsx("button", { onClick: () => setMoreQuestions(true), children: "See all questions" }),
1063
+ /* @__PURE__ */ jsx("button", { className: "sm-review-primary", onClick: () => setPanel("questions"), children: "Answer online" }),
1064
+ /* @__PURE__ */ jsx("p", { children: "Sign in to save your answers as you go. You can return whenever it suits you." }),
1065
+ /* @__PURE__ */ jsx("button", { onClick: async () => {
1066
+ try {
1067
+ await navigator.clipboard.writeText(questionText);
1068
+ setNotice("Questions copied. Paste them into an email and answer as many as you like.");
1069
+ } catch {
1070
+ setCopiedQuestions(true);
1071
+ }
1072
+ }, children: "Copy all questions" }),
1073
+ copiedQuestions && /* @__PURE__ */ jsxs("label", { children: [
1074
+ "Copy these questions",
1075
+ /* @__PURE__ */ jsx("textarea", { readOnly: true, rows: 10, value: questionText, onFocus: (e) => e.currentTarget.select() })
1076
+ ] }),
1077
+ questionsEmail && /* @__PURE__ */ jsxs("p", { children: [
1078
+ /* @__PURE__ */ jsx("a", { href: questionsEmail, children: "Answer by email" }),
1079
+ " \u2014 opens your email app with the questions ready."
1080
+ ] })
1095
1081
  ] }),
1096
- /* @__PURE__ */ jsxs("fieldset", { disabled: busy, className: "sm-review-response-fields", children: [
1097
- panel === "edits" && /* @__PURE__ */ jsxs(Fragment, { children: [
1098
- /* @__PURE__ */ jsx("p", { children: "Your suggestions will be reviewed by an editor. They do not replace the newsroom\u2019s draft." }),
1099
- /* @__PURE__ */ jsxs("label", { children: [
1100
- "Headline",
1101
- /* @__PURE__ */ jsx(
1102
- "input",
1103
- {
1104
- value: document.title,
1105
- onChange: (e) => setDocument({ ...document, title: e.target.value })
1106
- }
1107
- )
1108
- ] }),
1109
- /* @__PURE__ */ jsxs("label", { children: [
1110
- "Short introduction",
1111
- /* @__PURE__ */ jsx(
1112
- "input",
1113
- {
1114
- value: document.subtitle,
1115
- onChange: (e) => setDocument({ ...document, subtitle: e.target.value })
1116
- }
1117
- )
1118
- ] }),
1119
- /* @__PURE__ */ jsxs("label", { children: [
1120
- "Suggested article text",
1121
- /* @__PURE__ */ jsx(
1122
- "textarea",
1123
- {
1124
- rows: 16,
1125
- value: document.content_markdown,
1126
- onChange: (e) => setDocument({
1127
- ...document,
1128
- content_markdown: e.target.value
1129
- })
1130
- }
1131
- )
1082
+ panel === "identity" && /* @__PURE__ */ jsxs("section", { ref: responseRef, tabIndex: -1, className: "sm-review-form", "aria-label": "Introduce yourself", children: [
1083
+ /* @__PURE__ */ jsxs("div", { className: "sm-review-form-heading", children: [
1084
+ /* @__PURE__ */ jsx("h2", { children: identityStep === "code" ? "Check your email" : requestedPanel === "media" ? photoToReplace ? "Change photo" : "Contribute a photograph" : requestedPanel === "changes" ? "Suggest a change" : requestedPanel === "edits" ? "Propose changes" : requestedPanel === "comment" ? "Leave a comment" : "Continue to help with the story" }),
1085
+ /* @__PURE__ */ jsx("button", { disabled: busy, type: "button", onClick: () => setPanel("article"), "aria-label": "Close", children: "Close" })
1086
+ ] }),
1087
+ errorMessage,
1088
+ photoToReplace && /* @__PURE__ */ jsx("div", { className: "sm-review-target-preview", style: { padding: "10px 14px", background: "#f8fafc", borderRadius: "8px", border: "1px solid #e2e8f0", margin: "10px 0 16px" }, children: /* @__PURE__ */ jsxs("div", { style: { fontSize: "13px", color: "#334155" }, children: [
1089
+ /* @__PURE__ */ jsx("strong", { children: "Replacing:" }),
1090
+ " ",
1091
+ photoToReplace.artwork_title ? `\u201C${photoToReplace.artwork_title}\u201D` : photoToReplace.caption || "Header photograph"
1092
+ ] }) }),
1093
+ /* @__PURE__ */ jsx("p", { children: "Your name accompanies your feedback. Your email stays private." }),
1094
+ identityStep === "name" && renderSignIn && /* @__PURE__ */ jsxs(Fragment, { children: [
1095
+ readerLoading ? /* @__PURE__ */ jsx("p", { role: "status", children: "Checking your sign-in\u2026" }) : reader?.email_verified ? /* @__PURE__ */ jsxs(Fragment, { children: [
1096
+ /* @__PURE__ */ jsxs("button", { className: "sm-review-primary", disabled: busy, onClick: () => void connectReader(), children: [
1097
+ "Continue as ",
1098
+ reader.full_name || reader.email
1099
+ ] }),
1100
+ /* @__PURE__ */ jsx("p", { children: "Your reader account is ready. No email code is needed." })
1101
+ ] }) : renderSignIn(signInReturn),
1102
+ !useEmailCode && /* @__PURE__ */ jsx("button", { className: "sm-review-quiet", onClick: () => setUseEmailCode(true), children: "Use an email code instead" })
1103
+ ] }),
1104
+ identityStep === "name" && useEmailCode && /* @__PURE__ */ jsxs("form", { onSubmit: async (e) => {
1105
+ e.preventDefault();
1106
+ setBusy(true);
1107
+ setError("");
1108
+ try {
1109
+ await request("/join", { name: contributorName, email: contributorEmail });
1110
+ await request("/verify", {});
1111
+ setIdentityStep("code");
1112
+ setCode("");
1113
+ } catch (e2) {
1114
+ setError(e2.message);
1115
+ } finally {
1116
+ setBusy(false);
1117
+ }
1118
+ }, children: [
1119
+ /* @__PURE__ */ jsx("label", { htmlFor: "contributor-name", children: "Your name" }),
1120
+ /* @__PURE__ */ jsx("input", { id: "contributor-name", autoComplete: "name", value: contributorName, maxLength: 150, required: true, onChange: (e) => setContributorName(e.target.value) }),
1121
+ /* @__PURE__ */ jsx("label", { htmlFor: "contributor-email", children: "Your email" }),
1122
+ /* @__PURE__ */ jsx("input", { id: "contributor-email", type: "email", autoComplete: "email", value: contributorEmail, maxLength: 320, required: true, onChange: (e) => setContributorEmail(e.target.value) }),
1123
+ /* @__PURE__ */ jsx("p", { children: "We'll email you a code to confirm it's you." }),
1124
+ /* @__PURE__ */ jsx("p", { children: "No password or account is needed for this option." }),
1125
+ /* @__PURE__ */ jsx("button", { disabled: busy, children: busy ? "Sending your code\u2026" : "Email me a code" })
1126
+ ] }),
1127
+ identityStep === "code" && /* @__PURE__ */ jsxs("form", { onSubmit: async (e) => {
1128
+ e.preventDefault();
1129
+ setBusy(true);
1130
+ setError("");
1131
+ try {
1132
+ await request("/verify", { code });
1133
+ await load();
1134
+ setCode("");
1135
+ setPanelState(requestedPanel);
1136
+ } catch (e2) {
1137
+ setError(e2.message);
1138
+ } finally {
1139
+ setBusy(false);
1140
+ }
1141
+ }, children: [
1142
+ /* @__PURE__ */ jsxs("p", { children: [
1143
+ "Enter the eight-digit code sent to ",
1144
+ /* @__PURE__ */ jsx("strong", { children: contributorEmail }),
1145
+ ". It expires in ten minutes."
1132
1146
  ] }),
1133
- /* @__PURE__ */ jsx(
1134
- "button",
1135
- {
1136
- disabled: busy,
1137
- onClick: () => void send(
1138
- {
1139
- action: "PROPOSE_EDITS",
1140
- title: document.title,
1141
- subtitle: document.subtitle,
1142
- content_markdown: document.content_markdown,
1143
- message
1144
- },
1145
- "Your proposed changes have been sent to the newsroom."
1146
- ),
1147
- children: "Send proposed changes"
1147
+ /* @__PURE__ */ jsx("label", { htmlFor: "contributor-code", children: "Email verification code" }),
1148
+ /* @__PURE__ */ jsx("input", { id: "contributor-code", inputMode: "numeric", autoComplete: "one-time-code", pattern: "[0-9]{8}", maxLength: 8, value: code, required: true, onChange: (e) => setCode(e.target.value.replace(/\s/g, "")) }),
1149
+ /* @__PURE__ */ jsx("button", { disabled: busy, children: busy ? "Verifying\u2026" : "Verify and continue" }),
1150
+ /* @__PURE__ */ jsx("button", { type: "button", disabled: busy, onClick: async () => {
1151
+ setBusy(true);
1152
+ try {
1153
+ await request("/verify", {});
1154
+ setNotice("A new code was sent. Check your inbox and spam folder.");
1155
+ } catch (e) {
1156
+ setError(e.message);
1157
+ } finally {
1158
+ setBusy(false);
1148
1159
  }
1149
- )
1160
+ }, children: "Send a new code" }),
1161
+ /* @__PURE__ */ jsx("button", { type: "button", disabled: busy, onClick: () => setIdentityStep("name"), children: "Use a different email" })
1150
1162
  ] }),
1151
- panel === "questions" && /* @__PURE__ */ jsxs(
1152
- "form",
1153
- {
1154
- onSubmit: (e) => {
1155
- e.preventDefault();
1156
- void send(
1157
- { action: "ANSWER_QUESTIONS", answers },
1158
- "Thank you for sharing. We'll take care of turning your answers into an updated draft."
1159
- );
1160
- },
1161
- children: [
1162
- /* @__PURE__ */ jsx("p", { children: review.questions.some((q) => q.required) ? "Only questions marked required must be answered. Share what you would like considered for publication." : "Completely optional. Even one answer can help us make the announcement more personal." }),
1163
- review.questions.filter((q, i) => moreQuestions || i < 5 || q.required).map((q) => /* @__PURE__ */ jsxs("label", { children: [
1164
- q.prompt,
1165
- q.required ? " (required)" : " (optional)",
1163
+ emailAlternative,
1164
+ /* @__PURE__ */ jsx("button", { disabled: busy, onClick: () => setPanel("article"), children: "Just read the story" })
1165
+ ] }),
1166
+ panel === "feedback" && /* @__PURE__ */ jsxs("section", { ref: responseRef, tabIndex: -1, className: "sm-review-form", "aria-label": "Family feedback", children: [
1167
+ /* @__PURE__ */ jsx("h2", { children: "Looks good to you?" }),
1168
+ errorMessage,
1169
+ /* @__PURE__ */ jsxs("p", { children: [
1170
+ "We'll send your feedback as ",
1171
+ review.reviewer,
1172
+ ". The newsroom will request approval from the designated family reviewer separately. This will not publish the story."
1173
+ ] }),
1174
+ /* @__PURE__ */ jsx("button", { className: "sm-review-primary", disabled: busy, onClick: () => void send({ action: "COMMENT", message: "This draft looks good to me.", target: { kind: "STORY" } }, "Thank you. Your feedback has been sent to the newsroom. The story is still private.", false), children: "Send my feedback" }),
1175
+ /* @__PURE__ */ jsx("button", { disabled: busy, onClick: () => setPanel("article"), children: "Back to the story" })
1176
+ ] }),
1177
+ !["article", "identity", "share", "feedback", "question-list"].includes(panel) && /* @__PURE__ */ jsxs(
1178
+ "section",
1179
+ {
1180
+ ref: responseRef,
1181
+ tabIndex: -1,
1182
+ className: "sm-review-form",
1183
+ "aria-label": "Your response",
1184
+ children: [
1185
+ /* @__PURE__ */ jsxs("div", { className: "sm-review-form-heading", children: [
1186
+ /* @__PURE__ */ jsx("h2", { children: panel === "approve" ? "Approve this exact draft" : panel === "edits" ? "Propose changes" : panel === "media" ? photoToReplace ? "Change photo" : "Contribute a photograph" : panel === "questions" ? "Tell us a little more" : panel === "history" ? "Selected revision history" : panel === "decline" ? "Prefer not to publish" : panel === "changes" ? "Suggest a change" : "Message the newsroom" }),
1187
+ /* @__PURE__ */ jsx("button", { disabled: busy, onClick: () => setPanel("article"), children: "Close" })
1188
+ ] }),
1189
+ errorMessage,
1190
+ panel !== "history" && /* @__PURE__ */ jsxs("div", { className: "sm-review-save-state", role: "status", "aria-live": "polite", children: [
1191
+ currentDraft?.status === "conflict" ? /* @__PURE__ */ jsxs(Fragment, { children: [
1192
+ /* @__PURE__ */ jsx("p", { children: "A response was saved in another tab or device. Your writing here is still available. Choose which version to keep." }),
1193
+ /* @__PURE__ */ jsx("button", { disabled: busy, onClick: () => void drafts.resolve(panel, true).catch((e) => setError(e.message)), children: "Keep what I wrote here" }),
1194
+ /* @__PURE__ */ jsx("button", { disabled: busy, onClick: () => void drafts.resolve(panel, false).catch((e) => setError(e.message)), children: "Use the saved response" })
1195
+ ] }) : currentDraft?.status === "offline" ? /* @__PURE__ */ jsxs(Fragment, { children: [
1196
+ /* @__PURE__ */ jsx("p", { children: currentDraft.local ? "Saved on this device. Waiting for a connection to save online." : "Not saved yet. Keep this page open and try again." }),
1197
+ /* @__PURE__ */ jsx("button", { disabled: busy, onClick: () => void drafts.flush(panel).catch((e) => setError(e.message)), children: "Try saving again" })
1198
+ ] }) : /* @__PURE__ */ jsx("p", { children: currentDraft?.status === "saving" ? "Saving your writing\u2026" : hasDraft(currentDraft?.data) ? "Saved \u2014 not sent yet. You can safely come back later." : "Your writing saves automatically. Use the Send button below when you're ready to share it with the newsroom." }),
1199
+ hasDraft(currentDraft?.data) && /* @__PURE__ */ jsx("button", { className: "sm-review-quiet", disabled: busy, onClick: () => {
1200
+ if (window.confirm("Discard this unsent response? Responses already sent will be kept.")) void drafts.discard(panel).catch((e) => setError(e.message));
1201
+ }, children: "Discard this unsent response" })
1202
+ ] }),
1203
+ /* @__PURE__ */ jsxs("fieldset", { disabled: busy, className: "sm-review-response-fields", children: [
1204
+ panel === "edits" && /* @__PURE__ */ jsxs(Fragment, { children: [
1205
+ /* @__PURE__ */ jsx("p", { children: "Your suggestions will be reviewed by an editor. They do not replace the newsroom\u2019s draft." }),
1206
+ /* @__PURE__ */ jsxs("label", { children: [
1207
+ "Headline",
1208
+ /* @__PURE__ */ jsx(
1209
+ "input",
1210
+ {
1211
+ value: document.title,
1212
+ onChange: (e) => setDocument({ ...document, title: e.target.value })
1213
+ }
1214
+ )
1215
+ ] }),
1216
+ /* @__PURE__ */ jsxs("label", { children: [
1217
+ "Short introduction",
1218
+ /* @__PURE__ */ jsx(
1219
+ "input",
1220
+ {
1221
+ value: document.subtitle,
1222
+ onChange: (e) => setDocument({ ...document, subtitle: e.target.value })
1223
+ }
1224
+ )
1225
+ ] }),
1226
+ /* @__PURE__ */ jsxs("label", { children: [
1227
+ "Suggested article text",
1228
+ /* @__PURE__ */ jsx(
1229
+ "textarea",
1230
+ {
1231
+ rows: 16,
1232
+ value: document.content_markdown,
1233
+ onChange: (e) => setDocument({
1234
+ ...document,
1235
+ content_markdown: e.target.value
1236
+ })
1237
+ }
1238
+ )
1239
+ ] }),
1166
1240
  /* @__PURE__ */ jsx(
1167
- "textarea",
1241
+ "button",
1168
1242
  {
1169
- required: q.required,
1170
- rows: 3,
1171
- value: answers[q.id] || "",
1172
- onChange: (e) => setAnswers({ ...answers, [q.id]: e.target.value })
1243
+ disabled: busy,
1244
+ onClick: () => void send(
1245
+ {
1246
+ action: "PROPOSE_EDITS",
1247
+ title: document.title,
1248
+ subtitle: document.subtitle,
1249
+ content_markdown: document.content_markdown,
1250
+ message
1251
+ },
1252
+ "Your proposed changes have been sent to the newsroom."
1253
+ ),
1254
+ children: "Send proposed changes"
1173
1255
  }
1174
1256
  )
1175
- ] }, q.id)),
1176
- /* @__PURE__ */ jsx(
1177
- "button",
1178
- {
1179
- className: "sm-review-primary",
1180
- disabled: busy || !Object.values(answers).some((a) => a.trim()),
1181
- children: "Send These Answers"
1182
- }
1183
- ),
1184
- !moreQuestions && review.questions.length > 5 && /* @__PURE__ */ jsx(
1185
- "button",
1257
+ ] }),
1258
+ panel === "questions" && /* @__PURE__ */ jsxs(
1259
+ "form",
1186
1260
  {
1187
- type: "button",
1188
- onClick: () => setMoreQuestions(true),
1189
- children: "Answer More Questions"
1261
+ onSubmit: (e) => {
1262
+ e.preventDefault();
1263
+ void send(
1264
+ { action: "ANSWER_QUESTIONS", answers },
1265
+ "Thank you for sharing. We'll take care of turning your answers into an updated draft."
1266
+ );
1267
+ },
1268
+ children: [
1269
+ /* @__PURE__ */ jsx("p", { children: review.questions.some((q) => q.required) ? "Only questions marked required must be answered. Share what you would like considered for publication." : "Completely optional. Even one answer can help us make the announcement more personal." }),
1270
+ review.questions.filter((q, i) => moreQuestions || i < 5 || q.required).map((q) => /* @__PURE__ */ jsxs("label", { children: [
1271
+ q.prompt,
1272
+ q.required ? " (required)" : " (optional)",
1273
+ /* @__PURE__ */ jsx(
1274
+ "textarea",
1275
+ {
1276
+ required: q.required,
1277
+ rows: 3,
1278
+ value: answers[q.id] || "",
1279
+ onChange: (e) => setAnswers({ ...answers, [q.id]: e.target.value })
1280
+ }
1281
+ )
1282
+ ] }, q.id)),
1283
+ /* @__PURE__ */ jsx(
1284
+ "button",
1285
+ {
1286
+ className: "sm-review-primary",
1287
+ disabled: busy || !Object.values(answers).some((a) => a.trim()),
1288
+ children: "Send These Answers"
1289
+ }
1290
+ ),
1291
+ !moreQuestions && review.questions.length > 5 && /* @__PURE__ */ jsx(
1292
+ "button",
1293
+ {
1294
+ type: "button",
1295
+ onClick: () => setMoreQuestions(true),
1296
+ children: "Answer More Questions"
1297
+ }
1298
+ ),
1299
+ questionsEmail && /* @__PURE__ */ jsxs("p", { children: [
1300
+ /* @__PURE__ */ jsx("a", { href: questionsEmail, children: "Email these answers instead" }),
1301
+ ". Send them from your email app; your online draft stays saved."
1302
+ ] })
1303
+ ]
1190
1304
  }
1191
1305
  ),
1192
- questionsEmail && /* @__PURE__ */ jsxs("p", { children: [
1193
- /* @__PURE__ */ jsx("a", { href: questionsEmail, children: "Email these answers instead" }),
1194
- ". Send them from your email app; your online draft stays saved."
1195
- ] })
1196
- ]
1197
- }
1198
- ),
1199
- panel === "metadata" && /* @__PURE__ */ jsxs(Fragment, { children: [
1200
- /* @__PURE__ */ jsx("p", { children: "Caption and credit changes are proposals for the newsroom; historical captions remain intact." }),
1201
- ["caption", "credit", "alt"].map((key) => /* @__PURE__ */ jsxs("label", { children: [
1202
- key,
1203
- /* @__PURE__ */ jsx(
1204
- "input",
1205
- {
1206
- value: mediaInfo[key],
1207
- onChange: (e) => setMediaInfo({ ...mediaInfo, [key]: e.target.value })
1208
- }
1209
- )
1210
- ] }, key)),
1211
- /* @__PURE__ */ jsx(
1212
- "button",
1213
- {
1214
- disabled: busy,
1215
- onClick: () => void send(
1216
- {
1217
- action: "EDIT_MEDIA_METADATA",
1218
- contribution_id: drafts.entries.metadata?.data.editingMedia || editingMedia,
1219
- caption: mediaInfo.caption,
1220
- credit: mediaInfo.credit,
1221
- alt: mediaInfo.alt
1222
- },
1223
- "Your proposed media descriptions have been sent to the newsroom."
1224
- ),
1225
- children: "Propose caption / credit changes"
1226
- }
1227
- )
1228
- ] }),
1229
- panel === "media" && /* @__PURE__ */ jsxs(
1230
- "form",
1231
- {
1232
- onSubmit: (e) => {
1233
- e.preventDefault();
1234
- void upload();
1235
- },
1236
- children: [
1237
- photoToReplace && /* @__PURE__ */ jsx("p", { children: "Choose the photograph you'd like us to use instead. We'll review it and prepare an updated preview; the current photo stays in place until then." }),
1238
- /* @__PURE__ */ jsx("p", { children: "JPEG, PNG or WebP photos up to 12 MB; MP4 or WebM videos up to 25 MB. Media remain private until selected and approved by the newsroom." }),
1239
- /* @__PURE__ */ jsxs("div", { className: "sm-review-photo-picker", children: [
1240
- /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx("strong", { children: "1. Choose a photo" }) }),
1241
- /* @__PURE__ */ jsx("button", { type: "button", onClick: () => galleryRef.current?.click(), children: "Choose from my photos" }),
1242
- /* @__PURE__ */ jsx("button", { type: "button", onClick: () => cameraRef.current?.click(), children: "Take a photo" }),
1243
- /* @__PURE__ */ jsx("input", { ref: cameraRef, "aria-label": "Take a photo", type: "file", accept: "image/jpeg,image/png,image/webp", capture: "environment", hidden: true, onChange: (e) => choosePhoto(e.target.files?.[0] || null) }),
1244
- /* @__PURE__ */ jsxs("label", { className: "sm-review-native-file", children: [
1245
- "Photograph or video",
1306
+ panel === "metadata" && /* @__PURE__ */ jsxs(Fragment, { children: [
1307
+ /* @__PURE__ */ jsx("p", { children: "Caption and credit changes are proposals for the newsroom; historical captions remain intact." }),
1308
+ ["caption", "credit", "alt"].map((key) => /* @__PURE__ */ jsxs("label", { children: [
1309
+ key,
1246
1310
  /* @__PURE__ */ jsx(
1247
1311
  "input",
1248
1312
  {
1249
- ref: galleryRef,
1250
- type: "file",
1251
- accept: "image/jpeg,image/png,image/webp,video/mp4,video/webm",
1252
- onChange: (e) => choosePhoto(e.target.files?.[0] || null)
1313
+ value: mediaInfo[key],
1314
+ onChange: (e) => setMediaInfo({ ...mediaInfo, [key]: e.target.value })
1253
1315
  }
1254
1316
  )
1255
- ] })
1256
- ] }),
1257
- file && /* @__PURE__ */ jsxs("div", { className: "sm-review-selected-photo", children: [
1258
- pendingPhoto.preview && /* @__PURE__ */ jsx("img", { src: pendingPhoto.preview, alt: "Your selected photograph" }),
1259
- /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx("strong", { children: file.name }) }),
1260
- /* @__PURE__ */ jsx("p", { role: "status", children: pendingPhoto.saved ? "Photo kept on this device \u2014 not uploaded yet." : "Photo selected \u2014 not uploaded yet. Keep this page open." }),
1261
- /* @__PURE__ */ jsx("button", { type: "button", onClick: () => {
1262
- void pendingPhoto.select(null);
1263
- setRights(false);
1264
- }, children: "Remove selected photo" })
1265
- ] }),
1266
- /* @__PURE__ */ jsx("p", { className: "sm-review-help", children: "Choose a photograph and confirm permission below. All descriptions are optional; we can help with the caption." }),
1267
- Object.entries({
1268
- caption: "Caption (optional)",
1269
- credit: "Photographer / credit (optional)"
1270
- }).map(([key, label]) => /* @__PURE__ */ jsxs("label", { children: [
1271
- label,
1317
+ ] }, key)),
1272
1318
  /* @__PURE__ */ jsx(
1273
- "input",
1319
+ "button",
1274
1320
  {
1275
- value: mediaInfo[key],
1276
- onChange: (e) => setMediaInfo({ ...mediaInfo, [key]: e.target.value })
1321
+ disabled: busy,
1322
+ onClick: () => void send(
1323
+ {
1324
+ action: "EDIT_MEDIA_METADATA",
1325
+ contribution_id: drafts.entries.metadata?.data.editingMedia || editingMedia,
1326
+ caption: mediaInfo.caption,
1327
+ credit: mediaInfo.credit,
1328
+ alt: mediaInfo.alt
1329
+ },
1330
+ "Your proposed media descriptions have been sent to the newsroom."
1331
+ ),
1332
+ children: "Propose caption / credit changes"
1277
1333
  }
1278
1334
  )
1279
- ] }, key)),
1280
- /* @__PURE__ */ jsxs("details", { className: "sm-review-media-details", children: [
1281
- /* @__PURE__ */ jsx("summary", { children: "Add more photograph details (optional)" }),
1282
- Object.entries({
1283
- who_is_pictured: "Who is pictured?",
1284
- creator: "Photographer / creator",
1285
- when_taken: "When was it taken?",
1286
- where_taken: "Where was it taken?",
1287
- alt: "Describe the photograph for readers who cannot see it"
1288
- }).map(([key, label]) => /* @__PURE__ */ jsxs("label", { children: [
1289
- label,
1335
+ ] }),
1336
+ panel === "media" && /* @__PURE__ */ jsxs(
1337
+ "form",
1338
+ {
1339
+ onSubmit: (e) => {
1340
+ e.preventDefault();
1341
+ void upload();
1342
+ },
1343
+ children: [
1344
+ photoToReplace && /* @__PURE__ */ jsx("div", { className: "sm-review-target-preview", children: /* @__PURE__ */ jsxs("div", { className: "sm-review-target-preview-title", children: [
1345
+ /* @__PURE__ */ jsx("strong", { children: "Replacing:" }),
1346
+ " ",
1347
+ photoToReplace.artwork_title ? `\u201C${photoToReplace.artwork_title}\u201D` : photoToReplace.caption || "Header photograph"
1348
+ ] }) }),
1349
+ photoToReplace && /* @__PURE__ */ jsx("p", { children: "Choose the photograph you'd like us to use instead. We'll review it and prepare an updated preview; the current photo stays in place until then." }),
1350
+ /* @__PURE__ */ jsx("p", { children: "JPEG, PNG or WebP photos up to 12 MB; MP4 or WebM videos up to 25 MB. Media remain private until selected and approved by the newsroom." }),
1351
+ /* @__PURE__ */ jsxs("div", { className: "sm-review-photo-picker", children: [
1352
+ /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx("strong", { children: "1. Choose a photo" }) }),
1353
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => galleryRef.current?.click(), children: "Choose from my photos" }),
1354
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => cameraRef.current?.click(), children: "Take a photo" }),
1355
+ /* @__PURE__ */ jsx("input", { ref: cameraRef, "aria-label": "Take a photo", type: "file", accept: "image/jpeg,image/png,image/webp", capture: "environment", hidden: true, onChange: (e) => choosePhoto(e.target.files?.[0] || null) }),
1356
+ /* @__PURE__ */ jsxs("label", { className: "sm-review-native-file", children: [
1357
+ "Photograph or video",
1358
+ /* @__PURE__ */ jsx(
1359
+ "input",
1360
+ {
1361
+ ref: galleryRef,
1362
+ type: "file",
1363
+ accept: "image/jpeg,image/png,image/webp,video/mp4,video/webm",
1364
+ onChange: (e) => choosePhoto(e.target.files?.[0] || null)
1365
+ }
1366
+ )
1367
+ ] })
1368
+ ] }),
1369
+ file && /* @__PURE__ */ jsxs("div", { className: "sm-review-selected-photo", children: [
1370
+ pendingPhoto.preview && /* @__PURE__ */ jsx("img", { src: pendingPhoto.preview, alt: "Your selected photograph" }),
1371
+ /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx("strong", { children: file.name }) }),
1372
+ /* @__PURE__ */ jsx("p", { role: "status", children: pendingPhoto.saved ? "Photo kept on this device \u2014 not uploaded yet." : "Photo selected \u2014 not uploaded yet. Keep this page open." }),
1373
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => {
1374
+ void pendingPhoto.select(null);
1375
+ setRights(false);
1376
+ }, children: "Remove selected photo" })
1377
+ ] }),
1378
+ /* @__PURE__ */ jsx("p", { className: "sm-review-help", children: "Choose a photograph and confirm permission below. All descriptions are optional; we can help with the caption." }),
1379
+ Object.entries({
1380
+ caption: "Caption (optional)",
1381
+ credit: "Photographer / credit (optional)"
1382
+ }).map(([key, label]) => /* @__PURE__ */ jsxs("label", { children: [
1383
+ label,
1384
+ /* @__PURE__ */ jsx(
1385
+ "input",
1386
+ {
1387
+ value: mediaInfo[key],
1388
+ onChange: (e) => setMediaInfo({ ...mediaInfo, [key]: e.target.value })
1389
+ }
1390
+ )
1391
+ ] }, key)),
1392
+ /* @__PURE__ */ jsxs("details", { className: "sm-review-media-details", children: [
1393
+ /* @__PURE__ */ jsx("summary", { children: "Add more photograph details (optional)" }),
1394
+ Object.entries({
1395
+ who_is_pictured: "Who is pictured?",
1396
+ creator: "Photographer / creator",
1397
+ when_taken: "When was it taken?",
1398
+ where_taken: "Where was it taken?",
1399
+ alt: "Describe the photograph for readers who cannot see it"
1400
+ }).map(([key, label]) => /* @__PURE__ */ jsxs("label", { children: [
1401
+ label,
1402
+ /* @__PURE__ */ jsx(
1403
+ "input",
1404
+ {
1405
+ value: mediaInfo[key],
1406
+ onChange: (e) => setMediaInfo({
1407
+ ...mediaInfo,
1408
+ [key]: e.target.value
1409
+ })
1410
+ }
1411
+ )
1412
+ ] }, key))
1413
+ ] }),
1414
+ /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx("strong", { children: "2. Confirm permission" }) }),
1415
+ /* @__PURE__ */ jsxs("label", { className: "sm-review-check", children: [
1416
+ /* @__PURE__ */ jsx(
1417
+ "input",
1418
+ {
1419
+ type: "checkbox",
1420
+ required: true,
1421
+ checked: rights,
1422
+ onChange: (e) => setRights(e.target.checked)
1423
+ }
1424
+ ),
1425
+ "I confirm that I have the right or permission to provide this media to ",
1426
+ review.publication.name,
1427
+ " for editorial publication."
1428
+ ] }),
1429
+ /* @__PURE__ */ jsxs("div", { className: `sm-review-send-bar${rights && file ? " is-ready" : ""}`, children: [
1430
+ /* @__PURE__ */ jsx("p", { children: busy ? "Uploading your photo. Please keep this page open\u2026" : !file ? "Choose a photo above to continue." : !rights ? "Tick the permission box above, then upload your photo." : "Ready. Tap Upload privately to send this photo to the newsroom." }),
1431
+ busy && /* @__PURE__ */ jsx("progress", { "aria-label": "Uploading photo" }),
1432
+ /* @__PURE__ */ jsx("button", { className: "sm-review-primary", disabled: busy || !rights || !file, children: busy ? "Uploading\u2026" : "Upload privately" })
1433
+ ] })
1434
+ ]
1435
+ }
1436
+ ),
1437
+ ["approve", "changes", "comment", "decline"].includes(panel) && /* @__PURE__ */ jsxs(Fragment, { children: [
1438
+ panel === "approve" && /* @__PURE__ */ jsxs("p", { children: [
1439
+ "You are approving version ",
1440
+ review.revision.number,
1441
+ ". Changes to the article require a new approval.",
1442
+ review.review_policy === "SUBJECT_APPROVAL_AUTO_PUBLISH" ? " The editor has configured this version to publish or schedule after the required approvals are received." : " The newsroom will complete its editorial workflow before publication."
1443
+ ] }),
1444
+ panel === "decline" && /* @__PURE__ */ jsx("p", { children: ["FACT_CHECK", "CONTRIBUTOR_REVIEW"].includes(
1445
+ review.review_policy
1446
+ ) ? "You can decline this review. The newsroom may still publish its reporting." : "Declining participation prevents publication of this voluntary announcement." }),
1447
+ panel === "comment" && /* @__PURE__ */ jsxs("label", { children: [
1448
+ "Comment on",
1449
+ /* @__PURE__ */ jsxs(
1450
+ "select",
1451
+ {
1452
+ value: currentDraft?.data.commentTarget || commentTarget,
1453
+ onChange: (e) => {
1454
+ setCommentTarget(e.target.value);
1455
+ if (message) drafts.set("comment", { ...currentDraft?.data, commentTarget: e.target.value });
1456
+ },
1457
+ children: [
1458
+ /* @__PURE__ */ jsx("option", { value: "STORY", children: "Entire story" }),
1459
+ review.revision.document.content_markdown.split(/\n\s*\n/).slice(0, 200).map((_, i) => /* @__PURE__ */ jsxs("option", { value: `PARAGRAPH:${i + 1}`, children: [
1460
+ "Paragraph ",
1461
+ i + 1
1462
+ ] }, i)),
1463
+ review.revision.document.media.map((m, i) => /* @__PURE__ */ jsxs(
1464
+ "option",
1465
+ {
1466
+ value: `MEDIA:${m.contribution_id}`,
1467
+ children: [
1468
+ "Photo / caption ",
1469
+ i + 1
1470
+ ]
1471
+ },
1472
+ m.contribution_id
1473
+ )),
1474
+ review.questions.map((q) => /* @__PURE__ */ jsx("option", { value: `QUESTION:${q.id}`, children: q.prompt }, q.id))
1475
+ ]
1476
+ }
1477
+ )
1478
+ ] }),
1479
+ panel === "changes" && /* @__PURE__ */ jsxs(Fragment, { children: [
1480
+ /* @__PURE__ */ jsxs("label", { children: [
1481
+ "What would you like to change?",
1482
+ /* @__PURE__ */ jsx(
1483
+ "select",
1484
+ {
1485
+ value: correctionKind,
1486
+ onChange: (e) => setCorrectionKind(e.target.value),
1487
+ children: [
1488
+ "Correct a detail",
1489
+ "Correct a name",
1490
+ "Correct a professional / education detail",
1491
+ "Remove something",
1492
+ "Rewrite something",
1493
+ "Add something",
1494
+ "General comment"
1495
+ ].map((kind) => /* @__PURE__ */ jsx("option", { children: kind }, kind))
1496
+ }
1497
+ )
1498
+ ] }),
1499
+ /* @__PURE__ */ jsx("p", { children: "A sentence or two is enough. We'll take care of the wording." }),
1500
+ can("PROPOSE_EDITS") && /* @__PURE__ */ jsx("button", { onClick: () => setPanel("edits"), children: "Or edit the wording directly" })
1501
+ ] }),
1502
+ /* @__PURE__ */ jsxs("label", { children: [
1503
+ panel === "changes" ? "What should change?" : "Message (optional)",
1290
1504
  /* @__PURE__ */ jsx(
1291
- "input",
1505
+ "textarea",
1292
1506
  {
1293
- value: mediaInfo[key],
1294
- onChange: (e) => setMediaInfo({
1295
- ...mediaInfo,
1296
- [key]: e.target.value
1297
- })
1507
+ rows: 5,
1508
+ value: message,
1509
+ onChange: (e) => setMessage(e.target.value)
1298
1510
  }
1299
1511
  )
1300
- ] }, key))
1301
- ] }),
1302
- /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx("strong", { children: "2. Confirm permission" }) }),
1303
- /* @__PURE__ */ jsxs("label", { className: "sm-review-check", children: [
1512
+ ] }),
1304
1513
  /* @__PURE__ */ jsx(
1305
- "input",
1514
+ "button",
1306
1515
  {
1307
- type: "checkbox",
1308
- required: true,
1309
- checked: rights,
1310
- onChange: (e) => setRights(e.target.checked)
1516
+ disabled: busy || panel === "changes" && !message.trim() || panel === "comment" && !message.trim(),
1517
+ onClick: () => void send(
1518
+ {
1519
+ action: panel === "approve" ? "APPROVE" : panel === "changes" ? "REQUEST_CHANGES" : panel === "decline" ? "DECLINE" : "COMMENT",
1520
+ message: panel === "changes" ? `${correctionKind}: ${message}` : message || null,
1521
+ ...panel === "comment" ? {
1522
+ target: {
1523
+ kind: (currentDraft?.data.commentTarget || commentTarget).split(":")[0],
1524
+ reference: (currentDraft?.data.commentTarget || commentTarget).split(":")[1] || null
1525
+ }
1526
+ } : {}
1527
+ },
1528
+ panel === "approve" ? celebration && finalEditor ? approvalThanks : "Your approval has been recorded for this version." : panel === "decline" ? "Thank you for letting us know. The newsroom has received your decision." : "Your message has been sent to the newsroom."
1529
+ ),
1530
+ children: panel === "approve" ? `Approve version ${review.revision.number}` : panel === "changes" ? "Send change request" : panel === "decline" ? "Prefer not to publish" : "Send comment"
1311
1531
  }
1312
- ),
1313
- "I confirm that I have the right or permission to provide this media to ",
1314
- review.publication.name,
1315
- " for editorial publication."
1532
+ )
1316
1533
  ] }),
1317
- /* @__PURE__ */ jsxs("div", { className: `sm-review-send-bar${rights && file ? " is-ready" : ""}`, children: [
1318
- /* @__PURE__ */ jsx("p", { children: busy ? "Uploading your photo. Please keep this page open\u2026" : !file ? "Choose a photo above to continue." : !rights ? "Tick the permission box above, then upload your photo." : "Ready. Tap Upload privately to send this photo to the newsroom." }),
1319
- busy && /* @__PURE__ */ jsx("progress", { "aria-label": "Uploading photo" }),
1320
- /* @__PURE__ */ jsx("button", { className: "sm-review-primary", disabled: busy || !rights || !file, children: busy ? "Uploading\u2026" : "Upload privately" })
1321
- ] })
1322
- ]
1323
- }
1324
- ),
1325
- ["approve", "changes", "comment", "decline"].includes(panel) && /* @__PURE__ */ jsxs(Fragment, { children: [
1326
- panel === "approve" && /* @__PURE__ */ jsxs("p", { children: [
1327
- "You are approving version ",
1328
- review.revision.number,
1329
- ". Changes to the article require a new approval.",
1330
- review.review_policy === "SUBJECT_APPROVAL_AUTO_PUBLISH" ? " The editor has configured this version to publish or schedule after the required approvals are received." : " The newsroom will complete its editorial workflow before publication."
1331
- ] }),
1332
- panel === "decline" && /* @__PURE__ */ jsx("p", { children: ["FACT_CHECK", "CONTRIBUTOR_REVIEW"].includes(
1333
- review.review_policy
1334
- ) ? "You can decline this review. The newsroom may still publish its reporting." : "Declining participation prevents publication of this voluntary announcement." }),
1335
- panel === "comment" && /* @__PURE__ */ jsxs("label", { children: [
1336
- "Comment on",
1337
- /* @__PURE__ */ jsxs(
1338
- "select",
1339
- {
1340
- value: currentDraft?.data.commentTarget || commentTarget,
1341
- onChange: (e) => {
1342
- setCommentTarget(e.target.value);
1343
- if (message) drafts.set("comment", { ...currentDraft?.data, commentTarget: e.target.value });
1344
- },
1345
- children: [
1346
- /* @__PURE__ */ jsx("option", { value: "STORY", children: "Entire story" }),
1347
- review.revision.document.content_markdown.split(/\n\s*\n/).slice(0, 200).map((_, i) => /* @__PURE__ */ jsxs("option", { value: `PARAGRAPH:${i + 1}`, children: [
1348
- "Paragraph ",
1349
- i + 1
1350
- ] }, i)),
1351
- review.revision.document.media.map((m, i) => /* @__PURE__ */ jsxs(
1352
- "option",
1534
+ panel === "history" && /* @__PURE__ */ jsxs(Fragment, { children: [
1535
+ /* @__PURE__ */ jsx("p", { children: "Only revisions selected by the editor are visible here." }),
1536
+ /* @__PURE__ */ jsxs("label", { children: [
1537
+ "Compare from",
1538
+ /* @__PURE__ */ jsxs(
1539
+ "select",
1353
1540
  {
1354
- value: `MEDIA:${m.contribution_id}`,
1541
+ value: compare,
1542
+ onChange: (e) => setCompare(e.target.value),
1355
1543
  children: [
1356
- "Photo / caption ",
1357
- i + 1
1544
+ /* @__PURE__ */ jsx("option", { value: "", children: "Choose a shared revision" }),
1545
+ revisions.map((r) => /* @__PURE__ */ jsxs("option", { value: r.id, children: [
1546
+ "Version ",
1547
+ r.number
1548
+ ] }, r.id))
1358
1549
  ]
1359
- },
1360
- m.contribution_id
1361
- )),
1362
- review.questions.map((q) => /* @__PURE__ */ jsx("option", { value: `QUESTION:${q.id}`, children: q.prompt }, q.id))
1363
- ]
1364
- }
1365
- )
1366
- ] }),
1367
- panel === "changes" && /* @__PURE__ */ jsxs(Fragment, { children: [
1368
- /* @__PURE__ */ jsxs("label", { children: [
1369
- "What would you like to change?",
1370
- /* @__PURE__ */ jsx(
1371
- "select",
1372
- {
1373
- value: correctionKind,
1374
- onChange: (e) => setCorrectionKind(e.target.value),
1375
- children: [
1376
- "Correct a detail",
1377
- "Correct a name",
1378
- "Correct a professional / education detail",
1379
- "Remove something",
1380
- "Rewrite something",
1381
- "Add something",
1382
- "General comment"
1383
- ].map((kind) => /* @__PURE__ */ jsx("option", { children: kind }, kind))
1384
- }
1385
- )
1386
- ] }),
1387
- /* @__PURE__ */ jsx("p", { children: "A sentence or two is enough. We'll take care of the wording." }),
1388
- can("PROPOSE_EDITS") && /* @__PURE__ */ jsx("button", { onClick: () => setPanel("edits"), children: "Or edit the wording directly" })
1389
- ] }),
1390
- /* @__PURE__ */ jsxs("label", { children: [
1391
- panel === "changes" ? "What should change?" : "Message (optional)",
1392
- /* @__PURE__ */ jsx(
1393
- "textarea",
1394
- {
1395
- rows: 5,
1396
- value: message,
1397
- onChange: (e) => setMessage(e.target.value)
1398
- }
1399
- )
1400
- ] }),
1401
- /* @__PURE__ */ jsx(
1402
- "button",
1403
- {
1404
- disabled: busy || panel === "changes" && !message.trim() || panel === "comment" && !message.trim(),
1405
- onClick: () => void send(
1406
- {
1407
- action: panel === "approve" ? "APPROVE" : panel === "changes" ? "REQUEST_CHANGES" : panel === "decline" ? "DECLINE" : "COMMENT",
1408
- message: panel === "changes" ? `${correctionKind}: ${message}` : message || null,
1409
- ...panel === "comment" ? {
1410
- target: {
1411
- kind: (currentDraft?.data.commentTarget || commentTarget).split(":")[0],
1412
- reference: (currentDraft?.data.commentTarget || commentTarget).split(":")[1] || null
1413
1550
  }
1414
- } : {}
1415
- },
1416
- panel === "approve" ? celebration && finalEditor ? approvalThanks : "Your approval has been recorded for this version." : panel === "decline" ? "Thank you for letting us know. The newsroom has received your decision." : "Your message has been sent to the newsroom."
1417
- ),
1418
- children: panel === "approve" ? `Approve version ${review.revision.number}` : panel === "changes" ? "Send change request" : panel === "decline" ? "Prefer not to publish" : "Send comment"
1419
- }
1420
- )
1421
- ] }),
1422
- panel === "history" && /* @__PURE__ */ jsxs(Fragment, { children: [
1423
- /* @__PURE__ */ jsx("p", { children: "Only revisions selected by the editor are visible here." }),
1424
- /* @__PURE__ */ jsxs("label", { children: [
1425
- "Compare from",
1426
- /* @__PURE__ */ jsxs(
1427
- "select",
1428
- {
1429
- value: compare,
1430
- onChange: (e) => setCompare(e.target.value),
1431
- children: [
1432
- /* @__PURE__ */ jsx("option", { value: "", children: "Choose a shared revision" }),
1433
- revisions.map((r) => /* @__PURE__ */ jsxs("option", { value: r.id, children: [
1434
- "Version ",
1435
- r.number
1436
- ] }, r.id))
1437
- ]
1438
- }
1439
- )
1440
- ] }),
1441
- /* @__PURE__ */ jsxs("label", { children: [
1442
- "Compare to",
1443
- /* @__PURE__ */ jsx(
1444
- "select",
1445
- {
1446
- value: compareTo || review.revision.id,
1447
- onChange: (e) => setCompareTo(e.target.value),
1448
- children: revisions.map((r) => /* @__PURE__ */ jsxs("option", { value: r.id, children: [
1449
- "Version ",
1450
- r.number
1451
- ] }, r.id))
1452
- }
1453
- )
1454
- ] }),
1455
- selected && selectedAfter && /* @__PURE__ */ jsx(ReviewDiff, { before: selected, after: selectedAfter }),
1456
- /* @__PURE__ */ jsxs("details", { children: [
1457
- /* @__PURE__ */ jsx("summary", { children: "Read selected revision" }),
1458
- /* @__PURE__ */ jsx("h3", { children: selected?.document.title }),
1459
- /* @__PURE__ */ jsx("p", { children: selected?.document.subtitle }),
1460
- /* @__PURE__ */ jsx(EditorialNotices, { editorial: selected?.editorial }),
1461
- /* @__PURE__ */ jsx(
1462
- "div",
1463
- {
1464
- className: "sm-review-prose",
1465
- dangerouslySetInnerHTML: {
1466
- __html: selected?.content_html || ""
1467
- }
1468
- }
1469
- ),
1470
- selected?.document.media.map((m) => /* @__PURE__ */ jsx(PrivatePhoto, { media: m, base }, m.file_id))
1471
- ] })
1472
- ] })
1473
- ] })
1474
- ]
1551
+ )
1552
+ ] }),
1553
+ /* @__PURE__ */ jsxs("label", { children: [
1554
+ "Compare to",
1555
+ /* @__PURE__ */ jsx(
1556
+ "select",
1557
+ {
1558
+ value: compareTo || review.revision.id,
1559
+ onChange: (e) => setCompareTo(e.target.value),
1560
+ children: revisions.map((r) => /* @__PURE__ */ jsxs("option", { value: r.id, children: [
1561
+ "Version ",
1562
+ r.number
1563
+ ] }, r.id))
1564
+ }
1565
+ )
1566
+ ] }),
1567
+ selected && selectedAfter && /* @__PURE__ */ jsx(ReviewDiff, { before: selected, after: selectedAfter }),
1568
+ /* @__PURE__ */ jsxs("details", { children: [
1569
+ /* @__PURE__ */ jsx("summary", { children: "Read selected revision" }),
1570
+ /* @__PURE__ */ jsx("h3", { children: selected?.document.title }),
1571
+ /* @__PURE__ */ jsx("p", { children: selected?.document.subtitle }),
1572
+ /* @__PURE__ */ jsx(EditorialNotices, { editorial: selected?.editorial }),
1573
+ /* @__PURE__ */ jsx(
1574
+ "div",
1575
+ {
1576
+ className: "sm-review-prose",
1577
+ dangerouslySetInnerHTML: {
1578
+ __html: selected?.content_html || ""
1579
+ }
1580
+ }
1581
+ ),
1582
+ selected?.document.media.map((m) => /* @__PURE__ */ jsx(PrivatePhoto, { media: m, base }, m.file_id))
1583
+ ] })
1584
+ ] })
1585
+ ] })
1586
+ ]
1587
+ }
1588
+ )
1589
+ ] })
1475
1590
  }
1476
1591
  ),
1477
1592
  review.contributions.length > 0 && /* @__PURE__ */ jsxs("section", { className: "sm-review-contributions", children: [
@@ -1539,7 +1654,14 @@ function StoryReview({
1539
1654
  publicationFooter
1540
1655
  ] });
1541
1656
  }
1542
- function PrivatePhoto({ base, media, onChange, disabled }) {
1657
+ function PrivatePhoto({
1658
+ base,
1659
+ media,
1660
+ onChange,
1661
+ onSuggestCorrection,
1662
+ disabled,
1663
+ isInline
1664
+ }) {
1543
1665
  const [url, setUrl] = useState("");
1544
1666
  const [video, setVideo] = useState(false);
1545
1667
  useEffect(() => {
@@ -1562,25 +1684,69 @@ function PrivatePhoto({ base, media, onChange, disabled }) {
1562
1684
  if (object) URL.revokeObjectURL(object);
1563
1685
  };
1564
1686
  }, [base, media.file_id]);
1565
- return /* @__PURE__ */ jsxs("figure", { children: [
1566
- url ? video ? /* @__PURE__ */ jsx(
1567
- "video",
1568
- {
1569
- src: url,
1570
- controls: true,
1571
- playsInline: true,
1572
- preload: "metadata",
1573
- "aria-label": media.alt
1574
- }
1575
- ) : /* @__PURE__ */ jsx("img", { src: url, alt: media.alt, referrerPolicy: "no-referrer" }) : /* @__PURE__ */ jsx("p", { children: "Media unavailable." }),
1576
- /* @__PURE__ */ jsxs("figcaption", { className: "sm-review-photo-details", children: [
1577
- (media.caption || media.credit) && /* @__PURE__ */ jsxs("span", { children: [
1578
- media.caption,
1579
- media.credit && ` Photo: ${media.credit}`
1580
- ] }),
1581
- onChange && /* @__PURE__ */ jsx("button", { className: "sm-review-change-photo", type: "button", disabled, onClick: onChange, children: "Change photo" })
1582
- ] })
1583
- ] });
1687
+ const captionInfo = formatArtworkCaption(media);
1688
+ return /* @__PURE__ */ jsxs(
1689
+ "figure",
1690
+ {
1691
+ className: `sm-review-photo-figure ${isInline ? "sm-review-inline-figure" : "sm-review-hero-figure"}`,
1692
+ "data-media-id": media.file_id,
1693
+ children: [
1694
+ url ? video ? /* @__PURE__ */ jsx(
1695
+ "video",
1696
+ {
1697
+ src: url,
1698
+ controls: true,
1699
+ playsInline: true,
1700
+ preload: "metadata",
1701
+ "aria-label": media.alt || captionInfo.title || "Story video"
1702
+ }
1703
+ ) : /* @__PURE__ */ jsx(
1704
+ "img",
1705
+ {
1706
+ src: url,
1707
+ alt: media.alt || captionInfo.title || "Story photograph",
1708
+ referrerPolicy: "no-referrer"
1709
+ }
1710
+ ) : /* @__PURE__ */ jsx("p", { children: "Media unavailable." }),
1711
+ /* @__PURE__ */ jsxs("figcaption", { className: "sm-review-photo-details", children: [
1712
+ /* @__PURE__ */ jsxs("div", { className: "sm-review-caption-text", children: [
1713
+ captionInfo.title && /* @__PURE__ */ jsx("div", { className: "sm-review-artwork-title", children: /* @__PURE__ */ jsxs("strong", { children: [
1714
+ "\u201C",
1715
+ captionInfo.title,
1716
+ "\u201D"
1717
+ ] }) }),
1718
+ captionInfo.artist && /* @__PURE__ */ jsx("div", { className: "sm-review-artist-name", children: captionInfo.artist }),
1719
+ captionInfo.mediumAndDate && /* @__PURE__ */ jsx("div", { className: "sm-review-artwork-medium", children: captionInfo.mediumAndDate }),
1720
+ captionInfo.captionText && /* @__PURE__ */ jsx("div", { className: "sm-review-photo-caption", children: captionInfo.captionText }),
1721
+ captionInfo.credit && /* @__PURE__ */ jsx("div", { className: "sm-review-photo-credit", children: captionInfo.credit }),
1722
+ captionInfo.source && /* @__PURE__ */ jsx("div", { className: "sm-review-photo-source", children: captionInfo.source })
1723
+ ] }),
1724
+ /* @__PURE__ */ jsxs("div", { className: "sm-review-photo-controls", children: [
1725
+ onChange && /* @__PURE__ */ jsx(
1726
+ "button",
1727
+ {
1728
+ className: "sm-review-change-photo",
1729
+ type: "button",
1730
+ disabled,
1731
+ onClick: onChange,
1732
+ children: "Change photo"
1733
+ }
1734
+ ),
1735
+ onSuggestCorrection && /* @__PURE__ */ jsx(
1736
+ "button",
1737
+ {
1738
+ className: "sm-review-suggest-correction",
1739
+ type: "button",
1740
+ disabled,
1741
+ onClick: onSuggestCorrection,
1742
+ children: "Suggest correction"
1743
+ }
1744
+ )
1745
+ ] })
1746
+ ] })
1747
+ ]
1748
+ }
1749
+ );
1584
1750
  }
1585
1751
  function ReviewDiff({
1586
1752
  before,