@umituz/react-native-settings 5.4.17 → 5.4.19

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/dist/core/base/BaseService.d.ts +86 -0
  2. package/dist/core/index.d.ts +27 -0
  3. package/dist/core/patterns/Modal/ModalConfig.d.ts +197 -0
  4. package/dist/core/patterns/Modal/useModalState.d.ts +53 -0
  5. package/dist/core/patterns/Screen/ScreenConfig.d.ts +273 -0
  6. package/dist/core/patterns/Screen/useScreenData.d.ts +62 -0
  7. package/dist/core/utils/logger.d.ts +76 -0
  8. package/dist/core/utils/validators.d.ts +114 -0
  9. package/dist/domains/ai-consent/index.d.ts +37 -0
  10. package/dist/domains/ai-consent/presentation/components/AIConsentModal.d.ts +26 -0
  11. package/dist/domains/ai-consent/presentation/components/AIConsentSetting.d.ts +28 -0
  12. package/dist/domains/ai-consent/presentation/hooks/useAIConsent.d.ts +27 -0
  13. package/dist/domains/ai-consent/presentation/screens/AIConsentScreen.d.ts +37 -0
  14. package/dist/domains/disclaimer/index.d.ts +0 -2
  15. package/dist/domains/disclaimer/presentation/components/DisclaimerSetting.d.ts +1 -2
  16. package/dist/domains/disclaimer/presentation/screens/DisclaimerScreen.d.ts +11 -20
  17. package/dist/domains/feedback/index.d.ts +2 -1
  18. package/dist/domains/feedback/presentation/components/SupportSection.d.ts +1 -1
  19. package/dist/domains/feedback/presentation/screens/FeedbackScreen.d.ts +21 -0
  20. package/dist/domains/gamification/types/index.d.ts +1 -0
  21. package/dist/domains/gamification/utils/calculations.d.ts +3 -0
  22. package/dist/domains/localization/index.d.ts +1 -1
  23. package/dist/domains/localization/infrastructure/config/i18n.d.ts +1 -1
  24. package/dist/domains/notifications/infrastructure/services/NotificationService.d.ts +5 -2
  25. package/dist/domains/notifications/quietHours/infrastructure/hooks/useQuietHoursActions.d.ts +4 -4
  26. package/dist/domains/rating/application/services/RatingService.d.ts +50 -21
  27. package/dist/domains/rating/index.d.ts +2 -2
  28. package/dist/domains/rating/presentation/hooks/useAppRating.d.ts +1 -1
  29. package/dist/domains/rating/presentation/screens/RatingPromptScreen.d.ts +22 -0
  30. package/dist/index.d.ts +4 -0
  31. package/dist/infrastructure/services/SettingsService.d.ts +5 -2
  32. package/dist/presentation/components/GenericModal.d.ts +35 -0
  33. package/dist/presentation/components/GenericScreen.d.ts +41 -0
  34. package/dist/presentation/components/index.d.ts +17 -0
  35. package/dist/presentation/navigation/hooks/useSettingsNavigation.d.ts +21 -15
  36. package/dist/presentation/navigation/types.d.ts +8 -0
  37. package/dist/presentation/navigation/utils/navigationHelpers.d.ts +1 -1
  38. package/package.json +1 -1
  39. package/src/domains/ai-consent/index.ts +53 -0
  40. package/src/domains/ai-consent/presentation/components/AIConsentModal.tsx +64 -0
  41. package/src/domains/ai-consent/presentation/components/AIConsentSetting.tsx +106 -0
  42. package/src/domains/ai-consent/presentation/hooks/useAIConsent.ts +125 -0
  43. package/src/domains/ai-consent/presentation/screens/AIConsentScreen.tsx +414 -0
  44. package/src/index.ts +3 -0
  45. package/src/presentation/navigation/hooks/useSettingsScreens.ts +9 -1
  46. package/src/presentation/navigation/types.ts +2 -0
package/dist/index.d.ts CHANGED
@@ -7,12 +7,15 @@
7
7
  * Usage:
8
8
  * import { useSettings, SettingsScreen, AppearanceScreen, SettingsItemCard, DisclaimerSetting } from '@umituz/react-native-settings';
9
9
  */
10
+ export * from './core';
11
+ export * from './presentation/components';
10
12
  export type { ISettingsRepository, UserSettings, SettingsError, SettingsResult, } from './application/ports/ISettingsRepository';
11
13
  export { getSettingsService } from './infrastructure/services/SettingsService';
12
14
  export { SettingsRepository } from './infrastructure/repositories/SettingsRepository';
13
15
  export { useSettings } from './presentation/hooks/useSettings';
14
16
  export { useSettingsQuery } from './presentation/hooks/queries/useSettingsQuery';
15
17
  export { useUpdateSettingsMutation, useResetSettingsMutation } from './presentation/hooks/mutations/useSettingsMutations';
18
+ export { useSettingsScreenConfig, type SettingsFeatures, type UseSettingsScreenConfigParams, type SettingsScreenConfigResult } from './presentation/hooks/useSettingsScreenConfig';
16
19
  export { SettingsScreen } from './presentation/screens/SettingsScreen';
17
20
  export type { SettingsScreenProps } from './presentation/screens/SettingsScreen';
18
21
  export { AppearanceScreen } from './presentation/screens/AppearanceScreen';
@@ -37,5 +40,6 @@ export * from "./domains/cloud-sync";
37
40
  export * from "./domains/dev";
38
41
  export * from "./domains/gamification";
39
42
  export * from "./domains/localization";
43
+ export * from "./domains/ai-consent";
40
44
  export * from "./domains/notifications";
41
45
  export { createAppearanceConfig, createLanguageConfig, createNotificationsConfig, createAboutConfig, createLegalConfig, createFeedbackConfig, createRatingConfig, createFAQConfig, createSubscriptionConfig, createGamificationConfig, type TranslationFunction, type FeedbackFormData, } from './presentation/utils';
@@ -1,10 +1,13 @@
1
1
  /**
2
2
  * Settings Service
3
3
  *
4
- * Orchestrates settings operations using SettingsRepository
4
+ * Orchestrates settings operations using SettingsRepository.
5
+ * Refactored to extend BaseService for consistent error handling.
5
6
  */
7
+ import { BaseService } from '../../core/base/BaseService';
6
8
  import type { UserSettings, SettingsResult } from '../../application/ports/ISettingsRepository';
7
- export declare class SettingsService {
9
+ export declare class SettingsService extends BaseService {
10
+ protected serviceName: string;
8
11
  private repository;
9
12
  constructor();
10
13
  getSettings(userId: string): Promise<SettingsResult<UserSettings>>;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * GenericModal Component
3
+ *
4
+ * Universal modal component that works with ModalConfig.
5
+ * Replaces all custom modal implementations.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const modal = useModalState();
10
+ *
11
+ * return (
12
+ * <>
13
+ * <Button onPress={() => modal.show(ModalPresets.confirm(...))} />
14
+ * <GenericModal state={modal} />
15
+ * </>
16
+ * );
17
+ * ```
18
+ */
19
+ import React from 'react';
20
+ import type { ModalState } from '../../core/patterns/Modal/ModalConfig';
21
+ export interface GenericModalProps {
22
+ /**
23
+ * Modal state from useModalState hook
24
+ */
25
+ state: ModalState;
26
+ /**
27
+ * Custom container style
28
+ */
29
+ containerStyle?: object;
30
+ /**
31
+ * Test ID for E2E testing
32
+ */
33
+ testID?: string;
34
+ }
35
+ export declare const GenericModal: React.FC<GenericModalProps>;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * GenericScreen Component
3
+ *
4
+ * Universal screen component that works with ScreenConfig.
5
+ * Handles loading, error, and empty states automatically.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const screenData = useScreenData({ fetch: async () => await api.getData() });
10
+ *
11
+ * return (
12
+ * <GenericScreen
13
+ * data={screenData}
14
+ * config={ScreenPresets.default}
15
+ * >
16
+ * <MyContent data={screenData.data} />
17
+ * </GenericScreen>
18
+ * );
19
+ * ```
20
+ */
21
+ import React from 'react';
22
+ import type { ScreenConfig, ScreenData } from '../../core/patterns/Screen/ScreenConfig';
23
+ export interface GenericScreenProps<T> {
24
+ /**
25
+ * Screen data from useScreenData hook
26
+ */
27
+ data: ScreenData<T>;
28
+ /**
29
+ * Screen configuration
30
+ */
31
+ config?: ScreenConfig;
32
+ /**
33
+ * Content component (renders when data is loaded)
34
+ */
35
+ children: React.ReactNode | ((data: T) => React.ReactNode);
36
+ /**
37
+ * Test ID for E2E testing
38
+ */
39
+ testID?: string;
40
+ }
41
+ export declare function GenericScreen<T>({ data, config, children, testID, }: GenericScreenProps<T>): React.JSX.Element;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Presentation Components - Public API
3
+ *
4
+ * Shared UI components for the entire application.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * import { GenericModal, GenericScreen } from '@/presentation/components';
9
+ *
10
+ * const modal = useModalState();
11
+ * return <GenericModal state={modal} />;
12
+ * ```
13
+ */
14
+ export { GenericModal } from './GenericModal';
15
+ export type { GenericModalProps } from './GenericModal';
16
+ export { GenericScreen } from './GenericScreen';
17
+ export type { GenericScreenProps } from './GenericScreen';
@@ -1,22 +1,28 @@
1
1
  /**
2
- * Typed Settings Navigation Hook
2
+ * Settings Navigation Hook
3
3
  *
4
- * Provides type-safe navigation for Settings stack screens.
5
- * Replaces unsafe `as never` casts throughout the codebase.
6
- */
7
- import type { StackNavigationProp } from '@react-navigation/stack';
8
- import type { SettingsStackParamList } from '../types';
9
- /**
10
- * Type for Settings navigation prop
11
- */
12
- export type SettingsNavigationProp = StackNavigationProp<SettingsStackParamList>;
13
- /**
14
- * Hook to get typed navigation for Settings screens
4
+ * Provides standardized navigation for Settings stack screens.
5
+ * Uses useAppNavigation from design system for consistency.
15
6
  *
16
7
  * @example
17
8
  * ```typescript
18
- * const navigation = useSettingsNavigation();
19
- * navigation.navigate('LanguageSelection'); // Fully typed!
9
+ * import { useSettingsNavigation } from '@umituz/react-native-settings/presentation/navigation';
10
+ *
11
+ * function LanguageSelectionScreen() {
12
+ * const navigation = useSettingsNavigation();
13
+ * navigation.navigate('Appearance');
14
+ * navigation.goBack();
15
+ * }
20
16
  * ```
21
17
  */
22
- export declare const useSettingsNavigation: () => SettingsNavigationProp;
18
+ import { useAppNavigation } from '@umituz/react-native-design-system/molecules';
19
+ /**
20
+ * Navigation result type inferred from useAppNavigation
21
+ */
22
+ type SettingsNavigation = ReturnType<typeof useAppNavigation>;
23
+ /**
24
+ * Hook to get navigation for Settings screens
25
+ * Delegates to useAppNavigation for consistent API
26
+ */
27
+ export declare const useSettingsNavigation: () => SettingsNavigation;
28
+ export {};
@@ -5,6 +5,10 @@ import type React from 'react';
5
5
  import type { SettingsConfig, CustomSettingsSection } from "../screens/types";
6
6
  import type { DevSettingsProps } from "../../domains/dev";
7
7
  import type { FAQCategory } from "../../domains/faqs";
8
+ import type { DisclaimerScreenParams } from "../../domains/disclaimer/presentation/screens/DisclaimerScreen";
9
+ import type { FeedbackScreenParams } from "../../domains/feedback/presentation/screens/FeedbackScreen";
10
+ import type { RatingPromptScreenParams } from "../../domains/rating/presentation/screens/RatingPromptScreen";
11
+ import type { AIConsentScreenParams } from "../../domains/ai-consent/presentation/screens/AIConsentScreen";
8
12
  /**
9
13
  * App Info passed from main app (APP_INFO constant)
10
14
  */
@@ -44,6 +48,10 @@ export type SettingsStackParamList = {
44
48
  Account: undefined;
45
49
  VideoTutorial: undefined;
46
50
  FeatureRequest: undefined;
51
+ Disclaimer: DisclaimerScreenParams;
52
+ Feedback: FeedbackScreenParams;
53
+ RatingPrompt: RatingPromptScreenParams;
54
+ AIConsent: AIConsentScreenParams;
47
55
  PasswordPrompt: {
48
56
  onComplete: (password: string | null) => void;
49
57
  title?: string;
@@ -5,7 +5,7 @@
5
5
  import type { SettingsStackParamList } from '../types';
6
6
  type NavigateFunction = <RouteName extends keyof SettingsStackParamList>(route: RouteName, params?: SettingsStackParamList[RouteName]) => void;
7
7
  interface RouteOrPressConfig<T extends keyof SettingsStackParamList = keyof SettingsStackParamList> {
8
- route?: T;
8
+ route?: T | string;
9
9
  onPress?: () => void;
10
10
  fallback?: T;
11
11
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-settings",
3
- "version": "5.4.17",
3
+ "version": "5.4.19",
4
4
  "description": "Complete settings hub for React Native apps - consolidated package with settings, localization, about, legal, appearance, feedback, FAQs, rating, and gamification - expo-store-review and expo-device now lazy loaded",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./dist/index.d.ts",
@@ -0,0 +1,53 @@
1
+ /**
2
+ * @umituz/react-native-settings - AI Consent Domain
3
+ *
4
+ * AI consent management for React Native apps
5
+ * Required by Apple App Store Guidelines 5.1.1(i) & 5.1.2(i)
6
+ *
7
+ * Displays AI technology disclosure and obtains user consent before
8
+ * using AI generation features.
9
+ *
10
+ * Usage:
11
+ * import {
12
+ * AIConsentScreen,
13
+ * AIConsentModal,
14
+ * AIConsentSetting,
15
+ * useAIConsent
16
+ * } from '@umituz/react-native-settings/ai-consent';
17
+ *
18
+ * // Show modal on app launch
19
+ * const { isConsentModalVisible, handleAcceptConsent, handleDeclineConsent } = useAIConsent();
20
+ *
21
+ * <AIConsentModal
22
+ * visible={isConsentModalVisible}
23
+ * onAccept={handleAcceptConsent}
24
+ * onDecline={handleDeclineConsent}
25
+ * />
26
+ *
27
+ * // Add to settings screen
28
+ * <AIConsentSetting />
29
+ */
30
+
31
+ // =============================================================================
32
+ // PRESENTATION LAYER - Components
33
+ // =============================================================================
34
+
35
+ export { AIConsentModal } from './presentation/components/AIConsentModal';
36
+ export type { AIConsentModalProps } from './presentation/components/AIConsentModal';
37
+
38
+ export { AIConsentSetting } from './presentation/components/AIConsentSetting';
39
+ export type { AIConsentSettingProps } from './presentation/components/AIConsentSetting';
40
+
41
+ // =============================================================================
42
+ // PRESENTATION LAYER - Screens
43
+ // =============================================================================
44
+
45
+ export { AIConsentScreen } from './presentation/screens/AIConsentScreen';
46
+ export type { AIConsentScreenProps, AIConsentScreenParams, AIProvider } from './presentation/screens/AIConsentScreen';
47
+
48
+ // =============================================================================
49
+ // PRESENTATION LAYER - Hooks
50
+ // =============================================================================
51
+
52
+ export { useAIConsent } from './presentation/hooks/useAIConsent';
53
+ export type { UseAIConsentReturn, AIConsentState } from './presentation/hooks/useAIConsent';
@@ -0,0 +1,64 @@
1
+ /**
2
+ * AI Consent Modal
3
+ *
4
+ * Modal wrapper for AI consent screen.
5
+ * Required by Apple App Store Guidelines 5.1.1(i) & 5.1.2(i)
6
+ *
7
+ * Displays on first app launch before any AI features are used.
8
+ * Can also be shown manually from settings.
9
+ *
10
+ * Usage:
11
+ * <AIConsentModal
12
+ * visible={isConsentModalVisible}
13
+ * onAccept={handleAccept}
14
+ * onDecline={handleDecline}
15
+ * />
16
+ */
17
+
18
+ import React, { memo } from 'react';
19
+ import { Modal, View, StyleSheet } from 'react-native';
20
+ import { AIConsentScreen, type AIProvider } from '../screens/AIConsentScreen';
21
+
22
+ export interface AIConsentModalProps {
23
+ visible: boolean;
24
+ onAccept: () => void;
25
+ onDecline: () => void;
26
+ providers?: AIProvider[];
27
+ customMessage?: string;
28
+ }
29
+
30
+ export const AIConsentModal: React.FC<AIConsentModalProps> = memo(({
31
+ visible,
32
+ onAccept,
33
+ onDecline,
34
+ providers,
35
+ customMessage,
36
+ }) => {
37
+ return (
38
+ <Modal
39
+ visible={visible}
40
+ animationType="slide"
41
+ transparent={false}
42
+ onRequestClose={onDecline}
43
+ >
44
+ <View style={styles.container}>
45
+ <AIConsentScreen
46
+ providers={providers}
47
+ customMessage={customMessage}
48
+ onAccept={onAccept}
49
+ onDecline={onDecline}
50
+ standalone={true}
51
+ />
52
+ </View>
53
+ </Modal>
54
+ );
55
+ });
56
+
57
+ AIConsentModal.displayName = 'AIConsentModal';
58
+
59
+ const styles = StyleSheet.create({
60
+ container: {
61
+ flex: 1,
62
+ backgroundColor: '#FFFFFF',
63
+ },
64
+ });
@@ -0,0 +1,106 @@
1
+ /**
2
+ * AIConsentSetting Component
3
+ *
4
+ * Settings list item for AI consent management.
5
+ * Shows current consent status and navigates to full consent screen.
6
+ *
7
+ * Features:
8
+ * - Displays consent status (accepted/declined/pending)
9
+ * - Tappable card that opens AI consent screen
10
+ * - Icon with background color
11
+ * - Internationalized
12
+ * - Universal across iOS, Android, Web
13
+ *
14
+ * Usage:
15
+ * import { AIConsentSetting } from '@umituz/react-native-settings/ai-consent';
16
+ *
17
+ * <AIConsentSetting />
18
+ */
19
+
20
+ import React, { useCallback, memo } from 'react';
21
+ import { View, StyleSheet } from 'react-native';
22
+ import { useAppDesignTokens, withAlpha } from '@umituz/react-native-design-system/theme';
23
+ import { useAppNavigation } from '@umituz/react-native-design-system/molecules';
24
+ import { AtomicText } from '@umituz/react-native-design-system/atoms';
25
+ import { useAIConsent } from '../hooks/useAIConsent';
26
+
27
+ export interface AIConsentSettingProps {
28
+ /** Custom title */
29
+ title?: string;
30
+ /** Custom description when consented */
31
+ consentedDescription?: string;
32
+ /** Custom description when not consented */
33
+ notConsentedDescription?: string;
34
+ }
35
+
36
+ export const AIConsentSetting: React.FC<AIConsentSettingProps> = memo(({
37
+ title = 'AI Technology Consent',
38
+ consentedDescription = 'You have consented to use AI services',
39
+ notConsentedDescription = 'Review AI service disclosure',
40
+ }) => {
41
+ const tokens = useAppDesignTokens();
42
+ const navigation = useAppNavigation();
43
+ const { hasConsented, isLoading } = useAIConsent();
44
+
45
+ const handlePress = useCallback(() => {
46
+ navigation.push('AIConsent' as never);
47
+ }, [navigation]);
48
+
49
+ if (isLoading) {
50
+ return null;
51
+ }
52
+
53
+ const iconColor = hasConsented ? tokens.colors.success : tokens.colors.warning;
54
+ const backgroundColor = withAlpha(iconColor, 0.1);
55
+ const description = hasConsented ? consentedDescription : notConsentedDescription;
56
+
57
+ return (
58
+ <View style={[styles.container, { backgroundColor }]}>
59
+ <View style={[styles.iconContainer, { backgroundColor: iconColor }]}>
60
+ <AtomicText style={styles.icon}>🤖</AtomicText>
61
+ </View>
62
+
63
+ <View style={styles.content}>
64
+ <AtomicText style={styles.title}>{title}</AtomicText>
65
+ <AtomicText style={styles.description}>{description}</AtomicText>
66
+ </View>
67
+ </View>
68
+ );
69
+ });
70
+
71
+ AIConsentSetting.displayName = 'AIConsentSetting';
72
+
73
+ const styles = StyleSheet.create({
74
+ container: {
75
+ flexDirection: 'row',
76
+ alignItems: 'center',
77
+ padding: 16,
78
+ borderRadius: 12,
79
+ marginVertical: 8,
80
+ },
81
+ iconContainer: {
82
+ width: 48,
83
+ height: 48,
84
+ borderRadius: 24,
85
+ alignItems: 'center',
86
+ justifyContent: 'center',
87
+ marginRight: 16,
88
+ },
89
+ icon: {
90
+ fontSize: 24,
91
+ },
92
+ content: {
93
+ flex: 1,
94
+ },
95
+ title: {
96
+ fontSize: 16,
97
+ fontWeight: '600',
98
+ color: '#111827',
99
+ marginBottom: 4,
100
+ },
101
+ description: {
102
+ fontSize: 14,
103
+ color: '#6B7280',
104
+ lineHeight: 20,
105
+ },
106
+ });
@@ -0,0 +1,125 @@
1
+ /**
2
+ * useAIConsent Hook
3
+ * Manages AI consent state and display logic
4
+ * Required by Apple App Store Guidelines 5.1.1(i) & 5.1.2(i)
5
+ *
6
+ * Features:
7
+ * - Checks if user has consented to AI services
8
+ * - Shows modal on first use
9
+ * - Persists consent state
10
+ * - Memoized for performance
11
+ */
12
+
13
+ import { useState, useEffect, useCallback } from 'react';
14
+ import { useStorage } from '@umituz/react-native-design-system/storage';
15
+
16
+ export interface AIConsentState {
17
+ hasConsented: boolean;
18
+ consentTimestamp?: number;
19
+ consentVersion?: string;
20
+ }
21
+
22
+ const CONSENT_STORAGE_KEY = '@app:ai_consent_accepted';
23
+ const CURRENT_CONSENT_VERSION = '1.0';
24
+
25
+ export interface UseAIConsentReturn {
26
+ isLoading: boolean;
27
+ isConsentModalVisible: boolean;
28
+ hasConsented: boolean;
29
+ consentState: AIConsentState | null;
30
+ handleAcceptConsent: () => Promise<void>;
31
+ handleDeclineConsent: () => void;
32
+ showConsentModal: () => void;
33
+ checkConsent: () => Promise<void>;
34
+ }
35
+
36
+ export const useAIConsent = (): UseAIConsentReturn => {
37
+ const { getString, setString } = useStorage();
38
+ const [isConsentModalVisible, setIsConsentModalVisible] = useState(false);
39
+ const [hasConsented, setHasConsented] = useState(false);
40
+ const [consentState, setConsentState] = useState<AIConsentState | null>(null);
41
+ const [isLoading, setIsLoading] = useState(true);
42
+
43
+ /**
44
+ * Check if user has previously consented
45
+ */
46
+ const checkConsent = useCallback(async () => {
47
+ setIsLoading(true);
48
+ try {
49
+ const consentData = await getString(CONSENT_STORAGE_KEY, '');
50
+
51
+ if (consentData) {
52
+ const parsed: AIConsentState = JSON.parse(consentData);
53
+ setConsentState(parsed);
54
+ setHasConsented(parsed.hasConsented);
55
+
56
+ // Show modal if user hasn't consented
57
+ if (!parsed.hasConsented) {
58
+ setIsConsentModalVisible(true);
59
+ }
60
+ } else {
61
+ // No consent found - show modal
62
+ setIsConsentModalVisible(true);
63
+ }
64
+ } catch (error) {
65
+ console.error('[useAIConsent] Failed to check consent:', error);
66
+ // On error, show modal to be safe
67
+ setIsConsentModalVisible(true);
68
+ } finally {
69
+ setIsLoading(false);
70
+ }
71
+ }, [getString]);
72
+
73
+ /**
74
+ * Handle user accepting AI consent
75
+ */
76
+ const handleAcceptConsent = useCallback(async () => {
77
+ try {
78
+ const newState: AIConsentState = {
79
+ hasConsented: true,
80
+ consentTimestamp: Date.now(),
81
+ consentVersion: CURRENT_CONSENT_VERSION,
82
+ };
83
+
84
+ await setString(CONSENT_STORAGE_KEY, JSON.stringify(newState));
85
+ setConsentState(newState);
86
+ setHasConsented(true);
87
+ setIsConsentModalVisible(false);
88
+ } catch (error) {
89
+ console.error('[useAIConsent] Failed to save consent:', error);
90
+ throw error;
91
+ }
92
+ }, [setString]);
93
+
94
+ /**
95
+ * Handle user declining AI consent
96
+ */
97
+ const handleDeclineConsent = useCallback(() => {
98
+ setIsConsentModalVisible(false);
99
+ // User declined - they can still use the app but AI features will be blocked
100
+ // This is handled by the hasConsented flag
101
+ }, []);
102
+
103
+ /**
104
+ * Manually show consent modal (e.g., from settings)
105
+ */
106
+ const showConsentModal = useCallback(() => {
107
+ setIsConsentModalVisible(true);
108
+ }, []);
109
+
110
+ // Check consent on mount
111
+ useEffect(() => {
112
+ void checkConsent();
113
+ }, [checkConsent]);
114
+
115
+ return {
116
+ isLoading,
117
+ isConsentModalVisible,
118
+ hasConsented,
119
+ consentState,
120
+ handleAcceptConsent,
121
+ handleDeclineConsent,
122
+ showConsentModal,
123
+ checkConsent,
124
+ };
125
+ };