@umituz/react-native-settings 4.23.85 → 4.23.87
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 +3 -3
- package/src/domains/about/presentation/hooks/useAboutInfo.ts +1 -1
- package/src/domains/faqs/presentation/screens/FAQScreen.tsx +1 -1
- package/src/domains/feedback/presentation/components/FeedbackForm.styles.ts +1 -1
- package/src/domains/feedback/presentation/components/FeedbackForm.tsx +11 -4
- package/src/domains/gamification/components/GamificationScreen/GamificationScreen.tsx +1 -6
- package/src/domains/gamification/store/gamificationStore.ts +6 -7
- package/src/domains/localization/infrastructure/storage/LocalizationStore.ts +50 -181
- package/src/domains/localization/infrastructure/storage/localizationStoreUtils.ts +182 -0
- package/src/domains/notifications/infrastructure/utils/dev.ts +3 -3
- package/src/domains/notifications/reminders/presentation/components/ReminderForm.constants.ts +1 -1
- package/src/domains/notifications/reminders/presentation/components/ReminderForm.tsx +52 -46
- package/src/infrastructure/types/commonComponentTypes.ts +142 -0
- package/src/infrastructure/utils/async/core.ts +109 -0
- package/src/infrastructure/utils/async/debounceAndBatch.ts +69 -0
- package/src/infrastructure/utils/async/index.ts +8 -0
- package/src/infrastructure/utils/async/retryAndTimeout.ts +57 -0
- package/src/infrastructure/utils/configFactory.ts +101 -0
- package/src/infrastructure/utils/errorHandlers.ts +249 -0
- package/src/infrastructure/utils/index.ts +5 -0
- package/src/infrastructure/utils/memoComparisonUtils.ts +0 -2
- package/src/infrastructure/utils/memoUtils.ts +10 -2
- package/src/infrastructure/utils/styleTokens.ts +132 -0
- package/src/infrastructure/utils/validation/core.ts +42 -0
- package/src/infrastructure/utils/validation/formValidators.ts +82 -0
- package/src/infrastructure/utils/validation/index.ts +37 -0
- package/src/infrastructure/utils/validation/numericValidators.ts +66 -0
- package/src/infrastructure/utils/validation/passwordValidator.ts +53 -0
- package/src/infrastructure/utils/validation/textValidators.ts +118 -0
- package/src/presentation/hooks/useSettingsScreenConfig.ts +32 -79
- package/src/presentation/navigation/SettingsStackNavigator.tsx +1 -24
- package/src/presentation/navigation/hooks/useSettingsScreens.ts +1 -1
- package/src/presentation/utils/config-creators/base-configs.ts +54 -42
- package/src/presentation/utils/faqTranslator.ts +31 -0
- package/src/presentation/utils/index.ts +6 -1
- package/src/presentation/utils/screenFactory.ts +1 -1
- package/src/presentation/utils/settingsConfigFactory.ts +89 -0
- package/src/presentation/utils/useAuthHandlers.ts +98 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-settings",
|
|
3
|
-
"version": "4.23.
|
|
3
|
+
"version": "4.23.87",
|
|
4
4
|
"description": "Complete settings hub for React Native apps - consolidated package with settings, localization, about, legal, appearance, feedback, FAQs, rating, and gamification",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -74,8 +74,8 @@
|
|
|
74
74
|
"@types/react": "~19.1.10",
|
|
75
75
|
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
|
76
76
|
"@typescript-eslint/parser": "^7.18.0",
|
|
77
|
-
"@umituz/react-native-auth": "^3.6.
|
|
78
|
-
"@umituz/react-native-design-system": "
|
|
77
|
+
"@umituz/react-native-auth": "^3.6.76",
|
|
78
|
+
"@umituz/react-native-design-system": "latest",
|
|
79
79
|
"@umituz/react-native-firebase": "^1.13.102",
|
|
80
80
|
"@umituz/react-native-sentry": "*",
|
|
81
81
|
"eslint": "^8.57.0",
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import React, { useMemo } from 'react';
|
|
8
8
|
import { View, ScrollView, StyleSheet, ViewStyle, TextStyle, useWindowDimensions } from 'react-native';
|
|
9
|
-
import { useAppDesignTokens,
|
|
9
|
+
import { useAppDesignTokens, ScreenLayout, getContentMaxWidth, NavigationHeader, useAppNavigation } from '@umituz/react-native-design-system';
|
|
10
10
|
import { FAQCategory } from '../../domain/entities/FAQEntity';
|
|
11
11
|
import { useFAQSearch } from '../hooks/useFAQSearch';
|
|
12
12
|
import { useFAQExpansion } from '../hooks/useFAQExpansion';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { StyleSheet } from "react-native";
|
|
2
2
|
import type { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
3
3
|
|
|
4
|
-
export const getFeedbackFormStyles = (
|
|
4
|
+
export const getFeedbackFormStyles = (_tokens: ReturnType<typeof useAppDesignTokens>) =>
|
|
5
5
|
StyleSheet.create({
|
|
6
6
|
container: {
|
|
7
7
|
width: "100%",
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import React, { useState } from "react";
|
|
7
|
-
import { View,
|
|
7
|
+
import { View, TouchableOpacity, ScrollView, TextInput } from "react-native";
|
|
8
8
|
import { useAppDesignTokens, AtomicText, AtomicButton, AtomicIcon } from "@umituz/react-native-design-system";
|
|
9
9
|
import type { FeedbackType, FeedbackRating } from "../../domain/entities/FeedbackEntity";
|
|
10
|
+
import { validateFeedbackForm } from "../../../../infrastructure/utils/validation";
|
|
10
11
|
|
|
11
12
|
import { getFeedbackFormStyles as getStyles } from "./FeedbackForm.styles";
|
|
12
13
|
|
|
@@ -40,9 +41,15 @@ export const FeedbackForm: React.FC<FeedbackFormProps> = ({
|
|
|
40
41
|
const [isSubmittingLocal, setIsSubmittingLocal] = useState(false);
|
|
41
42
|
|
|
42
43
|
const handleSubmit = async () => {
|
|
43
|
-
// Validate
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
// Validate using centralized validation
|
|
45
|
+
const validationResult = validateFeedbackForm({
|
|
46
|
+
type: selectedType,
|
|
47
|
+
rating,
|
|
48
|
+
description,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
if (!validationResult.isValid) {
|
|
52
|
+
setError(validationResult.error || "Validation failed");
|
|
46
53
|
return;
|
|
47
54
|
}
|
|
48
55
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import React from "react";
|
|
8
|
-
import { View
|
|
8
|
+
import { View } from "react-native";
|
|
9
9
|
import { useAppDesignTokens, AtomicText, ScreenLayout, NavigationHeader, useAppNavigation } from "@umituz/react-native-design-system";
|
|
10
10
|
import { LevelProgress } from "../LevelProgress";
|
|
11
11
|
import { StreakDisplay } from "../StreakDisplay";
|
|
@@ -27,12 +27,8 @@ export const GamificationScreenInner: React.FC<GamificationScreenProps> = ({
|
|
|
27
27
|
achievements,
|
|
28
28
|
streakProps,
|
|
29
29
|
emptyAchievementsText,
|
|
30
|
-
containerStyle,
|
|
31
|
-
headerStyle,
|
|
32
|
-
titleStyle,
|
|
33
30
|
sectionTitleStyle,
|
|
34
31
|
accentColor,
|
|
35
|
-
backgroundColor,
|
|
36
32
|
cardBackgroundColor,
|
|
37
33
|
textColor,
|
|
38
34
|
subtextColor,
|
|
@@ -43,7 +39,6 @@ export const GamificationScreenInner: React.FC<GamificationScreenProps> = ({
|
|
|
43
39
|
|
|
44
40
|
// Use tokens for fallbacks
|
|
45
41
|
const finalAccentColor = accentColor || tokens.colors.primary;
|
|
46
|
-
const finalBackgroundColor = backgroundColor || tokens.colors.backgroundPrimary;
|
|
47
42
|
const finalCardBackgroundColor = cardBackgroundColor || tokens.colors.surface;
|
|
48
43
|
const finalTextColor = textColor || tokens.colors.textPrimary;
|
|
49
44
|
const finalSubtextColor = subtextColor || tokens.colors.textSecondary;
|
|
@@ -78,16 +78,16 @@ export const useGamificationStore = createStore<GamificationState, GamificationA
|
|
|
78
78
|
const state = get();
|
|
79
79
|
const pointsToAdd = currentConfig?.pointsPerAction ?? 15;
|
|
80
80
|
|
|
81
|
+
const newTotalTasks = state.totalTasksCompleted + 1;
|
|
82
|
+
|
|
81
83
|
set({
|
|
82
|
-
totalTasksCompleted:
|
|
84
|
+
totalTasksCompleted: newTotalTasks,
|
|
83
85
|
points: state.points + pointsToAdd,
|
|
84
86
|
});
|
|
85
87
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
// Check achievements
|
|
90
|
-
get().checkAchievements();
|
|
88
|
+
const actions = get() as GamificationActions;
|
|
89
|
+
actions.updateStreak();
|
|
90
|
+
actions.checkAchievements();
|
|
91
91
|
},
|
|
92
92
|
|
|
93
93
|
updateStreak: () => {
|
|
@@ -121,7 +121,6 @@ export const useGamificationStore = createStore<GamificationState, GamificationA
|
|
|
121
121
|
|
|
122
122
|
const state = get();
|
|
123
123
|
|
|
124
|
-
// Safety check for achievements array
|
|
125
124
|
if (!state.achievements || state.achievements.length === 0) {
|
|
126
125
|
return [];
|
|
127
126
|
}
|
|
@@ -3,187 +3,56 @@
|
|
|
3
3
|
* Creates and manages localization state with proper separation of concerns
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { create } from
|
|
7
|
-
import type { LocalizationState, LocalizationActions, LocalizationGetters } from
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { languageRepository } from '../repository/LanguageRepository';
|
|
11
|
-
|
|
12
|
-
declare const __DEV__: boolean;
|
|
6
|
+
import { create } from "zustand";
|
|
7
|
+
import type { LocalizationState, LocalizationActions, LocalizationGetters } from "./types/LocalizationState";
|
|
8
|
+
import { languageRepository } from "../repository/LanguageRepository";
|
|
9
|
+
import { InitializationManager, LanguageSwitchManager, localizationGetters } from "./localizationStoreUtils";
|
|
13
10
|
|
|
14
11
|
type LocalizationStoreType = LocalizationState & LocalizationActions & LocalizationGetters;
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
isInitialized
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
set({
|
|
63
|
-
currentLanguage: 'en-US',
|
|
64
|
-
isRTL: false,
|
|
65
|
-
isInitialized: true,
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
throw error; // Re-throw to allow error handling
|
|
69
|
-
} finally {
|
|
70
|
-
initializeInProgress = false;
|
|
71
|
-
initializePromise = null;
|
|
72
|
-
}
|
|
73
|
-
})();
|
|
74
|
-
|
|
75
|
-
return initializePromise;
|
|
76
|
-
},
|
|
77
|
-
|
|
78
|
-
setLanguage: async (languageCode: string) => {
|
|
79
|
-
// Validate input
|
|
80
|
-
if (!languageCode || typeof languageCode !== 'string') {
|
|
81
|
-
throw new Error('Invalid language code provided');
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Clear existing timer
|
|
85
|
-
if (languageSwitchTimer) {
|
|
86
|
-
clearTimeout(languageSwitchTimer);
|
|
87
|
-
languageSwitchTimer = null;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return new Promise<void>((resolve, reject) => {
|
|
91
|
-
// Add resolver to pending list
|
|
92
|
-
pendingResolvers.push(() => {
|
|
93
|
-
// Resolve successfully
|
|
94
|
-
resolve();
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// Create rejection handler
|
|
98
|
-
const rejectAndCleanup = (error: Error) => {
|
|
99
|
-
// Remove this resolver
|
|
100
|
-
const index = pendingResolvers.findIndex(r => r === resolve);
|
|
101
|
-
if (index > -1) {
|
|
102
|
-
pendingResolvers.splice(index, 1);
|
|
103
|
-
}
|
|
104
|
-
reject(error);
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
languageSwitchTimer = setTimeout(async () => {
|
|
108
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
try {
|
|
112
|
-
const result = await LanguageSwitcher.switchLanguage(languageCode);
|
|
113
|
-
|
|
114
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
set({
|
|
118
|
-
currentLanguage: result.languageCode,
|
|
119
|
-
isRTL: result.isRTL,
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Resolve ALL pending promises
|
|
126
|
-
const resolvers = [...pendingResolvers];
|
|
127
|
-
pendingResolvers.length = 0; // Clear array
|
|
128
|
-
resolvers.forEach(r => r());
|
|
129
|
-
} catch (error) {
|
|
130
|
-
const errorMessage = error instanceof Error ? error : new Error(String(error));
|
|
131
|
-
|
|
132
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Reject all pending promises
|
|
136
|
-
const resolvers = [...pendingResolvers];
|
|
137
|
-
pendingResolvers.length = 0; // Clear array
|
|
138
|
-
resolvers.forEach(() => {
|
|
139
|
-
// Each resolver is wrapped to handle rejection
|
|
140
|
-
// Note: We can't reject promises already created, so we just clear them
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
// Reject this specific promise
|
|
144
|
-
rejectAndCleanup(errorMessage);
|
|
145
|
-
} finally {
|
|
146
|
-
languageSwitchTimer = null;
|
|
147
|
-
}
|
|
148
|
-
}, LANGUAGE_SWITCH_DEBOUNCE_MS);
|
|
149
|
-
});
|
|
150
|
-
},
|
|
151
|
-
|
|
152
|
-
reset: () => {
|
|
153
|
-
// Clear any pending language switch
|
|
154
|
-
if (languageSwitchTimer) {
|
|
155
|
-
clearTimeout(languageSwitchTimer);
|
|
156
|
-
languageSwitchTimer = null;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Resolve any pending promises to prevent hanging
|
|
160
|
-
const resolvers = [...pendingResolvers];
|
|
161
|
-
pendingResolvers.length = 0; // Clear array
|
|
162
|
-
resolvers.forEach(r => r());
|
|
163
|
-
|
|
164
|
-
// Reset mutex
|
|
165
|
-
initializeInProgress = false;
|
|
166
|
-
initializePromise = null;
|
|
167
|
-
|
|
168
|
-
set({
|
|
169
|
-
currentLanguage: 'en-US',
|
|
170
|
-
isRTL: false,
|
|
171
|
-
isInitialized: false,
|
|
172
|
-
});
|
|
173
|
-
},
|
|
174
|
-
|
|
175
|
-
// Getters
|
|
176
|
-
getCurrentLanguage: () => {
|
|
177
|
-
const { currentLanguage } = get();
|
|
178
|
-
return languageRepository.getLanguageByCode(currentLanguage);
|
|
179
|
-
},
|
|
180
|
-
|
|
181
|
-
isLanguageSupported: (code: string) => {
|
|
182
|
-
return languageRepository.isLanguageSupported(code);
|
|
183
|
-
},
|
|
184
|
-
|
|
185
|
-
getSupportedLanguages: () => {
|
|
186
|
-
return languageRepository.getLanguages();
|
|
187
|
-
},
|
|
188
|
-
};
|
|
189
|
-
});
|
|
13
|
+
// Instance-level managers
|
|
14
|
+
const initManager = new InitializationManager();
|
|
15
|
+
const switchManager = new LanguageSwitchManager();
|
|
16
|
+
|
|
17
|
+
export const useLocalizationStore = create<LocalizationStoreType>((set, get) => ({
|
|
18
|
+
// State
|
|
19
|
+
currentLanguage: "en-US",
|
|
20
|
+
isRTL: false,
|
|
21
|
+
isInitialized: false,
|
|
22
|
+
supportedLanguages: languageRepository.getLanguages(),
|
|
23
|
+
|
|
24
|
+
// Actions
|
|
25
|
+
initialize: async () => {
|
|
26
|
+
const { isInitialized } = get();
|
|
27
|
+
await initManager.initialize(isInitialized, set);
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
setLanguage: async (languageCode: string) => {
|
|
31
|
+
await switchManager.setLanguage(languageCode, set);
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
reset: () => {
|
|
35
|
+
initManager.reset();
|
|
36
|
+
switchManager.reset();
|
|
37
|
+
|
|
38
|
+
set({
|
|
39
|
+
currentLanguage: "en-US",
|
|
40
|
+
isRTL: false,
|
|
41
|
+
isInitialized: false,
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
// Getters
|
|
46
|
+
getCurrentLanguage: () => {
|
|
47
|
+
const { currentLanguage } = get();
|
|
48
|
+
return localizationGetters.getCurrentLanguage(currentLanguage);
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
isLanguageSupported: (code: string) => {
|
|
52
|
+
return localizationGetters.isLanguageSupported(code);
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
getSupportedLanguages: () => {
|
|
56
|
+
return localizationGetters.getSupportedLanguages();
|
|
57
|
+
},
|
|
58
|
+
}));
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Localization Store Utilities
|
|
3
|
+
* Extracted business logic for localization store
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { LanguageInitializer } from "./LanguageInitializer";
|
|
7
|
+
import { LanguageSwitcher } from "./LanguageSwitcher";
|
|
8
|
+
import { languageRepository } from "../repository/LanguageRepository";
|
|
9
|
+
|
|
10
|
+
declare const __DEV__: boolean;
|
|
11
|
+
|
|
12
|
+
export const LANGUAGE_SWITCH_DEBOUNCE_MS = 300;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Manages localization initialization state
|
|
16
|
+
*/
|
|
17
|
+
export class InitializationManager {
|
|
18
|
+
private inProgress = false;
|
|
19
|
+
private promise: Promise<void> | null = null;
|
|
20
|
+
|
|
21
|
+
async initialize(
|
|
22
|
+
isAlreadyInitialized: boolean,
|
|
23
|
+
setState: (state: Partial<{ currentLanguage: string; isRTL: boolean; isInitialized: boolean }>) => void
|
|
24
|
+
): Promise<void> {
|
|
25
|
+
// Return existing promise if initialization is in progress
|
|
26
|
+
if (this.inProgress && this.promise) {
|
|
27
|
+
return this.promise;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Return if already initialized
|
|
31
|
+
if (isAlreadyInitialized) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Set mutex and create promise
|
|
36
|
+
this.inProgress = true;
|
|
37
|
+
this.promise = (async () => {
|
|
38
|
+
try {
|
|
39
|
+
const result = await LanguageInitializer.initialize();
|
|
40
|
+
|
|
41
|
+
setState({
|
|
42
|
+
currentLanguage: result.languageCode,
|
|
43
|
+
isRTL: result.isRTL,
|
|
44
|
+
isInitialized: true,
|
|
45
|
+
});
|
|
46
|
+
} catch (error) {
|
|
47
|
+
// Log and set fallback state
|
|
48
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
49
|
+
console.error("Localization initialization failed:", error);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
setState({
|
|
53
|
+
currentLanguage: "en-US",
|
|
54
|
+
isRTL: false,
|
|
55
|
+
isInitialized: true,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
throw error; // Re-throw to allow error handling
|
|
59
|
+
} finally {
|
|
60
|
+
this.inProgress = false;
|
|
61
|
+
this.promise = null;
|
|
62
|
+
}
|
|
63
|
+
})();
|
|
64
|
+
|
|
65
|
+
return this.promise;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
reset(): void {
|
|
69
|
+
this.inProgress = false;
|
|
70
|
+
this.promise = null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Manages debounced language switching with pending promise handling
|
|
76
|
+
* FIXED: Properly rejects pending promises on error
|
|
77
|
+
*/
|
|
78
|
+
export class LanguageSwitchManager {
|
|
79
|
+
private timer: ReturnType<typeof setTimeout> | null = null;
|
|
80
|
+
private pendingResolvers: Array<{ resolve: () => void; reject: (error: Error) => void }> = [];
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Sets language with debounce and promise handling
|
|
84
|
+
*/
|
|
85
|
+
setLanguage(
|
|
86
|
+
languageCode: string,
|
|
87
|
+
setState: (state: Partial<{ currentLanguage: string; isRTL: boolean }>) => void
|
|
88
|
+
): Promise<void> {
|
|
89
|
+
// Validate input
|
|
90
|
+
if (!languageCode || typeof languageCode !== "string") {
|
|
91
|
+
return Promise.reject(new Error("Invalid language code provided"));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Clear existing timer
|
|
95
|
+
if (this.timer) {
|
|
96
|
+
clearTimeout(this.timer);
|
|
97
|
+
this.timer = null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return new Promise<void>((resolve, reject) => {
|
|
101
|
+
// Add resolver and rejector to pending list
|
|
102
|
+
const pendingItem = { resolve, reject };
|
|
103
|
+
this.pendingResolvers.push(pendingItem);
|
|
104
|
+
|
|
105
|
+
this.timer = setTimeout(async () => {
|
|
106
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
107
|
+
console.log("[Localization] Switching language to:", languageCode);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
const result = await LanguageSwitcher.switchLanguage(languageCode);
|
|
112
|
+
|
|
113
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
114
|
+
console.log("[Localization] Language switched successfully");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const stateUpdate = {
|
|
118
|
+
currentLanguage: result.languageCode,
|
|
119
|
+
isRTL: result.isRTL,
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
setState(stateUpdate);
|
|
123
|
+
|
|
124
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
125
|
+
console.log("[Localization] State updated:", stateUpdate);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Resolve ALL pending promises
|
|
129
|
+
const resolvers = [...this.pendingResolvers];
|
|
130
|
+
this.pendingResolvers = [];
|
|
131
|
+
resolvers.forEach((r) => r.resolve());
|
|
132
|
+
} catch (error) {
|
|
133
|
+
const errorMessage =
|
|
134
|
+
error instanceof Error ? error : new Error(String(error));
|
|
135
|
+
|
|
136
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
137
|
+
console.error("[Localization] Language switch failed:", errorMessage);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Reject ALL pending promises - FIXED: Actually rejects them now
|
|
141
|
+
const resolvers = [...this.pendingResolvers];
|
|
142
|
+
this.pendingResolvers = [];
|
|
143
|
+
resolvers.forEach((r) => r.reject(errorMessage));
|
|
144
|
+
} finally {
|
|
145
|
+
this.timer = null;
|
|
146
|
+
}
|
|
147
|
+
}, LANGUAGE_SWITCH_DEBOUNCE_MS);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Clears any pending language switch and resolves all promises
|
|
153
|
+
*/
|
|
154
|
+
reset(): void {
|
|
155
|
+
if (this.timer) {
|
|
156
|
+
clearTimeout(this.timer);
|
|
157
|
+
this.timer = null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Resolve any pending promises to prevent hanging
|
|
161
|
+
const resolvers = [...this.pendingResolvers];
|
|
162
|
+
this.pendingResolvers = [];
|
|
163
|
+
resolvers.forEach((r) => r.resolve());
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Localization getters
|
|
169
|
+
*/
|
|
170
|
+
export const localizationGetters = {
|
|
171
|
+
getCurrentLanguage: (currentLanguage: string) => {
|
|
172
|
+
return languageRepository.getLanguageByCode(currentLanguage);
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
isLanguageSupported: (code: string) => {
|
|
176
|
+
return languageRepository.isLanguageSupported(code);
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
getSupportedLanguages: () => {
|
|
180
|
+
return languageRepository.getLanguages();
|
|
181
|
+
},
|
|
182
|
+
};
|
|
@@ -6,17 +6,17 @@ export const isDev = () => {
|
|
|
6
6
|
}
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
-
export const devLog = (
|
|
9
|
+
export const devLog = (_message: string, ..._args: unknown[]) => {
|
|
10
10
|
if (isDev()) {
|
|
11
11
|
}
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
-
export const devError = (
|
|
14
|
+
export const devError = (_message: string, ..._args: unknown[]) => {
|
|
15
15
|
if (isDev()) {
|
|
16
16
|
}
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
export const devWarn = (
|
|
19
|
+
export const devWarn = (_message: string, ..._args: unknown[]) => {
|
|
20
20
|
if (isDev()) {
|
|
21
21
|
}
|
|
22
22
|
};
|
package/src/domains/notifications/reminders/presentation/components/ReminderForm.constants.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Reminder,
|
|
1
|
+
import type { Reminder, CreateReminderInput, TimePreset } from '../../../infrastructure/services/types';
|
|
2
2
|
|
|
3
3
|
export const DEFAULT_HOUR = 9;
|
|
4
4
|
export const DEFAULT_MINUTE = 0;
|