@scalemule/news-publication 0.0.32 → 0.0.34

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.
@@ -158,7 +158,18 @@ function useReviewDrafts(session, revision, initial, request) {
158
158
  }
159
159
  };
160
160
  }
161
+ async function encodePendingPhoto(value) {
162
+ const { file, ...details } = value;
163
+ return { ...details, file: { bytes: await file.arrayBuffer(), name: file.name, type: file.type, lastModified: file.lastModified } };
164
+ }
165
+ function decodePendingPhoto(value) {
166
+ if (!value) return void 0;
167
+ if (value.file instanceof File) return value;
168
+ const file = value.file;
169
+ return { ...value, file: new File([file.bytes], file.name, { type: file.type, lastModified: file.lastModified }) };
170
+ }
161
171
  async function stored(key, value) {
172
+ const encoded = value ? await encodePendingPhoto(value) : value;
162
173
  const db = await new Promise((resolve, reject) => {
163
174
  const request = indexedDB.open("story-review-pending-media", 1);
164
175
  request.onupgradeneeded = () => request.result.createObjectStore("photos");
@@ -169,8 +180,14 @@ async function stored(key, value) {
169
180
  return await new Promise((resolve, reject) => {
170
181
  const tx = db.transaction("photos", value === void 0 ? "readonly" : "readwrite");
171
182
  const store = tx.objectStore("photos");
172
- const request = value === void 0 ? store.get(key) : value === null ? store.delete(key) : store.put(value, key);
173
- tx.oncomplete = () => resolve(value === void 0 ? request.result : void 0);
183
+ const request = value === void 0 ? store.get(key) : value === null ? store.delete(key) : store.put(encoded, key);
184
+ tx.oncomplete = () => {
185
+ try {
186
+ resolve(value === void 0 ? decodePendingPhoto(request.result) : void 0);
187
+ } catch (error) {
188
+ reject(error);
189
+ }
190
+ };
174
191
  tx.onerror = () => reject(tx.error);
175
192
  tx.onabort = () => reject(tx.error);
176
193
  });
@@ -452,7 +469,7 @@ function StoryReview({
452
469
  }
453
470
  setPanel("share");
454
471
  }
455
- async function send(action, success) {
472
+ async function send(action, success, consumeDraft = true) {
456
473
  if (!review || busy) return;
457
474
  if (action.action === "APPROVE" && file) {
458
475
  setNotice("Your photo is not uploaded yet. Upload it or remove the selection before approving.");
@@ -464,7 +481,7 @@ function StoryReview({
464
481
  setNotice("");
465
482
  try {
466
483
  const actionPanel = { ANSWER_QUESTIONS: "questions", REQUEST_CHANGES: "changes", COMMENT: "comment", PROPOSE_EDITS: "edits", EDIT_MEDIA_METADATA: "metadata", APPROVE: "approve", DECLINE: "decline" }[String(action.action)];
467
- const receipt = actionPanel ? await drafts.receipt(actionPanel) : void 0;
484
+ const receipt = consumeDraft && actionPanel ? await drafts.receipt(actionPanel) : void 0;
468
485
  const sent = await request("", { revision_id: review.revision.id, ...action, ...receipt ? { draft: receipt } : {} });
469
486
  if (sent.cleared_draft) drafts.consumed(sent.cleared_draft);
470
487
  try {
@@ -800,7 +817,7 @@ function StoryReview({
800
817
  className: `sm-review-actions${celebration ? " sm-review-concierge-actions" : ""}${panel !== "article" ? " sm-review-actions-editing" : ""}`,
801
818
  "aria-label": "Review actions",
802
819
  children: [
803
- !can("APPROVE") && can("COMMENT") && (review.identity_required || review.participant) && /* @__PURE__ */ jsxRuntime.jsx("button", { disabled: busy, onClick: () => setPanel("feedback"), children: "Looks good to me" }),
820
+ !can("APPROVE") && can("COMMENT") && (review.identity_required || review.participant) && /* @__PURE__ */ jsxRuntime.jsx("button", { className: "sm-review-primary", disabled: busy, onClick: () => setPanel("feedback"), children: "Looks Good \u2014 Send Feedback" }),
804
821
  can("APPROVE") && !review.published && /* @__PURE__ */ jsxRuntime.jsx(
805
822
  "button",
806
823
  {
@@ -921,7 +938,7 @@ function StoryReview({
921
938
  review.reviewer,
922
939
  ". The newsroom will request approval from the designated family reviewer separately. This will not publish the story."
923
940
  ] }),
924
- /* @__PURE__ */ jsxRuntime.jsx("button", { 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."), children: "Send my feedback" }),
941
+ /* @__PURE__ */ jsxRuntime.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" }),
925
942
  /* @__PURE__ */ jsxRuntime.jsx("button", { disabled: busy, onClick: () => setPanel("article"), children: "Back to the story" })
926
943
  ] }),
927
944
  !["article", "identity", "share", "feedback"].includes(panel) && /* @__PURE__ */ jsxRuntime.jsxs(
@@ -156,7 +156,18 @@ function useReviewDrafts(session, revision, initial, request) {
156
156
  }
157
157
  };
158
158
  }
159
+ async function encodePendingPhoto(value) {
160
+ const { file, ...details } = value;
161
+ return { ...details, file: { bytes: await file.arrayBuffer(), name: file.name, type: file.type, lastModified: file.lastModified } };
162
+ }
163
+ function decodePendingPhoto(value) {
164
+ if (!value) return void 0;
165
+ if (value.file instanceof File) return value;
166
+ const file = value.file;
167
+ return { ...value, file: new File([file.bytes], file.name, { type: file.type, lastModified: file.lastModified }) };
168
+ }
159
169
  async function stored(key, value) {
170
+ const encoded = value ? await encodePendingPhoto(value) : value;
160
171
  const db = await new Promise((resolve, reject) => {
161
172
  const request = indexedDB.open("story-review-pending-media", 1);
162
173
  request.onupgradeneeded = () => request.result.createObjectStore("photos");
@@ -167,8 +178,14 @@ async function stored(key, value) {
167
178
  return await new Promise((resolve, reject) => {
168
179
  const tx = db.transaction("photos", value === void 0 ? "readonly" : "readwrite");
169
180
  const store = tx.objectStore("photos");
170
- const request = value === void 0 ? store.get(key) : value === null ? store.delete(key) : store.put(value, key);
171
- tx.oncomplete = () => resolve(value === void 0 ? request.result : void 0);
181
+ const request = value === void 0 ? store.get(key) : value === null ? store.delete(key) : store.put(encoded, key);
182
+ tx.oncomplete = () => {
183
+ try {
184
+ resolve(value === void 0 ? decodePendingPhoto(request.result) : void 0);
185
+ } catch (error) {
186
+ reject(error);
187
+ }
188
+ };
172
189
  tx.onerror = () => reject(tx.error);
173
190
  tx.onabort = () => reject(tx.error);
174
191
  });
@@ -450,7 +467,7 @@ function StoryReview({
450
467
  }
451
468
  setPanel("share");
452
469
  }
453
- async function send(action, success) {
470
+ async function send(action, success, consumeDraft = true) {
454
471
  if (!review || busy) return;
455
472
  if (action.action === "APPROVE" && file) {
456
473
  setNotice("Your photo is not uploaded yet. Upload it or remove the selection before approving.");
@@ -462,7 +479,7 @@ function StoryReview({
462
479
  setNotice("");
463
480
  try {
464
481
  const actionPanel = { ANSWER_QUESTIONS: "questions", REQUEST_CHANGES: "changes", COMMENT: "comment", PROPOSE_EDITS: "edits", EDIT_MEDIA_METADATA: "metadata", APPROVE: "approve", DECLINE: "decline" }[String(action.action)];
465
- const receipt = actionPanel ? await drafts.receipt(actionPanel) : void 0;
482
+ const receipt = consumeDraft && actionPanel ? await drafts.receipt(actionPanel) : void 0;
466
483
  const sent = await request("", { revision_id: review.revision.id, ...action, ...receipt ? { draft: receipt } : {} });
467
484
  if (sent.cleared_draft) drafts.consumed(sent.cleared_draft);
468
485
  try {
@@ -798,7 +815,7 @@ function StoryReview({
798
815
  className: `sm-review-actions${celebration ? " sm-review-concierge-actions" : ""}${panel !== "article" ? " sm-review-actions-editing" : ""}`,
799
816
  "aria-label": "Review actions",
800
817
  children: [
801
- !can("APPROVE") && can("COMMENT") && (review.identity_required || review.participant) && /* @__PURE__ */ jsx("button", { disabled: busy, onClick: () => setPanel("feedback"), children: "Looks good to me" }),
818
+ !can("APPROVE") && can("COMMENT") && (review.identity_required || review.participant) && /* @__PURE__ */ jsx("button", { className: "sm-review-primary", disabled: busy, onClick: () => setPanel("feedback"), children: "Looks Good \u2014 Send Feedback" }),
802
819
  can("APPROVE") && !review.published && /* @__PURE__ */ jsx(
803
820
  "button",
804
821
  {
@@ -919,7 +936,7 @@ function StoryReview({
919
936
  review.reviewer,
920
937
  ". The newsroom will request approval from the designated family reviewer separately. This will not publish the story."
921
938
  ] }),
922
- /* @__PURE__ */ jsx("button", { 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."), children: "Send my feedback" }),
939
+ /* @__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" }),
923
940
  /* @__PURE__ */ jsx("button", { disabled: busy, onClick: () => setPanel("article"), children: "Back to the story" })
924
941
  ] }),
925
942
  !["article", "identity", "share", "feedback"].includes(panel) && /* @__PURE__ */ jsxs(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scalemule/news-publication",
3
- "version": "0.0.32",
3
+ "version": "0.0.34",
4
4
  "description": "Shared server framework for ScaleMule local news publications. Walnut Creek Times is the broadsheet default; each paper supplies its own identity and feature flags.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
package/story-review.css CHANGED
@@ -15,10 +15,15 @@
15
15
  margin: auto;
16
16
  padding-inline: 32px;
17
17
  }
18
- .sm-review-publication-frame .sm-review-banner-wrap {
19
- width: min(var(--pub-width, 1240px), calc(100% - 2 * var(--pub-gutter, 32px)));
18
+ .sm-review-frame .sm-review-banner-wrap {
19
+ width: 100%;
20
20
  max-width: none;
21
- padding-inline: 0;
21
+ padding-inline: max(16px, calc((100% - 976px) / 2));
22
+ background: #fff3c4;
23
+ border-bottom: 2px solid #d5ac30;
24
+ }
25
+ .sm-review-publication-frame .sm-review-banner-wrap {
26
+ padding-inline: max(var(--pub-gutter, 16px), calc((100% - var(--pub-width, 1240px)) / 2));
22
27
  }
23
28
  .sm-review-publication-frame {
24
29
  padding-bottom: 100px;
@@ -31,11 +36,10 @@
31
36
  max-width: none;
32
37
  }
33
38
  .sm-review-publication-frame .sm-review-private {
34
- color: var(--pub-muted, #545c55);
35
- border-color: var(--pub-rule, #d7dcd7);
39
+ color: #5b4816;
36
40
  }
37
41
  .sm-review-publication-frame .sm-review-private strong {
38
- color: var(--pub-ink, #171917);
42
+ color: #382700;
39
43
  }
40
44
  .sm-story-review {
41
45
  color-scheme: light;
@@ -51,8 +55,7 @@
51
55
  }
52
56
  .sm-review-private {
53
57
  padding: 14px 0;
54
- border-bottom: 1px solid #d7dcd7;
55
- color: var(--pub-muted, #545c55);
58
+ color: #5b4816;
56
59
  font:
57
60
  13px/1.5 var(--font-inter),
58
61
  Arial,
@@ -63,9 +66,9 @@
63
66
  flex-wrap: wrap;
64
67
  }
65
68
  .sm-review-private strong {
66
- color: var(--pub-ink, #171917);
69
+ color: #382700;
67
70
  text-transform: uppercase;
68
- font-size: 11px;
71
+ font-size: 13px;
69
72
  letter-spacing: 0.08em;
70
73
  }
71
74
  .sm-review-masthead {