@umituz/react-native-settings 4.23.63 → 4.23.64

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-settings",
3
- "version": "4.23.63",
3
+ "version": "4.23.64",
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",
@@ -76,7 +76,7 @@
76
76
  "@typescript-eslint/eslint-plugin": "^7.18.0",
77
77
  "@typescript-eslint/parser": "^7.18.0",
78
78
  "@umituz/react-native-auth": "^3.6.49",
79
- "@umituz/react-native-design-system": "^4.23.50",
79
+ "@umituz/react-native-design-system": "^4.23.58",
80
80
  "@umituz/react-native-firebase": "^1.13.102",
81
81
  "@umituz/react-native-sentry": "*",
82
82
  "eslint": "^8.57.0",
@@ -17,10 +17,12 @@ export const useAppearanceActions = () => {
17
17
  }, [setThemeMode]);
18
18
 
19
19
  const handleColorChange = useCallback((key: keyof CustomThemeColors, color: string) => {
20
- const newColors = { ...localCustomColors, [key]: color };
21
- setLocalCustomColors(newColors);
22
- setCustomColors(newColors);
23
- }, [localCustomColors, setCustomColors]);
20
+ setLocalCustomColors(prev => {
21
+ const newColors = { ...prev, [key]: color };
22
+ setCustomColors(newColors);
23
+ return newColors;
24
+ });
25
+ }, [setCustomColors]);
24
26
 
25
27
  const handleResetColors = useCallback(() => {
26
28
  reset();
@@ -23,11 +23,13 @@ export function useFAQExpansion(): UseFAQExpansionResult {
23
23
 
24
24
  const toggleExpansion = useCallback((itemId: string) => {
25
25
  setExpandedItems(prev => {
26
- if (prev.has(itemId)) {
27
- return new Set();
26
+ const next = new Set(prev);
27
+ if (next.has(itemId)) {
28
+ next.delete(itemId);
28
29
  } else {
29
- return new Set([itemId]);
30
+ next.add(itemId);
30
31
  }
32
+ return next;
31
33
  });
32
34
  }, []);
33
35
 
@@ -45,11 +45,10 @@ export const checkAchievementUnlock = (
45
45
  ): boolean => {
46
46
  switch (definition.type) {
47
47
  case "count":
48
+ case "milestone":
48
49
  return tasksCompleted >= definition.threshold;
49
50
  case "streak":
50
51
  return currentStreak >= definition.threshold;
51
- case "milestone":
52
- return tasksCompleted >= definition.threshold;
53
52
  default:
54
53
  return false;
55
54
  }
@@ -4,57 +4,7 @@
4
4
  * Extracted from screens to follow SOLID principles
5
5
  */
6
6
 
7
- export interface ContentValidationRule {
8
- content?: string;
9
- url?: string;
10
- title?: string;
11
- viewOnlineText?: string;
12
- openText?: string;
13
- privacyText?: string;
14
- termsText?: string;
15
- }
16
-
17
7
  export class ContentValidationService {
18
- /**
19
- * Validate screen content requirements
20
- */
21
- static validateScreenContent(
22
- _content: string | undefined,
23
- _url: string | undefined,
24
- _title: string | undefined,
25
- _viewOnlineText: string | undefined,
26
- _openText: string | undefined,
27
- _screenName: string
28
- ): void {
29
- // Silent validation - no console output
30
- void _content;
31
- void _url;
32
- void _title;
33
- void _viewOnlineText;
34
- void _openText;
35
- void _screenName;
36
- }
37
-
38
- /**
39
- * Validate legal links requirements
40
- */
41
- static validateLegalLinks(
42
- _privacyPolicyUrl: string | undefined,
43
- _termsOfServiceUrl: string | undefined,
44
- _privacyText: string | undefined,
45
- _termsText: string | undefined,
46
- _onPrivacyPress: (() => void) | undefined,
47
- _onTermsPress: (() => void) | undefined
48
- ): void {
49
- // Silent validation - no console output
50
- void _privacyPolicyUrl;
51
- void _termsOfServiceUrl;
52
- void _privacyText;
53
- void _termsText;
54
- void _onPrivacyPress;
55
- void _onTermsPress;
56
- }
57
-
58
8
  /**
59
9
  * Check if content is valid
60
10
  */
@@ -7,7 +7,6 @@
7
7
  import React from "react";
8
8
  import { View, TouchableOpacity, StyleSheet } from "react-native";
9
9
  import { AtomicText } from "@umituz/react-native-design-system";
10
- import { ContentValidationService } from "../../domain/services/ContentValidationService";
11
10
  import { UrlHandlerService } from "../../domain/services/UrlHandlerService";
12
11
 
13
12
  export interface LegalLinksProps {
@@ -51,18 +50,6 @@ export const LegalLinks: React.FC<LegalLinksProps> = React.memo(
51
50
  onTermsPress,
52
51
  style,
53
52
  }) => {
54
- // Validate required props
55
- React.useEffect(() => {
56
- ContentValidationService.validateLegalLinks(
57
- privacyPolicyUrl,
58
- termsOfServiceUrl,
59
- privacyText,
60
- termsText,
61
- onPrivacyPress,
62
- onTermsPress
63
- );
64
- }, [privacyPolicyUrl, termsOfServiceUrl, privacyText, termsText, onPrivacyPress, onTermsPress]);
65
-
66
53
  // Memoize press handlers to prevent child re-renders
67
54
  const handlePrivacyPress = React.useCallback(async () => {
68
55
  if (onPrivacyPress) {
@@ -37,17 +37,6 @@ export const LegalContentScreen: React.FC<LegalContentScreenProps> = React.memo(
37
37
  const tokens = useAppDesignTokens();
38
38
  const insets = useSafeAreaInsets();
39
39
 
40
- React.useEffect(() => {
41
- ContentValidationService.validateScreenContent(
42
- content,
43
- url,
44
- title,
45
- viewOnlineText,
46
- openText,
47
- styleCacheKey
48
- );
49
- }, [content, url, title, viewOnlineText, openText, styleCacheKey]);
50
-
51
40
  const styles = React.useMemo(() => {
52
41
  const cacheKey = StyleCacheService.createTokenCacheKey(tokens);
53
42
  return StyleCacheService.getCachedStyles(
@@ -25,15 +25,19 @@ export const useReminderActions = () => {
25
25
  updatedAt: now,
26
26
  };
27
27
 
28
- const trigger = buildTrigger(reminder);
29
- const notificationId = await scheduler.scheduleNotification({
30
- title: reminder.title,
31
- body: reminder.body,
32
- trigger,
33
- data: { reminderId: reminder.id },
34
- });
35
-
36
- reminder.notificationId = notificationId;
28
+ try {
29
+ const trigger = buildTrigger(reminder);
30
+ const notificationId = await scheduler.scheduleNotification({
31
+ title: reminder.title,
32
+ body: reminder.body,
33
+ trigger,
34
+ data: { reminderId: reminder.id },
35
+ });
36
+ reminder.notificationId = notificationId;
37
+ } catch {
38
+ reminder.enabled = false;
39
+ }
40
+
37
41
  await addReminder(reminder);
38
42
 
39
43
  return reminder;
@@ -86,14 +90,18 @@ export const useReminderActions = () => {
86
90
  await scheduler.cancelNotification(reminder.notificationId);
87
91
  await updateReminder(id, { enabled: false, notificationId: undefined });
88
92
  } else if (!reminder.enabled) {
89
- const trigger = buildTrigger(reminder);
90
- const notificationId = await scheduler.scheduleNotification({
91
- title: reminder.title,
92
- body: reminder.body,
93
- trigger,
94
- data: { reminderId: reminder.id },
95
- });
96
- await updateReminder(id, { enabled: true, notificationId });
93
+ try {
94
+ const trigger = buildTrigger(reminder);
95
+ const notificationId = await scheduler.scheduleNotification({
96
+ title: reminder.title,
97
+ body: reminder.body,
98
+ trigger,
99
+ data: { reminderId: reminder.id },
100
+ });
101
+ await updateReminder(id, { enabled: true, notificationId });
102
+ } catch {
103
+ await updateReminder(id, { enabled: false });
104
+ }
97
105
  }
98
106
  }, [updateReminder]);
99
107
 
@@ -3,6 +3,7 @@
3
3
  * Manages reminder state with AsyncStorage persistence
4
4
  */
5
5
 
6
+ import { useMemo } from 'react';
6
7
  import { createStore } from '@umituz/react-native-design-system';
7
8
  import type { Reminder, QuietHoursConfig, NotificationPreferences } from '../../../infrastructure/services/types';
8
9
 
@@ -76,7 +77,7 @@ interface PreferencesState {
76
77
  }
77
78
 
78
79
  interface PreferencesActions {
79
- initialize: () => Promise<void>;
80
+ initialize: () => void;
80
81
  updatePreferences: (updates: Partial<NotificationPreferences>) => void;
81
82
  updateQuietHours: (quietHours: QuietHoursConfig) => void;
82
83
  reset: () => void;
@@ -110,7 +111,7 @@ export const usePreferencesStore = createStore<PreferencesState, PreferencesActi
110
111
  initialState: initialPreferencesState,
111
112
  persist: true,
112
113
  actions: (set, get) => ({
113
- initialize: async () => {
114
+ initialize: () => {
114
115
  set({ isLoading: false, isInitialized: true });
115
116
  },
116
117
 
@@ -135,7 +136,10 @@ export const usePreferencesStore = createStore<PreferencesState, PreferencesActi
135
136
  // ============================================================================
136
137
 
137
138
  export const useReminders = () => useRemindersStore(state => state.reminders);
138
- export const useEnabledReminders = () => useRemindersStore(state => state.reminders.filter(r => r.enabled));
139
+ export const useEnabledReminders = () => {
140
+ const reminders = useRemindersStore(state => state.reminders);
141
+ return useMemo(() => reminders.filter(r => r.enabled), [reminders]);
142
+ };
139
143
  export const useReminderById = (id: string) => useRemindersStore(state => state.reminders.find(r => r.id === id));
140
144
 
141
145
  // ============================================================================
package/src/index.ts CHANGED
@@ -63,7 +63,7 @@ export type {
63
63
  SettingsStackParamList,
64
64
  AppInfo,
65
65
  LegalUrls,
66
- UserProfileConfig,
66
+ UserProfileDisplay,
67
67
  AdditionalScreen,
68
68
  FAQData,
69
69
  AccountConfig,
@@ -9,17 +9,11 @@ import type { UserSettings, SettingsResult } from '../../application/ports/ISett
9
9
 
10
10
  export class SettingsService {
11
11
  private repository: SettingsRepository;
12
- private initialized: boolean = false;
13
12
 
14
13
  constructor() {
15
14
  this.repository = new SettingsRepository();
16
15
  }
17
16
 
18
- async initialize(): Promise<void> {
19
- if (this.initialized) return;
20
- this.initialized = true;
21
- }
22
-
23
17
  async getSettings(userId: string): Promise<SettingsResult<UserSettings>> {
24
18
  return this.repository.getSettings(userId);
25
19
  }
@@ -17,7 +17,7 @@ export const SettingsFooter: React.FC<SettingsFooterProps> = ({
17
17
 
18
18
  const displayText = versionText || (appVersion && versionLabel
19
19
  ? `${versionLabel} ${appVersion}`
20
- : appVersion || versionText);
20
+ : appVersion);
21
21
 
22
22
  if (!displayText) return null;
23
23
 
@@ -29,7 +29,7 @@ import {
29
29
  } from "../utils/config-creators";
30
30
  import type { SettingsConfig } from "../screens/types";
31
31
  import type { FeedbackFormData } from "../utils/config-creators";
32
- import type { AppInfo, FAQData, UserProfileConfig, AdditionalScreen } from "../navigation/types";
32
+ import type { AppInfo, FAQData, UserProfileDisplay, AdditionalScreen } from "../navigation/types";
33
33
  import type { AccountScreenConfig } from "@umituz/react-native-auth";
34
34
 
35
35
  export interface SettingsFeatures {
@@ -54,7 +54,7 @@ export interface UseSettingsScreenConfigParams {
54
54
 
55
55
  export interface SettingsScreenConfigResult {
56
56
  settingsConfig: SettingsConfig;
57
- userProfile: UserProfileConfig;
57
+ userProfile: UserProfileDisplay;
58
58
  accountConfig: AccountScreenConfig;
59
59
  translatedFaqData: FAQData | undefined;
60
60
  isLoading: boolean;
@@ -127,7 +127,7 @@ export const useSettingsScreenConfig = (
127
127
  showAbout, showLegal, showFaqs, showRating,
128
128
  ]);
129
129
 
130
- const userProfile = useMemo((): UserProfileConfig => {
130
+ const userProfile = useMemo((): UserProfileDisplay => {
131
131
  const isAnonymous = userProfileData?.isAnonymous ?? true;
132
132
  return {
133
133
  displayName: userProfileData?.displayName || t("settings.profile.anonymousName"),
@@ -10,11 +10,11 @@ import { useAppDesignTokens, StackNavigator, type StackScreen, type StackNavigat
10
10
  import { useLocalization, LanguageSelectionScreen } from "../../domains/localization";
11
11
  import { NotificationSettingsScreen } from "../../domains/notifications";
12
12
  import { AccountScreen } from "@umituz/react-native-auth";
13
+ import { SettingsScreen } from "../screens/SettingsScreen";
13
14
  import { AppearanceScreen } from "../screens/AppearanceScreen";
14
15
  import { FAQScreen } from "../../domains/faqs";
15
16
  import { useNavigationHandlers } from "./hooks";
16
17
  import {
17
- SettingsScreenWrapper,
18
18
  LegalScreenWrapper,
19
19
  AboutScreenWrapper,
20
20
  } from "./components/wrappers";
@@ -79,7 +79,7 @@ export const SettingsStackNavigator: React.FC<SettingsStackNavigatorProps> = ({
79
79
  name: "SettingsMain",
80
80
  options: { headerShown: false },
81
81
  children: () => (
82
- <SettingsScreenWrapper
82
+ <SettingsScreen
83
83
  config={config}
84
84
  appVersion={appInfo.version}
85
85
  showUserProfile={showUserProfile}
@@ -4,14 +4,14 @@
4
4
  import React from "react";
5
5
  import { SettingsScreen } from "../../../screens/SettingsScreen";
6
6
  import type { SettingsConfig, CustomSettingsSection } from "../../../screens/types";
7
- import type { UserProfileConfig } from "../../types";
7
+ import type { UserProfileDisplay } from "../../types";
8
8
  import type { DevSettingsProps } from "../../../../domains/dev";
9
9
 
10
10
  export interface SettingsScreenWrapperProps {
11
11
  config?: SettingsConfig;
12
12
  appVersion: string;
13
13
  showUserProfile: boolean;
14
- userProfile?: UserProfileConfig;
14
+ userProfile?: UserProfileDisplay;
15
15
  devSettings?: DevSettingsProps;
16
16
  customSections?: CustomSettingsSection[];
17
17
  showHeader?: boolean;
@@ -1,8 +1,6 @@
1
1
  /**
2
2
  * Wrapper Components
3
3
  */
4
- export { SettingsScreenWrapper } from './SettingsScreenWrapper';
5
- export type { SettingsScreenWrapperProps } from './SettingsScreenWrapper';
6
4
  export { LegalScreenWrapper } from './LegalScreenWrapper';
7
5
  export type { LegalScreenWrapperProps } from './LegalScreenWrapper';
8
6
  export { AboutScreenWrapper } from './AboutScreenWrapper';
@@ -49,7 +49,7 @@ export type SettingsStackParamList = {
49
49
  /**
50
50
  * User profile configuration
51
51
  */
52
- export interface UserProfileConfig {
52
+ export interface UserProfileDisplay {
53
53
  displayName?: string;
54
54
  userId?: string;
55
55
  isAnonymous?: boolean;
@@ -93,7 +93,7 @@ export interface SettingsStackNavigatorProps {
93
93
  faqData?: FAQData;
94
94
  config?: SettingsConfig;
95
95
  showUserProfile?: boolean;
96
- userProfile?: UserProfileConfig;
96
+ userProfile?: UserProfileDisplay;
97
97
  accountConfig?: AccountConfig;
98
98
  additionalScreens?: AdditionalScreen[];
99
99
  devSettings?: DevSettingsProps;
@@ -26,7 +26,7 @@ export const createQuietHoursTranslations = (t: (key: string) => string) => ({
26
26
  });
27
27
 
28
28
  export const createLegalScreenProps = (
29
- t: any,
29
+ t: (key: string) => string,
30
30
  handlePrivacyPress: () => void,
31
31
  handleTermsPress: () => void,
32
32
  handleEulaPress: () => void
@@ -92,7 +92,6 @@ export const SettingsScreen: React.FC<SettingsScreenProps> = ({
92
92
  footerText={footerText}
93
93
  appVersion={appVersion}
94
94
  customSections={customSections}
95
- showCloseButton={showCloseButton}
96
95
  devSettings={devSettings}
97
96
  gamificationConfig={gamificationConfig}
98
97
  />
@@ -50,7 +50,6 @@ interface SettingsContentProps {
50
50
  footerText?: string;
51
51
  appVersion?: string;
52
52
  customSections?: CustomSettingsSection[];
53
- showCloseButton?: boolean;
54
53
  emptyStateText?: string;
55
54
  devSettings?: DevSettingsProps;
56
55
  gamificationConfig?: import("../../../domains/gamification").GamificationSettingsConfig;
@@ -65,7 +64,6 @@ export const SettingsContent: React.FC<SettingsContentProps> = ({
65
64
  footerText,
66
65
  appVersion,
67
66
  customSections = [],
68
- showCloseButton = false,
69
67
  emptyStateText,
70
68
  devSettings,
71
69
  gamificationConfig,