@apps-in-toss/framework 1.2.0 → 1.2.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.js CHANGED
@@ -848,18 +848,18 @@ import {
848
848
  import * as appsInTossAsyncBridges from "@apps-in-toss/native-modules/async-bridges";
849
849
  import * as appsInTossConstantBridges from "@apps-in-toss/native-modules/constant-bridges";
850
850
  import * as appsInTossEventBridges from "@apps-in-toss/native-modules/event-bridges";
851
- import { getSchemeUri as getSchemeUri6, useGraniteEvent } from "@granite-js/react-native";
851
+ import { getSchemeUri as getSchemeUri6 } from "@granite-js/react-native";
852
852
  import { ExternalWebViewScreen, tdsEvent } from "@toss/tds-react-native";
853
853
  import { useSafeAreaBottom, useSafeAreaTop as useSafeAreaTop2, useTopNavigation } from "@toss/tds-react-native/private";
854
- import { useMemo as useMemo4, useState as useState5 } from "react";
855
- import { Platform as Platform4 } from "react-native";
854
+ import { useEffect as useEffect11, useMemo as useMemo5, useRef as useRef6, useState as useState5 } from "react";
855
+ import { BackHandler as BackHandler2, Platform as Platform4 } from "react-native";
856
856
 
857
857
  // src/components/GameWebView.tsx
858
858
  import { setIosSwipeGestureEnabled as setIosSwipeGestureEnabled2, appsInTossEvent as appsInTossEvent2, getOperationalEnvironment as getOperationalEnvironment2 } from "@apps-in-toss/native-modules";
859
859
  import {
860
860
  WebView as PlainWebView
861
861
  } from "@granite-js/native/react-native-webview";
862
- import { forwardRef, useEffect as useEffect10, useState as useState3 } from "react";
862
+ import { forwardRef, useEffect as useEffect9, useState as useState3 } from "react";
863
863
  import { Platform as Platform3 } from "react-native";
864
864
 
865
865
  // src/components/GameProfile.tsx
@@ -1181,8 +1181,8 @@ import { closeView as closeView3 } from "@granite-js/react-native";
1181
1181
  import { PageNavbar, useDialog as useDialog5 } from "@toss/tds-react-native";
1182
1182
  import { NavigationRightContent, useSafeAreaTop } from "@toss/tds-react-native/private";
1183
1183
  import { josa as josa3 } from "es-hangul";
1184
- import { useCallback as useCallback5, useEffect as useEffect9 } from "react";
1185
- import { BackHandler as BackHandler2, Platform as Platform2, View as View2 } from "react-native";
1184
+ import { useCallback as useCallback5 } from "react";
1185
+ import { Platform as Platform2, View as View2 } from "react-native";
1186
1186
  import { Fragment as Fragment6, jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
1187
1187
  function GameWebviewNavigationBar() {
1188
1188
  const safeAreaTop = useSafeAreaTop();
@@ -1194,6 +1194,7 @@ function GameWebviewNavigationBar() {
1194
1194
  const parsedNavigationBar = global2.navigationBar != null ? safeParseNavigationBar(global2.navigationBar) : null;
1195
1195
  const initialAccessoryButton = parsedNavigationBar?.initialAccessoryButton;
1196
1196
  const handleGameWebviewClose = useCallback5(async () => {
1197
+ logging.closeButtonClick();
1197
1198
  const isConfirmed = await openConfirm({
1198
1199
  title: `${josa3(global2.brandDisplayName, "\uC744/\uB97C")} \uC885\uB8CC\uD560\uAE4C\uC694?`,
1199
1200
  leftButton: "\uCDE8\uC18C",
@@ -1207,14 +1208,6 @@ function GameWebviewNavigationBar() {
1207
1208
  closeView3();
1208
1209
  }
1209
1210
  }, [captureExitLog, global2.brandDisplayName, logging, openConfirm]);
1210
- useEffect9(() => {
1211
- const handleAndroidBackEvent = () => {
1212
- handleGameWebviewClose();
1213
- return true;
1214
- };
1215
- BackHandler2.addEventListener("hardwareBackPress", handleAndroidBackEvent);
1216
- return () => BackHandler2.removeEventListener("hardwareBackPress", handleAndroidBackEvent);
1217
- }, [handleGameWebviewClose]);
1218
1211
  return /* @__PURE__ */ jsxs4(Fragment6, { children: [
1219
1212
  /* @__PURE__ */ jsx9(PageNavbar, { preference: { type: "none" } }),
1220
1213
  /* @__PURE__ */ jsx9(
@@ -1239,7 +1232,6 @@ function GameWebviewNavigationBar() {
1239
1232
  fixedRightButton: initialAccessoryButton,
1240
1233
  onPressDots: openMoreButtonBottomSheet,
1241
1234
  onPressClose: () => {
1242
- logging.closeButtonClick();
1243
1235
  handleGameWebviewClose();
1244
1236
  },
1245
1237
  theme: "dark"
@@ -1254,7 +1246,7 @@ function GameWebviewNavigationBar() {
1254
1246
  import { Fragment as Fragment7, jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
1255
1247
  var GameWebView = forwardRef(function GameWebView2(props, ref) {
1256
1248
  const [isEntryMessageExited, setIsEntryMessageExited] = useState3(false);
1257
- useEffect10(() => {
1249
+ useEffect9(() => {
1258
1250
  if (Platform3.OS === "ios") {
1259
1251
  setIosSwipeGestureEnabled2({ isEnabled: false });
1260
1252
  return () => {
@@ -1263,7 +1255,7 @@ var GameWebView = forwardRef(function GameWebView2(props, ref) {
1263
1255
  }
1264
1256
  return;
1265
1257
  }, []);
1266
- useEffect10(() => {
1258
+ useEffect9(() => {
1267
1259
  appsInTossEvent2.addEventListener("entryMessageExited", {
1268
1260
  onEvent: () => {
1269
1261
  setIsEntryMessageExited(true);
@@ -1277,12 +1269,10 @@ var GameWebView = forwardRef(function GameWebView2(props, ref) {
1277
1269
  });
1278
1270
 
1279
1271
  // src/components/PartnerWebView.tsx
1280
- import { closeView as closeView5 } from "@apps-in-toss/native-modules";
1281
1272
  import {
1282
1273
  WebView as PlainWebView2
1283
1274
  } from "@granite-js/native/react-native-webview";
1284
- import { forwardRef as forwardRef2, useCallback as useCallback8, useEffect as useEffect11, useRef as useRef4 } from "react";
1285
- import { BackHandler as BackHandler3 } from "react-native";
1275
+ import { forwardRef as forwardRef2 } from "react";
1286
1276
 
1287
1277
  // src/components/NavigationBar/PartnerWebviewNavigationBar.tsx
1288
1278
  import { closeView as closeView4 } from "@granite-js/react-native";
@@ -1291,7 +1281,7 @@ import { NavigationBackButton as NavigationBackButton2, NavigationLeft as Naviga
1291
1281
  import { josa as josa4 } from "es-hangul";
1292
1282
  import { useCallback as useCallback6 } from "react";
1293
1283
  import { jsx as jsx11 } from "react/jsx-runtime";
1294
- function PartnerWebviewNavigationBar({ handleBackEvent, handleHomeIconButtonClick }) {
1284
+ function PartnerWebviewNavigationBar({ onBackButtonClick, onHomeButtonClick }) {
1295
1285
  const globals = getAppsInTossGlobals();
1296
1286
  const { captureExitLog } = useCaptureExitLog();
1297
1287
  const logging = useNavigationBarLogging();
@@ -1301,10 +1291,6 @@ function PartnerWebviewNavigationBar({ handleBackEvent, handleHomeIconButtonClic
1301
1291
  const withHomeButton = parsedNavigationBar?.withHomeButton ?? false;
1302
1292
  const withBackButton = parsedNavigationBar?.withBackButton ?? true;
1303
1293
  const initialAccessoryButton = parsedNavigationBar?.initialAccessoryButton;
1304
- const handlePressTitle = useCallback6(() => {
1305
- logging.homeButtonClick();
1306
- handleHomeIconButtonClick();
1307
- }, [handleHomeIconButtonClick, logging]);
1308
1294
  const handleClose = useCallback6(async () => {
1309
1295
  logging.closeButtonClick();
1310
1296
  const isConfirmed = await openConfirm({
@@ -1327,200 +1313,26 @@ function PartnerWebviewNavigationBar({ handleBackEvent, handleHomeIconButtonClic
1327
1313
  icon: toIcon(globals.brandIcon),
1328
1314
  onPressDots: openMoreButtonBottomSheet,
1329
1315
  contentVisible: true,
1330
- onPressTitle: withHomeButton ? handlePressTitle : void 0,
1316
+ onPressTitle: withHomeButton ? onHomeButtonClick : void 0,
1331
1317
  onPressClose: handleClose,
1332
1318
  withHome: withHomeButton,
1333
1319
  fixedRightButton: initialAccessoryButton,
1334
- children: /* @__PURE__ */ jsx11(NavigationLeft2, { visible: withBackButton, children: /* @__PURE__ */ jsx11(NavigationBackButton2, { onPress: handleBackEvent, canGoBack: false }) })
1320
+ children: /* @__PURE__ */ jsx11(NavigationLeft2, { visible: withBackButton, children: /* @__PURE__ */ jsx11(NavigationBackButton2, { onPress: onBackButtonClick, canGoBack: false }) })
1335
1321
  }
1336
1322
  ) });
1337
1323
  }
1338
1324
 
1339
- // src/core/utils/mergeRefs.ts
1340
- function mergeRefs(...refs) {
1341
- return (value) => {
1342
- refs.forEach((ref) => {
1343
- if (typeof ref === "function") {
1344
- ref(value);
1345
- } else if (ref != null) {
1346
- ref.current = value;
1347
- }
1348
- });
1349
- };
1350
- }
1351
-
1352
- // src/hooks/useWebviewHistoryStack.tsx
1353
- import { useCallback as useCallback7, useMemo as useMemo2, useReducer } from "react";
1354
- var INITIAL_STATE = { stack: [], index: -1 };
1355
- function reducer(state, action) {
1356
- switch (action.type) {
1357
- case "NAVIGATION_CHANGE": {
1358
- const { url, canGoForward } = action;
1359
- if (state.stack.length === 0) {
1360
- return { stack: [url], index: 0 };
1361
- }
1362
- const { stack, index } = state;
1363
- const cur = stack[index];
1364
- if (url === cur) {
1365
- return state;
1366
- }
1367
- const prev = index > 0 ? stack[index - 1] : void 0;
1368
- const next = index < stack.length - 1 ? stack[index + 1] : void 0;
1369
- if (prev && url === prev && canGoForward) {
1370
- return { ...state, index: index - 1 };
1371
- }
1372
- if (next && url === next) {
1373
- return { ...state, index: index + 1 };
1374
- }
1375
- const base = stack.slice(0, index + 1);
1376
- const nextStack = [...base, url];
1377
- return { stack: nextStack, index: nextStack.length - 1 };
1378
- }
1379
- default:
1380
- return state;
1381
- }
1382
- }
1383
- function useWebViewHistory() {
1384
- const [state, dispatch] = useReducer(reducer, INITIAL_STATE);
1385
- const onNavigationStateChange = useCallback7(({ url, canGoForward: canGoForward2 }) => {
1386
- dispatch({ type: "NAVIGATION_CHANGE", url, canGoForward: canGoForward2 });
1387
- }, []);
1388
- const { canGoBack, canGoForward } = useMemo2(() => {
1389
- const canBack = state.index > 0;
1390
- const canFwd = state.index >= 0 && state.index < state.stack.length - 1;
1391
- return { canGoBack: canBack, canGoForward: canFwd };
1392
- }, [state.index, state.stack.length]);
1393
- return { onNavigationStateChange, canGoBack, canGoForward };
1394
- }
1395
-
1396
- // src/utils/log.ts
1397
- import { eventLog as eventLogNative } from "@apps-in-toss/native-modules";
1398
- import { getSchemeUri as getSchemeUri5 } from "@granite-js/react-native";
1399
-
1400
- // src/utils/extractDateFromUUIDv7.ts
1401
- var extractDateFromUUIDv7 = (uuid) => {
1402
- const timestampHex = uuid.split("-").join("").slice(0, 12);
1403
- const timestamp = Number.parseInt(timestampHex, 16);
1404
- return new Date(timestamp);
1405
- };
1406
-
1407
- // src/utils/log.ts
1408
- var getGroupId = (url) => {
1409
- try {
1410
- const urlObject = new URL(url);
1411
- return {
1412
- groupId: urlObject.pathname,
1413
- search: urlObject.search.startsWith("?") ? urlObject.search.substring(1) : urlObject.search
1414
- };
1415
- } catch {
1416
- return {
1417
- groupId: "unknown",
1418
- search: "unknown"
1419
- };
1420
- }
1421
- };
1422
- var getReferrer = () => {
1423
- try {
1424
- const referrer = new URL(getSchemeUri5());
1425
- return referrer.searchParams.get("referrer");
1426
- } catch {
1427
- return "";
1428
- }
1429
- };
1430
- var trackScreen = (url) => {
1431
- const { groupId, search } = getGroupId(url);
1432
- const log = {
1433
- log_type: "screen",
1434
- log_name: `${groupId}::screen`,
1435
- params: {
1436
- search,
1437
- referrer: getReferrer(),
1438
- deployment_id: env.getDeploymentId(),
1439
- deployment_timestamp: extractDateFromUUIDv7(env.getDeploymentId()).getTime()
1440
- }
1441
- };
1442
- return eventLogNative(log);
1443
- };
1444
-
1445
1325
  // src/components/PartnerWebView.tsx
1446
1326
  import { Fragment as Fragment8, jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
1447
- var PartnerWebView = forwardRef2(
1448
- function PartnerWebViewScreen(webViewProps, ref) {
1449
- const webViewRef = useRef4(null);
1450
- const refs = mergeRefs(ref, webViewRef);
1451
- const { captureExitLog } = useCaptureExitLog();
1452
- const { canGoBack, onNavigationStateChange } = useWebViewHistory();
1453
- const historyBackScript = `
1454
- (function() {
1455
- window.history.back();
1456
- true;
1457
- })();
1458
- `;
1459
- const historyHomeScript = `
1460
- (function() {
1461
- window.location.href = '/';
1462
- true;
1463
- })();
1464
- `;
1465
- const handleBackEvent = useCallback8(() => {
1466
- if (canGoBack) {
1467
- webViewRef.current?.injectJavaScript(historyBackScript);
1468
- } else {
1469
- captureExitLog(Date.now());
1470
- closeView5();
1471
- }
1472
- }, [canGoBack, captureExitLog, historyBackScript]);
1473
- useEffect11(() => {
1474
- const handleAndroidBackEvent = () => {
1475
- if (canGoBack) {
1476
- webViewRef.current?.injectJavaScript(historyBackScript);
1477
- return true;
1478
- } else {
1479
- captureExitLog(Date.now());
1480
- return false;
1481
- }
1482
- };
1483
- BackHandler3.addEventListener("hardwareBackPress", handleAndroidBackEvent);
1484
- return () => BackHandler3.removeEventListener("hardwareBackPress", handleAndroidBackEvent);
1485
- }, [canGoBack, captureExitLog, historyBackScript]);
1486
- const handleHomeIconButtonClick = useCallback8(() => {
1487
- webViewRef.current?.injectJavaScript(historyHomeScript);
1488
- }, [historyHomeScript]);
1489
- const handleNavigationStateChange = useCallback8(
1490
- (event) => {
1491
- if (event.url) {
1492
- trackScreen(event.url);
1493
- }
1494
- onNavigationStateChange(event);
1495
- },
1496
- [onNavigationStateChange]
1497
- );
1498
- return /* @__PURE__ */ jsxs6(Fragment8, { children: [
1499
- /* @__PURE__ */ jsx12(
1500
- PartnerWebviewNavigationBar,
1501
- {
1502
- handleBackEvent,
1503
- handleHomeIconButtonClick
1504
- }
1505
- ),
1506
- /* @__PURE__ */ jsx12(
1507
- PlainWebView2,
1508
- {
1509
- ref: refs,
1510
- ...webViewProps,
1511
- style: { flex: 1 },
1512
- onNavigationStateChange: (event) => {
1513
- webViewProps?.onNavigationStateChange?.(event);
1514
- handleNavigationStateChange(event);
1515
- }
1516
- }
1517
- )
1518
- ] });
1519
- }
1520
- );
1327
+ var PartnerWebView = forwardRef2(function PartnerWebViewScreen({ onBackButtonClick, onHomeButtonClick, ...webViewProps }, ref) {
1328
+ return /* @__PURE__ */ jsxs6(Fragment8, { children: [
1329
+ /* @__PURE__ */ jsx12(PartnerWebviewNavigationBar, { onBackButtonClick, onHomeButtonClick }),
1330
+ /* @__PURE__ */ jsx12(PlainWebView2, { ref, ...webViewProps, style: { flex: 1 } })
1331
+ ] });
1332
+ });
1521
1333
 
1522
1334
  // src/bridge-handler/useBridgeHandler.tsx
1523
- import { useCallback as useCallback9, useMemo as useMemo3, useRef as useRef5 } from "react";
1335
+ import { useCallback as useCallback7, useMemo as useMemo2, useRef as useRef4 } from "react";
1524
1336
  function serializeError(error) {
1525
1337
  return JSON.stringify(error, (_, value) => {
1526
1338
  if (value instanceof Error) {
@@ -1573,8 +1385,8 @@ function useBridgeHandler({
1573
1385
  eventListenerMap,
1574
1386
  injectedJavaScript: originalInjectedJavaScript
1575
1387
  }) {
1576
- const ref = useRef5(null);
1577
- const injectedJavaScript = useMemo3(
1388
+ const ref = useRef4(null);
1389
+ const injectedJavaScript = useMemo2(
1578
1390
  () => [
1579
1391
  `window.__CONSTANT_HANDLER_MAP = ${JSON.stringify(
1580
1392
  Object.entries(constantHandlerMap).reduce(
@@ -1601,7 +1413,7 @@ function useBridgeHandler({
1601
1413
  window.__GRANITE_NATIVE_EMITTER.emit('${functionName}/onError/${eventId}', ${serializedError});
1602
1414
  `);
1603
1415
  };
1604
- const $onMessage = useCallback9(
1416
+ const $onMessage = useCallback7(
1605
1417
  async (e) => {
1606
1418
  onMessage?.(e);
1607
1419
  const data = JSON.parse(e.nativeEvent.data);
@@ -1651,6 +1463,155 @@ function useBridgeHandler({
1651
1463
  };
1652
1464
  }
1653
1465
 
1466
+ // src/core/hooks/useWebBackHandler.tsx
1467
+ import { closeView as closeView5, useBackEventState } from "@granite-js/react-native";
1468
+ import { useDialog as useDialog7 } from "@toss/tds-react-native";
1469
+ import { josa as josa5 } from "es-hangul";
1470
+ import { useCallback as useCallback9, useMemo as useMemo4 } from "react";
1471
+
1472
+ // src/hooks/useWebviewHistoryStack.tsx
1473
+ import { useCallback as useCallback8, useMemo as useMemo3, useReducer } from "react";
1474
+ var INITIAL_STATE = { stack: [], index: -1 };
1475
+ function reducer(state, action) {
1476
+ switch (action.type) {
1477
+ case "NAVIGATION_CHANGE": {
1478
+ const { url, canGoForward } = action;
1479
+ if (state.stack.length === 0) {
1480
+ return { stack: [url], index: 0 };
1481
+ }
1482
+ const { stack, index } = state;
1483
+ const cur = stack[index];
1484
+ if (url === cur) {
1485
+ return state;
1486
+ }
1487
+ const prev = index > 0 ? stack[index - 1] : void 0;
1488
+ const next = index < stack.length - 1 ? stack[index + 1] : void 0;
1489
+ if (prev && url === prev && canGoForward) {
1490
+ return { ...state, index: index - 1 };
1491
+ }
1492
+ if (next && url === next) {
1493
+ return { ...state, index: index + 1 };
1494
+ }
1495
+ const base = stack.slice(0, index + 1);
1496
+ const nextStack = [...base, url];
1497
+ return { stack: nextStack, index: nextStack.length - 1 };
1498
+ }
1499
+ default:
1500
+ return state;
1501
+ }
1502
+ }
1503
+ function useWebViewHistory() {
1504
+ const [state, dispatch] = useReducer(reducer, INITIAL_STATE);
1505
+ const onNavigationStateChange = useCallback8(({ url, canGoForward: canGoForward2 }) => {
1506
+ dispatch({ type: "NAVIGATION_CHANGE", url, canGoForward: canGoForward2 });
1507
+ }, []);
1508
+ const { canGoBack, canGoForward } = useMemo3(() => {
1509
+ const canBack = state.index > 0;
1510
+ const canFwd = state.index >= 0 && state.index < state.stack.length - 1;
1511
+ return { canGoBack: canBack, canGoForward: canFwd };
1512
+ }, [state.index, state.stack.length]);
1513
+ return { onNavigationStateChange, hasHistory: canGoBack, canGoForward };
1514
+ }
1515
+
1516
+ // src/core/hooks/useWebBackHandler.tsx
1517
+ var HISTORY_BACK_SCRIPT = `
1518
+ (function() {
1519
+ window.history.back();
1520
+ true;
1521
+ })();
1522
+ `;
1523
+ var HISTORY_HOME_SCRIPT = `
1524
+ (function() {
1525
+ window.location.href = '/';
1526
+ true;
1527
+ })();
1528
+ `;
1529
+ function useWebBackHandler(webViewRef) {
1530
+ const { captureExitLog } = useCaptureExitLog();
1531
+ const { hasHistory, onNavigationStateChange } = useWebViewHistory();
1532
+ const {
1533
+ handlersRef: webBackHandlersRef,
1534
+ hasBackEvent: hasWebBackEvent,
1535
+ addEventListener: addWebBackEventListener,
1536
+ removeEventListener: removeWebBackEventListener
1537
+ } = useBackEventState();
1538
+ const logging = useNavigationBarLogging();
1539
+ const { openConfirm } = useDialog7();
1540
+ const global2 = getAppsInTossGlobals();
1541
+ const addEventListener = useCallback9(
1542
+ (handler) => {
1543
+ addWebBackEventListener(handler);
1544
+ },
1545
+ [addWebBackEventListener]
1546
+ );
1547
+ const removeEventListener = useCallback9(
1548
+ (handler) => {
1549
+ removeWebBackEventListener(handler);
1550
+ },
1551
+ [removeWebBackEventListener]
1552
+ );
1553
+ const handleWebBack = useCallback9(async () => {
1554
+ if (hasWebBackEvent) {
1555
+ for (const handler of webBackHandlersRef) {
1556
+ handler();
1557
+ }
1558
+ return;
1559
+ }
1560
+ if (hasHistory) {
1561
+ webViewRef.current?.injectJavaScript(HISTORY_BACK_SCRIPT);
1562
+ } else {
1563
+ const isConfirmed = await openConfirm({
1564
+ title: `${josa5(global2.brandDisplayName, "\uC744/\uB97C")} \uC885\uB8CC\uD560\uAE4C\uC694?`,
1565
+ leftButton: "\uCDE8\uC18C",
1566
+ rightButton: "\uC885\uB8CC\uD558\uAE30",
1567
+ closeOnDimmerClick: true,
1568
+ onEntered: logging.closePopupShow
1569
+ });
1570
+ logging.closePopupCtaClick(isConfirmed);
1571
+ if (isConfirmed) {
1572
+ captureExitLog(Date.now());
1573
+ closeView5();
1574
+ }
1575
+ }
1576
+ }, [
1577
+ captureExitLog,
1578
+ global2.brandDisplayName,
1579
+ hasHistory,
1580
+ hasWebBackEvent,
1581
+ webBackHandlersRef,
1582
+ logging,
1583
+ openConfirm,
1584
+ webViewRef
1585
+ ]);
1586
+ const handleWebHome = useCallback9(() => {
1587
+ logging.homeButtonClick();
1588
+ if (hasWebBackEvent) {
1589
+ for (const handler of webBackHandlersRef) {
1590
+ handler();
1591
+ }
1592
+ return;
1593
+ }
1594
+ webViewRef.current?.injectJavaScript(HISTORY_HOME_SCRIPT);
1595
+ }, [hasWebBackEvent, webBackHandlersRef, logging, webViewRef]);
1596
+ return useMemo4(
1597
+ () => ({ addEventListener, removeEventListener, handleWebBack, handleWebHome, onNavigationStateChange }),
1598
+ [addEventListener, removeEventListener, handleWebBack, handleWebHome, onNavigationStateChange]
1599
+ );
1600
+ }
1601
+
1602
+ // src/core/utils/mergeRefs.ts
1603
+ function mergeRefs(...refs) {
1604
+ return (value) => {
1605
+ refs.forEach((ref) => {
1606
+ if (typeof ref === "function") {
1607
+ ref(value);
1608
+ } else if (ref != null) {
1609
+ ref.current = value;
1610
+ }
1611
+ });
1612
+ };
1613
+ }
1614
+
1654
1615
  // src/hooks/useCreateUserAgent.ts
1655
1616
  import { getPlatformOS, getTossAppVersion } from "@apps-in-toss/native-modules";
1656
1617
  import { useWindowDimensions } from "react-native";
@@ -1810,11 +1771,11 @@ function useCreateUserAgent({
1810
1771
  // src/hooks/useGeolocation.ts
1811
1772
  import { startUpdateLocation } from "@apps-in-toss/native-modules";
1812
1773
  import { useVisibility as useVisibility3 } from "@granite-js/react-native";
1813
- import { useEffect as useEffect12, useState as useState4 } from "react";
1774
+ import { useEffect as useEffect10, useState as useState4 } from "react";
1814
1775
  function useGeolocation({ accuracy, distanceInterval, timeInterval }) {
1815
1776
  const isVisible = useVisibility3();
1816
1777
  const [location, setLocation] = useState4(null);
1817
- useEffect12(() => {
1778
+ useEffect10(() => {
1818
1779
  if (!isVisible) {
1819
1780
  return;
1820
1781
  }
@@ -1831,6 +1792,85 @@ function useGeolocation({ accuracy, distanceInterval, timeInterval }) {
1831
1792
  return location;
1832
1793
  }
1833
1794
 
1795
+ // src/hooks/useWaitForReturnNavigator.tsx
1796
+ import { useNavigation as useNavigation2, useVisibilityChange } from "@granite-js/react-native";
1797
+ import { useCallback as useCallback10, useRef as useRef5 } from "react";
1798
+ function useWaitForReturnNavigator() {
1799
+ const callbacks = useRef5([]).current;
1800
+ const navigation = useNavigation2();
1801
+ const startNavigating = useCallback10(
1802
+ (route, params) => {
1803
+ return new Promise((resolve) => {
1804
+ callbacks.push(resolve);
1805
+ navigation.navigate(route, params);
1806
+ });
1807
+ },
1808
+ [callbacks, navigation]
1809
+ );
1810
+ const handleVisibilityChange = useCallback10(
1811
+ (state) => {
1812
+ if (state === "visible" && callbacks.length > 0) {
1813
+ for (const callback of callbacks) {
1814
+ callback();
1815
+ }
1816
+ callbacks.splice(0, callbacks.length);
1817
+ }
1818
+ },
1819
+ [callbacks]
1820
+ );
1821
+ useVisibilityChange(handleVisibilityChange);
1822
+ return startNavigating;
1823
+ }
1824
+
1825
+ // src/utils/log.ts
1826
+ import { eventLog as eventLogNative } from "@apps-in-toss/native-modules";
1827
+ import { getSchemeUri as getSchemeUri5 } from "@granite-js/react-native";
1828
+
1829
+ // src/utils/extractDateFromUUIDv7.ts
1830
+ var extractDateFromUUIDv7 = (uuid) => {
1831
+ const timestampHex = uuid.split("-").join("").slice(0, 12);
1832
+ const timestamp = Number.parseInt(timestampHex, 16);
1833
+ return new Date(timestamp);
1834
+ };
1835
+
1836
+ // src/utils/log.ts
1837
+ var getGroupId = (url) => {
1838
+ try {
1839
+ const urlObject = new URL(url);
1840
+ return {
1841
+ groupId: urlObject.pathname,
1842
+ search: urlObject.search.startsWith("?") ? urlObject.search.substring(1) : urlObject.search
1843
+ };
1844
+ } catch {
1845
+ return {
1846
+ groupId: "unknown",
1847
+ search: "unknown"
1848
+ };
1849
+ }
1850
+ };
1851
+ var getReferrer = () => {
1852
+ try {
1853
+ const referrer = new URL(getSchemeUri5());
1854
+ return referrer.searchParams.get("referrer");
1855
+ } catch {
1856
+ return "";
1857
+ }
1858
+ };
1859
+ var trackScreen = (url) => {
1860
+ const { groupId, search } = getGroupId(url);
1861
+ const log = {
1862
+ log_type: "screen",
1863
+ log_name: `${groupId}::screen`,
1864
+ params: {
1865
+ search,
1866
+ referrer: getReferrer(),
1867
+ deployment_id: env.getDeploymentId(),
1868
+ deployment_timestamp: extractDateFromUUIDv7(env.getDeploymentId()).getTime()
1869
+ }
1870
+ };
1871
+ return eventLogNative(log);
1872
+ };
1873
+
1834
1874
  // src/components/WebView.tsx
1835
1875
  import { jsx as jsx13 } from "react/jsx-runtime";
1836
1876
  var operationalEnvironment = appsInTossConstantBridges.getOperationalEnvironment();
@@ -1866,8 +1906,9 @@ function WebView({ type, local, onMessage, ...props }) {
1866
1906
  if (!TYPES.includes(type)) {
1867
1907
  throw new Error(`Invalid WebView type: '${type}'`);
1868
1908
  }
1869
- const graniteEvent = useGraniteEvent();
1870
- const uri = useMemo4(() => getWebViewUri(local), [local]);
1909
+ const webViewRef = useRef6(null);
1910
+ const webBackHandler = useWebBackHandler(webViewRef);
1911
+ const uri = useMemo5(() => getWebViewUri(local), [local]);
1871
1912
  const top = useSafeAreaTop2();
1872
1913
  const bottom = useSafeAreaBottom();
1873
1914
  const global2 = getAppsInTossGlobals();
@@ -1889,7 +1930,12 @@ function WebView({ type, local, onMessage, ...props }) {
1889
1930
  eventListenerMap: {
1890
1931
  ...appsInTossEventBridges,
1891
1932
  navigationAccessoryEvent: ({ onEvent, onError }) => tdsEvent.addEventListener("navigationAccessoryEvent", { onEvent, onError }),
1892
- backEvent: ({ onEvent, onError, options }) => graniteEvent.addEventListener("backEvent", { onEvent, onError, options }),
1933
+ backEvent: ({ onEvent }) => {
1934
+ webBackHandler.addEventListener(onEvent);
1935
+ return () => {
1936
+ webBackHandler.removeEventListener(onEvent);
1937
+ };
1938
+ },
1893
1939
  entryMessageExited: ({ onEvent, onError }) => appsInTossEvent3.addEventListener("entryMessageExited", { onEvent, onError }),
1894
1940
  updateLocationEvent: ({ onEvent, onError, options }) => appsInTossEvent3.addEventListener("updateLocationEvent", { onEvent, onError, options }),
1895
1941
  /** @internal */
@@ -1953,7 +1999,7 @@ function WebView({ type, local, onMessage, ...props }) {
1953
1999
  getCompletedOrRefundedOrders: IAP.getCompletedOrRefundedOrders
1954
2000
  }
1955
2001
  });
1956
- const headerPropForExternalWebView = useMemo4(() => {
2002
+ const headerPropForExternalWebView = useMemo5(() => {
1957
2003
  const parsedNavigationBar = global2.navigationBar != null ? safeParseNavigationBar(global2.navigationBar) : null;
1958
2004
  const initialAccessoryButton = parsedNavigationBar?.initialAccessoryButton;
1959
2005
  const withBackButton = parsedNavigationBar?.withBackButton ?? true;
@@ -1973,10 +2019,19 @@ function WebView({ type, local, onMessage, ...props }) {
1973
2019
  const userAgent = useCreateUserAgent({
1974
2020
  colorPreference: "light"
1975
2021
  });
2022
+ const refs = mergeRefs(handler.ref, webViewRef);
2023
+ useEffect11(() => {
2024
+ const callback = () => {
2025
+ webBackHandler.handleWebBack();
2026
+ return true;
2027
+ };
2028
+ BackHandler2.addEventListener("hardwareBackPress", callback);
2029
+ return () => BackHandler2.removeEventListener("hardwareBackPress", callback);
2030
+ }, [webBackHandler]);
1976
2031
  return /* @__PURE__ */ jsx13(
1977
2032
  BaseWebView,
1978
2033
  {
1979
- ref: handler.ref,
2034
+ ref: refs,
1980
2035
  ...props,
1981
2036
  ...headerPropForExternalWebView,
1982
2037
  source: {
@@ -1986,6 +2041,15 @@ function WebView({ type, local, onMessage, ...props }) {
1986
2041
  "User-Agent": userAgent
1987
2042
  }
1988
2043
  },
2044
+ onHomeButtonClick: webBackHandler.handleWebHome,
2045
+ onBackButtonClick: webBackHandler.handleWebBack,
2046
+ onNavigationStateChange: (event) => {
2047
+ if (event.url) {
2048
+ trackScreen(event.url);
2049
+ }
2050
+ props.onNavigationStateChange?.(event);
2051
+ webBackHandler.onNavigationStateChange(event);
2052
+ },
1989
2053
  userAgent: Platform4.OS === "ios" ? userAgent : void 0,
1990
2054
  sharedCookiesEnabled: true,
1991
2055
  webviewDebuggingEnabled: webViewDebuggingEnabled,
@@ -2001,7 +2065,7 @@ function WebView({ type, local, onMessage, ...props }) {
2001
2065
 
2002
2066
  // src/index.ts
2003
2067
  export * from "@apps-in-toss/analytics";
2004
- import { useTopNavigation as useTopNavigation2 } from "@toss/tds-react-native/private";
2068
+ import { useTopNavigation as useTopNavigation2, useOverlay as useOverlay3, OverlayProvider } from "@toss/tds-react-native/private";
2005
2069
  export * from "@apps-in-toss/native-modules";
2006
2070
  export * from "@apps-in-toss/types";
2007
2071
  var Analytics2 = {
@@ -2014,9 +2078,12 @@ export {
2014
2078
  Analytics2 as Analytics,
2015
2079
  AppsInToss,
2016
2080
  INTERNAL__onVisibilityChangedByTransparentServiceWeb,
2081
+ OverlayProvider,
2017
2082
  WebView,
2018
2083
  env,
2019
2084
  useCreateUserAgent,
2020
2085
  useGeolocation,
2021
- useTopNavigation2 as useTopNavigation
2086
+ useOverlay3 as useOverlay,
2087
+ useTopNavigation2 as useTopNavigation,
2088
+ useWaitForReturnNavigator
2022
2089
  };