@hook-sdk/template 0.7.2 → 0.8.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
@@ -273,6 +273,13 @@ interface PixPending {
273
273
  qrCodePayload: string | null;
274
274
  qrCodeBase64: string | null;
275
275
  expiresAt: string | null;
276
+ /**
277
+ * Flipa `true` quando o polling detecta subscription ACTIVE/TRIAL (webhook
278
+ * PAYMENT_RECEIVED ou AUTHORIZATION_ACTIVATED chegou). UI deve renderizar
279
+ * confirmação visual nesse momento; após 2-3s chama `dismissPix` pra
280
+ * limpar o modal e deixar SubscriptionGate liberar o app.
281
+ */
282
+ paid: boolean;
276
283
  }
277
284
  /**
278
285
  * Hook headless pro Paywall. Expõe status atual da subscription + ação
package/dist/index.d.ts CHANGED
@@ -273,6 +273,13 @@ interface PixPending {
273
273
  qrCodePayload: string | null;
274
274
  qrCodeBase64: string | null;
275
275
  expiresAt: string | null;
276
+ /**
277
+ * Flipa `true` quando o polling detecta subscription ACTIVE/TRIAL (webhook
278
+ * PAYMENT_RECEIVED ou AUTHORIZATION_ACTIVATED chegou). UI deve renderizar
279
+ * confirmação visual nesse momento; após 2-3s chama `dismissPix` pra
280
+ * limpar o modal e deixar SubscriptionGate liberar o app.
281
+ */
282
+ paid: boolean;
276
283
  }
277
284
  /**
278
285
  * Hook headless pro Paywall. Expõe status atual da subscription + ação
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/AppRoot.tsx
2
- import { useCallback as useCallback7, useEffect as useEffect8, useRef as useRef3, useState as useState11 } from "react";
2
+ import { useCallback as useCallback7, useEffect as useEffect8, useRef as useRef3, useState as useState12 } from "react";
3
3
  import { useHook as useHook8 } from "@hook-sdk/sdk";
4
4
 
5
5
  // src/internal/TemplateConfigContext.tsx
@@ -121,7 +121,8 @@ function usePaywallState() {
121
121
  method: "pix-auto",
122
122
  qrCodePayload: result.qrCodePayload,
123
123
  qrCodeBase64: result.qrCodeBase64,
124
- expiresAt: null
124
+ expiresAt: null,
125
+ paid: false
125
126
  });
126
127
  setOpening(false);
127
128
  return;
@@ -132,7 +133,8 @@ function usePaywallState() {
132
133
  method: "pix-once",
133
134
  qrCodePayload: result.qrCodePayload,
134
135
  qrCodeBase64: result.qrCodeBase64,
135
- expiresAt: result.expiresAt
136
+ expiresAt: result.expiresAt,
137
+ paid: false
136
138
  });
137
139
  setOpening(false);
138
140
  return;
@@ -156,7 +158,7 @@ function usePaywallState() {
156
158
  const subRef = useRef(subscription);
157
159
  subRef.current = subscription;
158
160
  useEffect2(() => {
159
- if (!pixPending) return;
161
+ if (!pixPending || pixPending.paid) return;
160
162
  let attempts = 0;
161
163
  const MAX_ATTEMPTS = 60;
162
164
  let cancelled = false;
@@ -168,7 +170,7 @@ function usePaywallState() {
168
170
  if (cancelled) return;
169
171
  const s = subRef.current.status();
170
172
  if (s === "active" || s === "trialing") {
171
- setPixPending(null);
173
+ setPixPending((prev) => prev ? { ...prev, paid: true } : prev);
172
174
  return;
173
175
  }
174
176
  } catch {
@@ -211,17 +213,37 @@ function SubscriptionGate({ Paywall, children }) {
211
213
  }
212
214
 
213
215
  // src/internal/PersistedKeysPrefetch.tsx
214
- import { useEffect as useEffect3 } from "react";
216
+ import { useEffect as useEffect3, useState as useState3 } from "react";
215
217
  import { useHook as useHook3 } from "@hook-sdk/sdk";
216
218
  import { Fragment as Fragment3, jsx as jsx6 } from "react/jsx-runtime";
219
+ var SAFETY_TIMEOUT_MS = 3e3;
217
220
  function PersistedKeysPrefetch({ children }) {
218
221
  const { appData } = useHook3();
219
222
  const config = useTemplateConfig();
223
+ const hasKeys = !!config.persistedKeys && config.persistedKeys.length > 0;
224
+ const [ready, setReady] = useState3(!hasKeys);
220
225
  useEffect3(() => {
221
226
  const keys = config.persistedKeys;
222
- if (!keys || keys.length === 0) return;
227
+ if (!keys || keys.length === 0) {
228
+ setReady(true);
229
+ return;
230
+ }
231
+ let cancelled = false;
223
232
  appData.cache.startPrefetch(keys, (ks) => appData.bulkRead(ks));
233
+ void appData.cache.waitForPrimed(SAFETY_TIMEOUT_MS).then((result) => {
234
+ if (cancelled) return;
235
+ if (result === "timeout") {
236
+ console.warn(
237
+ `[@hook-sdk/template] PersistedKeysPrefetch: bulk-read n\xE3o completou em ${SAFETY_TIMEOUT_MS}ms. Liberando render mesmo assim \u2014 usePersistedState pode expor defaultValue at\xE9 o fetch terminar (G77).`
238
+ );
239
+ }
240
+ setReady(true);
241
+ });
242
+ return () => {
243
+ cancelled = true;
244
+ };
224
245
  }, [appData, config.persistedKeys]);
246
+ if (!ready) return null;
225
247
  return /* @__PURE__ */ jsx6(Fragment3, { children });
226
248
  }
227
249
 
@@ -234,7 +256,7 @@ function PushPrompt() {
234
256
  import { useEffect as useEffect5, useRef as useRef2 } from "react";
235
257
 
236
258
  // src/hooks/useInstallPrompt.ts
237
- import { useCallback as useCallback2, useEffect as useEffect4, useState as useState3 } from "react";
259
+ import { useCallback as useCallback2, useEffect as useEffect4, useState as useState4 } from "react";
238
260
  var IOS_RE = /iPad|iPhone|iPod/;
239
261
  var IOS_NON_SAFARI_RE = /CriOS|FxiOS|EdgiOS/;
240
262
  var ANDROID_RE = /Android/;
@@ -367,17 +389,17 @@ function useInstallPrompt(slug) {
367
389
  const iosBrowser = detectIOSBrowser(ua);
368
390
  const androidBrowser = detectAndroidBrowser(ua);
369
391
  const inAppApp = detectInAppApp(ua);
370
- const [isInstallable, setIsInstallable] = useState3(() => {
392
+ const [isInstallable, setIsInstallable] = useState4(() => {
371
393
  if (typeof window === "undefined") return false;
372
394
  return window.__pwaInstallPrompt != null;
373
395
  });
374
- const [isInstalled, setIsInstalled] = useState3(() => {
396
+ const [isInstalled, setIsInstalled] = useState4(() => {
375
397
  const { installed } = detectStandalone();
376
398
  return installed || readInstalledMarker(slug);
377
399
  });
378
- const [isDismissedSession, setIsDismissedSession] = useState3(() => readSessionSkip(slug));
379
- const [isDismissedPermanent, setIsDismissedPermanent] = useState3(() => readPermanentDismiss(slug).dismissed);
380
- const [skipCount, setSkipCount] = useState3(() => readSkipCount(slug));
400
+ const [isDismissedSession, setIsDismissedSession] = useState4(() => readSessionSkip(slug));
401
+ const [isDismissedPermanent, setIsDismissedPermanent] = useState4(() => readPermanentDismiss(slug).dismissed);
402
+ const [skipCount, setSkipCount] = useState4(() => readSkipCount(slug));
381
403
  useEffect4(() => {
382
404
  if (typeof window === "undefined") return;
383
405
  if (window.__pwaInstallPrompt) {
@@ -1232,7 +1254,7 @@ function IOSOtherVariant({
1232
1254
  }
1233
1255
 
1234
1256
  // src/components/InstallGate/variants/InAppBrowserVariant.tsx
1235
- import { useState as useState4 } from "react";
1257
+ import { useState as useState5 } from "react";
1236
1258
  import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
1237
1259
  function InAppBrowserVariant({
1238
1260
  state,
@@ -1242,7 +1264,7 @@ function InAppBrowserVariant({
1242
1264
  const appCopy = INSTALL_COPY.inApp[app] ?? INSTALL_COPY.inApp.other;
1243
1265
  const copy = INSTALL_COPY.inApp;
1244
1266
  const showPermanent = shouldShowPermanentOption(state);
1245
- const [copied, setCopied] = useState4(false);
1267
+ const [copied, setCopied] = useState5(false);
1246
1268
  const handleCopy = async () => {
1247
1269
  await actions.copyLink();
1248
1270
  setCopied(true);
@@ -1511,7 +1533,7 @@ var ErrorBoundary = class extends Component {
1511
1533
  };
1512
1534
 
1513
1535
  // src/hooks/useLoginForm.ts
1514
- import { useCallback as useCallback3, useMemo as useMemo2, useState as useState5 } from "react";
1536
+ import { useCallback as useCallback3, useMemo as useMemo2, useState as useState6 } from "react";
1515
1537
  import { useHook as useHook4 } from "@hook-sdk/sdk";
1516
1538
 
1517
1539
  // src/errors.ts
@@ -1548,10 +1570,10 @@ var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1548
1570
  var MIN_PASSWORD = 8;
1549
1571
  function useLoginForm() {
1550
1572
  const { auth } = useHook4();
1551
- const [email, setEmail] = useState5("");
1552
- const [password, setPassword] = useState5("");
1553
- const [submitting, setSubmitting] = useState5(false);
1554
- const [error, setError] = useState5(null);
1573
+ const [email, setEmail] = useState6("");
1574
+ const [password, setPassword] = useState6("");
1575
+ const [submitting, setSubmitting] = useState6(false);
1576
+ const [error, setError] = useState6(null);
1555
1577
  const emailError = useMemo2(() => {
1556
1578
  if (email.length === 0) return null;
1557
1579
  if (!EMAIL_RE.test(email)) return "Formato de e-mail inv\xE1lido.";
@@ -1662,7 +1684,7 @@ function GoogleGlyph() {
1662
1684
  }
1663
1685
 
1664
1686
  // src/internal/OAuthErrorBanner.tsx
1665
- import { useEffect as useEffect6, useState as useState6 } from "react";
1687
+ import { useEffect as useEffect6, useState as useState7 } from "react";
1666
1688
  import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
1667
1689
  var ERROR_MESSAGES = {
1668
1690
  invalid_state: "Sess\xE3o expirou, tente de novo.",
@@ -1683,7 +1705,7 @@ function stripErrorFromUrl() {
1683
1705
  window.history.replaceState({}, "", url.toString());
1684
1706
  }
1685
1707
  function OAuthErrorBanner() {
1686
- const [code, setCode] = useState6(() => readErrorCode());
1708
+ const [code, setCode] = useState7(() => readErrorCode());
1687
1709
  useEffect6(() => {
1688
1710
  if (code !== null) stripErrorFromUrl();
1689
1711
  }, [code]);
@@ -1817,17 +1839,17 @@ function DefaultLoginScreen({ onNavigate }) {
1817
1839
  }
1818
1840
 
1819
1841
  // src/hooks/useSignupForm.ts
1820
- import { useCallback as useCallback4, useMemo as useMemo3, useState as useState7 } from "react";
1842
+ import { useCallback as useCallback4, useMemo as useMemo3, useState as useState8 } from "react";
1821
1843
  import { useHook as useHook5 } from "@hook-sdk/sdk";
1822
1844
  var EMAIL_RE2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1823
1845
  var MIN_PASSWORD2 = 8;
1824
1846
  function useSignupForm() {
1825
1847
  const { auth } = useHook5();
1826
- const [name, setName] = useState7("");
1827
- const [email, setEmail] = useState7("");
1828
- const [password, setPassword] = useState7("");
1829
- const [submitting, setSubmitting] = useState7(false);
1830
- const [error, setError] = useState7(null);
1848
+ const [name, setName] = useState8("");
1849
+ const [email, setEmail] = useState8("");
1850
+ const [password, setPassword] = useState8("");
1851
+ const [submitting, setSubmitting] = useState8(false);
1852
+ const [error, setError] = useState8(null);
1831
1853
  const nameError = useMemo3(() => {
1832
1854
  if (name.length === 0) return null;
1833
1855
  if (name.trim().length < 2) return "Nome muito curto.";
@@ -1932,15 +1954,15 @@ function DefaultSignupScreen({ onNavigate }) {
1932
1954
  }
1933
1955
 
1934
1956
  // src/hooks/useForgotForm.ts
1935
- import { useCallback as useCallback5, useMemo as useMemo4, useState as useState8 } from "react";
1957
+ import { useCallback as useCallback5, useMemo as useMemo4, useState as useState9 } from "react";
1936
1958
  import { useHook as useHook6 } from "@hook-sdk/sdk";
1937
1959
  var EMAIL_RE3 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1938
1960
  function useForgotForm() {
1939
1961
  const { auth } = useHook6();
1940
- const [email, setEmail] = useState8("");
1941
- const [submitting, setSubmitting] = useState8(false);
1942
- const [sent, setSent] = useState8(false);
1943
- const [error, setError] = useState8(null);
1962
+ const [email, setEmail] = useState9("");
1963
+ const [submitting, setSubmitting] = useState9(false);
1964
+ const [sent, setSent] = useState9(false);
1965
+ const [error, setError] = useState9(null);
1944
1966
  const emailError = useMemo4(() => {
1945
1967
  if (email.length === 0) return null;
1946
1968
  if (!EMAIL_RE3.test(email)) return "Formato de e-mail inv\xE1lido.";
@@ -2006,17 +2028,17 @@ function DefaultForgotScreen({ onNavigate }) {
2006
2028
  }
2007
2029
 
2008
2030
  // src/hooks/useResetForm.ts
2009
- import { useCallback as useCallback6, useEffect as useEffect7, useMemo as useMemo5, useState as useState9 } from "react";
2031
+ import { useCallback as useCallback6, useEffect as useEffect7, useMemo as useMemo5, useState as useState10 } from "react";
2010
2032
  import { useHook as useHook7 } from "@hook-sdk/sdk";
2011
2033
  var MIN_PASSWORD3 = 12;
2012
2034
  function useResetForm() {
2013
2035
  const { auth } = useHook7();
2014
- const [token, setToken] = useState9(null);
2015
- const [password, setPassword] = useState9("");
2016
- const [confirm, setConfirm] = useState9("");
2017
- const [submitting, setSubmitting] = useState9(false);
2018
- const [done, setDone] = useState9(false);
2019
- const [error, setError] = useState9(null);
2036
+ const [token, setToken] = useState10(null);
2037
+ const [password, setPassword] = useState10("");
2038
+ const [confirm, setConfirm] = useState10("");
2039
+ const [submitting, setSubmitting] = useState10(false);
2040
+ const [done, setDone] = useState10(false);
2041
+ const [error, setError] = useState10(null);
2020
2042
  useEffect7(() => {
2021
2043
  if (typeof window === "undefined") return;
2022
2044
  const params = new URLSearchParams(window.location.search);
@@ -2112,13 +2134,13 @@ function DefaultResetScreen({ onNavigate }) {
2112
2134
  }
2113
2135
 
2114
2136
  // src/defaults/DefaultPaywall.tsx
2115
- import { useState as useState10 } from "react";
2137
+ import { useState as useState11 } from "react";
2116
2138
  import { jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
2117
2139
  function DefaultPaywall() {
2118
2140
  const config = useTemplateConfig();
2119
2141
  const { checkout, opening, error } = usePaywallState();
2120
2142
  const p = config.subscription.paywall_config;
2121
- const [cpf, setCpf] = useState10("");
2143
+ const [cpf, setCpf] = useState11("");
2122
2144
  const cpfDigits = cpf.replace(/\D/g, "");
2123
2145
  const canCheckout = cpfDigits.length === 11 && !opening;
2124
2146
  return /* @__PURE__ */ jsxs18("main", { style: { padding: 24, maxWidth: 440, margin: "0 auto", textAlign: "center" }, children: [
@@ -2178,7 +2200,7 @@ function PaymentReturnHandler({ children }) {
2178
2200
  const subRef = useRef3(subscription);
2179
2201
  subRef.current = subscription;
2180
2202
  const runIdRef = useRef3(0);
2181
- const [state, setState] = useState11("idle");
2203
+ const [state, setState] = useState12("idle");
2182
2204
  const runPoll = useCallback7(() => {
2183
2205
  const runId = ++runIdRef.current;
2184
2206
  setState("confirming");
@@ -2282,7 +2304,7 @@ function AppRoot({
2282
2304
  }
2283
2305
 
2284
2306
  // src/hooks/usePush.ts
2285
- import { useCallback as useCallback8, useEffect as useEffect9, useState as useState12 } from "react";
2307
+ import { useCallback as useCallback8, useEffect as useEffect9, useState as useState13 } from "react";
2286
2308
  import { useHook as useHook9 } from "@hook-sdk/sdk";
2287
2309
  function detectIosNeedsInstall() {
2288
2310
  if (typeof navigator === "undefined" || typeof window === "undefined") return false;
@@ -2310,7 +2332,7 @@ function deriveState(push) {
2310
2332
  }
2311
2333
  function usePush() {
2312
2334
  const { push } = useHook9();
2313
- const [state, setState] = useState12(() => deriveState(push));
2335
+ const [state, setState] = useState13(() => deriveState(push));
2314
2336
  useEffect9(() => {
2315
2337
  setState(deriveState(push));
2316
2338
  }, [push]);
@@ -2463,13 +2485,13 @@ function useSubscription() {
2463
2485
  }
2464
2486
 
2465
2487
  // src/hooks/useReminders.ts
2466
- import { useCallback as useCallback9, useEffect as useEffect11, useState as useState13 } from "react";
2488
+ import { useCallback as useCallback9, useEffect as useEffect11, useState as useState14 } from "react";
2467
2489
  import { useHook as useHook13 } from "@hook-sdk/sdk";
2468
2490
  function useReminders() {
2469
2491
  const { push } = useHook13();
2470
2492
  const r = push.reminders;
2471
- const [reminders, setReminders] = useState13([]);
2472
- const [loading, setLoading] = useState13(true);
2493
+ const [reminders, setReminders] = useState14([]);
2494
+ const [loading, setLoading] = useState14(true);
2473
2495
  const reload = useCallback9(async () => {
2474
2496
  setLoading(true);
2475
2497
  try {
@@ -2500,9 +2522,9 @@ function useReminders() {
2500
2522
  }
2501
2523
 
2502
2524
  // src/hooks/useToast.ts
2503
- import { useCallback as useCallback10, useState as useState14 } from "react";
2525
+ import { useCallback as useCallback10, useState as useState15 } from "react";
2504
2526
  function useToast() {
2505
- const [items, setItems] = useState14([]);
2527
+ const [items, setItems] = useState15([]);
2506
2528
  const show = useCallback10((message, kind = "info") => {
2507
2529
  const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
2508
2530
  setItems((prev) => [...prev, { id, message, kind }]);