@hook-sdk/template 0.13.0 → 0.14.1
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.cjs +335 -166
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -1
- package/dist/index.d.ts +34 -1
- package/dist/index.js +311 -142
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -77,7 +77,11 @@ var DeepLinksSchema = z.object({
|
|
|
77
77
|
var AppConfigSchema = z.object({
|
|
78
78
|
slug: z.string().regex(/^[a-z0-9-]+$/),
|
|
79
79
|
name: z.string().min(1),
|
|
80
|
-
branding: z.object({
|
|
80
|
+
branding: z.object({
|
|
81
|
+
primaryColor: z.string(),
|
|
82
|
+
logoUrl: z.string().url(),
|
|
83
|
+
iconUrl: z.string().url().optional()
|
|
84
|
+
}),
|
|
81
85
|
authFlow: AuthFlowSchema,
|
|
82
86
|
paywall: PaywallSchema,
|
|
83
87
|
persistedKeys: z.array(PersistedKeySchema),
|
|
@@ -497,6 +501,7 @@ import { useEffect as useEffect4, useRef } from "react";
|
|
|
497
501
|
|
|
498
502
|
// src/hooks/useInstallPrompt.ts
|
|
499
503
|
import { useCallback as useCallback2, useEffect as useEffect3, useState as useState2 } from "react";
|
|
504
|
+
var ANDROID_PROMPT_WAIT_MS = 3e3;
|
|
500
505
|
var IOS_RE = /iPad|iPhone|iPod/;
|
|
501
506
|
var IOS_NON_SAFARI_RE = /CriOS|FxiOS|EdgiOS/;
|
|
502
507
|
var ANDROID_RE = /Android/;
|
|
@@ -566,11 +571,12 @@ function track(event, props) {
|
|
|
566
571
|
if (typeof window === "undefined") return;
|
|
567
572
|
window.posthog?.capture?.(event, props);
|
|
568
573
|
}
|
|
569
|
-
function pickVariant(state) {
|
|
574
|
+
function pickVariant(state, promptWaitElapsed) {
|
|
570
575
|
if (state.isInstalled) return "none";
|
|
571
576
|
switch (state.platform) {
|
|
572
577
|
case "android":
|
|
573
|
-
|
|
578
|
+
if (state.isInstallable) return "android-native";
|
|
579
|
+
return promptWaitElapsed ? "android-manual" : "android-pending";
|
|
574
580
|
case "ios-safari":
|
|
575
581
|
return "ios-safari";
|
|
576
582
|
case "ios-other":
|
|
@@ -640,6 +646,15 @@ function useInstallPrompt(slug) {
|
|
|
640
646
|
const [isDismissedSession, setIsDismissedSession] = useState2(() => readSessionSkip(slug));
|
|
641
647
|
const [isDismissedPermanent, setIsDismissedPermanent] = useState2(() => readPermanentDismiss(slug).dismissed);
|
|
642
648
|
const [skipCount, setSkipCount] = useState2(() => readSkipCount(slug));
|
|
649
|
+
const [promptWaitElapsed, setPromptWaitElapsed] = useState2(() => {
|
|
650
|
+
if (typeof window === "undefined") return true;
|
|
651
|
+
return window.__pwaInstallPrompt != null;
|
|
652
|
+
});
|
|
653
|
+
useEffect3(() => {
|
|
654
|
+
if (promptWaitElapsed) return;
|
|
655
|
+
const id = setTimeout(() => setPromptWaitElapsed(true), ANDROID_PROMPT_WAIT_MS);
|
|
656
|
+
return () => clearTimeout(id);
|
|
657
|
+
}, [promptWaitElapsed]);
|
|
643
658
|
useEffect3(() => {
|
|
644
659
|
if (typeof window === "undefined") return;
|
|
645
660
|
if (window.__pwaInstallPrompt) {
|
|
@@ -677,17 +692,20 @@ function useInstallPrompt(slug) {
|
|
|
677
692
|
mq.addEventListener?.("change", handler);
|
|
678
693
|
return () => mq.removeEventListener?.("change", handler);
|
|
679
694
|
}, [slug]);
|
|
680
|
-
const variant = pickVariant(
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
695
|
+
const variant = pickVariant(
|
|
696
|
+
{
|
|
697
|
+
platform,
|
|
698
|
+
iosBrowser,
|
|
699
|
+
androidBrowser,
|
|
700
|
+
inAppApp,
|
|
701
|
+
isInstallable,
|
|
702
|
+
isInstalled,
|
|
703
|
+
isDismissedSession,
|
|
704
|
+
isDismissedPermanent,
|
|
705
|
+
skipCount
|
|
706
|
+
},
|
|
707
|
+
promptWaitElapsed
|
|
708
|
+
);
|
|
691
709
|
const promptInstall = useCallback2(async () => {
|
|
692
710
|
if (typeof window === "undefined") return false;
|
|
693
711
|
const prompt = window.__pwaInstallPrompt;
|
|
@@ -1223,8 +1241,44 @@ function Step({ n, icon, children }) {
|
|
|
1223
1241
|
);
|
|
1224
1242
|
}
|
|
1225
1243
|
|
|
1244
|
+
// src/components/InstallGate/variants/AndroidPendingVariant.tsx
|
|
1245
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
1246
|
+
function AndroidPendingVariant() {
|
|
1247
|
+
const copy = INSTALL_COPY.android.native;
|
|
1248
|
+
return /* @__PURE__ */ jsx10(InstallSplash, { title: copy.title, children: /* @__PURE__ */ jsx10(
|
|
1249
|
+
"div",
|
|
1250
|
+
{
|
|
1251
|
+
style: {
|
|
1252
|
+
display: "flex",
|
|
1253
|
+
flexDirection: "column",
|
|
1254
|
+
alignItems: "center",
|
|
1255
|
+
gap: 16,
|
|
1256
|
+
padding: "24px 0"
|
|
1257
|
+
},
|
|
1258
|
+
children: /* @__PURE__ */ jsx10(Spinner, {})
|
|
1259
|
+
}
|
|
1260
|
+
) });
|
|
1261
|
+
}
|
|
1262
|
+
function Spinner() {
|
|
1263
|
+
return /* @__PURE__ */ jsx10(
|
|
1264
|
+
"div",
|
|
1265
|
+
{
|
|
1266
|
+
"aria-hidden": true,
|
|
1267
|
+
style: {
|
|
1268
|
+
width: 28,
|
|
1269
|
+
height: 28,
|
|
1270
|
+
borderRadius: "50%",
|
|
1271
|
+
border: "3px solid #e5e5e7",
|
|
1272
|
+
borderTopColor: "var(--hook-color-primary)",
|
|
1273
|
+
animation: "hook-install-spin 0.8s linear infinite"
|
|
1274
|
+
},
|
|
1275
|
+
children: /* @__PURE__ */ jsx10("style", { children: `@keyframes hook-install-spin { to { transform: rotate(360deg); } }` })
|
|
1276
|
+
}
|
|
1277
|
+
);
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1226
1280
|
// src/components/InstallGate/Step.tsx
|
|
1227
|
-
import { jsx as
|
|
1281
|
+
import { jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1228
1282
|
function Step2({
|
|
1229
1283
|
n,
|
|
1230
1284
|
title,
|
|
@@ -1242,7 +1296,7 @@ function Step2({
|
|
|
1242
1296
|
textAlign: "left"
|
|
1243
1297
|
},
|
|
1244
1298
|
children: [
|
|
1245
|
-
/* @__PURE__ */
|
|
1299
|
+
/* @__PURE__ */ jsx11(
|
|
1246
1300
|
"div",
|
|
1247
1301
|
{
|
|
1248
1302
|
style: {
|
|
@@ -1262,8 +1316,8 @@ function Step2({
|
|
|
1262
1316
|
}
|
|
1263
1317
|
),
|
|
1264
1318
|
/* @__PURE__ */ jsxs5("div", { style: { flex: 1 }, children: [
|
|
1265
|
-
/* @__PURE__ */
|
|
1266
|
-
subtitle && /* @__PURE__ */
|
|
1319
|
+
/* @__PURE__ */ jsx11("p", { style: { margin: 0, fontSize: 15, fontWeight: 500, color: "#111", lineHeight: 1.3 }, children: title }),
|
|
1320
|
+
subtitle && /* @__PURE__ */ jsx11("p", { style: { margin: "4px 0 0 0", fontSize: 13, color: "#777" }, children: subtitle }),
|
|
1267
1321
|
visual
|
|
1268
1322
|
] })
|
|
1269
1323
|
]
|
|
@@ -1272,7 +1326,7 @@ function Step2({
|
|
|
1272
1326
|
}
|
|
1273
1327
|
|
|
1274
1328
|
// src/components/InstallGate/variants/IOSafariVariant.tsx
|
|
1275
|
-
import { jsx as
|
|
1329
|
+
import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1276
1330
|
function IOSafariVariant({
|
|
1277
1331
|
state,
|
|
1278
1332
|
actions
|
|
@@ -1280,13 +1334,13 @@ function IOSafariVariant({
|
|
|
1280
1334
|
const copy = INSTALL_COPY.iosSafari;
|
|
1281
1335
|
const showPermanent = shouldShowPermanentOption(state);
|
|
1282
1336
|
return /* @__PURE__ */ jsxs6(InstallSplash, { title: copy.title, subtitle: copy.subtitle, children: [
|
|
1283
|
-
/* @__PURE__ */
|
|
1337
|
+
/* @__PURE__ */ jsx12(
|
|
1284
1338
|
Step2,
|
|
1285
1339
|
{
|
|
1286
1340
|
n: 1,
|
|
1287
1341
|
title: copy.step1.title,
|
|
1288
1342
|
subtitle: copy.step1.subtitle,
|
|
1289
|
-
visual: /* @__PURE__ */
|
|
1343
|
+
visual: /* @__PURE__ */ jsx12(
|
|
1290
1344
|
"div",
|
|
1291
1345
|
{
|
|
1292
1346
|
style: {
|
|
@@ -1298,12 +1352,12 @@ function IOSafariVariant({
|
|
|
1298
1352
|
padding: "12px 0",
|
|
1299
1353
|
marginTop: 8
|
|
1300
1354
|
},
|
|
1301
|
-
children: /* @__PURE__ */
|
|
1355
|
+
children: /* @__PURE__ */ jsx12(ShareIconIOS, { size: 32, style: { color: "var(--hook-color-primary)" } })
|
|
1302
1356
|
}
|
|
1303
1357
|
)
|
|
1304
1358
|
}
|
|
1305
1359
|
),
|
|
1306
|
-
/* @__PURE__ */
|
|
1360
|
+
/* @__PURE__ */ jsx12(
|
|
1307
1361
|
Step2,
|
|
1308
1362
|
{
|
|
1309
1363
|
n: 2,
|
|
@@ -1321,19 +1375,19 @@ function IOSafariVariant({
|
|
|
1321
1375
|
marginTop: 8
|
|
1322
1376
|
},
|
|
1323
1377
|
children: [
|
|
1324
|
-
/* @__PURE__ */
|
|
1325
|
-
/* @__PURE__ */
|
|
1378
|
+
/* @__PURE__ */ jsx12(SquarePlusIcon, { size: 22, style: { color: "#555" } }),
|
|
1379
|
+
/* @__PURE__ */ jsx12("span", { style: { fontSize: 14, color: "#333" }, children: copy.step2.iconLabel })
|
|
1326
1380
|
]
|
|
1327
1381
|
}
|
|
1328
1382
|
)
|
|
1329
1383
|
}
|
|
1330
1384
|
),
|
|
1331
|
-
/* @__PURE__ */
|
|
1385
|
+
/* @__PURE__ */ jsx12(
|
|
1332
1386
|
Step2,
|
|
1333
1387
|
{
|
|
1334
1388
|
n: 3,
|
|
1335
1389
|
title: copy.step3.title,
|
|
1336
|
-
visual: /* @__PURE__ */
|
|
1390
|
+
visual: /* @__PURE__ */ jsx12(
|
|
1337
1391
|
"div",
|
|
1338
1392
|
{
|
|
1339
1393
|
style: {
|
|
@@ -1344,7 +1398,7 @@ function IOSafariVariant({
|
|
|
1344
1398
|
padding: "10px 14px",
|
|
1345
1399
|
marginTop: 8
|
|
1346
1400
|
},
|
|
1347
|
-
children: /* @__PURE__ */
|
|
1401
|
+
children: /* @__PURE__ */ jsx12(
|
|
1348
1402
|
"span",
|
|
1349
1403
|
{
|
|
1350
1404
|
style: {
|
|
@@ -1359,7 +1413,7 @@ function IOSafariVariant({
|
|
|
1359
1413
|
)
|
|
1360
1414
|
}
|
|
1361
1415
|
),
|
|
1362
|
-
/* @__PURE__ */
|
|
1416
|
+
/* @__PURE__ */ jsx12(
|
|
1363
1417
|
"button",
|
|
1364
1418
|
{
|
|
1365
1419
|
"data-testid": "install-prompt-skip-session",
|
|
@@ -1369,7 +1423,7 @@ function IOSafariVariant({
|
|
|
1369
1423
|
children: copy.skip
|
|
1370
1424
|
}
|
|
1371
1425
|
),
|
|
1372
|
-
showPermanent && /* @__PURE__ */
|
|
1426
|
+
showPermanent && /* @__PURE__ */ jsx12(
|
|
1373
1427
|
"button",
|
|
1374
1428
|
{
|
|
1375
1429
|
"data-testid": "install-prompt-skip-permanent",
|
|
@@ -1383,7 +1437,7 @@ function IOSafariVariant({
|
|
|
1383
1437
|
}
|
|
1384
1438
|
|
|
1385
1439
|
// src/components/InstallGate/variants/IOSOtherVariant.tsx
|
|
1386
|
-
import { jsx as
|
|
1440
|
+
import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1387
1441
|
function IOSOtherVariant({
|
|
1388
1442
|
state,
|
|
1389
1443
|
actions
|
|
@@ -1391,13 +1445,13 @@ function IOSOtherVariant({
|
|
|
1391
1445
|
const copy = INSTALL_COPY.iosOther;
|
|
1392
1446
|
const showPermanent = shouldShowPermanentOption(state);
|
|
1393
1447
|
return /* @__PURE__ */ jsxs7(InstallSplash, { title: copy.title, subtitle: copy.subtitle, children: [
|
|
1394
|
-
/* @__PURE__ */
|
|
1448
|
+
/* @__PURE__ */ jsx13(
|
|
1395
1449
|
Step2,
|
|
1396
1450
|
{
|
|
1397
1451
|
n: 1,
|
|
1398
1452
|
title: copy.step1.title,
|
|
1399
1453
|
subtitle: copy.step1.subtitle,
|
|
1400
|
-
visual: /* @__PURE__ */
|
|
1454
|
+
visual: /* @__PURE__ */ jsx13(
|
|
1401
1455
|
"div",
|
|
1402
1456
|
{
|
|
1403
1457
|
style: {
|
|
@@ -1409,12 +1463,12 @@ function IOSOtherVariant({
|
|
|
1409
1463
|
padding: "12px 0",
|
|
1410
1464
|
marginTop: 8
|
|
1411
1465
|
},
|
|
1412
|
-
children: /* @__PURE__ */
|
|
1466
|
+
children: /* @__PURE__ */ jsx13(ShareIconIOS, { size: 32, style: { color: "var(--hook-color-primary)" } })
|
|
1413
1467
|
}
|
|
1414
1468
|
)
|
|
1415
1469
|
}
|
|
1416
1470
|
),
|
|
1417
|
-
/* @__PURE__ */
|
|
1471
|
+
/* @__PURE__ */ jsx13(
|
|
1418
1472
|
Step2,
|
|
1419
1473
|
{
|
|
1420
1474
|
n: 2,
|
|
@@ -1432,19 +1486,19 @@ function IOSOtherVariant({
|
|
|
1432
1486
|
marginTop: 8
|
|
1433
1487
|
},
|
|
1434
1488
|
children: [
|
|
1435
|
-
/* @__PURE__ */
|
|
1436
|
-
/* @__PURE__ */
|
|
1489
|
+
/* @__PURE__ */ jsx13(SquarePlusIcon, { size: 22, style: { color: "#555" } }),
|
|
1490
|
+
/* @__PURE__ */ jsx13("span", { style: { fontSize: 14, color: "#333" }, children: copy.step2.iconLabel })
|
|
1437
1491
|
]
|
|
1438
1492
|
}
|
|
1439
1493
|
)
|
|
1440
1494
|
}
|
|
1441
1495
|
),
|
|
1442
|
-
/* @__PURE__ */
|
|
1496
|
+
/* @__PURE__ */ jsx13(
|
|
1443
1497
|
Step2,
|
|
1444
1498
|
{
|
|
1445
1499
|
n: 3,
|
|
1446
1500
|
title: copy.step3.title,
|
|
1447
|
-
visual: /* @__PURE__ */
|
|
1501
|
+
visual: /* @__PURE__ */ jsx13(
|
|
1448
1502
|
"div",
|
|
1449
1503
|
{
|
|
1450
1504
|
style: {
|
|
@@ -1455,7 +1509,7 @@ function IOSOtherVariant({
|
|
|
1455
1509
|
padding: "10px 14px",
|
|
1456
1510
|
marginTop: 8
|
|
1457
1511
|
},
|
|
1458
|
-
children: /* @__PURE__ */
|
|
1512
|
+
children: /* @__PURE__ */ jsx13(
|
|
1459
1513
|
"span",
|
|
1460
1514
|
{
|
|
1461
1515
|
style: {
|
|
@@ -1470,7 +1524,7 @@ function IOSOtherVariant({
|
|
|
1470
1524
|
)
|
|
1471
1525
|
}
|
|
1472
1526
|
),
|
|
1473
|
-
/* @__PURE__ */
|
|
1527
|
+
/* @__PURE__ */ jsx13(
|
|
1474
1528
|
"button",
|
|
1475
1529
|
{
|
|
1476
1530
|
"data-testid": "install-prompt-skip-session",
|
|
@@ -1480,7 +1534,7 @@ function IOSOtherVariant({
|
|
|
1480
1534
|
children: copy.skip
|
|
1481
1535
|
}
|
|
1482
1536
|
),
|
|
1483
|
-
showPermanent && /* @__PURE__ */
|
|
1537
|
+
showPermanent && /* @__PURE__ */ jsx13(
|
|
1484
1538
|
"button",
|
|
1485
1539
|
{
|
|
1486
1540
|
"data-testid": "install-prompt-skip-permanent",
|
|
@@ -1495,7 +1549,7 @@ function IOSOtherVariant({
|
|
|
1495
1549
|
|
|
1496
1550
|
// src/components/InstallGate/variants/InAppBrowserVariant.tsx
|
|
1497
1551
|
import { useState as useState3 } from "react";
|
|
1498
|
-
import { jsx as
|
|
1552
|
+
import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1499
1553
|
function InAppBrowserVariant({
|
|
1500
1554
|
state,
|
|
1501
1555
|
actions
|
|
@@ -1512,9 +1566,9 @@ function InAppBrowserVariant({
|
|
|
1512
1566
|
};
|
|
1513
1567
|
const DotsIcon = app === "facebook" || app === "telegram" ? MenuDotsVerticalIcon : MenuDotsHorizontalIcon;
|
|
1514
1568
|
return /* @__PURE__ */ jsxs8(InstallSplash, { title: appCopy.title, children: [
|
|
1515
|
-
/* @__PURE__ */
|
|
1516
|
-
/* @__PURE__ */
|
|
1517
|
-
/* @__PURE__ */
|
|
1569
|
+
/* @__PURE__ */ jsx14(Step3, { n: 1, icon: /* @__PURE__ */ jsx14(DotsIcon, { size: 20 }), children: appCopy.step1 }),
|
|
1570
|
+
/* @__PURE__ */ jsx14(Step3, { n: 2, icon: /* @__PURE__ */ jsx14(ExternalLinkIcon, { size: 18 }), children: appCopy.step2 }),
|
|
1571
|
+
/* @__PURE__ */ jsx14(
|
|
1518
1572
|
"button",
|
|
1519
1573
|
{
|
|
1520
1574
|
"data-testid": "install-prompt-cta-inapp-copy",
|
|
@@ -1524,7 +1578,7 @@ function InAppBrowserVariant({
|
|
|
1524
1578
|
children: copied ? copy.copiedToast : copy.copy
|
|
1525
1579
|
}
|
|
1526
1580
|
),
|
|
1527
|
-
/* @__PURE__ */
|
|
1581
|
+
/* @__PURE__ */ jsx14(
|
|
1528
1582
|
"button",
|
|
1529
1583
|
{
|
|
1530
1584
|
"data-testid": "install-prompt-skip-session",
|
|
@@ -1534,7 +1588,7 @@ function InAppBrowserVariant({
|
|
|
1534
1588
|
children: copy.skip
|
|
1535
1589
|
}
|
|
1536
1590
|
),
|
|
1537
|
-
showPermanent && /* @__PURE__ */
|
|
1591
|
+
showPermanent && /* @__PURE__ */ jsx14(
|
|
1538
1592
|
"button",
|
|
1539
1593
|
{
|
|
1540
1594
|
"data-testid": "install-prompt-skip-permanent",
|
|
@@ -1565,7 +1619,7 @@ function Step3({
|
|
|
1565
1619
|
textAlign: "left"
|
|
1566
1620
|
},
|
|
1567
1621
|
children: [
|
|
1568
|
-
/* @__PURE__ */
|
|
1622
|
+
/* @__PURE__ */ jsx14(
|
|
1569
1623
|
"div",
|
|
1570
1624
|
{
|
|
1571
1625
|
style: {
|
|
@@ -1584,15 +1638,15 @@ function Step3({
|
|
|
1584
1638
|
children: n
|
|
1585
1639
|
}
|
|
1586
1640
|
),
|
|
1587
|
-
/* @__PURE__ */
|
|
1588
|
-
/* @__PURE__ */
|
|
1641
|
+
/* @__PURE__ */ jsx14("div", { style: { flex: 1, fontSize: 14, color: "#333" }, children }),
|
|
1642
|
+
/* @__PURE__ */ jsx14("div", { style: { color: "#888", flexShrink: 0 }, children: icon })
|
|
1589
1643
|
]
|
|
1590
1644
|
}
|
|
1591
1645
|
);
|
|
1592
1646
|
}
|
|
1593
1647
|
|
|
1594
1648
|
// src/components/InstallGate/variants/DesktopVariant.tsx
|
|
1595
|
-
import { jsx as
|
|
1649
|
+
import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1596
1650
|
function DesktopVariant({
|
|
1597
1651
|
state,
|
|
1598
1652
|
actions
|
|
@@ -1608,14 +1662,14 @@ function DesktopVariant({
|
|
|
1608
1662
|
"aria-label": copy.title,
|
|
1609
1663
|
style: bannerStyle,
|
|
1610
1664
|
children: [
|
|
1611
|
-
iconUrl ? /* @__PURE__ */
|
|
1665
|
+
iconUrl ? /* @__PURE__ */ jsx15(
|
|
1612
1666
|
"img",
|
|
1613
1667
|
{
|
|
1614
1668
|
src: iconUrl,
|
|
1615
1669
|
alt: "",
|
|
1616
1670
|
style: { width: 40, height: 40, borderRadius: 10, objectFit: "cover", flexShrink: 0 }
|
|
1617
1671
|
}
|
|
1618
|
-
) : /* @__PURE__ */
|
|
1672
|
+
) : /* @__PURE__ */ jsx15(
|
|
1619
1673
|
"div",
|
|
1620
1674
|
{
|
|
1621
1675
|
style: {
|
|
@@ -1635,8 +1689,8 @@ function DesktopVariant({
|
|
|
1635
1689
|
}
|
|
1636
1690
|
),
|
|
1637
1691
|
/* @__PURE__ */ jsxs9("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
1638
|
-
/* @__PURE__ */
|
|
1639
|
-
/* @__PURE__ */
|
|
1692
|
+
/* @__PURE__ */ jsx15("div", { style: { fontSize: 14, fontWeight: 600, color: "#111" }, children: copy.title }),
|
|
1693
|
+
/* @__PURE__ */ jsx15("div", { style: { fontSize: 12, color: "#666" }, children: copy.subtitle })
|
|
1640
1694
|
] }),
|
|
1641
1695
|
/* @__PURE__ */ jsxs9(
|
|
1642
1696
|
"button",
|
|
@@ -1659,12 +1713,12 @@ function DesktopVariant({
|
|
|
1659
1713
|
flexShrink: 0
|
|
1660
1714
|
},
|
|
1661
1715
|
children: [
|
|
1662
|
-
/* @__PURE__ */
|
|
1716
|
+
/* @__PURE__ */ jsx15(DownloadIcon, { size: 14 }),
|
|
1663
1717
|
copy.cta
|
|
1664
1718
|
]
|
|
1665
1719
|
}
|
|
1666
1720
|
),
|
|
1667
|
-
/* @__PURE__ */
|
|
1721
|
+
/* @__PURE__ */ jsx15(
|
|
1668
1722
|
"button",
|
|
1669
1723
|
{
|
|
1670
1724
|
"data-testid": "install-prompt-desktop-close",
|
|
@@ -1679,7 +1733,7 @@ function DesktopVariant({
|
|
|
1679
1733
|
padding: 4,
|
|
1680
1734
|
flexShrink: 0
|
|
1681
1735
|
},
|
|
1682
|
-
children: /* @__PURE__ */
|
|
1736
|
+
children: /* @__PURE__ */ jsx15(XIcon, { size: 16 })
|
|
1683
1737
|
}
|
|
1684
1738
|
)
|
|
1685
1739
|
]
|
|
@@ -1703,7 +1757,7 @@ var bannerStyle = {
|
|
|
1703
1757
|
};
|
|
1704
1758
|
|
|
1705
1759
|
// src/components/InstallGate/InstallGate.tsx
|
|
1706
|
-
import { Fragment as Fragment3, jsx as
|
|
1760
|
+
import { Fragment as Fragment3, jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1707
1761
|
function InstallGate({ children }) {
|
|
1708
1762
|
const { slug, features_enabled } = useTemplateConfig();
|
|
1709
1763
|
const enabled = features_enabled.includes("install_prompt");
|
|
@@ -1724,30 +1778,32 @@ function InstallGate({ children }) {
|
|
|
1724
1778
|
variant: installState.variant
|
|
1725
1779
|
});
|
|
1726
1780
|
}, [shouldBlock, slug, installState.variant, installState.platform, installState.iosBrowser, installState.androidBrowser, installState.inAppApp]);
|
|
1727
|
-
if (!enabled) return /* @__PURE__ */
|
|
1728
|
-
if (installState.isInstalled) return /* @__PURE__ */
|
|
1781
|
+
if (!enabled) return /* @__PURE__ */ jsx16(Fragment3, { children });
|
|
1782
|
+
if (installState.isInstalled) return /* @__PURE__ */ jsx16(Fragment3, { children });
|
|
1729
1783
|
if (installState.variant === "desktop") {
|
|
1730
1784
|
const showBanner = !installState.isDismissedSession && !installState.isDismissedPermanent;
|
|
1731
1785
|
return /* @__PURE__ */ jsxs10(Fragment3, { children: [
|
|
1732
1786
|
children,
|
|
1733
|
-
showBanner && /* @__PURE__ */
|
|
1787
|
+
showBanner && /* @__PURE__ */ jsx16(DesktopVariant, { state: installState, actions: installState })
|
|
1734
1788
|
] });
|
|
1735
1789
|
}
|
|
1736
|
-
if (!shouldBlock) return /* @__PURE__ */
|
|
1790
|
+
if (!shouldBlock) return /* @__PURE__ */ jsx16(Fragment3, { children });
|
|
1737
1791
|
switch (installState.variant) {
|
|
1738
1792
|
case "android-native":
|
|
1739
|
-
return /* @__PURE__ */
|
|
1793
|
+
return /* @__PURE__ */ jsx16(AndroidNativeVariant, { state: installState, actions: installState });
|
|
1740
1794
|
case "android-manual":
|
|
1741
|
-
return /* @__PURE__ */
|
|
1795
|
+
return /* @__PURE__ */ jsx16(AndroidManualVariant, { state: installState, actions: installState });
|
|
1796
|
+
case "android-pending":
|
|
1797
|
+
return /* @__PURE__ */ jsx16(AndroidPendingVariant, {});
|
|
1742
1798
|
case "ios-safari":
|
|
1743
|
-
return /* @__PURE__ */
|
|
1799
|
+
return /* @__PURE__ */ jsx16(IOSafariVariant, { state: installState, actions: installState });
|
|
1744
1800
|
case "ios-other":
|
|
1745
|
-
return /* @__PURE__ */
|
|
1801
|
+
return /* @__PURE__ */ jsx16(IOSOtherVariant, { state: installState, actions: installState });
|
|
1746
1802
|
case "in-app":
|
|
1747
|
-
return /* @__PURE__ */
|
|
1803
|
+
return /* @__PURE__ */ jsx16(InAppBrowserVariant, { state: installState, actions: installState });
|
|
1748
1804
|
case "none":
|
|
1749
1805
|
default:
|
|
1750
|
-
return /* @__PURE__ */
|
|
1806
|
+
return /* @__PURE__ */ jsx16(Fragment3, { children });
|
|
1751
1807
|
}
|
|
1752
1808
|
}
|
|
1753
1809
|
|
|
@@ -1759,7 +1815,7 @@ function PushPrompt() {
|
|
|
1759
1815
|
// src/internal/SessionExpiredBanner.tsx
|
|
1760
1816
|
import { useEffect as useEffect5, useRef as useRef2, useState as useState4 } from "react";
|
|
1761
1817
|
import { useHook as useHook3 } from "@hook-sdk/sdk";
|
|
1762
|
-
import { jsx as
|
|
1818
|
+
import { jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1763
1819
|
var DISMISS_KEY = "hook:session-expired-dismissed-until";
|
|
1764
1820
|
var DISMISS_TTL_MS = 60 * 60 * 1e3;
|
|
1765
1821
|
function SessionExpiredBanner() {
|
|
@@ -1788,11 +1844,11 @@ function SessionExpiredBanner() {
|
|
|
1788
1844
|
}
|
|
1789
1845
|
return /* @__PURE__ */ jsxs11("div", { role: "alert", className: "fixed top-0 inset-x-0 bg-red-600 text-white px-4 py-2 flex items-center justify-between gap-3 text-sm shadow", style: { zIndex: 10001 }, children: [
|
|
1790
1846
|
/* @__PURE__ */ jsxs11("span", { children: [
|
|
1791
|
-
/* @__PURE__ */
|
|
1847
|
+
/* @__PURE__ */ jsx17("strong", { children: "Sua sess\xE3o expirou." }),
|
|
1792
1848
|
" Fa\xE7a login novamente para continuar."
|
|
1793
1849
|
] }),
|
|
1794
1850
|
/* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2", children: [
|
|
1795
|
-
/* @__PURE__ */
|
|
1851
|
+
/* @__PURE__ */ jsx17(
|
|
1796
1852
|
"button",
|
|
1797
1853
|
{
|
|
1798
1854
|
type: "button",
|
|
@@ -1801,7 +1857,7 @@ function SessionExpiredBanner() {
|
|
|
1801
1857
|
children: "Fazer login"
|
|
1802
1858
|
}
|
|
1803
1859
|
),
|
|
1804
|
-
/* @__PURE__ */
|
|
1860
|
+
/* @__PURE__ */ jsx17(
|
|
1805
1861
|
"button",
|
|
1806
1862
|
{
|
|
1807
1863
|
type: "button",
|
|
@@ -1817,7 +1873,7 @@ function SessionExpiredBanner() {
|
|
|
1817
1873
|
|
|
1818
1874
|
// src/defaults/ErrorBoundary.tsx
|
|
1819
1875
|
import { Component } from "react";
|
|
1820
|
-
import { Fragment as Fragment4, jsx as
|
|
1876
|
+
import { Fragment as Fragment4, jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1821
1877
|
var ErrorBoundary = class extends Component {
|
|
1822
1878
|
state = { error: null };
|
|
1823
1879
|
static getDerivedStateFromError(error) {
|
|
@@ -1836,27 +1892,31 @@ var ErrorBoundary = class extends Component {
|
|
|
1836
1892
|
render() {
|
|
1837
1893
|
if (this.state.error) {
|
|
1838
1894
|
return this.props.fallback ?? /* @__PURE__ */ jsxs12("div", { role: "alert", style: { padding: 24, textAlign: "center" }, children: [
|
|
1839
|
-
/* @__PURE__ */
|
|
1840
|
-
/* @__PURE__ */
|
|
1895
|
+
/* @__PURE__ */ jsx18("h2", { children: "Algo deu errado" }),
|
|
1896
|
+
/* @__PURE__ */ jsx18("p", { style: { opacity: 0.7 }, children: "Recarregue a p\xE1gina pra tentar de novo." })
|
|
1841
1897
|
] });
|
|
1842
1898
|
}
|
|
1843
|
-
return /* @__PURE__ */
|
|
1899
|
+
return /* @__PURE__ */ jsx18(Fragment4, { children: this.props.children });
|
|
1844
1900
|
}
|
|
1845
1901
|
};
|
|
1846
1902
|
|
|
1847
1903
|
// src/internal/PaymentReturnHandler.tsx
|
|
1848
1904
|
import { useCallback as useCallback3, useEffect as useEffect6, useRef as useRef3, useState as useState5 } from "react";
|
|
1849
1905
|
import { useHook as useHook4 } from "@hook-sdk/sdk";
|
|
1850
|
-
import { Fragment as Fragment5, jsx as
|
|
1906
|
+
import { Fragment as Fragment5, jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1851
1907
|
var BACKOFF_MS = [2e3, 5e3, 1e4, 2e4, 4e4];
|
|
1908
|
+
var MAX_CYCLES = 3;
|
|
1909
|
+
var SUPPORT_MAILTO = "mailto:suporte@usehook.net?subject=Pagamento%20pendente";
|
|
1852
1910
|
function PaymentReturnHandler({ children }) {
|
|
1853
1911
|
const { subscription } = useHook4();
|
|
1854
1912
|
const subRef = useRef3(subscription);
|
|
1855
1913
|
subRef.current = subscription;
|
|
1856
1914
|
const runIdRef = useRef3(0);
|
|
1915
|
+
const cyclesRef = useRef3(0);
|
|
1857
1916
|
const [state, setState] = useState5("idle");
|
|
1858
1917
|
const runPoll = useCallback3(() => {
|
|
1859
1918
|
const runId = ++runIdRef.current;
|
|
1919
|
+
cyclesRef.current += 1;
|
|
1860
1920
|
setState("confirming");
|
|
1861
1921
|
let attempts = 0;
|
|
1862
1922
|
const tick = async () => {
|
|
@@ -1872,12 +1932,17 @@ function PaymentReturnHandler({ children }) {
|
|
|
1872
1932
|
const cleanUrl = new URL(window.location.href);
|
|
1873
1933
|
cleanUrl.searchParams.delete("paymentReturn");
|
|
1874
1934
|
window.history.replaceState({}, "", cleanUrl.toString());
|
|
1935
|
+
cyclesRef.current = 0;
|
|
1875
1936
|
setState("idle");
|
|
1876
1937
|
return;
|
|
1877
1938
|
}
|
|
1878
1939
|
const delay = BACKOFF_MS[attempts - 1];
|
|
1879
1940
|
if (delay === void 0) {
|
|
1880
|
-
|
|
1941
|
+
if (cyclesRef.current >= MAX_CYCLES) {
|
|
1942
|
+
setState("timeout");
|
|
1943
|
+
} else {
|
|
1944
|
+
setState("waiting");
|
|
1945
|
+
}
|
|
1881
1946
|
return;
|
|
1882
1947
|
}
|
|
1883
1948
|
setTimeout(tick, delay);
|
|
@@ -1888,21 +1953,67 @@ function PaymentReturnHandler({ children }) {
|
|
|
1888
1953
|
if (typeof window === "undefined") return;
|
|
1889
1954
|
const url = new URL(window.location.href);
|
|
1890
1955
|
if (url.searchParams.get("paymentReturn") !== "1") return;
|
|
1956
|
+
cyclesRef.current = 0;
|
|
1891
1957
|
runPoll();
|
|
1892
1958
|
return () => {
|
|
1893
1959
|
runIdRef.current++;
|
|
1894
1960
|
};
|
|
1895
1961
|
}, [runPoll]);
|
|
1962
|
+
const goHome = useCallback3(() => {
|
|
1963
|
+
const cleanUrl = new URL(window.location.href);
|
|
1964
|
+
cleanUrl.searchParams.delete("paymentReturn");
|
|
1965
|
+
cleanUrl.pathname = "/app/home";
|
|
1966
|
+
window.location.href = cleanUrl.toString();
|
|
1967
|
+
}, []);
|
|
1896
1968
|
if (state === "confirming") {
|
|
1897
|
-
return /* @__PURE__ */
|
|
1969
|
+
return /* @__PURE__ */ jsx19("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: "Confirmando pagamento\u2026" });
|
|
1898
1970
|
}
|
|
1899
1971
|
if (state === "waiting") {
|
|
1900
|
-
return /* @__PURE__ */
|
|
1901
|
-
/* @__PURE__ */
|
|
1902
|
-
/* @__PURE__ */
|
|
1972
|
+
return /* @__PURE__ */ jsx19("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: /* @__PURE__ */ jsxs13("div", { style: { maxWidth: 320, textAlign: "center", lineHeight: 1.5 }, children: [
|
|
1973
|
+
/* @__PURE__ */ jsx19("div", { style: { marginBottom: 16 }, children: "Pagamento aceito. Estamos confirmando com o banco \u2014 pode levar alguns minutos." }),
|
|
1974
|
+
/* @__PURE__ */ jsx19("button", { type: "button", onClick: runPoll, style: buttonStyle, children: "Atualizar" })
|
|
1903
1975
|
] }) });
|
|
1904
1976
|
}
|
|
1905
|
-
|
|
1977
|
+
if (state === "timeout") {
|
|
1978
|
+
return /* @__PURE__ */ jsx19("div", { role: "alert", "aria-live": "assertive", style: overlayStyle2, children: /* @__PURE__ */ jsxs13("div", { style: { maxWidth: 360, textAlign: "center", lineHeight: 1.5 }, children: [
|
|
1979
|
+
/* @__PURE__ */ jsx19("div", { style: { marginBottom: 16 }, children: "Ainda n\xE3o conseguimos confirmar seu pagamento com o banco. Voc\xEA pode tentar de novo, voltar pro app, ou falar com a gente." }),
|
|
1980
|
+
/* @__PURE__ */ jsxs13("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
|
|
1981
|
+
/* @__PURE__ */ jsx19(
|
|
1982
|
+
"button",
|
|
1983
|
+
{
|
|
1984
|
+
type: "button",
|
|
1985
|
+
onClick: () => {
|
|
1986
|
+
cyclesRef.current = 0;
|
|
1987
|
+
runPoll();
|
|
1988
|
+
},
|
|
1989
|
+
style: buttonStyle,
|
|
1990
|
+
"data-testid": "payment-timeout-retry",
|
|
1991
|
+
children: "Tentar de novo"
|
|
1992
|
+
}
|
|
1993
|
+
),
|
|
1994
|
+
/* @__PURE__ */ jsx19(
|
|
1995
|
+
"button",
|
|
1996
|
+
{
|
|
1997
|
+
type: "button",
|
|
1998
|
+
onClick: goHome,
|
|
1999
|
+
style: secondaryButtonStyle,
|
|
2000
|
+
"data-testid": "payment-timeout-home",
|
|
2001
|
+
children: "Voltar pro app"
|
|
2002
|
+
}
|
|
2003
|
+
),
|
|
2004
|
+
/* @__PURE__ */ jsx19(
|
|
2005
|
+
"a",
|
|
2006
|
+
{
|
|
2007
|
+
href: SUPPORT_MAILTO,
|
|
2008
|
+
style: linkStyle,
|
|
2009
|
+
"data-testid": "payment-timeout-support",
|
|
2010
|
+
children: "Falar com suporte"
|
|
2011
|
+
}
|
|
2012
|
+
)
|
|
2013
|
+
] })
|
|
2014
|
+
] }) });
|
|
2015
|
+
}
|
|
2016
|
+
return /* @__PURE__ */ jsx19(Fragment5, { children });
|
|
1906
2017
|
}
|
|
1907
2018
|
var overlayStyle2 = {
|
|
1908
2019
|
position: "fixed",
|
|
@@ -1926,9 +2037,22 @@ var buttonStyle = {
|
|
|
1926
2037
|
fontWeight: 600,
|
|
1927
2038
|
cursor: "pointer"
|
|
1928
2039
|
};
|
|
2040
|
+
var secondaryButtonStyle = {
|
|
2041
|
+
...buttonStyle,
|
|
2042
|
+
background: "transparent",
|
|
2043
|
+
color: "white",
|
|
2044
|
+
border: "1px solid rgba(255,255,255,0.5)"
|
|
2045
|
+
};
|
|
2046
|
+
var linkStyle = {
|
|
2047
|
+
color: "white",
|
|
2048
|
+
textDecoration: "underline",
|
|
2049
|
+
fontSize: "0.9rem",
|
|
2050
|
+
marginTop: 4,
|
|
2051
|
+
textAlign: "center"
|
|
2052
|
+
};
|
|
1929
2053
|
|
|
1930
2054
|
// src/AppRoot.tsx
|
|
1931
|
-
import { Fragment as Fragment6, jsx as
|
|
2055
|
+
import { Fragment as Fragment6, jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1932
2056
|
function buildLegacyConfigShim(config) {
|
|
1933
2057
|
const paywall = config.paywall;
|
|
1934
2058
|
const isFree = paywall.mode === "free";
|
|
@@ -1938,7 +2062,16 @@ function buildLegacyConfigShim(config) {
|
|
|
1938
2062
|
slug: config.slug,
|
|
1939
2063
|
name: config.name,
|
|
1940
2064
|
email_alias: config.slug,
|
|
1941
|
-
theme
|
|
2065
|
+
// Map branding into the legacy theme shape so InstallSplash (and
|
|
2066
|
+
// anything else reading theme.icon_url / theme.logo_url) can surface
|
|
2067
|
+
// the app icon instead of the generic "first letter of name" fallback.
|
|
2068
|
+
// Falls back to logoUrl when iconUrl is unset — apps that haven't
|
|
2069
|
+
// adopted iconUrl keep their previous behavior unchanged.
|
|
2070
|
+
theme: {
|
|
2071
|
+
primary_color: config.branding.primaryColor,
|
|
2072
|
+
icon_url: config.branding.iconUrl ?? config.branding.logoUrl,
|
|
2073
|
+
logo_url: config.branding.logoUrl
|
|
2074
|
+
},
|
|
1942
2075
|
features_enabled: config.features_enabled ?? [],
|
|
1943
2076
|
dependencies_allowlist: ["react", "react-dom"],
|
|
1944
2077
|
subscription: {
|
|
@@ -1996,10 +2129,10 @@ function AppRoot(props) {
|
|
|
1996
2129
|
const Router = testRouter === "memory" ? MemoryRouter : BrowserRouter;
|
|
1997
2130
|
const basename = `/app/${config.slug}`;
|
|
1998
2131
|
const routerProps = testRouter === "memory" ? { basename, initialEntries: testInitialEntries } : { basename };
|
|
1999
|
-
return /* @__PURE__ */
|
|
2000
|
-
/* @__PURE__ */
|
|
2001
|
-
/* @__PURE__ */
|
|
2002
|
-
/* @__PURE__ */
|
|
2132
|
+
return /* @__PURE__ */ jsx20(ErrorBoundary, { children: /* @__PURE__ */ jsx20(AppConfigProvider, { config, children: /* @__PURE__ */ jsx20(TemplateConfigProvider, { config: legacyShim, children: /* @__PURE__ */ jsx20(ThemeProvider, { children: /* @__PURE__ */ jsx20(PersistenceRegistry, { config: config.persistedKeys, children: /* @__PURE__ */ jsxs14(Router, { ...routerProps, children: [
|
|
2133
|
+
/* @__PURE__ */ jsx20(DeepLinkHandler, { deepLinks: config.deepLinks }),
|
|
2134
|
+
/* @__PURE__ */ jsx20(SessionExpiredBanner, {}),
|
|
2135
|
+
/* @__PURE__ */ jsx20(InstallGate, { children: /* @__PURE__ */ jsx20(
|
|
2003
2136
|
AuthGated,
|
|
2004
2137
|
{
|
|
2005
2138
|
config,
|
|
@@ -2013,7 +2146,7 @@ function AppRoot(props) {
|
|
|
2013
2146
|
PreAuthFlow,
|
|
2014
2147
|
children: /* @__PURE__ */ jsxs14(SubscriptionGate, { Paywall: Paywall ?? FallbackPaywall, children: [
|
|
2015
2148
|
children,
|
|
2016
|
-
/* @__PURE__ */
|
|
2149
|
+
/* @__PURE__ */ jsx20(PushPrompt, {})
|
|
2017
2150
|
] })
|
|
2018
2151
|
}
|
|
2019
2152
|
) })
|
|
@@ -2034,24 +2167,24 @@ function AuthGated({
|
|
|
2034
2167
|
if (authStatus !== "authenticated") {
|
|
2035
2168
|
if (config.onboarding?.trigger === "pre_signup_custom" && PreAuthFlow) {
|
|
2036
2169
|
return /* @__PURE__ */ jsxs14(Routes, { children: [
|
|
2037
|
-
/* @__PURE__ */
|
|
2038
|
-
/* @__PURE__ */
|
|
2039
|
-
/* @__PURE__ */
|
|
2040
|
-
/* @__PURE__ */
|
|
2041
|
-
EmailVerify ? /* @__PURE__ */
|
|
2042
|
-
/* @__PURE__ */
|
|
2170
|
+
/* @__PURE__ */ jsx20(Route, { path: "/signin", element: /* @__PURE__ */ jsx20(Login, {}) }),
|
|
2171
|
+
/* @__PURE__ */ jsx20(Route, { path: "/signup", element: /* @__PURE__ */ jsx20(Signup, {}) }),
|
|
2172
|
+
/* @__PURE__ */ jsx20(Route, { path: "/forgot", element: /* @__PURE__ */ jsx20(Forgot, {}) }),
|
|
2173
|
+
/* @__PURE__ */ jsx20(Route, { path: "/reset", element: /* @__PURE__ */ jsx20(Reset, {}) }),
|
|
2174
|
+
EmailVerify ? /* @__PURE__ */ jsx20(Route, { path: "/verify", element: /* @__PURE__ */ jsx20(EmailVerify, {}) }) : null,
|
|
2175
|
+
/* @__PURE__ */ jsx20(Route, { path: "/*", element: /* @__PURE__ */ jsx20(PreAuthFlow, {}) })
|
|
2043
2176
|
] });
|
|
2044
2177
|
}
|
|
2045
2178
|
return /* @__PURE__ */ jsxs14(Routes, { children: [
|
|
2046
|
-
/* @__PURE__ */
|
|
2047
|
-
/* @__PURE__ */
|
|
2048
|
-
/* @__PURE__ */
|
|
2049
|
-
/* @__PURE__ */
|
|
2050
|
-
EmailVerify ? /* @__PURE__ */
|
|
2051
|
-
/* @__PURE__ */
|
|
2179
|
+
/* @__PURE__ */ jsx20(Route, { path: "/", element: /* @__PURE__ */ jsx20(Login, {}) }),
|
|
2180
|
+
/* @__PURE__ */ jsx20(Route, { path: "/signup", element: /* @__PURE__ */ jsx20(Signup, {}) }),
|
|
2181
|
+
/* @__PURE__ */ jsx20(Route, { path: "/forgot", element: /* @__PURE__ */ jsx20(Forgot, {}) }),
|
|
2182
|
+
/* @__PURE__ */ jsx20(Route, { path: "/reset", element: /* @__PURE__ */ jsx20(Reset, {}) }),
|
|
2183
|
+
EmailVerify ? /* @__PURE__ */ jsx20(Route, { path: "/verify", element: /* @__PURE__ */ jsx20(EmailVerify, {}) }) : null,
|
|
2184
|
+
/* @__PURE__ */ jsx20(Route, { path: "*", element: /* @__PURE__ */ jsx20(Navigate, { to: "/", replace: true }) })
|
|
2052
2185
|
] });
|
|
2053
2186
|
}
|
|
2054
|
-
return /* @__PURE__ */
|
|
2187
|
+
return /* @__PURE__ */ jsx20(Fragment6, { children });
|
|
2055
2188
|
}
|
|
2056
2189
|
function FallbackPaywall() {
|
|
2057
2190
|
return null;
|
|
@@ -2142,7 +2275,7 @@ function usePush() {
|
|
|
2142
2275
|
}
|
|
2143
2276
|
|
|
2144
2277
|
// src/components/PushPrompt.tsx
|
|
2145
|
-
import { jsx as
|
|
2278
|
+
import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2146
2279
|
function platformRecoveryCopy(texts) {
|
|
2147
2280
|
if (typeof navigator === "undefined") return null;
|
|
2148
2281
|
const ua = navigator.userAgent || "";
|
|
@@ -2166,27 +2299,27 @@ function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, clas
|
|
|
2166
2299
|
if (state.kind === "subscribed" || state.kind === "dismissed") return null;
|
|
2167
2300
|
if (state.kind === "ios_needs_install") {
|
|
2168
2301
|
return /* @__PURE__ */ jsxs15("div", { className, role: "region", "aria-label": texts.iosInstallTitle, children: [
|
|
2169
|
-
/* @__PURE__ */
|
|
2170
|
-
/* @__PURE__ */
|
|
2171
|
-
onInstallRequested && texts.iosInstallCta && /* @__PURE__ */
|
|
2302
|
+
/* @__PURE__ */ jsx21("h3", { children: texts.iosInstallTitle }),
|
|
2303
|
+
/* @__PURE__ */ jsx21("p", { children: texts.iosInstallBody }),
|
|
2304
|
+
onInstallRequested && texts.iosInstallCta && /* @__PURE__ */ jsx21("button", { onClick: onInstallRequested, children: texts.iosInstallCta })
|
|
2172
2305
|
] });
|
|
2173
2306
|
}
|
|
2174
2307
|
if (state.kind === "denied") {
|
|
2175
2308
|
const recovery = platformRecoveryCopy(texts);
|
|
2176
2309
|
return /* @__PURE__ */ jsxs15("div", { className, role: "region", "aria-label": texts.deniedTitle, children: [
|
|
2177
|
-
/* @__PURE__ */
|
|
2178
|
-
/* @__PURE__ */
|
|
2179
|
-
recovery && /* @__PURE__ */
|
|
2310
|
+
/* @__PURE__ */ jsx21("h3", { children: texts.deniedTitle }),
|
|
2311
|
+
/* @__PURE__ */ jsx21("p", { children: texts.deniedBody }),
|
|
2312
|
+
recovery && /* @__PURE__ */ jsx21("p", { "data-testid": "denied-recovery", children: recovery })
|
|
2180
2313
|
] });
|
|
2181
2314
|
}
|
|
2182
2315
|
if (state.kind === "unsupported") {
|
|
2183
|
-
return /* @__PURE__ */
|
|
2316
|
+
return /* @__PURE__ */ jsx21("div", { className, role: "region", children: /* @__PURE__ */ jsx21("p", { children: texts.unsupportedBody }) });
|
|
2184
2317
|
}
|
|
2185
2318
|
if (state.kind === "error") {
|
|
2186
|
-
return /* @__PURE__ */
|
|
2319
|
+
return /* @__PURE__ */ jsx21("div", { className, role: "region", "aria-label": "error", children: /* @__PURE__ */ jsx21("p", { children: state.message }) });
|
|
2187
2320
|
}
|
|
2188
2321
|
return /* @__PURE__ */ jsxs15("div", { className, role: "region", children: [
|
|
2189
|
-
/* @__PURE__ */
|
|
2322
|
+
/* @__PURE__ */ jsx21(
|
|
2190
2323
|
"button",
|
|
2191
2324
|
{
|
|
2192
2325
|
type: "button",
|
|
@@ -2200,23 +2333,23 @@ function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, clas
|
|
|
2200
2333
|
children: texts.cta
|
|
2201
2334
|
}
|
|
2202
2335
|
),
|
|
2203
|
-
onDeclined && /* @__PURE__ */
|
|
2336
|
+
onDeclined && /* @__PURE__ */ jsx21("button", { type: "button", onClick: onDeclined, children: texts.declineCta })
|
|
2204
2337
|
] });
|
|
2205
2338
|
}
|
|
2206
2339
|
|
|
2207
2340
|
// src/defaults/LoadingState.tsx
|
|
2208
|
-
import { jsx as
|
|
2341
|
+
import { jsx as jsx22 } from "react/jsx-runtime";
|
|
2209
2342
|
function LoadingState({ message }) {
|
|
2210
|
-
return /* @__PURE__ */
|
|
2343
|
+
return /* @__PURE__ */ jsx22("div", { role: "status", "aria-live": "polite", style: { padding: 24, textAlign: "center" }, children: /* @__PURE__ */ jsx22("span", { children: message ?? "Carregando..." }) });
|
|
2211
2344
|
}
|
|
2212
2345
|
|
|
2213
2346
|
// src/defaults/EmptyState.tsx
|
|
2214
|
-
import { jsx as
|
|
2347
|
+
import { jsx as jsx23, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2215
2348
|
function EmptyState({ title, description, action }) {
|
|
2216
2349
|
return /* @__PURE__ */ jsxs16("div", { role: "status", style: { padding: 32, textAlign: "center" }, children: [
|
|
2217
|
-
/* @__PURE__ */
|
|
2218
|
-
description && /* @__PURE__ */
|
|
2219
|
-
action && /* @__PURE__ */
|
|
2350
|
+
/* @__PURE__ */ jsx23("h2", { style: { marginBottom: 8 }, children: title }),
|
|
2351
|
+
description && /* @__PURE__ */ jsx23("p", { style: { opacity: 0.7 }, children: description }),
|
|
2352
|
+
action && /* @__PURE__ */ jsx23("div", { style: { marginTop: 16 }, children: action })
|
|
2220
2353
|
] });
|
|
2221
2354
|
}
|
|
2222
2355
|
|
|
@@ -2262,18 +2395,24 @@ function useLoginForm() {
|
|
|
2262
2395
|
const [password, setPassword] = useState7("");
|
|
2263
2396
|
const [submitting, setSubmitting] = useState7(false);
|
|
2264
2397
|
const [error, setError] = useState7(null);
|
|
2265
|
-
const
|
|
2398
|
+
const [touchedEmail, setTouchedEmail] = useState7(false);
|
|
2399
|
+
const [touchedPassword, setTouchedPassword] = useState7(false);
|
|
2400
|
+
const [formSubmitAttempted, setFormSubmitAttempted] = useState7(false);
|
|
2401
|
+
const validateEmail = useMemo4(() => {
|
|
2266
2402
|
if (email.length === 0) return null;
|
|
2267
2403
|
if (!EMAIL_RE.test(email)) return "Formato de e-mail inv\xE1lido.";
|
|
2268
2404
|
return null;
|
|
2269
2405
|
}, [email]);
|
|
2270
|
-
const
|
|
2406
|
+
const validatePassword = useMemo4(() => {
|
|
2271
2407
|
if (password.length === 0) return null;
|
|
2272
2408
|
if (password.length < MIN_PASSWORD) return `M\xEDnimo de ${MIN_PASSWORD} caracteres.`;
|
|
2273
2409
|
return null;
|
|
2274
2410
|
}, [password]);
|
|
2275
|
-
const
|
|
2411
|
+
const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
|
|
2412
|
+
const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
|
|
2413
|
+
const canSubmit = email.length > 0 && password.length >= MIN_PASSWORD && validateEmail === null && validatePassword === null && !submitting;
|
|
2276
2414
|
const submit = useCallback5(async () => {
|
|
2415
|
+
setFormSubmitAttempted(true);
|
|
2277
2416
|
if (!canSubmit) return false;
|
|
2278
2417
|
setSubmitting(true);
|
|
2279
2418
|
setError(null);
|
|
@@ -2291,9 +2430,12 @@ function useLoginForm() {
|
|
|
2291
2430
|
email,
|
|
2292
2431
|
setEmail,
|
|
2293
2432
|
emailError,
|
|
2433
|
+
markEmailTouched: () => setTouchedEmail(true),
|
|
2294
2434
|
password,
|
|
2295
2435
|
setPassword,
|
|
2296
2436
|
passwordError,
|
|
2437
|
+
markPasswordTouched: () => setTouchedPassword(true),
|
|
2438
|
+
formSubmitAttempted,
|
|
2297
2439
|
submit,
|
|
2298
2440
|
submitting,
|
|
2299
2441
|
canSubmit,
|
|
@@ -2314,23 +2456,31 @@ function useSignupForm() {
|
|
|
2314
2456
|
const [password, setPassword] = useState8("");
|
|
2315
2457
|
const [submitting, setSubmitting] = useState8(false);
|
|
2316
2458
|
const [error, setError] = useState8(null);
|
|
2317
|
-
const
|
|
2459
|
+
const [touchedName, setTouchedName] = useState8(false);
|
|
2460
|
+
const [touchedEmail, setTouchedEmail] = useState8(false);
|
|
2461
|
+
const [touchedPassword, setTouchedPassword] = useState8(false);
|
|
2462
|
+
const [formSubmitAttempted, setFormSubmitAttempted] = useState8(false);
|
|
2463
|
+
const validateName = useMemo5(() => {
|
|
2318
2464
|
if (name.length === 0) return null;
|
|
2319
2465
|
if (name.trim().length < 2) return "Nome muito curto.";
|
|
2320
2466
|
return null;
|
|
2321
2467
|
}, [name]);
|
|
2322
|
-
const
|
|
2468
|
+
const validateEmail = useMemo5(() => {
|
|
2323
2469
|
if (email.length === 0) return null;
|
|
2324
2470
|
if (!EMAIL_RE2.test(email)) return "Formato de e-mail inv\xE1lido.";
|
|
2325
2471
|
return null;
|
|
2326
2472
|
}, [email]);
|
|
2327
|
-
const
|
|
2473
|
+
const validatePassword = useMemo5(() => {
|
|
2328
2474
|
if (password.length === 0) return null;
|
|
2329
2475
|
if (password.length < MIN_PASSWORD2) return `M\xEDnimo de ${MIN_PASSWORD2} caracteres.`;
|
|
2330
2476
|
return null;
|
|
2331
2477
|
}, [password]);
|
|
2332
|
-
const
|
|
2478
|
+
const nameError = touchedName || formSubmitAttempted ? validateName : null;
|
|
2479
|
+
const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
|
|
2480
|
+
const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
|
|
2481
|
+
const canSubmit = name.trim().length >= 2 && email.length > 0 && password.length >= MIN_PASSWORD2 && validateName === null && validateEmail === null && validatePassword === null && !submitting;
|
|
2333
2482
|
const submit = useCallback6(async () => {
|
|
2483
|
+
setFormSubmitAttempted(true);
|
|
2334
2484
|
if (!canSubmit) return false;
|
|
2335
2485
|
setSubmitting(true);
|
|
2336
2486
|
setError(null);
|
|
@@ -2348,12 +2498,16 @@ function useSignupForm() {
|
|
|
2348
2498
|
name,
|
|
2349
2499
|
setName,
|
|
2350
2500
|
nameError,
|
|
2501
|
+
markNameTouched: () => setTouchedName(true),
|
|
2351
2502
|
email,
|
|
2352
2503
|
setEmail,
|
|
2353
2504
|
emailError,
|
|
2505
|
+
markEmailTouched: () => setTouchedEmail(true),
|
|
2354
2506
|
password,
|
|
2355
2507
|
setPassword,
|
|
2356
2508
|
passwordError,
|
|
2509
|
+
markPasswordTouched: () => setTouchedPassword(true),
|
|
2510
|
+
formSubmitAttempted,
|
|
2357
2511
|
submit,
|
|
2358
2512
|
submitting,
|
|
2359
2513
|
canSubmit,
|
|
@@ -2372,13 +2526,17 @@ function useForgotForm() {
|
|
|
2372
2526
|
const [submitting, setSubmitting] = useState9(false);
|
|
2373
2527
|
const [sent, setSent] = useState9(false);
|
|
2374
2528
|
const [error, setError] = useState9(null);
|
|
2375
|
-
const
|
|
2529
|
+
const [touchedEmail, setTouchedEmail] = useState9(false);
|
|
2530
|
+
const [formSubmitAttempted, setFormSubmitAttempted] = useState9(false);
|
|
2531
|
+
const validateEmail = useMemo6(() => {
|
|
2376
2532
|
if (email.length === 0) return null;
|
|
2377
2533
|
if (!EMAIL_RE3.test(email)) return "Formato de e-mail inv\xE1lido.";
|
|
2378
2534
|
return null;
|
|
2379
2535
|
}, [email]);
|
|
2380
|
-
const
|
|
2536
|
+
const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
|
|
2537
|
+
const canSubmit = email.length > 0 && validateEmail === null && !submitting;
|
|
2381
2538
|
const submit = useCallback7(async () => {
|
|
2539
|
+
setFormSubmitAttempted(true);
|
|
2382
2540
|
if (!canSubmit) return false;
|
|
2383
2541
|
setSubmitting(true);
|
|
2384
2542
|
setError(null);
|
|
@@ -2397,6 +2555,8 @@ function useForgotForm() {
|
|
|
2397
2555
|
email,
|
|
2398
2556
|
setEmail,
|
|
2399
2557
|
emailError,
|
|
2558
|
+
markEmailTouched: () => setTouchedEmail(true),
|
|
2559
|
+
formSubmitAttempted,
|
|
2400
2560
|
submit,
|
|
2401
2561
|
submitting,
|
|
2402
2562
|
canSubmit,
|
|
@@ -2417,24 +2577,30 @@ function useResetForm() {
|
|
|
2417
2577
|
const [submitting, setSubmitting] = useState10(false);
|
|
2418
2578
|
const [done, setDone] = useState10(false);
|
|
2419
2579
|
const [error, setError] = useState10(null);
|
|
2580
|
+
const [touchedPassword, setTouchedPassword] = useState10(false);
|
|
2581
|
+
const [touchedConfirm, setTouchedConfirm] = useState10(false);
|
|
2582
|
+
const [formSubmitAttempted, setFormSubmitAttempted] = useState10(false);
|
|
2420
2583
|
useEffect8(() => {
|
|
2421
2584
|
if (typeof window === "undefined") return;
|
|
2422
2585
|
const params = new URLSearchParams(window.location.search);
|
|
2423
2586
|
const t = params.get("token");
|
|
2424
2587
|
setToken(t && t.length > 0 ? t : null);
|
|
2425
2588
|
}, []);
|
|
2426
|
-
const
|
|
2589
|
+
const validatePassword = useMemo7(() => {
|
|
2427
2590
|
if (password.length === 0) return null;
|
|
2428
2591
|
if (password.length < MIN_PASSWORD3) return `M\xEDnimo de ${MIN_PASSWORD3} caracteres.`;
|
|
2429
2592
|
return null;
|
|
2430
2593
|
}, [password]);
|
|
2431
|
-
const
|
|
2594
|
+
const validateConfirm = useMemo7(() => {
|
|
2432
2595
|
if (confirm.length === 0) return null;
|
|
2433
2596
|
if (confirm !== password) return "Senhas n\xE3o coincidem.";
|
|
2434
2597
|
return null;
|
|
2435
2598
|
}, [confirm, password]);
|
|
2436
|
-
const
|
|
2599
|
+
const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
|
|
2600
|
+
const confirmError = touchedConfirm || formSubmitAttempted ? validateConfirm : null;
|
|
2601
|
+
const canSubmit = token !== null && password.length >= MIN_PASSWORD3 && confirm === password && validatePassword === null && validateConfirm === null && !submitting && !done;
|
|
2437
2602
|
const submit = useCallback8(async () => {
|
|
2603
|
+
setFormSubmitAttempted(true);
|
|
2438
2604
|
if (!canSubmit || token === null) return;
|
|
2439
2605
|
setSubmitting(true);
|
|
2440
2606
|
setError(null);
|
|
@@ -2458,9 +2624,12 @@ function useResetForm() {
|
|
|
2458
2624
|
password,
|
|
2459
2625
|
setPassword,
|
|
2460
2626
|
passwordError,
|
|
2627
|
+
markPasswordTouched: () => setTouchedPassword(true),
|
|
2461
2628
|
confirm,
|
|
2462
2629
|
setConfirm,
|
|
2463
2630
|
confirmError,
|
|
2631
|
+
markConfirmTouched: () => setTouchedConfirm(true),
|
|
2632
|
+
formSubmitAttempted,
|
|
2464
2633
|
submit,
|
|
2465
2634
|
submitting,
|
|
2466
2635
|
canSubmit,
|
|
@@ -2607,20 +2776,20 @@ function useToast() {
|
|
|
2607
2776
|
|
|
2608
2777
|
// src/RouteBoundary.tsx
|
|
2609
2778
|
import { Routes as Routes2, Route as Route2 } from "react-router-dom";
|
|
2610
|
-
import { jsx as
|
|
2779
|
+
import { jsx as jsx24, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2611
2780
|
function RouteBoundary({ children }) {
|
|
2612
2781
|
return /* @__PURE__ */ jsxs17(Routes2, { children: [
|
|
2613
2782
|
children,
|
|
2614
|
-
/* @__PURE__ */
|
|
2783
|
+
/* @__PURE__ */ jsx24(Route2, { path: "*", element: /* @__PURE__ */ jsx24(DefaultNotFound, {}) })
|
|
2615
2784
|
] });
|
|
2616
2785
|
}
|
|
2617
2786
|
function DefaultNotFound() {
|
|
2618
|
-
return /* @__PURE__ */
|
|
2787
|
+
return /* @__PURE__ */ jsx24("div", { role: "alert", children: "P\xE1gina n\xE3o encontrada" });
|
|
2619
2788
|
}
|
|
2620
2789
|
|
|
2621
2790
|
// src/PreAuthShell.tsx
|
|
2622
2791
|
import { BrowserRouter as BrowserRouter2, MemoryRouter as MemoryRouter2, Routes as Routes3 } from "react-router-dom";
|
|
2623
|
-
import { jsx as
|
|
2792
|
+
import { jsx as jsx25 } from "react/jsx-runtime";
|
|
2624
2793
|
function PreAuthShell({
|
|
2625
2794
|
basename,
|
|
2626
2795
|
testRouter,
|
|
@@ -2628,9 +2797,9 @@ function PreAuthShell({
|
|
|
2628
2797
|
children
|
|
2629
2798
|
}) {
|
|
2630
2799
|
if (testRouter === "memory") {
|
|
2631
|
-
return /* @__PURE__ */
|
|
2800
|
+
return /* @__PURE__ */ jsx25(MemoryRouter2, { basename, initialEntries: testInitialEntries, children: /* @__PURE__ */ jsx25(Routes3, { children }) });
|
|
2632
2801
|
}
|
|
2633
|
-
return /* @__PURE__ */
|
|
2802
|
+
return /* @__PURE__ */ jsx25(BrowserRouter2, { basename, children: /* @__PURE__ */ jsx25(Routes3, { children }) });
|
|
2634
2803
|
}
|
|
2635
2804
|
|
|
2636
2805
|
// src/OnboardingFlow.tsx
|
|
@@ -2651,7 +2820,7 @@ function useOnboardingStep() {
|
|
|
2651
2820
|
}
|
|
2652
2821
|
|
|
2653
2822
|
// src/OnboardingFlow.tsx
|
|
2654
|
-
import { jsx as
|
|
2823
|
+
import { jsx as jsx26 } from "react/jsx-runtime";
|
|
2655
2824
|
var isFilled = (v) => v != null && v !== "";
|
|
2656
2825
|
var CURRENT_STEP_FIELD = "currentStep";
|
|
2657
2826
|
function readPersistedStepIdx(draft) {
|
|
@@ -2729,7 +2898,7 @@ function OnboardingFlow({
|
|
|
2729
2898
|
`[hook-template] OnboardingFlow: missing screen component for step '${step.id}' (expected key '${step.screen}' in screens prop)`
|
|
2730
2899
|
);
|
|
2731
2900
|
}
|
|
2732
|
-
return /* @__PURE__ */
|
|
2901
|
+
return /* @__PURE__ */ jsx26(OnboardingStepContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx26(Screen, {}) });
|
|
2733
2902
|
}
|
|
2734
2903
|
|
|
2735
2904
|
// src/hooks/useFeature.ts
|