User Preferences Store
Relevant source files
The following files were used as context for generating this wiki page:
The User Preferences Store is a specialized subsystem responsible for the persistence and retrieval of user-specific configurations, such as UI themes, table column layouts, and default search filters. It ensures that the application state remains consistent across different sessions by serializing data into a structured format on the local filesystem.
This system is designed following a hexagonal architecture pattern, where the core domain logic interacts with an abstract port (IUserPreferencesStore), and the infrastructure layer provides a concrete implementation (JsonUserPreferencesStore) using the JSON format for storage. This decoupling allows for easier testing and potential future migrations to other storage backends.
Sources: src/ports/IUserPreferencesStore.h, src/infra/preferences/JsonUserPreferencesStore.h, AGENTS.md:14-15
Architecture and Components
The architecture relies on three primary elements: a data transfer object (Snapshot), a port (Interface), and an adapter (Infrastructure implementation).
Class Hierarchy and Data Structures
The system centers around the UserPreferencesSnapshot, a plain data structure containing all configurable parameters. The storage mechanism is abstracted through IUserPreferencesStore to ensure that the MainViewModel remains independent of specific file handling or JSON libraries.
This diagram shows the relationship between the interface, the concrete JSON implementation, and the snapshot data structure.
Sources: src/ports/IUserPreferencesStore.h:11-33, src/infra/preferences/JsonUserPreferencesStore.h:11-18
Components Overview
| Component | Responsibility | Source File |
|---|---|---|
UserPreferencesSnapshot | Acts as a DTO (Data Transfer Object) holding a point-in-time state of user settings. | IUserPreferencesStore.h |
IUserPreferencesStore | Abstract interface defining the contract for loading and saving preferences. | IUserPreferencesStore.h |
JsonUserPreferencesStore | Infrastructure implementation using QJsonDocument to persist data to a file. | JsonUserPreferencesStore.cpp |
AppPaths | Provides the standard filesystem path for the preferences file. | AppPaths.cpp |
Data Flow and Persistence
The JsonUserPreferencesStore manages the transformation between C++ structures and JSON files on disk.
Loading Logic
When the application starts, the MainViewModel invokes the load() method. If the preferences file does not exist, the system returns a snapshot with default values defined in the ColumnCatalog and domain constants.
- Check if the file exists using
std::filesystem::exists. - Read the file into a
QJsonDocument. - Extract values using specific keys, applying fallback values if keys are missing.
- Validate data using domain constraints (e.g.,
clampPageSize).
Sources: src/infra/preferences/JsonUserPreferencesStore.cpp:21-72, src/domain/SsaTypes.h:21-27
Saving Logic and Debouncing
Saving is triggered by UI changes but is debounced via a QTimer to prevent excessive disk I/O. The MainViewModel schedules a save, and after a 250ms interval of inactivity, the data is serialized.
The flow shows how UI changes are throttled before reaching the filesystem.
Sources: src/presentation/MainViewModel.cpp:33-37, src/presentation/MainViewModel.cpp:286-335
Configuration Schema
The store manages several categories of data. Below are the fields persisted in the JSON schema:
| Key | Type | Default / Constraint | Description |
|---|---|---|---|
page_size | Integer | 100 (Min: 10, Max: 500) | Number of records per table page. |
theme | String | "system" | UI visual theme (e.g., "dark", "light"). |
density | String | "normal" | Row spacing (compact, normal, comfortable). |
details_visible | Boolean | true | Visibility of the side details panel. |
details_panel_width | Integer | 360 (Min: 280, Max: 680) | Width of the details panel in pixels. |
visible_columns | Array | [ColumnCatalog Defaults] | List of keys for columns displayed in the table. |
column_widths | Object | Map of keys to integers | Custom widths for specific table columns. |
exclude_sca_ses_ste | Boolean | true | Default filter to exclude specific SSA statuses. |
Sources: src/ports/IUserPreferencesStore.h:13-25, src/domain/SsaTypes.h:13-20, src/infra/preferences/JsonUserPreferencesStore.cpp:39-55
Implementation Details
JSON Serialization
The implementation uses QJsonObject and QJsonArray for serialization. It ensures that the parent directory for the preferences file exists before attempting to write.
// src/infra/preferences/JsonUserPreferencesStore.cpp:74-75
void JsonUserPreferencesStore::save(const ports::UserPreferencesSnapshot& snapshot) const {
std::filesystem::create_directories(path_.parent_path());
QJsonObject root;
root.insert("schema_version", snapshot.schemaVersion);
root.insert("page_size", domain::clampPageSize(snapshot.pageSize));
// ... serialization of maps and vectors ...
QFile output(QString::fromStdString(path_.string()));
if (output.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
output.write(QJsonDocument(root).toJson(QJsonDocument::Indented));
}
}
Sources: src/infra/preferences/JsonUserPreferencesStore.cpp:74-114
Integration with Presentation Layer
The MainViewModel coordinates the preferences store. It uses QtConcurrent::run to perform the actual file writing on a background thread to avoid blocking the UI main loop. It also manages a state machine for saves using preferencesSaveInProgress_ and pendingPreferencesSave_ flags to handle concurrent save requests.
Conclusion
The User Preferences Store provides a robust mechanism for persisting the application's visual and functional state. By utilizing an interface-based design and JSON serialization, it maintains a clear separation between domain requirements and infrastructure implementation. The use of asynchronous, debounced saving ensures that the application remains responsive even during frequent configuration changes.
Sources: src/infra/preferences/JsonUserPreferencesStore.cpp, src/presentation/MainViewModel.cpp