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

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 (28) hide show
  1. package/package.json +1 -1
  2. package/src/core/cache/domain/CleanupStrategy.ts +115 -0
  3. package/src/core/cache/domain/UnifiedCache.ts +196 -0
  4. package/src/core/cache/domain/types.ts +18 -0
  5. package/src/core/cache/index.ts +16 -0
  6. package/src/core/cache/infrastructure/CacheFactory.ts +102 -0
  7. package/src/core/index.ts +21 -0
  8. package/src/core/permissions/domain/PermissionHandler.ts +139 -0
  9. package/src/core/permissions/domain/types.ts +49 -0
  10. package/src/core/permissions/index.ts +15 -0
  11. package/src/core/repositories/domain/RepositoryKeyFactory.ts +41 -0
  12. package/src/core/repositories/domain/RepositoryUtils.ts +86 -0
  13. package/src/core/repositories/domain/types.ts +59 -0
  14. package/src/core/repositories/index.ts +23 -0
  15. package/src/device/detection/deviceDetection.ts +8 -2
  16. package/src/haptics/infrastructure/services/HapticService.ts +4 -1
  17. package/src/index.ts +1 -0
  18. package/src/media/domain/strategies/CameraPickerStrategy.ts +65 -0
  19. package/src/media/domain/strategies/LibraryPickerStrategy.ts +54 -0
  20. package/src/media/domain/strategies/PickerStrategy.ts +61 -0
  21. package/src/media/domain/strategies/index.ts +13 -0
  22. package/src/media/infrastructure/services/MediaPickerService.ts +118 -108
  23. package/src/media/infrastructure/utils/PermissionManager.ts +68 -66
  24. package/src/media/presentation/hooks/useMedia.ts +16 -4
  25. package/src/storage/cache/infrastructure/TTLCache.ts +3 -0
  26. package/src/tanstack/domain/repositories/BaseRepository.ts +18 -1
  27. package/src/timezone/infrastructure/utils/SimpleCache.ts +30 -75
  28. package/src/uuid/infrastructure/utils/UUIDUtils.ts +4 -1
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.15",
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",
@@ -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,16 @@
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 { CleanupStrategy, IntervalCleanupStrategy, TimeoutCleanupStrategy } from './domain/CleanupStrategy';
12
+ export type { CleanupType } from './domain/CleanupStrategy';
13
+
14
+ export { CacheFactory } from './infrastructure/CacheFactory';
15
+
16
+ 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,21 @@
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, CacheFactory } from './cache/domain/UnifiedCache';
10
+ export { CleanupStrategy, IntervalCleanupStrategy, TimeoutCleanupStrategy } from './cache/domain/CleanupStrategy';
11
+ export type { UnifiedCacheConfig } from './cache/domain/UnifiedCache';
12
+ export type { CacheEntry, CacheConfig } from './cache/domain/types';
13
+
14
+ // Permissions
15
+ export { PermissionHandler } from './permissions/domain/PermissionHandler';
16
+ export type { PermissionMethod, PermissionStatus, PermissionResult, PermissionHandlerConfig, PermissionMethods } from './permissions/domain/types';
17
+
18
+ // Repositories
19
+ export { createRepositoryKeyFactory } from './repositories/domain/RepositoryKeyFactory';
20
+ export { mergeRepositoryOptions, getCacheOptions, normalizeListParams, createRepositoryLogger } from './repositories/domain/RepositoryUtils';
21
+ 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
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Core Permissions Domain Types
3
+ *
4
+ * Generic types for permission handling
5
+ */
6
+
7
+ export type PermissionMethod = 'request' | 'get';
8
+
9
+ /**
10
+ * Permission status types
11
+ * Apps can extend this for custom permission types
12
+ */
13
+ export type PermissionStatus = 'granted' | 'denied' | 'limited' | 'undetermined';
14
+
15
+ /**
16
+ * Permission result with metadata
17
+ */
18
+ export interface PermissionResult {
19
+ status: PermissionStatus;
20
+ canAskAgain?: boolean;
21
+ }
22
+
23
+ /**
24
+ * Permission handler configuration
25
+ */
26
+ export interface PermissionHandlerConfig<TPermission extends PermissionStatus = PermissionStatus> {
27
+ /**
28
+ * Map permission API methods
29
+ */
30
+ methods: PermissionMethods;
31
+
32
+ /**
33
+ * Map API status to domain permission type
34
+ */
35
+ statusMapper: (apiStatus: string) => TPermission;
36
+
37
+ /**
38
+ * Default status when permission fails
39
+ */
40
+ defaultStatus?: TPermission;
41
+ }
42
+
43
+ /**
44
+ * Permission API methods mapping
45
+ */
46
+ export interface PermissionMethods {
47
+ request: Record<string, () => Promise<PermissionResult>>;
48
+ get: Record<string, () => Promise<PermissionResult>>;
49
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Core Permissions Module
3
+ *
4
+ * Generic permission handling with configurable methods and status mapping.
5
+ */
6
+
7
+ export { PermissionHandler } from './domain/PermissionHandler';
8
+
9
+ export type {
10
+ PermissionMethod,
11
+ PermissionStatus,
12
+ PermissionResult,
13
+ PermissionHandlerConfig,
14
+ PermissionMethods,
15
+ } from './domain/types';