@umituz/react-native-design-system 4.23.116 → 4.23.118

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 (31) hide show
  1. package/package.json +1 -1
  2. package/src/device/presentation/hooks/useAnonymousUser.ts +25 -31
  3. package/src/device/presentation/hooks/useDeviceInfo.ts +47 -91
  4. package/src/init/useAppInitialization.ts +24 -57
  5. package/src/media/domain/entities/Media.ts +1 -0
  6. package/src/media/infrastructure/utils/media-collection-utils.ts +9 -6
  7. package/src/media/presentation/hooks/useMedia.ts +73 -128
  8. package/src/molecules/alerts/AlertBanner.tsx +24 -46
  9. package/src/molecules/alerts/AlertInline.tsx +8 -9
  10. package/src/molecules/alerts/AlertModal.tsx +23 -14
  11. package/src/molecules/alerts/AlertToast.tsx +25 -53
  12. package/src/molecules/alerts/components/AlertContent.tsx +79 -0
  13. package/src/molecules/alerts/components/AlertIcon.tsx +31 -0
  14. package/src/molecules/alerts/components/index.ts +6 -0
  15. package/src/molecules/alerts/hooks/index.ts +6 -0
  16. package/src/molecules/alerts/hooks/useAlertAutoDismiss.ts +26 -0
  17. package/src/molecules/alerts/hooks/useAlertDismissHandler.ts +21 -0
  18. package/src/molecules/alerts/utils/alertUtils.ts +0 -21
  19. package/src/storage/cache/presentation/useCachedValue.ts +24 -65
  20. package/src/storage/presentation/hooks/useStorageState.ts +20 -29
  21. package/src/utilities/sharing/presentation/hooks/useSharing.ts +75 -140
  22. package/src/utils/errors/DesignSystemError.ts +57 -1
  23. package/src/utils/errors/ErrorHandler.ts +105 -1
  24. package/src/utils/errors/adapters/CacheErrorAdapter.ts +68 -0
  25. package/src/utils/errors/adapters/ImageErrorAdapter.ts +91 -0
  26. package/src/utils/errors/adapters/StorageErrorAdapter.ts +107 -0
  27. package/src/utils/errors/index.ts +5 -1
  28. package/src/utils/errors/types/Result.ts +64 -0
  29. package/src/utils/hooks/index.ts +12 -0
  30. package/src/utils/hooks/types/AsyncOperationTypes.ts +75 -0
  31. package/src/utils/hooks/useAsyncOperation.ts +223 -0
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Cache Error Adapter
3
+ *
4
+ * Adapts cache errors to unified DesignSystemError format.
5
+ * Maintains backward compatibility with CacheError.
6
+ */
7
+
8
+ import { DesignSystemError, ErrorCodes, ErrorCategory } from '../DesignSystemError';
9
+ import type { ErrorMetadata } from '../DesignSystemError';
10
+ import { CacheError } from '../../../storage/cache/domain/ErrorHandler';
11
+
12
+ export class CacheErrorAdapter {
13
+ /**
14
+ * Create a DesignSystemError for cache operations
15
+ */
16
+ static create(message: string, context: string, cause?: unknown): DesignSystemError {
17
+ const metadata: ErrorMetadata = {
18
+ category: ErrorCategory.CACHE,
19
+ operation: context,
20
+ cause,
21
+ retryable: true,
22
+ };
23
+
24
+ return new DesignSystemError(
25
+ `${context}: ${message}`,
26
+ ErrorCodes.CACHE_ERROR,
27
+ { context },
28
+ metadata
29
+ );
30
+ }
31
+
32
+ /**
33
+ * Convert legacy CacheError to DesignSystemError
34
+ */
35
+ static fromCacheError(error: CacheError): DesignSystemError {
36
+ return new DesignSystemError(
37
+ error.message,
38
+ ErrorCodes.CACHE_ERROR,
39
+ { cacheErrorCode: error.code },
40
+ {
41
+ category: ErrorCategory.CACHE,
42
+ retryable: true,
43
+ }
44
+ );
45
+ }
46
+
47
+ /**
48
+ * Handle with timeout (replaces cache ErrorHandler.withTimeout)
49
+ */
50
+ static async withTimeout<T>(
51
+ promise: Promise<T>,
52
+ timeoutMs: number,
53
+ context: string
54
+ ): Promise<T> {
55
+ const timeoutPromise = new Promise<never>((_, reject) => {
56
+ setTimeout(() => {
57
+ reject(
58
+ this.create(
59
+ `Operation timed out after ${timeoutMs}ms`,
60
+ context
61
+ )
62
+ );
63
+ }, timeoutMs);
64
+ });
65
+
66
+ return Promise.race([promise, timeoutPromise]);
67
+ }
68
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Image Error Adapter
3
+ *
4
+ * Adapts image errors to unified DesignSystemError format.
5
+ * Maintains backward compatibility with ImageError.
6
+ */
7
+
8
+ import { DesignSystemError, ErrorCodes, ErrorCategory } from '../DesignSystemError';
9
+ import type { ErrorMetadata } from '../DesignSystemError';
10
+ import {
11
+ ImageError,
12
+ IMAGE_ERROR_CODES,
13
+ type ImageErrorCode,
14
+ } from '../../../image/infrastructure/utils/ImageErrorHandler';
15
+
16
+ export class ImageErrorAdapter {
17
+ /**
18
+ * Map ImageErrorCode to DesignSystemError code
19
+ */
20
+ private static mapErrorCode(code: ImageErrorCode): string {
21
+ switch (code) {
22
+ case IMAGE_ERROR_CODES.INVALID_URI:
23
+ case IMAGE_ERROR_CODES.INVALID_DIMENSIONS:
24
+ case IMAGE_ERROR_CODES.INVALID_QUALITY:
25
+ case IMAGE_ERROR_CODES.VALIDATION_ERROR:
26
+ return ErrorCodes.VALIDATION_ERROR;
27
+
28
+ case IMAGE_ERROR_CODES.MANIPULATION_FAILED:
29
+ case IMAGE_ERROR_CODES.CONVERSION_FAILED:
30
+ return ErrorCodes.IMAGE_LOAD_ERROR;
31
+
32
+ case IMAGE_ERROR_CODES.STORAGE_FAILED:
33
+ return ErrorCodes.STORAGE_ERROR;
34
+
35
+ default:
36
+ return ErrorCodes.UNKNOWN_ERROR;
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Create a DesignSystemError for image operations
42
+ */
43
+ static create(
44
+ message: string,
45
+ code: ImageErrorCode,
46
+ operation?: string
47
+ ): DesignSystemError {
48
+ const metadata: ErrorMetadata = {
49
+ category: ErrorCategory.IMAGE,
50
+ operation,
51
+ retryable: code === IMAGE_ERROR_CODES.MANIPULATION_FAILED,
52
+ };
53
+
54
+ return new DesignSystemError(
55
+ message,
56
+ this.mapErrorCode(code),
57
+ { imageErrorCode: code, operation },
58
+ metadata
59
+ );
60
+ }
61
+
62
+ /**
63
+ * Convert legacy ImageError to DesignSystemError
64
+ */
65
+ static fromImageError(error: ImageError): DesignSystemError {
66
+ return this.create(error.message, error.code as ImageErrorCode, error.operation);
67
+ }
68
+
69
+ /**
70
+ * Handle unknown image errors
71
+ */
72
+ static handleUnknown(error: unknown, operation?: string): DesignSystemError {
73
+ if (error instanceof ImageError) {
74
+ return this.fromImageError(error);
75
+ }
76
+
77
+ const message = error instanceof Error ? error.message : 'Unknown image error occurred';
78
+
79
+ return new DesignSystemError(
80
+ message,
81
+ ErrorCodes.IMAGE_LOAD_ERROR,
82
+ { operation },
83
+ {
84
+ category: ErrorCategory.IMAGE,
85
+ operation,
86
+ cause: error,
87
+ retryable: true,
88
+ }
89
+ );
90
+ }
91
+ }
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Storage Error Adapter
3
+ *
4
+ * Adapts storage errors to unified DesignSystemError format.
5
+ * Maintains backward compatibility with StorageError hierarchy.
6
+ */
7
+
8
+ import { DesignSystemError, ErrorCodes, ErrorCategory } from '../DesignSystemError';
9
+ import type { ErrorMetadata } from '../DesignSystemError';
10
+ import {
11
+ StorageError,
12
+ StorageReadError,
13
+ StorageWriteError,
14
+ StorageDeleteError,
15
+ StorageSerializationError,
16
+ StorageDeserializationError,
17
+ } from '../../../storage/domain/errors/StorageError';
18
+
19
+ export class StorageErrorAdapter {
20
+ /**
21
+ * Create a DesignSystemError for storage read failures
22
+ */
23
+ static readError(key: string, cause?: unknown): DesignSystemError {
24
+ const metadata: ErrorMetadata = {
25
+ category: ErrorCategory.STORAGE,
26
+ operation: 'read',
27
+ key,
28
+ cause,
29
+ retryable: true,
30
+ };
31
+
32
+ return new DesignSystemError(
33
+ `Failed to read from storage: ${key}`,
34
+ ErrorCodes.STORAGE_ERROR,
35
+ { key },
36
+ metadata
37
+ );
38
+ }
39
+
40
+ /**
41
+ * Create a DesignSystemError for storage write failures
42
+ */
43
+ static writeError(key: string, cause?: unknown): DesignSystemError {
44
+ const metadata: ErrorMetadata = {
45
+ category: ErrorCategory.STORAGE,
46
+ operation: 'write',
47
+ key,
48
+ cause,
49
+ retryable: true,
50
+ };
51
+
52
+ return new DesignSystemError(
53
+ `Failed to write to storage: ${key}`,
54
+ ErrorCodes.STORAGE_ERROR,
55
+ { key },
56
+ metadata
57
+ );
58
+ }
59
+
60
+ /**
61
+ * Create a DesignSystemError for storage delete failures
62
+ */
63
+ static deleteError(key: string, cause?: unknown): DesignSystemError {
64
+ const metadata: ErrorMetadata = {
65
+ category: ErrorCategory.STORAGE,
66
+ operation: 'delete',
67
+ key,
68
+ cause,
69
+ retryable: true,
70
+ };
71
+
72
+ return new DesignSystemError(
73
+ `Failed to delete from storage: ${key}`,
74
+ ErrorCodes.STORAGE_ERROR,
75
+ { key },
76
+ metadata
77
+ );
78
+ }
79
+
80
+ /**
81
+ * Convert legacy StorageError to DesignSystemError
82
+ */
83
+ static fromStorageError(error: StorageError): DesignSystemError {
84
+ let operation = 'unknown';
85
+
86
+ if (error instanceof StorageReadError) operation = 'read';
87
+ else if (error instanceof StorageWriteError) operation = 'write';
88
+ else if (error instanceof StorageDeleteError) operation = 'delete';
89
+ else if (error instanceof StorageSerializationError) operation = 'serialize';
90
+ else if (error instanceof StorageDeserializationError) operation = 'deserialize';
91
+
92
+ const cause = 'cause' in error ? error.cause : undefined;
93
+
94
+ return new DesignSystemError(
95
+ error.message,
96
+ ErrorCodes.STORAGE_ERROR,
97
+ { key: error.key },
98
+ {
99
+ category: ErrorCategory.STORAGE,
100
+ operation,
101
+ key: error.key,
102
+ cause,
103
+ retryable: true,
104
+ }
105
+ );
106
+ }
107
+ }
@@ -3,5 +3,9 @@
3
3
  * Unified error handling for the design system
4
4
  */
5
5
 
6
- export { DesignSystemError, ErrorCodes, type ErrorCode } from './DesignSystemError';
6
+ export { DesignSystemError, ErrorCodes, ErrorCategory, type ErrorCode, type ErrorMetadata } from './DesignSystemError';
7
7
  export { ErrorHandler } from './ErrorHandler';
8
+
9
+ // Result type for explicit error handling
10
+ export type { Result } from './types/Result';
11
+ export { ok, err, unwrap, unwrapOr, map, mapError } from './types/Result';
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Result Type
3
+ *
4
+ * Rust/Go-inspired Result type for explicit error handling.
5
+ * Alternative to throwing errors or returning tuples.
6
+ */
7
+
8
+ export type Result<T, E = Error> =
9
+ | { success: true; value: T; error: null }
10
+ | { success: false; value: null; error: E };
11
+
12
+ /**
13
+ * Create a successful result
14
+ */
15
+ export function ok<T, E = Error>(value: T): Result<T, E> {
16
+ return { success: true, value, error: null };
17
+ }
18
+
19
+ /**
20
+ * Create an error result
21
+ */
22
+ export function err<T, E = Error>(error: E): Result<T, E> {
23
+ return { success: false, value: null, error };
24
+ }
25
+
26
+ /**
27
+ * Unwrap result value or throw error
28
+ */
29
+ export function unwrap<T, E = Error>(result: Result<T, E>): T {
30
+ if (result.success) {
31
+ return result.value;
32
+ }
33
+ throw result.error;
34
+ }
35
+
36
+ /**
37
+ * Unwrap result value or return default
38
+ */
39
+ export function unwrapOr<T, E = Error>(result: Result<T, E>, defaultValue: T): T {
40
+ return result.success ? result.value : defaultValue;
41
+ }
42
+
43
+ /**
44
+ * Map result value if successful
45
+ */
46
+ export function map<T, U, E = Error>(
47
+ result: Result<T, E>,
48
+ fn: (value: T) => U
49
+ ): Result<U, E> {
50
+ if (result.success) {
51
+ return ok(fn(result.value));
52
+ }
53
+ return { success: false, value: null, error: result.error };
54
+ }
55
+
56
+ /**
57
+ * Map result error if failed
58
+ */
59
+ export function mapError<T, E, F>(
60
+ result: Result<T, E>,
61
+ fn: (error: E) => F
62
+ ): Result<T, F> {
63
+ return result.success ? result : err(fn(result.error));
64
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Shared Hooks Barrel Export
3
+ */
4
+
5
+ export { useAsyncOperation } from './useAsyncOperation';
6
+ export type {
7
+ AsyncOperationOptions,
8
+ AsyncOperationState,
9
+ AsyncOperationActions,
10
+ AsyncOperationReturn,
11
+ ErrorHandler,
12
+ } from './types/AsyncOperationTypes';
@@ -0,0 +1,75 @@
1
+ /**
2
+ * AsyncOperation Types
3
+ * Type definitions for useAsyncOperation hook
4
+ */
5
+
6
+ export type ErrorHandler<E = Error> = (error: unknown) => E;
7
+
8
+ export interface AsyncOperationOptions<T, E = Error> {
9
+ /** Skip execution (useful for conditional operations) */
10
+ skip?: boolean;
11
+
12
+ /** Execute immediately on mount */
13
+ immediate?: boolean;
14
+
15
+ /** Initial data value */
16
+ initialData?: T | null;
17
+
18
+ /** Convert error to custom type */
19
+ errorHandler?: ErrorHandler<E>;
20
+
21
+ /** Callback on success */
22
+ onSuccess?: (data: T) => void;
23
+
24
+ /** Callback on error */
25
+ onError?: (error: E) => void;
26
+
27
+ /** Callback on finally (success or error) */
28
+ onFinally?: () => void;
29
+
30
+ /** Enable retry functionality */
31
+ enableRetry?: boolean;
32
+
33
+ /** Maximum retry attempts */
34
+ maxRetries?: number;
35
+ }
36
+
37
+ export interface AsyncOperationState<T, E = Error> {
38
+ /** Current data value */
39
+ data: T | null;
40
+
41
+ /** Loading state */
42
+ isLoading: boolean;
43
+
44
+ /** Error state */
45
+ error: E | null;
46
+
47
+ /** Is operation idle (not executed yet) */
48
+ isIdle: boolean;
49
+
50
+ /** Is operation successful */
51
+ isSuccess: boolean;
52
+
53
+ /** Is operation in error state */
54
+ isError: boolean;
55
+ }
56
+
57
+ export interface AsyncOperationActions<T, E = Error> {
58
+ /** Execute the async operation */
59
+ execute: (...args: any[]) => Promise<T | null>;
60
+
61
+ /** Retry the last operation */
62
+ retry: () => Promise<T | null>;
63
+
64
+ /** Reset to initial state */
65
+ reset: () => void;
66
+
67
+ /** Set data manually */
68
+ setData: (data: T | null) => void;
69
+
70
+ /** Set error manually */
71
+ setError: (error: E | null) => void;
72
+ }
73
+
74
+ export type AsyncOperationReturn<T, E = Error> =
75
+ AsyncOperationState<T, E> & AsyncOperationActions<T, E>;
@@ -0,0 +1,223 @@
1
+ /**
2
+ * useAsyncOperation Hook
3
+ *
4
+ * Eliminates duplicate async error handling patterns across hooks.
5
+ * Handles isMountedRef + try-catch-finally + loading/error state management.
6
+ *
7
+ * Based on the useAsyncData pattern from useDeviceInfo.ts but generalized
8
+ * for broader use cases.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * // Simple usage (like useAsyncData)
13
+ * const { data, isLoading, error, execute } = useAsyncOperation(
14
+ * async () => await fetchData(),
15
+ * { immediate: true }
16
+ * );
17
+ *
18
+ * // Manual execution (like useMedia)
19
+ * const { execute, isLoading, error } = useAsyncOperation(
20
+ * async (uri: string) => await pickImage(uri),
21
+ * { immediate: false }
22
+ * );
23
+ * const result = await execute('file://...');
24
+ *
25
+ * // With custom error handling
26
+ * const { data, error } = useAsyncOperation(
27
+ * async () => await riskyOperation(),
28
+ * {
29
+ * errorHandler: (err) => err instanceof Error ? err.message : 'Failed',
30
+ * onError: (err) => console.error('Operation failed:', err),
31
+ * }
32
+ * );
33
+ * ```
34
+ */
35
+
36
+ import { useState, useCallback, useEffect, useRef, useMemo } from 'react';
37
+ import type {
38
+ AsyncOperationOptions,
39
+ AsyncOperationReturn,
40
+ ErrorHandler,
41
+ } from './types/AsyncOperationTypes';
42
+
43
+ const defaultErrorHandler: ErrorHandler<Error> = (error: unknown): Error => {
44
+ if (error instanceof Error) return error;
45
+ return new Error(String(error));
46
+ };
47
+
48
+ export function useAsyncOperation<T, E = Error>(
49
+ operation: (...args: any[]) => Promise<T>,
50
+ options: AsyncOperationOptions<T, E> = {}
51
+ ): AsyncOperationReturn<T, E> {
52
+ const {
53
+ skip = false,
54
+ immediate = false,
55
+ initialData = null,
56
+ errorHandler = defaultErrorHandler as ErrorHandler<E>,
57
+ onSuccess,
58
+ onError,
59
+ onFinally,
60
+ enableRetry = false,
61
+ maxRetries = 3,
62
+ } = options;
63
+
64
+ // State
65
+ const [data, setData] = useState<T | null>(initialData);
66
+ const [isLoading, setIsLoading] = useState(false);
67
+ const [error, setErrorState] = useState<E | null>(null);
68
+ const [isIdle, setIsIdle] = useState(true);
69
+
70
+ // Refs for cleanup and retry
71
+ const isMountedRef = useRef(true);
72
+ const lastArgsRef = useRef<any[]>([]);
73
+ const retryCountRef = useRef(0);
74
+
75
+ // Stable callback refs
76
+ const onSuccessRef = useRef(onSuccess);
77
+ const onErrorRef = useRef(onError);
78
+ const onFinallyRef = useRef(onFinally);
79
+
80
+ useEffect(() => {
81
+ onSuccessRef.current = onSuccess;
82
+ onErrorRef.current = onError;
83
+ onFinallyRef.current = onFinally;
84
+ }, [onSuccess, onError, onFinally]);
85
+
86
+ // Cleanup on unmount
87
+ useEffect(() => {
88
+ return () => {
89
+ isMountedRef.current = false;
90
+ };
91
+ }, []);
92
+
93
+ const execute = useCallback(
94
+ async (...args: any[]): Promise<T | null> => {
95
+ if (!isMountedRef.current || skip) return null;
96
+
97
+ // Store args for retry
98
+ lastArgsRef.current = args;
99
+
100
+ if (isMountedRef.current) {
101
+ setIsLoading(true);
102
+ setErrorState(null);
103
+ setIsIdle(false);
104
+ }
105
+
106
+ try {
107
+ const result = await operation(...args);
108
+
109
+ if (isMountedRef.current) {
110
+ setData(result);
111
+ onSuccessRef.current?.(result);
112
+ retryCountRef.current = 0; // Reset retry count on success
113
+ }
114
+
115
+ return result;
116
+ } catch (err) {
117
+ const handledError = errorHandler(err);
118
+
119
+ if (isMountedRef.current) {
120
+ setErrorState(handledError);
121
+ onErrorRef.current?.(handledError);
122
+ }
123
+
124
+ return null;
125
+ } finally {
126
+ if (isMountedRef.current) {
127
+ setIsLoading(false);
128
+ onFinallyRef.current?.();
129
+ }
130
+ }
131
+ },
132
+ [operation, skip, errorHandler]
133
+ );
134
+
135
+ const retry = useCallback(async (): Promise<T | null> => {
136
+ if (!enableRetry) {
137
+ if (__DEV__) {
138
+ console.warn('Retry is not enabled for this operation');
139
+ }
140
+ return null;
141
+ }
142
+
143
+ if (retryCountRef.current >= maxRetries) {
144
+ if (__DEV__) {
145
+ console.warn(`Max retries (${maxRetries}) reached`);
146
+ }
147
+ return null;
148
+ }
149
+
150
+ retryCountRef.current++;
151
+ return execute(...lastArgsRef.current);
152
+ }, [execute, enableRetry, maxRetries]);
153
+
154
+ const reset = useCallback(() => {
155
+ if (isMountedRef.current) {
156
+ setData(initialData);
157
+ setErrorState(null);
158
+ setIsLoading(false);
159
+ setIsIdle(true);
160
+ retryCountRef.current = 0;
161
+ lastArgsRef.current = [];
162
+ }
163
+ }, [initialData]);
164
+
165
+ const setDataManual = useCallback((newData: T | null) => {
166
+ if (isMountedRef.current) {
167
+ setData(newData);
168
+ setErrorState(null);
169
+ setIsIdle(false);
170
+ }
171
+ }, []);
172
+
173
+ const setErrorManual = useCallback((newError: E | null) => {
174
+ if (isMountedRef.current) {
175
+ setErrorState(newError);
176
+ setIsIdle(false);
177
+ }
178
+ }, []);
179
+
180
+ // Auto-execute on mount if immediate
181
+ useEffect(() => {
182
+ if (immediate && !skip) {
183
+ execute();
184
+ }
185
+ // eslint-disable-next-line react-hooks/exhaustive-deps
186
+ }, [immediate, skip]);
187
+
188
+ // Derived state
189
+ const isSuccess = !isIdle && !isLoading && error === null && data !== null;
190
+ const isError = !isIdle && !isLoading && error !== null;
191
+
192
+ return useMemo(
193
+ () => ({
194
+ // State
195
+ data,
196
+ isLoading,
197
+ error,
198
+ isIdle,
199
+ isSuccess,
200
+ isError,
201
+
202
+ // Actions
203
+ execute,
204
+ retry,
205
+ reset,
206
+ setData: setDataManual,
207
+ setError: setErrorManual,
208
+ }),
209
+ [
210
+ data,
211
+ isLoading,
212
+ error,
213
+ isIdle,
214
+ isSuccess,
215
+ isError,
216
+ execute,
217
+ retry,
218
+ reset,
219
+ setDataManual,
220
+ setErrorManual,
221
+ ]
222
+ );
223
+ }