@umituz/react-native-design-system 4.27.13 → 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 -4
- 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
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repository Key Factory
|
|
3
|
+
*
|
|
4
|
+
* Generic query key factory for repositories.
|
|
5
|
+
* Provides consistent query key structure across all repositories.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { QueryKeyFactory, ListParams } from './types';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Create query key factory for a resource
|
|
12
|
+
*
|
|
13
|
+
* @param resource - Resource name (e.g., 'users', 'posts')
|
|
14
|
+
* @returns Query key factory
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const keys = createRepositoryKeyFactory('users');
|
|
19
|
+
*
|
|
20
|
+
* keys.all(); // ['users']
|
|
21
|
+
* keys.lists(); // ['users', 'list']
|
|
22
|
+
* keys.list({ page: 1 }); // ['users', 'list', { page: 1 }]
|
|
23
|
+
* keys.details(); // ['users', 'detail']
|
|
24
|
+
* keys.detail(123); // ['users', 'detail', 123]
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export function createRepositoryKeyFactory(
|
|
28
|
+
resource: string
|
|
29
|
+
): QueryKeyFactory {
|
|
30
|
+
return {
|
|
31
|
+
all: () => [resource] as const,
|
|
32
|
+
|
|
33
|
+
lists: () => [resource, 'list'] as const,
|
|
34
|
+
|
|
35
|
+
list: (params: ListParams) => [resource, 'list', params] as const,
|
|
36
|
+
|
|
37
|
+
details: () => [resource, 'detail'] as const,
|
|
38
|
+
|
|
39
|
+
detail: (id: string | number) => [resource, 'detail', id] as const,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repository Utilities
|
|
3
|
+
*
|
|
4
|
+
* Common utilities for repository implementations.
|
|
5
|
+
* Provides caching options and helper functions.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { RepositoryOptions, ListParams } from './types';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Default cache options
|
|
12
|
+
*/
|
|
13
|
+
const DEFAULT_CACHE_OPTIONS = {
|
|
14
|
+
staleTime: 5 * 60 * 1000, // 5 minutes
|
|
15
|
+
gcTime: 10 * 60 * 1000, // 10 minutes (formerly cacheTime)
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Merge repository options with defaults
|
|
20
|
+
*
|
|
21
|
+
* @param options - User provided options
|
|
22
|
+
* @returns Merged options
|
|
23
|
+
*/
|
|
24
|
+
export function mergeRepositoryOptions(
|
|
25
|
+
options: RepositoryOptions = {}
|
|
26
|
+
): Required<RepositoryOptions> {
|
|
27
|
+
return {
|
|
28
|
+
cache: {
|
|
29
|
+
staleTime: options.cache?.staleTime ?? DEFAULT_CACHE_OPTIONS.staleTime,
|
|
30
|
+
gcTime: options.cache?.gcTime ?? DEFAULT_CACHE_OPTIONS.gcTime,
|
|
31
|
+
},
|
|
32
|
+
debug: options.debug ?? __DEV__,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Get cache options from repository options
|
|
38
|
+
*
|
|
39
|
+
* @param options - Repository options
|
|
40
|
+
* @returns Cache options with defaults
|
|
41
|
+
*/
|
|
42
|
+
export function getCacheOptions(options: RepositoryOptions): {
|
|
43
|
+
staleTime: number;
|
|
44
|
+
gcTime: number;
|
|
45
|
+
} {
|
|
46
|
+
const merged = mergeRepositoryOptions(options);
|
|
47
|
+
return {
|
|
48
|
+
staleTime: merged.cache.staleTime,
|
|
49
|
+
gcTime: merged.cache.gcTime,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Normalize list parameters
|
|
55
|
+
*
|
|
56
|
+
* @param params - List parameters
|
|
57
|
+
* @returns Normalized parameters
|
|
58
|
+
*/
|
|
59
|
+
export function normalizeListParams(params: ListParams = {}): ListParams {
|
|
60
|
+
return {
|
|
61
|
+
page: params.page ?? 1,
|
|
62
|
+
limit: params.limit ?? 20,
|
|
63
|
+
sort: params.sort ?? 'createdAt',
|
|
64
|
+
filter: params.filter ?? {},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Create debug logger for repository
|
|
70
|
+
*
|
|
71
|
+
* @param resource - Resource name
|
|
72
|
+
* @param enabled - Enable logging
|
|
73
|
+
* @returns Logger function
|
|
74
|
+
*/
|
|
75
|
+
export function createRepositoryLogger(
|
|
76
|
+
resource: string,
|
|
77
|
+
enabled: boolean = __DEV__
|
|
78
|
+
): (method: string, ...args: unknown[]) => void {
|
|
79
|
+
if (!enabled) {
|
|
80
|
+
return () => {};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return (method: string, ...args: unknown[]) => {
|
|
84
|
+
console.log(`[Repository:${resource}] ${method}`, ...args);
|
|
85
|
+
};
|
|
86
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core Repository Domain Types
|
|
3
|
+
*
|
|
4
|
+
* Shared types for repository implementations
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Repository options
|
|
9
|
+
*/
|
|
10
|
+
export interface RepositoryOptions {
|
|
11
|
+
/**
|
|
12
|
+
* Cache configuration
|
|
13
|
+
*/
|
|
14
|
+
cache?: {
|
|
15
|
+
staleTime?: number;
|
|
16
|
+
gcTime?: number;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Enable debug logging
|
|
21
|
+
*/
|
|
22
|
+
debug?: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* List parameters for fetch operations
|
|
27
|
+
*/
|
|
28
|
+
export interface ListParams {
|
|
29
|
+
page?: number;
|
|
30
|
+
limit?: number;
|
|
31
|
+
sort?: string;
|
|
32
|
+
filter?: Record<string, unknown>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Create parameters
|
|
37
|
+
*/
|
|
38
|
+
export interface CreateParams<TVariables> {
|
|
39
|
+
data: TVariables;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Update parameters
|
|
44
|
+
*/
|
|
45
|
+
export interface UpdateParams<TVariables> {
|
|
46
|
+
id: string | number;
|
|
47
|
+
data: TVariables;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Query key factory result
|
|
52
|
+
*/
|
|
53
|
+
export type QueryKeyFactory = {
|
|
54
|
+
all: () => readonly string[];
|
|
55
|
+
lists: () => readonly string[];
|
|
56
|
+
list: (params: ListParams) => readonly string[];
|
|
57
|
+
details: () => readonly string[];
|
|
58
|
+
detail: (id: string | number) => readonly string[];
|
|
59
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core Repositories Module
|
|
3
|
+
*
|
|
4
|
+
* Common utilities and types for repository implementations.
|
|
5
|
+
* TanStack and Storage repositories remain separate but share these utilities.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export { createRepositoryKeyFactory } from './domain/RepositoryKeyFactory';
|
|
9
|
+
export type { QueryKeyFactory } from './domain/types';
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
mergeRepositoryOptions,
|
|
13
|
+
getCacheOptions,
|
|
14
|
+
normalizeListParams,
|
|
15
|
+
createRepositoryLogger,
|
|
16
|
+
} from './domain/RepositoryUtils';
|
|
17
|
+
|
|
18
|
+
export type {
|
|
19
|
+
RepositoryOptions,
|
|
20
|
+
ListParams,
|
|
21
|
+
CreateParams,
|
|
22
|
+
UpdateParams,
|
|
23
|
+
} from './domain/types';
|
|
@@ -18,7 +18,10 @@ const getDeviceModule = (): typeof import('expo-device') | null => {
|
|
|
18
18
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
19
19
|
_deviceModule = require('expo-device') as typeof import('expo-device');
|
|
20
20
|
return _deviceModule;
|
|
21
|
-
} catch {
|
|
21
|
+
} catch (error) {
|
|
22
|
+
if (__DEV__) {
|
|
23
|
+
console.warn('[deviceDetection] expo-device not available:', error);
|
|
24
|
+
}
|
|
22
25
|
return null;
|
|
23
26
|
}
|
|
24
27
|
};
|
|
@@ -35,7 +38,10 @@ export const getScreenDimensions = () => {
|
|
|
35
38
|
try {
|
|
36
39
|
validateScreenDimensions(width, height);
|
|
37
40
|
return { width, height };
|
|
38
|
-
} catch {
|
|
41
|
+
} catch (error) {
|
|
42
|
+
if (__DEV__) {
|
|
43
|
+
console.warn('[deviceDetection] Invalid screen dimensions, using fallback:', error);
|
|
44
|
+
}
|
|
39
45
|
return { width: 414, height: 896 };
|
|
40
46
|
}
|
|
41
47
|
};
|
|
@@ -31,7 +31,10 @@ const getHapticsModule = (): typeof import('expo-haptics') | null => {
|
|
|
31
31
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
32
32
|
_hapticsModule = require('expo-haptics') as typeof import('expo-haptics');
|
|
33
33
|
return _hapticsModule;
|
|
34
|
-
} catch {
|
|
34
|
+
} catch (error) {
|
|
35
|
+
if (__DEV__) {
|
|
36
|
+
console.warn('[HapticService] expo-haptics not available:', error);
|
|
37
|
+
}
|
|
35
38
|
return null;
|
|
36
39
|
}
|
|
37
40
|
};
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Camera Picker Strategy
|
|
3
|
+
*
|
|
4
|
+
* Strategy for capturing images/videos using device camera.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as ImagePicker from 'expo-image-picker';
|
|
8
|
+
import { PermissionManager } from '../../infrastructure/utils/PermissionManager';
|
|
9
|
+
import type { PickerStrategy, LaunchOptions } from './PickerStrategy';
|
|
10
|
+
import type { MediaLibraryPermission } from '../entities/Media';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Camera picker strategy configuration
|
|
14
|
+
*/
|
|
15
|
+
export interface CameraPickerConfig {
|
|
16
|
+
mediaType: 'images' | 'videos';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Camera picker strategy implementation
|
|
21
|
+
*/
|
|
22
|
+
export class CameraPickerStrategy implements PickerStrategy {
|
|
23
|
+
readonly name = 'CameraPicker';
|
|
24
|
+
|
|
25
|
+
constructor(
|
|
26
|
+
private config: CameraPickerConfig,
|
|
27
|
+
private permissionManager: typeof PermissionManager = PermissionManager
|
|
28
|
+
) {}
|
|
29
|
+
|
|
30
|
+
async getPermission(): Promise<MediaLibraryPermission> {
|
|
31
|
+
return this.permissionManager.requestCameraPermission();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async launch(options: LaunchOptions): Promise<any> {
|
|
35
|
+
const mediaTypes =
|
|
36
|
+
this.config.mediaType === 'videos' ? ['videos'] : ['images'];
|
|
37
|
+
|
|
38
|
+
const launchOptions: ImagePicker.ImagePickerOptions = {
|
|
39
|
+
mediaTypes,
|
|
40
|
+
allowsEditing: options.allowsEditing ?? false,
|
|
41
|
+
quality: options.quality ?? 1,
|
|
42
|
+
base64: options.base64 ?? false,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Add aspect ratio for images only
|
|
46
|
+
if (this.config.mediaType === 'images' && options.aspect) {
|
|
47
|
+
launchOptions.aspect = options.aspect;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Add video-specific options
|
|
51
|
+
if (this.config.mediaType === 'videos') {
|
|
52
|
+
if (options.videoMaxDuration !== undefined) {
|
|
53
|
+
launchOptions.videoMaxDuration = options.videoMaxDuration;
|
|
54
|
+
}
|
|
55
|
+
if (options.videoMaxBitrate !== undefined) {
|
|
56
|
+
launchOptions.videoMaxBitrate = options.videoMaxBitrate;
|
|
57
|
+
}
|
|
58
|
+
if (options.videoQuality) {
|
|
59
|
+
launchOptions.videoQuality = options.videoQuality;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return ImagePicker.launchCameraAsync(launchOptions);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Library Picker Strategy
|
|
3
|
+
*
|
|
4
|
+
* Strategy for picking images/videos from device gallery.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as ImagePicker from 'expo-image-picker';
|
|
8
|
+
import { PermissionManager } from '../../infrastructure/utils/PermissionManager';
|
|
9
|
+
import { MediaType } from '../entities/Media';
|
|
10
|
+
import type { PickerStrategy, LaunchOptions } from './PickerStrategy';
|
|
11
|
+
import type { MediaLibraryPermission } from '../entities/Media';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Helper to map media type to ImagePicker format
|
|
15
|
+
*/
|
|
16
|
+
function mapMediaType(mediaType?: string): ImagePicker.MediaType[] {
|
|
17
|
+
if (!mediaType || mediaType === MediaType.ALL) {
|
|
18
|
+
return ['images', 'videos'];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (mediaType === MediaType.VIDEO) {
|
|
22
|
+
return ['videos'];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return ['images'];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Library picker strategy implementation
|
|
30
|
+
*/
|
|
31
|
+
export class LibraryPickerStrategy implements PickerStrategy {
|
|
32
|
+
readonly name = 'LibraryPicker';
|
|
33
|
+
|
|
34
|
+
constructor(
|
|
35
|
+
private permissionManager: typeof PermissionManager = PermissionManager
|
|
36
|
+
) {}
|
|
37
|
+
|
|
38
|
+
async getPermission(): Promise<MediaLibraryPermission> {
|
|
39
|
+
return this.permissionManager.requestMediaLibraryPermission();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async launch(options: LaunchOptions): Promise<any> {
|
|
43
|
+
return ImagePicker.launchImageLibraryAsync({
|
|
44
|
+
mediaTypes: mapMediaType(options.mediaTypes),
|
|
45
|
+
allowsEditing: options.allowsEditing ?? false,
|
|
46
|
+
allowsMultipleSelection: options.allowsMultipleSelection ?? false,
|
|
47
|
+
aspect: options.aspect,
|
|
48
|
+
quality: options.quality ?? 1,
|
|
49
|
+
selectionLimit: options.selectionLimit ?? 1,
|
|
50
|
+
base64: options.base64 ?? false,
|
|
51
|
+
exif: options.exif ?? false,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Media Picker Strategy Interface
|
|
3
|
+
*
|
|
4
|
+
* Strategy pattern for different media picker types.
|
|
5
|
+
* Allows MediaPickerService to support camera, video, and library pickers polymorphically.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { MediaLibraryPermission } from '../entities/Media';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Common picker options
|
|
12
|
+
*/
|
|
13
|
+
export interface LaunchOptions {
|
|
14
|
+
allowsEditing?: boolean;
|
|
15
|
+
quality?: number;
|
|
16
|
+
aspect?: [number, number];
|
|
17
|
+
videoMaxDuration?: number;
|
|
18
|
+
videoMaxBitrate?: number;
|
|
19
|
+
videoQuality?: 'low' | 'medium' | 'high';
|
|
20
|
+
base64?: boolean;
|
|
21
|
+
exif?: boolean;
|
|
22
|
+
allowsMultipleSelection?: boolean;
|
|
23
|
+
selectionLimit?: number;
|
|
24
|
+
mediaTypes?: string;
|
|
25
|
+
maxFileSizeMB?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Result from picker launch
|
|
30
|
+
*/
|
|
31
|
+
export interface PickerLaunchResult {
|
|
32
|
+
canceled: boolean;
|
|
33
|
+
assets?: Array<{
|
|
34
|
+
uri: string;
|
|
35
|
+
width?: number;
|
|
36
|
+
height?: number;
|
|
37
|
+
type?: 'image' | 'video';
|
|
38
|
+
duration?: number;
|
|
39
|
+
fileSize?: number;
|
|
40
|
+
}>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Media picker strategy interface
|
|
45
|
+
*/
|
|
46
|
+
export interface PickerStrategy {
|
|
47
|
+
/**
|
|
48
|
+
* Get required permission for this picker type
|
|
49
|
+
*/
|
|
50
|
+
getPermission(): Promise<MediaLibraryPermission>;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Launch the picker with options
|
|
54
|
+
*/
|
|
55
|
+
launch(options: LaunchOptions): Promise<PickerLaunchResult>;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Strategy name for debugging
|
|
59
|
+
*/
|
|
60
|
+
readonly name: string;
|
|
61
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Media Picker Strategies
|
|
3
|
+
*
|
|
4
|
+
* Strategy pattern implementations for different picker types.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { PickerStrategy } from './PickerStrategy';
|
|
8
|
+
export type { LaunchOptions, PickerLaunchResult } from './PickerStrategy';
|
|
9
|
+
|
|
10
|
+
export { CameraPickerStrategy } from './CameraPickerStrategy';
|
|
11
|
+
export type { CameraPickerConfig } from './CameraPickerStrategy';
|
|
12
|
+
|
|
13
|
+
export { LibraryPickerStrategy } from './LibraryPickerStrategy';
|