mirror of
https://github.com/immich-app/immich.git
synced 2025-07-07 09:12:56 +02:00
Add information for uploading asset and error indication with error message for each failed upload. (#315)
* Added info box * Fixed upload endpoint doesn't report error status code * Added chip to show update error * Added chip to show failed upload * Add duplication check for upload * Better duplication-checking placement * Remove check for duplicated asset * Added failed backup status route * added page * Display error card with thumbnail * Improved styling * Set thumbnail with better quality * Remove force upload error
This commit is contained in:
parent
357f7d1c31
commit
58ec7553ea
19 changed files with 706 additions and 87 deletions
mobile/lib/modules/backup/views
|
@ -2,6 +2,7 @@ import 'package:auto_route/auto_route.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/backup/providers/error_backup_list.provider.dart';
|
||||
import 'package:immich_mobile/modules/login/models/authentication_state.model.dart';
|
||||
import 'package:immich_mobile/modules/backup/models/backup_state.model.dart';
|
||||
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
|
||||
|
@ -9,6 +10,7 @@ import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
|
|||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/shared/providers/websocket.provider.dart';
|
||||
import 'package:immich_mobile/modules/backup/ui/backup_info_card.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:percent_indicator/linear_percent_indicator.dart';
|
||||
|
||||
class BackupControllerPage extends HookConsumerWidget {
|
||||
|
@ -42,7 +44,7 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
title: const Text(
|
||||
"Server Storage",
|
||||
"Server storage",
|
||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
|
||||
),
|
||||
subtitle: Padding(
|
||||
|
@ -56,7 +58,7 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 0, vertical: 0),
|
||||
barRadius: const Radius.circular(2),
|
||||
lineHeight: 6.0,
|
||||
lineHeight: 10.0,
|
||||
percent: backupState.serverInfo.diskUsagePercentage / 100.0,
|
||||
backgroundColor: Colors.grey,
|
||||
progressColor: Theme.of(context).primaryColor,
|
||||
|
@ -246,6 +248,141 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||
);
|
||||
}
|
||||
|
||||
_buildCurrentBackupAssetInfoCard() {
|
||||
return ListTile(
|
||||
leading: Icon(
|
||||
Icons.info_outline_rounded,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
title: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(
|
||||
"Uploading file info",
|
||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
|
||||
),
|
||||
if (ref.watch(errorBackupListProvider).isNotEmpty)
|
||||
ActionChip(
|
||||
avatar: Icon(
|
||||
Icons.info,
|
||||
size: 24,
|
||||
color: Colors.red[400],
|
||||
),
|
||||
elevation: 1,
|
||||
visualDensity: VisualDensity.compact,
|
||||
label: Text(
|
||||
"Failed (${ref.watch(errorBackupListProvider).length})",
|
||||
style: TextStyle(
|
||||
color: Colors.red[400],
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 11,
|
||||
),
|
||||
),
|
||||
backgroundColor: Colors.white,
|
||||
onPressed: () {
|
||||
AutoRouter.of(context).push(const FailedBackupStatusRoute());
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: LinearPercentIndicator(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 0),
|
||||
barRadius: const Radius.circular(2),
|
||||
lineHeight: 10.0,
|
||||
trailing: Text(
|
||||
" ${backupState.progressInPercentage.toStringAsFixed(0)}%",
|
||||
style: const TextStyle(fontSize: 12),
|
||||
),
|
||||
percent: backupState.progressInPercentage / 100.0,
|
||||
backgroundColor: Colors.grey,
|
||||
progressColor: Theme.of(context).primaryColor,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Table(
|
||||
border: TableBorder.all(
|
||||
color: Colors.black12,
|
||||
width: 1,
|
||||
),
|
||||
children: [
|
||||
TableRow(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[100],
|
||||
),
|
||||
children: [
|
||||
TableCell(
|
||||
verticalAlignment: TableCellVerticalAlignment.middle,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(6.0),
|
||||
child: Text(
|
||||
'File name: ${backupState.currentUploadAsset.fileName} [${backupState.currentUploadAsset.fileType.toLowerCase()}]',
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold, fontSize: 10.0),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
TableRow(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[200],
|
||||
),
|
||||
children: [
|
||||
TableCell(
|
||||
verticalAlignment: TableCellVerticalAlignment.middle,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(6.0),
|
||||
child: Text(
|
||||
"Created on: ${DateFormat.yMMMMd('en_US').format(
|
||||
DateTime.parse(
|
||||
backupState.currentUploadAsset.createdAt
|
||||
.toString(),
|
||||
),
|
||||
)}",
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold, fontSize: 10.0),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
TableRow(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[100],
|
||||
),
|
||||
children: [
|
||||
TableCell(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(6.0),
|
||||
child: Text(
|
||||
"ID: ${backupState.currentUploadAsset.id}",
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 10.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void startBackup() {
|
||||
ref.watch(errorBackupListProvider.notifier).empty();
|
||||
ref.watch(backupProvider.notifier).startBackupProcess();
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
elevation: 0,
|
||||
|
@ -264,7 +401,7 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||
)),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
padding: const EdgeInsets.only(left: 16.0, right: 16, bottom: 32),
|
||||
child: ListView(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
@ -297,23 +434,11 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||
const Divider(),
|
||||
_buildStorageInformation(),
|
||||
const Divider(),
|
||||
_buildCurrentBackupAssetInfoCard(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
"Asset that were being backup: ${backupState.allUniqueAssets.length - backupState.selectedAlbumsBackupAssetsIds.length} [${backupState.progressInPercentage.toStringAsFixed(0)}%]"),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: Row(children: [
|
||||
const Text("Backup Progress:"),
|
||||
const Padding(padding: EdgeInsets.symmetric(horizontal: 2)),
|
||||
backupState.backupProgress == BackUpProgressEnum.inProgress
|
||||
? const CircularProgressIndicator.adaptive()
|
||||
: const Text("Done"),
|
||||
]),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
padding: const EdgeInsets.only(
|
||||
top: 24,
|
||||
),
|
||||
child: Container(
|
||||
child:
|
||||
backupState.backupProgress == BackUpProgressEnum.inProgress
|
||||
|
@ -321,25 +446,33 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||
style: ElevatedButton.styleFrom(
|
||||
primary: Colors.red[300],
|
||||
onPrimary: Colors.grey[50],
|
||||
padding: const EdgeInsets.all(14),
|
||||
),
|
||||
onPressed: () {
|
||||
ref.read(backupProvider.notifier).cancelBackup();
|
||||
},
|
||||
child: const Text("Cancel"),
|
||||
child: const Text(
|
||||
"CANCEL",
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
)
|
||||
: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
primary: Theme.of(context).primaryColor,
|
||||
onPrimary: Colors.grey[50],
|
||||
padding: const EdgeInsets.all(14),
|
||||
),
|
||||
onPressed: shouldBackup ? startBackup : null,
|
||||
child: const Text(
|
||||
"START BACKUP",
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
onPressed: shouldBackup
|
||||
? () {
|
||||
ref
|
||||
.read(backupProvider.notifier)
|
||||
.startBackupProcess();
|
||||
}
|
||||
: null,
|
||||
child: const Text("Start Backup"),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue