catalyst-core 0.1.0-canary.9 → 0.2.0-beta.2
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 +1 -1
- package/bin/catalyst.js +8 -1
- package/changelog.md +6 -0
- package/dist/native/androidProject/app/build.gradle.kts +34 -8
- package/dist/native/androidProject/app/src/main/AndroidManifest.xml +2 -1
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/BridgeMessageValidator.kt +1 -1
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CustomWebview.kt +270 -78
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt +34 -5
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeBridge.kt +149 -1
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeCameraManager.kt +337 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/OfflineCacheService.kt +374 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/TransitionManager.kt +202 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/WebCacheManager.kt +156 -39
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/BarcodeDetector.kt +123 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/CameraSessionManager.kt +244 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/HoldController.kt +70 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/TorchController.kt +57 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/VideoStreamState.kt +41 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/VideoStreamStateMachine.kt +40 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/ViewfinderMapper.kt +75 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/ZoomController.kt +156 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/plugins/CatalystPlugin.kt +7 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/plugins/GeneratedPluginIndex.kt +6 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/plugins/PluginBridge.kt +253 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/security/FridaDetector.kt +16 -5
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/BridgeUtils.kt +42 -5
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/FrameworkServerUtils.kt +7 -3
- package/dist/native/androidProject/app/src/main/res/drawable/debug_viewfinder_border.xml +8 -0
- package/dist/native/androidProject/app/src/main/res/layout/activity_main.xml +37 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/CustomWebviewTest.kt +7 -2
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/plugins/PluginBridgeTest.kt +139 -0
- package/dist/native/androidSetup.js +1 -0
- package/dist/native/bridge/WebBridge.js +5 -5
- package/dist/native/bridge/constants/NativeInterfaces.js +9 -5
- package/dist/native/bridge/hooks/index.js +1 -0
- package/dist/native/bridge/hooks/useCamera.js +9 -0
- package/dist/native/bridge/hooks/useDataProtection.js +2 -0
- package/dist/native/bridge/hooks/useDeviceInfo.js +2 -0
- package/dist/native/bridge/hooks/useFilePicker.js +2 -0
- package/dist/native/bridge/hooks/useGoogleSignIn.js +1 -0
- package/dist/native/bridge/hooks/useHapticFeedback.js +1 -0
- package/dist/native/bridge/hooks/useIntent.js +1 -0
- package/dist/native/bridge/hooks/useNativeTransition.js +43 -0
- package/dist/native/bridge/hooks/useNetworkStatus.js +3 -0
- package/dist/native/bridge/hooks/useNotification.js +2 -0
- package/dist/native/bridge/hooks/useSafeArea.js +2 -0
- package/dist/native/bridge/hooks/useVideoStream.js +6 -0
- package/dist/native/bridge/hooks/utils.js +1 -0
- package/dist/native/bridge/hooks.js +1 -137
- package/dist/native/bridge/useBaseHook.js +12 -7
- package/dist/native/bridge/utils/NativeBridge.js +41 -6
- package/dist/native/buildAndroid/assets.js +52 -0
- package/dist/native/buildAndroid/build.js +19 -0
- package/dist/native/buildAndroid/config.js +1 -0
- package/dist/native/buildAndroid/index.js +1 -0
- package/dist/native/buildAppAndroid.js +2 -150
- package/dist/native/buildAppIos.js +2 -312
- package/dist/native/buildIos/assets.js +8 -0
- package/dist/native/buildIos/build.js +40 -0
- package/dist/native/buildIos/config.js +71 -0
- package/dist/native/buildIos/index.js +6 -0
- package/dist/native/buildIos/plugins.js +45 -0
- package/dist/native/internalPluginUtils.js +7 -0
- package/dist/native/iosnativeWebView/Package.resolved +36 -0
- package/dist/native/iosnativeWebView/Sources/Core/Camera/BarcodeDetector.swift +116 -0
- package/dist/native/iosnativeWebView/Sources/Core/Camera/CameraPreviewView.swift +58 -0
- package/dist/native/iosnativeWebView/Sources/Core/Camera/CameraSessionManager.swift +308 -0
- package/dist/native/iosnativeWebView/Sources/Core/Camera/HoldController.swift +63 -0
- package/dist/native/iosnativeWebView/Sources/Core/Camera/NativeCameraManager.swift +194 -0
- package/dist/native/iosnativeWebView/Sources/Core/Camera/TorchController.swift +60 -0
- package/dist/native/iosnativeWebView/Sources/Core/Camera/VideoStreamState.swift +44 -0
- package/dist/native/iosnativeWebView/Sources/Core/Camera/VideoStreamStateMachine.swift +60 -0
- package/dist/native/iosnativeWebView/Sources/Core/Camera/ZoomController.swift +130 -0
- package/dist/native/iosnativeWebView/Sources/Core/Constants/CatalystConstants.swift +11 -1
- package/dist/native/iosnativeWebView/Sources/Core/Constants/ConfigConstants.swift +2 -1
- package/dist/native/iosnativeWebView/Sources/Core/Plugins/CatalystPlugin.swift +5 -0
- package/dist/native/iosnativeWebView/Sources/Core/Plugins/GeneratedPluginIndex.swift +6 -0
- package/dist/native/iosnativeWebView/Sources/Core/Plugins/PluginBridge.swift +364 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/CacheManager.swift +39 -4
- package/dist/native/iosnativeWebView/Sources/Core/Utils/OfflineCacheService.swift +683 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/OfflineURLSchemeHandler.swift +94 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/ResourceURLProtocol.swift +19 -6
- package/dist/native/iosnativeWebView/Sources/Core/Utils/SafeAreaUtils.swift +1 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeCommandHandler.swift +1 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeMessageValidator.swift +4 -1
- package/dist/native/iosnativeWebView/Sources/Core/WebView/NativeBridge.swift +227 -4
- package/dist/native/iosnativeWebView/Sources/Core/WebView/TransitionManager.swift +169 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WeakScriptMessageHandler.swift +14 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebView.swift +53 -8
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewNavigationDelegate.swift +88 -4
- package/dist/native/iosnativeWebView/iosnativeWebView/ContentView.swift +30 -11
- package/dist/native/iosnativeWebView/iosnativeWebView/Info.plist +11 -17
- package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.pbxproj +8 -2
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/BootTimingUtilityTests.swift +1 -1
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/ConfigMappingTests.swift +4 -2
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/PluginBridgeTests.swift +160 -0
- package/dist/native/plugin-bridge/PluginBridge.js +1 -0
- package/dist/native/pluginComposerAndroid.js +28 -0
- package/dist/native/pluginComposerIos.js +16 -0
- package/dist/native/renameAndroidProject.js +5 -5
- package/dist/native/setupEmulatorIos.js +6 -4
- package/dist/native/setupServer.js +2 -3
- package/dist/native/utils.js +4 -1
- package/dist/offline/catalyst-sw.js +1 -0
- package/dist/offline/registerServiceWorker.js +1 -0
- package/dist/otel.js +70 -2
- package/dist/router/ClientRouter.js +1 -1
- package/dist/scripts/build.js +1 -1
- package/dist/scripts/cleanCache.js +1 -0
- package/dist/scripts/devBuild.js +1 -1
- package/dist/scripts/devServe.js +1 -1
- package/dist/scripts/generateOfflineManifest.js +1 -0
- package/dist/scripts/loadEnvironmentVariables.js +1 -2
- package/dist/scripts/plugins.js +1 -0
- package/dist/scripts/registerAliases.js +6 -2
- package/dist/scripts/scriptUtils.js +2 -1
- package/dist/scripts/serve.js +2 -1
- package/dist/scripts/start.js +7 -2
- package/dist/server/expressServer.js +4 -8
- package/dist/server/renderer/document/Body.js +4 -4
- package/dist/server/renderer/document/Head.js +2 -2
- package/dist/server/renderer/extract.js +31 -27
- package/dist/server/renderer/handler.js +47 -24
- package/dist/server/renderer/index.js +1 -1
- package/dist/server/startServer.js +57 -7
- package/dist/server/utils/userAgentUtil.js +9 -3
- package/dist/server/utils/validator.js +1 -1
- package/dist/webpack/babel-plugins/remove-client.plugin.js +2 -2
- package/dist/webpack/babel-plugins/remove-ssr.plugin.js +2 -2
- package/dist/webpack/babel.config.client.js +1 -1
- package/dist/webpack/babel.config.ssr.js +1 -1
- package/dist/webpack/base.babel.js +7 -8
- package/dist/webpack/development.client.babel.js +7 -16
- package/dist/webpack/production.client.babel.js +1 -1
- package/dist/webpack/production.ssr.babel.js +5 -1
- package/mcp_v2/knowledge-base.json +206 -100
- package/mcp_v2/lib/helpers.js +42 -8
- package/mcp_v2/lib/seed.js +88 -0
- package/mcp_v2/mcp.js +150 -4
- package/mcp_v2/setup.js +30 -193
- package/mcp_v2/tools/build.js +1 -1
- package/mcp_v2/tools/config.js +16 -4
- package/mcp_v2/tools/conversion.js +11 -11
- package/mcp_v2/tools/github.js +1117 -0
- package/mcp_v2/tools/knowledge.js +16 -3
- package/mcp_v2/tools/sync.js +179 -10
- package/mcp_v2/tools/tasks.js +13 -4
- package/package.json +20 -8
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.default=void 0;var _NativeInterfaces=require("./constants/NativeInterfaces.js");var _NativeBridge=_interopRequireDefault(require("./utils/NativeBridge.js"));var _CameraUtils=_interopRequireDefault(require("./utils/CameraUtils.js"));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}class WebBridge{constructor(){this.handlers=new Map();this.initialized=false;this._logInitialization();}/**
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.default=void 0;var _NativeInterfaces=require("./constants/NativeInterfaces.js");var _NativeBridge=_interopRequireDefault(require("./utils/NativeBridge.js"));var _CameraUtils=_interopRequireDefault(require("./utils/CameraUtils.js"));var _PluginBridge=_interopRequireDefault(require("../plugin-bridge/PluginBridge.js"));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}const DEVICE_INFO_PLUGIN={pluginId:"io.catalyst.device_info",command:"getDeviceInfo",successEvent:"onSuccess",errorEvent:"onError"};const DEVICE_INFO_REQUEST_TIMEOUT_MS=5000;const DEVICE_INFO_PLUGIN_FALLBACK_CODES=new Set(["PLUGIN_NOT_FOUND","COMMAND_NOT_SUPPORTED","PLUGIN_NOT_REGISTERED"]);const DEVICE_INFO_PLUGIN_FALLBACK_MESSAGES=["pluginbridge is not available in this environment","pluginbridge is not available","device info plugin request timed out"];const normalizeDeviceInfoPayload=payload=>{if(typeof payload!=="string"){return payload;}try{return JSON.parse(payload);}catch{return{message:payload};}};const createDeviceInfoError=(payload,fallbackMessage)=>{const parsed=normalizeDeviceInfoPayload(payload);const message=parsed?.message||parsed?.error||fallbackMessage;const error=new Error(message);if(parsed?.code){error.code=parsed.code;}if(parsed?.pluginId){error.pluginId=parsed.pluginId;}if(parsed?.command){error.command=parsed.command;}return error;};class WebBridge{constructor(){this.handlers=new Map();this.initialized=false;this._logInitialization();}/**
|
|
2
2
|
* Log initialization info
|
|
3
3
|
*/_logInitialization(){console.log("🌉 WebBridge initialized");console.log("🌉 Available callback interfaces:",_NativeInterfaces.ALL_CALLBACKS.length);if(process.env.NODE_ENV==="development"){(0,_NativeInterfaces.debugInterfaces)();}}/**
|
|
4
4
|
* Static method to initialize WebBridge on window object
|
|
@@ -51,9 +51,9 @@ if(this.handlers.has(interfaceName)){console.warn(`🌉 Interface ${interfaceNam
|
|
|
51
51
|
* Request camera permission
|
|
52
52
|
* @param {Object} config - Permission configuration (includeDetails)
|
|
53
53
|
* @returns {Promise<Object>} - Promise that resolves with permission status or rejects with error
|
|
54
|
-
*/requestCameraPermission=(config={})=>{return _CameraUtils.default.requestCameraPermission(config,this.register,this.unregister);};/**
|
|
54
|
+
*/requestCameraPermission=(config={})=>{return _CameraUtils.default.requestCameraPermission(config,this.register,this.unregister);};getDeviceInfoFromLegacyBridge=parseDeviceInfo=>{return new Promise((resolve,reject)=>{let timeoutId=null;const cleanup=()=>{if(timeoutId!=null){clearTimeout(timeoutId);}this.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_DEVICE_INFO_SUCCESS);this.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_DEVICE_INFO_ERROR);};this.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_DEVICE_INFO_SUCCESS,data=>{cleanup();resolve(parseDeviceInfo(data));});this.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_DEVICE_INFO_ERROR,error=>{cleanup();reject(new Error(error));});try{const dispatched=_NativeBridge.default.device.getDeviceInfo();if(dispatched===false){cleanup();reject(new Error("Legacy device info bridge failed to dispatch"));return;}timeoutId=setTimeout(()=>{cleanup();reject(new Error("Legacy device info request timed out"));},DEVICE_INFO_REQUEST_TIMEOUT_MS);}catch(error){cleanup();reject(error);}});};getDeviceInfoFromPluginBridge=parseDeviceInfo=>{return new Promise((resolve,reject)=>{_PluginBridge.default.init();let isSettled=false;let timeoutId=null;const cleanups=[];const cleanup=()=>{if(timeoutId!=null){clearTimeout(timeoutId);}while(cleanups.length>0){const unregister=cleanups.pop();unregister();}};const settle=callback=>{if(isSettled){return;}isSettled=true;cleanup();callback();};cleanups.push(_PluginBridge.default.register({pluginId:DEVICE_INFO_PLUGIN.pluginId,eventName:DEVICE_INFO_PLUGIN.successEvent,command:DEVICE_INFO_PLUGIN.command,handler:payload=>{settle(()=>{try{resolve(parseDeviceInfo(payload));}catch(error){reject(error);}});}}));cleanups.push(_PluginBridge.default.register({pluginId:DEVICE_INFO_PLUGIN.pluginId,eventName:DEVICE_INFO_PLUGIN.errorEvent,command:DEVICE_INFO_PLUGIN.command,handler:payload=>{settle(()=>{reject(createDeviceInfoError(payload,"Device info plugin failed"));});}}));cleanups.push(_PluginBridge.default.register({pluginId:"__bridge__",eventName:"PLUGIN_BRIDGE_ERROR",command:DEVICE_INFO_PLUGIN.command,handler:payload=>{const pluginId=normalizeDeviceInfoPayload(payload)?.pluginId;if(pluginId!==DEVICE_INFO_PLUGIN.pluginId){return;}settle(()=>{reject(createDeviceInfoError(payload,"Device info plugin unavailable"));});}}));try{_PluginBridge.default.emit({pluginId:DEVICE_INFO_PLUGIN.pluginId,command:DEVICE_INFO_PLUGIN.command});timeoutId=setTimeout(()=>{settle(()=>{reject(new Error("Device info plugin request timed out"));});},DEVICE_INFO_REQUEST_TIMEOUT_MS);}catch(error){cleanup();reject(error);}});};shouldFallbackToLegacyDeviceInfo=error=>{if(!error){return false;}if(DEVICE_INFO_PLUGIN_FALLBACK_CODES.has(error.code)){return true;}if(typeof error.message!=="string"){return false;}const normalizedMessage=error.message.toLowerCase();return DEVICE_INFO_PLUGIN_FALLBACK_MESSAGES.some(message=>normalizedMessage.includes(message));};/**
|
|
55
55
|
* Get device information
|
|
56
56
|
* @returns {Promise<Object>} - Promise that resolves with device info or rejects with error
|
|
57
|
-
*/getDeviceInfo=()=>{const{platform,isNativeEnvironment}=_NativeBridge.default.getEnvironmentInfo();// Handle web environment
|
|
58
|
-
if(!isNativeEnvironment||platform==="web"){return
|
|
59
|
-
const DEVICE_INFO_KEY_MAP={android:{model:"model",manufacturer:"manufacturer",platform:"platform",screenWidth:"screenWidth",screenHeight:"screenHeight",screenDensity:"screenDensity",appInfo:"appInfo"},ios:{model:"model",manufacturer:"manufacturer",platform:"platform",screenWidth:"screenWidth",screenHeight:"screenHeight",screenDensity:"screenDensity",appInfo:"appInfo"}};const parseDeviceInfo=data=>{const rawDeviceInfo=typeof data==="string"?JSON.parse(data):data;const platformMapping=DEVICE_INFO_KEY_MAP[platform]||{};return Object.fromEntries(Object.entries(rawDeviceInfo).map(([key,value])=>[platformMapping[key]||key,value]));};return
|
|
57
|
+
*/getDeviceInfo=async()=>{const{platform,isNativeEnvironment}=_NativeBridge.default.getEnvironmentInfo();// Handle web environment
|
|
58
|
+
if(!isNativeEnvironment||platform==="web"){return{model:navigator.userAgent,manufacturer:"browser",platform:"web",screenWidth:screen.width,screenHeight:screen.height,screenDensity:window.devicePixelRatio};}// Key mapping from native to web keys
|
|
59
|
+
const DEVICE_INFO_KEY_MAP={android:{model:"model",manufacturer:"manufacturer",platform:"platform",screenWidth:"screenWidth",screenHeight:"screenHeight",screenDensity:"screenDensity",appInfo:"appInfo"},ios:{model:"model",manufacturer:"manufacturer",platform:"platform",screenWidth:"screenWidth",screenHeight:"screenHeight",screenDensity:"screenDensity",appInfo:"appInfo"}};const parseDeviceInfo=data=>{const rawDeviceInfo=typeof data==="string"?JSON.parse(data):data;const platformMapping=DEVICE_INFO_KEY_MAP[platform]||{};return Object.fromEntries(Object.entries(rawDeviceInfo).map(([key,value])=>[platformMapping[key]||key,value]));};try{return await this.getDeviceInfoFromPluginBridge(parseDeviceInfo);}catch(error){if(!this.shouldFallbackToLegacyDeviceInfo(error)){throw error;}return this.getDeviceInfoFromLegacyBridge(parseDeviceInfo);}};}var _default=exports.default=WebBridge;
|
|
@@ -2,17 +2,20 @@
|
|
|
2
2
|
// Centralized definitions for all native interface communications
|
|
3
3
|
// Command Interfaces: Web → Native (outgoing commands)
|
|
4
4
|
const NATIVE_COMMANDS=exports.NATIVE_COMMANDS={// Camera commands
|
|
5
|
-
OPEN_CAMERA:"openCamera",REQUEST_CAMERA_PERMISSION:"requestCameraPermission",//
|
|
5
|
+
OPEN_CAMERA:"openCamera",REQUEST_CAMERA_PERMISSION:"requestCameraPermission",// Video stream commands
|
|
6
|
+
START_VIDEO_STREAM:"startVideoStream",STOP_VIDEO_STREAM:"stopVideoStream",SET_VIDEO_STREAM_ZOOM:"setVideoStreamZoom",SET_VIDEO_STREAM_TORCH:"setVideoStreamTorch",SET_VIDEO_STREAM_FPS:"setVideoStreamFps",FLIP_VIDEO_STREAM:"flipVideoStream",// File commands
|
|
6
7
|
PICK_FILE:"pickFile",OPEN_FILE_WITH_INTENT:"openFileWithIntent",// Haptic feedback commands
|
|
7
8
|
REQUEST_HAPTIC_FEEDBACK:"requestHapticFeedback",// Notification commands
|
|
8
9
|
SCHEDULE_LOCAL_NOTIFICATION:"scheduleLocalNotification",CANCEL_LOCAL_NOTIFICATION:"cancelLocalNotification",REQUEST_NOTIFICATION_PERMISSION:"requestNotificationPermission",REGISTER_FOR_PUSH_NOTIFICATIONS:"registerForPushNotifications",UPDATE_BADGE_COUNT:"updateBadgeCount",SUBSCRIBE_TO_TOPIC:"subscribeToTopic",UNSUBSCRIBE_FROM_TOPIC:"unsubscribeFromTopic",GET_SUBSCRIBED_TOPICS:"getSubscribedTopics",// Device info commands
|
|
9
10
|
GET_DEVICE_INFO:"getDeviceInfo",// Network commands
|
|
10
11
|
GET_NETWORK_STATUS:"getNetworkStatus",// Auth commands
|
|
11
|
-
GOOGLE_SIGN_IN:"googleSignIn",//
|
|
12
|
+
GOOGLE_SIGN_IN:"googleSignIn",// Transition commands
|
|
13
|
+
START_TRANSITION:"startTransition",COMMIT_TRANSITION:"commitTransition",CANCEL_TRANSITION:"cancelTransition",// Safe area commands
|
|
12
14
|
GET_SAFE_AREA:"getSafeArea",// Security commands
|
|
13
15
|
SET_SCREEN_SECURE:"setScreenSecure",GET_SCREEN_SECURE:"getScreenSecure",CLEAR_WEB_DATA:"clearWebData"};// Callback Interfaces: Native → Web (incoming events/callbacks)
|
|
14
16
|
const NATIVE_CALLBACKS=exports.NATIVE_CALLBACKS={// Camera callbacks
|
|
15
|
-
CAMERA_PERMISSION_STATUS:"CAMERA_PERMISSION_STATUS",ON_CAMERA_CAPTURE:"ON_CAMERA_CAPTURE",ON_CAMERA_ERROR:"ON_CAMERA_ERROR",//
|
|
17
|
+
CAMERA_PERMISSION_STATUS:"CAMERA_PERMISSION_STATUS",ON_CAMERA_CAPTURE:"ON_CAMERA_CAPTURE",ON_CAMERA_ERROR:"ON_CAMERA_ERROR",// Video stream callbacks
|
|
18
|
+
ON_VIDEO_STREAM_READY:"ON_VIDEO_STREAM_READY",ON_VIDEO_STREAM_STOPPED:"ON_VIDEO_STREAM_STOPPED",ON_QR_DETECTED:"ON_QR_DETECTED",ON_TORCH_CHANGED:"ON_TORCH_CHANGED",ON_ZOOM_CHANGED:"ON_ZOOM_CHANGED",// File picker callbacks
|
|
16
19
|
ON_FILE_PICKED:"ON_FILE_PICKED",ON_FILE_PICK_ERROR:"ON_FILE_PICK_ERROR",ON_FILE_PICK_CANCELLED:"ON_FILE_PICK_CANCELLED",ON_FILE_PICK_STATE_UPDATE:"ON_FILE_PICK_STATE_UPDATE",// Intent callbacks
|
|
17
20
|
ON_INTENT_SUCCESS:"ON_INTENT_SUCCESS",ON_INTENT_ERROR:"ON_INTENT_ERROR",ON_INTENT_CANCELLED:"ON_INTENT_CANCELLED",// Haptic feedback callbacks
|
|
18
21
|
HAPTIC_FEEDBACK:"HAPTIC_FEEDBACK",// Notification callbacks
|
|
@@ -20,9 +23,10 @@ NOTIFICATION_PERMISSION_STATUS:"NOTIFICATION_PERMISSION_STATUS",LOCAL_NOTIFICATI
|
|
|
20
23
|
ON_DEVICE_INFO_SUCCESS:"ON_DEVICE_INFO_SUCCESS",ON_DEVICE_INFO_ERROR:"ON_DEVICE_INFO_ERROR",// Auth callbacks
|
|
21
24
|
ON_GOOGLE_SIGN_IN_SUCCESS:"ON_GOOGLE_SIGN_IN_SUCCESS",ON_GOOGLE_SIGN_IN_ERROR:"ON_GOOGLE_SIGN_IN_ERROR",ON_GOOGLE_SIGN_IN_CANCELLED:"ON_GOOGLE_SIGN_IN_CANCELLED",// Network callbacks
|
|
22
25
|
NETWORK_STATUS_CHANGED:"NETWORK_STATUS_CHANGED",// Safe area callbacks
|
|
23
|
-
ON_SAFE_AREA_INSETS_UPDATED:"ON_SAFE_AREA_INSETS_UPDATED",//
|
|
26
|
+
ON_SAFE_AREA_INSETS_UPDATED:"ON_SAFE_AREA_INSETS_UPDATED",// Transition callbacks
|
|
27
|
+
ON_TRANSITION_COMMITTED:"ON_TRANSITION_COMMITTED",ON_TRANSITION_CANCELLED:"ON_TRANSITION_CANCELLED",ON_TRANSITION_TIMEOUT:"ON_TRANSITION_TIMEOUT",// Security callbacks
|
|
24
28
|
ON_SCREEN_SECURE_SET:"ON_SCREEN_SECURE_SET",ON_SCREEN_SECURE_STATUS:"ON_SCREEN_SECURE_STATUS",ON_SCREEN_SECURE_ERROR:"ON_SCREEN_SECURE_ERROR",ON_WEB_DATA_CLEARED:"ON_WEB_DATA_CLEARED",ON_WEB_DATA_CLEAR_ERROR:"ON_WEB_DATA_CLEAR_ERROR"};// Interface Categories for easier management
|
|
25
|
-
const INTERFACE_CATEGORIES=exports.INTERFACE_CATEGORIES={CAMERA:{commands:[NATIVE_COMMANDS.OPEN_CAMERA,NATIVE_COMMANDS.REQUEST_CAMERA_PERMISSION],callbacks:[NATIVE_CALLBACKS.CAMERA_PERMISSION_STATUS,NATIVE_CALLBACKS.ON_CAMERA_CAPTURE,NATIVE_CALLBACKS.ON_CAMERA_ERROR]},FILE:{commands:[NATIVE_COMMANDS.PICK_FILE,NATIVE_COMMANDS.OPEN_FILE_WITH_INTENT],callbacks:[NATIVE_CALLBACKS.ON_FILE_PICKED,NATIVE_CALLBACKS.ON_FILE_PICK_ERROR,NATIVE_CALLBACKS.ON_FILE_PICK_CANCELLED,NATIVE_CALLBACKS.ON_FILE_PICK_STATE_UPDATE]},INTENT:{commands:[NATIVE_COMMANDS.OPEN_FILE_WITH_INTENT],callbacks:[NATIVE_CALLBACKS.ON_INTENT_SUCCESS,NATIVE_CALLBACKS.ON_INTENT_ERROR,NATIVE_CALLBACKS.ON_INTENT_CANCELLED]},HAPTIC:{commands:[NATIVE_COMMANDS.REQUEST_HAPTIC_FEEDBACK],callbacks:[NATIVE_CALLBACKS.HAPTIC_FEEDBACK]},NOTIFICATION:{commands:[NATIVE_COMMANDS.SCHEDULE_LOCAL_NOTIFICATION,NATIVE_COMMANDS.CANCEL_LOCAL_NOTIFICATION,NATIVE_COMMANDS.REQUEST_NOTIFICATION_PERMISSION,NATIVE_COMMANDS.REGISTER_FOR_PUSH_NOTIFICATIONS,NATIVE_COMMANDS.UPDATE_BADGE_COUNT,NATIVE_COMMANDS.SUBSCRIBE_TO_TOPIC,NATIVE_COMMANDS.UNSUBSCRIBE_FROM_TOPIC,NATIVE_COMMANDS.GET_SUBSCRIBED_TOPICS],callbacks:[NATIVE_CALLBACKS.NOTIFICATION_PERMISSION_STATUS,NATIVE_CALLBACKS.LOCAL_NOTIFICATION_SCHEDULED,NATIVE_CALLBACKS.LOCAL_NOTIFICATION_CANCELLED,NATIVE_CALLBACKS.PUSH_NOTIFICATION_TOKEN,NATIVE_CALLBACKS.NOTIFICATION_RECEIVED,NATIVE_CALLBACKS.NOTIFICATION_ACTION_PERFORMED,NATIVE_CALLBACKS.NOTIFICATION_TAPPED,NATIVE_CALLBACKS.TOPIC_SUBSCRIPTION_RESULT,NATIVE_CALLBACKS.SUBSCRIBED_TOPICS_RESULT]},DEVICE_INFO:{commands:[NATIVE_COMMANDS.GET_DEVICE_INFO],callbacks:[NATIVE_CALLBACKS.ON_DEVICE_INFO_SUCCESS,NATIVE_CALLBACKS.ON_DEVICE_INFO_ERROR]},AUTH:{commands:[NATIVE_COMMANDS.GOOGLE_SIGN_IN],callbacks:[NATIVE_CALLBACKS.ON_GOOGLE_SIGN_IN_SUCCESS,NATIVE_CALLBACKS.ON_GOOGLE_SIGN_IN_ERROR,NATIVE_CALLBACKS.ON_GOOGLE_SIGN_IN_CANCELLED]},NETWORK:{commands:[NATIVE_COMMANDS.GET_NETWORK_STATUS],callbacks:[NATIVE_CALLBACKS.NETWORK_STATUS_CHANGED]},SECURITY:{commands:[NATIVE_COMMANDS.SET_SCREEN_SECURE,NATIVE_COMMANDS.GET_SCREEN_SECURE,NATIVE_COMMANDS.CLEAR_WEB_DATA],callbacks:[NATIVE_CALLBACKS.ON_SCREEN_SECURE_SET,NATIVE_CALLBACKS.ON_SCREEN_SECURE_ERROR,NATIVE_CALLBACKS.ON_WEB_DATA_CLEARED,NATIVE_CALLBACKS.ON_WEB_DATA_CLEAR_ERROR]}};// All available interfaces (for validation)
|
|
29
|
+
const INTERFACE_CATEGORIES=exports.INTERFACE_CATEGORIES={CAMERA:{commands:[NATIVE_COMMANDS.OPEN_CAMERA,NATIVE_COMMANDS.REQUEST_CAMERA_PERMISSION],callbacks:[NATIVE_CALLBACKS.CAMERA_PERMISSION_STATUS,NATIVE_CALLBACKS.ON_CAMERA_CAPTURE,NATIVE_CALLBACKS.ON_CAMERA_ERROR]},FILE:{commands:[NATIVE_COMMANDS.PICK_FILE,NATIVE_COMMANDS.OPEN_FILE_WITH_INTENT],callbacks:[NATIVE_CALLBACKS.ON_FILE_PICKED,NATIVE_CALLBACKS.ON_FILE_PICK_ERROR,NATIVE_CALLBACKS.ON_FILE_PICK_CANCELLED,NATIVE_CALLBACKS.ON_FILE_PICK_STATE_UPDATE]},INTENT:{commands:[NATIVE_COMMANDS.OPEN_FILE_WITH_INTENT],callbacks:[NATIVE_CALLBACKS.ON_INTENT_SUCCESS,NATIVE_CALLBACKS.ON_INTENT_ERROR,NATIVE_CALLBACKS.ON_INTENT_CANCELLED]},HAPTIC:{commands:[NATIVE_COMMANDS.REQUEST_HAPTIC_FEEDBACK],callbacks:[NATIVE_CALLBACKS.HAPTIC_FEEDBACK]},NOTIFICATION:{commands:[NATIVE_COMMANDS.SCHEDULE_LOCAL_NOTIFICATION,NATIVE_COMMANDS.CANCEL_LOCAL_NOTIFICATION,NATIVE_COMMANDS.REQUEST_NOTIFICATION_PERMISSION,NATIVE_COMMANDS.REGISTER_FOR_PUSH_NOTIFICATIONS,NATIVE_COMMANDS.UPDATE_BADGE_COUNT,NATIVE_COMMANDS.SUBSCRIBE_TO_TOPIC,NATIVE_COMMANDS.UNSUBSCRIBE_FROM_TOPIC,NATIVE_COMMANDS.GET_SUBSCRIBED_TOPICS],callbacks:[NATIVE_CALLBACKS.NOTIFICATION_PERMISSION_STATUS,NATIVE_CALLBACKS.LOCAL_NOTIFICATION_SCHEDULED,NATIVE_CALLBACKS.LOCAL_NOTIFICATION_CANCELLED,NATIVE_CALLBACKS.PUSH_NOTIFICATION_TOKEN,NATIVE_CALLBACKS.NOTIFICATION_RECEIVED,NATIVE_CALLBACKS.NOTIFICATION_ACTION_PERFORMED,NATIVE_CALLBACKS.NOTIFICATION_TAPPED,NATIVE_CALLBACKS.TOPIC_SUBSCRIPTION_RESULT,NATIVE_CALLBACKS.SUBSCRIBED_TOPICS_RESULT]},DEVICE_INFO:{commands:[NATIVE_COMMANDS.GET_DEVICE_INFO],callbacks:[NATIVE_CALLBACKS.ON_DEVICE_INFO_SUCCESS,NATIVE_CALLBACKS.ON_DEVICE_INFO_ERROR]},AUTH:{commands:[NATIVE_COMMANDS.GOOGLE_SIGN_IN],callbacks:[NATIVE_CALLBACKS.ON_GOOGLE_SIGN_IN_SUCCESS,NATIVE_CALLBACKS.ON_GOOGLE_SIGN_IN_ERROR,NATIVE_CALLBACKS.ON_GOOGLE_SIGN_IN_CANCELLED]},NETWORK:{commands:[NATIVE_COMMANDS.GET_NETWORK_STATUS],callbacks:[NATIVE_CALLBACKS.NETWORK_STATUS_CHANGED]},SECURITY:{commands:[NATIVE_COMMANDS.SET_SCREEN_SECURE,NATIVE_COMMANDS.GET_SCREEN_SECURE,NATIVE_COMMANDS.CLEAR_WEB_DATA],callbacks:[NATIVE_CALLBACKS.ON_SCREEN_SECURE_SET,NATIVE_CALLBACKS.ON_SCREEN_SECURE_ERROR,NATIVE_CALLBACKS.ON_WEB_DATA_CLEARED,NATIVE_CALLBACKS.ON_WEB_DATA_CLEAR_ERROR]},TRANSITION:{commands:[NATIVE_COMMANDS.START_TRANSITION,NATIVE_COMMANDS.COMMIT_TRANSITION,NATIVE_COMMANDS.CANCEL_TRANSITION],callbacks:[NATIVE_CALLBACKS.ON_TRANSITION_COMMITTED,NATIVE_CALLBACKS.ON_TRANSITION_CANCELLED,NATIVE_CALLBACKS.ON_TRANSITION_TIMEOUT]}};// All available interfaces (for validation)
|
|
26
30
|
const ALL_COMMANDS=exports.ALL_COMMANDS=Object.values(NATIVE_COMMANDS);const ALL_CALLBACKS=exports.ALL_CALLBACKS=Object.values(NATIVE_CALLBACKS);const ALL_INTERFACES=exports.ALL_INTERFACES=[...ALL_COMMANDS,...ALL_CALLBACKS];// Permission status constants
|
|
27
31
|
const PERMISSION_STATUS=exports.PERMISSION_STATUS={GRANTED:"GRANTED",DENIED:"DENIED",NOT_DETERMINED:"NOT_DETERMINED",RESTRICTED:"RESTRICTED"};// Haptic feedback types (matching Android implementation)
|
|
28
32
|
const HAPTIC_FEEDBACK_TYPES=exports.HAPTIC_FEEDBACK_TYPES={VIRTUAL_KEY:"VIRTUAL_KEY",LONG_PRESS:"LONG_PRESS",DEFAULT:"DEFAULT",// Legacy types for backward compatibility
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"requestCameraPermission",{enumerable:true,get:function(){return _useCamera.requestCameraPermission;}});Object.defineProperty(exports,"requestHapticFeedback",{enumerable:true,get:function(){return _useHapticFeedback.requestHapticFeedback;}});Object.defineProperty(exports,"requestNotificationPermission",{enumerable:true,get:function(){return _useNotification.requestNotificationPermission;}});Object.defineProperty(exports,"sanitizeFilePickerOptions",{enumerable:true,get:function(){return _useFilePicker.sanitizeFilePickerOptions;}});Object.defineProperty(exports,"useCamera",{enumerable:true,get:function(){return _useCamera.useCamera;}});Object.defineProperty(exports,"useCameraPermission",{enumerable:true,get:function(){return _useCamera.useCameraPermission;}});Object.defineProperty(exports,"useDataProtection",{enumerable:true,get:function(){return _useDataProtection.useDataProtection;}});Object.defineProperty(exports,"useDeviceInfo",{enumerable:true,get:function(){return _useDeviceInfo.useDeviceInfo;}});Object.defineProperty(exports,"useFilePicker",{enumerable:true,get:function(){return _useFilePicker.useFilePicker;}});Object.defineProperty(exports,"useGoogleSignIn",{enumerable:true,get:function(){return _useGoogleSignIn.useGoogleSignIn;}});Object.defineProperty(exports,"useHapticFeedback",{enumerable:true,get:function(){return _useHapticFeedback.useHapticFeedback;}});Object.defineProperty(exports,"useIntent",{enumerable:true,get:function(){return _useIntent.useIntent;}});Object.defineProperty(exports,"useNativeTransition",{enumerable:true,get:function(){return _useNativeTransition.useNativeTransition;}});Object.defineProperty(exports,"useNetworkStatus",{enumerable:true,get:function(){return _useNetworkStatus.useNetworkStatus;}});Object.defineProperty(exports,"useNotification",{enumerable:true,get:function(){return _useNotification.useNotification;}});Object.defineProperty(exports,"useNotificationPermission",{enumerable:true,get:function(){return _useNotification.useNotificationPermission;}});Object.defineProperty(exports,"useSafeArea",{enumerable:true,get:function(){return _useSafeArea.useSafeArea;}});Object.defineProperty(exports,"useVideoStream",{enumerable:true,get:function(){return _useVideoStream.useVideoStream;}});var _useGoogleSignIn=require("./useGoogleSignIn.js");var _useCamera=require("./useCamera.js");var _useIntent=require("./useIntent.js");var _useFilePicker=require("./useFilePicker.js");var _useHapticFeedback=require("./useHapticFeedback.js");var _useNotification=require("./useNotification.js");var _useNetworkStatus=require("./useNetworkStatus.js");var _useDeviceInfo=require("./useDeviceInfo.js");var _useDataProtection=require("./useDataProtection.js");var _useSafeArea=require("./useSafeArea.js");var _useVideoStream=require("./useVideoStream.js");var _useNativeTransition=require("./useNativeTransition.js");
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.useCameraPermission=exports.useCamera=exports.requestCameraPermission=void 0;var _react=require("react");var _NativeBridge=_interopRequireDefault(require("../utils/NativeBridge.js"));var _NativeInterfaces=require("../constants/NativeInterfaces.js");var _useBaseHook=require("../useBaseHook.js");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}/* eslint-disable react-compiler/react-compiler, react-hooks/exhaustive-deps */const useCamera=({webFallback}={})=>{const base=(0,_useBaseHook.useBaseHook)("useCamera",{hasWebFallback:true,webFallback});const[permission,setPermission]=(0,_react.useState)(null);if(typeof window==="undefined"){return{data:null,loading:false,progress:null,error:null,isWeb:true,isNative:false,webFallbackActive:false,webFallbackDisabled:false,setWebFallback:()=>{},execute:()=>{},clear:()=>{},clearError:()=>{},permission:null,photo:null,takePhoto:()=>{},isLoading:false,clearPhoto:()=>{}};}if(base.webFallbackDisabled){return{data:null,loading:false,progress:null,error:null,isWeb:true,isNative:false,webFallbackActive:false,webFallbackDisabled:true,setWebFallback:base.setWebFallback,execute:()=>{},clear:()=>{},clearError:()=>{},permission:null,photo:null,takePhoto:()=>{},isLoading:false,clearPhoto:()=>{}};}// Web fallback callback — always declared (Rules of Hooks)
|
|
2
|
+
const webTakePhoto=(0,_react.useCallback)(()=>{const input=document.createElement("input");input.type="file";input.accept="image/*";input.capture="environment";input.onchange=()=>{const file=input.files?.[0];if(!file)return;const fileSrc=URL.createObjectURL(file);base.setDataAndComplete({fileSrc,fileName:file.name,size:file.size,mimeType:file.type,transport:"OBJECT_URL",source:"camera"});};input.click();},[base]);if(base.webFallbackActive){return{data:base.data,loading:base.loading,progress:base.progress,error:base.error,isWeb:true,isNative:false,webFallbackActive:true,webFallbackDisabled:false,setWebFallback:base.setWebFallback,execute:webTakePhoto,clear:base.clear,clearError:base.clearError,permission:null,photo:base.data,takePhoto:webTakePhoto,isLoading:base.loading,clearPhoto:base.clear};}(0,_react.useEffect)(()=>{window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_CAMERA_CAPTURE,data=>{try{const result=typeof data==="string"?JSON.parse(data):data;console.log("📷 Camera capture result:",result);const photoData=result.fileSrc?{fileSrc:result.fileSrc,fileName:result.fileName,size:result.size,mimeType:result.mimeType,transport:result.transport,source:result.source}:{fileSrc:result.imageUrl,fileName:"camera_photo.jpg",size:0,mimeType:"image/jpeg",transport:"LEGACY",source:"camera"};base.setDataAndComplete(photoData);if(photoData.transport){base.updateProgress({transport:photoData.transport,bytesTotal:photoData.size||null});}console.log("📷 Photo captured successfully via transport:",photoData.transport);}catch(parseError){console.error("📷 Error parsing camera capture data:",parseError);base.handleNativeError("Failed to process captured photo");}});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.CAMERA_PERMISSION_STATUS,data=>{setPermission(data);console.log("📷 Camera permission status:",data);});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_CAMERA_ERROR,data=>{console.error("📷 Camera error:",data);base.handleNativeError(data);});return()=>{window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.CAMERA_PERMISSION_STATUS);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_CAMERA_CAPTURE);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_CAMERA_ERROR);};},[base.setDataAndComplete,base.handleNativeError,base.updateProgress]);const takePhoto=()=>{console.log("📷 Camera open requested");base.executeOperation(()=>{base.updateProgress({state:"capturing",phase:"requesting",message:"Opening camera..."});_NativeBridge.default.camera.open();},"camera capture");};return{data:base.data,loading:base.loading,progress:base.progress,error:base.error,isWeb:base.isWeb,isNative:base.isNative,webFallbackActive:false,webFallbackDisabled:false,setWebFallback:base.setWebFallback,execute:takePhoto,clear:base.clear,clearError:base.clearError,permission,photo:base.data,takePhoto,isLoading:base.loading,clearPhoto:base.clear};};exports.useCamera=useCamera;const requestCameraPermission=()=>{if(typeof window==="undefined"){return Promise.resolve(null);}if(!window.WebBridge){throw new Error("WebBridge is not initialized. Call WebBridge.init() first.");}return new Promise((resolve,reject)=>{try{if(!_NativeBridge.default.isAvailable()){reject(new Error("Native bridge not available"));return;}const handlePermissionStatus=data=>{window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.CAMERA_PERMISSION_STATUS);if(data===_NativeInterfaces.PERMISSION_STATUS.GRANTED){resolve(data);}else{reject(new Error(`Camera permission ${data.toLowerCase()}`));}};window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.CAMERA_PERMISSION_STATUS,handlePermissionStatus);_NativeBridge.default.camera.requestPermission();console.log("📷 Camera permission requested");}catch(error){reject(error);}});};exports.requestCameraPermission=requestCameraPermission;const useCameraPermission=({webFallback}={})=>{const isWeb=typeof window==="undefined"?true:!_NativeBridge.default.isAvailable();// All hooks unconditionally at top level (Rules of Hooks)
|
|
3
|
+
const[webFallbackState,setWebFallback]=(0,_react.useState)(true);const webFallbackResolved=webFallback!==undefined?webFallback!==false:webFallbackState;const webFallbackActive=isWeb&&webFallbackResolved;const webFallbackDisabled=isWeb&&!webFallbackResolved;const[permission,setPermission]=(0,_react.useState)(null);const[isLoading,setIsLoading]=(0,_react.useState)(true);const permResultRef=(0,_react.useRef)(null);(0,_react.useEffect)(()=>{if(typeof window==="undefined"){setIsLoading(false);return;}if(webFallbackDisabled){setIsLoading(false);return;}if(!window.WebBridge){// Web fallback: navigator.permissions API (query only — never prompts)
|
|
4
|
+
if(typeof navigator==="undefined"||!navigator.permissions){setPermission(_NativeInterfaces.PERMISSION_STATUS.NOT_DETERMINED);setIsLoading(false);return;}const PERM_MAP={granted:_NativeInterfaces.PERMISSION_STATUS.GRANTED,denied:_NativeInterfaces.PERMISSION_STATUS.DENIED,prompt:_NativeInterfaces.PERMISSION_STATUS.NOT_DETERMINED};navigator.permissions.query({name:"camera"}).then(result=>{permResultRef.current=result;setPermission(PERM_MAP[result.state]??_NativeInterfaces.PERMISSION_STATUS.NOT_DETERMINED);setIsLoading(false);result.onchange=()=>{setPermission(PERM_MAP[result.state]??_NativeInterfaces.PERMISSION_STATUS.NOT_DETERMINED);};}).catch(()=>{setPermission(_NativeInterfaces.PERMISSION_STATUS.NOT_DETERMINED);setIsLoading(false);});return;}// Native path
|
|
5
|
+
const requestPermission=async()=>{try{if(!_NativeBridge.default.isAvailable()){setPermission(_NativeInterfaces.PERMISSION_STATUS.NOT_DETERMINED);setIsLoading(false);return;}window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.CAMERA_PERMISSION_STATUS,data=>{setPermission(data);setIsLoading(false);console.log("📷 Camera permission status updated:",data);});_NativeBridge.default.camera.requestPermission();}catch(error){console.error("📷 Error requesting camera permission:",error);setPermission(_NativeInterfaces.PERMISSION_STATUS.DENIED);setIsLoading(false);}};requestPermission();return()=>{if(window.WebBridge&&_NativeBridge.default.isAvailable()){window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.CAMERA_PERMISSION_STATUS);}};},[webFallbackDisabled]);// Web: navigator.permissions.query() never prompts. request() triggers getUserMedia
|
|
6
|
+
// which causes the browser to show the permission dialog, then re-reads permission state.
|
|
7
|
+
const request=(0,_react.useCallback)(async()=>{if(!isWeb||typeof window==="undefined")return;if(!navigator.mediaDevices?.getUserMedia)return;try{setIsLoading(true);const stream=await navigator.mediaDevices.getUserMedia({video:true,audio:false});stream.getTracks().forEach(t=>t.stop());}catch(_){// Ignore — the permission state change fires via permResultRef.current.onchange
|
|
8
|
+
}// Re-query to get the authoritative state after the prompt
|
|
9
|
+
if(navigator.permissions){try{const result=await navigator.permissions.query({name:"camera"});const PERM_MAP={granted:_NativeInterfaces.PERMISSION_STATUS.GRANTED,denied:_NativeInterfaces.PERMISSION_STATUS.DENIED,prompt:_NativeInterfaces.PERMISSION_STATUS.NOT_DETERMINED};setPermission(PERM_MAP[result.state]??_NativeInterfaces.PERMISSION_STATUS.NOT_DETERMINED);}catch(_){/* permission query not supported */}}setIsLoading(false);},[isWeb]);if(typeof window==="undefined"){return{permission:null,isLoading:false,webFallbackActive:false,webFallbackDisabled:false,setWebFallback:()=>{},request:()=>{}};}return{permission,isLoading,webFallbackActive,webFallbackDisabled,setWebFallback,request};};exports.useCameraPermission=useCameraPermission;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.useDataProtection=void 0;var _react=require("react");var _NativeBridge=_interopRequireDefault(require("../utils/NativeBridge.js"));var _NativeInterfaces=require("../constants/NativeInterfaces.js");var _useBaseHook=require("../useBaseHook.js");var _utils=require("./utils.js");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}/* eslint-disable react-compiler/react-compiler, react-hooks/exhaustive-deps */const useDataProtection=({webFallback}={})=>{const base=(0,_useBaseHook.useBaseHook)("useDataProtection",{hasWebFallback:true,webFallback});const[screenSecure,setScreenSecureState]=(0,_react.useState)(false);// Web fallback state — always declared (Rules of Hooks)
|
|
2
|
+
const[screenSecureWeb,setScreenSecureWeb]=(0,_react.useState)(false);const secureOverlayRef=(0,_react.useRef)(null);(0,_react.useEffect)(()=>{if(!base.webFallbackActive||!screenSecureWeb)return;const overlay=document.createElement("div");overlay.style.cssText="position:fixed;inset:0;background:#000;z-index:2147483647;display:none";document.body.appendChild(overlay);secureOverlayRef.current=overlay;const onVisibilityChange=()=>{overlay.style.display=document.hidden?"block":"none";};document.addEventListener("visibilitychange",onVisibilityChange);return()=>{document.removeEventListener("visibilitychange",onVisibilityChange);overlay.remove();secureOverlayRef.current=null;};},[base.webFallbackActive,screenSecureWeb]);const webSetScreenSecure=(0,_react.useCallback)(enable=>{setScreenSecureWeb(Boolean(enable));},[]);const webClearWebData=(0,_react.useCallback)(async()=>{try{localStorage.clear();sessionStorage.clear();}catch(_){/* storage may be unavailable in restricted contexts */}document.cookie.split(";").forEach(c=>{document.cookie=c.trim().split("=")[0]+"=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/";});if(window.indexedDB?.databases){const dbs=await window.indexedDB.databases().catch(()=>[]);await Promise.all(dbs.map(db=>new Promise(res=>{const req=window.indexedDB.deleteDatabase(db.name);req.onsuccess=req.onerror=res;})));}},[]);if(typeof window==="undefined"){return{screenSecure:false,setScreenSecure:()=>{},clearWebData:()=>{},data:null,loading:false,progress:null,error:null,isWeb:true,isNative:false,webFallbackActive:false,webFallbackDisabled:false,setWebFallback:()=>{},clear:()=>{},clearError:()=>{}};}if(base.webFallbackDisabled){return{screenSecure:false,setScreenSecure:()=>{},clearWebData:()=>{},data:null,loading:false,progress:null,error:null,isWeb:true,isNative:false,webFallbackActive:false,webFallbackDisabled:true,setWebFallback:base.setWebFallback,clear:()=>{},clearError:()=>{}};}if(base.webFallbackActive){return{screenSecure:screenSecureWeb,setScreenSecure:webSetScreenSecure,clearWebData:webClearWebData,data:null,loading:false,progress:null,error:null,isWeb:true,isNative:false,webFallbackActive:true,webFallbackDisabled:false,setWebFallback:base.setWebFallback,clear:()=>{},clearError:()=>{}};}(0,_react.useEffect)(()=>{if(!_NativeBridge.default.isAvailable())return;const handleScreenSecureSet=data=>{try{const result=typeof data==="string"?JSON.parse(data):data;if(result?.error){base.handleNativeError(result.error);return;}setScreenSecureState(Boolean(result?.secure));base.setDataAndComplete(result);}catch(error){base.handleNativeError(error);}};const handleScreenSecureError=data=>{try{const result=typeof data==="string"?JSON.parse(data):data;base.handleNativeError(result?.error||"setScreenSecure failed");}catch(error){base.handleNativeError(error);}};const handleWebDataCleared=data=>{try{const result=typeof data==="string"?JSON.parse(data):data;if(result?.error){base.handleNativeError(result.error);return;}base.setDataAndComplete(result);}catch(error){base.handleNativeError(error);}};const handleWebDataClearError=data=>{try{const result=typeof data==="string"?JSON.parse(data):data;base.handleNativeError(result?.error||"clearWebData failed");}catch(error){base.handleNativeError(error);}};const handleScreenSecureStatus=data=>{try{const result=typeof data==="string"?JSON.parse(data):data;if(result?.error){base.handleNativeError(result.error);return;}setScreenSecureState(Boolean(result?.secure));}catch(error){base.handleNativeError(error);}};const cleanup=(0,_utils.registerNativeHandlers)([[_NativeInterfaces.NATIVE_CALLBACKS.ON_SCREEN_SECURE_SET,handleScreenSecureSet],[_NativeInterfaces.NATIVE_CALLBACKS.ON_SCREEN_SECURE_STATUS,handleScreenSecureStatus],[_NativeInterfaces.NATIVE_CALLBACKS.ON_SCREEN_SECURE_ERROR,handleScreenSecureError],[_NativeInterfaces.NATIVE_CALLBACKS.ON_WEB_DATA_CLEARED,handleWebDataCleared],[_NativeInterfaces.NATIVE_CALLBACKS.ON_WEB_DATA_CLEAR_ERROR,handleWebDataClearError]]);base.callNative(()=>_NativeBridge.default.security.getScreenSecure());return cleanup;},[base.setDataAndComplete,base.handleNativeError]);const setScreenSecure=(0,_react.useCallback)(enable=>{base.callNative(()=>_NativeBridge.default.security.setScreenSecure(enable));},[base.callNative]);const clearWebData=(0,_react.useCallback)(()=>{base.callNative(()=>_NativeBridge.default.security.clearWebData());},[base.callNative]);return{data:base.data,loading:base.loading,progress:base.progress,error:base.error,isWeb:base.isWeb,isNative:base.isNative,webFallbackActive:false,webFallbackDisabled:false,setWebFallback:base.setWebFallback,clear:base.clear,clearError:base.clearError,screenSecure,setScreenSecure,clearWebData};};exports.useDataProtection=useDataProtection;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.useDeviceInfo=void 0;var _react=require("react");var _NativeBridge=_interopRequireDefault(require("../utils/NativeBridge.js"));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}/* eslint-disable react-compiler/react-compiler, react-hooks/exhaustive-deps */const useDeviceInfo=({webFallback}={})=>{const[deviceInfo,setDeviceInfo]=(0,_react.useState)(null);const[loading,setLoading]=(0,_react.useState)(true);const[error,setError]=(0,_react.useState)(null);const isNative=_NativeBridge.default.isAvailable();const[webFallbackState,setWebFallback]=(0,_react.useState)(true);const webFallbackResolved=webFallback!==undefined?webFallback!==false:webFallbackState;const webFallbackActive=!isNative&&webFallbackResolved;const webFallbackDisabled=!isNative&&!webFallbackResolved;(0,_react.useEffect)(()=>{if(typeof window==="undefined"){setLoading(false);return;}if(!_NativeBridge.default.isAvailable()){if(webFallbackDisabled){setLoading(false);return;}// Web fallback: derive info from browser APIs
|
|
2
|
+
const ua=navigator.userAgent||"";setDeviceInfo({model:ua,manufacturer:"browser",platform:"web",screenWidth:screen.width*(window.devicePixelRatio||1),screenHeight:screen.height*(window.devicePixelRatio||1),screenDensity:window.devicePixelRatio||1,appInfo:null});setLoading(false);return;}if(!window.WebBridge){setError("WebBridge not initialized");setLoading(false);return;}window.WebBridge.getDeviceInfo().then(info=>{setDeviceInfo(info);setLoading(false);}).catch(err=>{setError(err.message||"Failed to get device info");setLoading(false);});},[webFallbackDisabled]);return{deviceInfo,loading,error,isNative,isWeb:!isNative,webFallbackActive,webFallbackDisabled,setWebFallback};};exports.useDeviceInfo=useDeviceInfo;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.useFilePicker=exports.sanitizeFilePickerOptions=void 0;var _react=require("react");var _NativeBridge=_interopRequireDefault(require("../utils/NativeBridge.js"));var _NativeInterfaces=require("../constants/NativeInterfaces.js");var _useBaseHook=require("../useBaseHook.js");var _FileObjectConverter=require("../utils/FileObjectConverter.js");var _utils=require("./utils.js");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}/* eslint-disable react-compiler/react-compiler, react-hooks/exhaustive-deps */const SSR_FILE_PICKER_STUB={data:null,loading:false,progress:null,error:null,isWeb:true,isNative:false,execute:_utils.noop,clear:_utils.noop,clearError:_utils.noop,getFileObject:(0,_utils.createSSRUnavailable)("getFileObject"),getAllFileObjects:(0,_utils.createSSRUnavailable)("getAllFileObjects"),canCreateFileObject:false,canCreateFileObjects:[],selectedFile:null,selectedFiles:[],pickFile:_utils.noop,isLoading:false,processingState:null,clearFile:_utils.noop};const extractFiles=value=>{if(!value)return[];if(Array.isArray(value))return value.filter(Boolean);if(Array.isArray(value.files))return value.files.filter(Boolean);if(value.files)return[value.files];return[value].filter(Boolean);};const sanitizeFilePickerOptions=input=>{const options={...input};const mime=typeof options.mimeType==="string"?options.mimeType.trim():"";options.mimeType=mime.length>0?mime:"*/*";const coerceInteger=(value,min)=>{if(value==null)return undefined;const numeric=typeof value==="string"?Number(value.trim()):Number(value);if(!Number.isFinite(numeric)){throw new Error("File picker numeric options must be valid numbers");}if(!Number.isInteger(numeric)){throw new Error("File picker numeric options must be integers");}if(numeric<min){throw new Error(`File picker numeric options must be ≥ ${min}`);}return numeric;};const minFiles=coerceInteger(options.minFiles,1);const maxFiles=coerceInteger(options.maxFiles,1);const minFileSize=coerceInteger(options.minFileSize,0);const maxFileSize=coerceInteger(options.maxFileSize,0);if(minFiles!==undefined)options.minFiles=minFiles;if(maxFiles!==undefined)options.maxFiles=maxFiles;if(minFileSize!==undefined)options.minFileSize=minFileSize;if(maxFileSize!==undefined)options.maxFileSize=maxFileSize;if(minFiles!==undefined&&maxFiles!==undefined&&minFiles>maxFiles){throw new Error("minFiles cannot be greater than maxFiles");}if(minFileSize!==undefined&&maxFileSize!==undefined&&minFileSize>maxFileSize){throw new Error("minFileSize cannot be greater than maxFileSize");}const multiple=typeof options.multiple==="boolean"?options.multiple:Boolean(options.multiple);options.multiple=multiple||minFiles&&minFiles>1||maxFiles&&maxFiles>1;return options;};exports.sanitizeFilePickerOptions=sanitizeFilePickerOptions;const resolvePickPayload=input=>{if(input==null)return"*/*";if(typeof input==="string")return input.trim()||"*/*";return sanitizeFilePickerOptions(input);};const normalizeFilePickResult=payload=>{const parsed=(0,_utils.parseNativePayload)(payload);if(!parsed)return null;const files=extractFiles(parsed);if(!files.length)return null;const first=files[0];const rawOptions=!Array.isArray(parsed)&&parsed&&typeof parsed.options==="object"?parsed.options:null;let normalizedOptions=rawOptions||null;if(rawOptions){try{normalizedOptions=sanitizeFilePickerOptions(rawOptions);}catch(_error){normalizedOptions=rawOptions;}}const totalSize=parsed.totalSize??files.reduce((sum,file)=>{const size=typeof file?.size==="number"?file.size:0;return sum+size;},0);const base=Array.isArray(parsed)?{}:parsed;return{...base,files,multiple:parsed.multiple??files.length>1,count:parsed.count??files.length,totalSize,fileName:parsed.fileName??first?.fileName??first?.name??null,fileSrc:parsed.fileSrc??first?.fileSrc??first?.src??null,filePath:parsed.filePath??first?.filePath??null,size:parsed.size??first?.size??null,mimeType:parsed.mimeType??first?.mimeType??first?.type??null,transport:parsed.transport??first?.transport??null,options:normalizedOptions};};const updateProgressFromResult=(updateProgress,result)=>{if(!result)return;const first=result.files?.[0];const transport=result.transport??first?.transport??null;const bytesTotal=result.totalSize??result.size??(typeof first?.size==="number"?first.size:null);if(transport||bytesTotal!=null){updateProgress({transport:transport??null,bytesTotal});}};const mapStateToProgress=state=>{if(state==null){return{state:"starting",phase:null,message:"File picker: starting..."};}const rawState=String(state);const normalized=rawState.toLowerCase();const allowedStates=new Set(["opening","processing","routing"]);const progressState=allowedStates.has(normalized)?normalized:"starting";return{state:progressState,phase:rawState,message:`File picker: ${rawState}...`};};const useFilePicker=({webFallback}={})=>{const base=(0,_useBaseHook.useBaseHook)("useFilePicker",{hasWebFallback:true,webFallback});const fileObjectCache=(0,_react.useRef)(new Map());// Web fallback callbacks — always declared (Rules of Hooks)
|
|
2
|
+
const webPickFile=(0,_react.useCallback)((input=null)=>{let accept="*/*";let multiple=false;if(typeof input==="string"){accept=input.trim()||"*/*";}else if(input&&typeof input==="object"){try{const opts=sanitizeFilePickerOptions(input);accept=opts.mimeType||"*/*";multiple=Boolean(opts.multiple);}catch(err){base.handleNativeError(err instanceof Error?err.message:"Invalid file picker options");return;}}const el=document.createElement("input");el.type="file";el.accept=accept;el.multiple=multiple;el.onchange=()=>{const fileList=Array.from(el.files||[]);if(!fileList.length)return;const files=fileList.map(f=>({fileName:f.name,fileSrc:URL.createObjectURL(f),size:f.size,mimeType:f.type,transport:"OBJECT_URL"}));base.setDataAndComplete({files,multiple:files.length>1,count:files.length,totalSize:files.reduce((sum,f)=>sum+(f.size||0),0),fileName:files[0].fileName,fileSrc:files[0].fileSrc,filePath:null,size:files[0].size,mimeType:files[0].mimeType,transport:"OBJECT_URL",options:null});};el.click();},[base]);const webFiles=(0,_react.useMemo)(()=>extractFiles(base.data),[base.data]);if(typeof window==="undefined"){return SSR_FILE_PICKER_STUB;}if(base.webFallbackDisabled){return{data:null,loading:false,progress:null,error:null,isWeb:true,isNative:false,webFallbackActive:false,webFallbackDisabled:true,setWebFallback:base.setWebFallback,execute:_utils.noop,clear:_utils.noop,clearError:_utils.noop,getFileObject:(0,_utils.createSSRUnavailable)("getFileObject"),getAllFileObjects:(0,_utils.createSSRUnavailable)("getAllFileObjects"),canCreateFileObject:false,canCreateFileObjects:[],selectedFile:null,selectedFiles:[],pickFile:_utils.noop,isLoading:false,processingState:null,clearFile:_utils.noop};}if(base.webFallbackActive){return{data:base.data,loading:base.loading,progress:base.progress,error:base.error,isWeb:true,isNative:false,webFallbackActive:true,webFallbackDisabled:false,setWebFallback:base.setWebFallback,execute:webPickFile,clear:base.clear,clearError:base.clearError,getFileObject:(0,_utils.createSSRUnavailable)("getFileObject"),getAllFileObjects:(0,_utils.createSSRUnavailable)("getAllFileObjects"),canCreateFileObject:false,canCreateFileObjects:[],selectedFile:webFiles[0]||null,selectedFiles:webFiles,pickFile:webPickFile,isLoading:base.loading,processingState:null,clearFile:base.clear};}const{data,loading,progress,error,isWeb,isNative,setWebFallback,clear:baseClear,clearError,updateProgress,resetProgress,setDataAndComplete,handleNativeError,executeOperation,setLoading}=base;(0,_react.useEffect)(()=>{const handleFilePicked=payload=>{try{fileObjectCache.current.clear();const normalizedData=normalizeFilePickResult(payload);console.log("📁 File picked:",normalizedData);if(!normalizedData){throw new Error("No file data received from native file picker");}setDataAndComplete(normalizedData);updateProgressFromResult(updateProgress,normalizedData);}catch(error){console.error("📁 Error processing file data:",error);handleNativeError("Error processing selected file");}};const handleFilePickError=nativeError=>{console.error("📁 File pick error:",nativeError);handleNativeError(nativeError);};const handleFilePickCancelled=data=>{console.log("📁 File pick cancelled:",data);setLoading(false);resetProgress();};const handleFilePickStateUpdate=stateData=>{try{const parsedState=(0,_utils.parseNativePayload)(stateData);console.log("📁 File picker state:",parsedState?.state);const progressUpdate=mapStateToProgress(parsedState?.state);updateProgress(progressUpdate);if(parsedState?.state){setLoading(true);}}catch(parseError){console.error("📁 Error parsing state data:",parseError);}};return(0,_utils.registerNativeHandlers)([[_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICKED,handleFilePicked],[_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICK_ERROR,handleFilePickError],[_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICK_CANCELLED,handleFilePickCancelled],[_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICK_STATE_UPDATE,handleFilePickStateUpdate]]);},[handleNativeError,resetProgress,setDataAndComplete,setLoading,updateProgress]);const pickFile=(0,_react.useCallback)((input=null)=>{let payload;try{payload=resolvePickPayload(input);}catch(error){const message=error instanceof Error?error.message:"Invalid file picker options";handleNativeError(message);return;}if(typeof payload==="string"){console.log("📁 Picking file with MIME type:",payload);}else{console.log("📁 Picking file with options:",payload);}executeOperation(()=>{_NativeBridge.default.file.pick(payload);},"file pick");},[executeOperation,handleNativeError]);const files=(0,_react.useMemo)(()=>extractFiles(data),[data]);const getFileObject=(0,_react.useCallback)(async(index=0)=>{if(!files.length){throw new Error("No file data available. Please pick a file first.");}const targetIndex=Number(index);const fileEntry=files[targetIndex];if(!fileEntry){throw new Error(`No file data available at index ${targetIndex}.`);}if(fileObjectCache.current.has(targetIndex)){console.log("📁 Returning cached File object",{index:targetIndex});return fileObjectCache.current.get(targetIndex);}const{fileSrc,fileName,mimeType,transport}=fileEntry;if(!(0,_FileObjectConverter.canCreateFileObject)(transport)){const errorMessage=(0,_FileObjectConverter.getUnsupportedTransportMessage)(transport);console.error("❌",errorMessage);throw new Error(errorMessage);}console.log("📁 Converting to File object",{index:targetIndex,transport,fileName,mimeType});try{let fileObject;if(transport==="BRIDGE_BASE64"){fileObject=(0,_FileObjectConverter.base64ToFile)(fileSrc,fileName,mimeType);}else if(transport==="FRAMEWORK_SERVER"){fileObject=await(0,_FileObjectConverter.urlToFile)(fileSrc,fileName,mimeType);}fileObjectCache.current.set(targetIndex,fileObject);console.log("✅ File object created successfully",{index:targetIndex,name:fileObject.name,size:fileObject.size,type:fileObject.type});return fileObject;}catch(error){console.error("❌ Failed to create File object:",error);throw new Error(`Failed to create File object: ${error.message}`);}},[files]);const getAllFileObjects=(0,_react.useCallback)(async()=>{if(!files.length){throw new Error("No file data available. Please pick a file first.");}return Promise.all(files.map((_,index)=>getFileObject(index)));},[files,getFileObject]);const canCreateFileObjectForCurrentFile=(0,_react.useMemo)(()=>files.length>0?(0,_FileObjectConverter.canCreateFileObject)(files[0].transport):false,[files]);const canCreateFileObjectsList=(0,_react.useMemo)(()=>files.map(file=>(0,_FileObjectConverter.canCreateFileObject)(file.transport)),[files]);const clear=(0,_react.useCallback)(()=>{fileObjectCache.current.clear();baseClear();},[baseClear]);return{data,loading,progress,error,isWeb,isNative,webFallbackActive:false,webFallbackDisabled:false,setWebFallback,execute:pickFile,clear,clearError,getFileObject,getAllFileObjects,canCreateFileObject:canCreateFileObjectForCurrentFile,canCreateFileObjects:canCreateFileObjectsList,selectedFile:files[0]||null,selectedFiles:files,pickFile,isLoading:loading,processingState:progress?.phase||null,clearFile:clear};};exports.useFilePicker=useFilePicker;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.useGoogleSignIn=void 0;var _react=require("react");var _NativeBridge=_interopRequireDefault(require("../utils/NativeBridge.js"));var _NativeInterfaces=require("../constants/NativeInterfaces.js");var _useBaseHook=require("../useBaseHook.js");var _errors=require("../errors.js");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}/* eslint-disable react-compiler/react-compiler, react-hooks/exhaustive-deps */const useGoogleSignIn=(defaultOptions={})=>{const base=(0,_useBaseHook.useBaseHook)("useGoogleSignIn");if(typeof window==="undefined"){return{data:null,loading:false,error:null,progress:null,isWeb:true,isNative:false,signIn:()=>{},execute:()=>{},clear:()=>{},clearError:()=>{}};}if(!window.WebBridge){throw new Error("WebBridge is not initialized. Call WebBridge.init() first.");}(0,_react.useEffect)(()=>{const handleSuccess=payload=>{try{const parsed=typeof payload==="string"?JSON.parse(payload):payload;base.setDataAndComplete(parsed);}catch(err){base.handleNativeError(err);}};const handleError=nativeError=>{base.handleNativeError(nativeError);};const handleCancelled=()=>{const error=(0,_errors.createStandardError)(_errors.ERROR_CODES.OPERATION_CANCELLED,"Google sign-in cancelled",null,"User dismissed Google sign-in");base.handleNativeError(error);};window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_GOOGLE_SIGN_IN_SUCCESS,handleSuccess);window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_GOOGLE_SIGN_IN_ERROR,handleError);window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_GOOGLE_SIGN_IN_CANCELLED,handleCancelled);return()=>{window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_GOOGLE_SIGN_IN_SUCCESS);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_GOOGLE_SIGN_IN_ERROR);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_GOOGLE_SIGN_IN_CANCELLED);};},[base]);const signIn=(0,_react.useCallback)((options={})=>{base.executeOperation(()=>{const payload={...defaultOptions,...options};_NativeBridge.default.auth.googleSignIn(payload);},"google sign-in");},[base,defaultOptions]);return{data:base.data,loading:base.loading,progress:base.progress,error:base.error,isWeb:base.isWeb,isNative:base.isNative,signIn,execute:signIn,clear:base.clear,clearError:base.clearError};};exports.useGoogleSignIn=useGoogleSignIn;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.useHapticFeedback=exports.requestHapticFeedback=void 0;var _react=require("react");var _NativeBridge=_interopRequireDefault(require("../utils/NativeBridge.js"));var _NativeInterfaces=require("../constants/NativeInterfaces.js");var _useBaseHook=require("../useBaseHook.js");var _errors=require("../errors.js");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}/* eslint-disable react-compiler/react-compiler, react-hooks/exhaustive-deps */const requestHapticFeedback=(feedbackType="light")=>{if(typeof window==="undefined"){return Promise.resolve(null);}if(!window.WebBridge){throw new Error("WebBridge is not initialized. Call WebBridge.init() first.");}return new Promise((resolve,reject)=>{try{if(!_NativeBridge.default.isAvailable()){reject(new Error("Native bridge not available"));return;}const handleHapticResponse=data=>{window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.HAPTIC_FEEDBACK);if(data===_NativeInterfaces.RESPONSE_STATUS.SUCCESS){resolve(data);}else{reject(new Error(`Haptic feedback failed: ${data}`));}};window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.HAPTIC_FEEDBACK,handleHapticResponse);_NativeBridge.default.haptic.feedback(feedbackType);console.log("📳 Haptic feedback requested:",feedbackType);}catch(error){reject(error);}});};exports.requestHapticFeedback=requestHapticFeedback;const detectHapticCapabilities=isNative=>{const userAgent=typeof navigator!=="undefined"?navigator.userAgent.toLowerCase():"";if(isNative){const platform=userAgent.includes("android")?"android":userAgent.includes("iphone")||userAgent.includes("ipad")?"ios":"native";const availableTypes=platform==="ios"?["light","medium","heavy","success","warning","error","selection","impact"]:["light","medium","heavy"];return{isSupported:true,availableTypes,platform};}const isSupported=typeof navigator!=="undefined"&&"vibrate"in navigator;return{isSupported,availableTypes:isSupported?["light","medium","heavy"]:[],platform:"web"};};const useHapticFeedback=({webFallback}={})=>{const base=(0,_useBaseHook.useBaseHook)("useHapticFeedback",{hasWebFallback:true,webFallback});const[capabilities,setCapabilities]=(0,_react.useState)(()=>detectHapticCapabilities(base.isNative));if(base.webFallbackDisabled){return{data:null,loading:false,progress:null,error:null,isWeb:true,isNative:false,webFallbackActive:false,webFallbackDisabled:true,setWebFallback:base.setWebFallback,execute:()=>{},clear:()=>{},clearError:()=>{},triggerHaptic:()=>{},trigger:()=>{},light:()=>{},medium:()=>{},heavy:()=>{},success:()=>{},warning:()=>{},errorHaptic:()=>{},selection:()=>{},impact:()=>{},capabilities:{isSupported:false,availableTypes:[],platform:"web"},isSupported:false,isAvailable:false,availableTypes:[],HAPTIC_TYPES:{}};}const HAPTIC_TYPES={LIGHT:"light",MEDIUM:"medium",HEAVY:"heavy",SUCCESS:"success",WARNING:"warning",ERROR:"error",SELECTION:"selection",IMPACT:"impact"};(0,_react.useEffect)(()=>{setCapabilities(detectHapticCapabilities(base.isNative));},[base.isNative]);const handleWebHaptic=type=>{if(!navigator.vibrate)return false;const vibrationPatterns={[HAPTIC_TYPES.LIGHT]:[50],[HAPTIC_TYPES.MEDIUM]:[100],[HAPTIC_TYPES.HEAVY]:[200],[HAPTIC_TYPES.SUCCESS]:[100,50,100],[HAPTIC_TYPES.WARNING]:[200,100,200],[HAPTIC_TYPES.ERROR]:[300,100,300,100,300],[HAPTIC_TYPES.SELECTION]:[25],[HAPTIC_TYPES.IMPACT]:[150]};navigator.vibrate(vibrationPatterns[type]||[100]);return true;};const handleHapticTrigger=async(type,options={})=>{try{base.setLoading(true);base.updateProgress({state:"active",phase:"triggering",message:`Triggering ${type} haptic feedback...`});const success=base.isNative?await requestHapticFeedback(type):handleWebHaptic(type,options);base.setDataAndComplete({lastType:type,lastOptions:options,timestamp:new Date().toISOString(),success,capabilities,lastOperation:"trigger",operationSuccess:success});return success;}catch(error){console.error("📳 Haptic feedback failed:",error);base.handleNativeError(error);return false;}};const executeHaptic=(type="light",options={})=>{if(!capabilities.isSupported){base.handleNativeError((0,_errors.createStandardError)(_errors.ERROR_CODES.FEATURE_UNSUPPORTED,"Haptic feedback not supported",null,"Device does not support haptic feedback"));return;}return handleHapticTrigger(type,options);};return{data:base.data,loading:base.loading,progress:base.progress,error:base.error,isWeb:base.isWeb,isNative:base.isNative,webFallbackActive:base.webFallbackActive,webFallbackDisabled:false,setWebFallback:base.setWebFallback,clear:base.clear,clearError:base.clearError,execute:executeHaptic,triggerHaptic:executeHaptic,trigger:executeHaptic,light:()=>executeHaptic(HAPTIC_TYPES.LIGHT),medium:()=>executeHaptic(HAPTIC_TYPES.MEDIUM),heavy:()=>executeHaptic(HAPTIC_TYPES.HEAVY),success:()=>executeHaptic(HAPTIC_TYPES.SUCCESS),warning:()=>executeHaptic(HAPTIC_TYPES.WARNING),errorHaptic:()=>executeHaptic(HAPTIC_TYPES.ERROR),selection:()=>executeHaptic(HAPTIC_TYPES.SELECTION),impact:()=>executeHaptic(HAPTIC_TYPES.IMPACT),capabilities,isSupported:capabilities.isSupported,isAvailable:capabilities.isSupported,availableTypes:capabilities.availableTypes,HAPTIC_TYPES};};exports.useHapticFeedback=useHapticFeedback;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.useIntent=void 0;var _react=require("react");var _NativeBridge=_interopRequireDefault(require("../utils/NativeBridge.js"));var _NativeInterfaces=require("../constants/NativeInterfaces.js");var _useBaseHook=require("../useBaseHook.js");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}/* eslint-disable react-compiler/react-compiler, react-hooks/exhaustive-deps */const useIntent=()=>{const base=(0,_useBaseHook.useBaseHook)("useIntent");if(typeof window==="undefined"){return{data:null,loading:false,progress:null,error:null,isWeb:true,isNative:false,execute:()=>{},clear:()=>{},clearError:()=>{},isLoading:false,processingState:null,openFile:()=>{},success:null,reset:()=>{}};}if(!window.WebBridge){throw new Error("WebBridge is not initialized. Call WebBridge.init() first.");}(0,_react.useEffect)(()=>{window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_SUCCESS,data=>{console.log("📄 Intent completed successfully:",data);base.setDataAndComplete({result:data,success:true});});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_ERROR,data=>{console.error("📄 Intent error:",data);base.handleNativeError(data);});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_CANCELLED,data=>{console.log("📄 Intent cancelled:",data);base.setLoading(false);base.resetProgress();});return()=>{window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_SUCCESS);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_ERROR);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_CANCELLED);};},[base.setDataAndComplete,base.handleNativeError,base.setLoading,base.resetProgress]);const openFile=(fileUrl,mimeType=null)=>{if(!fileUrl){base.handleNativeError("File URL is required");return;}console.log("📄 File open with intent requested:",{fileUrl,mimeType});base.executeOperation(()=>{base.updateProgress({state:"opening_file",phase:"processing",message:"Opening file with external app..."});_NativeBridge.default.file.openWithIntent(fileUrl,mimeType);},"intent file open");};return{data:base.data,loading:base.loading,progress:base.progress,error:base.error,isWeb:base.isWeb,isNative:base.isNative,execute:openFile,clear:base.clear,clearError:base.clearError,isLoading:base.loading,processingState:base.progress?.phase||null,openFile,success:base.data?.success||null,reset:base.clear};};exports.useIntent=useIntent;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.useNativeTransition=void 0;var _react=require("react");var _reactRouterDom=require("react-router-dom");var _NativeBridge=_interopRequireDefault(require("../utils/NativeBridge.js"));var _NativeInterfaces=require("../constants/NativeInterfaces.js");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}/* eslint-disable react-compiler/react-compiler, react-hooks/exhaustive-deps */const DEFAULT_DURATION=300;const DEFAULT_TIMEOUT_MULTIPLIER=3;const MIN_TIMEOUT=800;/**
|
|
2
|
+
* Wraps useNavigate with native slide/fade transitions using a snapshot overlay pattern.
|
|
3
|
+
*
|
|
4
|
+
* Native path:
|
|
5
|
+
* navigate(to, opts) → startTransition → native snapshots screen → router.navigate(to) →
|
|
6
|
+
* commitTransition → native animates overlay out revealing new page.
|
|
7
|
+
*
|
|
8
|
+
* Safety net: native-side timer (configurable via opts.timeout, default max(duration*3, 800ms))
|
|
9
|
+
* force-fades the overlay if commitTransition is never called.
|
|
10
|
+
*
|
|
11
|
+
* Web fallback: CSS overlay on document.body, driven by the same state machine.
|
|
12
|
+
*
|
|
13
|
+
* @param {Object} defaults - Default transition options applied to every navigate call
|
|
14
|
+
* type: 'slide' | 'fade' (default: 'slide')
|
|
15
|
+
* direction: 'left' | 'right' | 'up' | 'down' (default: 'left')
|
|
16
|
+
* duration: ms (default: 300)
|
|
17
|
+
* timeout: ms safety timer (default: max(duration*3, 800))
|
|
18
|
+
*/const useNativeTransition=(defaults={})=>{const router=(0,_reactRouterDom.useNavigate)();const isNative=typeof window!=="undefined"&&_NativeBridge.default.isAvailable();const[transitioning,setTransitioning]=(0,_react.useState)(false);const overlayRef=(0,_react.useRef)(null);const webTimeoutRef=(0,_react.useRef)(null);const pendingCommitRef=(0,_react.useRef)(null);// Register native callbacks once
|
|
19
|
+
(0,_react.useEffect)(()=>{if(typeof window==="undefined"||!window.WebBridge)return;window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_TRANSITION_COMMITTED,()=>{setTransitioning(false);});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_TRANSITION_CANCELLED,()=>{setTransitioning(false);});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_TRANSITION_TIMEOUT,()=>{console.warn("🔀 useNativeTransition: native safety timeout fired — overlay force-faded");setTransitioning(false);});return()=>{window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_TRANSITION_COMMITTED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_TRANSITION_CANCELLED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_TRANSITION_TIMEOUT);};},[]);// Web fallback: create/remove a CSS overlay on document.body
|
|
20
|
+
const _showWebOverlay=(0,_react.useCallback)(duration=>{if(typeof document==="undefined")return;const overlay=document.createElement("div");overlay.style.cssText=["position:fixed","inset:0","z-index:9999","background:var(--transition-overlay-color,#fff)",`transition:opacity ${duration}ms ease`,"opacity:0","pointer-events:none"].join(";");document.body.appendChild(overlay);overlayRef.current=overlay;// Trigger enter animation (fade in)
|
|
21
|
+
requestAnimationFrame(()=>{requestAnimationFrame(()=>{overlay.style.opacity="1";});});},[]);const _hideWebOverlay=(0,_react.useCallback)(duration=>{const overlay=overlayRef.current;if(!overlay)return;overlay.style.opacity="0";const cleanup=()=>{overlay.remove();overlayRef.current=null;setTransitioning(false);};// Use transitionend when possible, fallback to setTimeout
|
|
22
|
+
overlay.addEventListener("transitionend",cleanup,{once:true});webTimeoutRef.current=setTimeout(cleanup,duration+50);},[]);// Cancel any in-flight web transition (e.g. safety timeout path)
|
|
23
|
+
const _cancelWebOverlay=(0,_react.useCallback)(()=>{clearTimeout(webTimeoutRef.current);if(overlayRef.current){overlayRef.current.remove();overlayRef.current=null;}setTransitioning(false);},[]);/**
|
|
24
|
+
* navigate(to, options)
|
|
25
|
+
*
|
|
26
|
+
* Options (in addition to standard react-router NavigateOptions):
|
|
27
|
+
* type: 'slide' | 'fade'
|
|
28
|
+
* direction: 'left' | 'right' | 'up' | 'down'
|
|
29
|
+
* duration: ms
|
|
30
|
+
* timeout: ms (native safety timer; web safety timer uses duration*3)
|
|
31
|
+
* replace: boolean (passed to router)
|
|
32
|
+
* state: any (passed to router)
|
|
33
|
+
*/const navigate=(0,_react.useCallback)((to,options={})=>{const{type=defaults.type??"slide",direction=defaults.direction??"left",duration=defaults.duration??DEFAULT_DURATION,timeout=defaults.timeout??Math.max(duration*DEFAULT_TIMEOUT_MULTIPLIER,MIN_TIMEOUT),replace=false,state,...rest}=options;const routerOpts={replace,state,...rest};setTransitioning(true);if(isNative){try{_NativeBridge.default.transition.start({type,direction,duration,timeout});// Route swap happens immediately — native overlay hides the skeleton
|
|
34
|
+
router(to,routerOpts);// Give the new page one frame to mount before committing
|
|
35
|
+
requestAnimationFrame(()=>{requestAnimationFrame(()=>{try{_NativeBridge.default.transition.commit();}catch(err){console.error("🔀 useNativeTransition: commitTransition failed:",err);_NativeBridge.default.transition.cancel();setTransitioning(false);}});});}catch(err){console.error("🔀 useNativeTransition: startTransition failed, falling back to plain navigate:",err);setTransitioning(false);router(to,routerOpts);}}else{// Web fallback — CSS overlay
|
|
36
|
+
_showWebOverlay(duration);const webSafetyTimeout=Math.max(duration*DEFAULT_TIMEOUT_MULTIPLIER,MIN_TIMEOUT);pendingCommitRef.current={duration,webSafetyTimeout};// Route swap after overlay is visible
|
|
37
|
+
requestAnimationFrame(()=>{requestAnimationFrame(()=>{router(to,routerOpts);// Commit: fade overlay back out
|
|
38
|
+
requestAnimationFrame(()=>{requestAnimationFrame(()=>{_hideWebOverlay(duration);});});});});// Web safety timeout
|
|
39
|
+
webTimeoutRef.current=setTimeout(()=>{console.warn("🔀 useNativeTransition: web safety timeout — removing overlay");_cancelWebOverlay();},webSafetyTimeout);}},[isNative,defaults,router,_showWebOverlay,_hideWebOverlay,_cancelWebOverlay]);/**
|
|
40
|
+
* Imperatively cancel an in-flight transition (e.g. error boundary).
|
|
41
|
+
* On native: fires cancelTransition bridge command.
|
|
42
|
+
* On web: removes overlay immediately.
|
|
43
|
+
*/const cancelTransition=(0,_react.useCallback)(()=>{if(isNative){try{_NativeBridge.default.transition.cancel();}catch(_){/* swallow — cancel is best-effort */}}else{_cancelWebOverlay();}setTransitioning(false);},[isNative,_cancelWebOverlay]);return{navigate,cancelTransition,transitioning,isNative,isWeb:!isNative};};exports.useNativeTransition=useNativeTransition;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.useNetworkStatus=void 0;var _react=require("react");var _NativeBridge=_interopRequireDefault(require("../utils/NativeBridge.js"));var _NativeInterfaces=require("../constants/NativeInterfaces.js");var _utils=require("./utils.js");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}/* eslint-disable react-compiler/react-compiler, react-hooks/exhaustive-deps */const useNetworkStatus=({webFallback}={})=>{// navigator.onLine is unreliable in some dev/Electron environments — default true if undefined
|
|
2
|
+
const initialOnline=typeof navigator!=="undefined"&&typeof navigator.onLine==="boolean"?navigator.onLine:true;const[status,setStatus]=(0,_react.useState)({online:initialOnline,type:null});const[error,setError]=(0,_react.useState)(null);const isNative=_NativeBridge.default.isAvailable();const[webFallbackState,setWebFallback]=(0,_react.useState)(true);const webFallbackResolved=webFallback!==undefined?webFallback!==false:webFallbackState;const webFallbackActive=!isNative&&webFallbackResolved;const webFallbackDisabled=!isNative&&!webFallbackResolved;(0,_react.useEffect)(()=>{if(typeof window==="undefined")return;if(!isNative){if(webFallbackDisabled)return;// Web fallback: browser online/offline events
|
|
3
|
+
const handleOnline=()=>setStatus({online:true,type:null});const handleOffline=()=>setStatus({online:false,type:null});window.addEventListener("online",handleOnline);window.addEventListener("offline",handleOffline);return()=>{window.removeEventListener("online",handleOnline);window.removeEventListener("offline",handleOffline);};}if(!window.WebBridge)return;const handleStatus=payload=>{try{const parsed=(0,_utils.parseNativePayload)(payload)||{};setStatus({online:Boolean(parsed.online),type:parsed.type||null});setError(null);}catch(e){console.error("🌐 Error parsing network status:",e);setError(e.message);}};window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.NETWORK_STATUS_CHANGED,handleStatus);try{_NativeBridge.default.network.getStatus();}catch(e){setError(e.message||"Network status unavailable");}return()=>{window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.NETWORK_STATUS_CHANGED);};},[isNative,webFallbackDisabled]);return{...status,error,isNative,isWeb:!isNative,webFallbackActive,webFallbackDisabled,setWebFallback};};exports.useNetworkStatus=useNetworkStatus;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.useNotificationPermission=exports.useNotification=exports.requestNotificationPermission=void 0;var _react=require("react");var _NativeBridge=_interopRequireDefault(require("../utils/NativeBridge.js"));var _NativeInterfaces=require("../constants/NativeInterfaces.js");var _useBaseHook=require("../useBaseHook.js");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}/* eslint-disable react-compiler/react-compiler, react-hooks/exhaustive-deps */// Shared callback system — allows hook + requestNotificationPermission to share one WebBridge listener
|
|
2
|
+
const permissionStatusListeners=new Set();let isPermissionCallbackRegistered=false;const registerPermissionStatusListener=callback=>{permissionStatusListeners.add(callback);if(!isPermissionCallbackRegistered&&typeof window!=="undefined"&&window.WebBridge){isPermissionCallbackRegistered=true;window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.NOTIFICATION_PERMISSION_STATUS,data=>{permissionStatusListeners.forEach(listener=>{try{listener(data);}catch(error){console.error("Error in permission status listener:",error);}});});}return()=>{permissionStatusListeners.delete(callback);};};const unregisterPermissionStatusListener=callback=>{permissionStatusListeners.delete(callback);};const requestNotificationPermission=()=>{if(typeof window==="undefined"){return Promise.resolve(null);}if(!window.WebBridge){throw new Error("WebBridge is not initialized. Call WebBridge.init() first.");}return new Promise((resolve,reject)=>{try{if(!_NativeBridge.default.isAvailable()){reject(new Error("Native bridge not available"));return;}const handlePermissionStatus=data=>{unregisterPermissionStatusListener(handlePermissionStatus);if(data===_NativeInterfaces.PERMISSION_STATUS.GRANTED){resolve(data);}else{reject(new Error(`Notification permission ${data.toLowerCase()}`));}};registerPermissionStatusListener(handlePermissionStatus);_NativeBridge.default.notification.requestPermission();}catch(error){reject(error);}});};exports.requestNotificationPermission=requestNotificationPermission;const useNotificationPermission=()=>{if(typeof window==="undefined"){return{permission:null,isLoading:false};}if(!window.WebBridge){throw new Error("WebBridge is not initialized. Call WebBridge.init() first.");}const[permission,setPermission]=(0,_react.useState)(null);const[isLoading,setIsLoading]=(0,_react.useState)(true);(0,_react.useEffect)(()=>{const requestPermission=async()=>{try{if(!_NativeBridge.default.isAvailable()){setPermission(_NativeInterfaces.PERMISSION_STATUS.NOT_DETERMINED);setIsLoading(false);return;}window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.NOTIFICATION_PERMISSION_STATUS,data=>{setPermission(data);setIsLoading(false);console.log("🔔 Notification permission status updated:",data);});_NativeBridge.default.notification.requestPermission();}catch(error){console.error("🔔 Error requesting notification permission:",error);setPermission(_NativeInterfaces.PERMISSION_STATUS.DENIED);setIsLoading(false);}};requestPermission();return()=>{window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.NOTIFICATION_PERMISSION_STATUS);};},[]);return{permission,isLoading};};exports.useNotificationPermission=useNotificationPermission;const useNotification=()=>{const base=(0,_useBaseHook.useBaseHook)("useNotification");const[permissionStatus,setPermissionStatus]=(0,_react.useState)(null);const[pushToken,setPushToken]=(0,_react.useState)(null);const[badges,setBadges]=(0,_react.useState)(0);const[lastNotification,setLastNotification]=(0,_react.useState)(null);const[subscribedTopics,setSubscribedTopics]=(0,_react.useState)([]);if(typeof window==="undefined"){return{data:null,loading:false,error:null,execute:()=>{},scheduleLocal:()=>{},cancelLocal:()=>{},registerForPush:()=>{},updateBadge:()=>{},subscribeToTopic:()=>{},unsubscribeFromTopic:()=>{},getSubscribedTopics:()=>{},permissionStatus:null,pushToken:null,badges:0,subscribedTopics:[]};}if(!window.WebBridge){throw new Error("WebBridge is not initialized. Call WebBridge.init() first.");}(0,_react.useEffect)(()=>{const unregister=registerPermissionStatusListener(data=>{setPermissionStatus(data);});if(_NativeBridge.default.isAvailable()){try{if(_NativeBridge.default.isAndroid&&window.NativeBridge?.checkNotificationPermissionStatus){window.NativeBridge.checkNotificationPermissionStatus(null);}else if(_NativeBridge.default.isIOS&&window.webkit?.messageHandlers?.NativeBridge){window.webkit.messageHandlers.NativeBridge.postMessage({command:"checkNotificationPermissionStatus",data:null});}}catch(error){console.error("🔔 Error checking notification permission status:",error);}}window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.LOCAL_NOTIFICATION_SCHEDULED,data=>{try{const result=typeof data==="string"?JSON.parse(data):data;if(result?.success===false||result?.error){base.handleNativeError(result.error||result);return;}base.setDataAndComplete(result);}catch(error){base.handleNativeError(error);}});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.LOCAL_NOTIFICATION_CANCELLED,data=>{try{const result=typeof data==="string"?JSON.parse(data):data;if(result?.success===false||result?.error){base.handleNativeError(result.error||result);return;}base.setDataAndComplete(result);}catch(error){base.handleNativeError(error);}});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.PUSH_NOTIFICATION_TOKEN,data=>{try{const result=typeof data==="string"?JSON.parse(data):data;if(result?.error){base.handleNativeError(result.error);return;}setPushToken(result.token);base.setDataAndComplete(result);}catch(error){base.handleNativeError(error);}});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.NOTIFICATION_RECEIVED,data=>{const notification=typeof data==="string"?JSON.parse(data):data;setLastNotification(notification);});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.NOTIFICATION_TAPPED,data=>{try{const result=typeof data==="string"?JSON.parse(data):data;base.setDataAndComplete(result);}catch(error){base.handleNativeError(error);}});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.NOTIFICATION_ACTION_PERFORMED,data=>{const action=typeof data==="string"?JSON.parse(data):data;base.setDataAndComplete(action);});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.TOPIC_SUBSCRIPTION_RESULT,data=>{try{const result=typeof data==="string"?JSON.parse(data):data;if(result?.error){base.handleNativeError(result.error);return;}if(result?.success===false){base.handleNativeError(result);return;}base.setDataAndComplete(result);}catch(error){base.handleNativeError(error);}});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.SUBSCRIBED_TOPICS_RESULT,data=>{try{const result=typeof data==="string"?JSON.parse(data):data;if(result?.error){base.handleNativeError(result.error);return;}if(result?.success===false){base.handleNativeError(result);return;}setSubscribedTopics(result.topics||[]);base.setDataAndComplete(result);}catch(error){base.handleNativeError(error);}});return()=>{unregister();window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.LOCAL_NOTIFICATION_SCHEDULED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.LOCAL_NOTIFICATION_CANCELLED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.PUSH_NOTIFICATION_TOKEN);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.NOTIFICATION_RECEIVED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.NOTIFICATION_TAPPED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.NOTIFICATION_ACTION_PERFORMED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.TOPIC_SUBSCRIPTION_RESULT);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.SUBSCRIBED_TOPICS_RESULT);};},[]);const requestPermission=()=>{requestNotificationPermission().catch(err=>base.handleNativeError(err));};const scheduleLocal=config=>{base.executeOperation(()=>{_NativeBridge.default.notification.scheduleLocal(config);},"schedule local notification");};const cancelLocal=notificationId=>{base.executeOperation(()=>{_NativeBridge.default.notification.cancelLocal(notificationId);},"cancel notification");};const registerForPush=()=>{base.executeOperation(()=>{_NativeBridge.default.notification.registerForPush();},"register for push");};const updateBadge=count=>{base.callNative(()=>_NativeBridge.default.notification.updateBadge(count));setBadges(count);};const subscribeToTopic=topic=>{base.executeOperation(()=>{_NativeBridge.default.notification.subscribeToTopic(topic);},"subscribe to topic");};const unsubscribeFromTopic=topic=>{base.executeOperation(()=>{_NativeBridge.default.notification.unsubscribeFromTopic(topic);},"unsubscribe from topic");};const getSubscribedTopics=()=>{base.executeOperation(()=>{_NativeBridge.default.notification.getSubscribedTopics();},"get subscribed topics");};return{data:base.data,loading:base.loading,progress:base.progress,error:base.error,execute:scheduleLocal,clear:base.clear,clearError:base.clearError,isNative:base.isNative,isWeb:base.isWeb,permissionStatus,pushToken,badges,lastNotification,subscribedTopics,scheduleLocal,cancelLocal,registerForPush,updateBadge,subscribeToTopic,unsubscribeFromTopic,getSubscribedTopics,schedule:scheduleLocal,requestPermission};};exports.useNotification=useNotification;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.useSafeArea=void 0;var _react=require("react");var _NativeBridge=_interopRequireDefault(require("../utils/NativeBridge.js"));var _safeArea=require("../safeArea.js");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}/* eslint-disable react-compiler/react-compiler, react-hooks/exhaustive-deps */const useSafeArea=({webFallback}={})=>{const fromGlobal=(0,_safeArea.getSafeAreaFromGlobal)();const initialValue=fromGlobal||{..._safeArea.DEFAULT_INSETS};const[insets,setInsets]=(0,_react.useState)(()=>initialValue);const isNative=_NativeBridge.default.isAvailable();const[webFallbackState,setWebFallback]=(0,_react.useState)(true);const webFallbackResolved=webFallback!==undefined?webFallback!==false:webFallbackState;const webFallbackActive=!isNative&&webFallbackResolved;const webFallbackDisabled=!isNative&&!webFallbackResolved;(0,_react.useEffect)(()=>{if(typeof window==="undefined")return;if(!_NativeBridge.default.isAvailable()){if(webFallbackDisabled)return;// Web fallback: read CSS env() safe area insets (Safari notch support)
|
|
2
|
+
const readCSSInsets=()=>{const style=getComputedStyle(document.documentElement);const read=prop=>{const val=style.getPropertyValue(prop).trim();return val?parseFloat(val)||0:0;};const webInsets={top:read("--sat")||read("env(safe-area-inset-top)"),right:read("--sar")||read("env(safe-area-inset-right)"),bottom:read("--sab")||read("env(safe-area-inset-bottom)"),left:read("--sal")||read("env(safe-area-inset-left)")};setInsets(webInsets);(0,_safeArea.setSafeAreaGlobal)(webInsets);};const id=setTimeout(readCSSInsets,0);return()=>clearTimeout(id);}if(!window.WebBridge)return;const handleInsetsUpdate=data=>{try{const parsed=typeof data==="string"?JSON.parse(data):data;const normalized={top:Number(parsed.top)||0,right:Number(parsed.right)||0,bottom:Number(parsed.bottom)||0,left:Number(parsed.left)||0};setInsets(normalized);(0,_safeArea.setSafeAreaGlobal)(normalized);}catch(error){console.error("Error parsing safe area insets:",error);setInsets({..._safeArea.DEFAULT_INSETS});}};window.WebBridge.register("ON_SAFE_AREA_INSETS_UPDATED",handleInsetsUpdate);_NativeBridge.default.safeArea.get();return()=>{window.WebBridge.unregister("ON_SAFE_AREA_INSETS_UPDATED");};},[webFallbackDisabled]);return{...insets,isNative,isWeb:!isNative,webFallbackActive,webFallbackDisabled,setWebFallback};};exports.useSafeArea=useSafeArea;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.useVideoStream=void 0;var _react=require("react");var _NativeBridge=_interopRequireDefault(require("../utils/NativeBridge.js"));var _NativeInterfaces=require("../constants/NativeInterfaces.js");var _useBaseHook=require("../useBaseHook.js");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}/* eslint-disable react-compiler/react-compiler, react-hooks/exhaustive-deps */const STREAM_STATE_DEFAULT={zoom:null,minZoom:null,maxZoom:null,torchOn:false,fpsMin:null,fpsMax:null};const useVideoStream=({onQRDetected,webFallback}={})=>{const base=(0,_useBaseHook.useBaseHook)("useVideoStream",{hasWebFallback:true,webFallback});// Native stream state
|
|
2
|
+
const[isStreaming,setIsStreaming]=(0,_react.useState)(false);const[streamState,setStreamState]=(0,_react.useState)(STREAM_STATE_DEFAULT);const onQRDetectedRef=(0,_react.useRef)(onQRDetected);onQRDetectedRef.current=onQRDetected;const viewfinderRef=(0,_react.useRef)(null);// Web fallback stream state — always declared (Rules of Hooks)
|
|
3
|
+
const[isStreamingWeb,setIsStreamingWeb]=(0,_react.useState)(false);const[streamStateWeb,setStreamStateWeb]=(0,_react.useState)(STREAM_STATE_DEFAULT);const mediaStreamRef=(0,_react.useRef)(null);const facingModeRef=(0,_react.useRef)("environment");// Web fallback: stop active stream when webFallbackActive toggles off
|
|
4
|
+
(0,_react.useEffect)(()=>{if(!base.webFallbackActive&&mediaStreamRef.current){mediaStreamRef.current.getTracks().forEach(t=>t.stop());mediaStreamRef.current=null;setIsStreamingWeb(false);setStreamStateWeb(STREAM_STATE_DEFAULT);}},[base.webFallbackActive]);// Web fallback: cleanup on unmount
|
|
5
|
+
(0,_react.useEffect)(()=>{return()=>{if(mediaStreamRef.current){mediaStreamRef.current.getTracks().forEach(t=>t.stop());mediaStreamRef.current=null;}};},[]);const applyTrackConstraints=(0,_react.useCallback)(async constraints=>{const track=mediaStreamRef.current?.getVideoTracks()[0];if(!track)return;try{await track.applyConstraints(constraints);}catch(e){base.setError({message:`applyConstraints failed: ${e.message}`});}},[base]);const webStart=(0,_react.useCallback)(async(options={})=>{if(!window.isSecureContext){base.setError({message:"getUserMedia requires a secure context (HTTPS or localhost)"});return;}if(!navigator.mediaDevices?.getUserMedia){base.setError({message:"getUserMedia is not available in this browser"});return;}try{if(mediaStreamRef.current)return;base.setLoading(true);const constraints={video:{facingMode:{ideal:facingModeRef.current},...(options.fps?{frameRate:{ideal:options.fps}}:{})},audio:false};const stream=await navigator.mediaDevices.getUserMedia(constraints);mediaStreamRef.current=stream;setIsStreamingWeb(true);base.setLoading(false);const track=stream.getVideoTracks()[0];const caps=track?.getCapabilities?.()||{};setStreamStateWeb(prev=>({...prev,minZoom:caps.zoom?.min??null,maxZoom:caps.zoom?.max??null,zoom:caps.zoom?.min??null}));}catch(e){base.setLoading(false);base.setError({message:e.message||"getUserMedia failed"});}},[base]);const webStop=(0,_react.useCallback)(()=>{if(!mediaStreamRef.current)return;mediaStreamRef.current.getTracks().forEach(t=>t.stop());mediaStreamRef.current=null;setIsStreamingWeb(false);setStreamStateWeb(STREAM_STATE_DEFAULT);},[]);const webFlip=(0,_react.useCallback)(async()=>{facingModeRef.current=facingModeRef.current==="environment"?"user":"environment";if(mediaStreamRef.current){webStop();await webStart();}},[webStart,webStop]);const webSendCommand=(0,_react.useCallback)(async(type,value)=>{if(!isStreamingWeb){base.setError({message:`sendCommand('${type}') called but stream is not active`});return;}switch(type){case"zoom":{if(typeof value!=="number"||value<1.0){base.setError({message:`sendCommand zoom: value must be a multiplier >= 1.0, got ${value}`});return;}await applyTrackConstraints({advanced:[{zoom:value}]});setStreamStateWeb(prev=>({...prev,zoom:value}));break;}case"torch":{if(typeof value!=="boolean"){base.setError({message:`sendCommand torch: value must be boolean, got ${value}`});return;}await applyTrackConstraints({advanced:[{torch:value}]});setStreamStateWeb(prev=>({...prev,torchOn:value}));break;}case"fps":{if(typeof value!=="object"||value===null){base.setError({message:`sendCommand fps: value must be { min, max }, got ${value}`});return;}const{min=null,max=null}=value;const frameRate={};if(min!==null)frameRate.min=min;if(max!==null)frameRate.max=max;await applyTrackConstraints({frameRate});setStreamStateWeb(prev=>({...prev,fpsMin:min,fpsMax:max}));break;}default:base.setError({message:`sendCommand: unknown type '${type}'`});}},[isStreamingWeb,applyTrackConstraints,base]);// Native event registration — skipped on web (WebBridge absent or native bridge unavailable)
|
|
6
|
+
(0,_react.useEffect)(()=>{if(typeof window==="undefined"||!window.WebBridge||!_NativeBridge.default.isAvailable())return;window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_VIDEO_STREAM_READY,()=>{console.log("📹 Video stream ready");setIsStreaming(true);});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_VIDEO_STREAM_STOPPED,()=>{console.log("📹 Video stream stopped");setIsStreaming(false);});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_QR_DETECTED,data=>{try{const result=typeof data==="string"?JSON.parse(data):data;console.log("📹 QR detected:",result);onQRDetectedRef.current?.(result);}catch(e){base.handleNativeError("Failed to parse QR data");}});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_TORCH_CHANGED,data=>{try{const result=typeof data==="string"?JSON.parse(data):data;const enabled=result?.enabled??false;console.log("📹 Torch changed:",enabled);setStreamState(prev=>({...prev,torchOn:enabled}));}catch(e){console.warn("[useVideoStream] Failed to parse ON_TORCH_CHANGED data:",e);}});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_ZOOM_CHANGED,data=>{try{const result=typeof data==="string"?JSON.parse(data):data;const zoom=result?.zoomLevel??null;const minZoom=result?.minZoom??null;const maxZoom=result?.maxZoom??null;console.log("📹 Zoom changed:",zoom,`(min=${minZoom} max=${maxZoom})`);setStreamState(prev=>({...prev,zoom,minZoom,maxZoom}));}catch(e){console.warn("[useVideoStream] Failed to parse ON_ZOOM_CHANGED data:",e);}});const handleBeforeUnload=()=>{_NativeBridge.default.videoStream.stop();};window.addEventListener("beforeunload",handleBeforeUnload);return()=>{window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_VIDEO_STREAM_READY);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_VIDEO_STREAM_STOPPED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_QR_DETECTED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_TORCH_CHANGED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_ZOOM_CHANGED);window.removeEventListener("beforeunload",handleBeforeUnload);_NativeBridge.default.videoStream.stop();};},[base.handleNativeError]);const nativeStart=(0,_react.useCallback)((options={})=>{base.executeOperation(()=>{console.log("[useVideoStream] start() options:",JSON.stringify(options));_NativeBridge.default.videoStream.start(options);},"start video stream");},[base.executeOperation]);const nativeStop=(0,_react.useCallback)(()=>{console.log("[useVideoStream] stop()");_NativeBridge.default.videoStream.stop();setIsStreaming(false);},[]);const nativeFlip=(0,_react.useCallback)(()=>{console.log("[useVideoStream] flip()");_NativeBridge.default.videoStream.flip();},[]);const nativeSendCommand=(0,_react.useCallback)((type,value)=>{if(!isStreaming){base.setError({message:`sendCommand('${type}') called but stream is not active`});return;}console.log(`[useVideoStream] sendCommand(${type}, ${value})`);switch(type){case"zoom":{if(typeof value!=="number"||value<1.0){base.setError({message:`sendCommand zoom: value must be a multiplier >= 1.0 (e.g. 1.0=1x, 2.0=2x), got ${value}`});return;}_NativeBridge.default.videoStream.setZoom(value);break;}case"torch":{if(typeof value!=="boolean"){base.setError({message:`sendCommand torch: value must be boolean, got ${value}`});return;}_NativeBridge.default.videoStream.setTorch(value);break;}case"fps":{if(typeof value!=="object"||value===null){base.setError({message:`sendCommand fps: value must be { min, max }, got ${value}`});return;}const{min=null,max=null}=value;if(min!==null&&(typeof min!=="number"||min<1)){base.setError({message:`sendCommand fps: min must be a positive number, got ${min}`});return;}if(max!==null&&(typeof max!=="number"||max<1)){base.setError({message:`sendCommand fps: max must be a positive number, got ${max}`});return;}if(min!==null&&max!==null&&min>max){base.setError({message:`sendCommand fps: min (${min}) must be <= max (${max})`});return;}_NativeBridge.default.videoStream.setFps(min,max);setStreamState(prev=>({...prev,fpsMin:min,fpsMax:max}));break;}default:base.setError({message:`sendCommand: unknown type '${type}'`});}},[isStreaming,base.setError]);if(typeof window==="undefined"){return{isStreaming:false,streamState:STREAM_STATE_DEFAULT,error:null,isNative:false,webFallbackActive:false,webFallbackDisabled:false,setWebFallback:()=>{},start:()=>{},stop:()=>{},sendCommand:()=>{},flip:()=>{},clearError:()=>{}};}if(base.webFallbackDisabled){return{isStreaming:false,streamState:STREAM_STATE_DEFAULT,error:null,isNative:false,webFallbackActive:false,webFallbackDisabled:true,setWebFallback:base.setWebFallback,start:()=>{},stop:()=>{},sendCommand:()=>{},flip:()=>{},clearError:()=>{}};}if(base.webFallbackActive){return{isStreaming:isStreamingWeb,streamState:streamStateWeb,error:base.error,isNative:false,webFallbackActive:true,webFallbackDisabled:false,setWebFallback:base.setWebFallback,mediaStream:mediaStreamRef.current,viewfinderRef,start:webStart,stop:webStop,sendCommand:webSendCommand,flip:webFlip,clearError:base.clearError};}return{isStreaming,streamState,error:base.error,isNative:base.isNative,webFallbackActive:false,webFallbackDisabled:false,setWebFallback:base.setWebFallback,viewfinderRef,start:nativeStart,stop:nativeStop,sendCommand:nativeSendCommand,flip:nativeFlip,clearError:base.clearError};};exports.useVideoStream=useVideoStream;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.registerNativeHandlers=exports.parseNativePayload=exports.noop=exports.createSSRUnavailable=void 0;/* eslint-disable react-compiler/react-compiler, react-hooks/exhaustive-deps */const noop=()=>{};exports.noop=noop;const createSSRUnavailable=methodName=>async()=>{throw new Error(`${methodName} is not available in SSR environment`);};exports.createSSRUnavailable=createSSRUnavailable;const parseNativePayload=payload=>{if(payload==null)return null;if(typeof payload!=="string")return payload;return JSON.parse(payload);};exports.parseNativePayload=parseNativePayload;const registerNativeHandlers=handlers=>{handlers.forEach(([event,handler])=>window.WebBridge.register(event,handler));return()=>{handlers.forEach(([event])=>window.WebBridge.unregister(event));};};exports.registerNativeHandlers=registerNativeHandlers;
|
|
@@ -1,137 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:true});
|
|
2
|
-
// This allows multiple listeners (hook + requestNotificationPermission) to receive updates
|
|
3
|
-
const permissionStatusListeners=new Set();let isPermissionCallbackRegistered=false;const registerPermissionStatusListener=callback=>{permissionStatusListeners.add(callback);// Register the shared handler with WebBridge if this is the first listener
|
|
4
|
-
if(!isPermissionCallbackRegistered&&typeof window!=="undefined"&&window.WebBridge){isPermissionCallbackRegistered=true;window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.NOTIFICATION_PERMISSION_STATUS,data=>{// Notify all listeners
|
|
5
|
-
permissionStatusListeners.forEach(listener=>{try{listener(data);}catch(error){console.error("Error in permission status listener:",error);}});});}return()=>{permissionStatusListeners.delete(callback);};};const unregisterPermissionStatusListener=callback=>{permissionStatusListeners.delete(callback);};const noop=()=>{};const createSSRUnavailable=methodName=>async()=>{throw new Error(`${methodName} is not available in SSR environment`);};const SSR_FILE_PICKER_STUB={data:null,loading:false,progress:null,error:null,isWeb:true,isNative:false,execute:noop,clear:noop,clearError:noop,getFileObject:createSSRUnavailable("getFileObject"),getAllFileObjects:createSSRUnavailable("getAllFileObjects"),canCreateFileObject:false,canCreateFileObjects:[],selectedFile:null,selectedFiles:[],pickFile:noop,isLoading:false,processingState:null,clearFile:noop};const parseNativePayload=payload=>{if(payload==null)return null;if(typeof payload!=="string")return payload;return JSON.parse(payload);};const extractFiles=value=>{if(!value)return[];if(Array.isArray(value))return value.filter(Boolean);if(Array.isArray(value.files))return value.files.filter(Boolean);if(value.files)return[value.files];return[value].filter(Boolean);};const normalizeFilePickResult=payload=>{const parsed=parseNativePayload(payload);if(!parsed)return null;const files=extractFiles(parsed);if(!files.length)return null;const first=files[0];const rawOptions=!Array.isArray(parsed)&&parsed&&typeof parsed.options==="object"?parsed.options:null;let normalizedOptions=rawOptions||null;if(rawOptions){try{normalizedOptions=sanitizeFilePickerOptions(rawOptions);}catch(_error){normalizedOptions=rawOptions;}}const totalSize=parsed.totalSize??files.reduce((sum,file)=>{const size=typeof file?.size==="number"?file.size:0;return sum+size;},0);const base=Array.isArray(parsed)?{}:parsed;return{...base,files,multiple:parsed.multiple??files.length>1,count:parsed.count??files.length,totalSize,fileName:parsed.fileName??first?.fileName??first?.name??null,fileSrc:parsed.fileSrc??first?.fileSrc??first?.src??null,filePath:parsed.filePath??first?.filePath??null,size:parsed.size??first?.size??null,mimeType:parsed.mimeType??first?.mimeType??first?.type??null,transport:parsed.transport??first?.transport??null,options:normalizedOptions};};/**
|
|
6
|
-
* Google Sign-In hook (Android-first, Credential Manager)
|
|
7
|
-
* Provides a promise-like sign-in call and standardized callbacks
|
|
8
|
-
*/const useGoogleSignIn=(defaultOptions={})=>{const base=(0,_useBaseHook.useBaseHook)("useGoogleSignIn");// Server-side rendering safety
|
|
9
|
-
if(typeof window==="undefined"){return{data:null,loading:false,error:null,progress:null,isWeb:true,isNative:false,signIn:()=>{},execute:()=>{},clear:()=>{},clearError:()=>{}};}if(!window.WebBridge){throw new Error("WebBridge is not initialized. Call WebBridge.init() first.");}(0,_react.useEffect)(()=>{const handleSuccess=payload=>{try{const parsed=typeof payload==="string"?JSON.parse(payload):payload;base.setDataAndComplete(parsed);}catch(err){base.handleNativeError(err);}};const handleError=nativeError=>{base.handleNativeError(nativeError);};const handleCancelled=()=>{const error=(0,_errors.createStandardError)(_errors.ERROR_CODES.OPERATION_CANCELLED,"Google sign-in cancelled",null,"User dismissed Google sign-in");base.handleNativeError(error);};window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_GOOGLE_SIGN_IN_SUCCESS,handleSuccess);window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_GOOGLE_SIGN_IN_ERROR,handleError);window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_GOOGLE_SIGN_IN_CANCELLED,handleCancelled);return()=>{window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_GOOGLE_SIGN_IN_SUCCESS);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_GOOGLE_SIGN_IN_ERROR);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_GOOGLE_SIGN_IN_CANCELLED);};},[base]);const signIn=(0,_react.useCallback)((options={})=>{base.executeOperation(()=>{const payload={...defaultOptions,...options};_NativeBridge.default.auth.googleSignIn(payload);},"google sign-in");},[base,defaultOptions]);return{data:base.data,loading:base.loading,progress:base.progress,error:base.error,isWeb:base.isWeb,isNative:base.isNative,signIn,execute:signIn,clear:base.clear,clearError:base.clearError};};exports.useGoogleSignIn=useGoogleSignIn;const updateProgressFromResult=(updateProgress,result)=>{if(!result)return;const first=result.files?.[0];const transport=result.transport??first?.transport??null;const bytesTotal=result.totalSize??result.size??(typeof first?.size==="number"?first.size:null);if(transport||bytesTotal!=null){updateProgress({transport:transport??null,bytesTotal});}};const mapStateToProgress=state=>{if(state==null){return{state:"starting",phase:null,message:"File picker: starting..."};}const rawState=String(state);const normalized=rawState.toLowerCase();const allowedStates=new Set(["opening","processing","routing"]);const progressState=allowedStates.has(normalized)?normalized:"starting";return{state:progressState,phase:rawState,message:`File picker: ${rawState}...`};};const sanitizeFilePickerOptions=input=>{const options={...input};const mime=typeof options.mimeType==="string"?options.mimeType.trim():"";options.mimeType=mime.length>0?mime:"*/*";const coerceInteger=(value,min)=>{if(value==null)return undefined;const numeric=typeof value==="string"?Number(value.trim()):Number(value);if(!Number.isFinite(numeric)){throw new Error("File picker numeric options must be valid numbers");}if(!Number.isInteger(numeric)){throw new Error("File picker numeric options must be integers");}if(numeric<min){throw new Error(`File picker numeric options must be ≥ ${min}`);}return numeric;};const minFiles=coerceInteger(options.minFiles,1);const maxFiles=coerceInteger(options.maxFiles,1);const minFileSize=coerceInteger(options.minFileSize,0);const maxFileSize=coerceInteger(options.maxFileSize,0);if(minFiles!==undefined)options.minFiles=minFiles;if(maxFiles!==undefined)options.maxFiles=maxFiles;if(minFileSize!==undefined)options.minFileSize=minFileSize;if(maxFileSize!==undefined)options.maxFileSize=maxFileSize;if(minFiles!==undefined&&maxFiles!==undefined&&minFiles>maxFiles){throw new Error("minFiles cannot be greater than maxFiles");}if(minFileSize!==undefined&&maxFileSize!==undefined&&minFileSize>maxFileSize){throw new Error("minFileSize cannot be greater than maxFileSize");}const multiple=typeof options.multiple==="boolean"?options.multiple:Boolean(options.multiple);options.multiple=multiple||minFiles&&minFiles>1||maxFiles&&maxFiles>1;return options;};const resolvePickPayload=input=>{if(input==null)return"*/*";if(typeof input==="string")return input.trim()||"*/*";return sanitizeFilePickerOptions(input);};const registerNativeHandlers=handlers=>{handlers.forEach(([event,handler])=>window.WebBridge.register(event,handler));return()=>{handlers.forEach(([event])=>window.WebBridge.unregister(event));};};/**
|
|
10
|
-
* React hook for camera functionality using standardized interface
|
|
11
|
-
* Handles camera permissions, photo capture, and error states
|
|
12
|
-
*/const useCamera=()=>{// Use standardized base hook
|
|
13
|
-
const base=(0,_useBaseHook.useBaseHook)("useCamera");// Camera-specific state
|
|
14
|
-
const[permission,setPermission]=(0,_react.useState)(null);// Server-side rendering safety
|
|
15
|
-
if(typeof window==="undefined"){return{data:null,loading:false,progress:null,error:null,isWeb:true,isNative:false,execute:()=>{},clear:()=>{},clearError:()=>{},permission:null,// Legacy aliases
|
|
16
|
-
photo:null,takePhoto:()=>{},isLoading:false,clearPhoto:()=>{}};}if(!window.WebBridge){throw new Error("WebBridge is not initialized. Call WebBridge.init() first.");}(0,_react.useEffect)(()=>{// Register callback handlers
|
|
17
|
-
window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_CAMERA_CAPTURE,data=>{try{const result=typeof data==="string"?JSON.parse(data):data;console.log("📷 Camera capture result:",result);// Handle new tri-transport format or legacy format
|
|
18
|
-
const photoData=result.fileSrc?{// New tri-transport format
|
|
19
|
-
fileSrc:result.fileSrc,fileName:result.fileName,size:result.size,mimeType:result.mimeType,transport:result.transport,source:result.source}:{// Legacy format (fallback)
|
|
20
|
-
fileSrc:result.imageUrl,fileName:"camera_photo.jpg",size:0,mimeType:"image/jpeg",transport:"LEGACY",source:"camera"};base.setDataAndComplete(photoData);// Update progress with transport info if available
|
|
21
|
-
if(photoData.transport){base.updateProgress({transport:photoData.transport,bytesTotal:photoData.size||null});}console.log("📷 Photo captured successfully via transport:",photoData.transport);}catch(parseError){console.error("📷 Error parsing camera capture data:",parseError);base.handleNativeError("Failed to process captured photo");}});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.CAMERA_PERMISSION_STATUS,data=>{setPermission(data);console.log("📷 Camera permission status:",data);});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_CAMERA_ERROR,data=>{console.error("📷 Camera error:",data);base.handleNativeError(data);});return()=>{// Cleanup: unregister all handlers
|
|
22
|
-
window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.CAMERA_PERMISSION_STATUS);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_CAMERA_CAPTURE);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_CAMERA_ERROR);};},[base.setDataAndComplete,base.handleNativeError,base.updateProgress]);const takePhoto=()=>{console.log("📷 Camera open requested");base.executeOperation(()=>{// Update progress to show capturing state
|
|
23
|
-
base.updateProgress({state:"capturing",phase:"requesting",message:"Opening camera..."});_NativeBridge.default.camera.open();},"camera capture");};// Standardized execute function (new interface)
|
|
24
|
-
const execute=takePhoto;return{// Standardized interface
|
|
25
|
-
data:base.data,loading:base.loading,progress:base.progress,error:base.error,isWeb:base.isWeb,isNative:base.isNative,execute,clear:base.clear,clearError:base.clearError,// Camera-specific extras
|
|
26
|
-
permission,// Legacy aliases for backward compatibility
|
|
27
|
-
photo:base.data,takePhoto,isLoading:base.loading,clearPhoto:base.clear};};/**
|
|
28
|
-
* React hook for intent handling using standardized interface
|
|
29
|
-
* Manages file opening operations with external apps
|
|
30
|
-
*/exports.useCamera=useCamera;const useIntent=()=>{// Use standardized base hook
|
|
31
|
-
const base=(0,_useBaseHook.useBaseHook)("useIntent");// Server-side rendering safety
|
|
32
|
-
if(typeof window==="undefined"){return{data:null,loading:false,progress:null,error:null,isWeb:true,isNative:false,execute:()=>{},clear:()=>{},clearError:()=>{},// Legacy aliases
|
|
33
|
-
isLoading:false,processingState:null,openFile:()=>{},success:null,reset:()=>{}};}if(!window.WebBridge){throw new Error("WebBridge is not initialized. Call WebBridge.init() first.");}(0,_react.useEffect)(()=>{// Register callback handlers
|
|
34
|
-
window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_SUCCESS,data=>{console.log("📄 Intent completed successfully:",data);base.setDataAndComplete({result:data,success:true});});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_ERROR,data=>{console.error("📄 Intent error:",data);base.handleNativeError(data);});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_CANCELLED,data=>{console.log("📄 Intent cancelled:",data);base.setLoading(false);base.resetProgress();// Keep data as is when cancelled
|
|
35
|
-
});return()=>{// Cleanup: unregister all handlers
|
|
36
|
-
window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_SUCCESS);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_ERROR);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_CANCELLED);};},[base.setDataAndComplete,base.handleNativeError,base.setLoading,base.resetProgress]);const openFile=(fileUrl,mimeType=null)=>{if(!fileUrl){base.handleNativeError("File URL is required");return;}console.log("📄 File open with intent requested:",{fileUrl,mimeType});base.executeOperation(()=>{// Update progress to show opening state
|
|
37
|
-
base.updateProgress({state:"opening_file",phase:"processing",message:"Opening file with external app..."});_NativeBridge.default.file.openWithIntent(fileUrl,mimeType);},"intent file open");};// Standardized execute function (new interface)
|
|
38
|
-
const execute=openFile;return{// Standardized interface
|
|
39
|
-
data:base.data,loading:base.loading,progress:base.progress,error:base.error,isWeb:base.isWeb,isNative:base.isNative,execute,clear:base.clear,clearError:base.clearError,// Legacy aliases for backward compatibility
|
|
40
|
-
isLoading:base.loading,processingState:base.progress?.phase||null,openFile,success:base.data?.success||null,reset:base.clear};};/**
|
|
41
|
-
* React hook for file picker functionality
|
|
42
|
-
* Manages file selection operations using standardized interface
|
|
43
|
-
*/exports.useIntent=useIntent;const useFilePicker=()=>{// Use standardized base hook
|
|
44
|
-
const base=(0,_useBaseHook.useBaseHook)("useFilePicker");// Cache for converted File objects to prevent redundant conversions
|
|
45
|
-
const fileObjectCache=(0,_react.useRef)(new Map());// Server-side rendering safety
|
|
46
|
-
if(typeof window==="undefined"){return SSR_FILE_PICKER_STUB;}if(!window.WebBridge){throw new Error("WebBridge is not initialized. Call WebBridge.init() first.");}const{data,loading,progress,error,isWeb,isNative,clear:baseClear,clearError,updateProgress,resetProgress,setDataAndComplete,handleNativeError,executeOperation,setLoading}=base;(0,_react.useEffect)(()=>{const handleFilePicked=payload=>{try{fileObjectCache.current.clear();const normalizedData=normalizeFilePickResult(payload);console.log("📁 File picked:",normalizedData);if(!normalizedData){throw new Error("No file data received from native file picker");}setDataAndComplete(normalizedData);updateProgressFromResult(updateProgress,normalizedData);}catch(error){console.error("📁 Error processing file data:",error);handleNativeError("Error processing selected file");}};const handleFilePickError=nativeError=>{console.error("📁 File pick error:",nativeError);handleNativeError(nativeError);};const handleFilePickCancelled=data=>{console.log("📁 File pick cancelled:",data);setLoading(false);resetProgress();};const handleFilePickStateUpdate=stateData=>{try{const parsedState=parseNativePayload(stateData);console.log("📁 File picker state:",parsedState?.state);const progressUpdate=mapStateToProgress(parsedState?.state);updateProgress(progressUpdate);if(parsedState?.state){setLoading(true);}}catch(parseError){console.error("📁 Error parsing state data:",parseError);}};return registerNativeHandlers([[_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICKED,handleFilePicked],[_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICK_ERROR,handleFilePickError],[_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICK_CANCELLED,handleFilePickCancelled],[_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICK_STATE_UPDATE,handleFilePickStateUpdate]]);},[handleNativeError,resetProgress,setDataAndComplete,setLoading,updateProgress]);const pickFile=(0,_react.useCallback)((input=null)=>{let payload;try{payload=resolvePickPayload(input);}catch(error){const message=error instanceof Error?error.message:"Invalid file picker options";handleNativeError(message);return;}if(typeof payload==="string"){console.log("📁 Picking file with MIME type:",payload);}else{console.log("📁 Picking file with options:",payload);}executeOperation(()=>{_NativeBridge.default.file.pick(payload);},"file pick");},[executeOperation,handleNativeError]);// Standardized execute function (new interface)
|
|
47
|
-
const execute=pickFile;const files=(0,_react.useMemo)(()=>extractFiles(data),[data]);/**
|
|
48
|
-
* Get File object from current file data
|
|
49
|
-
* Supports accessing specific file by index when multiple files are selected
|
|
50
|
-
* @param {number} index - Index of the file (default: 0)
|
|
51
|
-
* @returns {Promise<File>} JavaScript File object for FormData/POST API
|
|
52
|
-
* @throws {Error} If file data is unavailable or transport is unsupported
|
|
53
|
-
*/const getFileObject=(0,_react.useCallback)(async(index=0)=>{if(!files.length){throw new Error("No file data available. Please pick a file first.");}const targetIndex=Number(index);const fileEntry=files[targetIndex];if(!fileEntry){throw new Error(`No file data available at index ${targetIndex}.`);}if(fileObjectCache.current.has(targetIndex)){console.log("📁 Returning cached File object",{index:targetIndex});return fileObjectCache.current.get(targetIndex);}const{fileSrc,fileName,mimeType,transport}=fileEntry;// Check if transport supports File object creation
|
|
54
|
-
if(!(0,_FileObjectConverter.canCreateFileObject)(transport)){const errorMessage=(0,_FileObjectConverter.getUnsupportedTransportMessage)(transport);console.error("❌",errorMessage);throw new Error(errorMessage);}console.log("📁 Converting to File object",{index:targetIndex,transport,fileName,mimeType});try{let fileObject;if(transport==="BRIDGE_BASE64"){// Synchronous conversion for base64
|
|
55
|
-
fileObject=(0,_FileObjectConverter.base64ToFile)(fileSrc,fileName,mimeType);}else if(transport==="FRAMEWORK_SERVER"){// Asynchronous fetch and conversion for URL
|
|
56
|
-
fileObject=await(0,_FileObjectConverter.urlToFile)(fileSrc,fileName,mimeType);}// Cache the result per index
|
|
57
|
-
fileObjectCache.current.set(targetIndex,fileObject);console.log("✅ File object created successfully",{index:targetIndex,name:fileObject.name,size:fileObject.size,type:fileObject.type});return fileObject;}catch(error){console.error("❌ Failed to create File object:",error);throw new Error(`Failed to create File object: ${error.message}`);}},[files]);const getAllFileObjects=(0,_react.useCallback)(async()=>{if(!files.length){throw new Error("No file data available. Please pick a file first.");}const indices=files.map((_,index)=>index);return Promise.all(indices.map(index=>getFileObject(index)));},[files,getFileObject]);const canCreateFileObjectForCurrentFile=(0,_react.useMemo)(()=>files.length>0?(0,_FileObjectConverter.canCreateFileObject)(files[0].transport):false,[files]);const canCreateFileObjectsList=(0,_react.useMemo)(()=>files.map(file=>(0,_FileObjectConverter.canCreateFileObject)(file.transport)),[files]);const clear=(0,_react.useCallback)(()=>{fileObjectCache.current.clear();baseClear();},[baseClear]);return{// Standardized interface
|
|
58
|
-
data,loading,progress,error,isWeb,isNative,execute,clear,clearError,// File object conversion helpers
|
|
59
|
-
getFileObject,getAllFileObjects,canCreateFileObject:canCreateFileObjectForCurrentFile,canCreateFileObjects:canCreateFileObjectsList,// Legacy aliases for backward compatibility
|
|
60
|
-
selectedFile:files[0]||null,selectedFiles:files,pickFile,isLoading:loading,processingState:progress?.phase||null,clearFile:clear};};/**
|
|
61
|
-
* Promise-based camera permission request
|
|
62
|
-
* @returns {Promise<string>} Promise that resolves with permission status
|
|
63
|
-
*/exports.useFilePicker=useFilePicker;const requestCameraPermission=()=>{if(typeof window==="undefined"){return Promise.resolve(null);}if(!window.WebBridge){throw new Error("WebBridge is not initialized. Call WebBridge.init() first.");}return new Promise((resolve,reject)=>{try{if(!_NativeBridge.default.isAvailable()){reject(new Error("Native bridge not available"));return;}// Set up one-time listener
|
|
64
|
-
const handlePermissionStatus=data=>{window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.CAMERA_PERMISSION_STATUS);if(data===_NativeInterfaces.PERMISSION_STATUS.GRANTED){resolve(data);}else{reject(new Error(`Camera permission ${data.toLowerCase()}`));}};window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.CAMERA_PERMISSION_STATUS,handlePermissionStatus);_NativeBridge.default.camera.requestPermission();console.log("📷 Camera permission requested");}catch(error){reject(error);}});};/**
|
|
65
|
-
* React hook for camera permission status
|
|
66
|
-
* Automatically requests permission on mount
|
|
67
|
-
*/exports.requestCameraPermission=requestCameraPermission;const useCameraPermission=()=>{if(typeof window==="undefined"){return{permission:null,isLoading:false};}if(!window.WebBridge){throw new Error("WebBridge is not initialized. Call WebBridge.init() first.");}const[permission,setPermission]=(0,_react.useState)(null);const[isLoading,setIsLoading]=(0,_react.useState)(true);(0,_react.useEffect)(()=>{const requestPermission=async()=>{try{if(!_NativeBridge.default.isAvailable()){setPermission(_NativeInterfaces.PERMISSION_STATUS.NOT_DETERMINED);setIsLoading(false);return;}window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.CAMERA_PERMISSION_STATUS,data=>{setPermission(data);setIsLoading(false);console.log("📷 Camera permission status updated:",data);});_NativeBridge.default.camera.requestPermission();}catch(error){console.error("📷 Error requesting camera permission:",error);setPermission(_NativeInterfaces.PERMISSION_STATUS.DENIED);setIsLoading(false);}};requestPermission();return()=>{window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.CAMERA_PERMISSION_STATUS);};},[]);return{permission,isLoading};};/**
|
|
68
|
-
* Promise-based notification permission request
|
|
69
|
-
* @returns {Promise<string>} Promise that resolves with permission status
|
|
70
|
-
*/exports.useCameraPermission=useCameraPermission;const requestNotificationPermission=()=>{if(typeof window==="undefined"){return Promise.resolve(null);}if(!window.WebBridge){throw new Error("WebBridge is not initialized. Call WebBridge.init() first.");}return new Promise((resolve,reject)=>{try{if(!_NativeBridge.default.isAvailable()){reject(new Error("Native bridge not available"));return;}const handlePermissionStatus=data=>{// Unregister this one-time listener
|
|
71
|
-
unregisterPermissionStatusListener(handlePermissionStatus);if(data===_NativeInterfaces.PERMISSION_STATUS.GRANTED){resolve(data);}else{reject(new Error(`Notification permission ${data.toLowerCase()}`));}};registerPermissionStatusListener(handlePermissionStatus);_NativeBridge.default.notification.requestPermission();}catch(error){reject(error);}});};/**
|
|
72
|
-
* React hook for notification permission status
|
|
73
|
-
* Automatically requests permission on mount
|
|
74
|
-
*/exports.requestNotificationPermission=requestNotificationPermission;const useNotificationPermission=()=>{if(typeof window==="undefined"){return{permission:null,isLoading:false};}if(!window.WebBridge){throw new Error("WebBridge is not initialized. Call WebBridge.init() first.");}const[permission,setPermission]=(0,_react.useState)(null);const[isLoading,setIsLoading]=(0,_react.useState)(true);(0,_react.useEffect)(()=>{const requestPermission=async()=>{try{if(!_NativeBridge.default.isAvailable()){setPermission(_NativeInterfaces.PERMISSION_STATUS.NOT_DETERMINED);setIsLoading(false);return;}window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.NOTIFICATION_PERMISSION_STATUS,data=>{setPermission(data);setIsLoading(false);console.log("🔔 Notification permission status updated:",data);});_NativeBridge.default.notification.requestPermission();}catch(error){console.error("🔔 Error requesting notification permission:",error);setPermission(_NativeInterfaces.PERMISSION_STATUS.DENIED);setIsLoading(false);}};requestPermission();return()=>{window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.NOTIFICATION_PERMISSION_STATUS);};},[]);return{permission,isLoading};};/**
|
|
75
|
-
* Promise-based haptic feedback request
|
|
76
|
-
* @param {string} feedbackType - Type of haptic feedback (from HAPTIC_FEEDBACK_TYPES)
|
|
77
|
-
* @returns {Promise<string>} Promise that resolves with success status
|
|
78
|
-
*/exports.useNotificationPermission=useNotificationPermission;const requestHapticFeedback=(feedbackType="light")=>{if(typeof window==="undefined"){return Promise.resolve(null);}if(!window.WebBridge){throw new Error("WebBridge is not initialized. Call WebBridge.init() first.");}return new Promise((resolve,reject)=>{try{if(!_NativeBridge.default.isAvailable()){reject(new Error("Native bridge not available"));return;}// Set up one-time listener
|
|
79
|
-
const handleHapticResponse=data=>{window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.HAPTIC_FEEDBACK);if(data===_NativeInterfaces.RESPONSE_STATUS.SUCCESS){resolve(data);}else{reject(new Error(`Haptic feedback failed: ${data}`));}};window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.HAPTIC_FEEDBACK,handleHapticResponse);_NativeBridge.default.haptic.feedback(feedbackType);console.log("📳 Haptic feedback requested:",feedbackType);}catch(error){reject(error);}});};/**
|
|
80
|
-
* React hook for haptic feedback
|
|
81
|
-
* Provides a function to trigger haptic feedback
|
|
82
|
-
*/exports.requestHapticFeedback=requestHapticFeedback;const useHapticFeedback=()=>{const base=(0,_useBaseHook.useBaseHook)("useHapticFeedback");const[capabilities,setCapabilities]=(0,_react.useState)({isSupported:false,availableTypes:["light","medium","heavy"],platform:"unknown"});// Haptic types
|
|
83
|
-
const HAPTIC_TYPES={LIGHT:"light",MEDIUM:"medium",HEAVY:"heavy",SUCCESS:"success",WARNING:"warning",ERROR:"error",SELECTION:"selection",IMPACT:"impact"};(0,_react.useEffect)(()=>{// Initialize capabilities
|
|
84
|
-
const userAgent=typeof navigator!=="undefined"?navigator.userAgent.toLowerCase():"";let platform="unknown";let isSupported=false;let availableTypes=[];if(base.isNative){isSupported=true;if(userAgent.includes("android")){platform="android";availableTypes=[HAPTIC_TYPES.LIGHT,HAPTIC_TYPES.MEDIUM,HAPTIC_TYPES.HEAVY];}else if(userAgent.includes("iphone")||userAgent.includes("ipad")){platform="ios";availableTypes=Object.values(HAPTIC_TYPES);}}else{platform="web";isSupported=typeof navigator!=="undefined"&&"vibrate"in navigator;if(isSupported){availableTypes=[HAPTIC_TYPES.LIGHT,HAPTIC_TYPES.MEDIUM,HAPTIC_TYPES.HEAVY];}}setCapabilities({isSupported,availableTypes,platform});},[base.isNative]);// Main execute function for haptic superhook
|
|
85
|
-
const executeHaptic=(type="light",options={})=>{if(!capabilities.isSupported){const error=(0,_errors.createStandardError)(_errors.ERROR_CODES.FEATURE_UNSUPPORTED,"Haptic feedback not supported",null,"Device does not support haptic feedback");base.handleNativeError(error);return;}return handleHapticTrigger(type,options);};// Haptic trigger handler
|
|
86
|
-
const handleHapticTrigger=async(type,options={})=>{try{base.setLoading(true);base.updateProgress({state:"active",phase:"triggering",message:`Triggering ${type} haptic feedback...`});let success=false;if(base.isNative){// Use existing native implementation
|
|
87
|
-
success=await requestHapticFeedback(type);}else{// Web fallback
|
|
88
|
-
success=handleWebHaptic(type,options);}const hapticData={lastType:type,lastOptions:options,timestamp:new Date().toISOString(),success:success,capabilities:capabilities,lastOperation:"trigger",operationSuccess:success};base.setDataAndComplete(hapticData);return success;}catch(error){console.error("📳 Haptic feedback failed:",error);base.handleNativeError(error);return false;}};// Web haptic fallback
|
|
89
|
-
const handleWebHaptic=type=>{if(!navigator.vibrate){return false;}const vibrationPatterns={[HAPTIC_TYPES.LIGHT]:[50],[HAPTIC_TYPES.MEDIUM]:[100],[HAPTIC_TYPES.HEAVY]:[200],[HAPTIC_TYPES.SUCCESS]:[100,50,100],[HAPTIC_TYPES.WARNING]:[200,100,200],[HAPTIC_TYPES.ERROR]:[300,100,300,100,300],[HAPTIC_TYPES.SELECTION]:[25],[HAPTIC_TYPES.IMPACT]:[150]};const pattern=vibrationPatterns[type]||[100];navigator.vibrate(pattern);return true;};// Return standardized superhook interface
|
|
90
|
-
return{// Standard interface (from useBaseHook)
|
|
91
|
-
data:base.data,loading:base.loading,progress:base.progress,error:base.error,isWeb:base.isWeb,isNative:base.isNative,clear:base.clear,clearError:base.clearError,// Main execute function
|
|
92
|
-
execute:executeHaptic,// Semantic aliases for execute
|
|
93
|
-
triggerHaptic:executeHaptic,// Legacy compatibility
|
|
94
|
-
trigger:executeHaptic,light:()=>executeHaptic(HAPTIC_TYPES.LIGHT),medium:()=>executeHaptic(HAPTIC_TYPES.MEDIUM),heavy:()=>executeHaptic(HAPTIC_TYPES.HEAVY),success:()=>executeHaptic(HAPTIC_TYPES.SUCCESS),warning:()=>executeHaptic(HAPTIC_TYPES.WARNING),errorHaptic:()=>executeHaptic(HAPTIC_TYPES.ERROR),selection:()=>executeHaptic(HAPTIC_TYPES.SELECTION),impact:()=>executeHaptic(HAPTIC_TYPES.IMPACT),// Capability info
|
|
95
|
-
capabilities:capabilities,isSupported:capabilities.isSupported,isAvailable:capabilities.isSupported,// Legacy compatibility
|
|
96
|
-
availableTypes:capabilities.availableTypes,// Haptic types constant
|
|
97
|
-
HAPTIC_TYPES};};/**
|
|
98
|
-
* React hook for notification functionality
|
|
99
|
-
* Handles local notifications, push notifications, and badge management
|
|
100
|
-
*/exports.useHapticFeedback=useHapticFeedback;const useNotification=()=>{const base=(0,_useBaseHook.useBaseHook)("useNotification");const[permissionStatus,setPermissionStatus]=(0,_react.useState)(null);const[pushToken,setPushToken]=(0,_react.useState)(null);const[badges,setBadges]=(0,_react.useState)(0);const[lastNotification,setLastNotification]=(0,_react.useState)(null);const[subscribedTopics,setSubscribedTopics]=(0,_react.useState)([]);// Server-side rendering safety
|
|
101
|
-
if(typeof window==="undefined"){return{data:null,loading:false,error:null,execute:()=>{},scheduleLocal:()=>{},cancelLocal:()=>{},registerForPush:()=>{},updateBadge:()=>{},subscribeToTopic:()=>{},unsubscribeFromTopic:()=>{},getSubscribedTopics:()=>{},permissionStatus:null,pushToken:null,badges:0,subscribedTopics:[]};}if(!window.WebBridge){throw new Error("WebBridge is not initialized. Call WebBridge.init() first.");}(0,_react.useEffect)(()=>{const unregister=registerPermissionStatusListener(data=>{setPermissionStatus(data);});// Check initial permission status without requesting (internal call)
|
|
102
|
-
if(_NativeBridge.default.isAvailable()){try{// Call native bridge directly without going through public API
|
|
103
|
-
if(_NativeBridge.default.isAndroid&&window.NativeBridge?.checkNotificationPermissionStatus){window.NativeBridge.checkNotificationPermissionStatus(null);}else if(_NativeBridge.default.isIOS&&window.webkit?.messageHandlers?.NativeBridge){window.webkit.messageHandlers.NativeBridge.postMessage({command:"checkNotificationPermissionStatus",data:null});}}catch(error){console.error("🔔 Error checking notification permission status:",error);}}window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.LOCAL_NOTIFICATION_SCHEDULED,data=>{try{const result=typeof data==="string"?JSON.parse(data):data;if(result?.success===false||result?.error){base.handleNativeError(result.error||result);return;}base.setDataAndComplete(result);}catch(error){base.handleNativeError(error);}});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.LOCAL_NOTIFICATION_CANCELLED,data=>{try{const result=typeof data==="string"?JSON.parse(data):data;if(result?.success===false||result?.error){base.handleNativeError(result.error||result);return;}base.setDataAndComplete(result);}catch(error){base.handleNativeError(error);}});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.PUSH_NOTIFICATION_TOKEN,data=>{try{const result=typeof data==="string"?JSON.parse(data):data;if(result?.error){base.handleNativeError(result.error);return;}setPushToken(result.token);base.setDataAndComplete(result);}catch(error){base.handleNativeError(error);}});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.NOTIFICATION_RECEIVED,data=>{const notification=typeof data==="string"?JSON.parse(data):data;setLastNotification(notification);});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.NOTIFICATION_TAPPED,data=>{try{const result=typeof data==="string"?JSON.parse(data):data;base.setDataAndComplete(result);}catch(error){base.handleNativeError(error);}});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.NOTIFICATION_ACTION_PERFORMED,data=>{const action=typeof data==="string"?JSON.parse(data):data;base.setDataAndComplete(action);});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.TOPIC_SUBSCRIPTION_RESULT,data=>{try{const result=typeof data==="string"?JSON.parse(data):data;if(result?.error){base.handleNativeError(result.error);return;}const success=result?.success;if(success===false){base.handleNativeError(result);return;}base.setDataAndComplete(result);}catch(error){base.handleNativeError(error);}});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.SUBSCRIBED_TOPICS_RESULT,data=>{try{const result=typeof data==="string"?JSON.parse(data):data;if(result?.error){base.handleNativeError(result.error);return;}if(result?.success===false){base.handleNativeError(result);return;}setSubscribedTopics(result.topics||[]);base.setDataAndComplete(result);}catch(error){base.handleNativeError(error);}});return()=>{// Cleanup - unregister from shared callback system
|
|
104
|
-
unregister();// Cleanup other callbacks
|
|
105
|
-
window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.LOCAL_NOTIFICATION_SCHEDULED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.LOCAL_NOTIFICATION_CANCELLED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.PUSH_NOTIFICATION_TOKEN);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.NOTIFICATION_RECEIVED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.NOTIFICATION_TAPPED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.NOTIFICATION_ACTION_PERFORMED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.TOPIC_SUBSCRIPTION_RESULT);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.SUBSCRIBED_TOPICS_RESULT);};},[]);// Local notification scheduling with all supported styles
|
|
106
|
-
const scheduleLocal=config=>{base.executeOperation(()=>{_NativeBridge.default.notification.scheduleLocal(config);},"schedule local notification");};const cancelLocal=notificationId=>{base.executeOperation(()=>{_NativeBridge.default.notification.cancelLocal(notificationId);},"cancel notification");};const registerForPush=()=>{base.executeOperation(()=>{_NativeBridge.default.notification.registerForPush();},"register for push");};const updateBadge=count=>{base.callNative(()=>_NativeBridge.default.notification.updateBadge(count));setBadges(count);};const subscribeToTopic=topic=>{base.executeOperation(()=>{_NativeBridge.default.notification.subscribeToTopic(topic);},"subscribe to topic");};const unsubscribeFromTopic=topic=>{base.executeOperation(()=>{_NativeBridge.default.notification.unsubscribeFromTopic(topic);},"unsubscribe from topic");};const getSubscribedTopics=()=>{base.executeOperation(()=>{_NativeBridge.default.notification.getSubscribedTopics();},"get subscribed topics");};return{// Standardized interface
|
|
107
|
-
data:base.data,loading:base.loading,progress:base.progress,error:base.error,execute:scheduleLocal,clear:base.clear,clearError:base.clearError,// Notification-specific
|
|
108
|
-
permissionStatus,pushToken,badges,lastNotification,subscribedTopics,scheduleLocal,cancelLocal,registerForPush,updateBadge,subscribeToTopic,unsubscribeFromTopic,getSubscribedTopics};};/**
|
|
109
|
-
* Network status hook
|
|
110
|
-
* Listens to native network changes and exposes online/offline state
|
|
111
|
-
*/exports.useNotification=useNotification;const useNetworkStatus=()=>{const initialOnline=typeof navigator!=="undefined"?navigator.onLine:true;const[status,setStatus]=(0,_react.useState)({online:initialOnline,type:null});const[error,setError]=(0,_react.useState)(null);const isNative=_NativeBridge.default.isAvailable();(0,_react.useEffect)(()=>{if(typeof window==="undefined")return;if(!window.WebBridge)return;if(!isNative)return;const handleStatus=payload=>{try{const parsed=parseNativePayload(payload)||{};setStatus({online:Boolean(parsed.online),type:parsed.type||null});setError(null);}catch(e){console.error("🌐 Error parsing network status:",e);setError(e.message);}};window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.NETWORK_STATUS_CHANGED,handleStatus);try{_NativeBridge.default.network.getStatus();}catch(e){setError(e.message||"Network status unavailable");}return()=>{window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.NETWORK_STATUS_CHANGED);};},[isNative]);return{...status,error};};/**
|
|
112
|
-
* React hook for data protection primitives.
|
|
113
|
-
*
|
|
114
|
-
* Exposes screen-capture prevention and web data clearing as
|
|
115
|
-
* composable building blocks — the web layer decides when to call them
|
|
116
|
-
* (e.g. on sensitive screen mount/unmount, on logout).
|
|
117
|
-
*
|
|
118
|
-
* Extensible: add future security bridge commands here.
|
|
119
|
-
*
|
|
120
|
-
* @returns {{
|
|
121
|
-
* screenSecure: boolean,
|
|
122
|
-
* setScreenSecure: (enable: boolean) => void,
|
|
123
|
-
* clearWebData: () => void,
|
|
124
|
-
* data: Object|null,
|
|
125
|
-
* loading: boolean,
|
|
126
|
-
* error: Object|null,
|
|
127
|
-
* isWeb: boolean,
|
|
128
|
-
* isNative: boolean,
|
|
129
|
-
* clear: () => void,
|
|
130
|
-
* clearError: () => void,
|
|
131
|
-
* }}
|
|
132
|
-
*/exports.useNetworkStatus=useNetworkStatus;const useDataProtection=()=>{const base=(0,_useBaseHook.useBaseHook)("useDataProtection");const[screenSecure,setScreenSecureState]=(0,_react.useState)(false);// Server-side rendering safety
|
|
133
|
-
if(typeof window==="undefined"){return{screenSecure:false,setScreenSecure:()=>{},clearWebData:()=>{},data:null,loading:false,progress:null,error:null,isWeb:true,isNative:false,clear:()=>{},clearError:()=>{}};}if(!window.WebBridge){throw new Error("WebBridge is not initialized. Call WebBridge.init() first.");}(0,_react.useEffect)(()=>{const handleScreenSecureSet=data=>{try{const result=typeof data==="string"?JSON.parse(data):data;if(result?.error){base.handleNativeError(result.error);return;}setScreenSecureState(Boolean(result?.secure));base.setDataAndComplete(result);}catch(error){base.handleNativeError(error);}};const handleScreenSecureError=data=>{try{const result=typeof data==="string"?JSON.parse(data):data;base.handleNativeError(result?.error||"setScreenSecure failed");}catch(error){base.handleNativeError(error);}};const handleWebDataCleared=data=>{try{const result=typeof data==="string"?JSON.parse(data):data;if(result?.error){base.handleNativeError(result.error);return;}base.setDataAndComplete(result);}catch(error){base.handleNativeError(error);}};const handleWebDataClearError=data=>{try{const result=typeof data==="string"?JSON.parse(data):data;base.handleNativeError(result?.error||"clearWebData failed");}catch(error){base.handleNativeError(error);}};const handleScreenSecureStatus=data=>{try{const result=typeof data==="string"?JSON.parse(data):data;if(result?.error){base.handleNativeError(result.error);return;}setScreenSecureState(Boolean(result?.secure));}catch(error){base.handleNativeError(error);}};const cleanup=registerNativeHandlers([[_NativeInterfaces.NATIVE_CALLBACKS.ON_SCREEN_SECURE_SET,handleScreenSecureSet],[_NativeInterfaces.NATIVE_CALLBACKS.ON_SCREEN_SECURE_STATUS,handleScreenSecureStatus],[_NativeInterfaces.NATIVE_CALLBACKS.ON_SCREEN_SECURE_ERROR,handleScreenSecureError],[_NativeInterfaces.NATIVE_CALLBACKS.ON_WEB_DATA_CLEARED,handleWebDataCleared],[_NativeInterfaces.NATIVE_CALLBACKS.ON_WEB_DATA_CLEAR_ERROR,handleWebDataClearError]]);// Initialize screenSecure state from native on mount (native env only)
|
|
134
|
-
base.callNative(()=>_NativeBridge.default.security.getScreenSecure());return cleanup;},[base.setDataAndComplete,base.handleNativeError]);const setScreenSecure=(0,_react.useCallback)(enable=>{base.callNative(()=>_NativeBridge.default.security.setScreenSecure(enable));},[base.callNative]);const clearWebData=(0,_react.useCallback)(()=>{base.callNative(()=>_NativeBridge.default.security.clearWebData());},[base.callNative]);return{// Standardized base interface
|
|
135
|
-
data:base.data,loading:base.loading,progress:base.progress,error:base.error,isWeb:base.isWeb,isNative:base.isNative,clear:base.clear,clearError:base.clearError,// Security state
|
|
136
|
-
screenSecure,// Security actions
|
|
137
|
-
setScreenSecure,clearWebData};};exports.useDataProtection=useDataProtection;const useSafeArea=()=>{const fromGlobal=(0,_safeArea.getSafeAreaFromGlobal)();const initialValue=fromGlobal||{..._safeArea.DEFAULT_INSETS};const[insets,setInsets]=(0,_react.useState)(()=>initialValue);(0,_react.useEffect)(()=>{if(typeof window==="undefined")return;if(!window.WebBridge)return;if(!_NativeBridge.default.isAvailable())return;const handleInsetsUpdate=data=>{try{const parsed=typeof data==="string"?JSON.parse(data):data;const normalized={top:Number(parsed.top)||0,right:Number(parsed.right)||0,bottom:Number(parsed.bottom)||0,left:Number(parsed.left)||0};setInsets(normalized);(0,_safeArea.setSafeAreaGlobal)(normalized);}catch(error){console.error("Error parsing safe area insets:",error);setInsets({..._safeArea.DEFAULT_INSETS});}};window.WebBridge.register("ON_SAFE_AREA_INSETS_UPDATED",handleInsetsUpdate);_NativeBridge.default.safeArea.get();return()=>{window.WebBridge.unregister("ON_SAFE_AREA_INSETS_UPDATED");};},[]);return insets;};exports.useSafeArea=useSafeArea;
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});var _index=require("./hooks/index.js");Object.keys(_index).forEach(function(key){if(key==="default"||key==="__esModule")return;if(key in exports&&exports[key]===_index[key])return;Object.defineProperty(exports,key,{enumerable:true,get:function(){return _index[key];}});});
|