@umituz/react-native-settings 4.17.27 → 4.17.31
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 +15 -4
- package/src/domains/about/presentation/components/AboutSection.tsx +14 -71
- package/src/domains/appearance/application/ports/IAppearanceRepository.ts +8 -0
- package/src/domains/appearance/hooks/index.ts +1 -1
- package/src/domains/appearance/hooks/useAppearance.ts +18 -58
- package/src/domains/appearance/hooks/useAppearanceActions.ts +20 -128
- package/src/domains/appearance/infrastructure/repositories/AppearanceRepository.ts +34 -0
- package/src/domains/appearance/infrastructure/services/AppearanceService.ts +51 -0
- package/src/domains/appearance/presentation/components/AppearanceSection.tsx +2 -2
- package/src/domains/appearance/presentation/hooks/mutations/useAppearanceMutations.ts +36 -0
- package/src/domains/appearance/presentation/hooks/queries/useAppearanceQuery.ts +15 -0
- package/src/domains/disclaimer/presentation/components/DisclaimerModal.tsx +37 -40
- package/src/domains/faqs/presentation/components/FAQSection.tsx +1 -1
- package/src/domains/faqs/presentation/screens/FAQScreen.tsx +1 -1
- package/src/domains/feedback/presentation/components/FeedbackModal.tsx +11 -15
- package/src/domains/feedback/presentation/components/SupportSection.tsx +2 -2
- package/src/domains/legal/presentation/components/LegalItem.tsx +13 -129
- package/src/index.ts +19 -9
- package/src/infrastructure/repositories/SettingsRepository.ts +105 -0
- package/src/infrastructure/services/SettingsService.ts +47 -0
- package/src/presentation/components/SettingItem.tsx +77 -129
- package/src/presentation/components/SettingsFooter.tsx +9 -25
- package/src/presentation/components/SettingsSection.tsx +9 -20
- package/src/presentation/hooks/mutations/useSettingsMutations.ts +58 -0
- package/src/presentation/hooks/queries/useSettingsQuery.ts +27 -0
- package/src/presentation/hooks/useSettings.ts +45 -0
- package/src/presentation/screens/components/SettingsContent.tsx +20 -247
- package/src/presentation/screens/components/sections/CustomSettingsList.tsx +31 -0
- package/src/presentation/screens/components/sections/FeatureSettingsSection.tsx +68 -0
- package/src/presentation/screens/components/sections/IdentitySettingsSection.tsx +43 -0
- package/src/presentation/screens/components/sections/ProfileSectionLoader.tsx +47 -0
- package/src/presentation/screens/components/sections/SubscriptionSettingsItem.tsx +157 -0
- package/src/presentation/screens/components/sections/SupportSettingsSection.tsx +84 -0
- package/src/presentation/screens/hooks/useFeatureDetection.ts +1 -16
- package/src/presentation/screens/types/FeatureConfig.ts +35 -10
- package/src/presentation/screens/types/SettingsConfig.ts +7 -0
- package/src/presentation/screens/types/index.ts +1 -0
- package/src/domains/appearance/infrastructure/services/appearanceService.ts +0 -301
- package/src/domains/appearance/infrastructure/storage/appearanceStorage.ts +0 -120
- package/src/domains/appearance/infrastructure/stores/appearanceStore.ts +0 -132
- package/src/infrastructure/storage/SettingsStore.ts +0 -189
- package/src/infrastructure/storage/__tests__/SettingsStore.test.tsx +0 -302
- package/src/presentation/components/CloudSyncSetting.tsx +0 -58
- /package/src/{domain/repositories → application/ports}/ISettingsRepository.ts +0 -0
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-settings",
|
|
3
|
-
"version": "4.17.
|
|
3
|
+
"version": "4.17.31",
|
|
4
4
|
"description": "Complete settings hub for React Native apps - consolidated package with settings, about, legal, appearance, feedback, FAQs, and rating",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"typecheck": "
|
|
9
|
-
"lint": "
|
|
8
|
+
"typecheck": "tsc --noEmit",
|
|
9
|
+
"lint": "eslint src --ext .ts,.tsx --max-warnings 0",
|
|
10
|
+
"lint:fix": "eslint src --ext .ts,.tsx --fix",
|
|
10
11
|
"version:patch": "npm version patch -m 'chore: release v%s'",
|
|
11
12
|
"version:minor": "npm version minor -m 'chore: release v%s'",
|
|
12
13
|
"version:major": "npm version major -m 'chore: release v%s'"
|
|
@@ -37,6 +38,7 @@
|
|
|
37
38
|
"@expo/vector-icons": ">=14.0.0",
|
|
38
39
|
"@react-navigation/native": ">=6.0.0",
|
|
39
40
|
"@react-navigation/stack": ">=6.0.0",
|
|
41
|
+
"@tanstack/react-query": ">=5.0.0",
|
|
40
42
|
"@umituz/react-native-auth": "latest",
|
|
41
43
|
"@umituz/react-native-design-system": "latest",
|
|
42
44
|
"@umituz/react-native-localization": "latest",
|
|
@@ -44,6 +46,7 @@
|
|
|
44
46
|
"@umituz/react-native-onboarding": "latest",
|
|
45
47
|
"@umituz/react-native-sentry": "latest",
|
|
46
48
|
"@umituz/react-native-storage": "latest",
|
|
49
|
+
"@umituz/react-native-tanstack": "latest",
|
|
47
50
|
"react": ">=19.0.0",
|
|
48
51
|
"react-native": ">=0.81.0",
|
|
49
52
|
"react-native-safe-area-context": ">=4.0.0",
|
|
@@ -54,14 +57,19 @@
|
|
|
54
57
|
"@expo/vector-icons": "^15.0.0",
|
|
55
58
|
"@react-navigation/native": "^6.1.18",
|
|
56
59
|
"@react-navigation/stack": "^6.4.1",
|
|
60
|
+
"@tanstack/react-query": "^5.0.0",
|
|
57
61
|
"@types/jest": "^29.5.14",
|
|
58
62
|
"@types/react": "~19.1.10",
|
|
63
|
+
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
|
64
|
+
"@typescript-eslint/parser": "^7.0.0",
|
|
59
65
|
"@umituz/react-native-auth": "latest",
|
|
60
66
|
"@umituz/react-native-design-system": "latest",
|
|
61
67
|
"@umituz/react-native-localization": "latest",
|
|
62
68
|
"@umituz/react-native-notifications": "latest",
|
|
63
69
|
"@umituz/react-native-onboarding": "latest",
|
|
64
70
|
"@umituz/react-native-storage": "latest",
|
|
71
|
+
"@umituz/react-native-tanstack": "latest",
|
|
72
|
+
"eslint": "^8.57.0",
|
|
65
73
|
"react": "19.1.0",
|
|
66
74
|
"react-native": "0.81.5",
|
|
67
75
|
"typescript": "^5.3.0"
|
|
@@ -75,6 +83,9 @@
|
|
|
75
83
|
"LICENSE"
|
|
76
84
|
],
|
|
77
85
|
"dependencies": {
|
|
78
|
-
"@
|
|
86
|
+
"@react-native-async-storage/async-storage": "^2.2.0",
|
|
87
|
+
"@react-native-firebase/app": "^23.7.0",
|
|
88
|
+
"@react-native-firebase/firestore": "^23.7.0",
|
|
89
|
+
"firebase": "^12.7.0"
|
|
79
90
|
}
|
|
80
91
|
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { View,
|
|
2
|
+
import { View, StyleSheet, ViewStyle } from 'react-native';
|
|
3
3
|
import { useNavigation } from '@react-navigation/native';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
useResponsiveDesignTokens,
|
|
6
|
+
AtomicText,
|
|
7
|
+
ListItem
|
|
8
|
+
} from '@umituz/react-native-design-system';
|
|
5
9
|
import { AboutConfig } from '../../domain/entities/AppInfo';
|
|
6
10
|
|
|
7
11
|
export interface AboutSectionProps {
|
|
@@ -23,7 +27,6 @@ export const AboutSection: React.FC<AboutSectionProps> = ({
|
|
|
23
27
|
}) => {
|
|
24
28
|
const navigation = useNavigation();
|
|
25
29
|
const tokens = useResponsiveDesignTokens();
|
|
26
|
-
const colors = tokens.colors;
|
|
27
30
|
|
|
28
31
|
const route = config?.route || config?.defaultRoute || 'About';
|
|
29
32
|
const title = propsTitle || config?.title;
|
|
@@ -41,57 +44,21 @@ export const AboutSection: React.FC<AboutSectionProps> = ({
|
|
|
41
44
|
if (!title) return null;
|
|
42
45
|
|
|
43
46
|
return (
|
|
44
|
-
<View style={[styles.sectionContainer, { backgroundColor: colors.surface }, containerStyle]}>
|
|
47
|
+
<View style={[styles.sectionContainer, { backgroundColor: tokens.colors.surface }, containerStyle]}>
|
|
45
48
|
{!!sectionTitle && (
|
|
46
49
|
<View style={styles.headerContainer}>
|
|
47
|
-
<AtomicText
|
|
48
|
-
type="titleMedium"
|
|
49
|
-
color="primary"
|
|
50
|
-
>
|
|
50
|
+
<AtomicText type="titleMedium" color="primary">
|
|
51
51
|
{sectionTitle}
|
|
52
52
|
</AtomicText>
|
|
53
53
|
</View>
|
|
54
54
|
)}
|
|
55
|
-
<
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
},
|
|
61
|
-
]}
|
|
55
|
+
<ListItem
|
|
56
|
+
title={title}
|
|
57
|
+
subtitle={description}
|
|
58
|
+
leftIcon="information-circle"
|
|
59
|
+
rightIcon="chevron-forward"
|
|
62
60
|
onPress={handlePress}
|
|
63
|
-
|
|
64
|
-
<View style={styles.content}>
|
|
65
|
-
<View
|
|
66
|
-
style={[
|
|
67
|
-
styles.iconContainer,
|
|
68
|
-
{ backgroundColor: `${colors.primary}15` },
|
|
69
|
-
]}
|
|
70
|
-
>
|
|
71
|
-
<AtomicIcon name="information-circle" size="lg" color="primary" />
|
|
72
|
-
</View>
|
|
73
|
-
<View style={styles.textContainer}>
|
|
74
|
-
<AtomicText
|
|
75
|
-
type="bodyLarge"
|
|
76
|
-
color="primary"
|
|
77
|
-
numberOfLines={1}
|
|
78
|
-
style={{ marginBottom: 4 }}
|
|
79
|
-
>
|
|
80
|
-
{title}
|
|
81
|
-
</AtomicText>
|
|
82
|
-
{!!description && (
|
|
83
|
-
<AtomicText
|
|
84
|
-
type="bodyMedium"
|
|
85
|
-
color="secondary"
|
|
86
|
-
numberOfLines={2}
|
|
87
|
-
>
|
|
88
|
-
{description}
|
|
89
|
-
</AtomicText>
|
|
90
|
-
)}
|
|
91
|
-
</View>
|
|
92
|
-
<AtomicIcon name="chevron-forward" size="md" color="secondary" />
|
|
93
|
-
</View>
|
|
94
|
-
</Pressable>
|
|
61
|
+
/>
|
|
95
62
|
</View>
|
|
96
63
|
);
|
|
97
64
|
};
|
|
@@ -107,28 +74,4 @@ const styles = StyleSheet.create({
|
|
|
107
74
|
paddingTop: 16,
|
|
108
75
|
paddingBottom: 8,
|
|
109
76
|
},
|
|
110
|
-
itemContainer: {
|
|
111
|
-
flexDirection: 'row',
|
|
112
|
-
alignItems: 'center',
|
|
113
|
-
paddingHorizontal: 16,
|
|
114
|
-
paddingVertical: 16,
|
|
115
|
-
minHeight: 72,
|
|
116
|
-
},
|
|
117
|
-
content: {
|
|
118
|
-
flex: 1,
|
|
119
|
-
flexDirection: 'row',
|
|
120
|
-
alignItems: 'center',
|
|
121
|
-
},
|
|
122
|
-
iconContainer: {
|
|
123
|
-
width: 48,
|
|
124
|
-
height: 48,
|
|
125
|
-
borderRadius: 12,
|
|
126
|
-
justifyContent: 'center',
|
|
127
|
-
alignItems: 'center',
|
|
128
|
-
marginRight: 16,
|
|
129
|
-
},
|
|
130
|
-
textContainer: {
|
|
131
|
-
flex: 1,
|
|
132
|
-
marginRight: 8,
|
|
133
|
-
},
|
|
134
77
|
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ThemeMode, CustomThemeColors } from "@umituz/react-native-design-system";
|
|
2
|
+
import { AppearanceSettings } from "../../types";
|
|
3
|
+
|
|
4
|
+
export interface IAppearanceRepository {
|
|
5
|
+
getSettings(): Promise<AppearanceSettings>;
|
|
6
|
+
saveSettings(settings: AppearanceSettings): Promise<void>;
|
|
7
|
+
clearSettings(): Promise<void>;
|
|
8
|
+
}
|
|
@@ -1,61 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* Hook for accessing appearance state and actions
|
|
5
|
-
* Single Responsibility: Presentation layer data access
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { useCallback } from "react";
|
|
9
|
-
import { useAppearanceStore } from "../infrastructure/stores/appearanceStore";
|
|
10
|
-
import { appearanceService } from "../infrastructure/services/appearanceService";
|
|
11
|
-
import type { ThemeMode, CustomThemeColors } from "../types";
|
|
1
|
+
import { useAppearanceQuery } from "../presentation/hooks/queries/useAppearanceQuery";
|
|
2
|
+
import { useAppearanceMutations } from "../presentation/hooks/mutations/useAppearanceMutations";
|
|
3
|
+
import { ThemeMode, CustomThemeColors } from "@umituz/react-native-design-system";
|
|
12
4
|
|
|
13
5
|
export const useAppearance = () => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const setCustomColors = useCallback(
|
|
31
|
-
async (colors: CustomThemeColors) => {
|
|
32
|
-
await appearanceService.setCustomColors(colors);
|
|
33
|
-
},
|
|
34
|
-
[]
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
const resetCustomColors = useCallback(
|
|
38
|
-
async () => {
|
|
39
|
-
await appearanceService.resetCustomColors();
|
|
40
|
-
},
|
|
41
|
-
[]
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
const reset = useCallback(
|
|
45
|
-
async () => {
|
|
46
|
-
await appearanceService.reset();
|
|
47
|
-
},
|
|
48
|
-
[]
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
return {
|
|
52
|
-
themeMode: store.settings.themeMode,
|
|
53
|
-
customColors: store.settings.customColors,
|
|
54
|
-
isInitialized: store.isInitialized,
|
|
55
|
-
setThemeMode,
|
|
56
|
-
toggleTheme,
|
|
57
|
-
setCustomColors,
|
|
58
|
-
resetCustomColors,
|
|
59
|
-
reset,
|
|
60
|
-
};
|
|
6
|
+
const { data: settings, isLoading } = useAppearanceQuery();
|
|
7
|
+
const {
|
|
8
|
+
updateThemeMutation,
|
|
9
|
+
updateColorsMutation,
|
|
10
|
+
resetAppearanceMutation
|
|
11
|
+
} = useAppearanceMutations();
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
themeMode: settings?.themeMode || "dark",
|
|
15
|
+
customColors: settings?.customColors,
|
|
16
|
+
isLoading,
|
|
17
|
+
setThemeMode: (mode: ThemeMode) => updateThemeMutation.mutate(mode),
|
|
18
|
+
setCustomColors: (colors: CustomThemeColors) => updateColorsMutation.mutate(colors),
|
|
19
|
+
reset: () => resetAppearanceMutation.mutate(),
|
|
20
|
+
};
|
|
61
21
|
};
|
|
@@ -1,139 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
* Appearance Actions Hook
|
|
3
|
-
* Single Responsibility: Handle appearance-related presentation actions
|
|
4
|
-
* Business logic extracted to service layer
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { useCallback, useState, useEffect, useRef } from "react";
|
|
1
|
+
import { useState, useCallback, useEffect } from "react";
|
|
8
2
|
import { useAppearance } from "./useAppearance";
|
|
9
|
-
import
|
|
10
|
-
ThemeMode,
|
|
11
|
-
CustomThemeColors,
|
|
12
|
-
} from "@umituz/react-native-design-system";
|
|
13
|
-
|
|
14
|
-
export interface UseAppearanceActionsReturn {
|
|
15
|
-
localCustomColors: CustomThemeColors;
|
|
16
|
-
handleThemeSelect: (mode: ThemeMode) => Promise<void>;
|
|
17
|
-
handleColorChange: (key: keyof CustomThemeColors, color: string) => void;
|
|
18
|
-
handleResetColors: (onConfirm?: () => void) => void;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface AppearanceActionsConfig {
|
|
22
|
-
onResetConfirm?: () => void;
|
|
23
|
-
onResetCancel?: () => void;
|
|
24
|
-
resetTitle?: string;
|
|
25
|
-
resetMessage?: string;
|
|
26
|
-
confirmLabel?: string;
|
|
27
|
-
cancelLabel?: string;
|
|
28
|
-
}
|
|
3
|
+
import { CustomThemeColors } from "@umituz/react-native-design-system";
|
|
29
4
|
|
|
30
|
-
export
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const { customColors, setThemeMode, setCustomColors, resetCustomColors } =
|
|
34
|
-
useAppearance();
|
|
5
|
+
export const useAppearanceActions = () => {
|
|
6
|
+
const { themeMode, customColors, setThemeMode, setCustomColors, reset } = useAppearance();
|
|
7
|
+
const [localCustomColors, setLocalCustomColors] = useState<CustomThemeColors>(customColors || {});
|
|
35
8
|
|
|
36
|
-
// Use ref to prevent unnecessary re-renders and memory leaks
|
|
37
|
-
const configRef = useRef(config);
|
|
38
|
-
configRef.current = config;
|
|
39
|
-
|
|
40
|
-
// Initialize local state with custom colors, prevent unnecessary updates
|
|
41
|
-
const [localCustomColors, setLocalCustomColors] = useState<CustomThemeColors>(() =>
|
|
42
|
-
customColors || {}
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
// Sync local state with store changes, but only if different
|
|
46
9
|
useEffect(() => {
|
|
47
|
-
if (customColors
|
|
48
|
-
if (__DEV__) {
|
|
49
|
-
console.log("[useAppearanceActions] Syncing local colors with store");
|
|
50
|
-
}
|
|
10
|
+
if (customColors) {
|
|
51
11
|
setLocalCustomColors(customColors);
|
|
52
12
|
}
|
|
53
|
-
}, [customColors]);
|
|
54
|
-
|
|
55
|
-
const handleThemeSelect = useCallback(
|
|
56
|
-
async (mode: ThemeMode) => {
|
|
57
|
-
try {
|
|
58
|
-
await setThemeMode(mode);
|
|
59
|
-
} catch (error) {
|
|
60
|
-
if (__DEV__) {
|
|
61
|
-
console.error("[useAppearanceActions] Failed to set theme mode:", error);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
[setThemeMode],
|
|
66
|
-
);
|
|
13
|
+
}, [customColors]);
|
|
67
14
|
|
|
68
|
-
const
|
|
69
|
-
(
|
|
70
|
-
|
|
71
|
-
// Prevent unnecessary updates if color hasn't changed
|
|
72
|
-
if (localCustomColors[key] === color) {
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
15
|
+
const handleThemeSelect = useCallback((mode: any) => {
|
|
16
|
+
setThemeMode(mode);
|
|
17
|
+
}, [setThemeMode]);
|
|
75
18
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
19
|
+
const handleColorChange = useCallback((key: keyof CustomThemeColors, color: string) => {
|
|
20
|
+
const newColors = { ...localCustomColors, [key]: color };
|
|
21
|
+
setLocalCustomColors(newColors);
|
|
22
|
+
setCustomColors(newColors);
|
|
23
|
+
}, [localCustomColors, setCustomColors]);
|
|
80
24
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
setLocalCustomColors(newColors);
|
|
86
|
-
setCustomColors(newColors);
|
|
87
|
-
} catch (error) {
|
|
88
|
-
if (__DEV__) {
|
|
89
|
-
console.error("[useAppearanceActions] Failed to update color:", error);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
},
|
|
93
|
-
[localCustomColors, setCustomColors],
|
|
94
|
-
);
|
|
95
|
-
|
|
96
|
-
const handleResetColors = useCallback(
|
|
97
|
-
(onConfirm?: () => void) => {
|
|
98
|
-
try {
|
|
99
|
-
// Generic reset handler - the host app should handle the UI confirmation
|
|
100
|
-
const resetAction = async () => {
|
|
101
|
-
try {
|
|
102
|
-
setLocalCustomColors({});
|
|
103
|
-
await resetCustomColors();
|
|
104
|
-
onConfirm?.();
|
|
105
|
-
configRef.current?.onResetConfirm?.();
|
|
106
|
-
} catch (error) {
|
|
107
|
-
if (__DEV__) {
|
|
108
|
-
console.error("[useAppearanceActions] Failed to reset colors:", error);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
// If no custom config provided, just reset directly
|
|
114
|
-
if (!configRef.current?.resetTitle) {
|
|
115
|
-
resetAction();
|
|
116
|
-
}
|
|
117
|
-
// Otherwise, the host app should handle showing the confirmation dialog
|
|
118
|
-
// and call resetAction when confirmed
|
|
119
|
-
} catch (error) {
|
|
120
|
-
if (__DEV__) {
|
|
121
|
-
console.error("[useAppearanceActions] Failed to handle reset colors:", error);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
},
|
|
125
|
-
[resetCustomColors],
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
// Cleanup effect
|
|
129
|
-
useEffect(() => {
|
|
130
|
-
return () => {
|
|
131
|
-
// Cleanup any pending operations or references
|
|
132
|
-
if (__DEV__) {
|
|
133
|
-
console.log("[useAppearanceActions] Cleanup");
|
|
134
|
-
}
|
|
135
|
-
};
|
|
136
|
-
}, []);
|
|
25
|
+
const handleResetColors = useCallback(() => {
|
|
26
|
+
reset();
|
|
27
|
+
setLocalCustomColors({});
|
|
28
|
+
}, [reset]);
|
|
137
29
|
|
|
138
30
|
return {
|
|
139
31
|
localCustomColors,
|
|
@@ -141,4 +33,4 @@ export function useAppearanceActions(
|
|
|
141
33
|
handleColorChange,
|
|
142
34
|
handleResetColors,
|
|
143
35
|
};
|
|
144
|
-
}
|
|
36
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { storageRepository, unwrap } from "@umituz/react-native-storage";
|
|
2
|
+
import { IAppearanceRepository } from "../../application/ports/IAppearanceRepository";
|
|
3
|
+
import { AppearanceSettings } from "../../types";
|
|
4
|
+
|
|
5
|
+
const STORAGE_KEY = "@appearance_settings";
|
|
6
|
+
const DEFAULT_SETTINGS: AppearanceSettings = {
|
|
7
|
+
themeMode: "dark",
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export class AppearanceRepository implements IAppearanceRepository {
|
|
11
|
+
async getSettings(): Promise<AppearanceSettings> {
|
|
12
|
+
const result = await storageRepository.getItem<AppearanceSettings>(
|
|
13
|
+
STORAGE_KEY,
|
|
14
|
+
DEFAULT_SETTINGS
|
|
15
|
+
);
|
|
16
|
+
return unwrap(result, DEFAULT_SETTINGS);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async saveSettings(settings: AppearanceSettings): Promise<void> {
|
|
20
|
+
const result = await storageRepository.setItem(STORAGE_KEY, settings);
|
|
21
|
+
if (!result.success) {
|
|
22
|
+
throw new Error("Failed to save appearance settings");
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async clearSettings(): Promise<void> {
|
|
27
|
+
const result = await storageRepository.removeItem(STORAGE_KEY);
|
|
28
|
+
if (!result.success) {
|
|
29
|
+
throw new Error("Failed to clear appearance settings");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const getAppearanceRepository = () => new AppearanceRepository();
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useTheme,
|
|
3
|
+
useDesignSystemTheme,
|
|
4
|
+
ThemeMode,
|
|
5
|
+
CustomThemeColors
|
|
6
|
+
} from "@umituz/react-native-design-system";
|
|
7
|
+
import { IAppearanceRepository } from "../../application/ports/IAppearanceRepository";
|
|
8
|
+
import { getAppearanceRepository } from "../repositories/AppearanceRepository";
|
|
9
|
+
import { AppearanceSettings } from "../../types";
|
|
10
|
+
|
|
11
|
+
export class AppearanceService {
|
|
12
|
+
constructor(private readonly repository: IAppearanceRepository = getAppearanceRepository()) { }
|
|
13
|
+
|
|
14
|
+
async getSettings(): Promise<AppearanceSettings> {
|
|
15
|
+
return this.repository.getSettings();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async setThemeMode(mode: ThemeMode): Promise<void> {
|
|
19
|
+
const settings = await this.repository.getSettings();
|
|
20
|
+
const newSettings = { ...settings, themeMode: mode };
|
|
21
|
+
await this.repository.saveSettings(newSettings);
|
|
22
|
+
this.syncWithDesignSystem(newSettings);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async setCustomColors(colors: CustomThemeColors): Promise<void> {
|
|
26
|
+
const settings = await this.repository.getSettings();
|
|
27
|
+
const newSettings = {
|
|
28
|
+
...settings,
|
|
29
|
+
customColors: { ...settings.customColors, ...colors }
|
|
30
|
+
};
|
|
31
|
+
await this.repository.saveSettings(newSettings);
|
|
32
|
+
this.syncWithDesignSystem(newSettings);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async resetSettings(): Promise<void> {
|
|
36
|
+
await this.repository.clearSettings();
|
|
37
|
+
const defaultSettings = await this.repository.getSettings();
|
|
38
|
+
this.syncWithDesignSystem(defaultSettings);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private syncWithDesignSystem(settings: AppearanceSettings): void {
|
|
42
|
+
useTheme.getState().setThemeMode(settings.themeMode);
|
|
43
|
+
useDesignSystemTheme.getState().setThemeMode(settings.themeMode);
|
|
44
|
+
if (settings.customColors) {
|
|
45
|
+
useDesignSystemTheme.getState().setCustomColors(settings.customColors);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const getAppearanceService = () => new AppearanceService();
|
|
51
|
+
export const appearanceService = getAppearanceService();
|
|
@@ -73,7 +73,7 @@ export const AppearanceSection: React.FC<AppearanceSectionProps> = ({
|
|
|
73
73
|
{ backgroundColor: `${colors.primary}15` },
|
|
74
74
|
]}
|
|
75
75
|
>
|
|
76
|
-
<AtomicIcon name="
|
|
76
|
+
<AtomicIcon name="color-palette" size="lg" color="primary" />
|
|
77
77
|
</View>
|
|
78
78
|
<View style={styles.textContainer}>
|
|
79
79
|
<AtomicText
|
|
@@ -94,7 +94,7 @@ export const AppearanceSection: React.FC<AppearanceSectionProps> = ({
|
|
|
94
94
|
</AtomicText>
|
|
95
95
|
)}
|
|
96
96
|
</View>
|
|
97
|
-
<AtomicIcon name="chevron-forward
|
|
97
|
+
<AtomicIcon name="chevron-forward" size="md" color="secondary" />
|
|
98
98
|
</View>
|
|
99
99
|
</Pressable>
|
|
100
100
|
</View>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
2
|
+
import { getAppearanceService } from "../../../infrastructure/services/AppearanceService";
|
|
3
|
+
import { appearanceKeys } from "../queries/useAppearanceQuery";
|
|
4
|
+
import { ThemeMode, CustomThemeColors } from "@umituz/react-native-design-system";
|
|
5
|
+
|
|
6
|
+
export const useAppearanceMutations = () => {
|
|
7
|
+
const queryClient = useQueryClient();
|
|
8
|
+
const service = getAppearanceService();
|
|
9
|
+
|
|
10
|
+
const updateThemeMutation = useMutation({
|
|
11
|
+
mutationFn: (mode: ThemeMode) => service.setThemeMode(mode),
|
|
12
|
+
onSuccess: () => {
|
|
13
|
+
queryClient.invalidateQueries({ queryKey: appearanceKeys.settings() });
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const updateColorsMutation = useMutation({
|
|
18
|
+
mutationFn: (colors: CustomThemeColors) => service.setCustomColors(colors),
|
|
19
|
+
onSuccess: () => {
|
|
20
|
+
queryClient.invalidateQueries({ queryKey: appearanceKeys.settings() });
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const resetAppearanceMutation = useMutation({
|
|
25
|
+
mutationFn: () => service.resetSettings(),
|
|
26
|
+
onSuccess: () => {
|
|
27
|
+
queryClient.invalidateQueries({ queryKey: appearanceKeys.settings() });
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
updateThemeMutation,
|
|
33
|
+
updateColorsMutation,
|
|
34
|
+
resetAppearanceMutation,
|
|
35
|
+
};
|
|
36
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useQuery } from "@tanstack/react-query";
|
|
2
|
+
import { getAppearanceService } from "../../../infrastructure/services/AppearanceService";
|
|
3
|
+
|
|
4
|
+
export const appearanceKeys = {
|
|
5
|
+
all: ["appearance"] as const,
|
|
6
|
+
settings: () => [...appearanceKeys.all, "settings"] as const,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const useAppearanceQuery = () => {
|
|
10
|
+
const service = getAppearanceService();
|
|
11
|
+
return useQuery({
|
|
12
|
+
queryKey: appearanceKeys.settings(),
|
|
13
|
+
queryFn: () => service.getSettings(),
|
|
14
|
+
});
|
|
15
|
+
};
|