http-request-manager 18.7.21 → 18.7.24
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/fesm2022/http-request-manager.mjs +7633 -0
- package/fesm2022/http-request-manager.mjs.map +1 -0
- package/http-request-manager-18.7.24.tgz +0 -0
- package/package.json +16 -5
- package/types/http-request-manager.d.ts +2277 -0
- package/ARCHITECTURE.md +0 -483
- package/DATABASE_README.md +0 -1176
- package/HTTP_MANAGER_README.md +0 -579
- package/HTTP_SINGNALS_MANAGER_README.md +0 -654
- package/HTTP_STATE_MANAGER_README.md +0 -948
- package/INTERCEPTOR_README.md +0 -549
- package/LOCAL_STORAGE_README.md +0 -1056
- package/STORE_STATE_MANAGER_README.md +0 -1322
- package/UTILS_README.md +0 -1186
- package/WS_MANAGER_README.md +0 -613
- package/ng-package.json +0 -8
- package/src/lib/http-request-manager.module.ts +0 -132
- package/src/lib/http-request-services-demo/database-data-demo/database-data-demo.component.html +0 -65
- package/src/lib/http-request-services-demo/database-data-demo/database-data-demo.component.scss +0 -0
- package/src/lib/http-request-services-demo/database-data-demo/database-data-demo.component.ts +0 -224
- package/src/lib/http-request-services-demo/http-request-services-demo.component.html +0 -114
- package/src/lib/http-request-services-demo/http-request-services-demo.component.scss +0 -6
- package/src/lib/http-request-services-demo/http-request-services-demo.component.ts +0 -52
- package/src/lib/http-request-services-demo/local-storage-demo/local-storage-demo.component.html +0 -195
- package/src/lib/http-request-services-demo/local-storage-demo/local-storage-demo.component.scss +0 -17
- package/src/lib/http-request-services-demo/local-storage-demo/local-storage-demo.component.ts +0 -206
- package/src/lib/http-request-services-demo/local-storage-signals-demo/local-storage-signals-demo.component.html +0 -200
- package/src/lib/http-request-services-demo/local-storage-signals-demo/local-storage-signals-demo.component.scss +0 -17
- package/src/lib/http-request-services-demo/local-storage-signals-demo/local-storage-signals-demo.component.ts +0 -212
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/download-file/download-file.component.html +0 -53
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/download-file/download-file.component.scss +0 -60
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/download-file/download-file.component.ts +0 -72
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/file-download.module.ts +0 -28
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/file-downloader.component.html +0 -10
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/file-downloader.component.scss +0 -29
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/file-downloader.component.ts +0 -100
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/models/download-labels-model.ts +0 -22
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/spinner/spinner.component.html +0 -8
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/spinner/spinner.component.scss +0 -19
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/spinner/spinner.component.ts +0 -26
- package/src/lib/http-request-services-demo/request-manager-demo/models/app-session.model.ts +0 -30
- package/src/lib/http-request-services-demo/request-manager-demo/models/app.model.ts +0 -19
- package/src/lib/http-request-services-demo/request-manager-demo/models/get-sample.model.ts +0 -25
- package/src/lib/http-request-services-demo/request-manager-demo/models/sample-ai-prompt.ts +0 -19
- package/src/lib/http-request-services-demo/request-manager-demo/models/sample-client-details.ts +0 -24
- package/src/lib/http-request-services-demo/request-manager-demo/models/sample-client-info.ts +0 -30
- package/src/lib/http-request-services-demo/request-manager-demo/models/sample-client.model.ts +0 -49
- package/src/lib/http-request-services-demo/request-manager-demo/models/sample-mapper-client-info.ts +0 -33
- package/src/lib/http-request-services-demo/request-manager-demo/request-manager-demo.component.html +0 -392
- package/src/lib/http-request-services-demo/request-manager-demo/request-manager-demo.component.scss +0 -24
- package/src/lib/http-request-services-demo/request-manager-demo/request-manager-demo.component.ts +0 -461
- package/src/lib/http-request-services-demo/request-manager-state-demo/request-manager-state-demo.component.html +0 -393
- package/src/lib/http-request-services-demo/request-manager-state-demo/request-manager-state-demo.component.scss +0 -24
- package/src/lib/http-request-services-demo/request-manager-state-demo/request-manager-state-demo.component.ts +0 -421
- package/src/lib/http-request-services-demo/request-manager-state-demo/services/state-manager-demo.service.ts +0 -87
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/services/state-data-request.service.ts +0 -120
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-ai-messaging/ws-ai-messaging.component.css +0 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-ai-messaging/ws-ai-messaging.component.html +0 -3
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-ai-messaging/ws-ai-messaging.component.ts +0 -16
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-chats/ws-chats.component.css +0 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-chats/ws-chats.component.html +0 -3
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-chats/ws-chats.component.ts +0 -16
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-data-control/ws-data-control.component.css +0 -31
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-data-control/ws-data-control.component.html +0 -72
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-data-control/ws-data-control.component.scss +0 -41
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-data-control/ws-data-control.component.spec.ts +0 -205
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-data-control/ws-data-control.component.ts +0 -77
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-messaging/ws-messaging.component.css +0 -11
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-messaging/ws-messaging.component.html +0 -96
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-messaging/ws-messaging.component.spec.ts +0 -31
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-messaging/ws-messaging.component.ts +0 -229
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-notifications/ws-notifications.component.css +0 -30
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-notifications/ws-notifications.component.html +0 -172
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-notifications/ws-notifications.component.spec.ts +0 -31
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-notifications/ws-notifications.component.ts +0 -239
- package/src/lib/http-request-services-demo/request-manager-ws-demo/models/oidc-client.model.ts +0 -31
- package/src/lib/http-request-services-demo/request-manager-ws-demo/models/user-data.model.ts +0 -32
- package/src/lib/http-request-services-demo/request-manager-ws-demo/request-manager-ws-demo.component.css +0 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/request-manager-ws-demo.component.html +0 -84
- package/src/lib/http-request-services-demo/request-manager-ws-demo/request-manager-ws-demo.component.ts +0 -41
- package/src/lib/http-request-services-demo/request-manager-ws-demo/services/index.ts +0 -3
- package/src/lib/http-request-services-demo/request-manager-ws-demo/services/message-service-demo.service.ts +0 -83
- package/src/lib/http-request-services-demo/request-manager-ws-demo/services/notification-service-demo.service.ts +0 -147
- package/src/lib/http-request-services-demo/request-manager-ws-demo/services/state-service-demo.service.ts +0 -158
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/download-file/download-file.component.html +0 -53
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/download-file/download-file.component.scss +0 -60
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/download-file/download-file.component.ts +0 -72
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/file-download.module.ts +0 -28
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/file-downloader.component.html +0 -10
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/file-downloader.component.scss +0 -29
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/file-downloader.component.ts +0 -100
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/models/download-labels-model.ts +0 -22
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/spinner/spinner.component.html +0 -8
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/spinner/spinner.component.scss +0 -19
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/spinner/spinner.component.ts +0 -26
- package/src/lib/http-request-services-demo/request-signals-manager-demo/models/app-session.model.ts +0 -30
- package/src/lib/http-request-services-demo/request-signals-manager-demo/models/app.model.ts +0 -19
- package/src/lib/http-request-services-demo/request-signals-manager-demo/models/get-sample.model.ts +0 -25
- package/src/lib/http-request-services-demo/request-signals-manager-demo/models/sample-ai-prompt.ts +0 -19
- package/src/lib/http-request-services-demo/request-signals-manager-demo/models/sample-client-details.ts +0 -24
- package/src/lib/http-request-services-demo/request-signals-manager-demo/models/sample-client-info.ts +0 -30
- package/src/lib/http-request-services-demo/request-signals-manager-demo/models/sample-client.model.ts +0 -49
- package/src/lib/http-request-services-demo/request-signals-manager-demo/models/sample-mapper-client-info.ts +0 -33
- package/src/lib/http-request-services-demo/request-signals-manager-demo/request-signals-manager-demo.component.html +0 -380
- package/src/lib/http-request-services-demo/request-signals-manager-demo/request-signals-manager-demo.component.scss +0 -24
- package/src/lib/http-request-services-demo/request-signals-manager-demo/request-signals-manager-demo.component.ts +0 -410
- package/src/lib/http-request-services-demo/store-state-manager-demo/models/settings.model.ts +0 -28
- package/src/lib/http-request-services-demo/store-state-manager-demo/services/settings-state.service.ts +0 -48
- package/src/lib/http-request-services-demo/store-state-manager-demo/store-state-manager-demo.component.css +0 -0
- package/src/lib/http-request-services-demo/store-state-manager-demo/store-state-manager-demo.component.html +0 -23
- package/src/lib/http-request-services-demo/store-state-manager-demo/store-state-manager-demo.component.ts +0 -36
- package/src/lib/index.ts +0 -3
- package/src/lib/interceptors/credentials.interceptor.ts +0 -16
- package/src/lib/interceptors/index.ts +0 -6
- package/src/lib/interceptors/models/error-settings.model.ts +0 -22
- package/src/lib/interceptors/models/index.ts +0 -2
- package/src/lib/interceptors/proxy-debugger.interceptor.ts +0 -46
- package/src/lib/interceptors/request-error.interceptor.ts +0 -65
- package/src/lib/interceptors/request-header.interceptor.ts +0 -53
- package/src/lib/models/config-http-options.model.ts +0 -42
- package/src/lib/models/config-local-storage-options.model.ts +0 -27
- package/src/lib/models/config-options.model.ts +0 -27
- package/src/lib/models/config-token.model.ts +0 -9
- package/src/lib/models/data-type.enum.ts +0 -5
- package/src/lib/models/database-storage.model.ts +0 -24
- package/src/lib/models/index.ts +0 -12
- package/src/lib/models/retry-options.model.ts +0 -22
- package/src/lib/services/database-manager-service/database.manager.service.ts +0 -262
- package/src/lib/services/database-manager-service/db.storage.service.ts +0 -207
- package/src/lib/services/database-manager-service/index.ts +0 -4
- package/src/lib/services/database-manager-service/models/index.ts +0 -2
- package/src/lib/services/database-manager-service/models/table-schema.ts +0 -33
- package/src/lib/services/index.ts +0 -12
- package/src/lib/services/local-storage-manager-service/index.ts +0 -4
- package/src/lib/services/local-storage-manager-service/local-storage-manager.service.spec.ts +0 -71
- package/src/lib/services/local-storage-manager-service/local-storage-manager.service.ts +0 -426
- package/src/lib/services/local-storage-manager-service/local-storage-signals-manager.service.spec.ts +0 -67
- package/src/lib/services/local-storage-manager-service/local-storage-signals-manager.service.ts +0 -345
- package/src/lib/services/local-storage-manager-service/models/global-store-options.model.ts +0 -30
- package/src/lib/services/local-storage-manager-service/models/index.ts +0 -6
- package/src/lib/services/local-storage-manager-service/models/setting-options.model.ts +0 -35
- package/src/lib/services/local-storage-manager-service/models/storage-data.model.ts +0 -24
- package/src/lib/services/local-storage-manager-service/models/storage-option.model.ts +0 -32
- package/src/lib/services/local-storage-manager-service/models/storage-type.enum.ts +0 -5
- package/src/lib/services/request-manager-services/README.md +0 -268
- package/src/lib/services/request-manager-services/http-manager-signals.service.ts +0 -246
- package/src/lib/services/request-manager-services/http-manager.service.spec.ts +0 -232
- package/src/lib/services/request-manager-services/http-manager.service.ts +0 -274
- package/src/lib/services/request-manager-services/index.ts +0 -8
- package/src/lib/services/request-manager-services/request-signals.service.ts +0 -214
- package/src/lib/services/request-manager-services/request.service.ts +0 -309
- package/src/lib/services/request-manager-services/rxjs-operators/countdown.ts +0 -17
- package/src/lib/services/request-manager-services/rxjs-operators/delay-retry.ts +0 -16
- package/src/lib/services/request-manager-services/rxjs-operators/index.ts +0 -4
- package/src/lib/services/request-manager-services/rxjs-operators/request-polling.ts +0 -35
- package/src/lib/services/request-manager-services/rxjs-operators/request-streaming.ts +0 -436
- package/src/lib/services/request-manager-state-service/http-manager-state.store.ts +0 -1321
- package/src/lib/services/request-manager-state-service/index.ts +0 -3
- package/src/lib/services/request-manager-state-service/models/api-request.model.ts +0 -61
- package/src/lib/services/request-manager-state-service/models/index.ts +0 -6
- package/src/lib/services/request-manager-state-service/models/request-options.model.ts +0 -22
- package/src/lib/services/request-manager-state-service/models/stream-type.enum.ts +0 -13
- package/src/lib/services/request-manager-state-service/models/ws-options.model.ts +0 -39
- package/src/lib/services/store-state-manager-service/index.ts +0 -3
- package/src/lib/services/store-state-manager-service/models/index.ts +0 -2
- package/src/lib/services/store-state-manager-service/models/state-storage-options.model.ts +0 -24
- package/src/lib/services/store-state-manager-service/store-state-manager.service.ts +0 -88
- package/src/lib/services/utils/app.service.spec.ts +0 -25
- package/src/lib/services/utils/app.service.ts +0 -21
- package/src/lib/services/utils/encryption/README.md +0 -79
- package/src/lib/services/utils/encryption/asymmetrical-encryption.service.ts +0 -282
- package/src/lib/services/utils/encryption/encryption-test.service.ts +0 -39
- package/src/lib/services/utils/encryption/index.ts +0 -5
- package/src/lib/services/utils/encryption/random.ts +0 -81
- package/src/lib/services/utils/encryption/symmetrical-encryption.service.ts +0 -93
- package/src/lib/services/utils/headers.service.spec.ts +0 -80
- package/src/lib/services/utils/headers.service.ts +0 -18
- package/src/lib/services/utils/index.ts +0 -7
- package/src/lib/services/utils/object-merger.service.spec.ts +0 -18
- package/src/lib/services/utils/object-merger.service.ts +0 -78
- package/src/lib/services/utils/path-query.service.spec.ts +0 -117
- package/src/lib/services/utils/path-query.service.ts +0 -69
- package/src/lib/services/utils/random-color.utils.ts +0 -83
- package/src/lib/services/utils/utils.service.spec.ts +0 -165
- package/src/lib/services/utils/utils.service.ts +0 -192
- package/src/lib/services/ws-manager-service/index.ts +0 -4
- package/src/lib/services/ws-manager-service/models/channel-info.model.ts +0 -24
- package/src/lib/services/ws-manager-service/models/channel-message-data.model.ts +0 -24
- package/src/lib/services/ws-manager-service/models/channel-message.model.ts +0 -24
- package/src/lib/services/ws-manager-service/models/communication-type.enum.ts +0 -5
- package/src/lib/services/ws-manager-service/models/index.ts +0 -5
- package/src/lib/services/ws-manager-service/models/ws-user.model.ts +0 -38
- package/src/lib/services/ws-manager-service/services/index.ts +0 -3
- package/src/lib/services/ws-manager-service/services/websocket.service.ts +0 -392
- package/src/public-api.ts +0 -14
- package/tsconfig.lib.json +0 -32
- package/tsconfig.lib.prod.json +0 -10
- package/tsconfig.spec.json +0 -14
|
@@ -1,1322 +0,0 @@
|
|
|
1
|
-
# Store State Manager Service
|
|
2
|
-
|
|
3
|
-
The `StoreStateManagerService` extends `ComponentStore` to provide persistent state management synchronized with local or session storage. It bridges the gap between in-memory state and persistent storage.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
This service provides:
|
|
8
|
-
|
|
9
|
-
- **ComponentStore Extension** - Full ComponentStore functionality with persistence
|
|
10
|
-
- **Storage Synchronization** - Automatic state save/restore from localStorage/sessionStorage
|
|
11
|
-
- **Encryption Support** - Optional encryption for sensitive state data
|
|
12
|
-
- **Model Adaptation** - Support for model adapters to ensure state structure
|
|
13
|
-
- **Reactivity** - Maintains all ComponentStore benefits (selectors, updaters, effects)
|
|
14
|
-
- **Migration Support** - Built-in support for state migrations
|
|
15
|
-
|
|
16
|
-
## Installation
|
|
17
|
-
|
|
18
|
-
```typescript
|
|
19
|
-
import { HttpRequestManagerModule } from 'http-request-manager';
|
|
20
|
-
|
|
21
|
-
@NgModule({
|
|
22
|
-
imports: [HttpRequestManagerModule.forRoot({
|
|
23
|
-
LocalStorageOptions: {
|
|
24
|
-
storageName: 'app-state',
|
|
25
|
-
options: { encrypted: true }
|
|
26
|
-
}
|
|
27
|
-
})]
|
|
28
|
-
})
|
|
29
|
-
export class AppModule { }
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Basic Usage
|
|
33
|
-
|
|
34
|
-
### Creating a Persistent State Service
|
|
35
|
-
|
|
36
|
-
```typescript
|
|
37
|
-
import { Injectable } from '@angular/core';
|
|
38
|
-
import { StoreStateManagerService, StateStorageOptions, SettingOptions } from 'http-request-manager';
|
|
39
|
-
import { StorageType } from 'http-request-manager';
|
|
40
|
-
|
|
41
|
-
interface UserSettings {
|
|
42
|
-
theme: 'light' | 'dark';
|
|
43
|
-
language: string;
|
|
44
|
-
notifications: boolean;
|
|
45
|
-
emailDigest: boolean;
|
|
46
|
-
autoSave: boolean;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
@Injectable({
|
|
50
|
-
providedIn: 'root'
|
|
51
|
-
})
|
|
52
|
-
export class UserSettingsStateService extends StoreStateManagerService<UserSettings> {
|
|
53
|
-
|
|
54
|
-
constructor() {
|
|
55
|
-
super(
|
|
56
|
-
StateStorageOptions.adapt({
|
|
57
|
-
store: 'user-settings', // Unique store name
|
|
58
|
-
options: SettingOptions.adapt({
|
|
59
|
-
storage: StorageType.GLOBAL, // localStorage
|
|
60
|
-
encrypted: true, // Encrypt sensitive data
|
|
61
|
-
expiresIn: '30d' // 30 days expiration
|
|
62
|
-
}),
|
|
63
|
-
model: UserSettings.adapt // Model adapter for validation
|
|
64
|
-
})
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Public API methods
|
|
69
|
-
updateTheme(theme: 'light' | 'dark') {
|
|
70
|
-
this.updateData({ theme });
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
updateLanguage(language: string) {
|
|
74
|
-
this.updateData({ language });
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
toggleNotifications() {
|
|
78
|
-
this.updateData(state => ({
|
|
79
|
-
...state,
|
|
80
|
-
notifications: !state.notifications
|
|
81
|
-
}));
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
toggleEmailDigest() {
|
|
85
|
-
this.updateData(state => ({
|
|
86
|
-
...state,
|
|
87
|
-
emailDigest: !state.emailDigest
|
|
88
|
-
}));
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
updateAutoSave(enabled: boolean) {
|
|
92
|
-
this.updateData({ autoSave: enabled });
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
resetToDefaults() {
|
|
96
|
-
this.updateData({
|
|
97
|
-
theme: 'light',
|
|
98
|
-
language: 'en',
|
|
99
|
-
notifications: true,
|
|
100
|
-
emailDigest: false,
|
|
101
|
-
autoSave: true
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### Using in Components
|
|
108
|
-
|
|
109
|
-
```typescript
|
|
110
|
-
import { Component, inject } from '@angular/core';
|
|
111
|
-
import { UserSettingsStateService } from './user-settings-state.service';
|
|
112
|
-
|
|
113
|
-
@Component({
|
|
114
|
-
selector: 'app-settings',
|
|
115
|
-
template: `
|
|
116
|
-
<div class="settings-container">
|
|
117
|
-
<h2>User Settings</h2>
|
|
118
|
-
|
|
119
|
-
<div class="setting-section">
|
|
120
|
-
<h3>Appearance</h3>
|
|
121
|
-
<div class="setting-item">
|
|
122
|
-
<label>Theme:</label>
|
|
123
|
-
<select [value]="settings?.theme" (change)="updateTheme($any($event.target).value)">
|
|
124
|
-
<option value="light">Light</option>
|
|
125
|
-
<option value="dark">Dark</option>
|
|
126
|
-
</select>
|
|
127
|
-
</div>
|
|
128
|
-
</div>
|
|
129
|
-
|
|
130
|
-
<div class="setting-section">
|
|
131
|
-
<h3>Localization</h3>
|
|
132
|
-
<div class="setting-item">
|
|
133
|
-
<label>Language:</label>
|
|
134
|
-
<select [value]="settings?.language" (change)="updateLanguage($any($event.target).value)">
|
|
135
|
-
<option value="en">English</option>
|
|
136
|
-
<option value="es">Español</option>
|
|
137
|
-
<option value="fr">Français</option>
|
|
138
|
-
</select>
|
|
139
|
-
</div>
|
|
140
|
-
</div>
|
|
141
|
-
|
|
142
|
-
<div class="setting-section">
|
|
143
|
-
<h3>Notifications</h3>
|
|
144
|
-
<div class="setting-item">
|
|
145
|
-
<label>
|
|
146
|
-
<input type="checkbox" [checked]="settings?.notifications"
|
|
147
|
-
(change)="toggleNotifications()">
|
|
148
|
-
Enable Push Notifications
|
|
149
|
-
</label>
|
|
150
|
-
</div>
|
|
151
|
-
<div class="setting-item">
|
|
152
|
-
<label>
|
|
153
|
-
<input type="checkbox" [checked]="settings?.emailDigest"
|
|
154
|
-
(change)="toggleEmailDigest()">
|
|
155
|
-
Weekly Email Digest
|
|
156
|
-
</label>
|
|
157
|
-
</div>
|
|
158
|
-
</div>
|
|
159
|
-
|
|
160
|
-
<div class="setting-section">
|
|
161
|
-
<h3>Preferences</h3>
|
|
162
|
-
<div class="setting-item">
|
|
163
|
-
<label>
|
|
164
|
-
<input type="checkbox" [checked]="settings?.autoSave"
|
|
165
|
-
(change)="updateAutoSave($any($event.target).checked)">
|
|
166
|
-
Auto-save Changes
|
|
167
|
-
</label>
|
|
168
|
-
</div>
|
|
169
|
-
</div>
|
|
170
|
-
|
|
171
|
-
<div class="actions">
|
|
172
|
-
<button (click)="resetToDefaults()">Reset to Defaults</button>
|
|
173
|
-
<button (click)="exportSettings()">Export Settings</button>
|
|
174
|
-
<button (click)="clearSettings()">Clear All</button>
|
|
175
|
-
</div>
|
|
176
|
-
|
|
177
|
-
<div class="storage-info" *ngIf="storageInfo">
|
|
178
|
-
<p>Last updated: {{ storageInfo.lastUpdated | date:'medium' }}</p>
|
|
179
|
-
<p>Storage type: {{ storageInfo.storageType }}</p>
|
|
180
|
-
<p>Encrypted: {{ storageInfo.encrypted ? 'Yes' : 'No' }}</p>
|
|
181
|
-
</div>
|
|
182
|
-
</div>
|
|
183
|
-
`
|
|
184
|
-
})
|
|
185
|
-
export class SettingsComponent {
|
|
186
|
-
private settingsStore = inject(UserSettingsStateService);
|
|
187
|
-
|
|
188
|
-
settings = this.settingsStore.data$;
|
|
189
|
-
storageInfo = this.settingsStore.storageInfo$;
|
|
190
|
-
|
|
191
|
-
updateTheme(theme: 'light' | 'dark') {
|
|
192
|
-
this.settingsStore.updateTheme(theme);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
updateLanguage(language: string) {
|
|
196
|
-
this.settingsStore.updateLanguage(language);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
toggleNotifications() {
|
|
200
|
-
this.settingsStore.toggleNotifications();
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
toggleEmailDigest() {
|
|
204
|
-
this.settingsStore.toggleEmailDigest();
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
updateAutoSave(enabled: boolean) {
|
|
208
|
-
this.settingsStore.updateAutoSave(enabled);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
resetToDefaults() {
|
|
212
|
-
this.settingsStore.resetToDefaults();
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
exportSettings() {
|
|
216
|
-
const settings = this.settingsStore.exportData();
|
|
217
|
-
console.log('Settings exported:', settings);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
clearSettings() {
|
|
221
|
-
this.settingsStore.clearData();
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
## API Reference
|
|
227
|
-
|
|
228
|
-
### Constructor Options
|
|
229
|
-
|
|
230
|
-
#### StateStorageOptions
|
|
231
|
-
|
|
232
|
-
```typescript
|
|
233
|
-
interface StateStorageOptions {
|
|
234
|
-
store: string; // Unique store name
|
|
235
|
-
options: SettingOptions; // Storage configuration
|
|
236
|
-
model?: Function; // Model adapter for validation
|
|
237
|
-
version?: number; // State version for migrations
|
|
238
|
-
}
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
### State Management Methods
|
|
242
|
-
|
|
243
|
-
#### updateData(updater: ((state: T) => T) | T): void
|
|
244
|
-
|
|
245
|
-
Update state with new data or updater function:
|
|
246
|
-
|
|
247
|
-
```typescript
|
|
248
|
-
// Direct update
|
|
249
|
-
this.store.updateData({ theme: 'dark' });
|
|
250
|
-
|
|
251
|
-
// Function update
|
|
252
|
-
this.store.updateData(state => ({
|
|
253
|
-
...state,
|
|
254
|
-
notifications: !state.notifications
|
|
255
|
-
}));
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
#### clearData(): void
|
|
259
|
-
|
|
260
|
-
Clear all stored data:
|
|
261
|
-
|
|
262
|
-
```typescript
|
|
263
|
-
this.store.clearData();
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
### Selectors
|
|
267
|
-
|
|
268
|
-
#### data$: Observable<T>
|
|
269
|
-
|
|
270
|
-
Get current state:
|
|
271
|
-
|
|
272
|
-
```typescript
|
|
273
|
-
settings$ = this.store.data$;
|
|
274
|
-
|
|
275
|
-
<div *ngIf="settings$ | async as settings">
|
|
276
|
-
Theme: {{ settings.theme }}
|
|
277
|
-
</div>
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
#### storageInfo$: Observable<any>
|
|
281
|
-
|
|
282
|
-
Get storage metadata:
|
|
283
|
-
|
|
284
|
-
```typescript
|
|
285
|
-
info$ = this.store.storageInfo$;
|
|
286
|
-
|
|
287
|
-
<div *ngIf="info$ | async as info">
|
|
288
|
-
Last updated: {{ info.lastUpdated }}
|
|
289
|
-
Encrypted: {{ info.encrypted }}
|
|
290
|
-
</div>
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
### Data Operations
|
|
294
|
-
|
|
295
|
-
#### exportData(): any
|
|
296
|
-
|
|
297
|
-
Export current state:
|
|
298
|
-
|
|
299
|
-
```typescript
|
|
300
|
-
const exportedData = this.store.exportData();
|
|
301
|
-
console.log('Exported state:', exportedData);
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
#### importData(data: any): void
|
|
305
|
-
|
|
306
|
-
Import state data:
|
|
307
|
-
|
|
308
|
-
```typescript
|
|
309
|
-
this.store.importData({
|
|
310
|
-
theme: 'dark',
|
|
311
|
-
language: 'en',
|
|
312
|
-
notifications: true
|
|
313
|
-
});
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
## Advanced Examples
|
|
317
|
-
|
|
318
|
-
### Shopping Cart State
|
|
319
|
-
|
|
320
|
-
```typescript
|
|
321
|
-
interface CartItem {
|
|
322
|
-
id: string;
|
|
323
|
-
name: string;
|
|
324
|
-
price: number;
|
|
325
|
-
quantity: number;
|
|
326
|
-
image?: string;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
interface CartState {
|
|
330
|
-
items: CartItem[];
|
|
331
|
-
total: number;
|
|
332
|
-
itemCount: number;
|
|
333
|
-
lastUpdated: Date;
|
|
334
|
-
promoCode?: string;
|
|
335
|
-
discount: number;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
@Injectable()
|
|
339
|
-
export class ShoppingCartStateService extends StoreStateManagerService<CartState> {
|
|
340
|
-
|
|
341
|
-
constructor() {
|
|
342
|
-
super(
|
|
343
|
-
StateStorageOptions.adapt({
|
|
344
|
-
store: 'shopping-cart',
|
|
345
|
-
options: SettingOptions.adapt({
|
|
346
|
-
storage: StorageType.GLOBAL,
|
|
347
|
-
encrypted: false,
|
|
348
|
-
expiresIn: '7d'
|
|
349
|
-
}),
|
|
350
|
-
model: CartState.adapt
|
|
351
|
-
})
|
|
352
|
-
);
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// Cart operations
|
|
356
|
-
addItem(item: Omit<CartItem, 'quantity'>) {
|
|
357
|
-
this.updateData(state => {
|
|
358
|
-
const existingItem = state.items.find(i => i.id === item.id);
|
|
359
|
-
|
|
360
|
-
let newItems: CartItem[];
|
|
361
|
-
if (existingItem) {
|
|
362
|
-
newItems = state.items.map(i =>
|
|
363
|
-
i.id === item.id
|
|
364
|
-
? { ...i, quantity: i.quantity + 1 }
|
|
365
|
-
: i
|
|
366
|
-
);
|
|
367
|
-
} else {
|
|
368
|
-
newItems = [...state.items, { ...item, quantity: 1 }];
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
return this.calculateTotals({ ...state, items: newItems });
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
removeItem(itemId: string) {
|
|
376
|
-
this.updateData(state => ({
|
|
377
|
-
...state,
|
|
378
|
-
items: state.items.filter(item => item.id !== itemId),
|
|
379
|
-
lastUpdated: new Date()
|
|
380
|
-
}));
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
updateQuantity(itemId: string, quantity: number) {
|
|
384
|
-
if (quantity <= 0) {
|
|
385
|
-
this.removeItem(itemId);
|
|
386
|
-
return;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
this.updateData(state => {
|
|
390
|
-
const newItems = state.items.map(item =>
|
|
391
|
-
item.id === itemId ? { ...item, quantity } : item
|
|
392
|
-
);
|
|
393
|
-
|
|
394
|
-
return this.calculateTotals({ ...state, items: newItems });
|
|
395
|
-
});
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
clearCart() {
|
|
399
|
-
this.updateData({
|
|
400
|
-
items: [],
|
|
401
|
-
total: 0,
|
|
402
|
-
itemCount: 0,
|
|
403
|
-
lastUpdated: new Date(),
|
|
404
|
-
discount: 0
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
applyPromoCode(promoCode: string) {
|
|
409
|
-
this.updateData(state => {
|
|
410
|
-
const discount = this.calculateDiscount(promoCode, state.total);
|
|
411
|
-
return {
|
|
412
|
-
...state,
|
|
413
|
-
promoCode,
|
|
414
|
-
discount,
|
|
415
|
-
lastUpdated: new Date()
|
|
416
|
-
};
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
removePromoCode() {
|
|
421
|
-
this.updateData(state => ({
|
|
422
|
-
...state,
|
|
423
|
-
promoCode: undefined,
|
|
424
|
-
discount: 0,
|
|
425
|
-
lastUpdated: new Date()
|
|
426
|
-
}));
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// Computed values
|
|
430
|
-
getTotal() {
|
|
431
|
-
return this.data$.pipe(map(state => state.total));
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
getItemCount() {
|
|
435
|
-
return this.data$.pipe(map(state => state.itemCount));
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
getItems() {
|
|
439
|
-
return this.data$.pipe(map(state => state.items));
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
hasItems() {
|
|
443
|
-
return this.data$.pipe(map(state => state.items.length > 0));
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
getDiscount() {
|
|
447
|
-
return this.data$.pipe(map(state => state.discount));
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// Private helpers
|
|
451
|
-
private calculateTotals(state: CartState): CartState {
|
|
452
|
-
const subtotal = state.items.reduce((sum, item) => sum + (item.price * item.quantity), 0);
|
|
453
|
-
const discountAmount = (subtotal * state.discount) / 100;
|
|
454
|
-
const total = subtotal - discountAmount;
|
|
455
|
-
const itemCount = state.items.reduce((sum, item) => sum + item.quantity, 0);
|
|
456
|
-
|
|
457
|
-
return {
|
|
458
|
-
...state,
|
|
459
|
-
total,
|
|
460
|
-
itemCount,
|
|
461
|
-
lastUpdated: new Date()
|
|
462
|
-
};
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
private calculateDiscount(promoCode: string, total: number): number {
|
|
466
|
-
const discounts: { [key: string]: number } = {
|
|
467
|
-
'SAVE10': 10,
|
|
468
|
-
'WELCOME20': 20,
|
|
469
|
-
'HOLIDAY15': 15
|
|
470
|
-
};
|
|
471
|
-
|
|
472
|
-
return discounts[promoCode.toUpperCase()] || 0;
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
```
|
|
476
|
-
|
|
477
|
-
### Multi-tenant Application State
|
|
478
|
-
|
|
479
|
-
```typescript
|
|
480
|
-
interface TenantInfo {
|
|
481
|
-
id: string;
|
|
482
|
-
name: string;
|
|
483
|
-
domain: string;
|
|
484
|
-
logo?: string;
|
|
485
|
-
theme: {
|
|
486
|
-
primary: string;
|
|
487
|
-
secondary: string;
|
|
488
|
-
accent: string;
|
|
489
|
-
};
|
|
490
|
-
features: string[];
|
|
491
|
-
settings: {
|
|
492
|
-
maxUsers: number;
|
|
493
|
-
storageLimit: number;
|
|
494
|
-
apiRateLimit: number;
|
|
495
|
-
};
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
interface UserInfo {
|
|
499
|
-
id: string;
|
|
500
|
-
tenantId: string;
|
|
501
|
-
email: string;
|
|
502
|
-
name: string;
|
|
503
|
-
role: 'admin' | 'user' | 'guest';
|
|
504
|
-
permissions: string[];
|
|
505
|
-
lastLogin: Date;
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
interface AppState {
|
|
509
|
-
tenant: TenantInfo | null;
|
|
510
|
-
user: UserInfo | null;
|
|
511
|
-
isAuthenticated: boolean;
|
|
512
|
-
preferences: {
|
|
513
|
-
sidebarCollapsed: boolean;
|
|
514
|
-
theme: 'light' | 'dark' | 'auto';
|
|
515
|
-
language: string;
|
|
516
|
-
notifications: {
|
|
517
|
-
email: boolean;
|
|
518
|
-
push: boolean;
|
|
519
|
-
sms: boolean;
|
|
520
|
-
};
|
|
521
|
-
};
|
|
522
|
-
ui: {
|
|
523
|
-
loading: boolean;
|
|
524
|
-
error: string | null;
|
|
525
|
-
breadcrumbs: Array<{ label: string; url: string }>;
|
|
526
|
-
};
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
@Injectable()
|
|
530
|
-
export class AppStateService extends StoreStateManagerService<AppState> {
|
|
531
|
-
|
|
532
|
-
constructor() {
|
|
533
|
-
super(
|
|
534
|
-
StateStorageOptions.adapt({
|
|
535
|
-
store: 'app-state',
|
|
536
|
-
options: SettingOptions.adapt({
|
|
537
|
-
storage: StorageType.GLOBAL,
|
|
538
|
-
encrypted: true, // Encrypt sensitive data
|
|
539
|
-
expiresIn: '24h'
|
|
540
|
-
}),
|
|
541
|
-
model: AppState.adapt,
|
|
542
|
-
version: 1 // For future migrations
|
|
543
|
-
})
|
|
544
|
-
);
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
// Authentication
|
|
548
|
-
login(user: UserInfo, tenant: TenantInfo) {
|
|
549
|
-
this.updateData({
|
|
550
|
-
user,
|
|
551
|
-
tenant,
|
|
552
|
-
isAuthenticated: true,
|
|
553
|
-
ui: {
|
|
554
|
-
...this.getCurrentState().ui,
|
|
555
|
-
error: null
|
|
556
|
-
}
|
|
557
|
-
});
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
logout() {
|
|
561
|
-
this.updateData({
|
|
562
|
-
user: null,
|
|
563
|
-
tenant: null,
|
|
564
|
-
isAuthenticated: false,
|
|
565
|
-
preferences: {
|
|
566
|
-
...this.getCurrentState().preferences,
|
|
567
|
-
// Keep user preferences but clear auth data
|
|
568
|
-
}
|
|
569
|
-
});
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
updateUser(user: Partial<UserInfo>) {
|
|
573
|
-
this.updateData(state => ({
|
|
574
|
-
...state,
|
|
575
|
-
user: state.user ? { ...state.user, ...user } : null
|
|
576
|
-
}));
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
// Tenant management
|
|
580
|
-
switchTenant(tenant: TenantInfo) {
|
|
581
|
-
this.updateData(state => ({
|
|
582
|
-
...state,
|
|
583
|
-
tenant,
|
|
584
|
-
user: state.user ? { ...state.user, tenantId: tenant.id } : null
|
|
585
|
-
}));
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
updateTenantSettings(settings: Partial<TenantInfo['settings']>) {
|
|
589
|
-
this.updateData(state => ({
|
|
590
|
-
...state,
|
|
591
|
-
tenant: state.tenant ? {
|
|
592
|
-
...state.tenant,
|
|
593
|
-
settings: { ...state.tenant.settings, ...settings }
|
|
594
|
-
} : null
|
|
595
|
-
}));
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
// Preferences
|
|
599
|
-
updatePreferences(preferences: Partial<AppState['preferences']>) {
|
|
600
|
-
this.updateData(state => ({
|
|
601
|
-
...state,
|
|
602
|
-
preferences: { ...state.preferences, ...preferences }
|
|
603
|
-
}));
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
toggleSidebar() {
|
|
607
|
-
this.updateData(state => ({
|
|
608
|
-
...state,
|
|
609
|
-
preferences: {
|
|
610
|
-
...state.preferences,
|
|
611
|
-
sidebarCollapsed: !state.preferences.sidebarCollapsed
|
|
612
|
-
}
|
|
613
|
-
}));
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
setTheme(theme: 'light' | 'dark' | 'auto') {
|
|
617
|
-
this.updatePreferences({ theme });
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
setLanguage(language: string) {
|
|
621
|
-
this.updatePreferences({ language });
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
updateNotificationSettings(settings: Partial<AppState['preferences']['notifications']>) {
|
|
625
|
-
this.updateData(state => ({
|
|
626
|
-
...state,
|
|
627
|
-
preferences: {
|
|
628
|
-
...state.preferences,
|
|
629
|
-
notifications: {
|
|
630
|
-
...state.preferences.notifications,
|
|
631
|
-
...settings
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
}));
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
// UI State
|
|
638
|
-
setLoading(loading: boolean) {
|
|
639
|
-
this.updateData(state => ({
|
|
640
|
-
...state,
|
|
641
|
-
ui: { ...state.ui, loading }
|
|
642
|
-
}));
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
setError(error: string | null) {
|
|
646
|
-
this.updateData(state => ({
|
|
647
|
-
...state,
|
|
648
|
-
ui: { ...state.ui, error }
|
|
649
|
-
}));
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
clearError() {
|
|
653
|
-
this.setError(null);
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
setBreadcrumbs(breadcrumbs: Array<{ label: string; url: string }>) {
|
|
657
|
-
this.updateData(state => ({
|
|
658
|
-
...state,
|
|
659
|
-
ui: { ...state.ui, breadcrumbs }
|
|
660
|
-
}));
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
// Computed selectors
|
|
664
|
-
isAdmin() {
|
|
665
|
-
return this.data$.pipe(map(state => state.user?.role === 'admin'));
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
isAuthenticated() {
|
|
669
|
-
return this.data$.pipe(map(state => state.isAuthenticated));
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
currentTenant() {
|
|
673
|
-
return this.data$.pipe(map(state => state.tenant));
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
currentUser() {
|
|
677
|
-
return this.data$.pipe(map(state => state.user));
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
hasPermission(permission: string) {
|
|
681
|
-
return this.data$.pipe(
|
|
682
|
-
map(state => state.user?.permissions.includes(permission) || false)
|
|
683
|
-
);
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
isFeatureEnabled(feature: string) {
|
|
687
|
-
return this.data$.pipe(
|
|
688
|
-
map(state => state.tenant?.features.includes(feature) || false)
|
|
689
|
-
);
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
// Data persistence
|
|
693
|
-
exportState() {
|
|
694
|
-
return this.exportData();
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
importState(state: Partial<AppState>) {
|
|
698
|
-
this.importData({ ...this.getCurrentState(), ...state });
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
// Migration support
|
|
702
|
-
migrateFromVersion1(state: any): AppState {
|
|
703
|
-
// Example migration logic
|
|
704
|
-
return {
|
|
705
|
-
...state,
|
|
706
|
-
ui: {
|
|
707
|
-
...state.ui,
|
|
708
|
-
breadcrumbs: state.ui?.breadcrumbs || []
|
|
709
|
-
}
|
|
710
|
-
};
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
// Private helper
|
|
714
|
-
private getCurrentState(): AppState {
|
|
715
|
-
let currentState: AppState | undefined;
|
|
716
|
-
this.data$.pipe(take(1)).subscribe(state => currentState = state);
|
|
717
|
-
return currentState!;
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
```
|
|
721
|
-
|
|
722
|
-
### Game Progress Tracking
|
|
723
|
-
|
|
724
|
-
```typescript
|
|
725
|
-
interface GameLevel {
|
|
726
|
-
id: string;
|
|
727
|
-
name: string;
|
|
728
|
-
completed: boolean;
|
|
729
|
-
score: number;
|
|
730
|
-
stars: number; // 0-3 stars
|
|
731
|
-
timeSpent: number; // in seconds
|
|
732
|
-
completedAt?: Date;
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
interface GameProgress {
|
|
736
|
-
playerId: string;
|
|
737
|
-
totalScore: number;
|
|
738
|
-
currentLevel: string;
|
|
739
|
-
unlockedLevels: string[];
|
|
740
|
-
levels: GameLevel[];
|
|
741
|
-
achievements: string[];
|
|
742
|
-
settings: {
|
|
743
|
-
soundEnabled: boolean;
|
|
744
|
-
musicEnabled: boolean;
|
|
745
|
-
difficulty: 'easy' | 'normal' | 'hard';
|
|
746
|
-
showHints: boolean;
|
|
747
|
-
};
|
|
748
|
-
statistics: {
|
|
749
|
-
totalPlayTime: number;
|
|
750
|
-
levelsCompleted: number;
|
|
751
|
-
totalStars: number;
|
|
752
|
-
averageScore: number;
|
|
753
|
-
bestStreak: number;
|
|
754
|
-
currentStreak: number;
|
|
755
|
-
};
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
@Injectable()
|
|
759
|
-
export class GameProgressStateService extends StoreStateManagerService<GameProgress> {
|
|
760
|
-
|
|
761
|
-
constructor() {
|
|
762
|
-
super(
|
|
763
|
-
StateStorageOptions.adapt({
|
|
764
|
-
store: `game-progress-${this.getPlayerId()}`,
|
|
765
|
-
options: SettingOptions.adapt({
|
|
766
|
-
storage: StorageType.GLOBAL,
|
|
767
|
-
encrypted: false,
|
|
768
|
-
expiresIn: '1y' // Keep progress for a year
|
|
769
|
-
}),
|
|
770
|
-
model: GameProgress.adapt
|
|
771
|
-
})
|
|
772
|
-
);
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
// Level management
|
|
776
|
-
completeLevel(levelId: string, score: number, timeSpent: number, stars: number) {
|
|
777
|
-
this.updateData(state => {
|
|
778
|
-
const levelIndex = state.levels.findIndex(l => l.id === levelId);
|
|
779
|
-
if (levelIndex === -1) return state;
|
|
780
|
-
|
|
781
|
-
const updatedLevels = [...state.levels];
|
|
782
|
-
const level = { ...updatedLevels[levelIndex] };
|
|
783
|
-
|
|
784
|
-
if (!level.completed || score > level.score) {
|
|
785
|
-
level.completed = true;
|
|
786
|
-
level.score = score;
|
|
787
|
-
level.stars = Math.max(level.stars, stars);
|
|
788
|
-
level.timeSpent = timeSpent;
|
|
789
|
-
level.completedAt = new Date();
|
|
790
|
-
|
|
791
|
-
updatedLevels[levelIndex] = level;
|
|
792
|
-
|
|
793
|
-
// Unlock next level
|
|
794
|
-
const nextLevelIndex = levelIndex + 1;
|
|
795
|
-
const unlockedLevels = [...state.unlockedLevels];
|
|
796
|
-
if (nextLevelIndex < state.levels.length) {
|
|
797
|
-
const nextLevel = state.levels[nextLevelIndex];
|
|
798
|
-
if (!unlockedLevels.includes(nextLevel.id)) {
|
|
799
|
-
unlockedLevels.push(nextLevel.id);
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
// Update statistics
|
|
804
|
-
const statistics = this.calculateStatistics({ ...state, levels: updatedLevels, unlockedLevels });
|
|
805
|
-
|
|
806
|
-
return {
|
|
807
|
-
...state,
|
|
808
|
-
levels: updatedLevels,
|
|
809
|
-
unlockedLevels,
|
|
810
|
-
currentLevel: nextLevelIndex < state.levels.length ? state.levels[nextLevelIndex].id : state.currentLevel,
|
|
811
|
-
statistics
|
|
812
|
-
};
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
return state;
|
|
816
|
-
});
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
resetLevel(levelId: string) {
|
|
820
|
-
this.updateData(state => {
|
|
821
|
-
const levelIndex = state.levels.findIndex(l => l.id === levelId);
|
|
822
|
-
if (levelIndex === -1) return state;
|
|
823
|
-
|
|
824
|
-
const updatedLevels = [...state.levels];
|
|
825
|
-
updatedLevels[levelIndex] = {
|
|
826
|
-
...updatedLevels[levelIndex],
|
|
827
|
-
completed: false,
|
|
828
|
-
score: 0,
|
|
829
|
-
stars: 0,
|
|
830
|
-
timeSpent: 0,
|
|
831
|
-
completedAt: undefined
|
|
832
|
-
};
|
|
833
|
-
|
|
834
|
-
return {
|
|
835
|
-
...state,
|
|
836
|
-
levels: updatedLevels,
|
|
837
|
-
statistics: this.calculateStatistics({ ...state, levels: updatedLevels })
|
|
838
|
-
};
|
|
839
|
-
});
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
unlockLevel(levelId: string) {
|
|
843
|
-
this.updateData(state => {
|
|
844
|
-
if (!state.unlockedLevels.includes(levelId)) {
|
|
845
|
-
return {
|
|
846
|
-
...state,
|
|
847
|
-
unlockedLevels: [...state.unlockedLevels, levelId]
|
|
848
|
-
};
|
|
849
|
-
}
|
|
850
|
-
return state;
|
|
851
|
-
});
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
// Achievement system
|
|
855
|
-
unlockAchievement(achievementId: string) {
|
|
856
|
-
this.updateData(state => {
|
|
857
|
-
if (!state.achievements.includes(achievementId)) {
|
|
858
|
-
return {
|
|
859
|
-
...state,
|
|
860
|
-
achievements: [...state.achievements, achievementId]
|
|
861
|
-
};
|
|
862
|
-
}
|
|
863
|
-
return state;
|
|
864
|
-
});
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
// Settings
|
|
868
|
-
updateSettings(settings: Partial<GameProgress['settings']>) {
|
|
869
|
-
this.updateData(state => ({
|
|
870
|
-
...state,
|
|
871
|
-
settings: { ...state.settings, ...settings }
|
|
872
|
-
}));
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
toggleSound() {
|
|
876
|
-
this.updateData(state => ({
|
|
877
|
-
...state,
|
|
878
|
-
settings: {
|
|
879
|
-
...state.settings,
|
|
880
|
-
soundEnabled: !state.settings.soundEnabled
|
|
881
|
-
}
|
|
882
|
-
}));
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
toggleMusic() {
|
|
886
|
-
this.updateData(state => ({
|
|
887
|
-
...state,
|
|
888
|
-
settings: {
|
|
889
|
-
...state.settings,
|
|
890
|
-
musicEnabled: !state.settings.musicEnabled
|
|
891
|
-
}
|
|
892
|
-
}));
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
setDifficulty(difficulty: 'easy' | 'normal' | 'hard') {
|
|
896
|
-
this.updateSettings({ difficulty });
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
// Progress queries
|
|
900
|
-
getCompletedLevels() {
|
|
901
|
-
return this.data$.pipe(
|
|
902
|
-
map(state => state.levels.filter(level => level.completed))
|
|
903
|
-
);
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
getLevelProgress(levelId: string) {
|
|
907
|
-
return this.data$.pipe(
|
|
908
|
-
map(state => state.levels.find(level => level.id === levelId))
|
|
909
|
-
);
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
isLevelUnlocked(levelId: string) {
|
|
913
|
-
return this.data$.pipe(
|
|
914
|
-
map(state => state.unlockedLevels.includes(levelId))
|
|
915
|
-
);
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
getTotalStars() {
|
|
919
|
-
return this.data$.pipe(
|
|
920
|
-
map(state => state.statistics.totalStars)
|
|
921
|
-
);
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
getCompletionPercentage() {
|
|
925
|
-
return this.data$.pipe(
|
|
926
|
-
map(state => {
|
|
927
|
-
const completedLevels = state.levels.filter(l => l.completed).length;
|
|
928
|
-
return (completedLevels / state.levels.length) * 100;
|
|
929
|
-
})
|
|
930
|
-
);
|
|
931
|
-
}
|
|
932
|
-
|
|
933
|
-
getNextLevel() {
|
|
934
|
-
return this.data$.pipe(
|
|
935
|
-
map(state => {
|
|
936
|
-
const currentLevelIndex = state.levels.findIndex(l => l.id === state.currentLevel);
|
|
937
|
-
return state.levels[currentLevelIndex + 1] || null;
|
|
938
|
-
})
|
|
939
|
-
);
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
// Statistics
|
|
943
|
-
private calculateStatistics(state: GameProgress): GameProgress['statistics'] {
|
|
944
|
-
const completedLevels = state.levels.filter(l => l.completed);
|
|
945
|
-
const totalPlayTime = state.statistics.totalPlayTime +
|
|
946
|
-
completedLevels.reduce((sum, level) => sum + level.timeSpent, 0);
|
|
947
|
-
const totalStars = completedLevels.reduce((sum, level) => sum + level.stars, 0);
|
|
948
|
-
const averageScore = completedLevels.length > 0
|
|
949
|
-
? completedLevels.reduce((sum, level) => sum + level.score, 0) / completedLevels.length
|
|
950
|
-
: 0;
|
|
951
|
-
|
|
952
|
-
// Calculate streak (consecutive levels completed)
|
|
953
|
-
let currentStreak = 0;
|
|
954
|
-
for (let i = 0; i < state.levels.length; i++) {
|
|
955
|
-
if (state.levels[i].completed) {
|
|
956
|
-
currentStreak++;
|
|
957
|
-
} else {
|
|
958
|
-
break;
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
return {
|
|
963
|
-
...state.statistics,
|
|
964
|
-
totalPlayTime,
|
|
965
|
-
levelsCompleted: completedLevels.length,
|
|
966
|
-
totalStars,
|
|
967
|
-
averageScore,
|
|
968
|
-
bestStreak: Math.max(state.statistics.bestStreak, currentStreak),
|
|
969
|
-
currentStreak
|
|
970
|
-
};
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
// Data management
|
|
974
|
-
exportProgress() {
|
|
975
|
-
return this.exportData();
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
importProgress(progressData: GameProgress) {
|
|
979
|
-
this.importData(progressData);
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
resetAllProgress() {
|
|
983
|
-
this.updateData({
|
|
984
|
-
playerId: this.getPlayerId(),
|
|
985
|
-
totalScore: 0,
|
|
986
|
-
currentLevel: 'level-1',
|
|
987
|
-
unlockedLevels: ['level-1'],
|
|
988
|
-
levels: this.initializeLevels(),
|
|
989
|
-
achievements: [],
|
|
990
|
-
settings: {
|
|
991
|
-
soundEnabled: true,
|
|
992
|
-
musicEnabled: true,
|
|
993
|
-
difficulty: 'normal',
|
|
994
|
-
showHints: true
|
|
995
|
-
},
|
|
996
|
-
statistics: {
|
|
997
|
-
totalPlayTime: 0,
|
|
998
|
-
levelsCompleted: 0,
|
|
999
|
-
totalStars: 0,
|
|
1000
|
-
averageScore: 0,
|
|
1001
|
-
bestStreak: 0,
|
|
1002
|
-
currentStreak: 0
|
|
1003
|
-
}
|
|
1004
|
-
});
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
private getPlayerId(): string {
|
|
1008
|
-
// Generate or retrieve player ID
|
|
1009
|
-
let playerId = localStorage.getItem('playerId');
|
|
1010
|
-
if (!playerId) {
|
|
1011
|
-
playerId = 'player-' + Date.now();
|
|
1012
|
-
localStorage.setItem('playerId', playerId);
|
|
1013
|
-
}
|
|
1014
|
-
return playerId;
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
|
-
private initializeLevels(): GameLevel[] {
|
|
1018
|
-
// Initialize game levels - this would come from your game data
|
|
1019
|
-
return [
|
|
1020
|
-
{ id: 'level-1', name: 'Tutorial', completed: false, score: 0, stars: 0, timeSpent: 0 },
|
|
1021
|
-
{ id: 'level-2', name: 'Getting Started', completed: false, score: 0, stars: 0, timeSpent: 0 },
|
|
1022
|
-
{ id: 'level-3', name: 'First Challenge', completed: false, score: 0, stars: 0, timeSpent: 0 },
|
|
1023
|
-
// ... more levels
|
|
1024
|
-
];
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
```
|
|
1028
|
-
|
|
1029
|
-
## State Migration
|
|
1030
|
-
|
|
1031
|
-
### Version Management
|
|
1032
|
-
|
|
1033
|
-
```typescript
|
|
1034
|
-
@Injectable()
|
|
1035
|
-
export class MigratableStateService extends StoreStateManagerService<any> {
|
|
1036
|
-
|
|
1037
|
-
constructor() {
|
|
1038
|
-
super(
|
|
1039
|
-
StateStorageOptions.adapt({
|
|
1040
|
-
store: 'migratable-state',
|
|
1041
|
-
options: SettingOptions.adapt({
|
|
1042
|
-
storage: StorageType.GLOBAL,
|
|
1043
|
-
encrypted: false
|
|
1044
|
-
}),
|
|
1045
|
-
version: 2 // Current version
|
|
1046
|
-
})
|
|
1047
|
-
);
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
// Handle migrations
|
|
1051
|
-
protected override handleMigration(storedData: any, currentVersion: number): any {
|
|
1052
|
-
let migratedData = storedData;
|
|
1053
|
-
let version = currentVersion;
|
|
1054
|
-
|
|
1055
|
-
// Migrate from version 1 to 2
|
|
1056
|
-
if (version < 2) {
|
|
1057
|
-
migratedData = this.migrateFromV1ToV2(migratedData);
|
|
1058
|
-
version = 2;
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
// Future migrations would go here
|
|
1062
|
-
// if (version < 3) {
|
|
1063
|
-
// migratedData = this.migrateFromV2ToV3(migratedData);
|
|
1064
|
-
// version = 3;
|
|
1065
|
-
// }
|
|
1066
|
-
|
|
1067
|
-
return { data: migratedData, version };
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
private migrateFromV1ToV2(data: any): any {
|
|
1071
|
-
// Example migration: add new field with default value
|
|
1072
|
-
return {
|
|
1073
|
-
...data,
|
|
1074
|
-
newField: data.newField || 'default-value',
|
|
1075
|
-
migratedAt: new Date()
|
|
1076
|
-
};
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1079
|
-
```
|
|
1080
|
-
|
|
1081
|
-
## Best Practices
|
|
1082
|
-
|
|
1083
|
-
### 1. Model Validation
|
|
1084
|
-
|
|
1085
|
-
```typescript
|
|
1086
|
-
// ✅ Good - Use model adapters for validation
|
|
1087
|
-
@Injectable()
|
|
1088
|
-
export class ValidatedStateService extends StoreStateManagerService<UserSettings> {
|
|
1089
|
-
|
|
1090
|
-
constructor() {
|
|
1091
|
-
super(
|
|
1092
|
-
StateStorageOptions.adapt({
|
|
1093
|
-
store: 'validated-settings',
|
|
1094
|
-
model: UserSettings.adapt // Validates and normalizes data
|
|
1095
|
-
})
|
|
1096
|
-
);
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
|
-
|
|
1100
|
-
// ❌ Avoid - No model validation
|
|
1101
|
-
super(
|
|
1102
|
-
StateStorageOptions.adapt({
|
|
1103
|
-
store: 'settings',
|
|
1104
|
-
// No model validation
|
|
1105
|
-
})
|
|
1106
|
-
);
|
|
1107
|
-
```
|
|
1108
|
-
|
|
1109
|
-
### 2. Appropriate Storage Types
|
|
1110
|
-
|
|
1111
|
-
```typescript
|
|
1112
|
-
// ✅ Good - Choose appropriate storage
|
|
1113
|
-
// Session-specific data
|
|
1114
|
-
super(StateStorageOptions.adapt({
|
|
1115
|
-
store: 'temp-calculation',
|
|
1116
|
-
options: SettingOptions.adapt({
|
|
1117
|
-
storage: StorageType.SESSION // Cleared on browser close
|
|
1118
|
-
})
|
|
1119
|
-
}));
|
|
1120
|
-
|
|
1121
|
-
// Persistent user preferences
|
|
1122
|
-
super(StateStorageOptions.adapt({
|
|
1123
|
-
store: 'user-preferences',
|
|
1124
|
-
options: SettingOptions.adapt({
|
|
1125
|
-
storage: StorageType.GLOBAL // Persists across sessions
|
|
1126
|
-
})
|
|
1127
|
-
}));
|
|
1128
|
-
```
|
|
1129
|
-
|
|
1130
|
-
### 3. Error Handling
|
|
1131
|
-
|
|
1132
|
-
```typescript
|
|
1133
|
-
// ✅ Good - Handle storage errors gracefully
|
|
1134
|
-
updateData(updater: ((state: T) => T) | T): void {
|
|
1135
|
-
try {
|
|
1136
|
-
super.updateData(updater);
|
|
1137
|
-
} catch (error) {
|
|
1138
|
-
console.error('Failed to update state:', error);
|
|
1139
|
-
// Optionally show user notification
|
|
1140
|
-
this.showErrorNotification('Failed to save changes');
|
|
1141
|
-
}
|
|
1142
|
-
}
|
|
1143
|
-
```
|
|
1144
|
-
|
|
1145
|
-
### 4. State Size Management
|
|
1146
|
-
|
|
1147
|
-
```typescript
|
|
1148
|
-
// ✅ Good - Keep state size reasonable
|
|
1149
|
-
@Injectable()
|
|
1150
|
-
export class OptimizedStateService extends StoreStateManagerService<AppState> {
|
|
1151
|
-
|
|
1152
|
-
updateData(updater: ((state: T) => T) | T): void {
|
|
1153
|
-
super.updateData(state => {
|
|
1154
|
-
const newState = typeof updater === 'function' ? updater(state) : updater;
|
|
1155
|
-
|
|
1156
|
-
// Prune old data if state becomes too large
|
|
1157
|
-
if (this.getStateSize(newState) > MAX_STATE_SIZE) {
|
|
1158
|
-
return this.pruneState(newState);
|
|
1159
|
-
}
|
|
1160
|
-
|
|
1161
|
-
return newState;
|
|
1162
|
-
});
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
private getStateSize(state: any): number {
|
|
1166
|
-
return JSON.stringify(state).length;
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
|
-
private pruneState(state: any): any {
|
|
1170
|
-
// Remove old log entries, cache data, etc.
|
|
1171
|
-
return {
|
|
1172
|
-
...state,
|
|
1173
|
-
logs: state.logs?.slice(-100), // Keep only recent logs
|
|
1174
|
-
cache: undefined // Clear cache
|
|
1175
|
-
};
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
```
|
|
1179
|
-
|
|
1180
|
-
## Performance Optimization
|
|
1181
|
-
|
|
1182
|
-
### Selective Persistence
|
|
1183
|
-
|
|
1184
|
-
```typescript
|
|
1185
|
-
@Injectable()
|
|
1186
|
-
export class SelectivePersistenceService extends StoreStateManagerService<any> {
|
|
1187
|
-
|
|
1188
|
-
// Only persist specific parts of state
|
|
1189
|
-
protected override shouldPersist(key: string, value: any): boolean {
|
|
1190
|
-
// Don't persist large objects or temporary data
|
|
1191
|
-
if (key === 'tempData' || key === 'largeCache') {
|
|
1192
|
-
return false;
|
|
1193
|
-
}
|
|
1194
|
-
|
|
1195
|
-
// Don't persist computed values
|
|
1196
|
-
if (key.startsWith('computed') || key.startsWith('derived')) {
|
|
1197
|
-
return false;
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
|
-
return true;
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1203
|
-
// Custom persistence logic
|
|
1204
|
-
protected override persistState(state: any): void {
|
|
1205
|
-
const persistentState = this.filterPersistentData(state);
|
|
1206
|
-
super.persistState(persistentState);
|
|
1207
|
-
}
|
|
1208
|
-
|
|
1209
|
-
private filterPersistentData(state: any): any {
|
|
1210
|
-
const { tempData, largeCache, computedValues, ...persistentState } = state;
|
|
1211
|
-
return persistentState;
|
|
1212
|
-
}
|
|
1213
|
-
}
|
|
1214
|
-
```
|
|
1215
|
-
|
|
1216
|
-
### Debounced Updates
|
|
1217
|
-
|
|
1218
|
-
```typescript
|
|
1219
|
-
@Injectable()
|
|
1220
|
-
export class DebouncedStateService extends StoreStateManagerService<any> {
|
|
1221
|
-
|
|
1222
|
-
private updateTimeout?: any;
|
|
1223
|
-
|
|
1224
|
-
updateData(updater: ((state: T) => T) | T): void {
|
|
1225
|
-
// Clear previous timeout
|
|
1226
|
-
if (this.updateTimeout) {
|
|
1227
|
-
clearTimeout(this.updateTimeout);
|
|
1228
|
-
}
|
|
1229
|
-
|
|
1230
|
-
// Debounce updates to avoid excessive storage writes
|
|
1231
|
-
this.updateTimeout = setTimeout(() => {
|
|
1232
|
-
super.updateData(updater);
|
|
1233
|
-
}, 500); // 500ms debounce
|
|
1234
|
-
}
|
|
1235
|
-
}
|
|
1236
|
-
```
|
|
1237
|
-
|
|
1238
|
-
## Troubleshooting
|
|
1239
|
-
|
|
1240
|
-
### Common Issues
|
|
1241
|
-
|
|
1242
|
-
#### 1. Storage Quota Exceeded
|
|
1243
|
-
```typescript
|
|
1244
|
-
// Monitor storage usage
|
|
1245
|
-
@Injectable()
|
|
1246
|
-
export class StorageMonitoringService extends StoreStateManagerService<any> {
|
|
1247
|
-
|
|
1248
|
-
updateData(updater: ((state: T) => T) | T): void {
|
|
1249
|
-
try {
|
|
1250
|
-
super.updateData(updater);
|
|
1251
|
-
} catch (error) {
|
|
1252
|
-
if (error.name === 'QuotaExceededError') {
|
|
1253
|
-
this.handleQuotaExceeded();
|
|
1254
|
-
} else {
|
|
1255
|
-
throw error;
|
|
1256
|
-
}
|
|
1257
|
-
}
|
|
1258
|
-
}
|
|
1259
|
-
|
|
1260
|
-
private handleQuotaExceeded() {
|
|
1261
|
-
// Clean up old data
|
|
1262
|
-
this.cleanupOldData();
|
|
1263
|
-
|
|
1264
|
-
// Retry the update
|
|
1265
|
-
super.updateData(this.pendingUpdate);
|
|
1266
|
-
}
|
|
1267
|
-
|
|
1268
|
-
private cleanupOldData() {
|
|
1269
|
-
// Remove old state versions, logs, etc.
|
|
1270
|
-
const oldKeys = Object.keys(localStorage).filter(key =>
|
|
1271
|
-
key.startsWith('app-state-') && key !== this.storeName
|
|
1272
|
-
);
|
|
1273
|
-
oldKeys.forEach(key => localStorage.removeItem(key));
|
|
1274
|
-
}
|
|
1275
|
-
}
|
|
1276
|
-
```
|
|
1277
|
-
|
|
1278
|
-
#### 2. Corrupted State Data
|
|
1279
|
-
```typescript
|
|
1280
|
-
// Handle corrupted data gracefully
|
|
1281
|
-
protected override handleMigration(storedData: any, currentVersion: number): any {
|
|
1282
|
-
try {
|
|
1283
|
-
return super.handleMigration(storedData, currentVersion);
|
|
1284
|
-
} catch (error) {
|
|
1285
|
-
console.warn('Corrupted state data detected, resetting to defaults');
|
|
1286
|
-
|
|
1287
|
-
// Reset to default state
|
|
1288
|
-
return {
|
|
1289
|
-
data: this.getDefaultState(),
|
|
1290
|
-
version: currentVersion
|
|
1291
|
-
};
|
|
1292
|
-
}
|
|
1293
|
-
}
|
|
1294
|
-
```
|
|
1295
|
-
|
|
1296
|
-
#### 3. Encryption Issues
|
|
1297
|
-
```typescript
|
|
1298
|
-
// Ensure APP_ID is provided for encryption
|
|
1299
|
-
@NgModule({
|
|
1300
|
-
providers: [
|
|
1301
|
-
{ provide: APP_ID, useValue: "your-unique-guid-here" }
|
|
1302
|
-
]
|
|
1303
|
-
})
|
|
1304
|
-
export class AppModule { }
|
|
1305
|
-
|
|
1306
|
-
// Handle encryption errors
|
|
1307
|
-
protected override encryptData(data: any): string {
|
|
1308
|
-
try {
|
|
1309
|
-
return super.encryptData(data);
|
|
1310
|
-
} catch (error) {
|
|
1311
|
-
console.warn('Encryption failed, storing without encryption');
|
|
1312
|
-
return JSON.stringify(data);
|
|
1313
|
-
}
|
|
1314
|
-
}
|
|
1315
|
-
```
|
|
1316
|
-
|
|
1317
|
-
## Related Documentation
|
|
1318
|
-
|
|
1319
|
-
- [ComponentStore Documentation](https://ngrx.io/guide/component-store)
|
|
1320
|
-
- [Local Storage Service](local-storage/README.md)
|
|
1321
|
-
- [HTTP State Service](http-state/README.md)
|
|
1322
|
-
- [Architecture Overview](../architecture/README.md)
|