@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
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useLocalization } from "@umituz/react-native-localization";
|
|
3
|
+
import { SupportSection } from "../../../../domains/feedback/presentation/components/SupportSection";
|
|
4
|
+
import { FAQSection } from "../../../../domains/faqs/presentation/components/FAQSection";
|
|
5
|
+
import { SettingsSection } from "../../../components/SettingsSection";
|
|
6
|
+
import { SettingItem } from "../../../components/SettingItem";
|
|
7
|
+
|
|
8
|
+
interface SupportSettingsSectionProps {
|
|
9
|
+
features: any;
|
|
10
|
+
normalizedConfig: any;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const SupportSettingsSection: React.FC<SupportSettingsSectionProps> = ({
|
|
14
|
+
features,
|
|
15
|
+
normalizedConfig,
|
|
16
|
+
}) => {
|
|
17
|
+
const { t } = useLocalization();
|
|
18
|
+
|
|
19
|
+
if (!(features.feedback || features.rating || features.faqs)) return null;
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<SettingsSection title={t("settings.support.title")}>
|
|
23
|
+
{(features.feedback || features.rating) && (
|
|
24
|
+
<SupportSection
|
|
25
|
+
renderSection={(props: any) => <>{props.children}</>}
|
|
26
|
+
renderItem={(props: any) => <SettingItem {...props} />}
|
|
27
|
+
feedbackConfig={{
|
|
28
|
+
enabled: features.feedback,
|
|
29
|
+
config: {
|
|
30
|
+
...normalizedConfig.feedback.config,
|
|
31
|
+
title: normalizedConfig.feedback.config?.title || t("settings.feedback.title"),
|
|
32
|
+
description: normalizedConfig.feedback.config?.description || t("settings.feedback.description"),
|
|
33
|
+
}
|
|
34
|
+
}}
|
|
35
|
+
ratingConfig={{
|
|
36
|
+
enabled: features.rating,
|
|
37
|
+
config: {
|
|
38
|
+
...normalizedConfig.rating.config,
|
|
39
|
+
title: normalizedConfig.rating.config?.title || t("settings.rating.title"),
|
|
40
|
+
description: normalizedConfig.rating.config?.description || t("settings.rating.description"),
|
|
41
|
+
}
|
|
42
|
+
}}
|
|
43
|
+
feedbackModalTexts={{
|
|
44
|
+
title: t("settings.feedback.modal.title"),
|
|
45
|
+
ratingLabel: t("settings.feedback.modal.ratingLabel"),
|
|
46
|
+
descriptionPlaceholder: t("settings.feedback.modal.descriptionPlaceholder"),
|
|
47
|
+
submitButton: t("settings.feedback.modal.submitButton"),
|
|
48
|
+
submittingButton: t("settings.feedback.modal.submittingButton"),
|
|
49
|
+
feedbackTypes: [
|
|
50
|
+
{ type: 'general', label: t("settings.feedback.types.general") },
|
|
51
|
+
{ type: 'bug_report', label: t("settings.feedback.types.bugReport") },
|
|
52
|
+
{ type: 'feature_request', label: t("settings.feedback.types.featureRequest") },
|
|
53
|
+
{ type: 'improvement', label: t("settings.feedback.types.improvement") },
|
|
54
|
+
{ type: 'other', label: t("settings.feedback.types.other") },
|
|
55
|
+
],
|
|
56
|
+
defaultTitle: (type) => {
|
|
57
|
+
const titles: Record<string, string> = {
|
|
58
|
+
general: t("settings.feedback.types.general"),
|
|
59
|
+
bug_report: t("settings.feedback.types.bugReport"),
|
|
60
|
+
feature_request: t("settings.feedback.types.featureRequest"),
|
|
61
|
+
improvement: t("settings.feedback.types.improvement"),
|
|
62
|
+
other: t("settings.feedback.types.other"),
|
|
63
|
+
};
|
|
64
|
+
return titles[type] || type;
|
|
65
|
+
},
|
|
66
|
+
}}
|
|
67
|
+
/>
|
|
68
|
+
)}
|
|
69
|
+
|
|
70
|
+
{features.faqs && (
|
|
71
|
+
<FAQSection
|
|
72
|
+
renderSection={(props: any) => <>{props.children}</>}
|
|
73
|
+
renderItem={(props: any) => <SettingItem {...props} />}
|
|
74
|
+
config={{
|
|
75
|
+
enabled: features.faqs,
|
|
76
|
+
...normalizedConfig.faqs.config,
|
|
77
|
+
title: normalizedConfig.faqs.config?.title || t("settings.faqs.title"),
|
|
78
|
+
description: normalizedConfig.faqs.config?.description || t("settings.faqs.description"),
|
|
79
|
+
}}
|
|
80
|
+
/>
|
|
81
|
+
)}
|
|
82
|
+
</SettingsSection>
|
|
83
|
+
);
|
|
84
|
+
};
|
|
@@ -6,20 +6,6 @@
|
|
|
6
6
|
import { useMemo } from "react";
|
|
7
7
|
import type { NormalizedConfig } from "../utils/normalizeConfig";
|
|
8
8
|
|
|
9
|
-
// Optional notification service
|
|
10
|
-
let notificationService: {
|
|
11
|
-
hasPermissions?: () => Promise<boolean>;
|
|
12
|
-
requestPermissions?: () => Promise<void>;
|
|
13
|
-
} | null = null;
|
|
14
|
-
try {
|
|
15
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
16
|
-
const module = require("@umituz/react-native-notifications");
|
|
17
|
-
if (module?.notificationService && typeof module.notificationService === 'object') {
|
|
18
|
-
notificationService = module.notificationService;
|
|
19
|
-
}
|
|
20
|
-
} catch {
|
|
21
|
-
// Package not available
|
|
22
|
-
}
|
|
23
9
|
|
|
24
10
|
/**
|
|
25
11
|
* Check if navigation screen exists
|
|
@@ -75,8 +61,7 @@ export function useFeatureDetection(
|
|
|
75
61
|
faqs,
|
|
76
62
|
} = normalizedConfig;
|
|
77
63
|
|
|
78
|
-
const notificationServiceAvailable =
|
|
79
|
-
options?.notificationServiceAvailable ?? notificationService !== null;
|
|
64
|
+
const notificationServiceAvailable = !!options?.notificationServiceAvailable;
|
|
80
65
|
|
|
81
66
|
return {
|
|
82
67
|
appearance:
|
|
@@ -149,22 +149,29 @@ export interface UserProfileConfig {
|
|
|
149
149
|
accountSettingsRoute?: string;
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
+
/**
|
|
153
|
+
* Subscription Settings Item Config
|
|
154
|
+
* Simplified config for settings list item display
|
|
155
|
+
*/
|
|
156
|
+
export interface SubscriptionSettingsItemConfig {
|
|
157
|
+
title: string;
|
|
158
|
+
description?: string;
|
|
159
|
+
isPremium: boolean;
|
|
160
|
+
statusLabel: string;
|
|
161
|
+
icon?: string;
|
|
162
|
+
onPress?: () => void;
|
|
163
|
+
}
|
|
164
|
+
|
|
152
165
|
/**
|
|
153
166
|
* Subscription Settings Configuration
|
|
154
|
-
* App must provide all data via
|
|
167
|
+
* App must provide all data via props (no internal fetch)
|
|
155
168
|
*/
|
|
156
169
|
export interface SubscriptionConfig {
|
|
157
170
|
/** Show subscription section */
|
|
158
171
|
enabled?: FeatureVisibility;
|
|
159
|
-
/**
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
description?: string;
|
|
163
|
-
/** Custom icon name (Ionicons) */
|
|
164
|
-
icon?: string;
|
|
165
|
-
/** Custom section title for grouping */
|
|
166
|
-
sectionTitle?: string;
|
|
167
|
-
/** Section configuration (app provides all data) */
|
|
172
|
+
/** Settings item configuration (for settings list display) */
|
|
173
|
+
settingsItem?: SubscriptionSettingsItemConfig;
|
|
174
|
+
/** Detail section configuration (for detail screen) */
|
|
168
175
|
sectionConfig?: {
|
|
169
176
|
statusType: "active" | "expired" | "none";
|
|
170
177
|
isPremium: boolean;
|
|
@@ -248,3 +255,21 @@ export interface RatingConfig {
|
|
|
248
255
|
/** Custom handler for rating action (e.g. open store review) */
|
|
249
256
|
onRate?: () => void;
|
|
250
257
|
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Cloud Sync Settings Configuration
|
|
261
|
+
*/
|
|
262
|
+
export interface CloudSyncConfig {
|
|
263
|
+
/** Enable cloud sync feature */
|
|
264
|
+
enabled?: FeatureVisibility;
|
|
265
|
+
/** Custom title for the sync section */
|
|
266
|
+
title?: string;
|
|
267
|
+
/** Custom description for the sync toggle */
|
|
268
|
+
description?: string;
|
|
269
|
+
/** Custom icon name (Ionicons) */
|
|
270
|
+
icon?: string;
|
|
271
|
+
/** Custom section title for grouping */
|
|
272
|
+
sectionTitle?: string;
|
|
273
|
+
/** Firestore collection name */
|
|
274
|
+
collectionName?: string;
|
|
275
|
+
}
|
|
@@ -16,6 +16,7 @@ import type {
|
|
|
16
16
|
FeedbackConfig,
|
|
17
17
|
RatingConfig,
|
|
18
18
|
FAQConfig,
|
|
19
|
+
CloudSyncConfig,
|
|
19
20
|
} from "./FeatureConfig";
|
|
20
21
|
|
|
21
22
|
/**
|
|
@@ -115,6 +116,12 @@ export interface SettingsConfig {
|
|
|
115
116
|
*/
|
|
116
117
|
faqs?: FeatureVisibility | FAQConfig;
|
|
117
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Cloud sync settings configuration
|
|
121
|
+
* @default false
|
|
122
|
+
*/
|
|
123
|
+
cloudSync?: FeatureVisibility | CloudSyncConfig;
|
|
124
|
+
|
|
118
125
|
/**
|
|
119
126
|
* Custom empty state text when no settings are available
|
|
120
127
|
*/
|
|
@@ -1,301 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Appearance Service
|
|
3
|
-
*
|
|
4
|
-
* Business logic for appearance management
|
|
5
|
-
* Single Responsibility: Business logic only, no presentation logic
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { AppearanceStorage } from "../storage/appearanceStorage";
|
|
9
|
-
import { useAppearanceStore } from "../stores/appearanceStore";
|
|
10
|
-
import {
|
|
11
|
-
useTheme,
|
|
12
|
-
useDesignSystemTheme,
|
|
13
|
-
type ThemeMode,
|
|
14
|
-
type CustomThemeColors,
|
|
15
|
-
} from "@umituz/react-native-design-system";
|
|
16
|
-
import { getSystemTheme } from "./systemThemeDetection";
|
|
17
|
-
import { validateAppearanceSettings } from "./validation";
|
|
18
|
-
import type { AppearanceSettings } from "../../types";
|
|
19
|
-
|
|
20
|
-
export class AppearanceService {
|
|
21
|
-
private readonly DEFAULT_THEME_MODE: ThemeMode = "light"; // Use system preference as default
|
|
22
|
-
private _isInitialized = false;
|
|
23
|
-
private initPromise: Promise<void> | null = null;
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Initialize appearance settings
|
|
27
|
-
* Business logic: Coordinate initialization process
|
|
28
|
-
*/
|
|
29
|
-
async initialize(): Promise<void> {
|
|
30
|
-
// Prevent multiple initializations
|
|
31
|
-
if (this._isInitialized || this.initPromise) {
|
|
32
|
-
return this.initPromise || Promise.resolve();
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
this.initPromise = this._performInitialization();
|
|
36
|
-
return this.initPromise;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
private async _performInitialization(): Promise<void> {
|
|
40
|
-
try {
|
|
41
|
-
if (__DEV__) {
|
|
42
|
-
console.log("[AppearanceService] Initializing appearance settings");
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const savedSettings = await AppearanceStorage.getSettings();
|
|
46
|
-
|
|
47
|
-
if (savedSettings) {
|
|
48
|
-
// Load saved settings
|
|
49
|
-
useAppearanceStore.getState().setSettings(savedSettings);
|
|
50
|
-
useAppearanceStore.getState().setInitialized(true);
|
|
51
|
-
|
|
52
|
-
// Sync with design system theme
|
|
53
|
-
await this.syncWithDesignSystem(savedSettings);
|
|
54
|
-
} else {
|
|
55
|
-
// Use system theme as default, fallback to light
|
|
56
|
-
const systemTheme = getSystemTheme();
|
|
57
|
-
const defaultSettings: AppearanceSettings = {
|
|
58
|
-
themeMode: systemTheme || this.DEFAULT_THEME_MODE,
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
useAppearanceStore.getState().setSettings(defaultSettings);
|
|
62
|
-
useAppearanceStore.getState().setInitialized(true);
|
|
63
|
-
|
|
64
|
-
// Sync with design system theme
|
|
65
|
-
await this.syncWithDesignSystem(defaultSettings);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
this._isInitialized = true;
|
|
69
|
-
} catch (error) {
|
|
70
|
-
if (__DEV__) {
|
|
71
|
-
console.error("[AppearanceService] Initialization failed:", error);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Fallback to system theme or light mode on error
|
|
75
|
-
const systemTheme = getSystemTheme();
|
|
76
|
-
const fallbackSettings: AppearanceSettings = {
|
|
77
|
-
themeMode: systemTheme || this.DEFAULT_THEME_MODE,
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
useAppearanceStore.getState().setSettings(fallbackSettings);
|
|
81
|
-
useAppearanceStore.getState().setInitialized(true);
|
|
82
|
-
|
|
83
|
-
await this.syncWithDesignSystem(fallbackSettings);
|
|
84
|
-
this._isInitialized = true;
|
|
85
|
-
} finally {
|
|
86
|
-
this.initPromise = null;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Get current theme mode
|
|
92
|
-
* Business logic: Provide theme mode data
|
|
93
|
-
*/
|
|
94
|
-
getThemeMode(): ThemeMode {
|
|
95
|
-
return useAppearanceStore.getState().settings.themeMode;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Set theme mode
|
|
100
|
-
* Business logic: Validate and apply theme mode
|
|
101
|
-
*/
|
|
102
|
-
async setThemeMode(mode: ThemeMode): Promise<void> {
|
|
103
|
-
try {
|
|
104
|
-
if (__DEV__) {
|
|
105
|
-
console.log("[AppearanceService] Setting theme mode:", mode);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Validate theme mode
|
|
109
|
-
if (!mode || (mode !== 'light' && mode !== 'dark')) {
|
|
110
|
-
throw new Error(`Invalid theme mode: ${mode}`);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const currentSettings = useAppearanceStore.getState().settings;
|
|
114
|
-
const newSettings: AppearanceSettings = {
|
|
115
|
-
...currentSettings,
|
|
116
|
-
themeMode: mode,
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
// Update store
|
|
120
|
-
useAppearanceStore.getState().updateThemeMode(mode);
|
|
121
|
-
|
|
122
|
-
// Persist to storage
|
|
123
|
-
await AppearanceStorage.setSettings(newSettings);
|
|
124
|
-
|
|
125
|
-
// Sync with design system
|
|
126
|
-
await this.syncWithDesignSystem(newSettings);
|
|
127
|
-
} catch (error) {
|
|
128
|
-
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
129
|
-
if (__DEV__) {
|
|
130
|
-
console.error("[AppearanceService] Failed to set theme mode:", errorMessage);
|
|
131
|
-
}
|
|
132
|
-
throw error;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Toggle theme mode
|
|
138
|
-
* Business logic: Toggle between light and dark
|
|
139
|
-
*/
|
|
140
|
-
async toggleTheme(): Promise<void> {
|
|
141
|
-
try {
|
|
142
|
-
const currentMode = this.getThemeMode();
|
|
143
|
-
const newMode: ThemeMode =
|
|
144
|
-
currentMode === "light" ? "dark" : "light";
|
|
145
|
-
|
|
146
|
-
await this.setThemeMode(newMode);
|
|
147
|
-
} catch (error) {
|
|
148
|
-
if (__DEV__) {
|
|
149
|
-
console.error("[AppearanceService] Failed to toggle theme:", error);
|
|
150
|
-
}
|
|
151
|
-
throw error;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Get custom colors
|
|
157
|
-
* Business logic: Provide custom colors data
|
|
158
|
-
*/
|
|
159
|
-
getCustomColors(): CustomThemeColors | undefined {
|
|
160
|
-
return useAppearanceStore.getState().settings.customColors;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Set custom colors
|
|
165
|
-
* Business logic: Validate and apply custom colors
|
|
166
|
-
*/
|
|
167
|
-
async setCustomColors(colors: CustomThemeColors): Promise<void> {
|
|
168
|
-
try {
|
|
169
|
-
if (__DEV__) {
|
|
170
|
-
console.log("[AppearanceService] Setting custom colors:", colors);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Validate custom colors
|
|
174
|
-
const validation = validateAppearanceSettings({ customColors: colors });
|
|
175
|
-
if (!validation.isValid) {
|
|
176
|
-
throw new Error(`Invalid custom colors: ${validation.errors.join(', ')}`);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const currentSettings = useAppearanceStore.getState().settings;
|
|
180
|
-
const newSettings: AppearanceSettings = {
|
|
181
|
-
...currentSettings,
|
|
182
|
-
customColors: {
|
|
183
|
-
...currentSettings.customColors,
|
|
184
|
-
...colors,
|
|
185
|
-
},
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
// Update store
|
|
189
|
-
useAppearanceStore.getState().updateCustomColors(newSettings.customColors);
|
|
190
|
-
|
|
191
|
-
// Persist to storage
|
|
192
|
-
await AppearanceStorage.setSettings(newSettings);
|
|
193
|
-
|
|
194
|
-
// Sync with design system
|
|
195
|
-
await this.syncWithDesignSystem(newSettings);
|
|
196
|
-
} catch (error) {
|
|
197
|
-
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
198
|
-
if (__DEV__) {
|
|
199
|
-
console.error("[AppearanceService] Failed to set custom colors:", errorMessage);
|
|
200
|
-
}
|
|
201
|
-
throw error;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Reset custom colors to defaults
|
|
207
|
-
* Business logic: Reset custom colors
|
|
208
|
-
*/
|
|
209
|
-
async resetCustomColors(): Promise<void> {
|
|
210
|
-
try {
|
|
211
|
-
if (__DEV__) {
|
|
212
|
-
console.log("[AppearanceService] Resetting custom colors");
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const currentSettings = useAppearanceStore.getState().settings;
|
|
216
|
-
const newSettings: AppearanceSettings = {
|
|
217
|
-
...currentSettings,
|
|
218
|
-
customColors: undefined,
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
// Update store
|
|
222
|
-
useAppearanceStore.getState().updateCustomColors(undefined);
|
|
223
|
-
|
|
224
|
-
// Persist to storage
|
|
225
|
-
await AppearanceStorage.setSettings(newSettings);
|
|
226
|
-
|
|
227
|
-
// Sync with design System
|
|
228
|
-
await this.syncWithDesignSystem(newSettings);
|
|
229
|
-
} catch (error) {
|
|
230
|
-
if (__DEV__) {
|
|
231
|
-
console.error("[AppearanceService] Failed to reset custom colors:", error);
|
|
232
|
-
}
|
|
233
|
-
throw error;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Reset all appearance settings
|
|
239
|
-
* Business logic: Reset all settings
|
|
240
|
-
*/
|
|
241
|
-
async reset(): Promise<void> {
|
|
242
|
-
try {
|
|
243
|
-
if (__DEV__) {
|
|
244
|
-
console.log("[AppearanceService] Resetting all appearance settings");
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
// Clear storage
|
|
248
|
-
await AppearanceStorage.clear();
|
|
249
|
-
|
|
250
|
-
// Reset store to defaults
|
|
251
|
-
useAppearanceStore.getState().resetState();
|
|
252
|
-
|
|
253
|
-
// Reset design system theme to system preference
|
|
254
|
-
const systemTheme = getSystemTheme();
|
|
255
|
-
const defaultSettings: AppearanceSettings = {
|
|
256
|
-
themeMode: systemTheme || this.DEFAULT_THEME_MODE,
|
|
257
|
-
};
|
|
258
|
-
|
|
259
|
-
await this.syncWithDesignSystem(defaultSettings);
|
|
260
|
-
} catch (error) {
|
|
261
|
-
if (__DEV__) {
|
|
262
|
-
console.error("[AppearanceService] Failed to reset appearance:", error);
|
|
263
|
-
}
|
|
264
|
-
throw error;
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* Check if appearance is initialized
|
|
270
|
-
* Business logic: Provide initialization status
|
|
271
|
-
*/
|
|
272
|
-
isInitialized(): boolean {
|
|
273
|
-
return useAppearanceStore.getState().isInitialized;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* Sync appearance settings with design system
|
|
278
|
-
* Private helper method
|
|
279
|
-
*/
|
|
280
|
-
private async syncWithDesignSystem(settings: AppearanceSettings): Promise<void> {
|
|
281
|
-
try {
|
|
282
|
-
// Sync theme mode
|
|
283
|
-
useTheme.getState().setThemeMode(settings.themeMode);
|
|
284
|
-
useDesignSystemTheme.getState().setThemeMode(settings.themeMode);
|
|
285
|
-
|
|
286
|
-
// Sync custom colors
|
|
287
|
-
useDesignSystemTheme.getState().setCustomColors(settings.customColors);
|
|
288
|
-
|
|
289
|
-
if (__DEV__) {
|
|
290
|
-
console.log("[AppearanceService] Synced with design system:", settings);
|
|
291
|
-
}
|
|
292
|
-
} catch (error) {
|
|
293
|
-
if (__DEV__) {
|
|
294
|
-
console.error("[AppearanceService] Failed to sync with design system:", error);
|
|
295
|
-
}
|
|
296
|
-
throw error;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
export const appearanceService = new AppearanceService();
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Appearance Storage Service
|
|
3
|
-
*
|
|
4
|
-
* Handles persistence of appearance settings using AsyncStorage
|
|
5
|
-
* Single Responsibility: Pure storage operations only
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { storageRepository, unwrap } from "@umituz/react-native-storage";
|
|
9
|
-
import type { ThemeMode } from "@umituz/react-native-design-system";
|
|
10
|
-
import type { AppearanceSettings } from "../../types";
|
|
11
|
-
|
|
12
|
-
const STORAGE_KEYS = {
|
|
13
|
-
APPEARANCE_SETTINGS: "@appearance_settings",
|
|
14
|
-
} as const;
|
|
15
|
-
|
|
16
|
-
const DEFAULT_SETTINGS: AppearanceSettings = {
|
|
17
|
-
themeMode: "dark",
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export class AppearanceStorage {
|
|
21
|
-
/**
|
|
22
|
-
* Get saved appearance settings
|
|
23
|
-
* Pure storage operation - no business logic
|
|
24
|
-
*/
|
|
25
|
-
static async getSettings(): Promise<AppearanceSettings | null> {
|
|
26
|
-
try {
|
|
27
|
-
const result = await storageRepository.getItem<AppearanceSettings>(
|
|
28
|
-
STORAGE_KEYS.APPEARANCE_SETTINGS,
|
|
29
|
-
DEFAULT_SETTINGS,
|
|
30
|
-
);
|
|
31
|
-
const data = unwrap(result, DEFAULT_SETTINGS);
|
|
32
|
-
return data;
|
|
33
|
-
} catch (error) {
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Save appearance settings
|
|
40
|
-
* Pure storage operation - no business logic
|
|
41
|
-
*/
|
|
42
|
-
static async setSettings(settings: AppearanceSettings): Promise<void> {
|
|
43
|
-
const result = await storageRepository.setItem(
|
|
44
|
-
STORAGE_KEYS.APPEARANCE_SETTINGS,
|
|
45
|
-
settings,
|
|
46
|
-
);
|
|
47
|
-
if (!result.success) {
|
|
48
|
-
throw new Error("Failed to save appearance settings");
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Get saved theme mode
|
|
54
|
-
* Pure storage operation - no business logic
|
|
55
|
-
*/
|
|
56
|
-
static async getThemeMode(): Promise<ThemeMode | null> {
|
|
57
|
-
try {
|
|
58
|
-
const settings = await this.getSettings();
|
|
59
|
-
return settings?.themeMode || null;
|
|
60
|
-
} catch (error) {
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Save theme mode
|
|
67
|
-
* Pure storage operation - no business logic
|
|
68
|
-
*/
|
|
69
|
-
static async setThemeMode(themeMode: ThemeMode): Promise<void> {
|
|
70
|
-
const currentSettings = (await this.getSettings()) || {
|
|
71
|
-
themeMode: "dark",
|
|
72
|
-
};
|
|
73
|
-
await this.setSettings({
|
|
74
|
-
...currentSettings,
|
|
75
|
-
themeMode,
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Get custom theme colors
|
|
81
|
-
* Pure storage operation - no business logic
|
|
82
|
-
*/
|
|
83
|
-
static async getCustomColors() {
|
|
84
|
-
try {
|
|
85
|
-
const settings = await this.getSettings();
|
|
86
|
-
return settings?.customColors || null;
|
|
87
|
-
} catch (error) {
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Save custom theme colors
|
|
94
|
-
* Pure storage operation - no business logic
|
|
95
|
-
*/
|
|
96
|
-
static async setCustomColors(
|
|
97
|
-
customColors: AppearanceSettings["customColors"],
|
|
98
|
-
): Promise<void> {
|
|
99
|
-
const currentSettings = (await this.getSettings()) || {
|
|
100
|
-
themeMode: "dark",
|
|
101
|
-
};
|
|
102
|
-
await this.setSettings({
|
|
103
|
-
...currentSettings,
|
|
104
|
-
customColors,
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Clear all appearance settings
|
|
110
|
-
* Pure storage operation - no business logic
|
|
111
|
-
*/
|
|
112
|
-
static async clear(): Promise<void> {
|
|
113
|
-
const result = await storageRepository.removeItem(
|
|
114
|
-
STORAGE_KEYS.APPEARANCE_SETTINGS,
|
|
115
|
-
);
|
|
116
|
-
if (!result.success) {
|
|
117
|
-
throw new Error("Failed to clear appearance settings");
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|