217 lines
6.3 KiB
Dart
217 lines
6.3 KiB
Dart
// lib/widgets/activity/activity_data_loader.dart
|
|
|
|
import 'package:aves/model/activity/activity_entry.dart';
|
|
import 'package:aves/model/activity/activity_grouping.dart';
|
|
import 'package:aves/model/activity/activity_preview_point.dart';
|
|
import 'package:aves/model/db/folder_content_type.dart';
|
|
import 'package:aves/model/db/folder_stats.dart';
|
|
import 'package:aves/services/common/services.dart';
|
|
|
|
class ActivityEntriesLoadResult {
|
|
final List<ActivityEntry> entries;
|
|
final Map<int, List<ActivityPreviewPoint>> previewPointsByActivityId;
|
|
|
|
const ActivityEntriesLoadResult({
|
|
required this.entries,
|
|
required this.previewPointsByActivityId,
|
|
});
|
|
}
|
|
|
|
class ActivityGroupsLoadResult<T> {
|
|
final List<T> groups;
|
|
final Map<int, List<ActivityPreviewPoint>> previewPointsByActivityId;
|
|
|
|
const ActivityGroupsLoadResult({
|
|
required this.groups,
|
|
required this.previewPointsByActivityId,
|
|
});
|
|
}
|
|
|
|
class ActivityDataLoader {
|
|
const ActivityDataLoader._();
|
|
|
|
static Future<ActivityEntriesLoadResult> loadEntries({
|
|
required bool favouriteOnly,
|
|
required bool withoutSportOnly,
|
|
String? sport,
|
|
String? directory,
|
|
}) async {
|
|
final loadedActivities = await localMediaDb.loadActivities(
|
|
sport: withoutSportOnly ? null : sport,
|
|
directory: directory,
|
|
favourite: favouriteOnly ? true : null,
|
|
);
|
|
|
|
Iterable<ActivityEntry> activities = loadedActivities;
|
|
|
|
if (withoutSportOnly) {
|
|
activities = activities.where((entry) {
|
|
final sport = entry.sport?.trim();
|
|
return sport == null || sport.isEmpty;
|
|
});
|
|
}
|
|
|
|
final entries = activities.toList();
|
|
|
|
final ids = entries.map((entry) => entry.id).whereType<int>().toSet();
|
|
|
|
final previewPoints = await localMediaDb.loadActivityPreviewPointsByActivityIds(ids);
|
|
|
|
return ActivityEntriesLoadResult(
|
|
entries: entries,
|
|
previewPointsByActivityId: previewPoints,
|
|
);
|
|
}
|
|
|
|
static Future<ActivityGroupsLoadResult<ActivitySportGroup>> loadSportGroups() async {
|
|
final activities = await localMediaDb.loadActivities();
|
|
final groups = ActivityGrouping.buildSportGroups(activities);
|
|
|
|
final ids = groups.map((group) => group.coverEntry?.id).whereType<int>().toSet();
|
|
|
|
final previewPoints = await localMediaDb.loadActivityPreviewPointsByActivityIds(ids);
|
|
|
|
return ActivityGroupsLoadResult<ActivitySportGroup>(
|
|
groups: groups,
|
|
previewPointsByActivityId: previewPoints,
|
|
);
|
|
}
|
|
|
|
static Future<ActivityGroupsLoadResult<ActivityAlbumGroup>> loadAlbumGroups() async {
|
|
final folders = await localMediaDb.loadFolders(
|
|
contentType: FolderContentType.activity,
|
|
);
|
|
|
|
final activities = await localMediaDb.loadActivities();
|
|
|
|
// Fallback utile finché non hai fatto una nuova scansione dopo
|
|
// l'introduzione di folders.content_type = activity.
|
|
if (folders.isEmpty) {
|
|
final groups = ActivityGrouping.buildAlbumGroups(activities);
|
|
|
|
final ids = groups.map((group) => group.coverEntry?.id).whereType<int>().toSet();
|
|
|
|
final previewPoints = await localMediaDb.loadActivityPreviewPointsByActivityIds(ids);
|
|
|
|
return ActivityGroupsLoadResult<ActivityAlbumGroup>(
|
|
groups: groups,
|
|
previewPointsByActivityId: previewPoints,
|
|
);
|
|
}
|
|
|
|
final activitiesById = <int, ActivityEntry>{};
|
|
|
|
for (final entry in activities) {
|
|
final id = entry.id;
|
|
if (id != null) {
|
|
activitiesById[id] = entry;
|
|
}
|
|
}
|
|
|
|
final groups =
|
|
folders.where((folder) => folder.hidden == 0).map((folder) {
|
|
final coverEntry = _coverEntryForFolder(
|
|
folder: folder,
|
|
activities: activities,
|
|
activitiesById: activitiesById,
|
|
);
|
|
|
|
return ActivityAlbumGroup(
|
|
displayName: _folderDisplayName(folder),
|
|
directoryFilter: folder.path,
|
|
count: folder.fileCount,
|
|
coverEntry: coverEntry,
|
|
);
|
|
}).toList()..sort(
|
|
(a, b) => a.displayName.toLowerCase().compareTo(
|
|
b.displayName.toLowerCase(),
|
|
),
|
|
);
|
|
|
|
final ids = groups.map((group) => group.coverEntry?.id).whereType<int>().toSet();
|
|
|
|
final previewPoints = await localMediaDb.loadActivityPreviewPointsByActivityIds(ids);
|
|
|
|
return ActivityGroupsLoadResult<ActivityAlbumGroup>(
|
|
groups: groups,
|
|
previewPointsByActivityId: previewPoints,
|
|
);
|
|
}
|
|
|
|
static ActivityEntry? _coverEntryForFolder({
|
|
required FolderStats folder,
|
|
required Iterable<ActivityEntry> activities,
|
|
required Map<int, ActivityEntry> activitiesById,
|
|
}) {
|
|
final thumbEntryId = folder.thumbEntryId;
|
|
|
|
if (thumbEntryId != null) {
|
|
final entry = activitiesById[thumbEntryId];
|
|
if (entry != null) return entry;
|
|
}
|
|
|
|
final normalizedFolderPath = _normalizePath(folder.path);
|
|
|
|
final matching =
|
|
activities.where((entry) {
|
|
return _folderPathForEntry(entry) == normalizedFolderPath;
|
|
}).toList()..sort((a, b) {
|
|
final aDate = a.startTimeMillis ?? a.dateModifiedMillis;
|
|
final bDate = b.startTimeMillis ?? b.dateModifiedMillis;
|
|
return bDate.compareTo(aDate);
|
|
});
|
|
|
|
if (matching.isEmpty) return null;
|
|
|
|
return matching.first;
|
|
}
|
|
|
|
static String _folderDisplayName(FolderStats folder) {
|
|
final displayName = folder.displayName.trim();
|
|
|
|
if (displayName.isNotEmpty) {
|
|
return displayName;
|
|
}
|
|
|
|
return _displayNameForPath(folder.path);
|
|
}
|
|
|
|
static String _folderPathForEntry(ActivityEntry entry) {
|
|
final relativePath = entry.relativePath?.trim();
|
|
|
|
if (relativePath != null && relativePath.isNotEmpty) {
|
|
return _normalizePath(relativePath);
|
|
}
|
|
|
|
final directoryName = entry.directoryName?.trim();
|
|
|
|
if (directoryName != null && directoryName.isNotEmpty) {
|
|
return _normalizePath(directoryName);
|
|
}
|
|
|
|
return 'Activity';
|
|
}
|
|
|
|
static String _normalizePath(String path) {
|
|
var normalized = path.replaceAll('\\', '/').trim();
|
|
|
|
while (normalized.contains('//')) {
|
|
normalized = normalized.replaceAll('//', '/');
|
|
}
|
|
|
|
if (normalized.endsWith('/')) {
|
|
normalized = normalized.substring(0, normalized.length - 1);
|
|
}
|
|
|
|
return normalized.isEmpty ? 'Activity' : normalized;
|
|
}
|
|
|
|
static String _displayNameForPath(String path) {
|
|
final normalized = _normalizePath(path);
|
|
final parts = normalized.split('/').where((part) => part.isNotEmpty);
|
|
|
|
if (parts.isEmpty) return 'Activity';
|
|
|
|
return parts.last;
|
|
}
|
|
}
|