// 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 entries; final Map> previewPointsByActivityId; const ActivityEntriesLoadResult({ required this.entries, required this.previewPointsByActivityId, }); } class ActivityGroupsLoadResult { final List groups; final Map> previewPointsByActivityId; const ActivityGroupsLoadResult({ required this.groups, required this.previewPointsByActivityId, }); } class ActivityDataLoader { const ActivityDataLoader._(); static Future 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 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().toSet(); final previewPoints = await localMediaDb.loadActivityPreviewPointsByActivityIds(ids); return ActivityEntriesLoadResult( entries: entries, previewPointsByActivityId: previewPoints, ); } static Future> loadSportGroups() async { final activities = await localMediaDb.loadActivities(); final groups = ActivityGrouping.buildSportGroups(activities); final ids = groups.map((group) => group.coverEntry?.id).whereType().toSet(); final previewPoints = await localMediaDb.loadActivityPreviewPointsByActivityIds(ids); return ActivityGroupsLoadResult( groups: groups, previewPointsByActivityId: previewPoints, ); } static Future> 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().toSet(); final previewPoints = await localMediaDb.loadActivityPreviewPointsByActivityIds(ids); return ActivityGroupsLoadResult( groups: groups, previewPointsByActivityId: previewPoints, ); } final activitiesById = {}; 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().toSet(); final previewPoints = await localMediaDb.loadActivityPreviewPointsByActivityIds(ids); return ActivityGroupsLoadResult( groups: groups, previewPointsByActivityId: previewPoints, ); } static ActivityEntry? _coverEntryForFolder({ required FolderStats folder, required Iterable activities, required Map 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; } }