Content
Local preferences storage
Overview
The app uses SharedPreferences for local data persistence approach to manage different data scopes. For different platforms (ios, android, desktops) this storage has different naming. All these implementation are covered under one umbrella of flutter library shared_preferences.
This document describes the architecture, data flow, categorisation, and naming convention.
Architecture
Core Components
1. PreferencesStorage - Low-level wrapper around SharedPreferences
2. MultiUserPreferencesStorage - Helper for multi-user data storage
3. SettingsRepository - Repository layer for accessing settings
4. Use Cases - Business logic layer (GetUserSettingUseCase, SaveUserSettingUseCase)
5. BLoCs - Presentation layer that uses Use Cases
Data Flow
BLoC/Service
↓ (uses)
Use Case (GetUserSettingUseCase/SaveUserSettingUseCase)
↓ (gets current userId)
UserRepository
↓ (passes userId to)
SettingsRepository
↓ (uses)
MultiUserPreferencesStorage
↓ (uses)
PreferencesStorage (SharedPreferences wrapper)
Data Categories
Currently preferences are organised into four categories based on their scope and lifecycle:
1. Multi-User Preferences
Scope: Per-user data
Lifecycle: Persists across logout (for user switching support)
Storage Format: `{"userId1": data1, "userId2": data2}`
Access: Via Use Cases (GetUserSettingUseCase/SaveUserSettingUseCase)
2. App-Level Preferences
Scope: Application-wide settings
Lifecycle: Persists across all sessions and users
Storage Format: Direct key-value pairs
Access: Direct repository access (no Use Cases needed)
3. Instance-Level Preferences
Scope: OpenProject server configuration
Lifecycle: Persists until instance is changed
Storage Format: Direct key-value pairs
Access: Via InstanceConfigurationRepository
4. Legacy Preferences
Scope: Deprecated keys pending migration
Lifecycle: Various
Status: Should not be used in new code
Preference Keys Reference
Multi-User Preferences
Key |
Type |
Description |
Used In |
|---|---|---|---|
|
BottomBarConfiguration |
Bottom navigation bar customization per user |
BottomBarConfigurationRepository |
|
Timer |
Active time tracking timers per user |
TimeEntriesRepository |
|
WorkPackageSettings |
Work package display preferences (sorting, filtering, activities display) |
SettingsRepository |
|
LocalNotificationsSettings |
Local notification preferences (enabled/disabled, participants, weekdays) |
SettingsRepository |
|
HomeDashboardWidgets |
Home dashboard widget configuration and visibility |
SettingsRepository |
|
bool |
Push notification toggle per user |
PreferencesStorage (direct) |
|
int (timestamp) |
Last project sync timestamp for incremental sync |
ProjectsSyncPreferences |
|
int |
Default launch page index (which tab opens on startup) |
SettingsRepository |
App-Level Preferences
Key |
Type |
Description |
Used In |
|---|---|---|---|
|
String (ThemeMode) |
App theme mode: light, dark, or system |
SettingsRepository |
|
String |
User's selected language code (e.g., "en", "de") |
LocaleRepository |
|
DeveloperSettings |
Developer mode settings and feature toggles |
SettingsRepository |
|
FeatureFlags |
Feature flags configuration (experimental features) |
SettingsRepository |
Instance-Level Preferences
Key |
Type |
Description |
Used In |
|---|---|---|---|
|
String |
OpenProject instance base URL |
InstanceConfigurationRepository |
|
String |
OAuth client ID for the instance |
InstanceConfigurationRepository |
|
Map<String, String> |
Map of instance URLs to client IDs for multi-instance support |
InstanceConfigurationRepository |
Legacy Preferences (need to be reviewed)
Key |
Type |
Description |
Status |
|---|---|---|---|
|
List<int> |
IDs of unread notifications |
Used in background service, needs migration |
Naming Convention
Follow the naming convention for new keys:
- Multi-user: multi_user_<descriptive_name>
- App-level: app_<descriptive_name>
- Instance-level: instance_<descriptive_name>