@hook-sdk/template 0.11.0 → 0.12.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.
package/dist/index.d.cts CHANGED
@@ -118,6 +118,16 @@ interface PushPromptTexts {
118
118
  iosInstallCta?: string;
119
119
  deniedTitle: string;
120
120
  deniedBody: string;
121
+ /**
122
+ * Audit Wave 3 — Fix #33: per-platform recovery copy shown when permission
123
+ * is denied. All four are optional; if the matching platform's copy is
124
+ * missing the recovery paragraph is omitted (back-compat for callers that
125
+ * haven't supplied them yet).
126
+ */
127
+ deniedRecoveryIos?: string;
128
+ deniedRecoveryAndroid?: string;
129
+ deniedRecoveryDesktop?: string;
130
+ deniedRecoveryInApp?: string;
121
131
  unsupportedBody: string;
122
132
  }
123
133
  interface PushPromptProps {
@@ -534,6 +544,8 @@ type PushUiState = {
534
544
  kind: 'subscribed';
535
545
  } | {
536
546
  kind: 'denied';
547
+ } | {
548
+ kind: 'dismissed';
537
549
  } | {
538
550
  kind: 'error';
539
551
  code: string;
@@ -543,6 +555,7 @@ declare function usePush(): {
543
555
  state: PushUiState;
544
556
  subscribe: () => Promise<void>;
545
557
  unsubscribe: () => Promise<void>;
558
+ dismiss: () => void;
546
559
  };
547
560
 
548
561
  declare function useReminders(): {
@@ -560,7 +573,7 @@ declare function useReminders(): {
560
573
  sendAt: string | Date;
561
574
  title: string;
562
575
  body: string;
563
- url?: string;
576
+ url: string;
564
577
  }>) => Promise<{
565
578
  accepted: number;
566
579
  rejected: number;
@@ -572,7 +585,7 @@ declare function useReminders(): {
572
585
  slot: string;
573
586
  title: string;
574
587
  body: string;
575
- url?: string;
588
+ url: string;
576
589
  }>) => Promise<void>>[0]) => Promise<void>;
577
590
  };
578
591
 
package/dist/index.d.ts CHANGED
@@ -118,6 +118,16 @@ interface PushPromptTexts {
118
118
  iosInstallCta?: string;
119
119
  deniedTitle: string;
120
120
  deniedBody: string;
121
+ /**
122
+ * Audit Wave 3 — Fix #33: per-platform recovery copy shown when permission
123
+ * is denied. All four are optional; if the matching platform's copy is
124
+ * missing the recovery paragraph is omitted (back-compat for callers that
125
+ * haven't supplied them yet).
126
+ */
127
+ deniedRecoveryIos?: string;
128
+ deniedRecoveryAndroid?: string;
129
+ deniedRecoveryDesktop?: string;
130
+ deniedRecoveryInApp?: string;
121
131
  unsupportedBody: string;
122
132
  }
123
133
  interface PushPromptProps {
@@ -534,6 +544,8 @@ type PushUiState = {
534
544
  kind: 'subscribed';
535
545
  } | {
536
546
  kind: 'denied';
547
+ } | {
548
+ kind: 'dismissed';
537
549
  } | {
538
550
  kind: 'error';
539
551
  code: string;
@@ -543,6 +555,7 @@ declare function usePush(): {
543
555
  state: PushUiState;
544
556
  subscribe: () => Promise<void>;
545
557
  unsubscribe: () => Promise<void>;
558
+ dismiss: () => void;
546
559
  };
547
560
 
548
561
  declare function useReminders(): {
@@ -560,7 +573,7 @@ declare function useReminders(): {
560
573
  sendAt: string | Date;
561
574
  title: string;
562
575
  body: string;
563
- url?: string;
576
+ url: string;
564
577
  }>) => Promise<{
565
578
  accepted: number;
566
579
  rejected: number;
@@ -572,7 +585,7 @@ declare function useReminders(): {
572
585
  slot: string;
573
586
  title: string;
574
587
  body: string;
575
- url?: string;
588
+ url: string;
576
589
  }>) => Promise<void>>[0]) => Promise<void>;
577
590
  };
578
591
 
package/dist/index.js CHANGED
@@ -2000,6 +2000,8 @@ function FallbackPaywall() {
2000
2000
  // src/hooks/usePush.ts
2001
2001
  import { useCallback as useCallback4, useEffect as useEffect6, useState as useState5 } from "react";
2002
2002
  import { useHook as useHook5 } from "@hook-sdk/sdk";
2003
+ var DISMISS_STORAGE_KEY = "push:dismissed-until";
2004
+ var DISMISS_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
2003
2005
  function detectIosNeedsInstall() {
2004
2006
  if (typeof navigator === "undefined" || typeof window === "undefined") return false;
2005
2007
  const ua = navigator.userAgent || "";
@@ -2010,6 +2012,21 @@ function detectIosNeedsInstall() {
2010
2012
  const legacyStandalone = typeof navigator.standalone === "boolean" ? navigator.standalone : false;
2011
2013
  return !(standalone || legacyStandalone);
2012
2014
  }
2015
+ function readDismissedUntil() {
2016
+ if (typeof localStorage === "undefined") return null;
2017
+ try {
2018
+ const raw = localStorage.getItem(DISMISS_STORAGE_KEY);
2019
+ if (raw === null) return null;
2020
+ const n = Number.parseInt(raw, 10);
2021
+ return Number.isFinite(n) ? n : null;
2022
+ } catch {
2023
+ return null;
2024
+ }
2025
+ }
2026
+ function isDismissedNow() {
2027
+ const until = readDismissedUntil();
2028
+ return until !== null && until > Date.now();
2029
+ }
2013
2030
  function deriveState(push) {
2014
2031
  if (!push.isAvailable()) {
2015
2032
  if (detectIosNeedsInstall()) return { kind: "ios_needs_install" };
@@ -2022,6 +2039,7 @@ function deriveState(push) {
2022
2039
  if (detectIosNeedsInstall()) return { kind: "ios_needs_install" };
2023
2040
  return { kind: "unsupported" };
2024
2041
  }
2042
+ if (isDismissedNow()) return { kind: "dismissed" };
2025
2043
  return { kind: "prompt" };
2026
2044
  }
2027
2045
  function usePush() {
@@ -2051,14 +2069,41 @@ function usePush() {
2051
2069
  throw e;
2052
2070
  }
2053
2071
  }, [push]);
2054
- return { state, subscribe, unsubscribe };
2072
+ const dismiss = useCallback4(() => {
2073
+ if (typeof localStorage !== "undefined") {
2074
+ try {
2075
+ localStorage.setItem(DISMISS_STORAGE_KEY, String(Date.now() + DISMISS_TTL_MS));
2076
+ } catch {
2077
+ }
2078
+ }
2079
+ setState({ kind: "dismissed" });
2080
+ }, []);
2081
+ return { state, subscribe, unsubscribe, dismiss };
2055
2082
  }
2056
2083
 
2057
2084
  // src/components/PushPrompt.tsx
2058
2085
  import { jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
2086
+ function platformRecoveryCopy(texts) {
2087
+ if (typeof navigator === "undefined") return null;
2088
+ const ua = navigator.userAgent || "";
2089
+ const platform = detectPlatform(ua);
2090
+ switch (platform) {
2091
+ case "ios-safari":
2092
+ case "ios-other":
2093
+ return texts.deniedRecoveryIos ?? null;
2094
+ case "android":
2095
+ return texts.deniedRecoveryAndroid ?? null;
2096
+ case "desktop":
2097
+ return texts.deniedRecoveryDesktop ?? null;
2098
+ case "in-app":
2099
+ return texts.deniedRecoveryInApp ?? null;
2100
+ default:
2101
+ return null;
2102
+ }
2103
+ }
2059
2104
  function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, className }) {
2060
2105
  const { state, subscribe } = usePush();
2061
- if (state.kind === "subscribed") return null;
2106
+ if (state.kind === "subscribed" || state.kind === "dismissed") return null;
2062
2107
  if (state.kind === "ios_needs_install") {
2063
2108
  return /* @__PURE__ */ jsxs14("div", { className, role: "region", "aria-label": texts.iosInstallTitle, children: [
2064
2109
  /* @__PURE__ */ jsx19("h3", { children: texts.iosInstallTitle }),
@@ -2067,9 +2112,11 @@ function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, clas
2067
2112
  ] });
2068
2113
  }
2069
2114
  if (state.kind === "denied") {
2115
+ const recovery = platformRecoveryCopy(texts);
2070
2116
  return /* @__PURE__ */ jsxs14("div", { className, role: "region", "aria-label": texts.deniedTitle, children: [
2071
2117
  /* @__PURE__ */ jsx19("h3", { children: texts.deniedTitle }),
2072
- /* @__PURE__ */ jsx19("p", { children: texts.deniedBody })
2118
+ /* @__PURE__ */ jsx19("p", { children: texts.deniedBody }),
2119
+ recovery && /* @__PURE__ */ jsx19("p", { "data-testid": "denied-recovery", children: recovery })
2073
2120
  ] });
2074
2121
  }
2075
2122
  if (state.kind === "unsupported") {