backup_controller_page.dart 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744
  1. import 'dart:io';
  2. import 'package:auto_route/auto_route.dart';
  3. import 'package:easy_localization/easy_localization.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:flutter_hooks/flutter_hooks.dart';
  6. import 'package:hooks_riverpod/hooks_riverpod.dart';
  7. import 'package:immich_mobile/modules/backup/providers/error_backup_list.provider.dart';
  8. import 'package:immich_mobile/modules/login/models/authentication_state.model.dart';
  9. import 'package:immich_mobile/modules/backup/models/backup_state.model.dart';
  10. import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
  11. import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
  12. import 'package:immich_mobile/routing/router.dart';
  13. import 'package:immich_mobile/shared/providers/websocket.provider.dart';
  14. import 'package:immich_mobile/modules/backup/ui/backup_info_card.dart';
  15. import 'package:url_launcher/url_launcher.dart';
  16. class BackupControllerPage extends HookConsumerWidget {
  17. const BackupControllerPage({Key? key}) : super(key: key);
  18. @override
  19. Widget build(BuildContext context, WidgetRef ref) {
  20. BackUpState backupState = ref.watch(backupProvider);
  21. AuthenticationState authenticationState = ref.watch(authenticationProvider);
  22. bool hasExclusiveAccess =
  23. backupState.backupProgress != BackUpProgressEnum.inBackground;
  24. bool shouldBackup = backupState.allUniqueAssets.length -
  25. backupState.selectedAlbumsBackupAssetsIds.length ==
  26. 0 ||
  27. !hasExclusiveAccess
  28. ? false
  29. : true;
  30. useEffect(
  31. () {
  32. if (backupState.backupProgress != BackUpProgressEnum.inProgress) {
  33. ref.watch(backupProvider.notifier).getBackupInfo();
  34. }
  35. ref
  36. .watch(websocketProvider.notifier)
  37. .stopListenToEvent('on_upload_success');
  38. return null;
  39. },
  40. [],
  41. );
  42. Widget buildStorageInformation() {
  43. return ListTile(
  44. leading: Icon(
  45. Icons.storage_rounded,
  46. color: Theme.of(context).primaryColor,
  47. ),
  48. title: const Text(
  49. "backup_controller_page_server_storage",
  50. style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
  51. ).tr(),
  52. subtitle: Padding(
  53. padding: const EdgeInsets.only(top: 8.0),
  54. child: Column(
  55. crossAxisAlignment: CrossAxisAlignment.start,
  56. children: [
  57. Padding(
  58. padding: const EdgeInsets.only(top: 8.0),
  59. child: LinearProgressIndicator(
  60. minHeight: 10.0,
  61. value: backupState.serverInfo.diskUsagePercentage / 100.0,
  62. backgroundColor: Colors.grey,
  63. color: Theme.of(context).primaryColor,
  64. ),
  65. ),
  66. Padding(
  67. padding: const EdgeInsets.only(top: 12.0),
  68. child: const Text('backup_controller_page_storage_format').tr(
  69. args: [
  70. backupState.serverInfo.diskUse,
  71. backupState.serverInfo.diskSize
  72. ],
  73. ),
  74. ),
  75. ],
  76. ),
  77. ),
  78. );
  79. }
  80. ListTile buildAutoBackupController() {
  81. var backUpOption = authenticationState.deviceInfo.isAutoBackup
  82. ? "backup_controller_page_status_on".tr()
  83. : "backup_controller_page_status_off".tr();
  84. var isAutoBackup = authenticationState.deviceInfo.isAutoBackup;
  85. var backupBtnText = authenticationState.deviceInfo.isAutoBackup
  86. ? "backup_controller_page_turn_off".tr()
  87. : "backup_controller_page_turn_on".tr();
  88. return ListTile(
  89. isThreeLine: true,
  90. leading: isAutoBackup
  91. ? Icon(
  92. Icons.cloud_done_rounded,
  93. color: Theme.of(context).primaryColor,
  94. )
  95. : const Icon(Icons.cloud_off_rounded),
  96. title: Text(
  97. backUpOption,
  98. style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
  99. ),
  100. subtitle: Padding(
  101. padding: const EdgeInsets.symmetric(vertical: 8.0),
  102. child: Column(
  103. crossAxisAlignment: CrossAxisAlignment.start,
  104. children: [
  105. if (!isAutoBackup)
  106. const Text(
  107. "backup_controller_page_desc_backup",
  108. style: TextStyle(fontSize: 14),
  109. ).tr(),
  110. Padding(
  111. padding: const EdgeInsets.only(top: 8.0),
  112. child: ElevatedButton(
  113. onPressed: () {
  114. if (isAutoBackup) {
  115. ref
  116. .read(authenticationProvider.notifier)
  117. .setAutoBackup(false);
  118. } else {
  119. ref
  120. .read(authenticationProvider.notifier)
  121. .setAutoBackup(true);
  122. }
  123. },
  124. child: Text(
  125. backupBtnText,
  126. style: const TextStyle(
  127. fontWeight: FontWeight.bold,
  128. fontSize: 12,
  129. ),
  130. ),
  131. ),
  132. )
  133. ],
  134. ),
  135. ),
  136. );
  137. }
  138. void showErrorToUser(String msg) {
  139. final snackBar = SnackBar(
  140. content: Text(
  141. msg.tr(),
  142. ),
  143. backgroundColor: Colors.red,
  144. );
  145. ScaffoldMessenger.of(context).showSnackBar(snackBar);
  146. }
  147. void showBatteryOptimizationInfoToUser() {
  148. showDialog<void>(
  149. context: context,
  150. barrierDismissible: false,
  151. builder: (BuildContext context) {
  152. return AlertDialog(
  153. title: const Text(
  154. 'backup_controller_page_background_battery_info_title',
  155. ).tr(),
  156. content: SingleChildScrollView(
  157. child: const Text(
  158. 'backup_controller_page_background_battery_info_message',
  159. ).tr(),
  160. ),
  161. actions: [
  162. ElevatedButton(
  163. onPressed: () => launchUrl(
  164. Uri.parse('https://dontkillmyapp.com'),
  165. mode: LaunchMode.externalApplication,
  166. ),
  167. child: const Text(
  168. "backup_controller_page_background_battery_info_link",
  169. style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
  170. ).tr(),
  171. ),
  172. ElevatedButton(
  173. child: const Text(
  174. 'backup_controller_page_background_battery_info_ok',
  175. style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
  176. ).tr(),
  177. onPressed: () {
  178. Navigator.of(context).pop();
  179. },
  180. ),
  181. ],
  182. );
  183. },
  184. );
  185. }
  186. ListTile buildBackgroundBackupController() {
  187. final bool isBackgroundEnabled = backupState.backgroundBackup;
  188. final bool isWifiRequired = backupState.backupRequireWifi;
  189. final bool isChargingRequired = backupState.backupRequireCharging;
  190. final Color activeColor = Theme.of(context).primaryColor;
  191. String formatBackupDelaySliderValue(double v) {
  192. if (v == 0.0) {
  193. return 'setting_notifications_notify_seconds'.tr(args: const ['5']);
  194. } else if (v == 1.0) {
  195. return 'setting_notifications_notify_seconds'.tr(args: const ['30']);
  196. } else if (v == 2.0) {
  197. return 'setting_notifications_notify_minutes'.tr(args: const ['2']);
  198. } else {
  199. return 'setting_notifications_notify_minutes'.tr(args: const ['10']);
  200. }
  201. }
  202. int backupDelayToMilliseconds(double v) {
  203. if (v == 0.0) {
  204. return 5000;
  205. } else if (v == 1.0) {
  206. return 30000;
  207. } else if (v == 2.0) {
  208. return 120000;
  209. } else {
  210. return 600000;
  211. }
  212. }
  213. double backupDelayToSliderValue(int ms) {
  214. if (ms == 5000) {
  215. return 0.0;
  216. } else if (ms == 30000) {
  217. return 1.0;
  218. } else if (ms == 120000) {
  219. return 2.0;
  220. } else {
  221. return 3.0;
  222. }
  223. }
  224. final triggerDelay =
  225. useState(backupDelayToSliderValue(backupState.backupTriggerDelay));
  226. return ListTile(
  227. isThreeLine: true,
  228. leading: isBackgroundEnabled
  229. ? Icon(
  230. Icons.cloud_sync_rounded,
  231. color: activeColor,
  232. )
  233. : const Icon(Icons.cloud_sync_rounded),
  234. title: Text(
  235. isBackgroundEnabled
  236. ? "backup_controller_page_background_is_on"
  237. : "backup_controller_page_background_is_off",
  238. style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
  239. ).tr(),
  240. subtitle: Column(
  241. crossAxisAlignment: CrossAxisAlignment.start,
  242. children: [
  243. if (!isBackgroundEnabled)
  244. Padding(
  245. padding: const EdgeInsets.symmetric(vertical: 8.0),
  246. child:
  247. const Text("backup_controller_page_background_description")
  248. .tr(),
  249. ),
  250. if (isBackgroundEnabled)
  251. SwitchListTile(
  252. title:
  253. const Text("backup_controller_page_background_wifi").tr(),
  254. secondary: Icon(
  255. Icons.wifi,
  256. color: isWifiRequired ? activeColor : null,
  257. ),
  258. dense: true,
  259. activeColor: activeColor,
  260. value: isWifiRequired,
  261. onChanged: hasExclusiveAccess
  262. ? (isChecked) => ref
  263. .read(backupProvider.notifier)
  264. .configureBackgroundBackup(
  265. requireWifi: isChecked,
  266. onError: showErrorToUser,
  267. onBatteryInfo: showBatteryOptimizationInfoToUser,
  268. )
  269. : null,
  270. ),
  271. if (isBackgroundEnabled)
  272. SwitchListTile(
  273. title: const Text("backup_controller_page_background_charging")
  274. .tr(),
  275. secondary: Icon(
  276. Icons.charging_station,
  277. color: isChargingRequired ? activeColor : null,
  278. ),
  279. dense: true,
  280. activeColor: activeColor,
  281. value: isChargingRequired,
  282. onChanged: hasExclusiveAccess
  283. ? (isChecked) => ref
  284. .read(backupProvider.notifier)
  285. .configureBackgroundBackup(
  286. requireCharging: isChecked,
  287. onError: showErrorToUser,
  288. onBatteryInfo: showBatteryOptimizationInfoToUser,
  289. )
  290. : null,
  291. ),
  292. if (isBackgroundEnabled)
  293. ListTile(
  294. isThreeLine: false,
  295. dense: true,
  296. enabled: hasExclusiveAccess,
  297. title: const Text(
  298. 'backup_controller_page_background_delay',
  299. style: TextStyle(
  300. fontWeight: FontWeight.bold,
  301. ),
  302. ).tr(args: [formatBackupDelaySliderValue(triggerDelay.value)]),
  303. subtitle: Slider(
  304. value: triggerDelay.value,
  305. onChanged: hasExclusiveAccess
  306. ? (double v) => triggerDelay.value = v
  307. : null,
  308. onChangeEnd: (double v) => ref
  309. .read(backupProvider.notifier)
  310. .configureBackgroundBackup(
  311. triggerDelay: backupDelayToMilliseconds(v),
  312. onError: showErrorToUser,
  313. onBatteryInfo: showBatteryOptimizationInfoToUser,
  314. ),
  315. max: 3.0,
  316. divisions: 3,
  317. label: formatBackupDelaySliderValue(triggerDelay.value),
  318. activeColor: Theme.of(context).primaryColor,
  319. ),
  320. ),
  321. ElevatedButton(
  322. onPressed: () =>
  323. ref.read(backupProvider.notifier).configureBackgroundBackup(
  324. enabled: !isBackgroundEnabled,
  325. onError: showErrorToUser,
  326. onBatteryInfo: showBatteryOptimizationInfoToUser,
  327. ),
  328. child: Text(
  329. isBackgroundEnabled
  330. ? "backup_controller_page_background_turn_off"
  331. : "backup_controller_page_background_turn_on",
  332. style:
  333. const TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
  334. ).tr(),
  335. ),
  336. ],
  337. ),
  338. );
  339. }
  340. Widget buildSelectedAlbumName() {
  341. var text = "backup_controller_page_backup_selected".tr();
  342. var albums = ref.watch(backupProvider).selectedBackupAlbums;
  343. if (albums.isNotEmpty) {
  344. for (var album in albums) {
  345. if (album.name == "Recent" || album.name == "Recents") {
  346. text += "${album.name} (${'backup_all'.tr()}), ";
  347. } else {
  348. text += "${album.name}, ";
  349. }
  350. }
  351. return Padding(
  352. padding: const EdgeInsets.only(top: 8.0),
  353. child: Text(
  354. text.trim().substring(0, text.length - 2),
  355. style: TextStyle(
  356. color: Theme.of(context).primaryColor,
  357. fontSize: 12,
  358. fontWeight: FontWeight.bold,
  359. ),
  360. ),
  361. );
  362. } else {
  363. return Padding(
  364. padding: const EdgeInsets.only(top: 8.0),
  365. child: Text(
  366. "backup_controller_page_none_selected".tr(),
  367. style: TextStyle(
  368. color: Theme.of(context).primaryColor,
  369. fontSize: 12,
  370. fontWeight: FontWeight.bold,
  371. ),
  372. ),
  373. );
  374. }
  375. }
  376. Widget buildExcludedAlbumName() {
  377. var text = "backup_controller_page_excluded".tr();
  378. var albums = ref.watch(backupProvider).excludedBackupAlbums;
  379. if (albums.isNotEmpty) {
  380. for (var album in albums) {
  381. text += "${album.name}, ";
  382. }
  383. return Padding(
  384. padding: const EdgeInsets.only(top: 8.0),
  385. child: Text(
  386. text.trim().substring(0, text.length - 2),
  387. style: TextStyle(
  388. color: Colors.red[300],
  389. fontSize: 12,
  390. fontWeight: FontWeight.bold,
  391. ),
  392. ),
  393. );
  394. } else {
  395. return const SizedBox();
  396. }
  397. }
  398. buildFolderSelectionTile() {
  399. return Card(
  400. shape: RoundedRectangleBorder(
  401. borderRadius: BorderRadius.circular(5), // if you need this
  402. side: const BorderSide(
  403. color: Colors.black12,
  404. width: 1,
  405. ),
  406. ),
  407. elevation: 0,
  408. borderOnForeground: false,
  409. child: ListTile(
  410. minVerticalPadding: 15,
  411. title: const Text(
  412. "backup_controller_page_albums",
  413. style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
  414. ).tr(),
  415. subtitle: Padding(
  416. padding: const EdgeInsets.only(top: 8.0),
  417. child: Column(
  418. crossAxisAlignment: CrossAxisAlignment.start,
  419. children: [
  420. const Text(
  421. "backup_controller_page_to_backup",
  422. style: TextStyle(fontSize: 12),
  423. ).tr(),
  424. buildSelectedAlbumName(),
  425. buildExcludedAlbumName()
  426. ],
  427. ),
  428. ),
  429. trailing: ElevatedButton(
  430. onPressed: hasExclusiveAccess
  431. ? () {
  432. AutoRouter.of(context)
  433. .push(const BackupAlbumSelectionRoute());
  434. }
  435. : null,
  436. child: const Text(
  437. "backup_controller_page_select",
  438. style: TextStyle(
  439. fontWeight: FontWeight.bold,
  440. fontSize: 12,
  441. ),
  442. ).tr(),
  443. ),
  444. ),
  445. );
  446. }
  447. buildCurrentBackupAssetInfoCard() {
  448. return ListTile(
  449. leading: Icon(
  450. Icons.info_outline_rounded,
  451. color: Theme.of(context).primaryColor,
  452. ),
  453. title: Row(
  454. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  455. children: [
  456. const Text(
  457. "backup_controller_page_uploading_file_info",
  458. style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
  459. ).tr(),
  460. if (ref.watch(errorBackupListProvider).isNotEmpty)
  461. ActionChip(
  462. avatar: Icon(
  463. Icons.info,
  464. color: Colors.red[400],
  465. ),
  466. elevation: 1,
  467. visualDensity: VisualDensity.compact,
  468. label: Text(
  469. "backup_controller_page_failed",
  470. style: TextStyle(
  471. color: Colors.red[400],
  472. fontWeight: FontWeight.bold,
  473. fontSize: 11,
  474. ),
  475. ).tr(
  476. args: [ref.watch(errorBackupListProvider).length.toString()],
  477. ),
  478. backgroundColor: Colors.white,
  479. onPressed: () {
  480. AutoRouter.of(context).push(const FailedBackupStatusRoute());
  481. },
  482. ),
  483. ],
  484. ),
  485. subtitle: Column(
  486. children: [
  487. Padding(
  488. padding: const EdgeInsets.only(top: 8.0),
  489. child: Row(
  490. children: [
  491. Expanded(
  492. child: LinearProgressIndicator(
  493. minHeight: 10.0,
  494. value: backupState.progressInPercentage / 100.0,
  495. backgroundColor: Colors.grey,
  496. color: Theme.of(context).primaryColor,
  497. ),
  498. ),
  499. Text(
  500. " ${backupState.progressInPercentage.toStringAsFixed(0)}%",
  501. style: const TextStyle(fontSize: 12),
  502. )
  503. ],
  504. ),
  505. ),
  506. Padding(
  507. padding: const EdgeInsets.only(top: 8.0),
  508. child: Table(
  509. border: TableBorder.all(
  510. color: Theme.of(context).primaryColorLight,
  511. width: 1,
  512. ),
  513. children: [
  514. TableRow(
  515. decoration: const BoxDecoration(
  516. // color: Colors.grey[100],
  517. ),
  518. children: [
  519. TableCell(
  520. verticalAlignment: TableCellVerticalAlignment.middle,
  521. child: Padding(
  522. padding: const EdgeInsets.all(6.0),
  523. child: const Text(
  524. 'backup_controller_page_filename',
  525. style: TextStyle(
  526. fontWeight: FontWeight.bold,
  527. fontSize: 10.0,
  528. ),
  529. ).tr(
  530. args: [
  531. backupState.currentUploadAsset.fileName,
  532. backupState.currentUploadAsset.fileType
  533. .toLowerCase()
  534. ],
  535. ),
  536. ),
  537. ),
  538. ],
  539. ),
  540. TableRow(
  541. decoration: const BoxDecoration(
  542. // color: Colors.grey[200],
  543. ),
  544. children: [
  545. TableCell(
  546. verticalAlignment: TableCellVerticalAlignment.middle,
  547. child: Padding(
  548. padding: const EdgeInsets.all(6.0),
  549. child: const Text(
  550. "backup_controller_page_created",
  551. style: TextStyle(
  552. fontWeight: FontWeight.bold,
  553. fontSize: 10.0,
  554. ),
  555. ).tr(
  556. args: [
  557. DateFormat.yMMMMd('en_US').format(
  558. DateTime.parse(
  559. backupState.currentUploadAsset.createdAt
  560. .toString(),
  561. ).toLocal(),
  562. )
  563. ],
  564. ),
  565. ),
  566. ),
  567. ],
  568. ),
  569. TableRow(
  570. decoration: const BoxDecoration(
  571. // color: Colors.grey[100],
  572. ),
  573. children: [
  574. TableCell(
  575. child: Padding(
  576. padding: const EdgeInsets.all(6.0),
  577. child: const Text(
  578. "backup_controller_page_id",
  579. style: TextStyle(
  580. fontWeight: FontWeight.bold,
  581. fontSize: 10.0,
  582. ),
  583. ).tr(args: [backupState.currentUploadAsset.id]),
  584. ),
  585. ),
  586. ],
  587. ),
  588. ],
  589. ),
  590. ),
  591. ],
  592. ),
  593. );
  594. }
  595. void startBackup() {
  596. ref.watch(errorBackupListProvider.notifier).empty();
  597. if (ref.watch(backupProvider).backupProgress !=
  598. BackUpProgressEnum.inBackground) {
  599. ref.watch(backupProvider.notifier).startBackupProcess();
  600. }
  601. }
  602. return Scaffold(
  603. appBar: AppBar(
  604. elevation: 0,
  605. title: const Text(
  606. "backup_controller_page_backup",
  607. style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
  608. ).tr(),
  609. leading: IconButton(
  610. onPressed: () {
  611. ref.watch(websocketProvider.notifier).listenUploadEvent();
  612. AutoRouter.of(context).pop(true);
  613. },
  614. splashRadius: 24,
  615. icon: const Icon(
  616. Icons.arrow_back_ios_rounded,
  617. ),
  618. ),
  619. ),
  620. body: Padding(
  621. padding: const EdgeInsets.only(left: 16.0, right: 16, bottom: 32),
  622. child: ListView(
  623. // crossAxisAlignment: CrossAxisAlignment.start,
  624. children: [
  625. Padding(
  626. padding: const EdgeInsets.all(8.0),
  627. child: const Text(
  628. "backup_controller_page_info",
  629. style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
  630. ).tr(),
  631. ),
  632. hasExclusiveAccess
  633. ? const SizedBox.shrink()
  634. : Card(
  635. shape: RoundedRectangleBorder(
  636. borderRadius:
  637. BorderRadius.circular(5), // if you need this
  638. side: const BorderSide(
  639. color: Colors.black12,
  640. width: 1,
  641. ),
  642. ),
  643. elevation: 0,
  644. borderOnForeground: false,
  645. child: const Padding(
  646. padding: EdgeInsets.all(16.0),
  647. child: Text(
  648. "Background backup is currently running, some actions are disabled",
  649. style: TextStyle(fontWeight: FontWeight.bold),
  650. ),
  651. ),
  652. ),
  653. buildFolderSelectionTile(),
  654. BackupInfoCard(
  655. title: "backup_controller_page_total".tr(),
  656. subtitle: "backup_controller_page_total_sub".tr(),
  657. info: "${backupState.allUniqueAssets.length}",
  658. ),
  659. BackupInfoCard(
  660. title: "backup_controller_page_backup".tr(),
  661. subtitle: "backup_controller_page_backup_sub".tr(),
  662. info: "${backupState.selectedAlbumsBackupAssetsIds.length}",
  663. ),
  664. BackupInfoCard(
  665. title: "backup_controller_page_remainder".tr(),
  666. subtitle: "backup_controller_page_remainder_sub".tr(),
  667. info:
  668. "${backupState.allUniqueAssets.length - backupState.selectedAlbumsBackupAssetsIds.length}",
  669. ),
  670. const Divider(),
  671. buildAutoBackupController(),
  672. if (Platform.isAndroid) const Divider(),
  673. if (Platform.isAndroid) buildBackgroundBackupController(),
  674. const Divider(),
  675. buildStorageInformation(),
  676. const Divider(),
  677. buildCurrentBackupAssetInfoCard(),
  678. Padding(
  679. padding: const EdgeInsets.only(
  680. top: 24,
  681. ),
  682. child: Container(
  683. child:
  684. backupState.backupProgress == BackUpProgressEnum.inProgress
  685. ? ElevatedButton(
  686. style: ElevatedButton.styleFrom(
  687. foregroundColor: Colors.grey[50],
  688. backgroundColor: Colors.red[300],
  689. // padding: const EdgeInsets.all(14),
  690. ),
  691. onPressed: () {
  692. ref.read(backupProvider.notifier).cancelBackup();
  693. },
  694. child: const Text(
  695. "backup_controller_page_cancel",
  696. style: TextStyle(
  697. fontSize: 14,
  698. fontWeight: FontWeight.bold,
  699. ),
  700. ).tr(),
  701. )
  702. : ElevatedButton(
  703. onPressed: shouldBackup ? startBackup : null,
  704. child: const Text(
  705. "backup_controller_page_start_backup",
  706. style: TextStyle(
  707. fontSize: 14,
  708. fontWeight: FontWeight.bold,
  709. ),
  710. ).tr(),
  711. ),
  712. ),
  713. )
  714. ],
  715. ),
  716. ),
  717. );
  718. }
  719. }