@apps-in-toss/framework 1.12.0 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,25 @@
1
1
  # @apps-in-toss/framework
2
2
 
3
- Hub package for Apps In Toss
3
+ react-native framework for building [apps in toss](https://toss.im/apps-in-toss) services.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ # yarn
9
+ yarn add @apps-in-toss/framework
10
+
11
+ # npm
12
+ npm install @apps-in-toss/framework
13
+
14
+ # pnpm
15
+ pnpm add @apps-in-toss/framework
16
+ ```
17
+
18
+ ## Links
19
+
20
+ - [Document](https://developers-apps-in-toss.toss.im)
21
+ - [Support](https://techchat-apps-in-toss.toss.im)
22
+ - [Examples](https://github.com/toss/apps-in-toss-examples)
4
23
 
5
24
  ## License
6
25
 
package/dist/index.cjs CHANGED
@@ -38,7 +38,9 @@ __export(src_exports, {
38
38
  OverlayProvider: () => import_private10.OverlayProvider,
39
39
  WebView: () => WebView,
40
40
  env: () => env,
41
+ homeEvent: () => homeEvent,
41
42
  loadFullScreenAd: () => loadFullScreenAd,
43
+ safeAreaInsetsChange: () => safeAreaInsetsChange,
42
44
  showFullScreenAd: () => showFullScreenAd,
43
45
  useCreateUserAgent: () => useCreateUserAgent,
44
46
  useGeolocation: () => useGeolocation,
@@ -741,6 +743,46 @@ function useCloseConfirm() {
741
743
  );
742
744
  }
743
745
 
746
+ // src/utils/eventEmitter.ts
747
+ var EventEmitter = class {
748
+ listeners = {};
749
+ subscribe(event, listener) {
750
+ if (!this.listeners[event]) {
751
+ this.listeners[event] = [];
752
+ }
753
+ this.listeners[event].push(listener);
754
+ return () => {
755
+ this.listeners[event] = this.listeners[event]?.filter((l) => l !== listener) ?? [];
756
+ };
757
+ }
758
+ hasSubscriptions(event) {
759
+ return this.listeners[event] != null && this.listeners[event].length > 0;
760
+ }
761
+ clearSubscriptions(event) {
762
+ this.listeners[event] = [];
763
+ }
764
+ emit(event, ...args) {
765
+ if (!this.listeners[event]) {
766
+ return;
767
+ }
768
+ this.listeners[event].forEach((listener) => listener(...args));
769
+ }
770
+ };
771
+ var eventEmitter = new EventEmitter();
772
+ function createEvent(event) {
773
+ return {
774
+ name: event,
775
+ subscribe: (listener) => eventEmitter.subscribe(event, listener),
776
+ hasSubscriptions: () => eventEmitter.hasSubscriptions(event),
777
+ clearSubscriptions: () => eventEmitter.clearSubscriptions(event),
778
+ emit: (data) => eventEmitter.emit(event, data)
779
+ };
780
+ }
781
+
782
+ // src/events.ts
783
+ var homeEvent = createEvent("homeEvent");
784
+ var safeAreaInsetsChange = createEvent("safeAreaInsetsChange");
785
+
744
786
  // src/components/NavigationBar/common/useNavigationBarLogging.tsx
745
787
  var import_native_modules7 = require("@apps-in-toss/native-modules");
746
788
  var import_react_native13 = require("@granite-js/react-native");
@@ -855,7 +897,11 @@ function useNavigationEvent() {
855
897
  },
856
898
  handleHomeButtonClick: () => {
857
899
  logging.homeButtonClick();
858
- navigation.navigate("/");
900
+ if (homeEvent.hasSubscriptions()) {
901
+ homeEvent.emit({});
902
+ } else {
903
+ navigation.navigate("/");
904
+ }
859
905
  },
860
906
  handleCloseButtonClick: () => {
861
907
  logging.closeButtonClick();
@@ -1083,11 +1129,11 @@ function HomeShortcutMenu() {
1083
1129
  );
1084
1130
  }
1085
1131
 
1086
- // src/core/hooks/useMoreButtonBottomSheet/PermissionsMenu.tsx
1132
+ // src/core/hooks/useMoreButtonBottomSheet/SettingsMenu.tsx
1087
1133
  var import_native_modules11 = require("@apps-in-toss/native-modules");
1088
1134
  var import_react_native20 = require("@granite-js/react-native");
1089
1135
  var import_jsx_runtime8 = require("react/jsx-runtime");
1090
- function PermissionsMenu() {
1136
+ function SettingsMenu() {
1091
1137
  const globals = getAppsInTossGlobals();
1092
1138
  if ((0, import_native_modules11.getOperationalEnvironment)() === "sandbox") {
1093
1139
  return null;
@@ -1095,10 +1141,10 @@ function PermissionsMenu() {
1095
1141
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1096
1142
  Menu,
1097
1143
  {
1098
- title: "\uAD8C\uD55C \uC124\uC815",
1144
+ title: "\uC124\uC815",
1099
1145
  iconURL: "https://static.toss.im/icons/png/4x/icon-setting-mono.png",
1100
1146
  onPress: () => (0, import_react_native20.openURL)(
1101
- `servicetoss://apps-in-toss-menu/permissions?appName=${import_react_native20.Granite.appName}&displayAppName=${globals.brandDisplayName}`
1147
+ `servicetoss://apps-in-toss-menu/settings?appName=${import_react_native20.Granite.appName}&displayAppName=${globals.brandDisplayName}`
1102
1148
  )
1103
1149
  }
1104
1150
  );
@@ -1128,7 +1174,7 @@ var MIN_VERSION = {
1128
1174
  android: "5.246.0",
1129
1175
  ios: "5.246.0"
1130
1176
  },
1131
- PERMISSIONS_MENU: {
1177
+ SETTINGS_MENU: {
1132
1178
  android: "5.238.0",
1133
1179
  ios: "5.237.0"
1134
1180
  }
@@ -1144,7 +1190,7 @@ function useMoreButtonBottomSheet() {
1144
1190
  const isHomeShortcutSupported = (0, import_native_modules12.isMinVersionSupported)(MIN_VERSION.HOME_SHORTCUT_MENU);
1145
1191
  const isBottomSheetSupported = (0, import_native_modules12.isMinVersionSupported)(MIN_VERSION.BOTTOM_SHEET);
1146
1192
  const isShareListMenuSupported = (0, import_native_modules12.isMinVersionSupported)(MIN_VERSION.SHARE_LIST_MENU);
1147
- const [isPermissionsMenuSupported, setIsPermissionsMenuSupported] = (0, import_react15.useState)(false);
1193
+ const isSettingsMenuSupported = (0, import_native_modules12.isMinVersionSupported)(MIN_VERSION.SETTINGS_MENU);
1148
1194
  (0, import_react15.useEffect)(() => {
1149
1195
  if (!isBottomSheetSupported) {
1150
1196
  return;
@@ -1157,16 +1203,6 @@ function useMoreButtonBottomSheet() {
1157
1203
  onError: (error) => console.error("\uBA54\uB274 \uBAA9\uB85D\uC744 \uAC00\uC838\uC624\uB294 \uB370 \uC2E4\uD328\uD588\uC5B4\uC694:", error)
1158
1204
  }
1159
1205
  );
1160
- if ((0, import_native_modules12.isMinVersionSupported)(MIN_VERSION.PERMISSIONS_MENU)) {
1161
- import_native_modules12.INTERNAL__appBridgeHandler.invokeAppBridgeMethod(
1162
- "getAllPermission",
1163
- {},
1164
- {
1165
- onSuccess: (permissions) => setIsPermissionsMenuSupported(permissions.length > 0),
1166
- onError: (error) => console.error("\uAD8C\uD55C \uBAA9\uB85D\uC744 \uAC00\uC838\uC624\uB294 \uB370 \uC2E4\uD328\uD588\uC5B4\uC694:", error)
1167
- }
1168
- );
1169
- }
1170
1206
  }, [isBottomSheetSupported]);
1171
1207
  const onClickHandler = async () => {
1172
1208
  logging.open();
@@ -1218,7 +1254,7 @@ function useMoreButtonBottomSheet() {
1218
1254
  )),
1219
1255
  isHomeShortcutSupported && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(HomeShortcutMenu, {}),
1220
1256
  isShareListMenuSupported && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(AppShareListMenu, {}),
1221
- isPermissionsMenuSupported && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PermissionsMenu, {})
1257
+ isSettingsMenuSupported && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SettingsMenu, {})
1222
1258
  ] })
1223
1259
  }
1224
1260
  ) });
@@ -2105,40 +2141,18 @@ function parseNativeEventData(data) {
2105
2141
  }
2106
2142
  }
2107
2143
 
2108
- // src/core/hooks/useSafeAreaInsetsEmitter.tsx
2144
+ // src/core/hooks/useSafeAreaInsetsEvent.tsx
2109
2145
  var import_react_native_safe_area_context3 = require("@granite-js/native/react-native-safe-area-context");
2110
2146
  var import_react23 = require("react");
2111
- var EventEmitter = class {
2112
- listeners = {};
2113
- on(event, listener) {
2114
- if (!this.listeners[event]) {
2115
- this.listeners[event] = [];
2116
- }
2117
- this.listeners[event].push(listener);
2118
- }
2119
- emit(event, ...args) {
2120
- if (!this.listeners[event]) {
2121
- return;
2122
- }
2123
- this.listeners[event].forEach((listener) => listener(...args));
2124
- }
2125
- off(event, listener) {
2126
- if (!this.listeners[event]) {
2127
- return;
2128
- }
2129
- this.listeners[event] = this.listeners[event].filter((l) => l !== listener);
2130
- }
2131
- };
2132
- function useSafeAreaInsetsEmitter() {
2147
+ function useSafeAreaInsetsEvent() {
2133
2148
  const insets = (0, import_react_native_safe_area_context3.useSafeAreaInsets)();
2134
- const emitter = (0, import_react23.useMemo)(() => new EventEmitter(), []);
2135
2149
  (0, import_react23.useEffect)(() => {
2136
- emitter.emit("safeAreaInsetsChange", insets);
2137
- return () => {
2138
- emitter.off("safeAreaInsetsChange", (listener) => listener(insets));
2139
- };
2140
- }, [emitter, insets]);
2141
- return emitter;
2150
+ safeAreaInsetsChange.emit(insets);
2151
+ }, [insets]);
2152
+ (0, import_react23.useEffect)(() => {
2153
+ return () => safeAreaInsetsChange.clearSubscriptions();
2154
+ }, []);
2155
+ return safeAreaInsetsChange;
2142
2156
  }
2143
2157
 
2144
2158
  // src/core/hooks/useWebBackHandler.tsx
@@ -2251,10 +2265,8 @@ function useWebBackHandler(webViewInitialURL, webViewRef) {
2251
2265
  ]);
2252
2266
  const handleWebHome = (0, import_react25.useCallback)(() => {
2253
2267
  logging.homeButtonClick();
2254
- if (hasWebBackEvent) {
2255
- for (const handler of webBackHandlersRef) {
2256
- handler();
2257
- }
2268
+ if (homeEvent.hasSubscriptions()) {
2269
+ homeEvent.emit({});
2258
2270
  return;
2259
2271
  }
2260
2272
  webViewRef.current?.injectJavaScript(`
@@ -2631,7 +2643,7 @@ function WebView({ type, local, onMessage, ...props }) {
2631
2643
  const insets = (0, import_react_native_safe_area_context4.useSafeAreaInsets)();
2632
2644
  const global2 = getAppsInTossGlobals();
2633
2645
  const navigationBarContext = useNavigationBarContext();
2634
- const safeAreaInsetsEmitter = useSafeAreaInsetsEmitter();
2646
+ const safeAreaInsetsEvent = useSafeAreaInsetsEvent();
2635
2647
  const [allowsBackForwardNavigationGestures, setAllowsBackForwardNavigationGestures] = (0, import_react28.useState)(
2636
2648
  props.allowsBackForwardNavigationGestures
2637
2649
  );
@@ -2646,13 +2658,9 @@ function WebView({ type, local, onMessage, ...props }) {
2646
2658
  webBackHandler.removeEventListener(onEvent);
2647
2659
  };
2648
2660
  },
2661
+ homeEvent: ({ onEvent }) => homeEvent.subscribe(onEvent),
2649
2662
  updateLocationEvent: ({ onEvent, onError, options }) => import_native_modules24.appsInTossEvent.addEventListener("updateLocationEvent", { onEvent, onError, options }),
2650
- safeAreaInsetsChange: ({ onEvent }) => {
2651
- safeAreaInsetsEmitter.on("safeAreaInsetsChange", onEvent);
2652
- return () => {
2653
- safeAreaInsetsEmitter.off("safeAreaInsetsChange", onEvent);
2654
- };
2655
- },
2663
+ safeAreaInsetsChange: ({ onEvent }) => safeAreaInsetsEvent.subscribe(onEvent),
2656
2664
  /** @internal */
2657
2665
  appBridgeCallbackEvent: ({ onEvent, onError, options }) => import_native_modules24.appsInTossEvent.addEventListener("appBridgeCallbackEvent", { onEvent, onError, options }),
2658
2666
  /** AdMobV2 */
@@ -4096,7 +4104,9 @@ var Analytics2 = {
4096
4104
  OverlayProvider,
4097
4105
  WebView,
4098
4106
  env,
4107
+ homeEvent,
4099
4108
  loadFullScreenAd,
4109
+ safeAreaInsetsChange,
4100
4110
  showFullScreenAd,
4101
4111
  useCreateUserAgent,
4102
4112
  useGeolocation,
package/dist/index.d.cts CHANGED
@@ -9,6 +9,7 @@ import { ExternalWebViewScreenProps } from '@toss/tds-react-native';
9
9
  import { StartUpdateLocationOptions, Location, LoadFullScreenAdParams, ShowFullScreenAdParams } from '@apps-in-toss/types';
10
10
  export * from '@apps-in-toss/types';
11
11
  export { FetchTossAdOptions, LoadFullScreenAdEvent, LoadFullScreenAdOptions, LoadFullScreenAdParams, ShowFullScreenAdEvent, ShowFullScreenAdOptions, ShowFullScreenAdParams, TossAdEventLogParams } from '@apps-in-toss/types';
12
+ import { EdgeInsets } from '@granite-js/native/react-native-safe-area-context';
12
13
  import { onVisibilityChangedByTransparentServiceWeb } from '@apps-in-toss/native-modules';
13
14
  export * from '@apps-in-toss/native-modules';
14
15
  export { OverlayProvider, useOverlay } from '@toss/tds-react-native/private';
@@ -171,6 +172,21 @@ declare const env: {
171
172
  getAppName: () => string;
172
173
  };
173
174
 
175
+ declare const homeEvent: {
176
+ name: string;
177
+ subscribe: (listener: (data: any) => void) => () => void;
178
+ hasSubscriptions: () => boolean;
179
+ clearSubscriptions: () => void;
180
+ emit: (data: any) => void;
181
+ };
182
+ declare const safeAreaInsetsChange: {
183
+ name: string;
184
+ subscribe: (listener: (data: EdgeInsets) => void) => () => void;
185
+ hasSubscriptions: () => boolean;
186
+ clearSubscriptions: () => void;
187
+ emit: (data: EdgeInsets) => void;
188
+ };
189
+
174
190
  declare const INTERNAL__onVisibilityChangedByTransparentServiceWeb: typeof onVisibilityChangedByTransparentServiceWeb;
175
191
 
176
192
  declare const loadFullScreenAd: {
@@ -238,4 +254,4 @@ declare const Analytics: {
238
254
  Area: ({ children, params: _params, ...props }: _apps_in_toss_analytics.LoggingAreaProps) => react_jsx_runtime.JSX.Element;
239
255
  };
240
256
 
241
- export { type AdError, Analytics, AppsInToss, type BannerSlotCallbacks, type BannerSlotErrorPayload, type BannerSlotEventPayload, type ExternalWebViewProps, type GameWebViewProps, INTERNAL__onVisibilityChangedByTransparentServiceWeb, InlineAd, type InlineAdProps, type InlineAdTheme, type InlineAdTone, type InlineAdVariant, type PartnerWebViewProps, type UseGeolocationOptions, WebView, type WebViewProps, env, loadFullScreenAd, showFullScreenAd, useCreateUserAgent, useGeolocation, useTopNavigation, useWaitForReturnNavigator };
257
+ export { type AdError, Analytics, AppsInToss, type BannerSlotCallbacks, type BannerSlotErrorPayload, type BannerSlotEventPayload, type ExternalWebViewProps, type GameWebViewProps, INTERNAL__onVisibilityChangedByTransparentServiceWeb, InlineAd, type InlineAdProps, type InlineAdTheme, type InlineAdTone, type InlineAdVariant, type PartnerWebViewProps, type UseGeolocationOptions, WebView, type WebViewProps, env, homeEvent, loadFullScreenAd, safeAreaInsetsChange, showFullScreenAd, useCreateUserAgent, useGeolocation, useTopNavigation, useWaitForReturnNavigator };
package/dist/index.d.ts CHANGED
@@ -9,6 +9,7 @@ import { ExternalWebViewScreenProps } from '@toss/tds-react-native';
9
9
  import { StartUpdateLocationOptions, Location, LoadFullScreenAdParams, ShowFullScreenAdParams } from '@apps-in-toss/types';
10
10
  export * from '@apps-in-toss/types';
11
11
  export { FetchTossAdOptions, LoadFullScreenAdEvent, LoadFullScreenAdOptions, LoadFullScreenAdParams, ShowFullScreenAdEvent, ShowFullScreenAdOptions, ShowFullScreenAdParams, TossAdEventLogParams } from '@apps-in-toss/types';
12
+ import { EdgeInsets } from '@granite-js/native/react-native-safe-area-context';
12
13
  import { onVisibilityChangedByTransparentServiceWeb } from '@apps-in-toss/native-modules';
13
14
  export * from '@apps-in-toss/native-modules';
14
15
  export { OverlayProvider, useOverlay } from '@toss/tds-react-native/private';
@@ -171,6 +172,21 @@ declare const env: {
171
172
  getAppName: () => string;
172
173
  };
173
174
 
175
+ declare const homeEvent: {
176
+ name: string;
177
+ subscribe: (listener: (data: any) => void) => () => void;
178
+ hasSubscriptions: () => boolean;
179
+ clearSubscriptions: () => void;
180
+ emit: (data: any) => void;
181
+ };
182
+ declare const safeAreaInsetsChange: {
183
+ name: string;
184
+ subscribe: (listener: (data: EdgeInsets) => void) => () => void;
185
+ hasSubscriptions: () => boolean;
186
+ clearSubscriptions: () => void;
187
+ emit: (data: EdgeInsets) => void;
188
+ };
189
+
174
190
  declare const INTERNAL__onVisibilityChangedByTransparentServiceWeb: typeof onVisibilityChangedByTransparentServiceWeb;
175
191
 
176
192
  declare const loadFullScreenAd: {
@@ -238,4 +254,4 @@ declare const Analytics: {
238
254
  Area: ({ children, params: _params, ...props }: _apps_in_toss_analytics.LoggingAreaProps) => react_jsx_runtime.JSX.Element;
239
255
  };
240
256
 
241
- export { type AdError, Analytics, AppsInToss, type BannerSlotCallbacks, type BannerSlotErrorPayload, type BannerSlotEventPayload, type ExternalWebViewProps, type GameWebViewProps, INTERNAL__onVisibilityChangedByTransparentServiceWeb, InlineAd, type InlineAdProps, type InlineAdTheme, type InlineAdTone, type InlineAdVariant, type PartnerWebViewProps, type UseGeolocationOptions, WebView, type WebViewProps, env, loadFullScreenAd, showFullScreenAd, useCreateUserAgent, useGeolocation, useTopNavigation, useWaitForReturnNavigator };
257
+ export { type AdError, Analytics, AppsInToss, type BannerSlotCallbacks, type BannerSlotErrorPayload, type BannerSlotEventPayload, type ExternalWebViewProps, type GameWebViewProps, INTERNAL__onVisibilityChangedByTransparentServiceWeb, InlineAd, type InlineAdProps, type InlineAdTheme, type InlineAdTone, type InlineAdVariant, type PartnerWebViewProps, type UseGeolocationOptions, WebView, type WebViewProps, env, homeEvent, loadFullScreenAd, safeAreaInsetsChange, showFullScreenAd, useCreateUserAgent, useGeolocation, useTopNavigation, useWaitForReturnNavigator };
package/dist/index.js CHANGED
@@ -707,6 +707,46 @@ function useCloseConfirm() {
707
707
  );
708
708
  }
709
709
 
710
+ // src/utils/eventEmitter.ts
711
+ var EventEmitter = class {
712
+ listeners = {};
713
+ subscribe(event, listener) {
714
+ if (!this.listeners[event]) {
715
+ this.listeners[event] = [];
716
+ }
717
+ this.listeners[event].push(listener);
718
+ return () => {
719
+ this.listeners[event] = this.listeners[event]?.filter((l) => l !== listener) ?? [];
720
+ };
721
+ }
722
+ hasSubscriptions(event) {
723
+ return this.listeners[event] != null && this.listeners[event].length > 0;
724
+ }
725
+ clearSubscriptions(event) {
726
+ this.listeners[event] = [];
727
+ }
728
+ emit(event, ...args) {
729
+ if (!this.listeners[event]) {
730
+ return;
731
+ }
732
+ this.listeners[event].forEach((listener) => listener(...args));
733
+ }
734
+ };
735
+ var eventEmitter = new EventEmitter();
736
+ function createEvent(event) {
737
+ return {
738
+ name: event,
739
+ subscribe: (listener) => eventEmitter.subscribe(event, listener),
740
+ hasSubscriptions: () => eventEmitter.hasSubscriptions(event),
741
+ clearSubscriptions: () => eventEmitter.clearSubscriptions(event),
742
+ emit: (data) => eventEmitter.emit(event, data)
743
+ };
744
+ }
745
+
746
+ // src/events.ts
747
+ var homeEvent = createEvent("homeEvent");
748
+ var safeAreaInsetsChange = createEvent("safeAreaInsetsChange");
749
+
710
750
  // src/components/NavigationBar/common/useNavigationBarLogging.tsx
711
751
  import { INTERNAL__module as INTERNAL__module4 } from "@apps-in-toss/native-modules";
712
752
  import { Granite as Granite4 } from "@granite-js/react-native";
@@ -821,7 +861,11 @@ function useNavigationEvent() {
821
861
  },
822
862
  handleHomeButtonClick: () => {
823
863
  logging.homeButtonClick();
824
- navigation.navigate("/");
864
+ if (homeEvent.hasSubscriptions()) {
865
+ homeEvent.emit({});
866
+ } else {
867
+ navigation.navigate("/");
868
+ }
825
869
  },
826
870
  handleCloseButtonClick: () => {
827
871
  logging.closeButtonClick();
@@ -1049,11 +1093,11 @@ function HomeShortcutMenu() {
1049
1093
  );
1050
1094
  }
1051
1095
 
1052
- // src/core/hooks/useMoreButtonBottomSheet/PermissionsMenu.tsx
1096
+ // src/core/hooks/useMoreButtonBottomSheet/SettingsMenu.tsx
1053
1097
  import { getOperationalEnvironment as getOperationalEnvironment3 } from "@apps-in-toss/native-modules";
1054
1098
  import { Granite as Granite6, openURL as openURL4 } from "@granite-js/react-native";
1055
1099
  import { jsx as jsx8 } from "react/jsx-runtime";
1056
- function PermissionsMenu() {
1100
+ function SettingsMenu() {
1057
1101
  const globals = getAppsInTossGlobals();
1058
1102
  if (getOperationalEnvironment3() === "sandbox") {
1059
1103
  return null;
@@ -1061,10 +1105,10 @@ function PermissionsMenu() {
1061
1105
  return /* @__PURE__ */ jsx8(
1062
1106
  Menu,
1063
1107
  {
1064
- title: "\uAD8C\uD55C \uC124\uC815",
1108
+ title: "\uC124\uC815",
1065
1109
  iconURL: "https://static.toss.im/icons/png/4x/icon-setting-mono.png",
1066
1110
  onPress: () => openURL4(
1067
- `servicetoss://apps-in-toss-menu/permissions?appName=${Granite6.appName}&displayAppName=${globals.brandDisplayName}`
1111
+ `servicetoss://apps-in-toss-menu/settings?appName=${Granite6.appName}&displayAppName=${globals.brandDisplayName}`
1068
1112
  )
1069
1113
  }
1070
1114
  );
@@ -1094,7 +1138,7 @@ var MIN_VERSION = {
1094
1138
  android: "5.246.0",
1095
1139
  ios: "5.246.0"
1096
1140
  },
1097
- PERMISSIONS_MENU: {
1141
+ SETTINGS_MENU: {
1098
1142
  android: "5.238.0",
1099
1143
  ios: "5.237.0"
1100
1144
  }
@@ -1110,7 +1154,7 @@ function useMoreButtonBottomSheet() {
1110
1154
  const isHomeShortcutSupported = isMinVersionSupported2(MIN_VERSION.HOME_SHORTCUT_MENU);
1111
1155
  const isBottomSheetSupported = isMinVersionSupported2(MIN_VERSION.BOTTOM_SHEET);
1112
1156
  const isShareListMenuSupported = isMinVersionSupported2(MIN_VERSION.SHARE_LIST_MENU);
1113
- const [isPermissionsMenuSupported, setIsPermissionsMenuSupported] = useState4(false);
1157
+ const isSettingsMenuSupported = isMinVersionSupported2(MIN_VERSION.SETTINGS_MENU);
1114
1158
  useEffect6(() => {
1115
1159
  if (!isBottomSheetSupported) {
1116
1160
  return;
@@ -1123,16 +1167,6 @@ function useMoreButtonBottomSheet() {
1123
1167
  onError: (error) => console.error("\uBA54\uB274 \uBAA9\uB85D\uC744 \uAC00\uC838\uC624\uB294 \uB370 \uC2E4\uD328\uD588\uC5B4\uC694:", error)
1124
1168
  }
1125
1169
  );
1126
- if (isMinVersionSupported2(MIN_VERSION.PERMISSIONS_MENU)) {
1127
- INTERNAL__appBridgeHandler.invokeAppBridgeMethod(
1128
- "getAllPermission",
1129
- {},
1130
- {
1131
- onSuccess: (permissions) => setIsPermissionsMenuSupported(permissions.length > 0),
1132
- onError: (error) => console.error("\uAD8C\uD55C \uBAA9\uB85D\uC744 \uAC00\uC838\uC624\uB294 \uB370 \uC2E4\uD328\uD588\uC5B4\uC694:", error)
1133
- }
1134
- );
1135
- }
1136
1170
  }, [isBottomSheetSupported]);
1137
1171
  const onClickHandler = async () => {
1138
1172
  logging.open();
@@ -1184,7 +1218,7 @@ function useMoreButtonBottomSheet() {
1184
1218
  )),
1185
1219
  isHomeShortcutSupported && /* @__PURE__ */ jsx9(HomeShortcutMenu, {}),
1186
1220
  isShareListMenuSupported && /* @__PURE__ */ jsx9(AppShareListMenu, {}),
1187
- isPermissionsMenuSupported && /* @__PURE__ */ jsx9(PermissionsMenu, {})
1221
+ isSettingsMenuSupported && /* @__PURE__ */ jsx9(SettingsMenu, {})
1188
1222
  ] })
1189
1223
  }
1190
1224
  ) });
@@ -1426,7 +1460,7 @@ import { useSafeAreaInsets as useSafeAreaInsets4 } from "@granite-js/native/reac
1426
1460
  import { getSchemeUri as getSchemeUri8 } from "@granite-js/react-native";
1427
1461
  import { ExternalWebViewScreen, tdsEvent } from "@toss/tds-react-native";
1428
1462
  import { useSafeAreaBottom, useSafeAreaTop as useSafeAreaTop3 } from "@toss/tds-react-native/private";
1429
- import { useEffect as useEffect13, useMemo as useMemo7, useRef as useRef6, useState as useState6 } from "react";
1463
+ import { useEffect as useEffect13, useMemo as useMemo6, useRef as useRef6, useState as useState6 } from "react";
1430
1464
  import { BackHandler as BackHandler2, Linking, NativeModules as NativeModules3, Platform as Platform6 } from "react-native";
1431
1465
 
1432
1466
  // src/components/GameWebView.tsx
@@ -2092,50 +2126,28 @@ function parseNativeEventData(data) {
2092
2126
  }
2093
2127
  }
2094
2128
 
2095
- // src/core/hooks/useSafeAreaInsetsEmitter.tsx
2129
+ // src/core/hooks/useSafeAreaInsetsEvent.tsx
2096
2130
  import { useSafeAreaInsets as useSafeAreaInsets3 } from "@granite-js/native/react-native-safe-area-context";
2097
- import { useEffect as useEffect11, useMemo as useMemo4 } from "react";
2098
- var EventEmitter = class {
2099
- listeners = {};
2100
- on(event, listener) {
2101
- if (!this.listeners[event]) {
2102
- this.listeners[event] = [];
2103
- }
2104
- this.listeners[event].push(listener);
2105
- }
2106
- emit(event, ...args) {
2107
- if (!this.listeners[event]) {
2108
- return;
2109
- }
2110
- this.listeners[event].forEach((listener) => listener(...args));
2111
- }
2112
- off(event, listener) {
2113
- if (!this.listeners[event]) {
2114
- return;
2115
- }
2116
- this.listeners[event] = this.listeners[event].filter((l) => l !== listener);
2117
- }
2118
- };
2119
- function useSafeAreaInsetsEmitter() {
2131
+ import { useEffect as useEffect11 } from "react";
2132
+ function useSafeAreaInsetsEvent() {
2120
2133
  const insets = useSafeAreaInsets3();
2121
- const emitter = useMemo4(() => new EventEmitter(), []);
2122
2134
  useEffect11(() => {
2123
- emitter.emit("safeAreaInsetsChange", insets);
2124
- return () => {
2125
- emitter.off("safeAreaInsetsChange", (listener) => listener(insets));
2126
- };
2127
- }, [emitter, insets]);
2128
- return emitter;
2135
+ safeAreaInsetsChange.emit(insets);
2136
+ }, [insets]);
2137
+ useEffect11(() => {
2138
+ return () => safeAreaInsetsChange.clearSubscriptions();
2139
+ }, []);
2140
+ return safeAreaInsetsChange;
2129
2141
  }
2130
2142
 
2131
2143
  // src/core/hooks/useWebBackHandler.tsx
2132
2144
  import { closeView as closeView6, useBackEventState } from "@granite-js/react-native";
2133
2145
  import { useDialog as useDialog7 } from "@toss/tds-react-native";
2134
2146
  import { josa as josa5 } from "es-hangul";
2135
- import { useCallback as useCallback12, useMemo as useMemo6 } from "react";
2147
+ import { useCallback as useCallback12, useMemo as useMemo5 } from "react";
2136
2148
 
2137
2149
  // src/hooks/useWebviewHistoryStack.tsx
2138
- import { useCallback as useCallback11, useMemo as useMemo5, useReducer } from "react";
2150
+ import { useCallback as useCallback11, useMemo as useMemo4, useReducer } from "react";
2139
2151
  var INITIAL_STATE = { stack: [], index: -1 };
2140
2152
  function reducer(state, action) {
2141
2153
  switch (action.type) {
@@ -2170,7 +2182,7 @@ function useWebViewHistory() {
2170
2182
  const onNavigationStateChange = useCallback11(({ url, canGoForward: canGoForward2 }) => {
2171
2183
  dispatch({ type: "NAVIGATION_CHANGE", url, canGoForward: canGoForward2 });
2172
2184
  }, []);
2173
- const { canGoBack, canGoForward } = useMemo5(() => {
2185
+ const { canGoBack, canGoForward } = useMemo4(() => {
2174
2186
  const canBack = state.index > 0;
2175
2187
  const canFwd = state.index >= 0 && state.index < state.stack.length - 1;
2176
2188
  return { canGoBack: canBack, canGoForward: canFwd };
@@ -2238,10 +2250,8 @@ function useWebBackHandler(webViewInitialURL, webViewRef) {
2238
2250
  ]);
2239
2251
  const handleWebHome = useCallback12(() => {
2240
2252
  logging.homeButtonClick();
2241
- if (hasWebBackEvent) {
2242
- for (const handler of webBackHandlersRef) {
2243
- handler();
2244
- }
2253
+ if (homeEvent.hasSubscriptions()) {
2254
+ homeEvent.emit({});
2245
2255
  return;
2246
2256
  }
2247
2257
  webViewRef.current?.injectJavaScript(`
@@ -2251,7 +2261,7 @@ function useWebBackHandler(webViewInitialURL, webViewRef) {
2251
2261
  })();
2252
2262
  `);
2253
2263
  }, [hasWebBackEvent, webBackHandlersRef, logging, webViewInitialURL, webViewRef]);
2254
- return useMemo6(
2264
+ return useMemo5(
2255
2265
  () => ({ addEventListener, removeEventListener, handleWebBack, handleWebHome, onNavigationStateChange }),
2256
2266
  [addEventListener, removeEventListener, handleWebBack, handleWebHome, onNavigationStateChange]
2257
2267
  );
@@ -2611,14 +2621,14 @@ function WebView({ type, local, onMessage, ...props }) {
2611
2621
  throw new Error(`Invalid WebView type: '${type}'`);
2612
2622
  }
2613
2623
  const webViewRef = useRef6(null);
2614
- const url = useMemo7(() => getWebViewURL(local), [local]);
2624
+ const url = useMemo6(() => getWebViewURL(local), [local]);
2615
2625
  const webBackHandler = useWebBackHandler(url, webViewRef);
2616
2626
  const top = useSafeAreaTop3();
2617
2627
  const bottom = useSafeAreaBottom();
2618
2628
  const insets = useSafeAreaInsets4();
2619
2629
  const global2 = getAppsInTossGlobals();
2620
2630
  const navigationBarContext = useNavigationBarContext();
2621
- const safeAreaInsetsEmitter = useSafeAreaInsetsEmitter();
2631
+ const safeAreaInsetsEvent = useSafeAreaInsetsEvent();
2622
2632
  const [allowsBackForwardNavigationGestures, setAllowsBackForwardNavigationGestures] = useState6(
2623
2633
  props.allowsBackForwardNavigationGestures
2624
2634
  );
@@ -2633,13 +2643,9 @@ function WebView({ type, local, onMessage, ...props }) {
2633
2643
  webBackHandler.removeEventListener(onEvent);
2634
2644
  };
2635
2645
  },
2646
+ homeEvent: ({ onEvent }) => homeEvent.subscribe(onEvent),
2636
2647
  updateLocationEvent: ({ onEvent, onError, options }) => appsInTossEvent.addEventListener("updateLocationEvent", { onEvent, onError, options }),
2637
- safeAreaInsetsChange: ({ onEvent }) => {
2638
- safeAreaInsetsEmitter.on("safeAreaInsetsChange", onEvent);
2639
- return () => {
2640
- safeAreaInsetsEmitter.off("safeAreaInsetsChange", onEvent);
2641
- };
2642
- },
2648
+ safeAreaInsetsChange: ({ onEvent }) => safeAreaInsetsEvent.subscribe(onEvent),
2643
2649
  /** @internal */
2644
2650
  appBridgeCallbackEvent: ({ onEvent, onError, options }) => appsInTossEvent.addEventListener("appBridgeCallbackEvent", { onEvent, onError, options }),
2645
2651
  /** AdMobV2 */
@@ -2722,7 +2728,7 @@ function WebView({ type, local, onMessage, ...props }) {
2722
2728
  }
2723
2729
  }
2724
2730
  });
2725
- const headerPropForExternalWebView = useMemo7(() => {
2731
+ const headerPropForExternalWebView = useMemo6(() => {
2726
2732
  const parsedNavigationBar = global2.navigationBar != null ? safeParseNavigationBar(global2.navigationBar) : null;
2727
2733
  const initialAccessoryButton = parsedNavigationBar?.initialAccessoryButton;
2728
2734
  const withBackButton = parsedNavigationBar?.withBackButton ?? true;
@@ -4090,7 +4096,9 @@ export {
4090
4096
  OverlayProvider,
4091
4097
  WebView,
4092
4098
  env,
4099
+ homeEvent,
4093
4100
  loadFullScreenAd,
4101
+ safeAreaInsetsChange,
4094
4102
  showFullScreenAd,
4095
4103
  useCreateUserAgent,
4096
4104
  useGeolocation,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@apps-in-toss/framework",
3
3
  "type": "module",
4
- "version": "1.12.0",
4
+ "version": "1.14.0",
5
5
  "description": "The framework for Apps In Toss",
6
6
  "scripts": {
7
7
  "typecheck": "tsc --noEmit",
@@ -55,12 +55,12 @@
55
55
  "ait": "./bin/ait.js"
56
56
  },
57
57
  "dependencies": {
58
- "@apps-in-toss/analytics": "1.12.0",
59
- "@apps-in-toss/cli": "1.12.0",
60
- "@apps-in-toss/native-modules": "1.12.0",
61
- "@apps-in-toss/plugins": "1.12.0",
62
- "@apps-in-toss/types": "1.12.0",
63
- "@apps-in-toss/user-scripts": "^1.12.0",
58
+ "@apps-in-toss/analytics": "1.14.0",
59
+ "@apps-in-toss/cli": "1.14.0",
60
+ "@apps-in-toss/native-modules": "1.14.0",
61
+ "@apps-in-toss/plugins": "1.14.0",
62
+ "@apps-in-toss/types": "1.14.0",
63
+ "@apps-in-toss/user-scripts": "^1.14.0",
64
64
  "es-hangul": "^2.3.2"
65
65
  },
66
66
  "devDependencies": {