@umituz/react-native-settings 5.2.18 → 5.2.20

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 (46) hide show
  1. package/package.json +1 -1
  2. package/src/domains/about/presentation/hooks/useAboutInfo.ts +63 -98
  3. package/src/domains/feedback/presentation/components/SupportSection.tsx +2 -1
  4. package/src/domains/localization/infrastructure/hooks/useTranslation.ts +0 -3
  5. package/src/domains/localization/infrastructure/storage/LanguageInitializer.ts +0 -4
  6. package/src/domains/localization/infrastructure/storage/localizationStoreUtils.ts +6 -7
  7. package/src/domains/localization/presentation/components/LanguageItem.tsx +0 -2
  8. package/src/domains/localization/presentation/providers/LocalizationManager.tsx +2 -1
  9. package/src/domains/notifications/index.ts +7 -6
  10. package/src/domains/notifications/infrastructure/services/NotificationBadgeManager.ts +1 -1
  11. package/src/domains/notifications/infrastructure/services/NotificationManager.ts +1 -1
  12. package/src/domains/notifications/infrastructure/services/NotificationPermissions.ts +1 -1
  13. package/src/domains/notifications/infrastructure/storage/UnifiedNotificationStore.ts +223 -0
  14. package/src/domains/notifications/presentation/hooks/useNotificationSettingsUI.ts +2 -2
  15. package/src/domains/notifications/presentation/screens/NotificationSettingsScreen.tsx +1 -1
  16. package/src/domains/notifications/quietHours/infrastructure/hooks/useQuietHoursActions.ts +6 -6
  17. package/src/domains/notifications/reminders/infrastructure/hooks/useReminderActions.ts +5 -5
  18. package/src/domains/notifications/reminders/presentation/screens/ReminderListScreen.tsx +1 -1
  19. package/src/domains/rating/presentation/hooks/useAppRating.tsx +2 -1
  20. package/src/infrastructure/storage/storeConfig.ts +114 -0
  21. package/src/infrastructure/utils/async/core.ts +3 -2
  22. package/src/infrastructure/utils/errorHandlers.ts +4 -2
  23. package/src/infrastructure/utils/index.ts +1 -1
  24. package/src/presentation/components/SettingsNavigationItem.tsx +109 -0
  25. package/src/presentation/navigation/utils/index.ts +8 -1
  26. package/src/presentation/navigation/utils/navigationHelpers.ts +118 -0
  27. package/src/presentation/screens/components/SettingsContent.tsx +21 -11
  28. package/src/presentation/screens/components/sections/SupportSettingsSection.tsx +4 -2
  29. package/src/presentation/screens/types/UserFeatureConfig.ts +5 -4
  30. package/src/presentation/utils/config-creators/feature-configs.ts +3 -2
  31. package/src/presentation/utils/settingsConfigFactory.ts +2 -1
  32. package/src/utils/appUtils.ts +0 -6
  33. package/src/utils/errorUtils.ts +54 -0
  34. package/src/utils/hooks/index.ts +6 -0
  35. package/src/utils/hooks/useAsyncStateUpdate.ts +114 -0
  36. package/src/utils/hooks/useMountSafety.ts +30 -0
  37. package/src/utils/index.ts +2 -0
  38. package/src/domains/about/presentation/hooks/useAboutInfo.utils.ts +0 -167
  39. package/src/domains/notifications/infrastructure/storage/NotificationsStore.ts +0 -45
  40. package/src/domains/notifications/infrastructure/utils/dev.ts +0 -22
  41. package/src/domains/notifications/reminders/infrastructure/storage/RemindersStore.ts +0 -152
  42. package/src/infrastructure/utils/styleUtils.ts +0 -7
  43. package/src/presentation/screens/components/GamificationSettingsItem.tsx +0 -55
  44. package/src/presentation/screens/components/SubscriptionSettingsItem.tsx +0 -38
  45. package/src/presentation/screens/components/VideoTutorialSettingsItem.tsx +0 -51
  46. package/src/presentation/screens/components/WalletSettingsItem.tsx +0 -36
@@ -4,28 +4,28 @@
4
4
  */
5
5
 
6
6
  import { useCallback } from 'react';
7
- import { usePreferencesStore, useQuietHours } from '../../../reminders/infrastructure/storage/RemindersStore';
7
+ import { useNotificationStore, useQuietHours } from '../../../infrastructure/storage/UnifiedNotificationStore';
8
8
  import type { QuietHoursConfig } from '../../../infrastructure/services/types';
9
9
 
10
10
  export const useQuietHoursActions = () => {
11
11
  const quietHours = useQuietHours();
12
- const { updateQuietHours } = usePreferencesStore();
12
+ const { updateQuietHours } = useNotificationStore();
13
13
 
14
14
  const setQuietHoursEnabled = useCallback(async (enabled: boolean): Promise<void> => {
15
15
  // Use getState() to avoid stale closure and race conditions
16
- const currentQuietHours = usePreferencesStore.getState().preferences.quietHours;
16
+ const currentQuietHours = useNotificationStore.getState().preferences.quietHours;
17
17
  await updateQuietHours({ ...currentQuietHours, enabled });
18
18
  }, [updateQuietHours]);
19
19
 
20
20
  const setStartTime = useCallback(async (hour: number, minute: number): Promise<void> => {
21
21
  // Use getState() to avoid stale closure and race conditions
22
- const currentQuietHours = usePreferencesStore.getState().preferences.quietHours;
22
+ const currentQuietHours = useNotificationStore.getState().preferences.quietHours;
23
23
  await updateQuietHours({ ...currentQuietHours, startHour: hour, startMinute: minute });
24
24
  }, [updateQuietHours]);
25
25
 
26
26
  const setEndTime = useCallback(async (hour: number, minute: number): Promise<void> => {
27
27
  // Use getState() to avoid stale closure and race conditions
28
- const currentQuietHours = usePreferencesStore.getState().preferences.quietHours;
28
+ const currentQuietHours = useNotificationStore.getState().preferences.quietHours;
29
29
  await updateQuietHours({ ...currentQuietHours, endHour: hour, endMinute: minute });
30
30
  }, [updateQuietHours]);
31
31
 
@@ -35,7 +35,7 @@ export const useQuietHoursActions = () => {
35
35
 
36
36
  const isInQuietHours = useCallback((): boolean => {
37
37
  // Use getState() to get current quietHours for consistency
38
- const currentQuietHours = usePreferencesStore.getState().preferences.quietHours;
38
+ const currentQuietHours = useNotificationStore.getState().preferences.quietHours;
39
39
 
40
40
  if (!currentQuietHours.enabled) return false;
41
41
 
@@ -4,14 +4,14 @@
4
4
  */
5
5
 
6
6
  import { useCallback, useMemo } from 'react';
7
- import { useRemindersStore } from '../storage/RemindersStore';
7
+ import { useNotificationStore } from '../../../infrastructure/storage/UnifiedNotificationStore';
8
8
  import { NotificationScheduler } from '../../../infrastructure/services/NotificationScheduler';
9
9
  import { generateReminderId } from '../../../infrastructure/utils/idGenerator';
10
10
  import { buildTrigger } from '../../../infrastructure/utils/triggerBuilder';
11
11
  import type { Reminder, CreateReminderInput, UpdateReminderInput } from '../../../infrastructure/services/types';
12
12
 
13
13
  export const useReminderActions = () => {
14
- const { addReminder, updateReminder, deleteReminder, toggleReminder: _toggleReminder } = useRemindersStore();
14
+ const { addReminder, updateReminder, deleteReminder, toggleReminder: _toggleReminder } = useNotificationStore();
15
15
 
16
16
  // Lazy initialization of scheduler
17
17
  const scheduler = useMemo(() => new NotificationScheduler(), []);
@@ -47,7 +47,7 @@ export const useReminderActions = () => {
47
47
 
48
48
  const editReminder = useCallback(async (id: string, input: UpdateReminderInput): Promise<void> => {
49
49
  // Get current state BEFORE async operations to prevent race condition
50
- const existing = useRemindersStore.getState().reminders.find(r => r.id === id);
50
+ const existing = useNotificationStore.getState().reminders.find(r => r.id === id);
51
51
 
52
52
  if (!existing) {
53
53
  throw new Error(`Reminder with id ${id} not found`);
@@ -86,7 +86,7 @@ export const useReminderActions = () => {
86
86
 
87
87
  const removeReminder = useCallback(async (id: string): Promise<void> => {
88
88
  // Get current state BEFORE async operations to prevent race condition
89
- const reminder = useRemindersStore.getState().reminders.find(r => r.id === id);
89
+ const reminder = useNotificationStore.getState().reminders.find(r => r.id === id);
90
90
 
91
91
  if (!reminder) {
92
92
  throw new Error(`Reminder with id ${id} not found`);
@@ -105,7 +105,7 @@ export const useReminderActions = () => {
105
105
 
106
106
  const toggleReminderEnabled = useCallback(async (id: string): Promise<void> => {
107
107
  // Get current state BEFORE async operations to prevent race condition
108
- const reminder = useRemindersStore.getState().reminders.find(r => r.id === id);
108
+ const reminder = useNotificationStore.getState().reminders.find(r => r.id === id);
109
109
 
110
110
  if (!reminder) {
111
111
  throw new Error(`Reminder with id ${id} not found`);
@@ -15,7 +15,7 @@ import {
15
15
  useAppDesignTokens
16
16
  } from '@umituz/react-native-design-system';
17
17
  import { ReminderItem } from '../components/ReminderItem';
18
- import { useReminders, useRemindersLoading } from '../../infrastructure/storage/RemindersStore';
18
+ import { useReminders, useRemindersLoading } from '../../../infrastructure/storage/UnifiedNotificationStore';
19
19
  import { useReminderActions } from '../../infrastructure/hooks/useReminderActions';
20
20
  import type { Reminder, ReminderTranslations } from '../../../infrastructure/services/types';
21
21
  import { devError } from '../../../../../utils/devUtils';
@@ -13,6 +13,7 @@ import type {
13
13
  import { DEFAULT_RATING_CONFIG } from "../../domain/entities/RatingConfig";
14
14
  import * as RatingService from "../../application/services/RatingService";
15
15
  import { RatingPromptModal } from "../components/RatingPromptModal";
16
+ import { isDev } from "../../../../utils/devUtils";
16
17
 
17
18
  /**
18
19
  * App rating hook with 2-step prompt flow
@@ -72,7 +73,7 @@ export function useAppRating(config: RatingConfig): UseAppRatingResult {
72
73
  await StoreReview.requestReview();
73
74
  }
74
75
  } catch (error) {
75
- if (typeof __DEV__ !== "undefined" && __DEV__) {
76
+ if (isDev()) {
76
77
  console.error('[useAppRating] Failed to request review:', error);
77
78
  }
78
79
  }
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Store Configuration Standards
3
+ * Standardized patterns for all Zustand stores
4
+ *
5
+ * Provides consistent configuration for:
6
+ * - Persistence settings
7
+ * - Version management
8
+ * - State partializing (excluding transient state)
9
+ * - Storage service integration
10
+ */
11
+
12
+ import type { StateStorage } from 'zustand/middleware';
13
+ import { storageService } from '@umituz/react-native-design-system';
14
+
15
+ /**
16
+ * Standard store configuration interface
17
+ */
18
+ export interface StandardStoreConfig<T> {
19
+ /** Unique storage key name */
20
+ name: string;
21
+ /** Store version for migrations */
22
+ version: number;
23
+ /** Enable persistence */
24
+ persist: boolean;
25
+ /** Custom partialize function to control what gets persisted */
26
+ partialize?: (state: T) => Partial<T>;
27
+ /** Storage service (defaults to design-system's storageService) */
28
+ storage?: StateStorage;
29
+ }
30
+
31
+ /**
32
+ * Create standardized store configuration
33
+ *
34
+ * @param config - Store configuration
35
+ * @returns Complete store config with defaults
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * const config = createStoreConfig({
40
+ * name: 'my-store',
41
+ * version: 1,
42
+ * persist: true,
43
+ * partialize: excludeTransientState,
44
+ * });
45
+ * ```
46
+ */
47
+ export const createStoreConfig = <T extends object>(
48
+ config: StandardStoreConfig<T>
49
+ ): Required<StandardStoreConfig<T>> => {
50
+ return {
51
+ name: config.name,
52
+ version: config.version,
53
+ persist: config.persist,
54
+ partialize: config.partialize || ((state) => state),
55
+ storage: config.storage || storageService,
56
+ };
57
+ };
58
+
59
+ /**
60
+ * Base interface for store state with transient flags
61
+ */
62
+ export interface BaseStoreState {
63
+ /** Loading state (never persisted) */
64
+ isLoading?: boolean;
65
+ /** Initialization state (never persisted) */
66
+ isInitialized?: boolean;
67
+ }
68
+
69
+ /**
70
+ * Standard partialize function that excludes loading/initialization flags
71
+ * These flags are runtime state and should never be persisted
72
+ *
73
+ * @param state - Store state
74
+ * @returns State with transient flags excluded
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * const config = createStoreConfig({
79
+ * name: 'my-store',
80
+ * version: 1,
81
+ * persist: true,
82
+ * partialize: excludeTransientState,
83
+ * });
84
+ * ```
85
+ */
86
+ export const excludeTransientState = <T extends BaseStoreState>(
87
+ state: T
88
+ ): Partial<T> => {
89
+ const { isLoading, isInitialized, ...persistedState } = state;
90
+ return persistedState as Partial<T>;
91
+ };
92
+
93
+ /**
94
+ * Create partialize function that excludes specific keys
95
+ *
96
+ * @param excludeKeys - Keys to exclude from persistence
97
+ * @returns Partialize function
98
+ *
99
+ * @example
100
+ * ```typescript
101
+ * const partialize = createPartializeExcluding(['isLoading', 'error', 'tempData']);
102
+ * ```
103
+ */
104
+ export const createPartializeExcluding = <T extends object>(
105
+ excludeKeys: (keyof T)[]
106
+ ) => {
107
+ return (state: T): Partial<T> => {
108
+ const result = { ...state };
109
+ excludeKeys.forEach((key) => {
110
+ delete result[key];
111
+ });
112
+ return result;
113
+ };
114
+ };
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import type { ValidationResult } from "../validation";
7
+ import { isDev } from '../../../utils/devUtils';
7
8
 
8
9
  /**
9
10
  * Result type for async operations
@@ -58,7 +59,7 @@ export const createAsyncHandler = <T extends unknown[], R>(
58
59
  onSuccess?.(result);
59
60
  } catch (callbackError) {
60
61
  // Log callback error but don't treat it as handler error
61
- if (typeof __DEV__ !== "undefined" && __DEV__) {
62
+ if (isDev()) {
62
63
  console.error("[createAsyncHandler] onSuccess callback error:", callbackError);
63
64
  }
64
65
  }
@@ -72,7 +73,7 @@ export const createAsyncHandler = <T extends unknown[], R>(
72
73
  onLoadingEnd?.();
73
74
  } catch (callbackError) {
74
75
  // Log callback error but don't throw
75
- if (typeof __DEV__ !== "undefined" && __DEV__) {
76
+ if (isDev()) {
76
77
  console.error("[createAsyncHandler] onLoadingEnd callback error:", callbackError);
77
78
  }
78
79
  }
@@ -4,6 +4,8 @@
4
4
  * FIXED: Added safety checks for showToast and proper error handling
5
5
  */
6
6
 
7
+ import { isDev } from '../../utils/devUtils';
8
+
7
9
  /**
8
10
  * Error types for better error classification
9
11
  */
@@ -182,7 +184,7 @@ export const logError = (
182
184
  };
183
185
 
184
186
  // Log errors in development mode
185
- if (typeof __DEV__ !== "undefined" && __DEV__) {
187
+ if (isDev()) {
186
188
  console.error("[Error]", logData);
187
189
  }
188
190
 
@@ -216,7 +218,7 @@ export const handleError = (
216
218
  }
217
219
  } catch (toastError) {
218
220
  // Log toast error but don't crash error handling
219
- if (typeof __DEV__ !== "undefined" && __DEV__) {
221
+ if (isDev()) {
220
222
  console.error("[ErrorHandlers] showToast failed:", toastError);
221
223
  }
222
224
  }
@@ -3,7 +3,7 @@
3
3
  * Centralized utility functions for common operations
4
4
  */
5
5
 
6
- export * from './styleUtils';
6
+ export * from './styles';
7
7
  export * from './memoUtils';
8
8
  export * from './styleTokens';
9
9
  export * from './configFactory';
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Generic Settings Navigation Item
3
+ * Consolidates VideoTutorialSettingsItem, GamificationSettingsItem,
4
+ * WalletSettingsItem, and SubscriptionSettingsItem into one reusable component
5
+ */
6
+
7
+ import React from "react";
8
+ import type { IconName } from "@umituz/react-native-design-system";
9
+ import { SettingsItemCard } from "./SettingsItemCard";
10
+ import { useSettingsNavigation } from "../navigation/hooks/useSettingsNavigation";
11
+ import { createRouteOrPressHandler } from "../navigation/utils/navigationHelpers";
12
+ import type { SettingsStackParamList } from "../navigation/types";
13
+
14
+ /**
15
+ * Base configuration for navigation items
16
+ */
17
+ export interface SettingsNavigationItemConfig {
18
+ title?: string;
19
+ description?: string;
20
+ icon?: string;
21
+ sectionTitle?: string;
22
+ route?: keyof SettingsStackParamList;
23
+ onPress?: () => void;
24
+ }
25
+
26
+ /**
27
+ * Props for generic settings navigation item
28
+ */
29
+ export interface SettingsNavigationItemProps<T extends SettingsNavigationItemConfig = SettingsNavigationItemConfig> {
30
+ /** Item configuration */
31
+ config: T;
32
+ /** Default icon if not specified in config */
33
+ defaultIcon?: IconName;
34
+ /** Default route if not specified in config */
35
+ defaultRoute?: keyof SettingsStackParamList;
36
+ /** Function to render custom description */
37
+ renderDescription?: (config: T) => string | undefined;
38
+ /** Remove background styling */
39
+ noBackground?: boolean;
40
+ /** Remove margin */
41
+ hideMargin?: boolean;
42
+ }
43
+
44
+ /**
45
+ * Generic Settings Navigation Item Component
46
+ *
47
+ * @example
48
+ * ```tsx
49
+ * <SettingsNavigationItem
50
+ * config={videoConfig}
51
+ * defaultIcon="play-circle-outline"
52
+ * defaultRoute="VideoTutorial"
53
+ * />
54
+ * ```
55
+ *
56
+ * @example With custom description
57
+ * ```tsx
58
+ * <SettingsNavigationItem
59
+ * config={gamificationConfig}
60
+ * defaultIcon="trophy-outline"
61
+ * defaultRoute="Gamification"
62
+ * renderDescription={(cfg) => `${level} • ${points}`}
63
+ * />
64
+ * ```
65
+ */
66
+ const SettingsNavigationItemComponent = <T extends SettingsNavigationItemConfig>({
67
+ config,
68
+ defaultIcon = "apps-outline",
69
+ defaultRoute,
70
+ renderDescription,
71
+ noBackground,
72
+ hideMargin,
73
+ }: SettingsNavigationItemProps<T>) => {
74
+ const navigation = useSettingsNavigation();
75
+
76
+ const handlePress = React.useMemo(
77
+ () => createRouteOrPressHandler(navigation.navigate, {
78
+ route: config.route,
79
+ onPress: config.onPress,
80
+ fallback: defaultRoute,
81
+ }),
82
+ [navigation, config.route, config.onPress, defaultRoute]
83
+ );
84
+
85
+ const icon = (config.icon || defaultIcon) as IconName;
86
+ const description = renderDescription ? renderDescription(config) : config.description;
87
+
88
+ return (
89
+ <SettingsItemCard
90
+ title={config.title}
91
+ description={description}
92
+ icon={icon}
93
+ onPress={handlePress}
94
+ sectionTitle={config.sectionTitle}
95
+ noBackground={noBackground}
96
+ hideMargin={hideMargin}
97
+ />
98
+ );
99
+ };
100
+
101
+ /**
102
+ * Memoized Settings Navigation Item
103
+ * Uses shallow comparison by default
104
+ */
105
+ export const SettingsNavigationItem = React.memo(
106
+ SettingsNavigationItemComponent
107
+ ) as typeof SettingsNavigationItemComponent & { displayName?: string };
108
+
109
+ SettingsNavigationItem.displayName = "SettingsNavigationItem";
@@ -1,4 +1,11 @@
1
1
  /**
2
2
  * Navigation Utilities
3
3
  */
4
- export * from './navigationTranslations';
4
+ export {
5
+ createNavigationHandler,
6
+ createRouteOrPressHandler,
7
+ createNavigationHandlerWithParams,
8
+ } from './navigationHelpers';
9
+ export type { NavigateFunction, RouteOrPressConfig } from './navigationHelpers';
10
+
11
+ export { createNotificationTranslations, createQuietHoursTranslations, createLegalScreenProps } from './navigationTranslations';
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Navigation Helper Utilities
3
+ * Type-safe navigation handling to eliminate type casting
4
+ */
5
+
6
+ import type { SettingsStackParamList } from '../types';
7
+
8
+ /**
9
+ * Type for navigation function
10
+ */
11
+ export type NavigateFunction = <RouteName extends keyof SettingsStackParamList>(
12
+ route: RouteName,
13
+ params?: SettingsStackParamList[RouteName]
14
+ ) => void;
15
+
16
+ /**
17
+ * Create a type-safe navigation handler
18
+ *
19
+ * @param navigate - Navigation function from useNavigation
20
+ * @param route - Target route name
21
+ * @param fallback - Optional fallback route if primary route is undefined
22
+ * @returns Navigation handler function
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * const navigation = useSettingsNavigation();
27
+ * const handlePress = createNavigationHandler(
28
+ * navigation.navigate,
29
+ * config.route,
30
+ * 'Appearance'
31
+ * );
32
+ * ```
33
+ */
34
+ export const createNavigationHandler = <T extends keyof SettingsStackParamList>(
35
+ navigate: NavigateFunction,
36
+ route: T | undefined,
37
+ fallback?: T
38
+ ) => {
39
+ return () => {
40
+ const targetRoute = route || fallback;
41
+ if (targetRoute) {
42
+ navigate(targetRoute);
43
+ }
44
+ };
45
+ };
46
+
47
+ /**
48
+ * Configuration for route or press handler
49
+ */
50
+ export interface RouteOrPressConfig<T extends keyof SettingsStackParamList = keyof SettingsStackParamList> {
51
+ /** Route to navigate to */
52
+ route?: T;
53
+ /** Custom onPress handler */
54
+ onPress?: () => void;
55
+ /** Fallback route if primary route is undefined */
56
+ fallback?: T;
57
+ }
58
+
59
+ /**
60
+ * Create a handler that supports both route navigation and custom onPress
61
+ * Prioritizes onPress if provided, otherwise navigates to route
62
+ *
63
+ * @param navigate - Navigation function from useNavigation
64
+ * @param config - Configuration with route and/or onPress
65
+ * @returns Handler function
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * const navigation = useSettingsNavigation();
70
+ * const handlePress = createRouteOrPressHandler(
71
+ * navigation.navigate,
72
+ * { route: config.route, onPress: config.onPress, fallback: 'Appearance' }
73
+ * );
74
+ * ```
75
+ */
76
+ export const createRouteOrPressHandler = <T extends keyof SettingsStackParamList>(
77
+ navigate: NavigateFunction,
78
+ config: RouteOrPressConfig<T>
79
+ ) => {
80
+ return () => {
81
+ if (config.onPress) {
82
+ config.onPress();
83
+ } else {
84
+ const targetRoute = config.route || config.fallback;
85
+ if (targetRoute) {
86
+ navigate(targetRoute);
87
+ }
88
+ }
89
+ };
90
+ };
91
+
92
+ /**
93
+ * Create a navigation handler with parameters
94
+ *
95
+ * @param navigate - Navigation function from useNavigation
96
+ * @param route - Target route name
97
+ * @param params - Navigation parameters for the route
98
+ * @returns Navigation handler function
99
+ *
100
+ * @example
101
+ * ```typescript
102
+ * const navigation = useSettingsNavigation();
103
+ * const handlePress = createNavigationHandlerWithParams(
104
+ * navigation.navigate,
105
+ * 'LegalDetail',
106
+ * { document: 'privacy' }
107
+ * );
108
+ * ```
109
+ */
110
+ export const createNavigationHandlerWithParams = <T extends keyof SettingsStackParamList>(
111
+ navigate: NavigateFunction,
112
+ route: T,
113
+ params: SettingsStackParamList[T]
114
+ ) => {
115
+ return () => {
116
+ navigate(route, params);
117
+ };
118
+ };
@@ -1,7 +1,8 @@
1
- import React, { useMemo } from "react";
1
+ import React, { useMemo, useCallback } from "react";
2
2
  import { View, StyleSheet } from "react-native";
3
3
  import { SettingsFooter } from "../../components/SettingsFooter";
4
4
  import { SettingsSection } from "../../components/SettingsSection";
5
+ import { SettingsNavigationItem } from "../../components/SettingsNavigationItem";
5
6
  import { DevSettingsSection } from "../../../domains/dev";
6
7
  import { DisclaimerSetting } from "../../../domains/disclaimer";
7
8
  import { ProfileSectionLoader } from "./sections/ProfileSectionLoader";
@@ -10,13 +11,9 @@ import { IdentitySettingsSection } from "./sections/IdentitySettingsSection";
10
11
  import { SupportSettingsSection } from "./sections/SupportSettingsSection";
11
12
  import { CustomSettingsList } from "./sections/CustomSettingsList";
12
13
  import { hasAnyFeaturesEnabled } from "./utils/featureChecker";
14
+ import { useGamification } from "../../../domains/gamification";
13
15
  import type { SettingsContentProps } from "./types/SettingsContentProps";
14
16
 
15
- // Extracted Item Components
16
- import { SubscriptionSettingsItem } from "./SubscriptionSettingsItem";
17
- import { WalletSettingsItem } from "./WalletSettingsItem";
18
- import { GamificationSettingsItem } from "./GamificationSettingsItem";
19
-
20
17
  export const SettingsContent: React.FC<SettingsContentProps> = ({
21
18
  normalizedConfig,
22
19
  features,
@@ -31,11 +28,20 @@ export const SettingsContent: React.FC<SettingsContentProps> = ({
31
28
  gamificationConfig,
32
29
  }) => {
33
30
  const translations = normalizedConfig.translations;
31
+ const { level } = useGamification(gamificationConfig);
32
+
34
33
  const hasAnyFeatures = useMemo(
35
34
  () => hasAnyFeaturesEnabled(features, customSections),
36
35
  [features, customSections]
37
36
  );
38
37
 
38
+ const renderGamificationDescription = useCallback(
39
+ (cfg: typeof normalizedConfig.gamification.config) => {
40
+ return cfg?.description || `${level.currentLevel} • ${level.currentPoints}`;
41
+ },
42
+ [level.currentLevel, level.currentPoints]
43
+ );
44
+
39
45
  return (
40
46
  <View style={styles.container}>
41
47
  {showUserProfile && (
@@ -49,15 +55,17 @@ export const SettingsContent: React.FC<SettingsContentProps> = ({
49
55
 
50
56
  {features.subscription && (normalizedConfig.subscription.config?.route || normalizedConfig.subscription.config?.onPress) && (
51
57
  <SettingsSection title={translations?.sections?.subscription}>
52
- <SubscriptionSettingsItem
53
- config={normalizedConfig.subscription.config}
58
+ <SettingsNavigationItem
59
+ config={normalizedConfig.subscription.config}
60
+ defaultIcon="star"
54
61
  />
55
62
  </SettingsSection>
56
63
  )}
57
64
 
58
65
  {features.wallet && normalizedConfig.wallet.config?.route && (
59
- <WalletSettingsItem
66
+ <SettingsNavigationItem
60
67
  config={normalizedConfig.wallet.config}
68
+ defaultIcon="wallet"
61
69
  />
62
70
  )}
63
71
 
@@ -65,9 +73,11 @@ export const SettingsContent: React.FC<SettingsContentProps> = ({
65
73
 
66
74
  {features.gamification && (
67
75
  <SettingsSection title={translations?.sections?.progress}>
68
- <GamificationSettingsItem
76
+ <SettingsNavigationItem
69
77
  config={normalizedConfig.gamification.config || {}}
70
- gamificationConfig={gamificationConfig}
78
+ defaultIcon="trophy-outline"
79
+ defaultRoute="Gamification"
80
+ renderDescription={renderGamificationDescription}
71
81
  noBackground={true}
72
82
  hideMargin={true}
73
83
  />
@@ -2,7 +2,7 @@ import React, { useCallback } from "react";
2
2
  import { SupportSection } from "../../../../domains/feedback/presentation/components/SupportSection";
3
3
  import { SettingsSection } from "../../../components/SettingsSection";
4
4
  import { SettingsItemCard } from "../../../components/SettingsItemCard";
5
- import { VideoTutorialSettingsItem } from "../VideoTutorialSettingsItem";
5
+ import { SettingsNavigationItem } from "../../../components/SettingsNavigationItem";
6
6
  import type { NormalizedConfig } from "../../utils/normalizeConfig";
7
7
  import { compareConfigAndFeatures } from "../../../../infrastructure/utils/memoComparisonUtils";
8
8
  import { useSettingsNavigation } from "../../../navigation/hooks/useSettingsNavigation";
@@ -97,8 +97,10 @@ export const SupportSettingsSection: React.FC<SupportSettingsSectionProps> = ({
97
97
  )}
98
98
 
99
99
  {features.videoTutorial && (
100
- <VideoTutorialSettingsItem
100
+ <SettingsNavigationItem
101
101
  config={normalizedConfig.videoTutorial.config || {}}
102
+ defaultIcon="play-circle-outline"
103
+ defaultRoute="VideoTutorial"
102
104
  noBackground={true}
103
105
  hideMargin={true}
104
106
  />
@@ -6,6 +6,7 @@
6
6
  import type { FeatureVisibility } from "./BaseTypes";
7
7
  import type { FeedbackType } from "../../../domains/feedback/domain/entities/FeedbackEntity";
8
8
  import type { FAQCategory } from "../../../domains/faqs/domain/entities/FAQEntity";
9
+ import type { SettingsStackParamList } from "../../navigation/types";
9
10
 
10
11
  /**
11
12
  * User Profile Settings Configuration
@@ -114,7 +115,7 @@ export interface SubscriptionConfig {
114
115
  /** Custom section title for grouping */
115
116
  sectionTitle?: string;
116
117
  /** Navigation route for subscription screen (preferred over onPress) */
117
- route?: string;
118
+ route?: keyof SettingsStackParamList;
118
119
  /** Handler to open subscription screen (use route instead for stack navigation) */
119
120
  onPress?: () => void;
120
121
  /** Whether user is premium (to show active status) */
@@ -136,7 +137,7 @@ export interface WalletConfig {
136
137
  /** Custom section title for grouping */
137
138
  sectionTitle?: string;
138
139
  /** Navigation route for wallet screen */
139
- route?: string;
140
+ route?: keyof SettingsStackParamList;
140
141
  /** Current balance to display */
141
142
  balance?: number;
142
143
  }
@@ -156,7 +157,7 @@ export interface GamificationItemConfig {
156
157
  /** Custom section title for grouping */
157
158
  sectionTitle?: string;
158
159
  /** Navigation route for gamification screen */
159
- route?: string;
160
+ route?: keyof SettingsStackParamList;
160
161
  /** Achievements to display */
161
162
  achievementsCount?: number;
162
163
  }
@@ -178,6 +179,6 @@ export interface VideoTutorialConfig {
178
179
  /** Handler to open video tutorial screen */
179
180
  onPress?: () => void;
180
181
  /** Navigation route for video tutorial screen */
181
- route?: string;
182
+ route?: keyof SettingsStackParamList;
182
183
  }
183
184