@umituz/react-native-settings 4.23.84 → 4.23.86

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 (59) hide show
  1. package/package.json +3 -3
  2. package/src/domains/about/presentation/hooks/useAboutInfo.ts +1 -3
  3. package/src/domains/appearance/presentation/components/ColorPicker.tsx +0 -1
  4. package/src/domains/appearance/presentation/components/ThemeModeSection.tsx +0 -1
  5. package/src/domains/appearance/presentation/components/ThemeOption.tsx +0 -1
  6. package/src/domains/faqs/presentation/screens/FAQScreen.tsx +1 -1
  7. package/src/domains/feedback/presentation/components/FeedbackForm.styles.ts +1 -1
  8. package/src/domains/feedback/presentation/components/FeedbackForm.tsx +1 -2
  9. package/src/domains/feedback/presentation/components/FeedbackModal.tsx +12 -12
  10. package/src/domains/feedback/presentation/components/SupportSection.tsx +0 -1
  11. package/src/domains/gamification/components/GamificationScreen/GamificationScreen.tsx +1 -6
  12. package/src/domains/localization/infrastructure/components/useLanguageSwitcher.ts +0 -1
  13. package/src/domains/localization/infrastructure/hooks/useLanguageSelection.ts +0 -4
  14. package/src/domains/localization/infrastructure/hooks/useTranslation.ts +0 -1
  15. package/src/domains/localization/infrastructure/storage/LanguageInitializer.ts +0 -1
  16. package/src/domains/localization/infrastructure/storage/LanguageSwitcher.ts +0 -4
  17. package/src/domains/localization/infrastructure/storage/LocalizationStore.ts +0 -5
  18. package/src/domains/localization/presentation/components/LanguageItem.tsx +0 -1
  19. package/src/domains/localization/presentation/providers/LocalizationManager.tsx +0 -1
  20. package/src/domains/localization/presentation/screens/LanguageSelectionScreen.tsx +0 -3
  21. package/src/domains/notifications/infrastructure/services/NotificationService.ts +0 -1
  22. package/src/domains/notifications/infrastructure/utils/dev.ts +3 -6
  23. package/src/domains/notifications/quietHours/infrastructure/hooks/useQuietHoursActions.ts +0 -1
  24. package/src/domains/notifications/reminders/infrastructure/hooks/useReminderActions.ts +0 -6
  25. package/src/domains/notifications/reminders/presentation/components/ReminderForm.constants.ts +1 -1
  26. package/src/domains/notifications/reminders/presentation/components/ReminderForm.tsx +1 -6
  27. package/src/domains/rating/application/services/RatingService.ts +13 -63
  28. package/src/domains/rating/infrastructure/storage/RatingStorage.ts +14 -36
  29. package/src/domains/rating/presentation/hooks/useAppRating.tsx +0 -1
  30. package/src/infrastructure/utils/dateUtils.ts +61 -0
  31. package/src/infrastructure/utils/memoComparisonUtils.ts +66 -0
  32. package/src/infrastructure/utils/sanitizers.ts +49 -0
  33. package/src/infrastructure/utils/translationHelpers.ts +81 -0
  34. package/src/infrastructure/utils/validators.ts +59 -0
  35. package/src/presentation/components/SettingsItemCard.tsx +129 -172
  36. package/src/presentation/components/settings/SettingsItemCardContent.tsx +70 -0
  37. package/src/presentation/components/settings/SettingsItemCardRightElement.tsx +42 -0
  38. package/src/presentation/components/settings/SettingsItemCardSection.tsx +29 -0
  39. package/src/presentation/hooks/useSettingsScreenConfig.ts +19 -54
  40. package/src/presentation/navigation/SettingsStackNavigator.tsx +1 -24
  41. package/src/presentation/navigation/hooks/useSettingsScreens.ts +76 -90
  42. package/src/presentation/screens/components/GamificationSettingsItem.tsx +20 -17
  43. package/src/presentation/screens/components/SettingsContent.tsx +29 -0
  44. package/src/presentation/screens/components/SubscriptionSettingsItem.tsx +13 -4
  45. package/src/presentation/screens/components/VideoTutorialSettingsItem.tsx +47 -0
  46. package/src/presentation/screens/components/WalletSettingsItem.tsx +13 -4
  47. package/src/presentation/screens/components/sections/CustomSettingsList.tsx +12 -5
  48. package/src/presentation/screens/components/sections/FeatureSettingsSection.tsx +12 -11
  49. package/src/presentation/screens/components/sections/IdentitySettingsSection.tsx +4 -1
  50. package/src/presentation/screens/components/sections/ProfileSectionLoader.tsx +16 -10
  51. package/src/presentation/screens/components/sections/SupportSettingsSection.tsx +9 -4
  52. package/src/presentation/screens/hooks/useFeatureDetection.ts +2 -0
  53. package/src/presentation/screens/types/SettingsConfig.ts +8 -1
  54. package/src/presentation/screens/types/UserFeatureConfig.ts +20 -0
  55. package/src/presentation/screens/types/index.ts +1 -0
  56. package/src/presentation/screens/utils/normalizeConfig.ts +7 -1
  57. package/src/presentation/utils/accountConfigUtils.ts +67 -0
  58. package/src/presentation/utils/screenFactory.ts +87 -0
  59. package/src/presentation/utils/userProfileUtils.ts +51 -0
@@ -24,10 +24,7 @@ export async function getEventCount(eventType: string): Promise<number> {
24
24
  const result = await storageRepository.getString(KEYS.eventCount(eventType), "0");
25
25
  const count = parseInt(unwrap(result, "0"), 10);
26
26
  return Number.isNaN(count) ? 0 : count;
27
- } catch (error) {
28
- if (typeof __DEV__ !== "undefined" && __DEV__) {
29
- console.error("[RatingStorage] Error getting event count:", error);
30
- }
27
+ } catch {
31
28
  return 0;
32
29
  }
33
30
  }
@@ -38,10 +35,8 @@ export async function getEventCount(eventType: string): Promise<number> {
38
35
  export async function setEventCount(eventType: string, count: number): Promise<void> {
39
36
  try {
40
37
  await storageRepository.setString(KEYS.eventCount(eventType), count.toString());
41
- } catch (error) {
42
- if (typeof __DEV__ !== "undefined" && __DEV__) {
43
- console.error("[RatingStorage] Error setting event count:", error);
44
- }
38
+ } catch {
39
+ // Silent error handling
45
40
  }
46
41
  }
47
42
 
@@ -61,10 +56,7 @@ export async function getLastPromptDate(eventType: string): Promise<string | nul
61
56
  const result = await storageRepository.getString(KEYS.lastPrompt(eventType), "");
62
57
  const date = unwrap(result, "");
63
58
  return date || null;
64
- } catch (error) {
65
- if (typeof __DEV__ !== "undefined" && __DEV__) {
66
- console.error("[RatingStorage] Error getting last prompt date:", error);
67
- }
59
+ } catch {
68
60
  return null;
69
61
  }
70
62
  }
@@ -75,10 +67,8 @@ export async function getLastPromptDate(eventType: string): Promise<string | nul
75
67
  export async function setLastPromptDate(eventType: string, date: string): Promise<void> {
76
68
  try {
77
69
  await storageRepository.setString(KEYS.lastPrompt(eventType), date);
78
- } catch (error) {
79
- if (typeof __DEV__ !== "undefined" && __DEV__) {
80
- console.error("[RatingStorage] Error setting last prompt date:", error);
81
- }
70
+ } catch {
71
+ // Silent error handling
82
72
  }
83
73
  }
84
74
 
@@ -89,10 +79,7 @@ export async function getHasRated(): Promise<boolean> {
89
79
  try {
90
80
  const result = await storageRepository.getString(KEYS.hasRated, "false");
91
81
  return unwrap(result, "false") === "true";
92
- } catch (error) {
93
- if (typeof __DEV__ !== "undefined" && __DEV__) {
94
- console.error("[RatingStorage] Error getting hasRated:", error);
95
- }
82
+ } catch {
96
83
  return false;
97
84
  }
98
85
  }
@@ -103,10 +90,8 @@ export async function getHasRated(): Promise<boolean> {
103
90
  export async function setHasRated(value: boolean): Promise<void> {
104
91
  try {
105
92
  await storageRepository.setString(KEYS.hasRated, value.toString());
106
- } catch (error) {
107
- if (typeof __DEV__ !== "undefined" && __DEV__) {
108
- console.error("[RatingStorage] Error setting hasRated:", error);
109
- }
93
+ } catch {
94
+ // Silent error handling
110
95
  }
111
96
  }
112
97
 
@@ -117,10 +102,7 @@ export async function getDismissed(): Promise<boolean> {
117
102
  try {
118
103
  const result = await storageRepository.getString(KEYS.dismissed, "false");
119
104
  return unwrap(result, "false") === "true";
120
- } catch (error) {
121
- if (typeof __DEV__ !== "undefined" && __DEV__) {
122
- console.error("[RatingStorage] Error getting dismissed:", error);
123
- }
105
+ } catch {
124
106
  return false;
125
107
  }
126
108
  }
@@ -131,10 +113,8 @@ export async function getDismissed(): Promise<boolean> {
131
113
  export async function setDismissed(value: boolean): Promise<void> {
132
114
  try {
133
115
  await storageRepository.setString(KEYS.dismissed, value.toString());
134
- } catch (error) {
135
- if (typeof __DEV__ !== "undefined" && __DEV__) {
136
- console.error("[RatingStorage] Error setting dismissed:", error);
137
- }
116
+ } catch {
117
+ // Silent error handling
138
118
  }
139
119
  }
140
120
 
@@ -174,9 +154,7 @@ export async function reset(eventType?: string): Promise<void> {
174
154
  ratingKeys.map((key) => storageRepository.removeItem(key))
175
155
  );
176
156
  }
177
- } catch (error) {
178
- if (typeof __DEV__ !== "undefined" && __DEV__) {
179
- console.error("[RatingStorage] Error resetting data:", error);
180
- }
157
+ } catch {
158
+ // Silent error handling
181
159
  }
182
160
  }
@@ -73,7 +73,6 @@ export function useAppRating(config: RatingConfig): UseAppRatingResult {
73
73
  }
74
74
  } catch (error) {
75
75
  if (typeof __DEV__ !== "undefined" && __DEV__) {
76
- console.error("[useAppRating] Error requesting review:", error);
77
76
  }
78
77
  }
79
78
 
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Date Utilities
3
+ *
4
+ * Helper functions for date manipulation and calculations.
5
+ */
6
+
7
+ /**
8
+ * Calculate days between two dates
9
+ * @param dateString ISO date string
10
+ * @param now Current date (defaults to new Date())
11
+ * @returns Number of days between dates
12
+ */
13
+ export function daysBetween(dateString: string, now: Date = new Date()): number {
14
+ const date = new Date(dateString);
15
+ const diffMs = now.getTime() - date.getTime();
16
+ return Math.floor(diffMs / (1000 * 60 * 60 * 24));
17
+ }
18
+
19
+ /**
20
+ * Format date to ISO string
21
+ * @param date Date to format
22
+ * @returns ISO date string
23
+ */
24
+ export function toISOString(date: Date = new Date()): string {
25
+ return date.toISOString();
26
+ }
27
+
28
+ /**
29
+ * Check if a date is within the last N days
30
+ * @param dateString ISO date string to check
31
+ * @param days Number of days to check within
32
+ * @returns true if date is within the last N days
33
+ */
34
+ export function isWithinLastDays(dateString: string, days: number): boolean {
35
+ const daysDiff = daysBetween(dateString);
36
+ return daysDiff >= 0 && daysDiff <= days;
37
+ }
38
+
39
+ /**
40
+ * Check if a date has passed (is older than N days ago)
41
+ * @param dateString ISO date string to check
42
+ * @param days Number of days threshold
43
+ * @returns true if date is older than N days
44
+ */
45
+ export function isOlderThanDays(dateString: string, days: number): boolean {
46
+ const daysDiff = daysBetween(dateString);
47
+ return daysDiff > days;
48
+ }
49
+
50
+ /**
51
+ * Add days to a date
52
+ * @param dateString ISO date string
53
+ * @param days Number of days to add
54
+ * @returns New date with days added
55
+ */
56
+ export function addDays(dateString: string, days: number): Date {
57
+ const date = new Date(dateString);
58
+ const result = new Date(date);
59
+ result.setDate(result.getDate() + days);
60
+ return result;
61
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Memo Comparison Utilities
3
+ *
4
+ * Provides reusable comparison functions for React.memo to reduce code duplication.
5
+ * These functions follow best practices for shallow comparison in React components.
6
+ */
7
+
8
+ /**
9
+ * Standard comparison for components with config and translation props
10
+ * @param prevProps Previous props
11
+ * @param nextProps Next props
12
+ * @returns true if props are equal (should not re-render)
13
+ */
14
+ export function compareConfigAndTranslate(
15
+ prevProps: Record<string, unknown>,
16
+ nextProps: Record<string, unknown>
17
+ ): boolean {
18
+ return (
19
+ prevProps.config === nextProps.config &&
20
+ prevProps.t === nextProps.t
21
+ );
22
+ }
23
+
24
+ /**
25
+ * Standard comparison for components with normalized config and features
26
+ * @param prevProps Previous props
27
+ * @param nextProps Next props
28
+ * @returns true if props are equal (should not re-render)
29
+ */
30
+ export function compareConfigAndFeatures(
31
+ prevProps: Record<string, unknown>,
32
+ nextProps: Record<string, unknown>
33
+ ): boolean {
34
+ return (
35
+ prevProps.normalizedConfig === nextProps.normalizedConfig &&
36
+ prevProps.features === nextProps.features
37
+ );
38
+ }
39
+
40
+ /**
41
+ * Standard comparison for components with single prop
42
+ * @param propKey Property key to compare
43
+ * @returns Comparison function
44
+ */
45
+ export function createSinglePropComparator<K extends string>(
46
+ propKey: K
47
+ ): (prevProps: Record<string, unknown>, nextProps: Record<string, unknown>) => boolean {
48
+ return (prevProps, nextProps) => prevProps[propKey] === nextProps[propKey];
49
+ }
50
+
51
+ /**
52
+ * Standard comparison for components with gamification config
53
+ * @param prevProps Previous props
54
+ * @param nextProps Next props
55
+ * @returns true if props are equal (should not re-render)
56
+ */
57
+ export function compareGamificationProps(
58
+ prevProps: Record<string, unknown>,
59
+ nextProps: Record<string, unknown>
60
+ ): boolean {
61
+ return (
62
+ prevProps.config === nextProps.config &&
63
+ prevProps.gamificationConfig === nextProps.gamificationConfig &&
64
+ prevProps.t === nextProps.t
65
+ );
66
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Sanitization Utilities
3
+ *
4
+ * Provides sanitization functions for user input and props.
5
+ */
6
+
7
+ /**
8
+ * Sanitize string by trimming and limiting length
9
+ */
10
+ export function sanitizeString(str: string | undefined, maxLength: number): string {
11
+ if (!str) return "";
12
+ return str.trim().slice(0, maxLength);
13
+ }
14
+
15
+ /**
16
+ * Sanitize title prop
17
+ */
18
+ export function sanitizeTitle(title: string): string {
19
+ return sanitizeString(title, 200) || "";
20
+ }
21
+
22
+ /**
23
+ * Sanitize description prop
24
+ */
25
+ export function sanitizeDescription(description: string | undefined): string | undefined {
26
+ return sanitizeString(description || "", 500);
27
+ }
28
+
29
+ /**
30
+ * Truncate text with ellipsis
31
+ */
32
+ export function truncateText(text: string, maxLength: number): string {
33
+ if (text.length <= maxLength) return text;
34
+ return text.slice(0, maxLength - 3) + "...";
35
+ }
36
+
37
+ /**
38
+ * Escape HTML entities (for security)
39
+ */
40
+ export function escapeHtml(text: string): string {
41
+ const htmlEntities: Record<string, string> = {
42
+ "&": "&amp;",
43
+ "<": "&lt;",
44
+ ">": "&gt;",
45
+ '"': "&quot;",
46
+ "'": "&#39;",
47
+ };
48
+ return text.replace(/[&<>"']/g, (char) => htmlEntities[char] || char);
49
+ }
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Translation Helper Utilities
3
+ *
4
+ * Provides safe translation utilities that handle cases where
5
+ * the translation function might not be available yet.
6
+ */
7
+
8
+ export type TranslationFunction = (key: string, params?: Record<string, string | number>) => string;
9
+
10
+ /**
11
+ * Safely get a translation with fallback
12
+ * @param t Translation function
13
+ * @param key Translation key
14
+ * @param fallback Fallback text if translation fails
15
+ * @returns Translated text or fallback
16
+ */
17
+ export function getTranslationWithFallback(
18
+ t: TranslationFunction | unknown,
19
+ key: string,
20
+ fallback: string
21
+ ): string {
22
+ if (typeof t === "function") {
23
+ try {
24
+ return t(key);
25
+ } catch {
26
+ return fallback;
27
+ }
28
+ }
29
+ return fallback;
30
+ }
31
+
32
+ /**
33
+ * Get translation with parameters support
34
+ * @param t Translation function
35
+ * @param key Translation key
36
+ * @param params Parameters for translation
37
+ * @param fallback Fallback text if translation fails
38
+ * @returns Translated text or fallback
39
+ */
40
+ export function getTranslationWithParams(
41
+ t: TranslationFunction | unknown,
42
+ key: string,
43
+ params: Record<string, string | number>,
44
+ fallback: string
45
+ ): string {
46
+ if (typeof t === "function") {
47
+ try {
48
+ return t(key, params);
49
+ } catch {
50
+ return fallback;
51
+ }
52
+ }
53
+ return fallback;
54
+ }
55
+
56
+ /**
57
+ * Check if translation function is available
58
+ * @param t Translation function to check
59
+ * @returns true if translation function is available
60
+ */
61
+ export function isTranslationFunctionAvailable(t: unknown): t is TranslationFunction {
62
+ return typeof t === "function";
63
+ }
64
+
65
+ /**
66
+ * Create a safe translation wrapper that always returns a string
67
+ * @param t Translation function
68
+ * @returns Safe translation function
69
+ */
70
+ export function createSafeTranslator(t: TranslationFunction | unknown): TranslationFunction {
71
+ return (key: string, params?: Record<string, string | number>) => {
72
+ if (isTranslationFunctionAvailable(t)) {
73
+ try {
74
+ return t(key, params);
75
+ } catch {
76
+ return key;
77
+ }
78
+ }
79
+ return key;
80
+ };
81
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Validation Utilities
3
+ *
4
+ * Provides validation functions for component props.
5
+ */
6
+
7
+ export interface ValidationWarning {
8
+ message: string;
9
+ value: unknown;
10
+ }
11
+
12
+ /**
13
+ * Validate settings item title
14
+ */
15
+ export function validateTitle(title: unknown): ValidationWarning | null {
16
+ if (!title || typeof title !== "string") {
17
+ return {
18
+ message: "[SettingsItemCard] Invalid title prop",
19
+ value: title,
20
+ };
21
+ }
22
+ return null;
23
+ }
24
+
25
+ /**
26
+ * Validate settings item description
27
+ */
28
+ export function validateDescription(description: unknown): ValidationWarning | null {
29
+ if (description && typeof description !== "string") {
30
+ return {
31
+ message: "[SettingsItemCard] Invalid description prop",
32
+ value: description,
33
+ };
34
+ }
35
+ return null;
36
+ }
37
+
38
+ /**
39
+ * Validate switch props
40
+ */
41
+ export function validateSwitchProps(
42
+ showSwitch: boolean,
43
+ onSwitchChange?: ((value: boolean) => void) | null
44
+ ): ValidationWarning | null {
45
+ if (showSwitch && !onSwitchChange) {
46
+ return {
47
+ message: "[SettingsItemCard] Switch shown but no onSwitchChange provided",
48
+ value: { showSwitch, hasOnSwitchChange: !!onSwitchChange },
49
+ };
50
+ }
51
+ return null;
52
+ }
53
+
54
+ /**
55
+ * Return validation result (no logging)
56
+ */
57
+ export function getValidationResult(warning: ValidationWarning | null): ValidationWarning | null {
58
+ return warning;
59
+ }