577 lines
9.3 KiB
Markdown
577 lines
9.3 KiB
Markdown
Sì Fabio.
|
||
`lib/widgets/activity/activity_data_loader.dart` è un **helper di caricamento dati** per la pagina Activity.
|
||
|
||
In pratica serve a togliere da `activity_files_page.dart` tutta la logica ripetitiva di:
|
||
|
||
```text
|
||
1. leggere attività dal DB
|
||
2. applicare filtri
|
||
3. caricare preview points
|
||
4. costruire gruppi Sport
|
||
5. costruire gruppi Album
|
||
6. restituire un risultato pronto per la UI
|
||
```
|
||
|
||
***
|
||
|
||
## Perché lo abbiamo creato
|
||
|
||
Prima `activity_files_page.dart` faceva direttamente cose tipo:
|
||
|
||
```dart
|
||
final activities = await localMediaDb.loadActivities(...);
|
||
final ids = activities.map((entry) => entry.id).whereType<int>().toSet();
|
||
final previewPoints = await localMediaDb.loadActivityPreviewPointsByActivityIds(ids);
|
||
```
|
||
|
||
e anche:
|
||
|
||
```dart
|
||
final groups = ActivityGrouping.buildSportGroups(activities);
|
||
```
|
||
|
||
oppure:
|
||
|
||
```dart
|
||
final groups = ActivityGrouping.buildAlbumGroups(activities);
|
||
```
|
||
|
||
Ora invece quella logica sta dentro `ActivityDataLoader`, così `activity_files_page.dart` resta più pulito.
|
||
|
||
***
|
||
|
||
# Cosa contiene `activity_data_loader.dart`
|
||
|
||
Il file contiene tre classi principali:
|
||
|
||
```dart
|
||
ActivityEntriesLoadResult
|
||
ActivityGroupsLoadResult<T>
|
||
ActivityDataLoader
|
||
```
|
||
|
||
***
|
||
|
||
## 1. `ActivityEntriesLoadResult`
|
||
|
||
Serve come contenitore per il risultato del caricamento della lista attività.
|
||
|
||
Contiene:
|
||
|
||
```dart
|
||
final List<ActivityEntry> entries;
|
||
final Map<int, List<ActivityPreviewPoint>> previewPointsByActivityId;
|
||
```
|
||
|
||
Quindi quando carichi la lista attività, il risultato contiene:
|
||
|
||
```text
|
||
entries
|
||
lista delle attività DB da mostrare
|
||
|
||
previewPointsByActivityId
|
||
mappa id attività → punti preview per disegnare la miniatura
|
||
```
|
||
|
||
Esempio concettuale:
|
||
|
||
```dart
|
||
ActivityEntriesLoadResult(
|
||
entries: [activity1, activity2, activity3],
|
||
previewPointsByActivityId: {
|
||
1: [point1, point2, point3],
|
||
2: [point1, point2, point3],
|
||
},
|
||
)
|
||
```
|
||
|
||
***
|
||
|
||
## 2. `ActivityGroupsLoadResult<T>`
|
||
|
||
Serve come contenitore generico per i gruppi.
|
||
|
||
È generico perché può contenere:
|
||
|
||
```dart
|
||
ActivitySportGroup
|
||
```
|
||
|
||
oppure:
|
||
|
||
```dart
|
||
ActivityAlbumGroup
|
||
```
|
||
|
||
Contiene:
|
||
|
||
```dart
|
||
final List<T> groups;
|
||
final Map<int, List<ActivityPreviewPoint>> previewPointsByActivityId;
|
||
```
|
||
|
||
Quindi per Sport restituisce:
|
||
|
||
```text
|
||
groups = lista di ActivitySportGroup
|
||
previewPointsByActivityId = preview della cover di ogni sport
|
||
```
|
||
|
||
Per Album restituisce:
|
||
|
||
```text
|
||
groups = lista di ActivityAlbumGroup
|
||
previewPointsByActivityId = preview della cover di ogni album
|
||
```
|
||
|
||
***
|
||
|
||
# 3. `ActivityDataLoader`
|
||
|
||
È la classe utility vera e propria.
|
||
|
||
Ha solo metodi statici, quindi non la istanzi:
|
||
|
||
```dart
|
||
ActivityDataLoader.loadEntries(...)
|
||
ActivityDataLoader.loadSportGroups()
|
||
ActivityDataLoader.loadAlbumGroups()
|
||
```
|
||
|
||
***
|
||
|
||
## Metodo 1 — `loadEntries(...)`
|
||
|
||
Firma:
|
||
|
||
```dart
|
||
static Future<ActivityEntriesLoadResult> loadEntries({
|
||
required bool favouriteOnly,
|
||
required bool withoutSportOnly,
|
||
String? sport,
|
||
String? directory,
|
||
})
|
||
```
|
||
|
||
Questo metodo carica la lista attività normale.
|
||
|
||
Viene usato da `activity_files_page.dart` dentro `_loadDbActivities()`.
|
||
|
||
### Cosa fa passo per passo
|
||
|
||
### 1. Legge attività dal DB
|
||
|
||
Chiama:
|
||
|
||
```dart
|
||
localMediaDb.loadActivities(
|
||
sport: withoutSportOnly ? null : sport,
|
||
directory: directory,
|
||
favourite: favouriteOnly ? true : null,
|
||
);
|
||
```
|
||
|
||
Quindi applica questi filtri:
|
||
|
||
```text
|
||
favouriteOnly → solo preferiti
|
||
sport → solo uno sport specifico
|
||
directory → solo un album/cartella specifica
|
||
withoutSportOnly → caso speciale per attività senza sport
|
||
```
|
||
|
||
***
|
||
|
||
### 2. Gestisce il caso “Senza sport”
|
||
|
||
Questo caso è speciale.
|
||
|
||
Perché:
|
||
|
||
```dart
|
||
sport: null
|
||
```
|
||
|
||
di solito vuol dire:
|
||
|
||
```text
|
||
non filtrare per sport
|
||
```
|
||
|
||
non significa:
|
||
|
||
```text
|
||
dammi solo attività senza sport
|
||
```
|
||
|
||
Quindi il file fa un filtro manuale:
|
||
|
||
```dart
|
||
if (withoutSportOnly) {
|
||
activities = activities.where((entry) {
|
||
final sport = entry.sport?.trim();
|
||
return sport == null || sport.isEmpty;
|
||
});
|
||
}
|
||
```
|
||
|
||
Così quando tocchi la card **Senza sport**, mostra solo attività con:
|
||
|
||
```text
|
||
sport null
|
||
```
|
||
|
||
oppure:
|
||
|
||
```text
|
||
sport vuoto
|
||
```
|
||
|
||
***
|
||
|
||
### 3. Converte in lista
|
||
|
||
```dart
|
||
final entries = activities.toList();
|
||
```
|
||
|
||
Questo prepara i dati per la UI.
|
||
|
||
***
|
||
|
||
### 4. Carica i preview points
|
||
|
||
Prende gli ID attività:
|
||
|
||
```dart
|
||
final ids = entries
|
||
.map((entry) => entry.id)
|
||
.whereType<int>()
|
||
.toSet();
|
||
```
|
||
|
||
Poi carica le miniature:
|
||
|
||
```dart
|
||
final previewPoints =
|
||
await localMediaDb.loadActivityPreviewPointsByActivityIds(ids);
|
||
```
|
||
|
||
***
|
||
|
||
### 5. Ritorna tutto insieme
|
||
|
||
```dart
|
||
return ActivityEntriesLoadResult(
|
||
entries: entries,
|
||
previewPointsByActivityId: previewPoints,
|
||
);
|
||
```
|
||
|
||
Quindi `activity_files_page.dart` riceve già:
|
||
|
||
```text
|
||
lista attività + preview pronte
|
||
```
|
||
|
||
***
|
||
|
||
## Metodo 2 — `loadSportGroups()`
|
||
|
||
Firma:
|
||
|
||
```dart
|
||
static Future<ActivityGroupsLoadResult<ActivitySportGroup>> loadSportGroups()
|
||
```
|
||
|
||
Viene usato quando tocchi il tab **Sport**.
|
||
|
||
### Cosa fa
|
||
|
||
### 1. Carica tutte le attività
|
||
|
||
```dart
|
||
final activities = await localMediaDb.loadActivities();
|
||
```
|
||
|
||
Qui non applica filtri, perché deve costruire tutti i gruppi sport.
|
||
|
||
***
|
||
|
||
### 2. Costruisce i gruppi sport
|
||
|
||
Chiama:
|
||
|
||
```dart
|
||
final groups = ActivityGrouping.buildSportGroups(activities);
|
||
```
|
||
|
||
Questa logica sta in:
|
||
|
||
```text
|
||
lib/model/activity/activity_grouping.dart
|
||
```
|
||
|
||
e produce una lista tipo:
|
||
|
||
```text
|
||
Corsa 12 attività
|
||
Bici 8 attività
|
||
Camminata 3 attività
|
||
Senza sport 1 attività
|
||
```
|
||
|
||
Ogni gruppo ha:
|
||
|
||
```dart
|
||
displayName
|
||
sportFilter
|
||
withoutSport
|
||
count
|
||
coverEntry
|
||
```
|
||
|
||
***
|
||
|
||
### 3. Carica preview della cover
|
||
|
||
Per ogni gruppo prende la `coverEntry`, cioè l’attività usata come thumbnail:
|
||
|
||
```dart
|
||
final ids = groups
|
||
.map((group) => group.coverEntry?.id)
|
||
.whereType<int>()
|
||
.toSet();
|
||
```
|
||
|
||
Poi carica preview points:
|
||
|
||
```dart
|
||
final previewPoints =
|
||
await localMediaDb.loadActivityPreviewPointsByActivityIds(ids);
|
||
```
|
||
|
||
***
|
||
|
||
### 4. Ritorna gruppi + preview
|
||
|
||
```dart
|
||
return ActivityGroupsLoadResult<ActivitySportGroup>(
|
||
groups: groups,
|
||
previewPointsByActivityId: previewPoints,
|
||
);
|
||
```
|
||
|
||
***
|
||
|
||
## Metodo 3 — `loadAlbumGroups()`
|
||
|
||
Firma:
|
||
|
||
```dart
|
||
static Future<ActivityGroupsLoadResult<ActivityAlbumGroup>> loadAlbumGroups()
|
||
```
|
||
|
||
Viene usato quando tocchi il tab **Album**.
|
||
|
||
### Cosa fa
|
||
|
||
### 1. Carica tutte le attività
|
||
|
||
```dart
|
||
final activities = await localMediaDb.loadActivities();
|
||
```
|
||
|
||
***
|
||
|
||
### 2. Costruisce i gruppi album
|
||
|
||
Chiama:
|
||
|
||
```dart
|
||
final groups = ActivityGrouping.buildAlbumGroups(activities);
|
||
```
|
||
|
||
Questa logica crea gruppi usando:
|
||
|
||
```dart
|
||
entry.directoryName
|
||
entry.relativePath
|
||
```
|
||
|
||
Quindi produce card tipo:
|
||
|
||
```text
|
||
Activity
|
||
main
|
||
Garmin
|
||
Zepp
|
||
Strava
|
||
```
|
||
|
||
Ogni gruppo ha:
|
||
|
||
```dart
|
||
displayName
|
||
directoryFilter
|
||
count
|
||
coverEntry
|
||
```
|
||
|
||
***
|
||
|
||
### 3. Carica preview della cover
|
||
|
||
Come per gli sport:
|
||
|
||
```dart
|
||
final ids = groups
|
||
.map((group) => group.coverEntry?.id)
|
||
.whereType<int>()
|
||
.toSet();
|
||
```
|
||
|
||
Poi:
|
||
|
||
```dart
|
||
final previewPoints =
|
||
await localMediaDb.loadActivityPreviewPointsByActivityIds(ids);
|
||
```
|
||
|
||
***
|
||
|
||
### 4. Ritorna gruppi + preview
|
||
|
||
```dart
|
||
return ActivityGroupsLoadResult<ActivityAlbumGroup>(
|
||
groups: groups,
|
||
previewPointsByActivityId: previewPoints,
|
||
);
|
||
```
|
||
|
||
***
|
||
|
||
# Come viene usato in `activity_files_page.dart`
|
||
|
||
Ora `_loadDbActivities()` è molto più pulito:
|
||
|
||
```dart
|
||
final result = await ActivityDataLoader.loadEntries(
|
||
favouriteOnly: _activityFavouriteOnly,
|
||
withoutSportOnly: _activityWithoutSportOnly,
|
||
sport: _activitySportFilter,
|
||
directory: _activityDirectoryFilter,
|
||
);
|
||
```
|
||
|
||
Poi aggiorna lo state:
|
||
|
||
```dart
|
||
_dbActivities
|
||
..clear()
|
||
..addAll(result.entries);
|
||
|
||
_dbPreviewPoints
|
||
..clear()
|
||
..addAll(result.previewPointsByActivityId);
|
||
```
|
||
|
||
***
|
||
|
||
Per Sport:
|
||
|
||
```dart
|
||
final result = await ActivityDataLoader.loadSportGroups();
|
||
```
|
||
|
||
Poi:
|
||
|
||
```dart
|
||
_sportGroups
|
||
..clear()
|
||
..addAll(result.groups);
|
||
|
||
_dbPreviewPoints
|
||
..clear()
|
||
..addAll(result.previewPointsByActivityId);
|
||
```
|
||
|
||
***
|
||
|
||
Per Album:
|
||
|
||
```dart
|
||
final result = await ActivityDataLoader.loadAlbumGroups();
|
||
```
|
||
|
||
Poi:
|
||
|
||
```dart
|
||
_albumGroups
|
||
..clear()
|
||
..addAll(result.groups);
|
||
|
||
_dbPreviewPoints
|
||
..clear()
|
||
..addAll(result.previewPointsByActivityId);
|
||
```
|
||
|
||
***
|
||
|
||
# In sintesi breve
|
||
|
||
`activity_data_loader.dart` è il file che dice:
|
||
|
||
```text
|
||
"Dammi i dati pronti da mostrare nella UI Activity"
|
||
```
|
||
|
||
Fa da ponte tra:
|
||
|
||
```text
|
||
DB / grouping / preview points
|
||
```
|
||
|
||
e:
|
||
|
||
```text
|
||
ActivityFilesPage / UI
|
||
```
|
||
|
||
***
|
||
|
||
## Responsabilità attuale
|
||
|
||
| Metodo | Cosa carica | Usato da |
|
||
| ------------------- | --------------------------------- | ------------------------------------------------------------- |
|
||
| `loadEntries(...)` | Lista attività filtrata + preview | tab Attività, Preferiti, Sport selezionato, Album selezionato |
|
||
| `loadSportGroups()` | Gruppi sport + cover preview | tab Sport |
|
||
| `loadAlbumGroups()` | Gruppi album + cover preview | tab Album |
|
||
|
||
***
|
||
|
||
## Perché è utile
|
||
|
||
Perché adesso `activity_files_page.dart` non deve più sapere **come**:
|
||
|
||
```text
|
||
caricare attività
|
||
filtrare senza sport
|
||
costruire gruppi
|
||
recuperare preview
|
||
```
|
||
|
||
Sa solo:
|
||
|
||
```dart
|
||
final result = await ActivityDataLoader.loadEntries(...);
|
||
```
|
||
|
||
Quindi sarà più facile in futuro:
|
||
|
||
* aggiungere ordinamenti;
|
||
* aggiungere statistiche per gruppo;
|
||
* aggiungere cache;
|
||
* aggiungere paginazione;
|
||
* cambiare logica DB;
|
||
* trasformare la lista in griglia senza toccare il caricamento dati.
|