@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.
- package/package.json +1 -1
- package/src/core/cache/domain/CleanupStrategy.ts +115 -0
- package/src/core/cache/domain/UnifiedCache.ts +196 -0
- package/src/core/cache/domain/types.ts +18 -0
- package/src/core/cache/index.ts +16 -0
- package/src/core/cache/infrastructure/CacheFactory.ts +102 -0
- package/src/core/index.ts +21 -0
- package/src/core/permissions/domain/PermissionHandler.ts +139 -0
- package/src/core/permissions/domain/types.ts +49 -0
- package/src/core/permissions/index.ts +15 -0
- package/src/core/repositories/domain/RepositoryKeyFactory.ts +41 -0
- package/src/core/repositories/domain/RepositoryUtils.ts +86 -0
- package/src/core/repositories/domain/types.ts +59 -0
- package/src/core/repositories/index.ts +23 -0
- package/src/device/detection/deviceDetection.ts +8 -2
- package/src/haptics/infrastructure/services/HapticService.ts +4 -1
- package/src/index.ts +1 -0
- package/src/media/domain/strategies/CameraPickerStrategy.ts +65 -0
- package/src/media/domain/strategies/LibraryPickerStrategy.ts +54 -0
- package/src/media/domain/strategies/PickerStrategy.ts +61 -0
- package/src/media/domain/strategies/index.ts +13 -0
- package/src/media/infrastructure/services/MediaPickerService.ts +118 -108
- package/src/media/infrastructure/utils/PermissionManager.ts +68 -66
- package/src/media/presentation/hooks/useMedia.ts +16 -4
- package/src/storage/cache/infrastructure/TTLCache.ts +3 -0
- package/src/tanstack/domain/repositories/BaseRepository.ts +18 -1
- package/src/timezone/infrastructure/utils/SimpleCache.ts +30 -75
- 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.
|
|
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';
|