@umituz/react-native-settings 4.20.58 → 4.20.60
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/.github/ISSUE_TEMPLATE/bug_report.md +51 -0
- package/.github/ISSUE_TEMPLATE/documentation.md +52 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +63 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +84 -0
- package/AI_AGENT_GUIDELINES.md +367 -0
- package/ARCHITECTURE.md +246 -0
- package/CHANGELOG.md +67 -0
- package/CODE_OF_CONDUCT.md +75 -0
- package/CONTRIBUTING.md +107 -0
- package/DOCUMENTATION_MIGRATION.md +319 -0
- package/DOCUMENTATION_TEMPLATE.md +155 -0
- package/LICENSE +21 -0
- package/README.md +321 -498
- package/SECURITY.md +98 -0
- package/SETTINGS_SCREEN_GUIDE.md +185 -0
- package/TESTING.md +358 -0
- package/package.json +13 -2
- package/src/application/README.md +85 -271
- package/src/domains/about/README.md +85 -440
- package/src/domains/about/presentation/hooks/README.md +93 -348
- package/src/domains/appearance/README.md +95 -584
- package/src/domains/appearance/hooks/README.md +95 -303
- package/src/domains/appearance/infrastructure/services/README.md +83 -397
- package/src/domains/appearance/presentation/components/README.md +95 -489
- package/src/domains/cloud-sync/README.md +73 -439
- package/src/domains/cloud-sync/presentation/components/README.md +95 -493
- package/src/domains/dev/README.md +71 -457
- package/src/domains/disclaimer/README.md +77 -411
- package/src/domains/disclaimer/presentation/components/README.md +95 -392
- package/src/domains/faqs/README.md +86 -574
- package/src/domains/feedback/README.md +79 -553
- package/src/domains/feedback/presentation/hooks/README.md +93 -426
- package/src/domains/legal/README.md +88 -537
- package/src/domains/rating/README.md +73 -440
- package/src/domains/rating/presentation/components/README.md +95 -475
- package/src/domains/video-tutorials/README.md +77 -470
- package/src/domains/video-tutorials/presentation/components/README.md +95 -431
- package/src/infrastructure/README.md +78 -425
- package/src/infrastructure/repositories/README.md +88 -420
- package/src/infrastructure/services/README.md +74 -460
- package/src/presentation/components/README.md +97 -480
- package/src/presentation/components/SettingsErrorBoundary/README.md +48 -436
- package/src/presentation/components/SettingsFooter/README.md +48 -427
- package/src/presentation/components/SettingsItemCard/README.md +152 -391
- package/src/presentation/components/SettingsItemCard/STRATEGY.md +164 -0
- package/src/presentation/components/SettingsSection/README.md +47 -401
- package/src/presentation/hooks/README.md +95 -389
- package/src/presentation/hooks/mutations/README.md +99 -376
- package/src/presentation/hooks/queries/README.md +111 -353
- package/src/presentation/navigation/README.md +70 -502
- package/src/presentation/navigation/SettingsStackNavigator.tsx +2 -0
- package/src/presentation/navigation/components/README.md +70 -295
- package/src/presentation/navigation/components/wrappers/SettingsScreenWrapper.tsx +3 -0
- package/src/presentation/navigation/hooks/README.md +75 -367
- package/src/presentation/navigation/types.ts +1 -0
- package/src/presentation/navigation/utils/README.md +100 -380
- package/src/presentation/screens/README.md +53 -504
- package/src/presentation/screens/SettingsScreen.tsx +4 -2
- package/src/presentation/screens/components/SettingsContent/README.md +53 -382
- package/src/presentation/screens/components/SettingsHeader/README.md +48 -303
- package/src/presentation/screens/components/sections/CustomSettingsList/README.md +47 -359
- package/src/presentation/screens/components/sections/FeatureSettingsSection/README.md +81 -176
- package/src/presentation/screens/components/sections/IdentitySettingsSection/README.md +40 -297
- package/src/presentation/screens/components/sections/ProfileSectionLoader/README.md +47 -451
- package/src/presentation/screens/components/sections/SupportSettingsSection/README.md +45 -361
- package/src/presentation/screens/hooks/README.md +64 -354
- package/src/presentation/screens/types/README.md +79 -409
- package/src/presentation/screens/utils/README.md +65 -255
|
@@ -1,143 +1,109 @@
|
|
|
1
1
|
# Infrastructure Layer
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Provides concrete implementations of the interfaces defined in the application layer, handling data persistence and external service integrations.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
The infrastructure layer is responsible for all external interactions including storage, network calls, and third-party integrations. It implements the interfaces defined in the application layer and provides concrete implementations for data persistence and retrieval.
|
|
8
|
+
|
|
9
|
+
## File Paths
|
|
6
10
|
|
|
7
11
|
```
|
|
8
|
-
infrastructure/
|
|
12
|
+
src/infrastructure/
|
|
9
13
|
├── repositories/
|
|
10
14
|
│ └── SettingsRepository.ts # Settings storage implementation
|
|
11
15
|
└── services/
|
|
12
16
|
└── SettingsService.ts # Settings business logic service
|
|
13
17
|
```
|
|
14
18
|
|
|
15
|
-
##
|
|
19
|
+
## Strategy
|
|
16
20
|
|
|
17
|
-
|
|
21
|
+
1. **Interface Implementation**: Implement all interfaces defined in the application layer
|
|
22
|
+
2. **Dependency Injection**: Accept dependencies through constructors for testability
|
|
23
|
+
3. **Error Transformation**: Convert external errors into domain-specific error types
|
|
24
|
+
4. **Default Management**: Automatically create and apply default values for new users
|
|
25
|
+
5. **Storage Abstraction**: Use abstract storage interfaces to remain implementation-agnostic
|
|
18
26
|
|
|
19
|
-
|
|
27
|
+
## Restrictions
|
|
20
28
|
|
|
21
|
-
|
|
29
|
+
### DO NOT
|
|
22
30
|
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
31
|
+
- ❌ DO NOT include business logic that belongs in the application layer
|
|
32
|
+
- ❌ DO NOT expose storage-specific details to upper layers
|
|
33
|
+
- ❌ DO NOT directly access React Native components
|
|
34
|
+
- ❌ DO NOT make network calls without proper error handling
|
|
35
|
+
- ❌ DO NOT store raw exceptions; transform to domain errors
|
|
27
36
|
|
|
28
|
-
|
|
37
|
+
### NEVER
|
|
29
38
|
|
|
30
|
-
|
|
31
|
-
|
|
39
|
+
- ❌ NEVER bypass validation rules when saving data
|
|
40
|
+
- ❌ NEVER expose internal storage format to external layers
|
|
41
|
+
- ❌ EVER couple to specific storage implementations without abstraction
|
|
42
|
+
- ❌ EVER assume data always exists; handle null/undefined cases
|
|
32
43
|
|
|
33
|
-
|
|
44
|
+
### AVOID
|
|
34
45
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
46
|
+
- ❌ AVOID hardcoding storage keys or paths
|
|
47
|
+
- ❌ AVOID synchronous operations that could block the UI
|
|
48
|
+
- ❌ AVOID caching without proper invalidation strategies
|
|
49
|
+
- ❌ AVOID exposing implementation details in error messages
|
|
40
50
|
|
|
41
|
-
|
|
42
|
-
await repository.saveSettings({
|
|
43
|
-
userId: 'user123',
|
|
44
|
-
theme: 'dark',
|
|
45
|
-
language: 'en-US',
|
|
46
|
-
// ... other settings
|
|
47
|
-
});
|
|
51
|
+
## Rules
|
|
48
52
|
|
|
49
|
-
|
|
50
|
-
await repository.deleteSettings('user123');
|
|
51
|
-
```
|
|
53
|
+
### ALWAYS
|
|
52
54
|
|
|
53
|
-
|
|
55
|
+
- ✅ ALWAYS implement interfaces from the application layer
|
|
56
|
+
- ✅ ALWAYS return Results with proper error handling
|
|
57
|
+
- ✅ ALWAYS provide default values for missing data
|
|
58
|
+
- ✅ ALWAYS use dependency injection for external services
|
|
59
|
+
- ✅ ALWAYS handle storage errors gracefully
|
|
54
60
|
|
|
55
|
-
|
|
61
|
+
### MUST
|
|
56
62
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
5. Returns error on failure
|
|
63
|
+
- ✅ MUST validate data before persistence
|
|
64
|
+
- ✅ MUST transform storage errors to domain errors
|
|
65
|
+
- ✅ MUST use async/await for all I/O operations
|
|
66
|
+
- ✅ MUST ensure thread safety for concurrent operations
|
|
62
67
|
|
|
63
|
-
|
|
64
|
-
const storageKey = createUserKey(StorageKey.USER_PREFERENCES, userId);
|
|
65
|
-
const defaults = this.defaultSettings(userId);
|
|
66
|
-
const result = await storageRepository.getItem<UserSettings>(storageKey, defaults);
|
|
68
|
+
### SHOULD
|
|
67
69
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
- ✅ SHOULD cache frequently accessed data
|
|
71
|
+
- ✅ SHOULD provide retry logic for transient failures
|
|
72
|
+
- ✅ SHOULD log all storage operations for debugging
|
|
73
|
+
- ✅ SHOULD handle schema migrations gracefully
|
|
72
74
|
|
|
73
|
-
|
|
74
|
-
```
|
|
75
|
+
## AI Agent Guidelines
|
|
75
76
|
|
|
76
|
-
**
|
|
77
|
+
1. **When implementing repositories**: Always inject storage dependencies and implement the full interface
|
|
78
|
+
2. **When handling errors**: Transform technical errors into user-friendly domain errors with actionable messages
|
|
79
|
+
3. **When adding caching**: Implement proper cache invalidation and consider cache coherence
|
|
80
|
+
4. **When dealing with defaults**: Provide sensible defaults that match the application's expected behavior
|
|
81
|
+
5. **When creating migrations**: Ensure backward compatibility and provide migration paths for existing data
|
|
77
82
|
|
|
78
|
-
|
|
79
|
-
2. Saves settings to storage
|
|
80
|
-
3. Returns success or error
|
|
83
|
+
## Components Reference
|
|
81
84
|
|
|
82
|
-
|
|
83
|
-
const storageKey = createUserKey(StorageKey.USER_PREFERENCES, settings.userId);
|
|
84
|
-
const result = await storageRepository.setItem(storageKey, settings);
|
|
85
|
+
### SettingsRepository
|
|
85
86
|
|
|
86
|
-
|
|
87
|
-
return {
|
|
88
|
-
success: false,
|
|
89
|
-
error: {
|
|
90
|
-
code: 'SAVE_SETTINGS_FAILED',
|
|
91
|
-
message: 'Failed to save settings to storage',
|
|
92
|
-
},
|
|
93
|
-
};
|
|
94
|
-
}
|
|
87
|
+
Concrete implementation of `ISettingsRepository` using storage abstraction for data persistence.
|
|
95
88
|
|
|
96
|
-
|
|
97
|
-
```
|
|
89
|
+
**Location**: `/Users/umituz/Desktop/github/umituz/apps/artificial_intelligence/npm-packages/react-native-settings/src/infrastructure/repositories/SettingsRepository.ts`
|
|
98
90
|
|
|
99
|
-
**
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
```typescript
|
|
106
|
-
const storageKey = createUserKey(StorageKey.USER_PREFERENCES, userId);
|
|
107
|
-
const result = await storageRepository.removeItem(storageKey);
|
|
108
|
-
|
|
109
|
-
return result.success
|
|
110
|
-
? { success: true }
|
|
111
|
-
: {
|
|
112
|
-
success: false,
|
|
113
|
-
error: {
|
|
114
|
-
code: 'DELETE_SETTINGS_FAILED',
|
|
115
|
-
message: 'Failed to delete settings',
|
|
116
|
-
},
|
|
117
|
-
};
|
|
118
|
-
```
|
|
91
|
+
**Features**:
|
|
92
|
+
- Automatic defaults creation for new users
|
|
93
|
+
- Full TypeScript type safety
|
|
94
|
+
- Comprehensive error catching and reporting
|
|
95
|
+
- Storage repository integration
|
|
119
96
|
|
|
120
97
|
### SettingsService
|
|
121
98
|
|
|
122
99
|
Business logic service for high-level settings operations.
|
|
123
100
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
```typescript
|
|
127
|
-
import { SettingsService } from '@umituz/react-native-settings';
|
|
101
|
+
**Location**: `/Users/umituz/Desktop/github/umituz/apps/artificial_intelligence/npm-packages/react-native-settings/src/infrastructure/services/SettingsService.ts`
|
|
128
102
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
// Update specific settings
|
|
135
|
-
await service.updateTheme('user123', 'dark');
|
|
136
|
-
await service.updateLanguage('user123', 'tr-TR');
|
|
137
|
-
|
|
138
|
-
// Reset to defaults
|
|
139
|
-
await service.resetToDefaults('user123');
|
|
140
|
-
```
|
|
103
|
+
**Features**:
|
|
104
|
+
- User initialization
|
|
105
|
+
- Partial setting updates
|
|
106
|
+
- Reset to defaults functionality
|
|
141
107
|
|
|
142
108
|
## Storage Integration
|
|
143
109
|
|
|
@@ -145,35 +111,13 @@ await service.resetToDefaults('user123');
|
|
|
145
111
|
|
|
146
112
|
The repository uses the following storage keys:
|
|
147
113
|
|
|
148
|
-
|
|
149
|
-
enum StorageKey {
|
|
150
|
-
USER_PREFERENCES = 'user_preferences', // Main settings storage
|
|
151
|
-
}
|
|
152
|
-
```
|
|
114
|
+
- `USER_PREFERENCES` - Main settings storage key
|
|
153
115
|
|
|
154
116
|
### Storage Format
|
|
155
117
|
|
|
156
|
-
Settings are stored as JSON
|
|
157
|
-
|
|
158
|
-
```json
|
|
159
|
-
{
|
|
160
|
-
"userId": "user123",
|
|
161
|
-
"theme": "dark",
|
|
162
|
-
"language": "en-US",
|
|
163
|
-
"notificationsEnabled": true,
|
|
164
|
-
"emailNotifications": true,
|
|
165
|
-
"pushNotifications": false,
|
|
166
|
-
"soundEnabled": true,
|
|
167
|
-
"vibrationEnabled": false,
|
|
168
|
-
"privacyMode": false,
|
|
169
|
-
"disclaimerAccepted": true,
|
|
170
|
-
"updatedAt": "2025-01-08T10:30:00.000Z"
|
|
171
|
-
}
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
## Default Settings
|
|
118
|
+
Settings are stored as JSON with all user preferences including theme, language, notifications, and timestamps.
|
|
175
119
|
|
|
176
|
-
|
|
120
|
+
## Default Values
|
|
177
121
|
|
|
178
122
|
| Setting | Default Value | Description |
|
|
179
123
|
|---------|---------------|-------------|
|
|
@@ -191,19 +135,8 @@ When creating new settings, these defaults are used:
|
|
|
191
135
|
|
|
192
136
|
All repository methods return `SettingsResult<T>`:
|
|
193
137
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
{ success: true, data: settings }
|
|
197
|
-
|
|
198
|
-
// Error case
|
|
199
|
-
{
|
|
200
|
-
success: false,
|
|
201
|
-
error: {
|
|
202
|
-
code: 'GET_SETTINGS_FAILED',
|
|
203
|
-
message: 'Detailed error message'
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
```
|
|
138
|
+
**Success case**: `{ success: true, data: settings }`
|
|
139
|
+
**Error case**: `{ success: false, error: { code: string, message: string } }`
|
|
207
140
|
|
|
208
141
|
### Error Codes
|
|
209
142
|
|
|
@@ -213,296 +146,16 @@ All repository methods return `SettingsResult<T>`:
|
|
|
213
146
|
| `SAVE_SETTINGS_FAILED` | Failed to save settings |
|
|
214
147
|
| `DELETE_SETTINGS_FAILED` | Failed to delete settings |
|
|
215
148
|
|
|
216
|
-
## Examples
|
|
217
|
-
|
|
218
|
-
### Custom Repository Implementation
|
|
219
|
-
|
|
220
|
-
```typescript
|
|
221
|
-
import type { ISettingsRepository, UserSettings, SettingsResult } from '@umituz/react-native-settings';
|
|
222
|
-
|
|
223
|
-
class AsyncStorageRepository implements ISettingsRepository {
|
|
224
|
-
async getSettings(userId: string): Promise<SettingsResult<UserSettings>> {
|
|
225
|
-
try {
|
|
226
|
-
const key = `settings_${userId}`;
|
|
227
|
-
const json = await AsyncStorage.getItem(key);
|
|
228
|
-
|
|
229
|
-
if (!json) {
|
|
230
|
-
return {
|
|
231
|
-
success: false,
|
|
232
|
-
error: { code: 'NOT_FOUND', message: 'Settings not found' },
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
const data = JSON.parse(json);
|
|
237
|
-
return { success: true, data };
|
|
238
|
-
} catch (error) {
|
|
239
|
-
return {
|
|
240
|
-
success: false,
|
|
241
|
-
error: {
|
|
242
|
-
code: 'GET_SETTINGS_FAILED',
|
|
243
|
-
message: error.message,
|
|
244
|
-
},
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
async saveSettings(settings: UserSettings): Promise<SettingsResult<void>> {
|
|
250
|
-
try {
|
|
251
|
-
const key = `settings_${settings.userId}`;
|
|
252
|
-
const json = JSON.stringify(settings);
|
|
253
|
-
await AsyncStorage.setItem(key, json);
|
|
254
|
-
return { success: true };
|
|
255
|
-
} catch (error) {
|
|
256
|
-
return {
|
|
257
|
-
success: false,
|
|
258
|
-
error: {
|
|
259
|
-
code: 'SAVE_SETTINGS_FAILED',
|
|
260
|
-
message: error.message,
|
|
261
|
-
},
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
async deleteSettings(userId: string): Promise<SettingsResult<void>> {
|
|
267
|
-
try {
|
|
268
|
-
const key = `settings_${userId}`;
|
|
269
|
-
await AsyncStorage.removeItem(key);
|
|
270
|
-
return { success: true };
|
|
271
|
-
} catch (error) {
|
|
272
|
-
return {
|
|
273
|
-
success: false,
|
|
274
|
-
error: {
|
|
275
|
-
code: 'DELETE_SETTINGS_FAILED',
|
|
276
|
-
message: error.message,
|
|
277
|
-
},
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
### Using Custom Repository
|
|
285
|
-
|
|
286
|
-
```typescript
|
|
287
|
-
import { useSettings } from '@umituz/react-native-settings';
|
|
288
|
-
|
|
289
|
-
// Provide custom repository to your app
|
|
290
|
-
const customRepository = new AsyncStorageRepository();
|
|
291
|
-
|
|
292
|
-
function App() {
|
|
293
|
-
return (
|
|
294
|
-
<SettingsProvider repository={customRepository}>
|
|
295
|
-
<MyApp />
|
|
296
|
-
</SettingsProvider>
|
|
297
|
-
);
|
|
298
|
-
}
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
### Migration Handler
|
|
302
|
-
|
|
303
|
-
```typescript
|
|
304
|
-
class MigrationSettingsRepository implements ISettingsRepository {
|
|
305
|
-
constructor(private baseRepository: ISettingsRepository) {}
|
|
306
|
-
|
|
307
|
-
async getSettings(userId: string): Promise<SettingsResult<UserSettings>> {
|
|
308
|
-
const result = await this.baseRepository.getSettings(userId);
|
|
309
|
-
|
|
310
|
-
if (result.success && result.data) {
|
|
311
|
-
// Migration: Add new fields
|
|
312
|
-
const migrated = this.migrateSettings(result.data);
|
|
313
|
-
if (migrated !== result.data) {
|
|
314
|
-
await this.saveSettings(migrated);
|
|
315
|
-
return { success: true, data: migrated };
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
return result;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
private migrateSettings(settings: UserSettings): UserSettings {
|
|
323
|
-
// Add new field with default
|
|
324
|
-
if (!('newField' in settings)) {
|
|
325
|
-
return { ...settings, newField: defaultValue };
|
|
326
|
-
}
|
|
327
|
-
return settings;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// Delegate other methods
|
|
331
|
-
async saveSettings(settings: UserSettings) {
|
|
332
|
-
return this.baseRepository.saveSettings(settings);
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
async deleteSettings(userId: string) {
|
|
336
|
-
return this.baseRepository.deleteSettings(userId);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
### Encrypted Storage
|
|
342
|
-
|
|
343
|
-
```typescript
|
|
344
|
-
import * as SecureStore from 'expo-secure-store';
|
|
345
|
-
|
|
346
|
-
class SecureSettingsRepository implements ISettingsRepository {
|
|
347
|
-
async getSettings(userId: string): Promise<SettingsResult<UserSettings>> {
|
|
348
|
-
try {
|
|
349
|
-
const key = `secure_settings_${userId}`;
|
|
350
|
-
const json = await SecureStore.getItemAsync(key);
|
|
351
|
-
|
|
352
|
-
if (!json) {
|
|
353
|
-
return { success: false, error: { code: 'NOT_FOUND', message: 'Not found' } };
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
const data = JSON.parse(json);
|
|
357
|
-
return { success: true, data };
|
|
358
|
-
} catch (error) {
|
|
359
|
-
return {
|
|
360
|
-
success: false,
|
|
361
|
-
error: { code: 'GET_SETTINGS_FAILED', message: error.message },
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
async saveSettings(settings: UserSettings): Promise<SettingsResult<void>> {
|
|
367
|
-
try {
|
|
368
|
-
const key = `secure_settings_${settings.userId}`;
|
|
369
|
-
const json = JSON.stringify(settings);
|
|
370
|
-
await SecureStore.setItemAsync(key, json);
|
|
371
|
-
return { success: true };
|
|
372
|
-
} catch (error) {
|
|
373
|
-
return {
|
|
374
|
-
success: false,
|
|
375
|
-
error: { code: 'SAVE_SETTINGS_FAILED', message: error.message },
|
|
376
|
-
};
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
async deleteSettings(userId: string): Promise<SettingsResult<void>> {
|
|
381
|
-
try {
|
|
382
|
-
const key = `secure_settings_${userId}`;
|
|
383
|
-
await SecureStore.deleteItemAsync(key);
|
|
384
|
-
return { success: true };
|
|
385
|
-
} catch (error) {
|
|
386
|
-
return {
|
|
387
|
-
success: false,
|
|
388
|
-
error: { code: 'DELETE_SETTINGS_FAILED', message: error.message },
|
|
389
|
-
};
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
```
|
|
394
|
-
|
|
395
149
|
## Testing
|
|
396
150
|
|
|
397
|
-
### Mock Repository
|
|
398
|
-
|
|
399
|
-
```typescript
|
|
400
|
-
class MockSettingsRepository implements ISettingsRepository {
|
|
401
|
-
private storage: Map<string, UserSettings> = new Map();
|
|
402
|
-
|
|
403
|
-
async getSettings(userId: string): Promise<SettingsResult<UserSettings>> {
|
|
404
|
-
const settings = this.storage.get(userId);
|
|
405
|
-
return settings
|
|
406
|
-
? { success: true, data: settings }
|
|
407
|
-
: { success: false, error: { code: 'NOT_FOUND', message: 'Not found' } };
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
async saveSettings(settings: UserSettings): Promise<SettingsResult<void>> {
|
|
411
|
-
this.storage.set(settings.userId, settings);
|
|
412
|
-
return { success: true };
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
async deleteSettings(userId: string): Promise<SettingsResult<void>> {
|
|
416
|
-
this.storage.delete(userId);
|
|
417
|
-
return { success: true };
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
// Test helper methods
|
|
421
|
-
clear() {
|
|
422
|
-
this.storage.clear();
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
hasSettings(userId: string): boolean {
|
|
426
|
-
return this.storage.has(userId);
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
```
|
|
430
|
-
|
|
431
|
-
### Test Example
|
|
432
|
-
|
|
433
|
-
```typescript
|
|
434
|
-
import { SettingsRepository } from '@umituz/react-native-settings';
|
|
435
|
-
|
|
436
|
-
describe('SettingsRepository', () => {
|
|
437
|
-
let repository: SettingsRepository;
|
|
438
|
-
|
|
439
|
-
beforeEach(() => {
|
|
440
|
-
repository = new SettingsRepository();
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
it('should save and retrieve settings', async () => {
|
|
444
|
-
const settings: UserSettings = {
|
|
445
|
-
userId: 'test123',
|
|
446
|
-
theme: 'dark',
|
|
447
|
-
language: 'en-US',
|
|
448
|
-
notificationsEnabled: true,
|
|
449
|
-
emailNotifications: true,
|
|
450
|
-
pushNotifications: false,
|
|
451
|
-
soundEnabled: true,
|
|
452
|
-
vibrationEnabled: false,
|
|
453
|
-
privacyMode: false,
|
|
454
|
-
disclaimerAccepted: true,
|
|
455
|
-
updatedAt: new Date(),
|
|
456
|
-
};
|
|
457
|
-
|
|
458
|
-
const saveResult = await repository.saveSettings(settings);
|
|
459
|
-
expect(saveResult.success).toBe(true);
|
|
460
|
-
|
|
461
|
-
const getResult = await repository.getSettings('test123');
|
|
462
|
-
expect(getResult.success).toBe(true);
|
|
463
|
-
expect(getResult.data?.theme).toBe('dark');
|
|
464
|
-
});
|
|
465
|
-
|
|
466
|
-
it('should return defaults for new user', async () => {
|
|
467
|
-
const result = await repository.getSettings('newuser');
|
|
468
|
-
|
|
469
|
-
expect(result.success).toBe(true);
|
|
470
|
-
expect(result.data?.theme).toBe('auto');
|
|
471
|
-
expect(result.data?.language).toBe('en-US');
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
it('should delete settings', async () => {
|
|
475
|
-
await repository.saveSettings({
|
|
476
|
-
userId: 'deleteMe',
|
|
477
|
-
theme: 'light',
|
|
478
|
-
// ... other required fields
|
|
479
|
-
} as any);
|
|
480
|
-
|
|
481
|
-
const deleteResult = await repository.deleteSettings('deleteMe');
|
|
482
|
-
expect(deleteResult.success).toBe(true);
|
|
483
|
-
|
|
484
|
-
const getResult = await repository.getSettings('deleteMe');
|
|
485
|
-
expect(getResult.data?.theme).toBe('auto'); // Returns defaults
|
|
486
|
-
});
|
|
487
|
-
});
|
|
488
|
-
```
|
|
489
|
-
|
|
490
|
-
## Best Practices
|
|
491
|
-
|
|
492
|
-
1. **Use Repository Interface**: Program to `ISettingsRepository`, not concrete implementation
|
|
493
|
-
2. **Handle Errors**: Always check `success` flag
|
|
494
|
-
3. **Type Safety**: Use TypeScript types for all operations
|
|
495
|
-
4. **Validation**: Validate data before saving
|
|
496
|
-
5. **Default Values**: Always provide sensible defaults
|
|
497
|
-
6. **Error Messages**: Include detailed, helpful error messages
|
|
498
|
-
7. **Testing**: Use mock repositories in tests
|
|
499
|
-
8. **Migration**: Handle schema migrations gracefully
|
|
151
|
+
### Mock Repository Pattern
|
|
500
152
|
|
|
501
|
-
|
|
153
|
+
Create mock implementations for testing that implement `ISettingsRepository`:
|
|
502
154
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
155
|
+
1. Use in-memory storage (Map or object)
|
|
156
|
+
2. Implement all interface methods
|
|
157
|
+
3. Provide test helper methods (clear, hasSettings, etc.)
|
|
158
|
+
4. Simulate errors for error handling tests
|
|
506
159
|
|
|
507
160
|
## License
|
|
508
161
|
|