@umituz/react-native-design-system 4.27.14 → 4.27.16

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.
Files changed (59) hide show
  1. package/package.json +11 -6
  2. package/src/atoms/GlassView/GlassView.tsx +0 -2
  3. package/src/core/cache/domain/CleanupStrategy.ts +115 -0
  4. package/src/core/cache/domain/UnifiedCache.ts +196 -0
  5. package/src/core/cache/domain/types.ts +18 -0
  6. package/src/core/cache/index.ts +17 -0
  7. package/src/core/cache/infrastructure/CacheFactory.ts +102 -0
  8. package/src/core/index.ts +23 -0
  9. package/src/core/permissions/domain/PermissionHandler.ts +139 -0
  10. package/src/core/permissions/domain/types.ts +49 -0
  11. package/src/core/permissions/index.ts +15 -0
  12. package/src/core/repositories/domain/RepositoryKeyFactory.ts +41 -0
  13. package/src/core/repositories/domain/RepositoryUtils.ts +86 -0
  14. package/src/core/repositories/domain/types.ts +59 -0
  15. package/src/core/repositories/index.ts +23 -0
  16. package/src/device/detection/deviceDetection.ts +8 -2
  17. package/src/haptics/infrastructure/services/HapticService.ts +4 -1
  18. package/src/hooks/index.ts +22 -25
  19. package/src/index.ts +1 -0
  20. package/src/infinite-scroll/presentation/hooks/useInfiniteScroll.ts +32 -29
  21. package/src/media/domain/strategies/CameraPickerStrategy.ts +61 -0
  22. package/src/media/domain/strategies/LibraryPickerStrategy.ts +54 -0
  23. package/src/media/domain/strategies/PickerStrategy.ts +61 -0
  24. package/src/media/domain/strategies/index.ts +13 -0
  25. package/src/media/infrastructure/services/MediaPickerService.ts +109 -110
  26. package/src/media/infrastructure/utils/PermissionManager.ts +68 -66
  27. package/src/media/infrastructure/utils/mediaPickerMappers.ts +29 -6
  28. package/src/media/presentation/hooks/useMedia.ts +16 -4
  29. package/src/molecules/swipe-actions/domain/entities/SwipeAction.ts +0 -1
  30. package/src/offline/index.ts +1 -1
  31. package/src/offline/presentation/hooks/useOffline.ts +0 -8
  32. package/src/storage/README.md +0 -1
  33. package/src/storage/cache/domain/types/README.md +0 -1
  34. package/src/storage/cache/infrastructure/TTLCache.ts +3 -0
  35. package/src/storage/cache/presentation/README.md +0 -1
  36. package/src/storage/cache/presentation/useCachedValue.ts +12 -2
  37. package/src/storage/domain/constants/README.md +0 -1
  38. package/src/storage/infrastructure/adapters/README.md +0 -1
  39. package/src/storage/presentation/hooks/README.md +0 -6
  40. package/src/storage/presentation/hooks/useStorageState.ts +13 -4
  41. package/src/tanstack/domain/repositories/BaseRepository.ts +18 -1
  42. package/src/theme/hooks/useAppDesignTokens.ts +29 -3
  43. package/src/timezone/infrastructure/services/TimezoneProvider.ts +2 -2
  44. package/src/uuid/infrastructure/utils/UUIDUtils.ts +4 -1
  45. package/src/init/index.ts +0 -29
  46. package/src/layouts/ScreenLayout/index.ts +0 -1
  47. package/src/layouts/index.ts +0 -5
  48. package/src/molecules/SearchBar/index.ts +0 -4
  49. package/src/molecules/StepProgress/index.ts +0 -1
  50. package/src/molecules/alerts/index.ts +0 -47
  51. package/src/molecules/bottom-sheet/index.ts +0 -10
  52. package/src/molecules/circular-menu/index.ts +0 -3
  53. package/src/molecules/filter-group/index.ts +0 -3
  54. package/src/molecules/index.ts +0 -38
  55. package/src/molecules/info-grid/index.ts +0 -3
  56. package/src/molecules/swipe-actions/index.ts +0 -6
  57. package/src/presentation/utils/variants/index.ts +0 -6
  58. package/src/timezone/infrastructure/utils/SimpleCache.ts +0 -109
  59. package/src/utilities/index.ts +0 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-design-system",
3
- "version": "4.27.14",
3
+ "version": "4.27.16",
4
4
  "description": "Universal design system for React Native apps - Consolidated package with atoms, molecules, organisms, theme, typography, responsive, safe area, exception, infinite scroll, UUID, image, timezone, offline, onboarding, and loading utilities - TanStack persistence and expo-image-manipulator now lazy loaded",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./dist/index.d.ts",
@@ -208,13 +208,14 @@
208
208
  "url": "https://github.com/umituz/react-native-design-system"
209
209
  },
210
210
  "peerDependencies": {
211
- "expo": ">=54.0.0",
211
+ "@react-native-community/datetimepicker": ">=8.0.0",
212
212
  "@react-navigation/bottom-tabs": ">=7.0.0",
213
213
  "@react-navigation/native": ">=7.0.0",
214
214
  "@react-navigation/stack": ">=7.0.0",
215
215
  "@tanstack/query-async-storage-persister": ">=5.0.0",
216
216
  "@tanstack/react-query": ">=5.0.0",
217
217
  "@tanstack/react-query-persist-client": ">=5.0.0",
218
+ "expo": ">=54.0.0",
218
219
  "expo-application": ">=5.0.0",
219
220
  "expo-clipboard": ">=8.0.0",
220
221
  "expo-crypto": ">=13.0.0",
@@ -230,10 +231,9 @@
230
231
  "react": ">=19.0.0",
231
232
  "react-native": "*",
232
233
  "react-native-gesture-handler": ">=2.20.0",
234
+ "react-native-keyboard-controller": ">=1.0.0",
233
235
  "react-native-safe-area-context": ">=5.6.2",
234
- "zustand": ">=5.0.0",
235
- "@react-native-community/datetimepicker": ">=8.0.0",
236
- "react-native-keyboard-controller": ">=1.0.0"
236
+ "zustand": ">=5.0.0"
237
237
  },
238
238
  "peerDependenciesMeta": {
239
239
  "react-native-keyboard-controller": {
@@ -298,13 +298,18 @@
298
298
  "@eslint/js": "^9.39.2",
299
299
  "@react-native-async-storage/async-storage": "^2.2.0",
300
300
  "@react-native-community/datetimepicker": "^8.5.1",
301
+ "@react-navigation/bottom-tabs": "^7.15.5",
302
+ "@react-navigation/native": "^7.1.33",
303
+ "@react-navigation/stack": "^7.8.5",
301
304
  "@tanstack/query-async-storage-persister": "^5.66.7",
302
305
  "@tanstack/react-query": "^5.66.7",
303
306
  "@tanstack/react-query-persist-client": "^5.66.7",
304
307
  "@testing-library/react": "^16.3.1",
305
308
  "@testing-library/react-native": "^13.3.3",
306
309
  "@types/jest": "^30.0.0",
307
- "@types/react": "^19.1.0",
310
+ "@types/node": "^25.5.0",
311
+ "@types/react": "^19.2.14",
312
+ "@types/react-native": "^0.72.8",
308
313
  "@typescript-eslint/eslint-plugin": "^8.50.1",
309
314
  "@typescript-eslint/parser": "^8.50.1",
310
315
  "eslint": "^9.39.2",
@@ -1,7 +1,5 @@
1
1
  import React from 'react';
2
2
  import { StyleSheet, ViewStyle, StyleProp, View } from 'react-native';
3
- // Remove expo-blur import to fix native module error
4
- // import { BlurView, BlurTint } from 'expo-blur';
5
3
  import { useDesignSystemTheme } from '../../theme';
6
4
  import { intensityToOpacity } from '../../utils/math';
7
5
 
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Cleanup Strategy
3
+ *
4
+ * Strategy pattern for cache cleanup mechanisms.
5
+ * Allows switching between interval-based and recursive timeout-based cleanup.
6
+ */
7
+
8
+ import type { CleanupType } from './types';
9
+
10
+ /**
11
+ * Cleanup strategy interface for cache expiration
12
+ */
13
+ export interface CleanupStrategy {
14
+ /**
15
+ * Start cleanup mechanism
16
+ * @param callback - Function to call on each cleanup cycle
17
+ */
18
+ start(callback: () => void): void;
19
+
20
+ /**
21
+ * Stop cleanup mechanism and release resources
22
+ */
23
+ stop(): void;
24
+
25
+ /**
26
+ * Strategy type identifier
27
+ */
28
+ readonly type: CleanupType;
29
+ }
30
+
31
+ /**
32
+ * Interval-based cleanup strategy
33
+ * Uses setInterval for periodic cleanup
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * const strategy = new IntervalCleanupStrategy(60000); // Every minute
38
+ * strategy.start(() => cache.cleanup());
39
+ * ```
40
+ */
41
+ export class IntervalCleanupStrategy implements CleanupStrategy {
42
+ private interval: ReturnType<typeof setInterval> | null = null;
43
+
44
+ constructor(private readonly intervalMs: number) {}
45
+
46
+ start(callback: () => void): void {
47
+ if (this.interval) {
48
+ return;
49
+ }
50
+
51
+ this.interval = setInterval(callback, this.intervalMs);
52
+ }
53
+
54
+ stop(): void {
55
+ if (this.interval) {
56
+ clearInterval(this.interval);
57
+ this.interval = null;
58
+ }
59
+ }
60
+
61
+ get type(): 'interval' {
62
+ return 'interval';
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Timeout-based cleanup strategy
68
+ * Uses recursive setTimeout for cleanup
69
+ *
70
+ * @example
71
+ * ```ts
72
+ * const strategy = new TimeoutCleanupStrategy(60000); // Every minute
73
+ * strategy.start(() => cache.cleanup());
74
+ * ```
75
+ */
76
+ export class TimeoutCleanupStrategy implements CleanupStrategy {
77
+ private timeout: ReturnType<typeof setTimeout> | null = null;
78
+ private destroyed = false;
79
+
80
+ constructor(private readonly timeoutMs: number) {}
81
+
82
+ start(callback: () => void): void {
83
+ if (this.destroyed || this.timeout) {
84
+ return;
85
+ }
86
+
87
+ const reschedule = () => {
88
+ if (this.destroyed) {
89
+ return;
90
+ }
91
+
92
+ this.timeout = setTimeout(() => {
93
+ callback();
94
+ if (!this.destroyed) {
95
+ reschedule();
96
+ }
97
+ }, this.timeoutMs);
98
+ };
99
+
100
+ reschedule();
101
+ }
102
+
103
+ stop(): void {
104
+ this.destroyed = true;
105
+
106
+ if (this.timeout) {
107
+ clearTimeout(this.timeout);
108
+ this.timeout = null;
109
+ }
110
+ }
111
+
112
+ get type(): 'timeout' {
113
+ return 'timeout';
114
+ }
115
+ }
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Unified Cache
3
+ *
4
+ * Generic TTL-based cache with pluggable cleanup strategy.
5
+ * Merges functionality from SimpleCache and TTLCache.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * // With interval cleanup (like TTLCache)
10
+ * const cache = new UnifiedCache<string>({
11
+ * defaultTTL: 60000,
12
+ * cleanupStrategy: new IntervalCleanupStrategy(60000)
13
+ * });
14
+ *
15
+ * // With timeout cleanup (like SimpleCache)
16
+ * const cache = new UnifiedCache<string>({
17
+ * defaultTTL: 60000,
18
+ * cleanupStrategy: new TimeoutCleanupStrategy(60000)
19
+ * });
20
+ * ```
21
+ */
22
+
23
+ import type { CacheEntry, CacheConfig } from './types';
24
+ import { CleanupStrategy, IntervalCleanupStrategy } from './CleanupStrategy';
25
+
26
+ export interface UnifiedCacheConfig extends CacheConfig {
27
+ cleanupStrategy?: CleanupStrategy;
28
+ onExpired?: (key: string, count: number) => void;
29
+ }
30
+
31
+ /**
32
+ * Unified generic cache with TTL support and pluggable cleanup
33
+ */
34
+ export class UnifiedCache<T = unknown> {
35
+ protected cache = new Map<string, CacheEntry<T>>();
36
+ protected defaultTTL: number;
37
+ private cleanupStrategy: CleanupStrategy;
38
+ private isDestroyed = false;
39
+ private onExpiredCallback?: (key: string, count: number) => void;
40
+
41
+ constructor(config: UnifiedCacheConfig = {}) {
42
+ this.defaultTTL = config.defaultTTL ?? 60000;
43
+ this.cleanupStrategy = config.cleanupStrategy ?? new IntervalCleanupStrategy(60000);
44
+ this.onExpiredCallback = config.onExpired;
45
+
46
+ this.cleanupStrategy.start(() => this.cleanup());
47
+ }
48
+
49
+ /**
50
+ * Set value with optional TTL override
51
+ */
52
+ set(key: string, value: T, ttl?: number): void {
53
+ if (this.isDestroyed) {
54
+ this.warnDestroyed('set');
55
+ return;
56
+ }
57
+
58
+ const now = Date.now();
59
+ this.cache.set(key, {
60
+ value,
61
+ expires: now + (ttl ?? this.defaultTTL),
62
+ timestamp: now,
63
+ });
64
+ }
65
+
66
+ /**
67
+ * Get value, returns undefined if expired or not found
68
+ */
69
+ get(key: string): T | undefined {
70
+ if (this.isDestroyed) {
71
+ this.warnDestroyed('get');
72
+ return undefined;
73
+ }
74
+
75
+ const entry = this.cache.get(key);
76
+
77
+ if (!entry) {
78
+ return undefined;
79
+ }
80
+
81
+ if (this.isExpired(entry)) {
82
+ this.cache.delete(key);
83
+ return undefined;
84
+ }
85
+
86
+ return entry.value;
87
+ }
88
+
89
+ /**
90
+ * Check if key exists and is not expired
91
+ */
92
+ has(key: string): boolean {
93
+ if (this.isDestroyed) {
94
+ return false;
95
+ }
96
+
97
+ return this.get(key) !== undefined;
98
+ }
99
+
100
+ /**
101
+ * Delete specific key
102
+ */
103
+ delete(key: string): boolean {
104
+ if (this.isDestroyed) {
105
+ return false;
106
+ }
107
+
108
+ return this.cache.delete(key);
109
+ }
110
+
111
+ /**
112
+ * Clear all entries
113
+ */
114
+ clear(): void {
115
+ if (this.isDestroyed) {
116
+ return;
117
+ }
118
+
119
+ this.cache.clear();
120
+ }
121
+
122
+ /**
123
+ * Get all non-expired keys
124
+ */
125
+ keys(): string[] {
126
+ if (this.isDestroyed) {
127
+ return [];
128
+ }
129
+
130
+ return Array.from(this.cache.keys()).filter((key) => {
131
+ const entry = this.cache.get(key);
132
+ return entry && !this.isExpired(entry);
133
+ });
134
+ }
135
+
136
+ /**
137
+ * Get cache size (excluding expired entries)
138
+ */
139
+ size(): number {
140
+ return this.keys().length;
141
+ }
142
+
143
+ /**
144
+ * Destroy cache and stop cleanup
145
+ */
146
+ destroy(): void {
147
+ if (this.isDestroyed) {
148
+ return;
149
+ }
150
+
151
+ this.isDestroyed = true;
152
+ this.cleanupStrategy.stop();
153
+ this.cache.clear();
154
+ }
155
+
156
+ /**
157
+ * Cleanup expired entries
158
+ */
159
+ protected cleanup(): void {
160
+ if (this.isDestroyed) {
161
+ return;
162
+ }
163
+
164
+ const now = Date.now();
165
+ let cleanedCount = 0;
166
+
167
+ for (const [key, entry] of this.cache.entries()) {
168
+ if (now > entry.expires) {
169
+ this.cache.delete(key);
170
+ cleanedCount++;
171
+ }
172
+ }
173
+
174
+ if (cleanedCount > 0 && this.onExpiredCallback) {
175
+ this.onExpiredCallback('cleanup', cleanedCount);
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Check if entry is expired
181
+ */
182
+ private isExpired(entry: CacheEntry<T>): boolean {
183
+ return Date.now() > entry.expires;
184
+ }
185
+
186
+ /**
187
+ * Warn about operation on destroyed cache
188
+ */
189
+ private warnDestroyed(operation: string): void {
190
+ if (__DEV__) {
191
+ console.warn(
192
+ `[UnifiedCache] Cannot perform "${operation}" on destroyed cache`
193
+ );
194
+ }
195
+ }
196
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Core Cache Domain Types
3
+ *
4
+ * Shared types for cache implementations
5
+ */
6
+
7
+ export interface CacheEntry<T> {
8
+ value: T;
9
+ expires: number;
10
+ timestamp: number;
11
+ }
12
+
13
+ export interface CacheConfig {
14
+ defaultTTL?: number;
15
+ cleanupIntervalMs?: number;
16
+ }
17
+
18
+ export type CleanupType = 'interval' | 'timeout';
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Core Cache Module
3
+ *
4
+ * Unified cache implementation with strategy pattern.
5
+ * Replaces SimpleCache and TTLCache with single implementation.
6
+ */
7
+
8
+ export { UnifiedCache } from './domain/UnifiedCache';
9
+ export type { UnifiedCacheConfig } from './domain/UnifiedCache';
10
+
11
+ export type { CleanupStrategy } from './domain/CleanupStrategy';
12
+ export { IntervalCleanupStrategy, TimeoutCleanupStrategy } from './domain/CleanupStrategy';
13
+ export type { CleanupType } from './domain/types';
14
+
15
+ export { CacheFactory } from './infrastructure/CacheFactory';
16
+
17
+ export type { CacheEntry, CacheConfig } from './domain/types';
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Cache Factory
3
+ *
4
+ * Factory for creating cache instances with common configurations.
5
+ * Provides convenient methods for different cache types.
6
+ */
7
+
8
+ import { UnifiedCache } from '../domain/UnifiedCache';
9
+ import { IntervalCleanupStrategy, TimeoutCleanupStrategy } from '../domain/CleanupStrategy';
10
+ import type { UnifiedCacheConfig } from '../domain/UnifiedCache';
11
+
12
+ /**
13
+ * Factory for creating cache instances
14
+ */
15
+ export class CacheFactory {
16
+ /**
17
+ * Create cache with interval-based cleanup (like TTLCache)
18
+ *
19
+ * @param defaultTTL - Default time-to-live in milliseconds
20
+ * @param cleanupInterval - Cleanup interval in milliseconds
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * const cache = CacheFactory.createIntervalCache(60000, 60000);
25
+ * ```
26
+ */
27
+ static createIntervalCache<T = unknown>(
28
+ defaultTTL: number = 60000,
29
+ cleanupInterval: number = 60000
30
+ ): UnifiedCache<T> {
31
+ return new UnifiedCache<T>({
32
+ defaultTTL,
33
+ cleanupStrategy: new IntervalCleanupStrategy(cleanupInterval),
34
+ });
35
+ }
36
+
37
+ /**
38
+ * Create cache with timeout-based cleanup (like SimpleCache)
39
+ *
40
+ * @param defaultTTL - Default time-to-live in milliseconds
41
+ * @param cleanupTimeout - Cleanup timeout in milliseconds
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * const cache = CacheFactory.createTimeoutCache(60000, 60000);
46
+ * ```
47
+ */
48
+ static createTimeoutCache<T = unknown>(
49
+ defaultTTL: number = 60000,
50
+ cleanupTimeout: number = 60000
51
+ ): UnifiedCache<T> {
52
+ return new UnifiedCache<T>({
53
+ defaultTTL,
54
+ cleanupStrategy: new TimeoutCleanupStrategy(cleanupTimeout),
55
+ });
56
+ }
57
+
58
+ /**
59
+ * Create cache with custom configuration
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * const cache = CacheFactory.createCustomCache({
64
+ * defaultTTL: 120000,
65
+ * cleanupStrategy: new IntervalCleanupStrategy(30000),
66
+ * onExpired: (key, count) => console.log(`Expired ${count} items`)
67
+ * });
68
+ * ```
69
+ */
70
+ static createCustomCache<T = unknown>(
71
+ config: UnifiedCacheConfig
72
+ ): UnifiedCache<T> {
73
+ return new UnifiedCache<T>(config);
74
+ }
75
+
76
+ /**
77
+ * Create cache without automatic cleanup (manual cleanup only)
78
+ *
79
+ * @param defaultTTL - Default time-to-live in milliseconds
80
+ *
81
+ * @example
82
+ * ```ts
83
+ * const cache = CacheFactory.createManualCache(60000);
84
+ * // ... later
85
+ * cache.cleanup(); // Manual cleanup
86
+ * ```
87
+ */
88
+ static createManualCache<T = unknown>(
89
+ defaultTTL: number = 60000
90
+ ): UnifiedCache<T> {
91
+ return new UnifiedCache<T>({
92
+ defaultTTL,
93
+ cleanupStrategy: {
94
+ start: () => {},
95
+ stop: () => {},
96
+ get type(): 'interval' | 'timeout' {
97
+ return 'interval';
98
+ },
99
+ },
100
+ });
101
+ }
102
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Core Module
3
+ *
4
+ * Shared abstractions and utilities for the design system.
5
+ * Provides cache, permissions, errors, and repository foundations.
6
+ */
7
+
8
+ // Cache
9
+ export { UnifiedCache } from './cache/domain/UnifiedCache';
10
+ export { CacheFactory } from './cache/infrastructure/CacheFactory';
11
+ export type { CleanupStrategy } from './cache/domain/CleanupStrategy';
12
+ export { IntervalCleanupStrategy, TimeoutCleanupStrategy } from './cache/domain/CleanupStrategy';
13
+ export type { UnifiedCacheConfig } from './cache/domain/UnifiedCache';
14
+ export type { CacheEntry, CacheConfig, CleanupType } from './cache/domain/types';
15
+
16
+ // Permissions
17
+ export { PermissionHandler } from './permissions/domain/PermissionHandler';
18
+ export type { PermissionMethod, PermissionStatus, PermissionResult, PermissionHandlerConfig, PermissionMethods } from './permissions/domain/types';
19
+
20
+ // Repositories
21
+ export { createRepositoryKeyFactory } from './repositories/domain/RepositoryKeyFactory';
22
+ export { mergeRepositoryOptions, getCacheOptions, normalizeListParams, createRepositoryLogger } from './repositories/domain/RepositoryUtils';
23
+ export type { RepositoryOptions, ListParams, CreateParams, UpdateParams, QueryKeyFactory } from './repositories/domain/types';
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Generic Permission Handler
3
+ *
4
+ * Generic permission handling using strategy pattern.
5
+ * Reduces permission method duplication from 4 methods → 1 generic handler.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const handler = new PermissionHandler({
10
+ * methods: {
11
+ * request: {
12
+ * camera: ImagePicker.requestCameraPermissionsAsync,
13
+ * mediaLibrary: ImagePicker.requestMediaLibraryPermissionsAsync,
14
+ * },
15
+ * get: {
16
+ * camera: ImagePicker.getCameraPermissionsAsync,
17
+ * mediaLibrary: ImagePicker.getMediaLibraryPermissionsAsync,
18
+ * },
19
+ * },
20
+ * statusMapper: mapPermissionStatus,
21
+ * defaultStatus: MediaLibraryPermission.DENIED,
22
+ * });
23
+ *
24
+ * // Request permission
25
+ * const status = await handler.handle('request', 'camera');
26
+ *
27
+ * // Check if granted
28
+ * if (handler.isGranted(status)) {
29
+ * // Proceed with operation
30
+ * }
31
+ * ```
32
+ */
33
+
34
+ import type {
35
+ PermissionMethod,
36
+ PermissionStatus,
37
+ PermissionHandlerConfig,
38
+ PermissionMethods,
39
+ } from './types';
40
+
41
+ /**
42
+ * Generic permission handler with configurable methods and status mapping
43
+ */
44
+ export class PermissionHandler<TPermission extends PermissionStatus = PermissionStatus> {
45
+ private methods: PermissionMethods;
46
+ private statusMapper: (apiStatus: string) => TPermission;
47
+ private defaultStatus: TPermission;
48
+
49
+ constructor(config: PermissionHandlerConfig<TPermission>) {
50
+ this.methods = config.methods;
51
+ this.statusMapper = config.statusMapper;
52
+ this.defaultStatus = config.defaultStatus ?? ('denied' as TPermission);
53
+ }
54
+
55
+ /**
56
+ * Handle permission request or status check
57
+ *
58
+ * @param method - 'request' or 'get'
59
+ * @param type - Permission type key (e.g., 'camera', 'mediaLibrary')
60
+ * @returns Permission status
61
+ */
62
+ async handle(
63
+ method: PermissionMethod,
64
+ type: string
65
+ ): Promise<TPermission> {
66
+ const permissionFn = this.methods[method]?.[type];
67
+
68
+ if (!permissionFn) {
69
+ if (__DEV__) {
70
+ console.warn(
71
+ `[PermissionHandler] No ${method} method found for permission type: ${type}`
72
+ );
73
+ }
74
+ return this.defaultStatus;
75
+ }
76
+
77
+ try {
78
+ const result = await permissionFn();
79
+ return this.statusMapper(result.status);
80
+ } catch (error) {
81
+ if (__DEV__) {
82
+ console.warn(
83
+ `[PermissionHandler] Failed to ${method} ${type} permission:`,
84
+ error
85
+ );
86
+ }
87
+ return this.defaultStatus;
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Check if permission status is granted
93
+ *
94
+ * @param status - Permission status to check
95
+ * @returns true if permission is granted or limited
96
+ */
97
+ isGranted(status: TPermission): boolean {
98
+ return (
99
+ status === 'granted' ||
100
+ status === 'limited' ||
101
+ (status as string) === 'granted' ||
102
+ (status as string) === 'limited'
103
+ );
104
+ }
105
+
106
+ /**
107
+ * Check if permission status is denied
108
+ *
109
+ * @param status - Permission status to check
110
+ * @returns true if permission is denied
111
+ */
112
+ isDenied(status: TPermission): boolean {
113
+ return !this.isGranted(status);
114
+ }
115
+
116
+ /**
117
+ * Request permission by type
118
+ *
119
+ * @example
120
+ * ```ts
121
+ * const status = await handler.request('camera');
122
+ * ```
123
+ */
124
+ async request(type: string): Promise<TPermission> {
125
+ return this.handle('request', type);
126
+ }
127
+
128
+ /**
129
+ * Get permission status by type
130
+ *
131
+ * @example
132
+ * ```ts
133
+ * const status = await handler.getStatus('camera');
134
+ * ```
135
+ */
136
+ async getStatus(type: string): Promise<TPermission> {
137
+ return this.handle('get', type);
138
+ }
139
+ }