mirror of
https://github.com/immich-app/immich.git
synced 2026-01-14 22:13:14 -08:00
feat: add error section
This commit is contained in:
@@ -1122,6 +1122,7 @@
|
||||
"unable_to_update_workflow": "Unable to update workflow",
|
||||
"unable_to_upload_file": "Unable to upload file"
|
||||
},
|
||||
"errors_text": "Errors",
|
||||
"exclusion_pattern": "Exclusion pattern",
|
||||
"exif": "Exif",
|
||||
"exif_bottom_sheet_description": "Add Description...",
|
||||
|
||||
@@ -28,17 +28,14 @@ class _DriftUploadDetailPageState extends ConsumerState<DriftUploadDetailPage> {
|
||||
final uploadItems = ref.watch(driftBackupProvider.select((state) => state.uploadItems));
|
||||
final iCloudProgress = ref.watch(driftBackupProvider.select((state) => state.iCloudDownloadProgress));
|
||||
|
||||
// Track failed items to exclude them from completed list
|
||||
for (final item in uploadItems.values) {
|
||||
if (item.isFailed == true) {
|
||||
_failedTaskIds.add(item.taskId);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove any items from completed list that have since failed
|
||||
_completedItems.removeWhere((item) => _failedTaskIds.contains(item.taskId));
|
||||
|
||||
// Watch for items with progress >= 1.0 (completed successfully) before they get removed
|
||||
for (final item in uploadItems.values) {
|
||||
if (item.progress >= 1.0 && item.isFailed != true && !_failedTaskIds.contains(item.taskId)) {
|
||||
if (!_seenTaskIds.contains(item.taskId)) {
|
||||
@@ -51,10 +48,11 @@ class _DriftUploadDetailPageState extends ConsumerState<DriftUploadDetailPage> {
|
||||
}
|
||||
}
|
||||
|
||||
// Get current uploading items (progress < 1.0 or failed)
|
||||
final currentUploads = uploadItems.values.where((item) => item.progress < 1.0 || item.isFailed == true).toList();
|
||||
final uploadingItems = uploadItems.values.where((item) => item.progress < 1.0 && item.isFailed != true).toList();
|
||||
final failedItems = uploadItems.values.where((item) => item.isFailed == true).toList();
|
||||
|
||||
final hasContent = currentUploads.isNotEmpty || iCloudProgress.isNotEmpty || _completedItems.isNotEmpty;
|
||||
final hasContent =
|
||||
uploadingItems.isNotEmpty || failedItems.isNotEmpty || iCloudProgress.isNotEmpty || _completedItems.isNotEmpty;
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
@@ -79,7 +77,7 @@ class _DriftUploadDetailPageState extends ConsumerState<DriftUploadDetailPage> {
|
||||
),
|
||||
body: !hasContent
|
||||
? _buildEmptyState(context)
|
||||
: _buildTwoSectionLayout(context, currentUploads, iCloudProgress, _completedItems),
|
||||
: _buildTwoSectionLayout(context, uploadingItems, failedItems, iCloudProgress, _completedItems),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -101,7 +99,8 @@ class _DriftUploadDetailPageState extends ConsumerState<DriftUploadDetailPage> {
|
||||
|
||||
Widget _buildTwoSectionLayout(
|
||||
BuildContext context,
|
||||
List<DriftUploadStatus> currentUploads,
|
||||
List<DriftUploadStatus> uploadingItems,
|
||||
List<DriftUploadStatus> failedItems,
|
||||
Map<String, double> iCloudProgress,
|
||||
List<DriftUploadStatus> completedItems,
|
||||
) {
|
||||
@@ -131,10 +130,12 @@ class _DriftUploadDetailPageState extends ConsumerState<DriftUploadDetailPage> {
|
||||
),
|
||||
],
|
||||
|
||||
// Uploading Section
|
||||
SliverToBoxAdapter(
|
||||
child: _buildSectionHeader(
|
||||
context,
|
||||
title: "uploading".t(context: context),
|
||||
count: uploadingItems.length,
|
||||
color: context.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
@@ -142,8 +143,8 @@ class _DriftUploadDetailPageState extends ConsumerState<DriftUploadDetailPage> {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
sliver: SliverList(
|
||||
delegate: SliverChildBuilderDelegate((context, index) {
|
||||
if (index < currentUploads.length) {
|
||||
final item = currentUploads[index];
|
||||
if (index < uploadingItems.length) {
|
||||
final item = uploadingItems[index];
|
||||
return _buildCurrentUploadCard(context, item);
|
||||
} else {
|
||||
return _buildPlaceholderCard(context);
|
||||
@@ -152,6 +153,27 @@ class _DriftUploadDetailPageState extends ConsumerState<DriftUploadDetailPage> {
|
||||
),
|
||||
),
|
||||
|
||||
// Errors Section
|
||||
if (failedItems.isNotEmpty) ...[
|
||||
SliverToBoxAdapter(
|
||||
child: _buildSectionHeader(
|
||||
context,
|
||||
title: "errors_text".t(context: context),
|
||||
count: failedItems.length,
|
||||
color: context.colorScheme.error,
|
||||
),
|
||||
),
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
sliver: SliverList(
|
||||
delegate: SliverChildBuilderDelegate((context, index) {
|
||||
final item = failedItems[index];
|
||||
return Padding(padding: const EdgeInsets.only(bottom: 8), child: _buildErrorCard(context, item));
|
||||
}, childCount: failedItems.length),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
// Completed Section
|
||||
if (completedItems.isNotEmpty) ...[
|
||||
SliverToBoxAdapter(
|
||||
@@ -409,6 +431,53 @@ class _DriftUploadDetailPageState extends ConsumerState<DriftUploadDetailPage> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildErrorCard(BuildContext context, DriftUploadStatus item) {
|
||||
return Card(
|
||||
elevation: 0,
|
||||
color: context.colorScheme.errorContainer,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
side: BorderSide(color: context.colorScheme.error.withValues(alpha: 0.3), width: 1),
|
||||
),
|
||||
child: InkWell(
|
||||
onTap: () => _showFileDetailDialog(context, item),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
children: [
|
||||
_CurrentUploadThumbnail(taskId: item.taskId),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
path.basename(item.filename),
|
||||
style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
item.error ?? "Upload failed",
|
||||
style: context.textTheme.bodySmall?.copyWith(color: context.colorScheme.error),
|
||||
maxLines: 4,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Icon(Icons.error_rounded, color: context.colorScheme.error, size: 28),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPlaceholderCard(BuildContext context) {
|
||||
return Card(
|
||||
elevation: 0,
|
||||
|
||||
@@ -237,6 +237,9 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
|
||||
if (_ref.read(backupProvider.notifier).backupProgress != BackUpProgressEnum.manualInProgress) {
|
||||
_ref.read(backupProvider.notifier).cancelBackup();
|
||||
}
|
||||
} else {
|
||||
// Cancel foreground upload when app goes to background
|
||||
await _ref.read(driftBackupProvider.notifier).stopForegroundBackup();
|
||||
}
|
||||
|
||||
_ref.read(websocketProvider.notifier).disconnect();
|
||||
|
||||
Reference in New Issue
Block a user