@the-portland-company/shell 0.45.0 → 0.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -454,6 +454,15 @@ function BellGlyph({ size = 20, className }) {
454
454
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M13.73 21a2 2 0 0 1-3.46 0" })
455
455
  ] });
456
456
  }
457
+ function BellOffGlyph({ size = 14, className }) {
458
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", className, "aria-hidden": true, children: [
459
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M13.73 21a2 2 0 0 1-3.46 0" }),
460
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18.63 13A17.9 17.9 0 0 1 18 8" }),
461
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6.26 6.26A5.86 5.86 0 0 0 6 8c0 7-3 9-3 9h14" }),
462
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 8a6 6 0 0 0-9.33-5" }),
463
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "1", y1: "1", x2: "23", y2: "23" })
464
+ ] });
465
+ }
457
466
  function LifeBuoyGlyph({ size = 20, className }) {
458
467
  return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", className, "aria-hidden": true, children: [
459
468
  /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }),
@@ -699,6 +708,23 @@ function writeNotifState(orgId, state) {
699
708
  }
700
709
  }
701
710
  var TEN_DLC_REDIRECTED_KEY = "politogy_10dlc_redirected";
711
+ var TOASTS_MUTED_KEY = "politogy_chrome_toasts_muted";
712
+ function readToastsMuted() {
713
+ if (typeof window === "undefined") return false;
714
+ try {
715
+ return window.sessionStorage.getItem(TOASTS_MUTED_KEY) === "1";
716
+ } catch {
717
+ return false;
718
+ }
719
+ }
720
+ function writeToastsMuted(muted) {
721
+ if (typeof window === "undefined") return;
722
+ try {
723
+ if (muted) window.sessionStorage.setItem(TOASTS_MUTED_KEY, "1");
724
+ else window.sessionStorage.removeItem(TOASTS_MUTED_KEY);
725
+ } catch {
726
+ }
727
+ }
702
728
  function formatPhone(raw) {
703
729
  const digits = raw.replace(/\D/g, "");
704
730
  const ten = digits.length === 11 && digits.startsWith("1") ? digits.slice(1) : digits;
@@ -728,6 +754,7 @@ function NewReplyToast({
728
754
  reply,
729
755
  onActivate,
730
756
  onClose,
757
+ onSilence,
731
758
  ttlMs = 12e3
732
759
  }) {
733
760
  const [visible, setVisible] = react.useState(false);
@@ -774,8 +801,38 @@ function NewReplyToast({
774
801
  className: "absolute right-[18px] top-[-7px] h-0 w-0 border-x-[8px] border-x-transparent border-b-[8px] border-b-[#161616]"
775
802
  }
776
803
  ),
777
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate text-sm font-semibold", children: reply.name }),
778
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 truncate text-xs opacity-85", children: reply.preview })
804
+ /* @__PURE__ */ jsxRuntime.jsx(
805
+ "button",
806
+ {
807
+ type: "button",
808
+ "aria-label": "Dismiss notification",
809
+ onClick: (e) => {
810
+ e.stopPropagation();
811
+ setVisible(false);
812
+ onClose();
813
+ },
814
+ className: "absolute right-2 top-2 flex h-5 w-5 items-center justify-center rounded-full text-white opacity-60 transition hover:bg-white/10 hover:opacity-100",
815
+ children: /* @__PURE__ */ jsxRuntime.jsx(XGlyph, { size: 12 })
816
+ }
817
+ ),
818
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate pr-6 text-sm font-semibold", children: reply.name }),
819
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 truncate text-xs opacity-85", children: reply.preview }),
820
+ /* @__PURE__ */ jsxRuntime.jsxs(
821
+ "button",
822
+ {
823
+ type: "button",
824
+ onClick: (e) => {
825
+ e.stopPropagation();
826
+ setVisible(false);
827
+ onSilence();
828
+ },
829
+ className: "mt-2 inline-flex items-center gap-1 text-[11px] font-semibold text-white opacity-60 transition hover:opacity-100",
830
+ children: [
831
+ /* @__PURE__ */ jsxRuntime.jsx(BellOffGlyph, { size: 12 }),
832
+ "Silence notifications"
833
+ ]
834
+ }
835
+ )
779
836
  ]
780
837
  }
781
838
  );
@@ -787,6 +844,7 @@ function NotificationsAction({ orgId }) {
787
844
  const [error, setError] = react.useState(false);
788
845
  const [notif, setNotif] = react.useState(() => readNotifState(orgId));
789
846
  const [toastReply, setToastReply] = react.useState(null);
847
+ const [toastsMuted, setToastsMuted] = react.useState(() => readToastsMuted());
790
848
  const [openToken, setOpenToken] = react.useState(0);
791
849
  const [panelOpen, setPanelOpen] = react.useState(false);
792
850
  const anyPanelOpen = useAnyTopBarPanelOpen();
@@ -869,6 +927,7 @@ function NotificationsAction({ orgId }) {
869
927
  const imports = payload?.imports ?? [];
870
928
  const quarantined = payload?.quarantined ?? [];
871
929
  react.useEffect(() => {
930
+ if (toastsMuted) return;
872
931
  if (!payload || toastReply || panelOpen) return;
873
932
  const candidate = replies.filter((r) => {
874
933
  if (!r.createdAt) return false;
@@ -885,7 +944,24 @@ function NotificationsAction({ orgId }) {
885
944
  writeNotifState(orgId, next);
886
945
  return next;
887
946
  });
888
- }, [payload, replies, toastReply, panelOpen, notif.lastSeenAt, notif.toastedReplyIds, orgId]);
947
+ }, [
948
+ payload,
949
+ replies,
950
+ toastReply,
951
+ panelOpen,
952
+ toastsMuted,
953
+ notif.lastSeenAt,
954
+ notif.toastedReplyIds,
955
+ orgId
956
+ ]);
957
+ const setMuted = react.useCallback((muted) => {
958
+ writeToastsMuted(muted);
959
+ setToastsMuted(muted);
960
+ }, []);
961
+ const onToastSilence = react.useCallback(() => {
962
+ setMuted(true);
963
+ setToastReply(null);
964
+ }, [setMuted]);
889
965
  const onToastActivate = react.useCallback(() => {
890
966
  const href = toastReply?.href;
891
967
  setToastReply(null);
@@ -919,7 +995,18 @@ function NotificationsAction({ orgId }) {
919
995
  openToken,
920
996
  onOpenChange: setPanelOpen,
921
997
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-3", children: [
922
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Notifications" }),
998
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [
999
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Notifications" }),
1000
+ toastsMuted ? /* @__PURE__ */ jsxRuntime.jsx(
1001
+ "button",
1002
+ {
1003
+ type: "button",
1004
+ onClick: () => setMuted(false),
1005
+ className: "text-[11px] font-semibold text-[color:var(--color-race-red)] hover:underline",
1006
+ children: "Unsilence"
1007
+ }
1008
+ ) : null
1009
+ ] }),
923
1010
  loading ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 py-1", children: [
924
1011
  /* @__PURE__ */ jsxRuntime.jsx(ValueSkeleton, { className: "h-4 w-full" }),
925
1012
  /* @__PURE__ */ jsxRuntime.jsx(ValueSkeleton, { className: "h-4 w-3/4" })
@@ -952,10 +1039,21 @@ function NotificationsAction({ orgId }) {
952
1039
  )
953
1040
  ] }) }, a.id);
954
1041
  }),
955
- replies.map((r) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "rounded-lg px-2 py-2 hover:bg-[color:var(--panel)]", children: [
956
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-[color:var(--foreground)]", children: r.name }),
957
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate text-xs text-[color:var(--muted)]", children: r.preview })
958
- ] }, r.id))
1042
+ replies.map((r) => {
1043
+ const body = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1044
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-[color:var(--foreground)]", children: r.name }),
1045
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate text-xs text-[color:var(--muted)]", children: r.preview })
1046
+ ] });
1047
+ return /* @__PURE__ */ jsxRuntime.jsx("li", { children: r.href ? /* @__PURE__ */ jsxRuntime.jsx(
1048
+ "a",
1049
+ {
1050
+ href: r.href,
1051
+ "aria-label": `Open the conversation with ${r.name}`,
1052
+ className: "block rounded-lg px-2 py-2 transition hover:bg-[color:var(--panel)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--color-race-red)]",
1053
+ children: body
1054
+ }
1055
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg px-2 py-2", children: body }) }, r.id);
1056
+ })
959
1057
  ] }) : null,
960
1058
  imports.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
961
1059
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-1 px-2 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Imports" }),
@@ -998,7 +1096,8 @@ function NotificationsAction({ orgId }) {
998
1096
  {
999
1097
  reply: toastReply,
1000
1098
  onActivate: onToastActivate,
1001
- onClose: () => setToastReply(null)
1099
+ onClose: () => setToastReply(null),
1100
+ onSilence: onToastSilence
1002
1101
  }
1003
1102
  ) : null
1004
1103
  ] })