@apps-in-toss/framework 0.0.0-dev.1743134070910 → 0.0.0-dev.1744801739343

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 CHANGED
@@ -32,6 +32,7 @@ var src_exports = {};
32
32
  __export(src_exports, {
33
33
  Accuracy: () => Accuracy2,
34
34
  AppsInToss: () => AppsInToss,
35
+ Storage: () => Storage,
35
36
  TossPay: () => TossPay,
36
37
  WebView: () => WebView,
37
38
  appLogin: () => appLogin,
@@ -40,7 +41,9 @@ __export(src_exports, {
40
41
  fetchContacts: () => fetchContacts,
41
42
  getClipboardText: () => getClipboardText,
42
43
  getCurrentLocation: () => getCurrentLocation,
44
+ getDeviceId: () => getDeviceId,
43
45
  getOperationalEnvironment: () => getOperationalEnvironment,
46
+ getTossAppVersion: () => getTossAppVersion,
44
47
  openCamera: () => openCamera,
45
48
  setClipboardText: () => setClipboardText,
46
49
  startUpdateLocation: () => startUpdateLocation,
@@ -282,6 +285,39 @@ function getOperationalEnvironment() {
282
285
  return AppsInTossModule.operationalEnvironment;
283
286
  }
284
287
 
288
+ // src/native-modules/getTossAppVersion.ts
289
+ function getTossAppVersion() {
290
+ return AppsInTossModule.tossAppVersion;
291
+ }
292
+
293
+ // src/native-modules/getDeviceId.ts
294
+ function getDeviceId() {
295
+ return AppsInTossModule.deviceId;
296
+ }
297
+
298
+ // src/native-modules/storage.ts
299
+ function getItem(key) {
300
+ return AppsInTossModule.getStorageItem({ key });
301
+ }
302
+ function setItem(key, value) {
303
+ return AppsInTossModule.setStorageItem({
304
+ key,
305
+ value
306
+ });
307
+ }
308
+ function removeItem(key) {
309
+ return AppsInTossModule.removeStorageItem({ key });
310
+ }
311
+ function clearItems() {
312
+ return AppsInTossModule.clearStorage({});
313
+ }
314
+ var Storage = {
315
+ getItem,
316
+ setItem,
317
+ removeItem,
318
+ clearItems
319
+ };
320
+
285
321
  // src/native-modules/index.ts
286
322
  var TossPay = {
287
323
  checkoutPayment,
@@ -289,16 +325,19 @@ var TossPay = {
289
325
  };
290
326
 
291
327
  // src/components/WebView.tsx
292
- var import_react_native11 = require("@toss-design-system/react-native");
293
- var import_react3 = require("react");
328
+ var import_react_native12 = require("@toss-design-system/react-native");
329
+ var import_private = require("@toss-design-system/react-native/private");
330
+ var import_react4 = require("react");
294
331
  var import_react_native_bedrock5 = require("react-native-bedrock");
295
332
  var bedrockAsyncBridges = __toESM(require("react-native-bedrock/async-bridges"), 1);
296
333
  var bedrockConstantBridges = __toESM(require("react-native-bedrock/constant-bridges"), 1);
297
334
 
298
335
  // src/components/GameWebView.tsx
299
336
  var import_react_native_webview = require("@react-native-bedrock/native/react-native-webview");
337
+ var import_react_native10 = require("@toss-design-system/react-native");
338
+ var import_es_hangul = require("es-hangul");
300
339
  var import_react2 = require("react");
301
- var import_react_native10 = require("react-native");
340
+ var import_react_native11 = require("react-native");
302
341
  var import_react_native_bedrock4 = require("react-native-bedrock");
303
342
 
304
343
  // src/components/GameWebViewNavigationBar/GameNavigationBar.tsx
@@ -382,6 +421,9 @@ function GameNavigationBar({ onClose }) {
382
421
  import_react_native9.TouchableOpacity,
383
422
  {
384
423
  hitSlop: { left: 8, right: 8 },
424
+ accessibilityRole: "button",
425
+ accessible: true,
426
+ accessibilityLabel: "\uAC8C\uC784\uC885\uB8CC",
385
427
  style: {
386
428
  padding: import_react_native9.Platform.OS === "ios" ? 7 : 9
387
429
  },
@@ -397,16 +439,41 @@ function GameNavigationBar({ onClose }) {
397
439
  // src/components/GameWebView.tsx
398
440
  var import_jsx_runtime4 = require("react/jsx-runtime");
399
441
  var GameWebView = (0, import_react2.forwardRef)(function GameWebView2(props, ref) {
442
+ const { openConfirm } = (0, import_react_native10.useDialog)();
443
+ const { brandDisplayName } = getAppsInTossGlobals();
444
+ const handleClose = (0, import_react2.useCallback)(async () => {
445
+ const isConfirmed = await openConfirm({
446
+ title: `${(0, import_es_hangul.josa)(brandDisplayName, "\uC744/\uB97C")} \uC885\uB8CC\uD560\uAE4C\uC694?`,
447
+ leftButton: "\uCDE8\uC18C",
448
+ rightButton: "\uC885\uB8CC\uD558\uAE30",
449
+ closeOnDimmerClick: true
450
+ });
451
+ if (isConfirmed) {
452
+ (0, import_react_native_bedrock4.closeView)();
453
+ }
454
+ }, [brandDisplayName, openConfirm]);
455
+ (0, import_react2.useEffect)(() => {
456
+ if (import_react_native11.Platform.OS === "ios") {
457
+ (0, import_react_native_bedrock4.setIosSwipeGestureEnabled)({ isEnabled: false });
458
+ return () => {
459
+ (0, import_react_native_bedrock4.setIosSwipeGestureEnabled)({ isEnabled: true });
460
+ };
461
+ }
462
+ return;
463
+ }, []);
464
+ (0, import_react2.useEffect)(() => {
465
+ const backHandler = () => {
466
+ handleClose();
467
+ return true;
468
+ };
469
+ import_react_native11.BackHandler.addEventListener("hardwareBackPress", backHandler);
470
+ return () => {
471
+ import_react_native11.BackHandler.removeEventListener("hardwareBackPress", backHandler);
472
+ };
473
+ }, [handleClose]);
400
474
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
401
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
402
- GameNavigationBar,
403
- {
404
- onClose: () => {
405
- (0, import_react_native_bedrock4.closeView)();
406
- }
407
- }
408
- ),
409
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native10.View, { style: { flex: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_webview.WebView, { ref, ...props }) })
475
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(GameNavigationBar, { onClose: handleClose }),
476
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native11.View, { style: { flex: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_webview.WebView, { ref, ...props }) })
410
477
  ] });
411
478
  });
412
479
 
@@ -424,10 +491,138 @@ __export(async_bridges_exports, {
424
491
  setClipboardText: () => setClipboardText
425
492
  });
426
493
 
494
+ // src/bridge-handler/useBridgeHandler.tsx
495
+ var import_react3 = require("react");
496
+ function serializeError(error) {
497
+ return JSON.stringify(error, (_, value) => {
498
+ if (value instanceof Error) {
499
+ return {
500
+ name: value.name,
501
+ message: value.message,
502
+ stack: value.stack,
503
+ __isError: true
504
+ };
505
+ }
506
+ return value;
507
+ });
508
+ }
509
+ function methodHandler({
510
+ args,
511
+ eventId,
512
+ functionName,
513
+ handlerMap,
514
+ injectJavaScript
515
+ }) {
516
+ const func = async (...args2) => {
517
+ const result = await handlerMap[functionName](...args2);
518
+ return result;
519
+ };
520
+ if (!func) {
521
+ console.error(`${functionName} is not a function`);
522
+ return;
523
+ }
524
+ func(...args).then((result) => {
525
+ injectJavaScript?.(`
526
+ window.__BEDROCK_NATIVE_EMITTER.emit('${functionName}/resolve/${eventId}', ${JSON.stringify(result, null, 0)});
527
+ `);
528
+ }).catch((error) => {
529
+ const serializedError = serializeError(error);
530
+ injectJavaScript?.(`
531
+ window.__BEDROCK_NATIVE_EMITTER.emit('${functionName}/reject/${eventId}', ${serializedError});
532
+ `);
533
+ });
534
+ }
535
+ var globalEventListenerMap = /* @__PURE__ */ new Map();
536
+ function useBridgeHandler({
537
+ onMessage,
538
+ constantHandlerMap,
539
+ asyncHandlerMap,
540
+ eventListenerMap,
541
+ injectedJavaScript: originalInjectedJavaScript
542
+ }) {
543
+ const ref = (0, import_react3.useRef)(null);
544
+ const injectedJavaScript = (0, import_react3.useMemo)(
545
+ () => [
546
+ `window.__CONSTANT_HANDLER_MAP = ${JSON.stringify(
547
+ Object.entries(constantHandlerMap).reduce(
548
+ (acc, [key, value]) => {
549
+ acc[key] = typeof value === "function" ? value() : value;
550
+ return acc;
551
+ },
552
+ {}
553
+ )
554
+ )}`,
555
+ originalInjectedJavaScript,
556
+ "true"
557
+ ].join("\n"),
558
+ [constantHandlerMap, originalInjectedJavaScript]
559
+ );
560
+ const createHandleOnEvent = (functionName, eventId) => (response) => {
561
+ ref.current?.injectJavaScript(`
562
+ window.__BEDROCK_NATIVE_EMITTER.emit('${functionName}/onEvent/${eventId}', ${JSON.stringify(response, null, 0)});
563
+ `);
564
+ };
565
+ const createHandleOnError = (functionName, eventId) => (error) => {
566
+ ref.current?.injectJavaScript(`
567
+ window.__BEDROCK_NATIVE_EMITTER.emit('${functionName}/onError/${eventId}', ${JSON.stringify(error, null, 0)});
568
+ `);
569
+ };
570
+ const $onMessage = (0, import_react3.useCallback)(
571
+ async (e) => {
572
+ onMessage?.(e);
573
+ const data = JSON.parse(e.nativeEvent.data);
574
+ if (typeof data !== "object" || data === null || typeof data.functionName !== "string" || typeof data.eventId !== "string" || typeof data.type !== "string" || !["addEventListener", "removeEventListener", "method"].includes(data.type)) {
575
+ return;
576
+ }
577
+ switch (data.type) {
578
+ case "addEventListener": {
579
+ const handleOnEvent = createHandleOnEvent(data.functionName, data.eventId);
580
+ const handleOnError = createHandleOnError(data.functionName, data.eventId);
581
+ const remove = eventListenerMap[data.functionName]?.({
582
+ onEvent: handleOnEvent,
583
+ onError: handleOnError,
584
+ options: data.args
585
+ });
586
+ if (remove) {
587
+ globalEventListenerMap.set(`${data.functionName}/${data.eventId}`, remove);
588
+ }
589
+ break;
590
+ }
591
+ case "removeEventListener": {
592
+ const key = `${data.functionName}/${data.eventId}`;
593
+ const remove = globalEventListenerMap.get(key);
594
+ remove?.();
595
+ globalEventListenerMap.delete(key);
596
+ break;
597
+ }
598
+ case "method": {
599
+ methodHandler({
600
+ args: data.args,
601
+ eventId: data.eventId,
602
+ functionName: data.functionName,
603
+ handlerMap: asyncHandlerMap,
604
+ injectJavaScript: ref.current?.injectJavaScript
605
+ });
606
+ break;
607
+ }
608
+ }
609
+ },
610
+ // eslint-disable-next-line react-hooks/exhaustive-deps
611
+ [onMessage]
612
+ );
613
+ return {
614
+ ref,
615
+ injectedJavaScript,
616
+ onMessage: $onMessage
617
+ };
618
+ }
619
+
427
620
  // src/constant-bridges.ts
428
621
  var constant_bridges_exports = {};
429
622
  __export(constant_bridges_exports, {
430
- getOperationalEnvironment: () => getOperationalEnvironment
623
+ getDeviceId: () => getDeviceId,
624
+ getOperationalEnvironment: () => getOperationalEnvironment,
625
+ getTossAppVersion: () => getTossAppVersion
431
626
  });
432
627
 
433
628
  // src/env.ts
@@ -444,9 +639,10 @@ __export(event_bridges_exports, {
444
639
  // src/components/WebView.tsx
445
640
  var import_jsx_runtime5 = require("react/jsx-runtime");
446
641
  var appsInTossGlobals = getAppsInTossGlobals();
642
+ var TYPES = ["partner", "external", "game"];
447
643
  var WEBVIEW_TYPES = {
448
- partner: import_react_native11.PartnerWebViewScreen,
449
- external: import_react_native11.ExternalWebViewScreen,
644
+ partner: import_react_native12.PartnerWebViewScreen,
645
+ external: import_react_native12.ExternalWebViewScreen,
450
646
  game: GameWebView
451
647
  };
452
648
  function mergeSchemeQueryParamsInto(url) {
@@ -472,45 +668,65 @@ function getWebViewUri(local) {
472
668
  return url.toString();
473
669
  }
474
670
  function WebView({ type, local, onMessage, ...props }) {
475
- const uri = (0, import_react3.useMemo)(() => getWebViewUri(local), [local]);
476
- const handler = (0, import_react_native_bedrock5.useBridgeHandler)({
671
+ if (!TYPES.includes(type)) {
672
+ throw new Error(`Invalid WebView type: '${type}'`);
673
+ }
674
+ const bedrockEvent = (0, import_react_native_bedrock5.useBedrockEvent)();
675
+ const uri = (0, import_react4.useMemo)(() => getWebViewUri(local), [local]);
676
+ const top = (0, import_private.useSafeAreaTop)();
677
+ const bottom = (0, import_private.useSafeAreaBottom)();
678
+ const handler = useBridgeHandler({
477
679
  onMessage,
478
680
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
479
- eventListenerMap: event_bridges_exports,
681
+ eventListenerMap: {
682
+ ...event_bridges_exports,
683
+ backEvent: ({ onEvent, onError, options }) => bedrockEvent.addEventListener("backEvent", { onEvent, onError, options })
684
+ },
480
685
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
481
686
  // @ts-expect-error
482
687
  constantHandlerMap: {
483
688
  ...bedrockConstantBridges,
484
- ...constant_bridges_exports
689
+ ...constant_bridges_exports,
690
+ getSafeAreaTop: () => top,
691
+ getSafeAreaBottom: () => bottom
485
692
  },
486
693
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
487
694
  // @ts-expect-error
488
695
  asyncHandlerMap: {
489
696
  ...bedrockAsyncBridges,
490
- ...async_bridges_exports
697
+ ...async_bridges_exports,
698
+ /** internal */
699
+ openPermissionDialog: AppsInTossModule.openPermissionDialog,
700
+ /** Storage */
701
+ getStorageItem: Storage.getItem,
702
+ setStorageItem: Storage.setItem,
703
+ removeStorageItem: Storage.removeItem,
704
+ clearItems: Storage.clearItems
491
705
  }
492
706
  });
493
- const baseProps = (0, import_react3.useMemo)(() => {
707
+ const baseProps = (0, import_react4.useMemo)(() => {
494
708
  switch (type) {
495
709
  case "partner": {
496
- return {
710
+ const headerOnlyProp = {
497
711
  header: {
498
- ...props.header,
499
- icon: toIcon(appsInTossGlobals.brandIcon),
500
- title: appsInTossGlobals.brandDisplayName,
712
+ ..."header" in props ? props.header : {},
713
+ icon: toIcon(ensureValue(appsInTossGlobals.brandIcon, "icon")),
714
+ title: ensureValue(appsInTossGlobals.brandDisplayName, "displayName"),
501
715
  rightButtons: void 0
502
716
  // TODO: onClick 이벤트를 받아야 하기에 런타임에서 설정 받아야 함
503
717
  }
504
718
  };
719
+ return headerOnlyProp;
505
720
  }
506
721
  case "external": {
507
- return {
722
+ const headerOnlyProp = {
508
723
  header: {
509
- ...props.header,
510
- icon: toIcon(appsInTossGlobals.brandIcon),
511
- title: appsInTossGlobals.brandDisplayName
724
+ ..."header" in props ? props.header : {},
725
+ icon: toIcon(ensureValue(appsInTossGlobals.brandIcon, "icon")),
726
+ title: ensureValue(appsInTossGlobals.brandDisplayName, "displayName")
512
727
  }
513
728
  };
729
+ return headerOnlyProp;
514
730
  }
515
731
  default: {
516
732
  return {};
@@ -518,31 +734,40 @@ function WebView({ type, local, onMessage, ...props }) {
518
734
  }
519
735
  }, [type, props]);
520
736
  const BaseWebView = WEBVIEW_TYPES[type];
737
+ const webviewDebuggingEnabled = (0, import_react4.useMemo)(
738
+ () => getOperationalEnvironment() === "sandbox",
739
+ []
740
+ );
521
741
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
522
742
  BaseWebView,
523
743
  {
524
744
  ref: handler.ref,
525
- ...baseProps,
526
745
  ...props,
746
+ ...baseProps,
527
747
  source: { uri },
528
748
  sharedCookiesEnabled: true,
749
+ webviewDebuggingEnabled,
529
750
  thirdPartyCookiesEnabled: true,
530
- cacheEnabled: false,
531
- cacheMode: "LOAD_NO_CACHE",
532
751
  onMessage: handler.onMessage,
533
752
  injectedJavaScript: handler.injectedJavaScript,
534
753
  injectedJavaScriptBeforeContentLoaded: handler.injectedJavaScript
535
754
  }
536
755
  );
537
756
  }
757
+ function ensureValue(value, name) {
758
+ if (value === void 0) {
759
+ throw new Error(`${name} is required`);
760
+ }
761
+ return value;
762
+ }
538
763
 
539
764
  // src/hooks/useGeolocation.ts
540
- var import_react4 = require("react");
765
+ var import_react5 = require("react");
541
766
  var import_react_native_bedrock6 = require("react-native-bedrock");
542
767
  function useGeolocation({ accuracy, distanceInterval, timeInterval }) {
543
768
  const isVisible = (0, import_react_native_bedrock6.useVisibility)();
544
- const [location, setLocation] = (0, import_react4.useState)(null);
545
- (0, import_react4.useEffect)(() => {
769
+ const [location, setLocation] = (0, import_react5.useState)(null);
770
+ (0, import_react5.useEffect)(() => {
546
771
  if (!isVisible) {
547
772
  return;
548
773
  }
@@ -573,6 +798,7 @@ var Accuracy2 = /* @__PURE__ */ ((Accuracy3) => {
573
798
  0 && (module.exports = {
574
799
  Accuracy,
575
800
  AppsInToss,
801
+ Storage,
576
802
  TossPay,
577
803
  WebView,
578
804
  appLogin,
@@ -581,7 +807,9 @@ var Accuracy2 = /* @__PURE__ */ ((Accuracy3) => {
581
807
  fetchContacts,
582
808
  getClipboardText,
583
809
  getCurrentLocation,
810
+ getDeviceId,
584
811
  getOperationalEnvironment,
812
+ getTossAppVersion,
585
813
  openCamera,
586
814
  setClipboardText,
587
815
  startUpdateLocation,
package/dist/index.d.cts CHANGED
@@ -70,7 +70,7 @@ interface Location {
70
70
  */
71
71
  timestamp: number;
72
72
  /**
73
- * @description 위치 정보를 나타내는 객체예요. 자세한 내용은 [LocationCoords](/reference/framework/Types/LocationCoords.html)을 참고해주세요.
73
+ * @description 위치 정보를 나타내는 객체예요. 자세한 내용은 [LocationCoords](/react-native/reference/framework/Types/LocationCoords.html)을 참고해주세요.
74
74
  */
75
75
  coords: LocationCoords;
76
76
  }
@@ -154,7 +154,7 @@ interface UpdateLocationEventEmitter extends EventEmitterSchema<'updateLocation'
154
154
  * @param {number} [options.accuracy] 위치 정확도를 설정해요.
155
155
  * @param {number} [options.timeInterval] 위치 정보를 업데이트하는 최소 주기로, 단위는 밀리초(ms)예요. 이 값은 위치 업데이트가 발생하는 가장 짧은 간격을 설정하지만, 시스템이나 환경의 영향을 받아 지정한 주기보다 더 긴 간격으로 업데이트될 수 있어요.
156
156
  * @param {number} [options.distanceInterval] 위치 변경 거리를 미터(m) 단위로 설정해요.
157
- * @param {(location: Location) => void} [options.callback] 위치 정보가 변경될 때 호출되는 콜백 함수예요. 자세한 내용은 [Location](/reference/framework/Types/Location.html)을 참고해주세요.
157
+ * @param {(location: Location) => void} [options.callback] 위치 정보가 변경될 때 호출되는 콜백 함수예요. 자세한 내용은 [Location](/react-native/reference/framework/Types/Location.html)을 참고해주세요.
158
158
  *
159
159
  * @example
160
160
  * ### 위치 정보 변경 감지하기
@@ -569,7 +569,7 @@ interface TossMoneyExecutePaymentResult {
569
569
  * @param {ExecutePaymentOptions} options 결제를 실행할 때 필요한 옵션이에요.
570
570
  * @param {string} options.orderNo 결제를 생성할 때 사용한 주문번호예요.
571
571
  * @param {string} options.payToken 결제 인증 과정에서 전달받은 결제 토큰이에요. 결제를 실행할 때 사용해요.
572
- * @returns {Promise<CardExecutePaymentResult | TossMoneyExecutePaymentResult>} 결제 결과 객체를 반환해요. 자세한 내용은 [카드 결제 결과](reference/framework/토스페이/CardExecutePaymentResult.html) 또는 [토스머니 결제 결과](reference/framework/토스페이/TossMoneyExecutePaymentResult.html)를 참고하세요.
572
+ * @returns {Promise<CardExecutePaymentResult | TossMoneyExecutePaymentResult>} 결제 결과 객체를 반환해요. 자세한 내용은 [카드 결제 결과](/react-native/reference/framework/토스페이/CardExecutePaymentResult.html) 또는 [토스머니 결제 결과](/react-native/reference/framework/토스페이/TossMoneyExecutePaymentResult.html)를 참고하세요.
573
573
  *
574
574
  * @example
575
575
  *
@@ -858,7 +858,7 @@ interface GetCurrentLocationOptions {
858
858
  *
859
859
  * @param {GetCurrentLocationOptions} options 위치 정보를 가져올 때 사용하는 옵션 객체예요.
860
860
  * @param {Accuracy} [options.accuracy] 위치 정보의 정확도 수준이에요. 정확도는 `Accuracy` 타입으로 설정돼요.
861
- * @returns {Promise<Location>} 디바이스의 위치 정보가 담긴 객체를 반환해요. 자세한 내용은 [Location](/reference/framework/Types/Location.html)을 참고해주세요.
861
+ * @returns {Promise<Location>} 디바이스의 위치 정보가 담긴 객체를 반환해요. 자세한 내용은 [Location](/react-native/reference/framework/Types/Location.html)을 참고해주세요.
862
862
  *
863
863
  * @example
864
864
  * ### 디바이스의 현재 위치 정보 가져오기
@@ -1027,13 +1027,157 @@ declare function appLogin(): Promise<{
1027
1027
  */
1028
1028
  declare function getOperationalEnvironment(): 'toss' | 'sandbox';
1029
1029
 
1030
+ /**
1031
+ * @public
1032
+ * @category 환경 확인
1033
+ * @name getTossAppVersion
1034
+ * @description 토스 앱 버전을 가져옵니다. 예를 들어, `5.206.0`과 같은 형태로 반환돼요. 토스 앱 버전을 로그로 남기거나, 특정 기능이 특정 버전 이상에서만 실행될 때 사용돼요.
1035
+ * @signature
1036
+ * ```typescript
1037
+ * function getTossAppVersion(): string
1038
+ * ```
1039
+ *
1040
+ * @returns {string} 토스 앱 버전
1041
+ *
1042
+ * @example
1043
+ *
1044
+ *
1045
+ * ### 토스 앱 버전 확인하기
1046
+ *
1047
+ * ```tsx
1048
+ * import { getTossAppVersion } from '@apps-in-toss/framework';
1049
+ * import { Text } from 'react-native';
1050
+ *
1051
+ * export function TossAppVersionPage() {
1052
+ * return (
1053
+ * <Text>{getTossAppVersion()}</Text>
1054
+ * )
1055
+ * }
1056
+ * ```
1057
+ */
1058
+ declare function getTossAppVersion(): string;
1059
+
1060
+ /**
1061
+ * @public
1062
+ * @category 환경 확인
1063
+ * @kind function
1064
+ * @name getDeviceId
1065
+ * @description
1066
+ * 사용 중인 기기의 고유 식별자를 문자열로 반환해요.
1067
+ *
1068
+ * 이 함수는 현재 사용 중인 기기의 고유 식별자를 문자열로 반환해요. 기기별로 설정이나 데이터를 저장하거나 사용자의 기기를 식별해서 로그를 기록하고 분석하는 데 사용할 수 있어요. 같은 사용자의 여러 기기를 구분하는 데도 유용해요.
1069
+ *
1070
+ * @returns {string} 기기의 고유 식별자를 나타내는 문자열이에요.
1071
+ *
1072
+ * @example
1073
+ * ### 기기 고유 식별자 가져오기
1074
+ *
1075
+ * ```tsx
1076
+ * import { getDeviceId } from '@apps-in-toss/framework';
1077
+ * import { Text } from 'react-native';
1078
+ *
1079
+ * function MyPage() {
1080
+ * const id = getDeviceId();
1081
+ *
1082
+ * return (
1083
+ * <Text>사용자의 기기 고유 식별자: {id}</Text>
1084
+ * );
1085
+ * }
1086
+ * ```
1087
+ */
1088
+ declare function getDeviceId(): string;
1089
+
1090
+ /**
1091
+ * @public
1092
+ * @category 저장소
1093
+ * @name getItem
1094
+ * @description 모바일 앱의 로컬 저장소에서 문자열 데이터를 가져와요. 주로 앱이 종료되었다가 다시 시작해도 데이터가 유지되어야 하는 경우에 사용해요.
1095
+ * @param {string} key - 가져올 아이템의 키를 입력해요.
1096
+ * @returns {Promise<string | null>} 지정한 키에 저장된 문자열 값을 반환해요. 값이 없으면 `null`을 반환해요.
1097
+ * @example
1098
+ *
1099
+ * ### `my-key`에 저장된 아이템 가져오기
1100
+ * ```ts
1101
+ * const value = await Storage.getItem('my-key');
1102
+ * console.log(value); // 'value'
1103
+ * ```
1104
+ */
1105
+ declare function getItem(key: string): Promise<string | null>;
1106
+ /**
1107
+ * @public
1108
+ * @category 저장소
1109
+ * @name setItem
1110
+ * @description 모바일 앱의 로컬 저장소에 문자열 데이터를 저장해요. 주로 앱이 종료되었다가 다시 시작해도 데이터가 유지되어야 하는 경우에 사용해요.
1111
+ * @param {string} key - 저장할 아이템의 키를 입력해요.
1112
+ * @param {string} value - 저장할 아이템의 값을 입력해요.
1113
+ * @returns {Promise<void>} 아이템을 성공적으로 저장하면 아무 값도 반환하지 않아요.
1114
+ * @example
1115
+ *
1116
+ * ### `my-key`에 아이템 저장하기
1117
+ * ```ts
1118
+ * import { Storage } from '@apps-in-toss/framework';
1119
+ *
1120
+ * await Storage.setItem('my-key', 'value');
1121
+ * ```
1122
+ */
1123
+ declare function setItem(key: string, value: string): Promise<void>;
1124
+ /**
1125
+ * @public
1126
+ * @category 저장소
1127
+ * @name removeItem
1128
+ * @description 모바일 앱의 로컬 저장소에서 특정 키에 해당하는 아이템을 삭제해요.
1129
+ * @param {string} key - 삭제할 아이템의 키를 입력해요.
1130
+ * @returns {Promise<void>} 아이템을 삭제하면 아무 값도 반환하지 않아요.
1131
+ * @example
1132
+ *
1133
+ * ### `my-key`에 저장된 아이템 삭제하기
1134
+ * ```ts
1135
+ * import { Storage } from '@apps-in-toss/framework';
1136
+ *
1137
+ * await Storage.removeItem('my-key');
1138
+ * ```
1139
+ */
1140
+ declare function removeItem(key: string): Promise<void>;
1141
+ /**
1142
+ * @public
1143
+ * @category 저장소
1144
+ * @name clearItems
1145
+ * @description 모바일 앱의 로컬 저장소의 모든 아이템을 삭제해요.
1146
+ * @returns {Promise<void>} 아이템을 삭제하면 아무 값도 반환하지 않고 저장소가 초기화돼요.
1147
+ * @example
1148
+ *
1149
+ * ### 저장소 초기화하기
1150
+ * ```ts
1151
+ * import { Storage } from '@apps-in-toss/framework';
1152
+ *
1153
+ * await Storage.clearItems();
1154
+ * ```
1155
+ */
1156
+ declare function clearItems(): Promise<void>;
1157
+ /**
1158
+ * @public
1159
+ * @category 저장소
1160
+ * @name Storage
1161
+ * @description 네이티브의 저장소를 사용해요.
1162
+ * @property {typeof getItem} [getItem] 모바일 앱의 로컬 저장소에서 아이템을 가져오는 함수예요. 자세한 내용은 [getItem](/react-native/reference/framework/저장소/getItem)을 참고하세요.
1163
+ * @property {typeof setItem} [setItem] 모바일 앱의 로컬 저장소에 아이템을 저장하는 함수예요. 자세한 내용은 [setItem](/react-native/reference/framework/저장소/setItem)을 참고하세요.
1164
+ * @property {typeof removeItem} [removeItem] 모바일 앱의 로컬 저장소에서 아이템을 삭제하는 함수예요. 자세한 내용은 [removeItem](/react-native/reference/framework/저장소/removeItem)을 참고하세요.
1165
+ * @property {typeof clearItems} [clearItems] 모바일 앱의 로컬 저장소를 초기화하는 함수예요. 자세한 내용은 [clearItems](/react-native/reference/framework/저장소/clearItems)을 참고하세요.
1166
+ */
1167
+ declare const Storage: {
1168
+ getItem: typeof getItem;
1169
+ setItem: typeof setItem;
1170
+ removeItem: typeof removeItem;
1171
+ clearItems: typeof clearItems;
1172
+ };
1173
+
1030
1174
  /**
1031
1175
  * @public
1032
1176
  * @category 토스페이
1033
1177
  * @name TossPay
1034
1178
  * @description 토스페이 결제 관련 함수를 모아둔 객체예요.
1035
- * @property {typeof checkoutPayment} [checkoutPayment] 토스페이 결제를 생성하는 함수예요. 자세한 내용은 [checkoutPayment](reference/framework/토스페이/checkoutPayment)를 참고하세요.
1036
- * @property {typeof executePayment} [executePayment] 토스페이 결제를 실행하는 함수예요. 자세한 내용은 [executePayment](reference/framework/토스페이/executePayment)를 참고하세요.
1179
+ * @property {typeof checkoutPayment} [checkoutPayment] 토스페이 결제를 생성하는 함수예요. 자세한 내용은 [checkoutPayment](/react-native/reference/framework/토스페이/checkoutPayment)를 참고하세요.
1180
+ * @property {typeof executePayment} [executePayment] 토스페이 결제를 실행하는 함수예요. 자세한 내용은 [executePayment](/react-native/reference/framework/토스페이/executePayment)를 참고하세요.
1037
1181
  */
1038
1182
  declare const TossPay: {
1039
1183
  checkoutPayment: typeof checkoutPayment;
@@ -1076,7 +1220,7 @@ type UseGeolocationOptions = Omit<StartUpdateLocationOptions, 'callback'>;
1076
1220
  * @param {Accuracy} [options.accuracy] 위치 정확도를 설정해요. `Accuracy.Lowest`: 오차범위 3KM 이내, `Accuracy.Low`: 오차범위 1KM 이내, `Accuracy.Balanced`: 오차범위 몇 백미터 이내, `Accuracy.High`: 오차범위 10M 이내, `Accuracy.Highest`: 가장 높은 정확도, `Accuracy.BestForNavigation`: 네비게이션을 위한 최고 정확도
1077
1221
  * @param {number} [options.timeInterval] 위치 정보를 업데이트하는 최소 주기로, 단위는 밀리초(ms)예요. 이 값은 위치 업데이트가 발생하는 가장 짧은 간격을 설정하지만, 시스템이나 환경의 영향을 받아 지정한 주기보다 더 긴 간격으로 업데이트될 수 있어요.
1078
1222
  * @param {number} [options.distanceInterval] 위치 변경 거리를 미터(m) 단위로 설정해요.
1079
- * @returns {Location | null} 디바이스의 위치 정보가 담긴 객체를 반환해요. 자세한 내용은 [Location](/reference/framework/Types/Location.html)을 참고해주세요.
1223
+ * @returns {Location | null} 디바이스의 위치 정보가 담긴 객체를 반환해요. 자세한 내용은 [Location](/react-native/reference/framework/Types/Location.html)을 참고해주세요.
1080
1224
  *
1081
1225
  * @example
1082
1226
  * ### 위치 정보 변경 감지하기
@@ -1112,4 +1256,4 @@ declare const env: {
1112
1256
  getDeploymentId: () => string | undefined;
1113
1257
  };
1114
1258
 
1115
- export { Accuracy, AppsInToss, type ContactEntity, type ExternalWebViewProps, type FetchAlbumPhotosOptions, type GameWebViewProps, type GetCurrentLocationOptions, type ImageResponse, type Location, type LocationCoords, type OpenCameraOptions, type PartnerWebViewProps, type StartUpdateLocationOptions, type StartUpdateLocationSubscription, TossPay, type UpdateLocationEventEmitter, type UseGeolocationOptions, WebView, type WebViewProps, appLogin, env, fetchAlbumPhotos, fetchContacts, getClipboardText, getCurrentLocation, getOperationalEnvironment, openCamera, setClipboardText, startUpdateLocation, useGeolocation };
1259
+ export { Accuracy, AppsInToss, type ContactEntity, type ExternalWebViewProps, type FetchAlbumPhotosOptions, type GameWebViewProps, type GetCurrentLocationOptions, type ImageResponse, type Location, type LocationCoords, type OpenCameraOptions, type PartnerWebViewProps, type StartUpdateLocationOptions, type StartUpdateLocationSubscription, Storage, TossPay, type UpdateLocationEventEmitter, type UseGeolocationOptions, WebView, type WebViewProps, appLogin, env, fetchAlbumPhotos, fetchContacts, getClipboardText, getCurrentLocation, getDeviceId, getOperationalEnvironment, getTossAppVersion, openCamera, setClipboardText, startUpdateLocation, useGeolocation };
package/dist/index.d.ts CHANGED
@@ -70,7 +70,7 @@ interface Location {
70
70
  */
71
71
  timestamp: number;
72
72
  /**
73
- * @description 위치 정보를 나타내는 객체예요. 자세한 내용은 [LocationCoords](/reference/framework/Types/LocationCoords.html)을 참고해주세요.
73
+ * @description 위치 정보를 나타내는 객체예요. 자세한 내용은 [LocationCoords](/react-native/reference/framework/Types/LocationCoords.html)을 참고해주세요.
74
74
  */
75
75
  coords: LocationCoords;
76
76
  }
@@ -154,7 +154,7 @@ interface UpdateLocationEventEmitter extends EventEmitterSchema<'updateLocation'
154
154
  * @param {number} [options.accuracy] 위치 정확도를 설정해요.
155
155
  * @param {number} [options.timeInterval] 위치 정보를 업데이트하는 최소 주기로, 단위는 밀리초(ms)예요. 이 값은 위치 업데이트가 발생하는 가장 짧은 간격을 설정하지만, 시스템이나 환경의 영향을 받아 지정한 주기보다 더 긴 간격으로 업데이트될 수 있어요.
156
156
  * @param {number} [options.distanceInterval] 위치 변경 거리를 미터(m) 단위로 설정해요.
157
- * @param {(location: Location) => void} [options.callback] 위치 정보가 변경될 때 호출되는 콜백 함수예요. 자세한 내용은 [Location](/reference/framework/Types/Location.html)을 참고해주세요.
157
+ * @param {(location: Location) => void} [options.callback] 위치 정보가 변경될 때 호출되는 콜백 함수예요. 자세한 내용은 [Location](/react-native/reference/framework/Types/Location.html)을 참고해주세요.
158
158
  *
159
159
  * @example
160
160
  * ### 위치 정보 변경 감지하기
@@ -569,7 +569,7 @@ interface TossMoneyExecutePaymentResult {
569
569
  * @param {ExecutePaymentOptions} options 결제를 실행할 때 필요한 옵션이에요.
570
570
  * @param {string} options.orderNo 결제를 생성할 때 사용한 주문번호예요.
571
571
  * @param {string} options.payToken 결제 인증 과정에서 전달받은 결제 토큰이에요. 결제를 실행할 때 사용해요.
572
- * @returns {Promise<CardExecutePaymentResult | TossMoneyExecutePaymentResult>} 결제 결과 객체를 반환해요. 자세한 내용은 [카드 결제 결과](reference/framework/토스페이/CardExecutePaymentResult.html) 또는 [토스머니 결제 결과](reference/framework/토스페이/TossMoneyExecutePaymentResult.html)를 참고하세요.
572
+ * @returns {Promise<CardExecutePaymentResult | TossMoneyExecutePaymentResult>} 결제 결과 객체를 반환해요. 자세한 내용은 [카드 결제 결과](/react-native/reference/framework/토스페이/CardExecutePaymentResult.html) 또는 [토스머니 결제 결과](/react-native/reference/framework/토스페이/TossMoneyExecutePaymentResult.html)를 참고하세요.
573
573
  *
574
574
  * @example
575
575
  *
@@ -858,7 +858,7 @@ interface GetCurrentLocationOptions {
858
858
  *
859
859
  * @param {GetCurrentLocationOptions} options 위치 정보를 가져올 때 사용하는 옵션 객체예요.
860
860
  * @param {Accuracy} [options.accuracy] 위치 정보의 정확도 수준이에요. 정확도는 `Accuracy` 타입으로 설정돼요.
861
- * @returns {Promise<Location>} 디바이스의 위치 정보가 담긴 객체를 반환해요. 자세한 내용은 [Location](/reference/framework/Types/Location.html)을 참고해주세요.
861
+ * @returns {Promise<Location>} 디바이스의 위치 정보가 담긴 객체를 반환해요. 자세한 내용은 [Location](/react-native/reference/framework/Types/Location.html)을 참고해주세요.
862
862
  *
863
863
  * @example
864
864
  * ### 디바이스의 현재 위치 정보 가져오기
@@ -1027,13 +1027,157 @@ declare function appLogin(): Promise<{
1027
1027
  */
1028
1028
  declare function getOperationalEnvironment(): 'toss' | 'sandbox';
1029
1029
 
1030
+ /**
1031
+ * @public
1032
+ * @category 환경 확인
1033
+ * @name getTossAppVersion
1034
+ * @description 토스 앱 버전을 가져옵니다. 예를 들어, `5.206.0`과 같은 형태로 반환돼요. 토스 앱 버전을 로그로 남기거나, 특정 기능이 특정 버전 이상에서만 실행될 때 사용돼요.
1035
+ * @signature
1036
+ * ```typescript
1037
+ * function getTossAppVersion(): string
1038
+ * ```
1039
+ *
1040
+ * @returns {string} 토스 앱 버전
1041
+ *
1042
+ * @example
1043
+ *
1044
+ *
1045
+ * ### 토스 앱 버전 확인하기
1046
+ *
1047
+ * ```tsx
1048
+ * import { getTossAppVersion } from '@apps-in-toss/framework';
1049
+ * import { Text } from 'react-native';
1050
+ *
1051
+ * export function TossAppVersionPage() {
1052
+ * return (
1053
+ * <Text>{getTossAppVersion()}</Text>
1054
+ * )
1055
+ * }
1056
+ * ```
1057
+ */
1058
+ declare function getTossAppVersion(): string;
1059
+
1060
+ /**
1061
+ * @public
1062
+ * @category 환경 확인
1063
+ * @kind function
1064
+ * @name getDeviceId
1065
+ * @description
1066
+ * 사용 중인 기기의 고유 식별자를 문자열로 반환해요.
1067
+ *
1068
+ * 이 함수는 현재 사용 중인 기기의 고유 식별자를 문자열로 반환해요. 기기별로 설정이나 데이터를 저장하거나 사용자의 기기를 식별해서 로그를 기록하고 분석하는 데 사용할 수 있어요. 같은 사용자의 여러 기기를 구분하는 데도 유용해요.
1069
+ *
1070
+ * @returns {string} 기기의 고유 식별자를 나타내는 문자열이에요.
1071
+ *
1072
+ * @example
1073
+ * ### 기기 고유 식별자 가져오기
1074
+ *
1075
+ * ```tsx
1076
+ * import { getDeviceId } from '@apps-in-toss/framework';
1077
+ * import { Text } from 'react-native';
1078
+ *
1079
+ * function MyPage() {
1080
+ * const id = getDeviceId();
1081
+ *
1082
+ * return (
1083
+ * <Text>사용자의 기기 고유 식별자: {id}</Text>
1084
+ * );
1085
+ * }
1086
+ * ```
1087
+ */
1088
+ declare function getDeviceId(): string;
1089
+
1090
+ /**
1091
+ * @public
1092
+ * @category 저장소
1093
+ * @name getItem
1094
+ * @description 모바일 앱의 로컬 저장소에서 문자열 데이터를 가져와요. 주로 앱이 종료되었다가 다시 시작해도 데이터가 유지되어야 하는 경우에 사용해요.
1095
+ * @param {string} key - 가져올 아이템의 키를 입력해요.
1096
+ * @returns {Promise<string | null>} 지정한 키에 저장된 문자열 값을 반환해요. 값이 없으면 `null`을 반환해요.
1097
+ * @example
1098
+ *
1099
+ * ### `my-key`에 저장된 아이템 가져오기
1100
+ * ```ts
1101
+ * const value = await Storage.getItem('my-key');
1102
+ * console.log(value); // 'value'
1103
+ * ```
1104
+ */
1105
+ declare function getItem(key: string): Promise<string | null>;
1106
+ /**
1107
+ * @public
1108
+ * @category 저장소
1109
+ * @name setItem
1110
+ * @description 모바일 앱의 로컬 저장소에 문자열 데이터를 저장해요. 주로 앱이 종료되었다가 다시 시작해도 데이터가 유지되어야 하는 경우에 사용해요.
1111
+ * @param {string} key - 저장할 아이템의 키를 입력해요.
1112
+ * @param {string} value - 저장할 아이템의 값을 입력해요.
1113
+ * @returns {Promise<void>} 아이템을 성공적으로 저장하면 아무 값도 반환하지 않아요.
1114
+ * @example
1115
+ *
1116
+ * ### `my-key`에 아이템 저장하기
1117
+ * ```ts
1118
+ * import { Storage } from '@apps-in-toss/framework';
1119
+ *
1120
+ * await Storage.setItem('my-key', 'value');
1121
+ * ```
1122
+ */
1123
+ declare function setItem(key: string, value: string): Promise<void>;
1124
+ /**
1125
+ * @public
1126
+ * @category 저장소
1127
+ * @name removeItem
1128
+ * @description 모바일 앱의 로컬 저장소에서 특정 키에 해당하는 아이템을 삭제해요.
1129
+ * @param {string} key - 삭제할 아이템의 키를 입력해요.
1130
+ * @returns {Promise<void>} 아이템을 삭제하면 아무 값도 반환하지 않아요.
1131
+ * @example
1132
+ *
1133
+ * ### `my-key`에 저장된 아이템 삭제하기
1134
+ * ```ts
1135
+ * import { Storage } from '@apps-in-toss/framework';
1136
+ *
1137
+ * await Storage.removeItem('my-key');
1138
+ * ```
1139
+ */
1140
+ declare function removeItem(key: string): Promise<void>;
1141
+ /**
1142
+ * @public
1143
+ * @category 저장소
1144
+ * @name clearItems
1145
+ * @description 모바일 앱의 로컬 저장소의 모든 아이템을 삭제해요.
1146
+ * @returns {Promise<void>} 아이템을 삭제하면 아무 값도 반환하지 않고 저장소가 초기화돼요.
1147
+ * @example
1148
+ *
1149
+ * ### 저장소 초기화하기
1150
+ * ```ts
1151
+ * import { Storage } from '@apps-in-toss/framework';
1152
+ *
1153
+ * await Storage.clearItems();
1154
+ * ```
1155
+ */
1156
+ declare function clearItems(): Promise<void>;
1157
+ /**
1158
+ * @public
1159
+ * @category 저장소
1160
+ * @name Storage
1161
+ * @description 네이티브의 저장소를 사용해요.
1162
+ * @property {typeof getItem} [getItem] 모바일 앱의 로컬 저장소에서 아이템을 가져오는 함수예요. 자세한 내용은 [getItem](/react-native/reference/framework/저장소/getItem)을 참고하세요.
1163
+ * @property {typeof setItem} [setItem] 모바일 앱의 로컬 저장소에 아이템을 저장하는 함수예요. 자세한 내용은 [setItem](/react-native/reference/framework/저장소/setItem)을 참고하세요.
1164
+ * @property {typeof removeItem} [removeItem] 모바일 앱의 로컬 저장소에서 아이템을 삭제하는 함수예요. 자세한 내용은 [removeItem](/react-native/reference/framework/저장소/removeItem)을 참고하세요.
1165
+ * @property {typeof clearItems} [clearItems] 모바일 앱의 로컬 저장소를 초기화하는 함수예요. 자세한 내용은 [clearItems](/react-native/reference/framework/저장소/clearItems)을 참고하세요.
1166
+ */
1167
+ declare const Storage: {
1168
+ getItem: typeof getItem;
1169
+ setItem: typeof setItem;
1170
+ removeItem: typeof removeItem;
1171
+ clearItems: typeof clearItems;
1172
+ };
1173
+
1030
1174
  /**
1031
1175
  * @public
1032
1176
  * @category 토스페이
1033
1177
  * @name TossPay
1034
1178
  * @description 토스페이 결제 관련 함수를 모아둔 객체예요.
1035
- * @property {typeof checkoutPayment} [checkoutPayment] 토스페이 결제를 생성하는 함수예요. 자세한 내용은 [checkoutPayment](reference/framework/토스페이/checkoutPayment)를 참고하세요.
1036
- * @property {typeof executePayment} [executePayment] 토스페이 결제를 실행하는 함수예요. 자세한 내용은 [executePayment](reference/framework/토스페이/executePayment)를 참고하세요.
1179
+ * @property {typeof checkoutPayment} [checkoutPayment] 토스페이 결제를 생성하는 함수예요. 자세한 내용은 [checkoutPayment](/react-native/reference/framework/토스페이/checkoutPayment)를 참고하세요.
1180
+ * @property {typeof executePayment} [executePayment] 토스페이 결제를 실행하는 함수예요. 자세한 내용은 [executePayment](/react-native/reference/framework/토스페이/executePayment)를 참고하세요.
1037
1181
  */
1038
1182
  declare const TossPay: {
1039
1183
  checkoutPayment: typeof checkoutPayment;
@@ -1076,7 +1220,7 @@ type UseGeolocationOptions = Omit<StartUpdateLocationOptions, 'callback'>;
1076
1220
  * @param {Accuracy} [options.accuracy] 위치 정확도를 설정해요. `Accuracy.Lowest`: 오차범위 3KM 이내, `Accuracy.Low`: 오차범위 1KM 이내, `Accuracy.Balanced`: 오차범위 몇 백미터 이내, `Accuracy.High`: 오차범위 10M 이내, `Accuracy.Highest`: 가장 높은 정확도, `Accuracy.BestForNavigation`: 네비게이션을 위한 최고 정확도
1077
1221
  * @param {number} [options.timeInterval] 위치 정보를 업데이트하는 최소 주기로, 단위는 밀리초(ms)예요. 이 값은 위치 업데이트가 발생하는 가장 짧은 간격을 설정하지만, 시스템이나 환경의 영향을 받아 지정한 주기보다 더 긴 간격으로 업데이트될 수 있어요.
1078
1222
  * @param {number} [options.distanceInterval] 위치 변경 거리를 미터(m) 단위로 설정해요.
1079
- * @returns {Location | null} 디바이스의 위치 정보가 담긴 객체를 반환해요. 자세한 내용은 [Location](/reference/framework/Types/Location.html)을 참고해주세요.
1223
+ * @returns {Location | null} 디바이스의 위치 정보가 담긴 객체를 반환해요. 자세한 내용은 [Location](/react-native/reference/framework/Types/Location.html)을 참고해주세요.
1080
1224
  *
1081
1225
  * @example
1082
1226
  * ### 위치 정보 변경 감지하기
@@ -1112,4 +1256,4 @@ declare const env: {
1112
1256
  getDeploymentId: () => string | undefined;
1113
1257
  };
1114
1258
 
1115
- export { Accuracy, AppsInToss, type ContactEntity, type ExternalWebViewProps, type FetchAlbumPhotosOptions, type GameWebViewProps, type GetCurrentLocationOptions, type ImageResponse, type Location, type LocationCoords, type OpenCameraOptions, type PartnerWebViewProps, type StartUpdateLocationOptions, type StartUpdateLocationSubscription, TossPay, type UpdateLocationEventEmitter, type UseGeolocationOptions, WebView, type WebViewProps, appLogin, env, fetchAlbumPhotos, fetchContacts, getClipboardText, getCurrentLocation, getOperationalEnvironment, openCamera, setClipboardText, startUpdateLocation, useGeolocation };
1259
+ export { Accuracy, AppsInToss, type ContactEntity, type ExternalWebViewProps, type FetchAlbumPhotosOptions, type GameWebViewProps, type GetCurrentLocationOptions, type ImageResponse, type Location, type LocationCoords, type OpenCameraOptions, type PartnerWebViewProps, type StartUpdateLocationOptions, type StartUpdateLocationSubscription, Storage, TossPay, type UpdateLocationEventEmitter, type UseGeolocationOptions, WebView, type WebViewProps, appLogin, env, fetchAlbumPhotos, fetchContacts, getClipboardText, getCurrentLocation, getDeviceId, getOperationalEnvironment, getTossAppVersion, openCamera, setClipboardText, startUpdateLocation, useGeolocation };
package/dist/index.js CHANGED
@@ -238,6 +238,39 @@ function getOperationalEnvironment() {
238
238
  return AppsInTossModule.operationalEnvironment;
239
239
  }
240
240
 
241
+ // src/native-modules/getTossAppVersion.ts
242
+ function getTossAppVersion() {
243
+ return AppsInTossModule.tossAppVersion;
244
+ }
245
+
246
+ // src/native-modules/getDeviceId.ts
247
+ function getDeviceId() {
248
+ return AppsInTossModule.deviceId;
249
+ }
250
+
251
+ // src/native-modules/storage.ts
252
+ function getItem(key) {
253
+ return AppsInTossModule.getStorageItem({ key });
254
+ }
255
+ function setItem(key, value) {
256
+ return AppsInTossModule.setStorageItem({
257
+ key,
258
+ value
259
+ });
260
+ }
261
+ function removeItem(key) {
262
+ return AppsInTossModule.removeStorageItem({ key });
263
+ }
264
+ function clearItems() {
265
+ return AppsInTossModule.clearStorage({});
266
+ }
267
+ var Storage = {
268
+ getItem,
269
+ setItem,
270
+ removeItem,
271
+ clearItems
272
+ };
273
+
241
274
  // src/native-modules/index.ts
242
275
  var TossPay = {
243
276
  checkoutPayment,
@@ -249,8 +282,9 @@ import {
249
282
  PartnerWebViewScreen,
250
283
  ExternalWebViewScreen
251
284
  } from "@toss-design-system/react-native";
252
- import { useMemo } from "react";
253
- import { getSchemeUri, useBridgeHandler } from "react-native-bedrock";
285
+ import { useSafeAreaBottom, useSafeAreaTop as useSafeAreaTop2 } from "@toss-design-system/react-native/private";
286
+ import { useMemo as useMemo2 } from "react";
287
+ import { getSchemeUri, useBedrockEvent } from "react-native-bedrock";
254
288
  import * as bedrockAsyncBridges from "react-native-bedrock/async-bridges";
255
289
  import * as bedrockConstantBridges from "react-native-bedrock/constant-bridges";
256
290
 
@@ -258,9 +292,11 @@ import * as bedrockConstantBridges from "react-native-bedrock/constant-bridges";
258
292
  import {
259
293
  WebView as PlainWebView
260
294
  } from "@react-native-bedrock/native/react-native-webview";
261
- import { forwardRef } from "react";
262
- import { View as View3 } from "react-native";
263
- import { closeView } from "react-native-bedrock";
295
+ import { useDialog } from "@toss-design-system/react-native";
296
+ import { josa } from "es-hangul";
297
+ import { forwardRef, useCallback, useEffect as useEffect2 } from "react";
298
+ import { BackHandler, Platform as Platform4, View as View3 } from "react-native";
299
+ import { closeView, setIosSwipeGestureEnabled } from "react-native-bedrock";
264
300
 
265
301
  // src/components/GameWebViewNavigationBar/GameNavigationBar.tsx
266
302
  import { SvgXml } from "@react-native-bedrock/native/react-native-svg";
@@ -343,6 +379,9 @@ function GameNavigationBar({ onClose }) {
343
379
  TouchableOpacity,
344
380
  {
345
381
  hitSlop: { left: 8, right: 8 },
382
+ accessibilityRole: "button",
383
+ accessible: true,
384
+ accessibilityLabel: "\uAC8C\uC784\uC885\uB8CC",
346
385
  style: {
347
386
  padding: Platform3.OS === "ios" ? 7 : 9
348
387
  },
@@ -358,15 +397,40 @@ function GameNavigationBar({ onClose }) {
358
397
  // src/components/GameWebView.tsx
359
398
  import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
360
399
  var GameWebView = forwardRef(function GameWebView2(props, ref) {
400
+ const { openConfirm } = useDialog();
401
+ const { brandDisplayName } = getAppsInTossGlobals();
402
+ const handleClose = useCallback(async () => {
403
+ const isConfirmed = await openConfirm({
404
+ title: `${josa(brandDisplayName, "\uC744/\uB97C")} \uC885\uB8CC\uD560\uAE4C\uC694?`,
405
+ leftButton: "\uCDE8\uC18C",
406
+ rightButton: "\uC885\uB8CC\uD558\uAE30",
407
+ closeOnDimmerClick: true
408
+ });
409
+ if (isConfirmed) {
410
+ closeView();
411
+ }
412
+ }, [brandDisplayName, openConfirm]);
413
+ useEffect2(() => {
414
+ if (Platform4.OS === "ios") {
415
+ setIosSwipeGestureEnabled({ isEnabled: false });
416
+ return () => {
417
+ setIosSwipeGestureEnabled({ isEnabled: true });
418
+ };
419
+ }
420
+ return;
421
+ }, []);
422
+ useEffect2(() => {
423
+ const backHandler = () => {
424
+ handleClose();
425
+ return true;
426
+ };
427
+ BackHandler.addEventListener("hardwareBackPress", backHandler);
428
+ return () => {
429
+ BackHandler.removeEventListener("hardwareBackPress", backHandler);
430
+ };
431
+ }, [handleClose]);
361
432
  return /* @__PURE__ */ jsxs2(Fragment3, { children: [
362
- /* @__PURE__ */ jsx4(
363
- GameNavigationBar,
364
- {
365
- onClose: () => {
366
- closeView();
367
- }
368
- }
369
- ),
433
+ /* @__PURE__ */ jsx4(GameNavigationBar, { onClose: handleClose }),
370
434
  /* @__PURE__ */ jsx4(View3, { style: { flex: 1 }, children: /* @__PURE__ */ jsx4(PlainWebView, { ref, ...props }) })
371
435
  ] });
372
436
  });
@@ -385,10 +449,138 @@ __export(async_bridges_exports, {
385
449
  setClipboardText: () => setClipboardText
386
450
  });
387
451
 
452
+ // src/bridge-handler/useBridgeHandler.tsx
453
+ import { useCallback as useCallback2, useMemo, useRef } from "react";
454
+ function serializeError(error) {
455
+ return JSON.stringify(error, (_, value) => {
456
+ if (value instanceof Error) {
457
+ return {
458
+ name: value.name,
459
+ message: value.message,
460
+ stack: value.stack,
461
+ __isError: true
462
+ };
463
+ }
464
+ return value;
465
+ });
466
+ }
467
+ function methodHandler({
468
+ args,
469
+ eventId,
470
+ functionName,
471
+ handlerMap,
472
+ injectJavaScript
473
+ }) {
474
+ const func = async (...args2) => {
475
+ const result = await handlerMap[functionName](...args2);
476
+ return result;
477
+ };
478
+ if (!func) {
479
+ console.error(`${functionName} is not a function`);
480
+ return;
481
+ }
482
+ func(...args).then((result) => {
483
+ injectJavaScript?.(`
484
+ window.__BEDROCK_NATIVE_EMITTER.emit('${functionName}/resolve/${eventId}', ${JSON.stringify(result, null, 0)});
485
+ `);
486
+ }).catch((error) => {
487
+ const serializedError = serializeError(error);
488
+ injectJavaScript?.(`
489
+ window.__BEDROCK_NATIVE_EMITTER.emit('${functionName}/reject/${eventId}', ${serializedError});
490
+ `);
491
+ });
492
+ }
493
+ var globalEventListenerMap = /* @__PURE__ */ new Map();
494
+ function useBridgeHandler({
495
+ onMessage,
496
+ constantHandlerMap,
497
+ asyncHandlerMap,
498
+ eventListenerMap,
499
+ injectedJavaScript: originalInjectedJavaScript
500
+ }) {
501
+ const ref = useRef(null);
502
+ const injectedJavaScript = useMemo(
503
+ () => [
504
+ `window.__CONSTANT_HANDLER_MAP = ${JSON.stringify(
505
+ Object.entries(constantHandlerMap).reduce(
506
+ (acc, [key, value]) => {
507
+ acc[key] = typeof value === "function" ? value() : value;
508
+ return acc;
509
+ },
510
+ {}
511
+ )
512
+ )}`,
513
+ originalInjectedJavaScript,
514
+ "true"
515
+ ].join("\n"),
516
+ [constantHandlerMap, originalInjectedJavaScript]
517
+ );
518
+ const createHandleOnEvent = (functionName, eventId) => (response) => {
519
+ ref.current?.injectJavaScript(`
520
+ window.__BEDROCK_NATIVE_EMITTER.emit('${functionName}/onEvent/${eventId}', ${JSON.stringify(response, null, 0)});
521
+ `);
522
+ };
523
+ const createHandleOnError = (functionName, eventId) => (error) => {
524
+ ref.current?.injectJavaScript(`
525
+ window.__BEDROCK_NATIVE_EMITTER.emit('${functionName}/onError/${eventId}', ${JSON.stringify(error, null, 0)});
526
+ `);
527
+ };
528
+ const $onMessage = useCallback2(
529
+ async (e) => {
530
+ onMessage?.(e);
531
+ const data = JSON.parse(e.nativeEvent.data);
532
+ if (typeof data !== "object" || data === null || typeof data.functionName !== "string" || typeof data.eventId !== "string" || typeof data.type !== "string" || !["addEventListener", "removeEventListener", "method"].includes(data.type)) {
533
+ return;
534
+ }
535
+ switch (data.type) {
536
+ case "addEventListener": {
537
+ const handleOnEvent = createHandleOnEvent(data.functionName, data.eventId);
538
+ const handleOnError = createHandleOnError(data.functionName, data.eventId);
539
+ const remove = eventListenerMap[data.functionName]?.({
540
+ onEvent: handleOnEvent,
541
+ onError: handleOnError,
542
+ options: data.args
543
+ });
544
+ if (remove) {
545
+ globalEventListenerMap.set(`${data.functionName}/${data.eventId}`, remove);
546
+ }
547
+ break;
548
+ }
549
+ case "removeEventListener": {
550
+ const key = `${data.functionName}/${data.eventId}`;
551
+ const remove = globalEventListenerMap.get(key);
552
+ remove?.();
553
+ globalEventListenerMap.delete(key);
554
+ break;
555
+ }
556
+ case "method": {
557
+ methodHandler({
558
+ args: data.args,
559
+ eventId: data.eventId,
560
+ functionName: data.functionName,
561
+ handlerMap: asyncHandlerMap,
562
+ injectJavaScript: ref.current?.injectJavaScript
563
+ });
564
+ break;
565
+ }
566
+ }
567
+ },
568
+ // eslint-disable-next-line react-hooks/exhaustive-deps
569
+ [onMessage]
570
+ );
571
+ return {
572
+ ref,
573
+ injectedJavaScript,
574
+ onMessage: $onMessage
575
+ };
576
+ }
577
+
388
578
  // src/constant-bridges.ts
389
579
  var constant_bridges_exports = {};
390
580
  __export(constant_bridges_exports, {
391
- getOperationalEnvironment: () => getOperationalEnvironment
581
+ getDeviceId: () => getDeviceId,
582
+ getOperationalEnvironment: () => getOperationalEnvironment,
583
+ getTossAppVersion: () => getTossAppVersion
392
584
  });
393
585
 
394
586
  // src/env.ts
@@ -405,6 +597,7 @@ __export(event_bridges_exports, {
405
597
  // src/components/WebView.tsx
406
598
  import { jsx as jsx5 } from "react/jsx-runtime";
407
599
  var appsInTossGlobals = getAppsInTossGlobals();
600
+ var TYPES = ["partner", "external", "game"];
408
601
  var WEBVIEW_TYPES = {
409
602
  partner: PartnerWebViewScreen,
410
603
  external: ExternalWebViewScreen,
@@ -433,45 +626,65 @@ function getWebViewUri(local) {
433
626
  return url.toString();
434
627
  }
435
628
  function WebView({ type, local, onMessage, ...props }) {
436
- const uri = useMemo(() => getWebViewUri(local), [local]);
629
+ if (!TYPES.includes(type)) {
630
+ throw new Error(`Invalid WebView type: '${type}'`);
631
+ }
632
+ const bedrockEvent = useBedrockEvent();
633
+ const uri = useMemo2(() => getWebViewUri(local), [local]);
634
+ const top = useSafeAreaTop2();
635
+ const bottom = useSafeAreaBottom();
437
636
  const handler = useBridgeHandler({
438
637
  onMessage,
439
638
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
440
- eventListenerMap: event_bridges_exports,
639
+ eventListenerMap: {
640
+ ...event_bridges_exports,
641
+ backEvent: ({ onEvent, onError, options }) => bedrockEvent.addEventListener("backEvent", { onEvent, onError, options })
642
+ },
441
643
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
442
644
  // @ts-expect-error
443
645
  constantHandlerMap: {
444
646
  ...bedrockConstantBridges,
445
- ...constant_bridges_exports
647
+ ...constant_bridges_exports,
648
+ getSafeAreaTop: () => top,
649
+ getSafeAreaBottom: () => bottom
446
650
  },
447
651
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
448
652
  // @ts-expect-error
449
653
  asyncHandlerMap: {
450
654
  ...bedrockAsyncBridges,
451
- ...async_bridges_exports
655
+ ...async_bridges_exports,
656
+ /** internal */
657
+ openPermissionDialog: AppsInTossModule.openPermissionDialog,
658
+ /** Storage */
659
+ getStorageItem: Storage.getItem,
660
+ setStorageItem: Storage.setItem,
661
+ removeStorageItem: Storage.removeItem,
662
+ clearItems: Storage.clearItems
452
663
  }
453
664
  });
454
- const baseProps = useMemo(() => {
665
+ const baseProps = useMemo2(() => {
455
666
  switch (type) {
456
667
  case "partner": {
457
- return {
668
+ const headerOnlyProp = {
458
669
  header: {
459
- ...props.header,
460
- icon: toIcon(appsInTossGlobals.brandIcon),
461
- title: appsInTossGlobals.brandDisplayName,
670
+ ..."header" in props ? props.header : {},
671
+ icon: toIcon(ensureValue(appsInTossGlobals.brandIcon, "icon")),
672
+ title: ensureValue(appsInTossGlobals.brandDisplayName, "displayName"),
462
673
  rightButtons: void 0
463
674
  // TODO: onClick 이벤트를 받아야 하기에 런타임에서 설정 받아야 함
464
675
  }
465
676
  };
677
+ return headerOnlyProp;
466
678
  }
467
679
  case "external": {
468
- return {
680
+ const headerOnlyProp = {
469
681
  header: {
470
- ...props.header,
471
- icon: toIcon(appsInTossGlobals.brandIcon),
472
- title: appsInTossGlobals.brandDisplayName
682
+ ..."header" in props ? props.header : {},
683
+ icon: toIcon(ensureValue(appsInTossGlobals.brandIcon, "icon")),
684
+ title: ensureValue(appsInTossGlobals.brandDisplayName, "displayName")
473
685
  }
474
686
  };
687
+ return headerOnlyProp;
475
688
  }
476
689
  default: {
477
690
  return {};
@@ -479,31 +692,40 @@ function WebView({ type, local, onMessage, ...props }) {
479
692
  }
480
693
  }, [type, props]);
481
694
  const BaseWebView = WEBVIEW_TYPES[type];
695
+ const webviewDebuggingEnabled = useMemo2(
696
+ () => getOperationalEnvironment() === "sandbox",
697
+ []
698
+ );
482
699
  return /* @__PURE__ */ jsx5(
483
700
  BaseWebView,
484
701
  {
485
702
  ref: handler.ref,
486
- ...baseProps,
487
703
  ...props,
704
+ ...baseProps,
488
705
  source: { uri },
489
706
  sharedCookiesEnabled: true,
707
+ webviewDebuggingEnabled,
490
708
  thirdPartyCookiesEnabled: true,
491
- cacheEnabled: false,
492
- cacheMode: "LOAD_NO_CACHE",
493
709
  onMessage: handler.onMessage,
494
710
  injectedJavaScript: handler.injectedJavaScript,
495
711
  injectedJavaScriptBeforeContentLoaded: handler.injectedJavaScript
496
712
  }
497
713
  );
498
714
  }
715
+ function ensureValue(value, name) {
716
+ if (value === void 0) {
717
+ throw new Error(`${name} is required`);
718
+ }
719
+ return value;
720
+ }
499
721
 
500
722
  // src/hooks/useGeolocation.ts
501
- import { useState, useEffect as useEffect2 } from "react";
723
+ import { useState, useEffect as useEffect3 } from "react";
502
724
  import { useVisibility } from "react-native-bedrock";
503
725
  function useGeolocation({ accuracy, distanceInterval, timeInterval }) {
504
726
  const isVisible = useVisibility();
505
727
  const [location, setLocation] = useState(null);
506
- useEffect2(() => {
728
+ useEffect3(() => {
507
729
  if (!isVisible) {
508
730
  return;
509
731
  }
@@ -533,6 +755,7 @@ var Accuracy2 = /* @__PURE__ */ ((Accuracy3) => {
533
755
  export {
534
756
  Accuracy2 as Accuracy,
535
757
  AppsInToss,
758
+ Storage,
536
759
  TossPay,
537
760
  WebView,
538
761
  appLogin,
@@ -541,7 +764,9 @@ export {
541
764
  fetchContacts,
542
765
  getClipboardText,
543
766
  getCurrentLocation,
767
+ getDeviceId,
544
768
  getOperationalEnvironment,
769
+ getTossAppVersion,
545
770
  openCamera,
546
771
  setClipboardText,
547
772
  startUpdateLocation,
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@apps-in-toss/framework",
3
3
  "type": "module",
4
- "version": "0.0.0-dev.1743134070910",
4
+ "version": "0.0.0-dev.1744801739343",
5
5
  "description": "The framework for Apps In Toss",
6
6
  "scripts": {
7
7
  "prepack": "yarn build",
8
8
  "typecheck": "tsc --noEmit",
9
+ "test:no-parallel": "vitest --no-watch",
9
10
  "lint": "eslint .",
10
11
  "build": "tsup"
11
12
  },
@@ -55,32 +56,40 @@
55
56
  "ait": "./bin/ait.js"
56
57
  },
57
58
  "dependencies": {
58
- "@apps-in-toss/cli": "0.0.0-dev.1743134070910",
59
- "@apps-in-toss/plugins": "0.0.0-dev.1743134070910"
59
+ "@apps-in-toss/cli": "0.0.0-dev.1744801739343",
60
+ "@apps-in-toss/plugins": "0.0.0-dev.1744801739343",
61
+ "es-hangul": "^2.3.2"
60
62
  },
61
63
  "devDependencies": {
62
- "@react-native-bedrock/mpack-next": "0.0.14",
63
- "@react-native-bedrock/native": "0.0.14",
64
- "@toss-design-system/react-native": "^0.4.1",
64
+ "@react-native-bedrock/mpack-next": "0.0.0-dev.1744801739343",
65
+ "@react-native-bedrock/native": "workspace:*",
66
+ "@toss-design-system/react-native": "^0.5.0",
67
+ "@types/kill-port": "^2.0.1",
65
68
  "@types/react": "18.3.3",
69
+ "@types/yauzl": "^2.10.3",
70
+ "es-toolkit": "^1.34.1",
66
71
  "eslint": "^9.7.0",
72
+ "execa": "^9.5.2",
73
+ "kill-port": "^2.0.1",
67
74
  "react": "18.2.0",
68
75
  "react-native": "0.72.6",
69
- "react-native-bedrock": "0.0.14",
76
+ "react-native-bedrock": "0.0.0-dev.1744801739343",
70
77
  "tsup": "^8.3.5",
71
78
  "typescript": "4.9.5",
72
- "vitest": "^3.0.3"
79
+ "vitest": "^3.0.3",
80
+ "workspace-tools": "^0.38.2",
81
+ "yauzl": "^3.2.0"
73
82
  },
74
83
  "peerDependencies": {
75
- "@react-native-bedrock/native": "*",
76
- "@toss-design-system/react-native": "^0.4.1",
84
+ "@react-native-bedrock/native": ">= 0.0.18",
85
+ "@toss-design-system/react-native": ">= 0.5.0",
77
86
  "@types/react": "*",
78
87
  "react": "*",
79
88
  "react-native": "*",
80
- "react-native-bedrock": "*"
89
+ "react-native-bedrock": ">= 0.0.18"
81
90
  },
82
91
  "publishConfig": {
83
92
  "access": "public"
84
93
  },
85
- "gitHead": "5acf03f70ea94cd53a91b74d34b1a196e45604f3"
94
+ "gitHead": "aebed4e46de1690956a3b9be49b55811b5ddfa24"
86
95
  }
@@ -1 +1,3 @@
1
1
  export * from './native-modules/getOperationalEnvironment.js';
2
+ export * from './native-modules/getTossAppVersion.js';
3
+ export * from './native-modules/getDeviceId.js';