@flightdev/mobile 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,332 @@
1
+ import { useMemo, useCallback, useState, useEffect } from 'react';
2
+
3
+ // src/react/index.ts
4
+
5
+ // src/platform.ts
6
+ function getCapacitor() {
7
+ try {
8
+ const Capacitor = globalThis.Capacitor;
9
+ return Capacitor || null;
10
+ } catch {
11
+ return null;
12
+ }
13
+ }
14
+ function getPlatform() {
15
+ const Capacitor = getCapacitor();
16
+ if (!Capacitor) {
17
+ return {
18
+ platform: "web",
19
+ isNative: false,
20
+ isIOS: false,
21
+ isAndroid: false,
22
+ isWeb: true,
23
+ hasCapacitor: false
24
+ };
25
+ }
26
+ const platform = Capacitor.getPlatform();
27
+ const isNative = Capacitor.isNativePlatform();
28
+ return {
29
+ platform,
30
+ isNative,
31
+ isIOS: platform === "ios",
32
+ isAndroid: platform === "android",
33
+ isWeb: platform === "web",
34
+ hasCapacitor: true
35
+ };
36
+ }
37
+ function isPluginAvailable(pluginName) {
38
+ const Capacitor = getCapacitor();
39
+ if (!Capacitor || !Capacitor.isPluginAvailable) {
40
+ return false;
41
+ }
42
+ return Capacitor.isPluginAvailable(pluginName);
43
+ }
44
+
45
+ // src/react/index.ts
46
+ function usePlatform() {
47
+ return useMemo(() => getPlatform(), []);
48
+ }
49
+ function useCamera() {
50
+ const available = isPluginAvailable("Camera");
51
+ const takePhoto = useCallback(async (options) => {
52
+ if (!available) return null;
53
+ try {
54
+ const { Camera, CameraResultType, CameraSource } = await import('@capacitor/camera');
55
+ const photo = await Camera.getPhoto({
56
+ quality: options?.quality ?? 90,
57
+ allowEditing: options?.allowEditing ?? false,
58
+ saveToGallery: options?.saveToGallery ?? false,
59
+ width: options?.width,
60
+ height: options?.height,
61
+ resultType: CameraResultType.Uri,
62
+ source: CameraSource.Camera
63
+ });
64
+ return {
65
+ webPath: photo.webPath,
66
+ path: photo.path,
67
+ format: photo.format,
68
+ saved: photo.saved
69
+ };
70
+ } catch (error) {
71
+ console.error("[Flight Mobile] Camera error:", error);
72
+ return null;
73
+ }
74
+ }, [available]);
75
+ const pickImage = useCallback(async (options) => {
76
+ if (!available) return null;
77
+ try {
78
+ const { Camera, CameraResultType, CameraSource } = await import('@capacitor/camera');
79
+ const photo = await Camera.getPhoto({
80
+ quality: options?.quality ?? 90,
81
+ allowEditing: options?.allowEditing ?? false,
82
+ resultType: CameraResultType.Uri,
83
+ source: CameraSource.Photos
84
+ });
85
+ return {
86
+ webPath: photo.webPath,
87
+ path: photo.path,
88
+ format: photo.format
89
+ };
90
+ } catch (error) {
91
+ console.error("[Flight Mobile] Gallery error:", error);
92
+ return null;
93
+ }
94
+ }, [available]);
95
+ return { takePhoto, pickImage, available };
96
+ }
97
+ function mapPosition(pos) {
98
+ return {
99
+ coords: {
100
+ latitude: pos.coords.latitude,
101
+ longitude: pos.coords.longitude,
102
+ accuracy: pos.coords.accuracy,
103
+ altitude: pos.coords.altitude ?? null,
104
+ altitudeAccuracy: pos.coords.altitudeAccuracy ?? null,
105
+ heading: pos.coords.heading ?? null,
106
+ speed: pos.coords.speed ?? null
107
+ },
108
+ timestamp: pos.timestamp
109
+ };
110
+ }
111
+ function useGeolocation() {
112
+ const [position, setPosition] = useState(null);
113
+ const [error, setError] = useState(null);
114
+ const [loading, setLoading] = useState(false);
115
+ const available = isPluginAvailable("Geolocation");
116
+ const getCurrentPosition = useCallback(async () => {
117
+ if (!available) return null;
118
+ setLoading(true);
119
+ setError(null);
120
+ try {
121
+ const { Geolocation } = await import('@capacitor/geolocation');
122
+ const rawPos = await Geolocation.getCurrentPosition();
123
+ const pos = mapPosition(rawPos);
124
+ setPosition(pos);
125
+ return pos;
126
+ } catch (e) {
127
+ setError(e);
128
+ return null;
129
+ } finally {
130
+ setLoading(false);
131
+ }
132
+ }, [available]);
133
+ const watchPosition = useCallback(async (callback) => {
134
+ if (!available) return null;
135
+ try {
136
+ const { Geolocation } = await import('@capacitor/geolocation');
137
+ const watchId = await Geolocation.watchPosition({}, (rawPos, err) => {
138
+ if (err) {
139
+ setError(err);
140
+ return;
141
+ }
142
+ if (rawPos) {
143
+ const pos = mapPosition(rawPos);
144
+ setPosition(pos);
145
+ callback(pos);
146
+ }
147
+ });
148
+ return watchId;
149
+ } catch (e) {
150
+ setError(e);
151
+ return null;
152
+ }
153
+ }, [available]);
154
+ const clearWatch = useCallback(async (watchId) => {
155
+ if (!available) return;
156
+ try {
157
+ const { Geolocation } = await import('@capacitor/geolocation');
158
+ await Geolocation.clearWatch({ id: watchId });
159
+ } catch (e) {
160
+ console.error("[Flight Mobile] Clear watch error:", e);
161
+ }
162
+ }, [available]);
163
+ return { position, error, loading, getCurrentPosition, watchPosition, clearWatch, available };
164
+ }
165
+ function usePushNotifications() {
166
+ const [token, setToken] = useState(null);
167
+ const available = isPluginAvailable("PushNotifications");
168
+ useEffect(() => {
169
+ if (!available) return;
170
+ let cleanup;
171
+ (async () => {
172
+ const { PushNotifications } = await import('@capacitor/push-notifications');
173
+ const listener = await PushNotifications.addListener("registration", (t) => {
174
+ setToken(t.value);
175
+ });
176
+ cleanup = () => listener.remove();
177
+ })();
178
+ return () => {
179
+ cleanup?.();
180
+ };
181
+ }, [available]);
182
+ const requestPermission = useCallback(async () => {
183
+ if (!available) return false;
184
+ try {
185
+ const { PushNotifications } = await import('@capacitor/push-notifications');
186
+ const result = await PushNotifications.requestPermissions();
187
+ if (result.receive === "granted") {
188
+ await PushNotifications.register();
189
+ return true;
190
+ }
191
+ return false;
192
+ } catch (e) {
193
+ console.error("[Flight Mobile] Push notification error:", e);
194
+ return false;
195
+ }
196
+ }, [available]);
197
+ const onNotificationReceived = useCallback(async (callback) => {
198
+ if (!available) return () => {
199
+ };
200
+ const { PushNotifications } = await import('@capacitor/push-notifications');
201
+ const listener = await PushNotifications.addListener("pushNotificationReceived", callback);
202
+ return () => listener.remove();
203
+ }, [available]);
204
+ const onNotificationAction = useCallback(async (callback) => {
205
+ if (!available) return () => {
206
+ };
207
+ const { PushNotifications } = await import('@capacitor/push-notifications');
208
+ const listener = await PushNotifications.addListener("pushNotificationActionPerformed", callback);
209
+ return () => listener.remove();
210
+ }, [available]);
211
+ return { token, requestPermission, onNotificationReceived, onNotificationAction, available };
212
+ }
213
+ function useHaptics() {
214
+ const available = isPluginAvailable("Haptics");
215
+ const impact = useCallback(async (style = "medium") => {
216
+ if (!available) return;
217
+ try {
218
+ const { Haptics, ImpactStyle } = await import('@capacitor/haptics');
219
+ const styleMap = {
220
+ light: ImpactStyle.Light,
221
+ medium: ImpactStyle.Medium,
222
+ heavy: ImpactStyle.Heavy
223
+ };
224
+ await Haptics.impact({ style: styleMap[style] });
225
+ } catch (e) {
226
+ console.error("[Flight Mobile] Haptics error:", e);
227
+ }
228
+ }, [available]);
229
+ const notification = useCallback(async (type = "success") => {
230
+ if (!available) return;
231
+ try {
232
+ const { Haptics, NotificationType } = await import('@capacitor/haptics');
233
+ const typeMap = {
234
+ success: NotificationType.Success,
235
+ warning: NotificationType.Warning,
236
+ error: NotificationType.Error
237
+ };
238
+ await Haptics.notification({ type: typeMap[type] });
239
+ } catch (e) {
240
+ console.error("[Flight Mobile] Haptics error:", e);
241
+ }
242
+ }, [available]);
243
+ const selection = useCallback(async () => {
244
+ if (!available) return;
245
+ try {
246
+ const { Haptics } = await import('@capacitor/haptics');
247
+ await Haptics.selectionStart();
248
+ await Haptics.selectionEnd();
249
+ } catch (e) {
250
+ console.error("[Flight Mobile] Haptics error:", e);
251
+ }
252
+ }, [available]);
253
+ const vibrate = useCallback(async (duration = 300) => {
254
+ if (!available) return;
255
+ try {
256
+ const { Haptics } = await import('@capacitor/haptics');
257
+ await Haptics.vibrate({ duration });
258
+ } catch (e) {
259
+ console.error("[Flight Mobile] Haptics error:", e);
260
+ }
261
+ }, [available]);
262
+ return { impact, notification, selection, vibrate, available };
263
+ }
264
+ function useStorage() {
265
+ const available = isPluginAvailable("Preferences");
266
+ const { isWeb } = usePlatform();
267
+ const get = useCallback(async (key) => {
268
+ if (isWeb || !available) {
269
+ return localStorage.getItem(key);
270
+ }
271
+ try {
272
+ const { Preferences } = await import('@capacitor/preferences');
273
+ const { value } = await Preferences.get({ key });
274
+ return value;
275
+ } catch {
276
+ return localStorage.getItem(key);
277
+ }
278
+ }, [available, isWeb]);
279
+ const set = useCallback(async (key, value) => {
280
+ if (isWeb || !available) {
281
+ localStorage.setItem(key, value);
282
+ return;
283
+ }
284
+ try {
285
+ const { Preferences } = await import('@capacitor/preferences');
286
+ await Preferences.set({ key, value });
287
+ } catch {
288
+ localStorage.setItem(key, value);
289
+ }
290
+ }, [available, isWeb]);
291
+ const remove = useCallback(async (key) => {
292
+ if (isWeb || !available) {
293
+ localStorage.removeItem(key);
294
+ return;
295
+ }
296
+ try {
297
+ const { Preferences } = await import('@capacitor/preferences');
298
+ await Preferences.remove({ key });
299
+ } catch {
300
+ localStorage.removeItem(key);
301
+ }
302
+ }, [available, isWeb]);
303
+ const keys = useCallback(async () => {
304
+ if (isWeb || !available) {
305
+ return Object.keys(localStorage);
306
+ }
307
+ try {
308
+ const { Preferences } = await import('@capacitor/preferences');
309
+ const { keys: k } = await Preferences.keys();
310
+ return k;
311
+ } catch {
312
+ return Object.keys(localStorage);
313
+ }
314
+ }, [available, isWeb]);
315
+ const clear = useCallback(async () => {
316
+ if (isWeb || !available) {
317
+ localStorage.clear();
318
+ return;
319
+ }
320
+ try {
321
+ const { Preferences } = await import('@capacitor/preferences');
322
+ await Preferences.clear();
323
+ } catch {
324
+ localStorage.clear();
325
+ }
326
+ }, [available, isWeb]);
327
+ return { get, set, remove, keys, clear, available };
328
+ }
329
+
330
+ export { useCamera, useGeolocation, useHaptics, usePlatform, usePushNotifications, useStorage };
331
+ //# sourceMappingURL=index.js.map
332
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/platform.ts","../../src/react/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\r\n * Platform detection utilities.\r\n * Works without Capacitor installed - gracefully degrades.\r\n */\r\n\r\n// =============================================================================\r\n// Types\r\n// =============================================================================\r\n\r\nexport type Platform = 'web' | 'ios' | 'android';\r\n\r\nexport interface PlatformInfo {\r\n /** Current platform: 'web', 'ios', or 'android' */\r\n platform: Platform;\r\n /** True if running as a native app (iOS or Android) */\r\n isNative: boolean;\r\n /** True if running on iOS */\r\n isIOS: boolean;\r\n /** True if running on Android */\r\n isAndroid: boolean;\r\n /** True if running as a web app */\r\n isWeb: boolean;\r\n /** True if Capacitor is available */\r\n hasCapacitor: boolean;\r\n}\r\n\r\n// =============================================================================\r\n// Detection\r\n// =============================================================================\r\n\r\n/**\r\n * Get Capacitor instance if available.\r\n * Returns null if Capacitor is not installed.\r\n */\r\nfunction getCapacitor(): any {\r\n try {\r\n // Dynamic import to avoid errors when Capacitor is not installed\r\n const Capacitor = (globalThis as any).Capacitor;\r\n return Capacitor || null;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Detect the current platform.\r\n * \r\n * @returns Platform information object\r\n * \r\n * @example\r\n * ```typescript\r\n * const { isNative, isIOS, platform } = getPlatform();\r\n * \r\n * if (isNative) {\r\n * // Use native features\r\n * }\r\n * \r\n * if (isIOS) {\r\n * // iOS-specific code\r\n * }\r\n * ```\r\n */\r\nexport function getPlatform(): PlatformInfo {\r\n const Capacitor = getCapacitor();\r\n\r\n if (!Capacitor) {\r\n return {\r\n platform: 'web',\r\n isNative: false,\r\n isIOS: false,\r\n isAndroid: false,\r\n isWeb: true,\r\n hasCapacitor: false,\r\n };\r\n }\r\n\r\n const platform = Capacitor.getPlatform() as Platform;\r\n const isNative = Capacitor.isNativePlatform();\r\n\r\n return {\r\n platform,\r\n isNative,\r\n isIOS: platform === 'ios',\r\n isAndroid: platform === 'android',\r\n isWeb: platform === 'web',\r\n hasCapacitor: true,\r\n };\r\n}\r\n\r\n/**\r\n * Check if a Capacitor plugin is available.\r\n * \r\n * @param pluginName - Name of the plugin to check\r\n * @returns True if the plugin is registered\r\n * \r\n * @example\r\n * ```typescript\r\n * if (isPluginAvailable('Camera')) {\r\n * // Camera plugin is installed\r\n * }\r\n * ```\r\n */\r\nexport function isPluginAvailable(pluginName: string): boolean {\r\n const Capacitor = getCapacitor();\r\n\r\n if (!Capacitor || !Capacitor.isPluginAvailable) {\r\n return false;\r\n }\r\n\r\n return Capacitor.isPluginAvailable(pluginName);\r\n}\r\n\r\n/**\r\n * Get safe area insets for notched devices.\r\n * Returns zeros on web or when not available.\r\n */\r\nexport function getSafeAreaInsets(): {\r\n top: number;\r\n bottom: number;\r\n left: number;\r\n right: number;\r\n} {\r\n // Try to get from CSS env variables\r\n if (typeof window !== 'undefined' && typeof window.getComputedStyle === 'function') {\r\n const style = getComputedStyle(document.documentElement);\r\n return {\r\n top: parseInt(style.getPropertyValue('--sat') || '0', 10),\r\n bottom: parseInt(style.getPropertyValue('--sab') || '0', 10),\r\n left: parseInt(style.getPropertyValue('--sal') || '0', 10),\r\n right: parseInt(style.getPropertyValue('--sar') || '0', 10),\r\n };\r\n }\r\n\r\n return { top: 0, bottom: 0, left: 0, right: 0 };\r\n}\r\n\r\n/**\r\n * Check if running in a PWA installed context.\r\n */\r\nexport function isPWA(): boolean {\r\n if (typeof window === 'undefined') return false;\r\n\r\n return window.matchMedia('(display-mode: standalone)').matches ||\r\n (window.navigator as any).standalone === true;\r\n}\r\n","/**\r\n * React hooks for @flightdev/mobile\r\n * \r\n * Provides React-friendly wrappers around Capacitor plugins.\r\n * All hooks gracefully degrade on web - no errors if plugin not installed.\r\n * \r\n * Philosophy: Flight doesn't impose - use only the hooks you need.\r\n * Each Capacitor plugin is an optional peer dependency.\r\n * \r\n * @example\r\n * ```typescript\r\n * import { usePlatform, useCamera, useGeolocation } from '@flightdev/mobile/react';\r\n * \r\n * function App() {\r\n * const { isNative, isIOS } = usePlatform();\r\n * const { takePhoto } = useCamera();\r\n * const { position, getCurrentPosition } = useGeolocation();\r\n * \r\n * // ...\r\n * }\r\n * ```\r\n */\r\n\r\nimport { useState, useEffect, useCallback, useMemo } from 'react';\r\nimport { getPlatform, isPluginAvailable, type PlatformInfo } from '../platform';\r\n\r\n// =============================================================================\r\n// Platform Hook\r\n// =============================================================================\r\n\r\n/**\r\n * Get current platform information.\r\n * \r\n * @example\r\n * ```typescript\r\n * function App() {\r\n * const { isNative, isIOS, isAndroid, isWeb } = usePlatform();\r\n * \r\n * if (isNative) {\r\n * return <NativeApp />;\r\n * }\r\n * \r\n * return <WebApp />;\r\n * }\r\n * ```\r\n */\r\nexport function usePlatform(): PlatformInfo {\r\n return useMemo(() => getPlatform(), []);\r\n}\r\n\r\n// =============================================================================\r\n// Camera Hook\r\n// =============================================================================\r\n\r\ninterface CameraResult {\r\n /** Path to the image that can be used as src */\r\n webPath?: string;\r\n /** Base64 encoded image data */\r\n base64String?: string;\r\n /** Platform-specific path */\r\n path?: string;\r\n /** Image format (jpeg, png, etc.) */\r\n format?: string;\r\n /** Whether the image was saved to gallery */\r\n saved?: boolean;\r\n}\r\n\r\ninterface UseCameraOptions {\r\n /** Image quality (0-100) */\r\n quality?: number;\r\n /** Allow editing before returning */\r\n allowEditing?: boolean;\r\n /** Save to photo gallery */\r\n saveToGallery?: boolean;\r\n /** Width constraint */\r\n width?: number;\r\n /** Height constraint */\r\n height?: number;\r\n}\r\n\r\ninterface UseCameraReturn {\r\n /** Take a photo with the camera */\r\n takePhoto: (options?: UseCameraOptions) => Promise<CameraResult | null>;\r\n /** Pick an image from gallery */\r\n pickImage: (options?: UseCameraOptions) => Promise<CameraResult | null>;\r\n /** Camera is available */\r\n available: boolean;\r\n}\r\n\r\n/**\r\n * Camera hook for taking photos and picking images.\r\n * Requires @capacitor/camera to be installed.\r\n * \r\n * @example\r\n * ```typescript\r\n * function PhotoCapture() {\r\n * const { takePhoto, pickImage, available } = useCamera();\r\n * \r\n * const handleCapture = async () => {\r\n * const photo = await takePhoto({ quality: 90 });\r\n * if (photo?.webPath) {\r\n * setImage(photo.webPath);\r\n * }\r\n * };\r\n * \r\n * if (!available) return <p>Camera not available</p>;\r\n * \r\n * return <button onClick={handleCapture}>Take Photo</button>;\r\n * }\r\n * ```\r\n */\r\nexport function useCamera(): UseCameraReturn {\r\n const available = isPluginAvailable('Camera');\r\n\r\n const takePhoto = useCallback(async (options?: UseCameraOptions): Promise<CameraResult | null> => {\r\n if (!available) return null;\r\n\r\n try {\r\n const { Camera, CameraResultType, CameraSource } = await import('@capacitor/camera');\r\n\r\n const photo = await Camera.getPhoto({\r\n quality: options?.quality ?? 90,\r\n allowEditing: options?.allowEditing ?? false,\r\n saveToGallery: options?.saveToGallery ?? false,\r\n width: options?.width,\r\n height: options?.height,\r\n resultType: CameraResultType.Uri,\r\n source: CameraSource.Camera,\r\n });\r\n\r\n return {\r\n webPath: photo.webPath,\r\n path: photo.path,\r\n format: photo.format,\r\n saved: photo.saved,\r\n };\r\n } catch (error) {\r\n console.error('[Flight Mobile] Camera error:', error);\r\n return null;\r\n }\r\n }, [available]);\r\n\r\n const pickImage = useCallback(async (options?: UseCameraOptions): Promise<CameraResult | null> => {\r\n if (!available) return null;\r\n\r\n try {\r\n const { Camera, CameraResultType, CameraSource } = await import('@capacitor/camera');\r\n\r\n const photo = await Camera.getPhoto({\r\n quality: options?.quality ?? 90,\r\n allowEditing: options?.allowEditing ?? false,\r\n resultType: CameraResultType.Uri,\r\n source: CameraSource.Photos,\r\n });\r\n\r\n return {\r\n webPath: photo.webPath,\r\n path: photo.path,\r\n format: photo.format,\r\n };\r\n } catch (error) {\r\n console.error('[Flight Mobile] Gallery error:', error);\r\n return null;\r\n }\r\n }, [available]);\r\n\r\n return { takePhoto, pickImage, available };\r\n}\r\n\r\n// =============================================================================\r\n// Geolocation Hook\r\n// =============================================================================\r\n\r\ninterface GeolocationPosition {\r\n coords: {\r\n latitude: number;\r\n longitude: number;\r\n accuracy: number;\r\n altitude: number | null;\r\n altitudeAccuracy: number | null;\r\n heading: number | null;\r\n speed: number | null;\r\n };\r\n timestamp: number;\r\n}\r\n\r\n/**\r\n * Map Capacitor Position to our GeolocationPosition type.\r\n * Handles undefined values from Capacitor's Position type.\r\n */\r\nfunction mapPosition(pos: any): GeolocationPosition {\r\n return {\r\n coords: {\r\n latitude: pos.coords.latitude,\r\n longitude: pos.coords.longitude,\r\n accuracy: pos.coords.accuracy,\r\n altitude: pos.coords.altitude ?? null,\r\n altitudeAccuracy: pos.coords.altitudeAccuracy ?? null,\r\n heading: pos.coords.heading ?? null,\r\n speed: pos.coords.speed ?? null,\r\n },\r\n timestamp: pos.timestamp,\r\n };\r\n}\r\n\r\ninterface UseGeolocationReturn {\r\n /** Current position (null until fetched) */\r\n position: GeolocationPosition | null;\r\n /** Fetch error if any */\r\n error: Error | null;\r\n /** Loading state */\r\n loading: boolean;\r\n /** Get current position */\r\n getCurrentPosition: () => Promise<GeolocationPosition | null>;\r\n /** Start watching position changes */\r\n watchPosition: (callback: (pos: GeolocationPosition) => void) => Promise<string | null>;\r\n /** Stop watching position */\r\n clearWatch: (watchId: string) => Promise<void>;\r\n /** Geolocation is available */\r\n available: boolean;\r\n}\r\n\r\n/**\r\n * Geolocation hook for getting device position.\r\n * Requires @capacitor/geolocation to be installed.\r\n * \r\n * @example\r\n * ```typescript\r\n * function LocationTracker() {\r\n * const { position, error, loading, getCurrentPosition, available } = useGeolocation();\r\n * \r\n * if (!available) return <p>Geolocation not available</p>;\r\n * if (loading) return <p>Getting location...</p>;\r\n * if (error) return <p>Error: {error.message}</p>;\r\n * \r\n * return (\r\n * <div>\r\n * <p>Lat: {position?.coords.latitude}</p>\r\n * <p>Lng: {position?.coords.longitude}</p>\r\n * <button onClick={getCurrentPosition}>Refresh</button>\r\n * </div>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function useGeolocation(): UseGeolocationReturn {\r\n const [position, setPosition] = useState<GeolocationPosition | null>(null);\r\n const [error, setError] = useState<Error | null>(null);\r\n const [loading, setLoading] = useState(false);\r\n\r\n const available = isPluginAvailable('Geolocation');\r\n\r\n const getCurrentPosition = useCallback(async (): Promise<GeolocationPosition | null> => {\r\n if (!available) return null;\r\n\r\n setLoading(true);\r\n setError(null);\r\n\r\n try {\r\n const { Geolocation } = await import('@capacitor/geolocation');\r\n const rawPos = await Geolocation.getCurrentPosition();\r\n const pos = mapPosition(rawPos);\r\n setPosition(pos);\r\n return pos;\r\n } catch (e) {\r\n setError(e as Error);\r\n return null;\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [available]);\r\n\r\n const watchPosition = useCallback(async (\r\n callback: (pos: GeolocationPosition) => void\r\n ): Promise<string | null> => {\r\n if (!available) return null;\r\n\r\n try {\r\n const { Geolocation } = await import('@capacitor/geolocation');\r\n const watchId = await Geolocation.watchPosition({}, (rawPos, err) => {\r\n if (err) {\r\n setError(err);\r\n return;\r\n }\r\n if (rawPos) {\r\n const pos = mapPosition(rawPos);\r\n setPosition(pos);\r\n callback(pos);\r\n }\r\n });\r\n return watchId;\r\n } catch (e) {\r\n setError(e as Error);\r\n return null;\r\n }\r\n }, [available]);\r\n\r\n const clearWatch = useCallback(async (watchId: string): Promise<void> => {\r\n if (!available) return;\r\n\r\n try {\r\n const { Geolocation } = await import('@capacitor/geolocation');\r\n await Geolocation.clearWatch({ id: watchId });\r\n } catch (e) {\r\n console.error('[Flight Mobile] Clear watch error:', e);\r\n }\r\n }, [available]);\r\n\r\n return { position, error, loading, getCurrentPosition, watchPosition, clearWatch, available };\r\n}\r\n\r\n// =============================================================================\r\n// Push Notifications Hook\r\n// =============================================================================\r\n\r\ninterface UsePushNotificationsReturn {\r\n /** FCM/APNs token for the device */\r\n token: string | null;\r\n /** Request permission and register for notifications */\r\n requestPermission: () => Promise<boolean>;\r\n /** Add listener for notification received */\r\n onNotificationReceived: (callback: (notification: any) => void) => Promise<() => void>;\r\n /** Add listener for notification action */\r\n onNotificationAction: (callback: (notification: any) => void) => Promise<() => void>;\r\n /** Push notifications available */\r\n available: boolean;\r\n}\r\n\r\n/**\r\n * Push notifications hook.\r\n * Requires @capacitor/push-notifications to be installed.\r\n * \r\n * @example\r\n * ```typescript\r\n * function NotificationSetup() {\r\n * const { token, requestPermission, onNotificationReceived, available } = usePushNotifications();\r\n * \r\n * useEffect(() => {\r\n * if (!available) return;\r\n * \r\n * requestPermission();\r\n * \r\n * const cleanup = onNotificationReceived((notification) => {\r\n * console.log('Received:', notification);\r\n * });\r\n * \r\n * return () => { cleanup.then(fn => fn?.()); };\r\n * }, []);\r\n * \r\n * return <p>Token: {token}</p>;\r\n * }\r\n * ```\r\n */\r\nexport function usePushNotifications(): UsePushNotificationsReturn {\r\n const [token, setToken] = useState<string | null>(null);\r\n const available = isPluginAvailable('PushNotifications');\r\n\r\n useEffect(() => {\r\n if (!available) return;\r\n\r\n let cleanup: (() => void) | undefined;\r\n\r\n (async () => {\r\n const { PushNotifications } = await import('@capacitor/push-notifications');\r\n\r\n const listener = await PushNotifications.addListener('registration', (t) => {\r\n setToken(t.value);\r\n });\r\n\r\n cleanup = () => listener.remove();\r\n })();\r\n\r\n return () => { cleanup?.(); };\r\n }, [available]);\r\n\r\n const requestPermission = useCallback(async (): Promise<boolean> => {\r\n if (!available) return false;\r\n\r\n try {\r\n const { PushNotifications } = await import('@capacitor/push-notifications');\r\n\r\n const result = await PushNotifications.requestPermissions();\r\n\r\n if (result.receive === 'granted') {\r\n await PushNotifications.register();\r\n return true;\r\n }\r\n\r\n return false;\r\n } catch (e) {\r\n console.error('[Flight Mobile] Push notification error:', e);\r\n return false;\r\n }\r\n }, [available]);\r\n\r\n const onNotificationReceived = useCallback(async (\r\n callback: (notification: any) => void\r\n ): Promise<() => void> => {\r\n if (!available) return () => { };\r\n\r\n const { PushNotifications } = await import('@capacitor/push-notifications');\r\n\r\n const listener = await PushNotifications.addListener('pushNotificationReceived', callback);\r\n\r\n return () => listener.remove();\r\n }, [available]);\r\n\r\n const onNotificationAction = useCallback(async (\r\n callback: (notification: any) => void\r\n ): Promise<() => void> => {\r\n if (!available) return () => { };\r\n\r\n const { PushNotifications } = await import('@capacitor/push-notifications');\r\n\r\n const listener = await PushNotifications.addListener('pushNotificationActionPerformed', callback);\r\n\r\n return () => listener.remove();\r\n }, [available]);\r\n\r\n return { token, requestPermission, onNotificationReceived, onNotificationAction, available };\r\n}\r\n\r\n// =============================================================================\r\n// Haptics Hook\r\n// =============================================================================\r\n\r\ninterface UseHapticsReturn {\r\n /** Light impact feedback */\r\n impact: (style?: 'light' | 'medium' | 'heavy') => Promise<void>;\r\n /** Notification feedback */\r\n notification: (type?: 'success' | 'warning' | 'error') => Promise<void>;\r\n /** Selection feedback */\r\n selection: () => Promise<void>;\r\n /** Vibrate for duration */\r\n vibrate: (duration?: number) => Promise<void>;\r\n /** Haptics available */\r\n available: boolean;\r\n}\r\n\r\n/**\r\n * Haptic feedback hook.\r\n * Requires @capacitor/haptics to be installed.\r\n * \r\n * @example\r\n * ```typescript\r\n * function HapticButton() {\r\n * const { impact, notification, available } = useHaptics();\r\n * \r\n * const handlePress = async () => {\r\n * await impact('medium');\r\n * // Do action\r\n * };\r\n * \r\n * const handleSuccess = async () => {\r\n * await notification('success');\r\n * };\r\n * \r\n * return <button onClick={handlePress}>Press me</button>;\r\n * }\r\n * ```\r\n */\r\nexport function useHaptics(): UseHapticsReturn {\r\n const available = isPluginAvailable('Haptics');\r\n\r\n const impact = useCallback(async (style: 'light' | 'medium' | 'heavy' = 'medium'): Promise<void> => {\r\n if (!available) return;\r\n\r\n try {\r\n const { Haptics, ImpactStyle } = await import('@capacitor/haptics');\r\n const styleMap = {\r\n light: ImpactStyle.Light,\r\n medium: ImpactStyle.Medium,\r\n heavy: ImpactStyle.Heavy,\r\n };\r\n await Haptics.impact({ style: styleMap[style] });\r\n } catch (e) {\r\n console.error('[Flight Mobile] Haptics error:', e);\r\n }\r\n }, [available]);\r\n\r\n const notification = useCallback(async (type: 'success' | 'warning' | 'error' = 'success'): Promise<void> => {\r\n if (!available) return;\r\n\r\n try {\r\n const { Haptics, NotificationType } = await import('@capacitor/haptics');\r\n const typeMap = {\r\n success: NotificationType.Success,\r\n warning: NotificationType.Warning,\r\n error: NotificationType.Error,\r\n };\r\n await Haptics.notification({ type: typeMap[type] });\r\n } catch (e) {\r\n console.error('[Flight Mobile] Haptics error:', e);\r\n }\r\n }, [available]);\r\n\r\n const selection = useCallback(async (): Promise<void> => {\r\n if (!available) return;\r\n\r\n try {\r\n const { Haptics } = await import('@capacitor/haptics');\r\n await Haptics.selectionStart();\r\n await Haptics.selectionEnd();\r\n } catch (e) {\r\n console.error('[Flight Mobile] Haptics error:', e);\r\n }\r\n }, [available]);\r\n\r\n const vibrate = useCallback(async (duration: number = 300): Promise<void> => {\r\n if (!available) return;\r\n\r\n try {\r\n const { Haptics } = await import('@capacitor/haptics');\r\n await Haptics.vibrate({ duration });\r\n } catch (e) {\r\n console.error('[Flight Mobile] Haptics error:', e);\r\n }\r\n }, [available]);\r\n\r\n return { impact, notification, selection, vibrate, available };\r\n}\r\n\r\n// =============================================================================\r\n// Storage Hook\r\n// =============================================================================\r\n\r\ninterface UseStorageReturn {\r\n /** Get a value from storage */\r\n get: (key: string) => Promise<string | null>;\r\n /** Set a value in storage */\r\n set: (key: string, value: string) => Promise<void>;\r\n /** Remove a value from storage */\r\n remove: (key: string) => Promise<void>;\r\n /** Get all keys */\r\n keys: () => Promise<string[]>;\r\n /** Clear all storage */\r\n clear: () => Promise<void>;\r\n /** Storage available */\r\n available: boolean;\r\n}\r\n\r\n/**\r\n * Secure storage hook using Capacitor Preferences.\r\n * Falls back to localStorage on web.\r\n * Requires @capacitor/preferences to be installed for native.\r\n * \r\n * @example\r\n * ```typescript\r\n * function UserSettings() {\r\n * const { get, set, remove } = useStorage();\r\n * \r\n * const saveTheme = async (theme: string) => {\r\n * await set('theme', theme);\r\n * };\r\n * \r\n * const loadTheme = async () => {\r\n * return await get('theme') ?? 'light';\r\n * };\r\n * }\r\n * ```\r\n */\r\nexport function useStorage(): UseStorageReturn {\r\n const available = isPluginAvailable('Preferences');\r\n const { isWeb } = usePlatform();\r\n\r\n const get = useCallback(async (key: string): Promise<string | null> => {\r\n if (isWeb || !available) {\r\n return localStorage.getItem(key);\r\n }\r\n\r\n try {\r\n const { Preferences } = await import('@capacitor/preferences');\r\n const { value } = await Preferences.get({ key });\r\n return value;\r\n } catch {\r\n return localStorage.getItem(key);\r\n }\r\n }, [available, isWeb]);\r\n\r\n const set = useCallback(async (key: string, value: string): Promise<void> => {\r\n if (isWeb || !available) {\r\n localStorage.setItem(key, value);\r\n return;\r\n }\r\n\r\n try {\r\n const { Preferences } = await import('@capacitor/preferences');\r\n await Preferences.set({ key, value });\r\n } catch {\r\n localStorage.setItem(key, value);\r\n }\r\n }, [available, isWeb]);\r\n\r\n const remove = useCallback(async (key: string): Promise<void> => {\r\n if (isWeb || !available) {\r\n localStorage.removeItem(key);\r\n return;\r\n }\r\n\r\n try {\r\n const { Preferences } = await import('@capacitor/preferences');\r\n await Preferences.remove({ key });\r\n } catch {\r\n localStorage.removeItem(key);\r\n }\r\n }, [available, isWeb]);\r\n\r\n const keys = useCallback(async (): Promise<string[]> => {\r\n if (isWeb || !available) {\r\n return Object.keys(localStorage);\r\n }\r\n\r\n try {\r\n const { Preferences } = await import('@capacitor/preferences');\r\n const { keys: k } = await Preferences.keys();\r\n return k;\r\n } catch {\r\n return Object.keys(localStorage);\r\n }\r\n }, [available, isWeb]);\r\n\r\n const clear = useCallback(async (): Promise<void> => {\r\n if (isWeb || !available) {\r\n localStorage.clear();\r\n return;\r\n }\r\n\r\n try {\r\n const { Preferences } = await import('@capacitor/preferences');\r\n await Preferences.clear();\r\n } catch {\r\n localStorage.clear();\r\n }\r\n }, [available, isWeb]);\r\n\r\n return { get, set, remove, keys, clear, available };\r\n}\r\n"]}
@@ -0,0 +1,82 @@
1
+ import { Ref } from 'vue';
2
+ import { a as PlatformInfo } from '../platform-Cq72g4Qx.js';
3
+
4
+ /**
5
+ * Vue composables for @flightdev/mobile
6
+ *
7
+ * Provides Vue-friendly wrappers around Capacitor plugins.
8
+ * All composables gracefully degrade on web.
9
+ */
10
+
11
+ /**
12
+ * Get current platform information.
13
+ */
14
+ declare function usePlatform(): PlatformInfo;
15
+ interface UseCameraReturn {
16
+ takePhoto: (options?: {
17
+ quality?: number;
18
+ allowEditing?: boolean;
19
+ }) => Promise<{
20
+ webPath?: string;
21
+ } | null>;
22
+ pickImage: (options?: {
23
+ quality?: number;
24
+ }) => Promise<{
25
+ webPath?: string;
26
+ } | null>;
27
+ available: boolean;
28
+ }
29
+ /**
30
+ * Camera composable for taking photos and picking images.
31
+ */
32
+ declare function useCamera(): UseCameraReturn;
33
+ interface GeolocationPosition {
34
+ coords: {
35
+ latitude: number;
36
+ longitude: number;
37
+ accuracy: number;
38
+ };
39
+ timestamp: number;
40
+ }
41
+ interface UseGeolocationReturn {
42
+ position: Ref<GeolocationPosition | null>;
43
+ error: Ref<Error | null>;
44
+ loading: Ref<boolean>;
45
+ getCurrentPosition: () => Promise<GeolocationPosition | null>;
46
+ available: boolean;
47
+ }
48
+ /**
49
+ * Geolocation composable for getting device position.
50
+ */
51
+ declare function useGeolocation(): UseGeolocationReturn;
52
+ interface UsePushNotificationsReturn {
53
+ token: Ref<string | null>;
54
+ requestPermission: () => Promise<boolean>;
55
+ available: boolean;
56
+ }
57
+ /**
58
+ * Push notifications composable.
59
+ */
60
+ declare function usePushNotifications(): UsePushNotificationsReturn;
61
+ interface UseHapticsReturn {
62
+ impact: (style?: 'light' | 'medium' | 'heavy') => Promise<void>;
63
+ notification: (type?: 'success' | 'warning' | 'error') => Promise<void>;
64
+ vibrate: (duration?: number) => Promise<void>;
65
+ available: boolean;
66
+ }
67
+ /**
68
+ * Haptic feedback composable.
69
+ */
70
+ declare function useHaptics(): UseHapticsReturn;
71
+ interface UseStorageReturn {
72
+ get: (key: string) => Promise<string | null>;
73
+ set: (key: string, value: string) => Promise<void>;
74
+ remove: (key: string) => Promise<void>;
75
+ available: boolean;
76
+ }
77
+ /**
78
+ * Secure storage composable.
79
+ */
80
+ declare function useStorage(): UseStorageReturn;
81
+
82
+ export { useCamera, useGeolocation, useHaptics, usePlatform, usePushNotifications, useStorage };