@rn-bridge-tools/expo 0.0.7 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/WebViewBridge.d.ts +22 -0
- package/dist/WebViewBridge.d.ts.map +1 -0
- package/dist/__tests__/createHandlers.test.d.ts +2 -0
- package/dist/__tests__/createHandlers.test.d.ts.map +1 -0
- package/dist/createHandlers.d.ts +5 -0
- package/dist/createHandlers.d.ts.map +1 -0
- package/dist/handlers/auth.d.ts +6 -0
- package/dist/handlers/auth.d.ts.map +1 -0
- package/dist/handlers/browser.d.ts +6 -0
- package/dist/handlers/browser.d.ts.map +1 -0
- package/dist/handlers/camera.d.ts +6 -0
- package/dist/handlers/camera.d.ts.map +1 -0
- package/dist/handlers/clipboard.d.ts +6 -0
- package/dist/handlers/clipboard.d.ts.map +1 -0
- package/dist/handlers/device.d.ts +7 -0
- package/dist/handlers/device.d.ts.map +1 -0
- package/dist/handlers/file.d.ts +8 -0
- package/dist/handlers/file.d.ts.map +1 -0
- package/dist/handlers/haptic.d.ts +7 -0
- package/dist/handlers/haptic.d.ts.map +1 -0
- package/dist/handlers/iap.d.ts +7 -0
- package/dist/handlers/iap.d.ts.map +1 -0
- package/dist/handlers/keyboard.d.ts +6 -0
- package/dist/handlers/keyboard.d.ts.map +1 -0
- package/dist/handlers/location.d.ts +7 -0
- package/dist/handlers/location.d.ts.map +1 -0
- package/dist/handlers/navigation.d.ts +8 -0
- package/dist/handlers/navigation.d.ts.map +1 -0
- package/dist/handlers/permission.d.ts +7 -0
- package/dist/handlers/permission.d.ts.map +1 -0
- package/dist/handlers/preference.d.ts +8 -0
- package/dist/handlers/preference.d.ts.map +1 -0
- package/dist/handlers/push.d.ts +6 -0
- package/dist/handlers/push.d.ts.map +1 -0
- package/dist/handlers/scanner.d.ts +5 -0
- package/dist/handlers/scanner.d.ts.map +1 -0
- package/dist/handlers/share.d.ts +5 -0
- package/dist/handlers/share.d.ts.map +1 -0
- package/dist/handlers/statusbar.d.ts +7 -0
- package/dist/handlers/statusbar.d.ts.map +1 -0
- package/dist/index.d.ts +22 -124
- package/dist/index.d.ts.map +1 -0
- package/package.json +7 -6
- package/src/WebViewBridge.tsx +58 -0
- package/src/__tests__/createHandlers.test.ts +88 -0
- package/src/createHandlers.ts +67 -0
- package/src/handlers/auth.ts +69 -0
- package/src/handlers/browser.ts +42 -0
- package/src/handlers/camera.ts +97 -0
- package/src/handlers/clipboard.ts +32 -0
- package/src/handlers/device.ts +71 -0
- package/src/handlers/file.ts +126 -0
- package/src/handlers/haptic.ts +56 -0
- package/src/handlers/iap.ts +23 -0
- package/src/handlers/keyboard.ts +20 -0
- package/src/handlers/location.ts +104 -0
- package/src/handlers/navigation.ts +29 -0
- package/src/handlers/permission.ts +107 -0
- package/src/handlers/preference.ts +58 -0
- package/src/handlers/push.ts +47 -0
- package/src/handlers/scanner.ts +31 -0
- package/src/handlers/share.ts +31 -0
- package/src/handlers/statusbar.ts +35 -0
- package/src/index.ts +23 -0
- package/dist/index.js +0 -846
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { KeyboardNamespace } from '@rn-bridge-tools/core';
|
|
2
|
+
import { Keyboard } from 'react-native';
|
|
3
|
+
|
|
4
|
+
export const keyboardHandlers = {
|
|
5
|
+
'keyboard.dismiss': async (
|
|
6
|
+
_payload: KeyboardNamespace['keyboard.dismiss']['request'],
|
|
7
|
+
): Promise<KeyboardNamespace['keyboard.dismiss']['response']> => {
|
|
8
|
+
Keyboard.dismiss();
|
|
9
|
+
return { success: true };
|
|
10
|
+
},
|
|
11
|
+
|
|
12
|
+
'keyboard.getState': async (
|
|
13
|
+
_payload: KeyboardNamespace['keyboard.getState']['request'],
|
|
14
|
+
): Promise<KeyboardNamespace['keyboard.getState']['response']> => {
|
|
15
|
+
return {
|
|
16
|
+
visible: false,
|
|
17
|
+
height: 0,
|
|
18
|
+
};
|
|
19
|
+
},
|
|
20
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { LocationNamespace } from '@rn-bridge-tools/core';
|
|
2
|
+
|
|
3
|
+
declare const require: (id: string) => unknown;
|
|
4
|
+
|
|
5
|
+
interface ExpoLocationModule {
|
|
6
|
+
Accuracy: {
|
|
7
|
+
Lowest: number;
|
|
8
|
+
Low: number;
|
|
9
|
+
Balanced: number;
|
|
10
|
+
High: number;
|
|
11
|
+
Highest: number;
|
|
12
|
+
};
|
|
13
|
+
getCurrentPositionAsync: (opts: {
|
|
14
|
+
accuracy?: number;
|
|
15
|
+
}) => Promise<{
|
|
16
|
+
coords: {
|
|
17
|
+
latitude: number;
|
|
18
|
+
longitude: number;
|
|
19
|
+
altitude: number | null;
|
|
20
|
+
accuracy: number | null;
|
|
21
|
+
};
|
|
22
|
+
timestamp: number;
|
|
23
|
+
}>;
|
|
24
|
+
watchPositionAsync: (
|
|
25
|
+
opts: { accuracy?: number; distanceInterval?: number; timeInterval?: number },
|
|
26
|
+
callback: (location: unknown) => void,
|
|
27
|
+
) => Promise<{ remove: () => void }>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const watchSubscriptions = new Map<string, { remove: () => void }>();
|
|
31
|
+
let watchCounter = 0;
|
|
32
|
+
|
|
33
|
+
export const locationHandlers = {
|
|
34
|
+
'location.getCurrent': async (
|
|
35
|
+
payload: LocationNamespace['location.getCurrent']['request'],
|
|
36
|
+
): Promise<LocationNamespace['location.getCurrent']['response']> => {
|
|
37
|
+
let Location: ExpoLocationModule | null = null;
|
|
38
|
+
try { Location = require('expo-location') as ExpoLocationModule; } catch {}
|
|
39
|
+
if (!Location) return { success: false, error: 'MODULE_NOT_INSTALLED: expo-location' } as never;
|
|
40
|
+
|
|
41
|
+
const accuracyMap: Record<string, number> = {
|
|
42
|
+
lowest: Location.Accuracy.Lowest,
|
|
43
|
+
low: Location.Accuracy.Low,
|
|
44
|
+
balanced: Location.Accuracy.Balanced,
|
|
45
|
+
high: Location.Accuracy.High,
|
|
46
|
+
highest: Location.Accuracy.Highest,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const result = await Location.getCurrentPositionAsync({
|
|
50
|
+
accuracy: accuracyMap[payload.accuracy ?? 'balanced'] ?? Location.Accuracy.Balanced,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
lat: result.coords.latitude,
|
|
55
|
+
lng: result.coords.longitude,
|
|
56
|
+
altitude: result.coords.altitude ?? undefined,
|
|
57
|
+
accuracy: result.coords.accuracy ?? undefined,
|
|
58
|
+
timestamp: result.timestamp,
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
'location.watchStart': async (
|
|
63
|
+
payload: LocationNamespace['location.watchStart']['request'],
|
|
64
|
+
): Promise<LocationNamespace['location.watchStart']['response']> => {
|
|
65
|
+
let Location: ExpoLocationModule | null = null;
|
|
66
|
+
try { Location = require('expo-location') as ExpoLocationModule; } catch {}
|
|
67
|
+
if (!Location) return { success: false, error: 'MODULE_NOT_INSTALLED: expo-location' } as never;
|
|
68
|
+
|
|
69
|
+
watchCounter += 1;
|
|
70
|
+
const watchId = `watch_${watchCounter}`;
|
|
71
|
+
|
|
72
|
+
const accuracyMap: Record<string, number> = {
|
|
73
|
+
low: Location.Accuracy.Low,
|
|
74
|
+
balanced: Location.Accuracy.Balanced,
|
|
75
|
+
high: Location.Accuracy.High,
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const subscription = await Location.watchPositionAsync(
|
|
79
|
+
{
|
|
80
|
+
accuracy: accuracyMap[payload.accuracy ?? 'balanced'] ?? Location.Accuracy.Balanced,
|
|
81
|
+
distanceInterval: payload.distanceInterval,
|
|
82
|
+
timeInterval: payload.timeInterval,
|
|
83
|
+
},
|
|
84
|
+
(_location) => {
|
|
85
|
+
// Event push is handled by the WebViewBridge component via emit
|
|
86
|
+
},
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
watchSubscriptions.set(watchId, subscription);
|
|
90
|
+
return { watchId };
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
'location.watchStop': async (
|
|
94
|
+
payload: LocationNamespace['location.watchStop']['request'],
|
|
95
|
+
): Promise<LocationNamespace['location.watchStop']['response']> => {
|
|
96
|
+
const subscription = watchSubscriptions.get(payload.watchId);
|
|
97
|
+
if (subscription) {
|
|
98
|
+
subscription.remove();
|
|
99
|
+
watchSubscriptions.delete(payload.watchId);
|
|
100
|
+
return { success: true };
|
|
101
|
+
}
|
|
102
|
+
return { success: false };
|
|
103
|
+
},
|
|
104
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { NavigationNamespace } from '@rn-bridge-tools/core';
|
|
2
|
+
|
|
3
|
+
export const navigationHandlers = {
|
|
4
|
+
'navigation.goBack': async (
|
|
5
|
+
_payload: NavigationNamespace['navigation.goBack']['request'],
|
|
6
|
+
): Promise<NavigationNamespace['navigation.goBack']['response']> => {
|
|
7
|
+
// WebView goBack is handled by the WebViewBridge component
|
|
8
|
+
return { success: true };
|
|
9
|
+
},
|
|
10
|
+
|
|
11
|
+
'navigation.push': async (
|
|
12
|
+
_payload: NavigationNamespace['navigation.push']['request'],
|
|
13
|
+
): Promise<NavigationNamespace['navigation.push']['response']> => {
|
|
14
|
+
// Navigation push requires access to the navigation context
|
|
15
|
+
return { success: true };
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
'navigation.close': async (
|
|
19
|
+
_payload: NavigationNamespace['navigation.close']['request'],
|
|
20
|
+
): Promise<NavigationNamespace['navigation.close']['response']> => {
|
|
21
|
+
return { success: true };
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
'navigation.setSwipeBack': async (
|
|
25
|
+
_payload: NavigationNamespace['navigation.setSwipeBack']['request'],
|
|
26
|
+
): Promise<NavigationNamespace['navigation.setSwipeBack']['response']> => {
|
|
27
|
+
return { success: true };
|
|
28
|
+
},
|
|
29
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type { PermissionNamespace, PermissionType } from '@rn-bridge-tools/core';
|
|
2
|
+
import { Linking } from 'react-native';
|
|
3
|
+
|
|
4
|
+
declare const require: (id: string) => unknown;
|
|
5
|
+
|
|
6
|
+
interface PermissionCameraModule {
|
|
7
|
+
Camera: {
|
|
8
|
+
getCameraPermissionsAsync: () => Promise<{ status: string; canAskAgain?: boolean }>;
|
|
9
|
+
requestCameraPermissionsAsync: () => Promise<{ status: string; canAskAgain?: boolean }>;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface PermissionLocationModule {
|
|
14
|
+
getForegroundPermissionsAsync: () => Promise<{ status: string; canAskAgain?: boolean }>;
|
|
15
|
+
requestForegroundPermissionsAsync: () => Promise<{ status: string; canAskAgain?: boolean }>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface PermissionNotificationsModule {
|
|
19
|
+
getPermissionsAsync: () => Promise<{ status: string; canAskAgain?: boolean }>;
|
|
20
|
+
requestPermissionsAsync: () => Promise<{ status: string; canAskAgain?: boolean }>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function getPermissionModule(permission: PermissionType) {
|
|
24
|
+
switch (permission) {
|
|
25
|
+
case 'camera': {
|
|
26
|
+
let mod: PermissionCameraModule | null = null;
|
|
27
|
+
try { mod = require('expo-camera') as PermissionCameraModule; } catch {}
|
|
28
|
+
return mod
|
|
29
|
+
? {
|
|
30
|
+
check: () => mod.Camera.getCameraPermissionsAsync(),
|
|
31
|
+
request: () => mod.Camera.requestCameraPermissionsAsync(),
|
|
32
|
+
}
|
|
33
|
+
: null;
|
|
34
|
+
}
|
|
35
|
+
case 'location': {
|
|
36
|
+
let mod: PermissionLocationModule | null = null;
|
|
37
|
+
try { mod = require('expo-location') as PermissionLocationModule; } catch {}
|
|
38
|
+
return mod
|
|
39
|
+
? {
|
|
40
|
+
check: () => mod.getForegroundPermissionsAsync(),
|
|
41
|
+
request: () => mod.requestForegroundPermissionsAsync(),
|
|
42
|
+
}
|
|
43
|
+
: null;
|
|
44
|
+
}
|
|
45
|
+
case 'notifications': {
|
|
46
|
+
let mod: PermissionNotificationsModule | null = null;
|
|
47
|
+
try { mod = require('expo-notifications') as PermissionNotificationsModule; } catch {}
|
|
48
|
+
return mod
|
|
49
|
+
? {
|
|
50
|
+
check: () => mod.getPermissionsAsync(),
|
|
51
|
+
request: () => mod.requestPermissionsAsync(),
|
|
52
|
+
}
|
|
53
|
+
: null;
|
|
54
|
+
}
|
|
55
|
+
default:
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const permissionHandlers = {
|
|
61
|
+
'permission.check': async (
|
|
62
|
+
payload: PermissionNamespace['permission.check']['request'],
|
|
63
|
+
): Promise<PermissionNamespace['permission.check']['response']> => {
|
|
64
|
+
const mod = getPermissionModule(payload.permission);
|
|
65
|
+
if (!mod) {
|
|
66
|
+
return { status: 'undetermined', canAskAgain: true };
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
const result = await mod.check();
|
|
70
|
+
return {
|
|
71
|
+
status: result.status as 'granted' | 'denied' | 'undetermined',
|
|
72
|
+
canAskAgain: result.canAskAgain ?? true,
|
|
73
|
+
};
|
|
74
|
+
} catch {
|
|
75
|
+
return { status: 'undetermined', canAskAgain: true };
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
'permission.request': async (
|
|
80
|
+
payload: PermissionNamespace['permission.request']['request'],
|
|
81
|
+
): Promise<PermissionNamespace['permission.request']['response']> => {
|
|
82
|
+
const mod = getPermissionModule(payload.permission);
|
|
83
|
+
if (!mod) {
|
|
84
|
+
return { status: 'undetermined', canAskAgain: true };
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
const result = await mod.request();
|
|
88
|
+
return {
|
|
89
|
+
status: result.status as 'granted' | 'denied' | 'undetermined',
|
|
90
|
+
canAskAgain: result.canAskAgain ?? true,
|
|
91
|
+
};
|
|
92
|
+
} catch {
|
|
93
|
+
return { status: 'undetermined', canAskAgain: true };
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
'permission.openSettings': async (
|
|
98
|
+
_payload: PermissionNamespace['permission.openSettings']['request'],
|
|
99
|
+
): Promise<PermissionNamespace['permission.openSettings']['response']> => {
|
|
100
|
+
try {
|
|
101
|
+
await Linking.openSettings();
|
|
102
|
+
return { success: true };
|
|
103
|
+
} catch {
|
|
104
|
+
return { success: false };
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { PreferenceNamespace } from '@rn-bridge-tools/core';
|
|
2
|
+
|
|
3
|
+
declare const require: (id: string) => unknown;
|
|
4
|
+
|
|
5
|
+
type AsyncStorage = {
|
|
6
|
+
default: {
|
|
7
|
+
getItem: (key: string) => Promise<string | null>;
|
|
8
|
+
setItem: (key: string, value: string) => Promise<void>;
|
|
9
|
+
removeItem: (key: string) => Promise<void>;
|
|
10
|
+
clear: () => Promise<void>;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const preferenceHandlers = {
|
|
15
|
+
'preference.get': async (
|
|
16
|
+
payload: PreferenceNamespace['preference.get']['request'],
|
|
17
|
+
): Promise<PreferenceNamespace['preference.get']['response']> => {
|
|
18
|
+
let mod: AsyncStorage | null = null;
|
|
19
|
+
try { mod = require('@react-native-async-storage/async-storage') as AsyncStorage; } catch {}
|
|
20
|
+
if (!mod) return { value: null };
|
|
21
|
+
|
|
22
|
+
const value = await mod.default.getItem(payload.key);
|
|
23
|
+
return { value };
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
'preference.set': async (
|
|
27
|
+
payload: PreferenceNamespace['preference.set']['request'],
|
|
28
|
+
): Promise<PreferenceNamespace['preference.set']['response']> => {
|
|
29
|
+
let mod: AsyncStorage | null = null;
|
|
30
|
+
try { mod = require('@react-native-async-storage/async-storage') as AsyncStorage; } catch {}
|
|
31
|
+
if (!mod) return { success: false };
|
|
32
|
+
|
|
33
|
+
await mod.default.setItem(payload.key, payload.value);
|
|
34
|
+
return { success: true };
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
'preference.remove': async (
|
|
38
|
+
payload: PreferenceNamespace['preference.remove']['request'],
|
|
39
|
+
): Promise<PreferenceNamespace['preference.remove']['response']> => {
|
|
40
|
+
let mod: AsyncStorage | null = null;
|
|
41
|
+
try { mod = require('@react-native-async-storage/async-storage') as AsyncStorage; } catch {}
|
|
42
|
+
if (!mod) return { success: false };
|
|
43
|
+
|
|
44
|
+
await mod.default.removeItem(payload.key);
|
|
45
|
+
return { success: true };
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
'preference.clear': async (
|
|
49
|
+
_payload: PreferenceNamespace['preference.clear']['request'],
|
|
50
|
+
): Promise<PreferenceNamespace['preference.clear']['response']> => {
|
|
51
|
+
let mod: AsyncStorage | null = null;
|
|
52
|
+
try { mod = require('@react-native-async-storage/async-storage') as AsyncStorage; } catch {}
|
|
53
|
+
if (!mod) return { success: false };
|
|
54
|
+
|
|
55
|
+
await mod.default.clear();
|
|
56
|
+
return { success: true };
|
|
57
|
+
},
|
|
58
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { PushNamespace } from '@rn-bridge-tools/core';
|
|
2
|
+
import { Platform } from 'react-native';
|
|
3
|
+
|
|
4
|
+
declare const require: (id: string) => unknown;
|
|
5
|
+
|
|
6
|
+
interface NotificationsModule {
|
|
7
|
+
getExpoPushTokenAsync: () => Promise<{ data: string }>;
|
|
8
|
+
requestPermissionsAsync: () => Promise<{ status: string }>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const pushHandlers = {
|
|
12
|
+
'push.getToken': async (
|
|
13
|
+
_payload: PushNamespace['push.getToken']['request'],
|
|
14
|
+
): Promise<PushNamespace['push.getToken']['response']> => {
|
|
15
|
+
let Notifications: NotificationsModule | null = null;
|
|
16
|
+
try { Notifications = require('expo-notifications') as NotificationsModule; } catch {}
|
|
17
|
+
if (!Notifications) return { success: false, error: 'MODULE_NOT_INSTALLED: expo-notifications' } as never;
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const token = await Notifications.getExpoPushTokenAsync();
|
|
21
|
+
return {
|
|
22
|
+
token: token.data,
|
|
23
|
+
platform: Platform.OS === 'ios' ? 'apns' : 'fcm',
|
|
24
|
+
};
|
|
25
|
+
} catch {
|
|
26
|
+
return { token: '', platform: Platform.OS === 'ios' ? 'apns' : 'fcm' };
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
'push.requestPermission': async (
|
|
31
|
+
_payload: PushNamespace['push.requestPermission']['request'],
|
|
32
|
+
): Promise<PushNamespace['push.requestPermission']['response']> => {
|
|
33
|
+
let Notifications: NotificationsModule | null = null;
|
|
34
|
+
try { Notifications = require('expo-notifications') as NotificationsModule; } catch {}
|
|
35
|
+
if (!Notifications) return { success: false, error: 'MODULE_NOT_INSTALLED: expo-notifications' } as never;
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const { status } = await Notifications.requestPermissionsAsync();
|
|
39
|
+
return {
|
|
40
|
+
granted: status === 'granted',
|
|
41
|
+
status: status as 'granted' | 'denied' | 'undetermined',
|
|
42
|
+
};
|
|
43
|
+
} catch {
|
|
44
|
+
return { granted: false, status: 'denied' };
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ScannerNamespace } from '@rn-bridge-tools/core';
|
|
2
|
+
|
|
3
|
+
declare const require: (id: string) => unknown;
|
|
4
|
+
|
|
5
|
+
interface CameraModule {
|
|
6
|
+
Camera: {
|
|
7
|
+
requestCameraPermissionsAsync: () => Promise<{ status: string }>;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const scannerHandlers = {
|
|
12
|
+
'scanner.scanQR': async (
|
|
13
|
+
_payload: ScannerNamespace['scanner.scanQR']['request'],
|
|
14
|
+
): Promise<ScannerNamespace['scanner.scanQR']['response']> => {
|
|
15
|
+
let Camera: CameraModule | null = null;
|
|
16
|
+
try { Camera = require('expo-camera') as CameraModule; } catch {}
|
|
17
|
+
if (!Camera) return { success: false, error: 'MODULE_NOT_INSTALLED: expo-camera' } as never;
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const { status } = await Camera.Camera.requestCameraPermissionsAsync();
|
|
21
|
+
if (status !== 'granted') {
|
|
22
|
+
return { success: false, cancelled: true };
|
|
23
|
+
}
|
|
24
|
+
// QR scanning is typically handled via a component, not a direct call.
|
|
25
|
+
// Return a placeholder indicating the scanner should be opened.
|
|
26
|
+
return { success: false, error: 'Use Camera component for QR scanning' } as never;
|
|
27
|
+
} catch {
|
|
28
|
+
return { success: false };
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ShareNamespace } from '@rn-bridge-tools/core';
|
|
2
|
+
|
|
3
|
+
declare const require: (id: string) => unknown;
|
|
4
|
+
|
|
5
|
+
interface SharingModule {
|
|
6
|
+
isAvailableAsync: () => Promise<boolean>;
|
|
7
|
+
shareAsync: (url: string, opts?: { dialogTitle?: string }) => Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const shareHandlers = {
|
|
11
|
+
'share.open': async (
|
|
12
|
+
payload: ShareNamespace['share.open']['request'],
|
|
13
|
+
): Promise<ShareNamespace['share.open']['response']> => {
|
|
14
|
+
let Sharing: SharingModule | null = null;
|
|
15
|
+
try { Sharing = require('expo-sharing') as SharingModule; } catch {}
|
|
16
|
+
if (!Sharing) return { success: false, error: 'MODULE_NOT_INSTALLED: expo-sharing' } as never;
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const isAvailable = await Sharing.isAvailableAsync();
|
|
20
|
+
if (!isAvailable) {
|
|
21
|
+
return { success: false };
|
|
22
|
+
}
|
|
23
|
+
await Sharing.shareAsync(payload.url ?? '', {
|
|
24
|
+
dialogTitle: payload.title,
|
|
25
|
+
});
|
|
26
|
+
return { success: true };
|
|
27
|
+
} catch {
|
|
28
|
+
return { success: false };
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { StatusBarNamespace } from '@rn-bridge-tools/core';
|
|
2
|
+
import { StatusBar } from 'react-native';
|
|
3
|
+
|
|
4
|
+
export const statusbarHandlers = {
|
|
5
|
+
'statusbar.setStyle': async (
|
|
6
|
+
payload: StatusBarNamespace['statusbar.setStyle']['request'],
|
|
7
|
+
): Promise<StatusBarNamespace['statusbar.setStyle']['response']> => {
|
|
8
|
+
const styleMap: Record<string, 'default' | 'light-content' | 'dark-content'> = {
|
|
9
|
+
light: 'light-content',
|
|
10
|
+
dark: 'dark-content',
|
|
11
|
+
auto: 'default',
|
|
12
|
+
};
|
|
13
|
+
StatusBar.setBarStyle(styleMap[payload.style] ?? 'default');
|
|
14
|
+
return { success: true };
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
'statusbar.setBackgroundColor': async (
|
|
18
|
+
payload: StatusBarNamespace['statusbar.setBackgroundColor']['request'],
|
|
19
|
+
): Promise<StatusBarNamespace['statusbar.setBackgroundColor']['response']> => {
|
|
20
|
+
StatusBar.setBackgroundColor(payload.color, payload.animated ?? false);
|
|
21
|
+
return { success: true };
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
'statusbar.setHidden': async (
|
|
25
|
+
payload: StatusBarNamespace['statusbar.setHidden']['request'],
|
|
26
|
+
): Promise<StatusBarNamespace['statusbar.setHidden']['response']> => {
|
|
27
|
+
const animationMap: Record<string, 'fade' | 'slide' | 'none'> = {
|
|
28
|
+
fade: 'fade',
|
|
29
|
+
slide: 'slide',
|
|
30
|
+
none: 'none',
|
|
31
|
+
};
|
|
32
|
+
StatusBar.setHidden(payload.hidden, animationMap[payload.animation ?? 'none']);
|
|
33
|
+
return { success: true };
|
|
34
|
+
},
|
|
35
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export { WebViewBridge, createBridgeWebView } from './WebViewBridge';
|
|
2
|
+
export type { WebViewBridgeProps, WebViewBridgeRef } from './WebViewBridge';
|
|
3
|
+
export { createDefaultHandlers } from './createHandlers';
|
|
4
|
+
export type { HandlerFn, HandlerMap } from './createHandlers';
|
|
5
|
+
|
|
6
|
+
// Re-export handler maps for advanced usage
|
|
7
|
+
export { cameraHandlers } from './handlers/camera';
|
|
8
|
+
export { locationHandlers } from './handlers/location';
|
|
9
|
+
export { fileHandlers } from './handlers/file';
|
|
10
|
+
export { shareHandlers } from './handlers/share';
|
|
11
|
+
export { deviceHandlers } from './handlers/device';
|
|
12
|
+
export { statusbarHandlers } from './handlers/statusbar';
|
|
13
|
+
export { keyboardHandlers } from './handlers/keyboard';
|
|
14
|
+
export { hapticHandlers } from './handlers/haptic';
|
|
15
|
+
export { clipboardHandlers } from './handlers/clipboard';
|
|
16
|
+
export { scannerHandlers } from './handlers/scanner';
|
|
17
|
+
export { authHandlers } from './handlers/auth';
|
|
18
|
+
export { iapHandlers } from './handlers/iap';
|
|
19
|
+
export { pushHandlers } from './handlers/push';
|
|
20
|
+
export { permissionHandlers } from './handlers/permission';
|
|
21
|
+
export { preferenceHandlers } from './handlers/preference';
|
|
22
|
+
export { navigationHandlers } from './handlers/navigation';
|
|
23
|
+
export { browserHandlers } from './handlers/browser';
|