@umituz/react-native-settings 5.2.19 → 5.2.21
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/domains/about/presentation/hooks/useAboutInfo.ts +63 -98
- package/src/domains/feedback/presentation/components/SupportSection.tsx +2 -1
- package/src/domains/localization/infrastructure/hooks/useTranslation.ts +0 -3
- package/src/domains/localization/infrastructure/storage/LanguageInitializer.ts +0 -4
- package/src/domains/localization/infrastructure/storage/localizationStoreUtils.ts +6 -7
- package/src/domains/localization/presentation/components/LanguageItem.tsx +0 -2
- package/src/domains/localization/presentation/providers/LocalizationManager.tsx +2 -1
- package/src/domains/notifications/index.ts +7 -6
- package/src/domains/notifications/infrastructure/services/NotificationBadgeManager.ts +1 -1
- package/src/domains/notifications/infrastructure/services/NotificationManager.ts +1 -1
- package/src/domains/notifications/infrastructure/services/NotificationPermissions.ts +1 -1
- package/src/domains/notifications/infrastructure/storage/UnifiedNotificationStore.ts +223 -0
- package/src/domains/notifications/presentation/hooks/useNotificationSettingsUI.ts +2 -2
- package/src/domains/notifications/presentation/screens/NotificationSettingsScreen.tsx +1 -1
- package/src/domains/notifications/quietHours/infrastructure/hooks/useQuietHoursActions.ts +6 -6
- package/src/domains/notifications/reminders/infrastructure/hooks/useReminderActions.ts +5 -5
- package/src/domains/notifications/reminders/presentation/screens/ReminderListScreen.tsx +1 -1
- package/src/domains/rating/presentation/hooks/useAppRating.tsx +2 -1
- package/src/infrastructure/storage/storeConfig.ts +114 -0
- package/src/infrastructure/utils/async/core.ts +3 -2
- package/src/infrastructure/utils/errorHandlers.ts +4 -2
- package/src/infrastructure/utils/index.ts +1 -1
- package/src/presentation/components/SettingsNavigationItem.tsx +109 -0
- package/src/presentation/navigation/utils/index.ts +8 -1
- package/src/presentation/navigation/utils/navigationHelpers.ts +118 -0
- package/src/presentation/screens/components/SettingsContent.tsx +21 -11
- package/src/presentation/screens/components/sections/SupportSettingsSection.tsx +4 -2
- package/src/presentation/screens/types/UserFeatureConfig.ts +5 -4
- package/src/presentation/utils/config-creators/feature-configs.ts +3 -2
- package/src/presentation/utils/settingsConfigFactory.ts +2 -1
- package/src/utils/appUtils.ts +0 -6
- package/src/utils/errorUtils.ts +54 -0
- package/src/utils/hooks/index.ts +6 -0
- package/src/utils/hooks/useAsyncStateUpdate.ts +114 -0
- package/src/utils/hooks/useMountSafety.ts +30 -0
- package/src/utils/index.ts +2 -0
- package/src/domains/about/presentation/hooks/useAboutInfo.utils.ts +0 -167
- package/src/domains/notifications/infrastructure/storage/NotificationsStore.ts +0 -45
- package/src/domains/notifications/infrastructure/utils/dev.ts +0 -22
- package/src/domains/notifications/reminders/infrastructure/storage/RemindersStore.ts +0 -152
- package/src/infrastructure/utils/styleUtils.ts +0 -7
- package/src/presentation/screens/components/GamificationSettingsItem.tsx +0 -55
- package/src/presentation/screens/components/SubscriptionSettingsItem.tsx +0 -38
- package/src/presentation/screens/components/VideoTutorialSettingsItem.tsx +0 -51
- package/src/presentation/screens/components/WalletSettingsItem.tsx +0 -36
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
AchievementDefinition,
|
|
10
10
|
LevelDefinition,
|
|
11
11
|
} from "../../../domains/gamification/types";
|
|
12
|
+
import type { SettingsStackParamList } from "../../navigation/types";
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Create subscription configuration
|
|
@@ -17,11 +18,11 @@ import type {
|
|
|
17
18
|
*/
|
|
18
19
|
export const createSubscriptionConfig = (
|
|
19
20
|
isPremium: boolean,
|
|
20
|
-
routeOrOnPress:
|
|
21
|
+
routeOrOnPress: keyof SettingsStackParamList | (() => void),
|
|
21
22
|
): SubscriptionConfig => ({
|
|
22
23
|
enabled: true,
|
|
23
24
|
icon: "diamond",
|
|
24
|
-
route: typeof routeOrOnPress === "
|
|
25
|
+
route: typeof routeOrOnPress === "function" ? undefined : routeOrOnPress,
|
|
25
26
|
onPress: typeof routeOrOnPress === "function" ? routeOrOnPress : undefined,
|
|
26
27
|
isPremium,
|
|
27
28
|
});
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { useMemo } from "react";
|
|
7
7
|
import type { FeedbackFormData } from "./config-creators/types";
|
|
8
8
|
import type { SettingsConfig } from "../screens/types";
|
|
9
|
+
import type { SettingsStackParamList } from "../navigation/types";
|
|
9
10
|
import {
|
|
10
11
|
createAppearanceConfig,
|
|
11
12
|
createLanguageConfig,
|
|
@@ -61,7 +62,7 @@ export const createSettingsConfig = (
|
|
|
61
62
|
legal: features.legal ? createLegalConfig() : false,
|
|
62
63
|
faqs: features.faqs ? createFAQConfig() : false,
|
|
63
64
|
rating: features.rating ? createRatingConfig(handleRatePress, appStoreUrl) : false,
|
|
64
|
-
subscription: features.subscription ? createSubscriptionConfig(isPremium, "SubscriptionDetail") : false,
|
|
65
|
+
subscription: features.subscription ? createSubscriptionConfig(isPremium, "SubscriptionDetail" as keyof SettingsStackParamList) : false,
|
|
65
66
|
gamification: features.gamification ? true : false,
|
|
66
67
|
videoTutorial: features.videoTutorial ? true : false,
|
|
67
68
|
disclaimer: false,
|
package/src/utils/appUtils.ts
CHANGED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Utilities
|
|
3
|
+
* Centralized error message formatting and handling
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Format error message from unknown error type
|
|
8
|
+
* Handles Error instances, string errors, and unknown error types
|
|
9
|
+
*
|
|
10
|
+
* @param error - The error to format
|
|
11
|
+
* @returns Formatted error message string
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* try {
|
|
16
|
+
* await riskyOperation();
|
|
17
|
+
* } catch (err) {
|
|
18
|
+
* const message = formatErrorMessage(err);
|
|
19
|
+
* console.error(message);
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export const formatErrorMessage = (error: unknown): string => {
|
|
24
|
+
if (error instanceof Error) {
|
|
25
|
+
return error.message;
|
|
26
|
+
}
|
|
27
|
+
if (typeof error === 'string') {
|
|
28
|
+
return error;
|
|
29
|
+
}
|
|
30
|
+
return 'Unknown error';
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Format error for logging with optional context
|
|
35
|
+
* Useful for debugging and error tracking
|
|
36
|
+
*
|
|
37
|
+
* @param error - The error to format
|
|
38
|
+
* @param context - Optional context string (e.g., function name, operation)
|
|
39
|
+
* @returns Formatted error string with context
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* try {
|
|
44
|
+
* await fetchUserData();
|
|
45
|
+
* } catch (err) {
|
|
46
|
+
* console.error(formatErrorForLogging(err, 'fetchUserData'));
|
|
47
|
+
* // Output: "[fetchUserData] Network timeout"
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export const formatErrorForLogging = (error: unknown, context?: string): string => {
|
|
52
|
+
const message = formatErrorMessage(error);
|
|
53
|
+
return context ? `[${context}] ${message}` : message;
|
|
54
|
+
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Async State Update Hook
|
|
3
|
+
* Safely handles async operations with loading, error, and mounted state management
|
|
4
|
+
*
|
|
5
|
+
* Eliminates common patterns:
|
|
6
|
+
* - Manual isMountedRef checks
|
|
7
|
+
* - Duplicate try-catch-finally blocks
|
|
8
|
+
* - Repetitive loading/error state management
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* const { data, loading, error, execute } = useAsyncStateUpdate<User>({
|
|
13
|
+
* onSuccess: (user) => console.log('User loaded:', user),
|
|
14
|
+
* onError: (error) => console.error('Failed:', error),
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* const loadUser = async () => {
|
|
18
|
+
* await execute(async () => {
|
|
19
|
+
* return await api.fetchUser(userId);
|
|
20
|
+
* });
|
|
21
|
+
* };
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
import { useState, useCallback } from 'react';
|
|
25
|
+
import { useMountSafety } from './useMountSafety';
|
|
26
|
+
import { formatErrorMessage } from '../errorUtils';
|
|
27
|
+
|
|
28
|
+
export interface AsyncStateOptions<T> {
|
|
29
|
+
/** Initial data value */
|
|
30
|
+
initialData?: T | null;
|
|
31
|
+
/** Callback invoked on successful operation */
|
|
32
|
+
onSuccess?: (data: T) => void;
|
|
33
|
+
/** Callback invoked on error */
|
|
34
|
+
onError?: (error: string) => void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface AsyncStateResult<T> {
|
|
38
|
+
/** Current data value */
|
|
39
|
+
data: T | null;
|
|
40
|
+
/** Loading state */
|
|
41
|
+
loading: boolean;
|
|
42
|
+
/** Error message if operation failed */
|
|
43
|
+
error: string | null;
|
|
44
|
+
/** Execute an async operation with automatic state management */
|
|
45
|
+
execute: (operation: () => Promise<T>) => Promise<T | null>;
|
|
46
|
+
/** Manually set data */
|
|
47
|
+
setData: (data: T | null) => void;
|
|
48
|
+
/** Manually set error */
|
|
49
|
+
setError: (error: string | null) => void;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Hook for managing async operations with automatic state updates
|
|
54
|
+
*
|
|
55
|
+
* Features:
|
|
56
|
+
* - Automatic loading state management
|
|
57
|
+
* - Safe mounted state checks
|
|
58
|
+
* - Standardized error handling
|
|
59
|
+
* - Optional success/error callbacks
|
|
60
|
+
*
|
|
61
|
+
* @param options - Configuration options
|
|
62
|
+
* @returns Async state result with execute function
|
|
63
|
+
*/
|
|
64
|
+
export const useAsyncStateUpdate = <T = unknown>(
|
|
65
|
+
options?: AsyncStateOptions<T>
|
|
66
|
+
): AsyncStateResult<T> => {
|
|
67
|
+
const [data, setData] = useState<T | null>(options?.initialData ?? null);
|
|
68
|
+
const [loading, setLoading] = useState(false);
|
|
69
|
+
const [error, setError] = useState<string | null>(null);
|
|
70
|
+
const isMountedRef = useMountSafety();
|
|
71
|
+
|
|
72
|
+
const execute = useCallback(
|
|
73
|
+
async (operation: () => Promise<T>): Promise<T | null> => {
|
|
74
|
+
if (!isMountedRef.current) return null;
|
|
75
|
+
|
|
76
|
+
setLoading(true);
|
|
77
|
+
setError(null);
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
const result = await operation();
|
|
81
|
+
|
|
82
|
+
if (isMountedRef.current) {
|
|
83
|
+
setData(result);
|
|
84
|
+
options?.onSuccess?.(result);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return result;
|
|
88
|
+
} catch (err) {
|
|
89
|
+
const errorMessage = formatErrorMessage(err);
|
|
90
|
+
|
|
91
|
+
if (isMountedRef.current) {
|
|
92
|
+
setError(errorMessage);
|
|
93
|
+
options?.onError?.(errorMessage);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return null;
|
|
97
|
+
} finally {
|
|
98
|
+
if (isMountedRef.current) {
|
|
99
|
+
setLoading(false);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
[isMountedRef, options]
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
data,
|
|
108
|
+
loading,
|
|
109
|
+
error,
|
|
110
|
+
execute,
|
|
111
|
+
setData,
|
|
112
|
+
setError,
|
|
113
|
+
};
|
|
114
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mount Safety Hook
|
|
3
|
+
* Provides safe mounted state tracking to prevent state updates after unmount
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```typescript
|
|
7
|
+
* const isMountedRef = useMountSafety();
|
|
8
|
+
*
|
|
9
|
+
* const fetchData = async () => {
|
|
10
|
+
* const data = await api.fetch();
|
|
11
|
+
* if (isMountedRef.current) {
|
|
12
|
+
* setData(data);
|
|
13
|
+
* }
|
|
14
|
+
* };
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
import { useRef, useEffect } from 'react';
|
|
18
|
+
|
|
19
|
+
export const useMountSafety = () => {
|
|
20
|
+
const isMountedRef = useRef(true);
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
isMountedRef.current = true;
|
|
24
|
+
return () => {
|
|
25
|
+
isMountedRef.current = false;
|
|
26
|
+
};
|
|
27
|
+
}, []);
|
|
28
|
+
|
|
29
|
+
return isMountedRef;
|
|
30
|
+
};
|
package/src/utils/index.ts
CHANGED
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useAboutInfo Hook Utilities
|
|
3
|
-
* Shared utility functions for about info hook
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { MutableRefObject } from 'react';
|
|
7
|
-
import type { AppInfo, AboutConfig } from '../../domain/entities/AppInfo';
|
|
8
|
-
import type { AboutRepository } from '../../infrastructure/repositories/AboutRepository';
|
|
9
|
-
import { createDefaultAppInfo } from '../../utils/AppInfoFactory';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Safely set error if component is mounted
|
|
13
|
-
*/
|
|
14
|
-
export const setErrorIfMounted = (
|
|
15
|
-
isMountedRef: MutableRefObject<boolean>,
|
|
16
|
-
setError: (error: string | null) => void,
|
|
17
|
-
err: string | null
|
|
18
|
-
) => {
|
|
19
|
-
if (isMountedRef.current) {
|
|
20
|
-
setError(err);
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Safely set loading state if component is mounted
|
|
26
|
-
*/
|
|
27
|
-
export const setLoadingIfMounted = (
|
|
28
|
-
isMountedRef: MutableRefObject<boolean>,
|
|
29
|
-
setLoading: (loading: boolean) => void,
|
|
30
|
-
value: boolean
|
|
31
|
-
) => {
|
|
32
|
-
if (isMountedRef.current) {
|
|
33
|
-
setLoading(value);
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Initialize app info with config
|
|
39
|
-
*/
|
|
40
|
-
export const initializeAppInfo = async (
|
|
41
|
-
config: AboutConfig,
|
|
42
|
-
repository: AboutRepository,
|
|
43
|
-
isMountedRef: MutableRefObject<boolean>,
|
|
44
|
-
isInitializedRef: MutableRefObject<boolean>,
|
|
45
|
-
setAppInfo: (info: AppInfo | null) => void,
|
|
46
|
-
setError: (error: string | null) => void,
|
|
47
|
-
setLoading: (loading: boolean) => void,
|
|
48
|
-
force = false
|
|
49
|
-
): Promise<void> => {
|
|
50
|
-
if (isInitializedRef.current && !force) {
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (!isMountedRef.current) {
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
setLoading(true);
|
|
59
|
-
setError(null);
|
|
60
|
-
|
|
61
|
-
try {
|
|
62
|
-
const defaultAppInfo = createDefaultAppInfo(config);
|
|
63
|
-
await repository.saveAppInfo(defaultAppInfo);
|
|
64
|
-
|
|
65
|
-
if (isMountedRef.current) {
|
|
66
|
-
setAppInfo(defaultAppInfo);
|
|
67
|
-
isInitializedRef.current = true;
|
|
68
|
-
}
|
|
69
|
-
} catch (err) {
|
|
70
|
-
setError(err instanceof Error ? err.message : 'Unknown error');
|
|
71
|
-
} finally {
|
|
72
|
-
setLoading(false);
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Update app info with new config
|
|
78
|
-
*/
|
|
79
|
-
export const updateAppInfoConfig = async (
|
|
80
|
-
config: AboutConfig,
|
|
81
|
-
repository: AboutRepository,
|
|
82
|
-
isMountedRef: MutableRefObject<boolean>,
|
|
83
|
-
setAppInfo: (info: AppInfo | null) => void,
|
|
84
|
-
setError: (error: string | null) => void,
|
|
85
|
-
setLoading: (loading: boolean) => void
|
|
86
|
-
): Promise<void> => {
|
|
87
|
-
if (!isMountedRef.current) {
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
setLoading(true);
|
|
92
|
-
setError(null);
|
|
93
|
-
|
|
94
|
-
try {
|
|
95
|
-
const updatedAppInfo = createDefaultAppInfo(config);
|
|
96
|
-
await repository.saveAppInfo(updatedAppInfo);
|
|
97
|
-
|
|
98
|
-
if (isMountedRef.current) {
|
|
99
|
-
setAppInfo(updatedAppInfo);
|
|
100
|
-
}
|
|
101
|
-
} catch (err) {
|
|
102
|
-
setError(err instanceof Error ? err.message : 'Unknown error');
|
|
103
|
-
} finally {
|
|
104
|
-
setLoading(false);
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Update app info with partial updates
|
|
110
|
-
*/
|
|
111
|
-
export const updateAppInfoPartial = async (
|
|
112
|
-
updates: Partial<AppInfo>,
|
|
113
|
-
repository: AboutRepository,
|
|
114
|
-
isMountedRef: MutableRefObject<boolean>,
|
|
115
|
-
setAppInfo: (info: AppInfo | null) => void,
|
|
116
|
-
setError: (error: string | null) => void,
|
|
117
|
-
setLoading: (loading: boolean) => void
|
|
118
|
-
): Promise<void> => {
|
|
119
|
-
if (!isMountedRef.current) {
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
setLoading(true);
|
|
124
|
-
setError(null);
|
|
125
|
-
|
|
126
|
-
try {
|
|
127
|
-
const updatedInfo = await repository.updateAppInfo(updates);
|
|
128
|
-
|
|
129
|
-
if (isMountedRef.current) {
|
|
130
|
-
setAppInfo(updatedInfo);
|
|
131
|
-
}
|
|
132
|
-
} catch (err) {
|
|
133
|
-
setError(err instanceof Error ? err.message : 'Unknown error');
|
|
134
|
-
} finally {
|
|
135
|
-
setLoading(false);
|
|
136
|
-
}
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Refresh app info from repository
|
|
141
|
-
*/
|
|
142
|
-
export const refreshAppInfo = async (
|
|
143
|
-
repository: AboutRepository,
|
|
144
|
-
isMountedRef: MutableRefObject<boolean>,
|
|
145
|
-
setAppInfo: (info: AppInfo | null) => void,
|
|
146
|
-
setError: (error: string | null) => void,
|
|
147
|
-
setLoading: (loading: boolean) => void
|
|
148
|
-
): Promise<void> => {
|
|
149
|
-
if (!isMountedRef.current) {
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
setLoading(true);
|
|
154
|
-
setError(null);
|
|
155
|
-
|
|
156
|
-
try {
|
|
157
|
-
const refreshedInfo = await repository.getAppInfo();
|
|
158
|
-
|
|
159
|
-
if (isMountedRef.current) {
|
|
160
|
-
setAppInfo(refreshedInfo);
|
|
161
|
-
}
|
|
162
|
-
} catch (err) {
|
|
163
|
-
setError(err instanceof Error ? err.message : 'Unknown error');
|
|
164
|
-
} finally {
|
|
165
|
-
setLoading(false);
|
|
166
|
-
}
|
|
167
|
-
};
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Notifications Store - Zustand State Management
|
|
3
|
-
* Simple offline-first notification state
|
|
4
|
-
* NO backend, NO user IDs, NO notification history
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { createStore } from '@umituz/react-native-design-system';
|
|
8
|
-
|
|
9
|
-
interface NotificationsState {
|
|
10
|
-
hasPermissions: boolean;
|
|
11
|
-
isInitialized: boolean;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
interface NotificationsActions {
|
|
15
|
-
setPermissions: (granted: boolean) => void;
|
|
16
|
-
setInitialized: (initialized: boolean) => void;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export const useNotificationsStore = createStore<NotificationsState, NotificationsActions>({
|
|
20
|
-
name: 'notifications-store',
|
|
21
|
-
initialState: {
|
|
22
|
-
hasPermissions: false,
|
|
23
|
-
isInitialized: false,
|
|
24
|
-
},
|
|
25
|
-
persist: false,
|
|
26
|
-
actions: (set) => ({
|
|
27
|
-
setPermissions: (granted) => set({ hasPermissions: granted }),
|
|
28
|
-
setInitialized: (initialized) => set({ isInitialized: initialized }),
|
|
29
|
-
}),
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Hook for accessing notifications state
|
|
34
|
-
*/
|
|
35
|
-
export const useNotifications = () => {
|
|
36
|
-
const store = useNotificationsStore();
|
|
37
|
-
const { hasPermissions, isInitialized, setPermissions, setInitialized } = store;
|
|
38
|
-
|
|
39
|
-
return {
|
|
40
|
-
hasPermissions,
|
|
41
|
-
isInitialized,
|
|
42
|
-
setPermissions,
|
|
43
|
-
setInitialized,
|
|
44
|
-
};
|
|
45
|
-
};
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
export const isDev = () => {
|
|
2
|
-
try {
|
|
3
|
-
return typeof __DEV__ !== 'undefined' && __DEV__;
|
|
4
|
-
} catch {
|
|
5
|
-
return false;
|
|
6
|
-
}
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
export const devLog = (_message: string, ..._args: unknown[]) => {
|
|
10
|
-
if (isDev()) {
|
|
11
|
-
}
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export const devError = (_message: string, ..._args: unknown[]) => {
|
|
15
|
-
if (isDev()) {
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export const devWarn = (_message: string, ..._args: unknown[]) => {
|
|
20
|
-
if (isDev()) {
|
|
21
|
-
}
|
|
22
|
-
};
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reminders Store - Zustand State Management
|
|
3
|
-
* Manages reminder state with AsyncStorage persistence
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { useMemo } from 'react';
|
|
7
|
-
import { createStore } from '@umituz/react-native-design-system';
|
|
8
|
-
import type { Reminder, QuietHoursConfig, NotificationPreferences } from '../../../infrastructure/services/types';
|
|
9
|
-
|
|
10
|
-
// ============================================================================
|
|
11
|
-
// REMINDERS STORE
|
|
12
|
-
// ============================================================================
|
|
13
|
-
|
|
14
|
-
interface RemindersState {
|
|
15
|
-
reminders: Reminder[];
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface RemindersActions {
|
|
19
|
-
addReminder: (reminder: Reminder) => void;
|
|
20
|
-
updateReminder: (id: string, updates: Partial<Reminder>) => void;
|
|
21
|
-
deleteReminder: (id: string) => void;
|
|
22
|
-
toggleReminder: (id: string) => void;
|
|
23
|
-
resetReminders: () => void;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const DEFAULT_REMINDERS_STATE: RemindersState = {
|
|
27
|
-
reminders: [],
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export const useRemindersStore = createStore<RemindersState, RemindersActions>({
|
|
31
|
-
name: 'reminders-store',
|
|
32
|
-
initialState: DEFAULT_REMINDERS_STATE,
|
|
33
|
-
persist: true,
|
|
34
|
-
actions: (set, get) => ({
|
|
35
|
-
addReminder: (reminder: Reminder) => {
|
|
36
|
-
const { reminders } = get();
|
|
37
|
-
set({ reminders: [...reminders, reminder] });
|
|
38
|
-
},
|
|
39
|
-
|
|
40
|
-
updateReminder: (id: string, updates: Partial<Reminder>) => {
|
|
41
|
-
const { reminders } = get();
|
|
42
|
-
set({
|
|
43
|
-
reminders: reminders.map(r =>
|
|
44
|
-
r.id === id ? { ...r, ...updates, updatedAt: new Date().toISOString() } : r
|
|
45
|
-
),
|
|
46
|
-
});
|
|
47
|
-
},
|
|
48
|
-
|
|
49
|
-
deleteReminder: (id: string) => {
|
|
50
|
-
const { reminders } = get();
|
|
51
|
-
set({ reminders: reminders.filter(r => r.id !== id) });
|
|
52
|
-
},
|
|
53
|
-
|
|
54
|
-
toggleReminder: (id: string) => {
|
|
55
|
-
const { reminders } = get();
|
|
56
|
-
set({
|
|
57
|
-
reminders: reminders.map(r =>
|
|
58
|
-
r.id === id ? { ...r, enabled: !r.enabled, updatedAt: new Date().toISOString() } : r
|
|
59
|
-
),
|
|
60
|
-
});
|
|
61
|
-
},
|
|
62
|
-
|
|
63
|
-
resetReminders: () => {
|
|
64
|
-
set({ reminders: [] });
|
|
65
|
-
},
|
|
66
|
-
}),
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
// ============================================================================
|
|
70
|
-
// PREFERENCES STORE
|
|
71
|
-
// ============================================================================
|
|
72
|
-
|
|
73
|
-
interface PreferencesState {
|
|
74
|
-
preferences: NotificationPreferences;
|
|
75
|
-
isLoading: boolean;
|
|
76
|
-
isInitialized: boolean;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
interface PreferencesActions {
|
|
80
|
-
initialize: () => void;
|
|
81
|
-
updatePreferences: (updates: Partial<NotificationPreferences>) => void;
|
|
82
|
-
updateQuietHours: (quietHours: QuietHoursConfig) => void;
|
|
83
|
-
reset: () => void;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// ============================================================================
|
|
87
|
-
// DEFAULT VALUES
|
|
88
|
-
// ============================================================================
|
|
89
|
-
|
|
90
|
-
const DEFAULT_PREFERENCES: NotificationPreferences = {
|
|
91
|
-
enabled: true,
|
|
92
|
-
sound: true,
|
|
93
|
-
vibration: true,
|
|
94
|
-
quietHours: {
|
|
95
|
-
enabled: false,
|
|
96
|
-
startHour: 22,
|
|
97
|
-
startMinute: 0,
|
|
98
|
-
endHour: 7,
|
|
99
|
-
endMinute: 0,
|
|
100
|
-
},
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
const initialPreferencesState: PreferencesState = {
|
|
104
|
-
preferences: DEFAULT_PREFERENCES,
|
|
105
|
-
isLoading: true,
|
|
106
|
-
isInitialized: false,
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
export const usePreferencesStore = createStore<PreferencesState, PreferencesActions>({
|
|
110
|
-
name: 'preferences-store',
|
|
111
|
-
initialState: initialPreferencesState,
|
|
112
|
-
persist: true,
|
|
113
|
-
actions: (set, get) => ({
|
|
114
|
-
initialize: () => {
|
|
115
|
-
set({ isLoading: false, isInitialized: true });
|
|
116
|
-
},
|
|
117
|
-
|
|
118
|
-
updatePreferences: (updates: Partial<NotificationPreferences>) => {
|
|
119
|
-
const { preferences } = get();
|
|
120
|
-
set({ preferences: { ...preferences, ...updates } });
|
|
121
|
-
},
|
|
122
|
-
|
|
123
|
-
updateQuietHours: (quietHours: QuietHoursConfig) => {
|
|
124
|
-
const { preferences } = get();
|
|
125
|
-
set({ preferences: { ...preferences, quietHours } });
|
|
126
|
-
},
|
|
127
|
-
|
|
128
|
-
reset: () => {
|
|
129
|
-
set({ preferences: DEFAULT_PREFERENCES });
|
|
130
|
-
},
|
|
131
|
-
}),
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
// ============================================================================
|
|
135
|
-
// SELECTOR HOOKS - REMINDERS
|
|
136
|
-
// ============================================================================
|
|
137
|
-
|
|
138
|
-
export const useReminders = () => useRemindersStore(state => state.reminders);
|
|
139
|
-
export const useEnabledReminders = () => {
|
|
140
|
-
const reminders = useRemindersStore(state => state.reminders);
|
|
141
|
-
return useMemo(() => reminders.filter(r => r.enabled), [reminders]);
|
|
142
|
-
};
|
|
143
|
-
export const useReminderById = (id: string) => useRemindersStore(state => state.reminders.find(r => r.id === id));
|
|
144
|
-
|
|
145
|
-
// ============================================================================
|
|
146
|
-
// SELECTOR HOOKS - PREFERENCES
|
|
147
|
-
// ============================================================================
|
|
148
|
-
|
|
149
|
-
export const useNotificationPreferences = () => usePreferencesStore(state => state.preferences);
|
|
150
|
-
export const useQuietHours = () => usePreferencesStore(state => state.preferences.quietHours);
|
|
151
|
-
export const useRemindersLoading = () => usePreferencesStore(state => state.isLoading);
|
|
152
|
-
export const useRemindersInitialized = () => usePreferencesStore(state => state.isInitialized);
|