mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-04 03:27:09 -05:00 
			
		
		
		
	* fix: improve sync indicator error * prefer backup disabled icon before error --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Co-authored-by: Alex <alex.tran1502@gmail.com>
		
			
				
	
	
		
			506 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			506 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
import 'dart:async';
 | 
						|
import 'dart:io';
 | 
						|
 | 
						|
import 'package:auto_route/auto_route.dart';
 | 
						|
import 'package:easy_localization/easy_localization.dart';
 | 
						|
import 'package:flutter/material.dart';
 | 
						|
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
						|
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
 | 
						|
import 'package:immich_mobile/domain/services/sync_linked_album.service.dart';
 | 
						|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
 | 
						|
import 'package:immich_mobile/extensions/translate_extensions.dart';
 | 
						|
import 'package:immich_mobile/providers/app_settings.provider.dart';
 | 
						|
import 'package:immich_mobile/providers/background_sync.provider.dart';
 | 
						|
import 'package:immich_mobile/providers/backup/backup_album.provider.dart';
 | 
						|
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
 | 
						|
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
 | 
						|
import 'package:immich_mobile/providers/user.provider.dart';
 | 
						|
import 'package:immich_mobile/services/app_settings.service.dart';
 | 
						|
import 'package:immich_mobile/widgets/backup/drift_album_info_list_tile.dart';
 | 
						|
import 'package:immich_mobile/widgets/common/search_field.dart';
 | 
						|
import 'package:logging/logging.dart';
 | 
						|
 | 
						|
@RoutePage()
 | 
						|
class DriftBackupAlbumSelectionPage extends ConsumerStatefulWidget {
 | 
						|
  const DriftBackupAlbumSelectionPage({super.key});
 | 
						|
 | 
						|
  @override
 | 
						|
  ConsumerState<DriftBackupAlbumSelectionPage> createState() => _DriftBackupAlbumSelectionPageState();
 | 
						|
}
 | 
						|
 | 
						|
class _DriftBackupAlbumSelectionPageState extends ConsumerState<DriftBackupAlbumSelectionPage> {
 | 
						|
  String _searchQuery = '';
 | 
						|
  bool _isSearchMode = false;
 | 
						|
  int _initialTotalAssetCount = 0;
 | 
						|
  late ValueNotifier<bool> _enableSyncUploadAlbum;
 | 
						|
  late TextEditingController _searchController;
 | 
						|
  late FocusNode _searchFocusNode;
 | 
						|
  Future? _handleLinkedAlbumFuture;
 | 
						|
 | 
						|
  @override
 | 
						|
  void initState() {
 | 
						|
    super.initState();
 | 
						|
    _enableSyncUploadAlbum = ValueNotifier<bool>(false);
 | 
						|
    _searchController = TextEditingController();
 | 
						|
    _searchFocusNode = FocusNode();
 | 
						|
 | 
						|
    _enableSyncUploadAlbum.value = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.syncAlbums);
 | 
						|
    ref.read(backupAlbumProvider.notifier).getAll();
 | 
						|
 | 
						|
    _initialTotalAssetCount = ref.read(driftBackupProvider.select((p) => p.totalCount));
 | 
						|
  }
 | 
						|
 | 
						|
  Future<void> _handlePagePopped() async {
 | 
						|
    final user = ref.read(currentUserProvider);
 | 
						|
    if (user == null) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    final enableSyncUploadAlbum = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.syncAlbums);
 | 
						|
    final selectedAlbums = ref
 | 
						|
        .read(backupAlbumProvider)
 | 
						|
        .where((a) => a.backupSelection == BackupSelection.selected)
 | 
						|
        .toList();
 | 
						|
 | 
						|
    if (enableSyncUploadAlbum && selectedAlbums.isNotEmpty) {
 | 
						|
      setState(() {
 | 
						|
        _handleLinkedAlbumFuture = ref.read(syncLinkedAlbumServiceProvider).manageLinkedAlbums(selectedAlbums, user.id);
 | 
						|
      });
 | 
						|
      await _handleLinkedAlbumFuture;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  @override
 | 
						|
  void dispose() {
 | 
						|
    _enableSyncUploadAlbum.dispose();
 | 
						|
    _searchController.dispose();
 | 
						|
    _searchFocusNode.dispose();
 | 
						|
    super.dispose();
 | 
						|
  }
 | 
						|
 | 
						|
  @override
 | 
						|
  Widget build(BuildContext context) {
 | 
						|
    final albums = ref.watch(backupAlbumProvider);
 | 
						|
    final albumCount = albums.length;
 | 
						|
    // Filter albums based on search query
 | 
						|
    final filteredAlbums = albums.where((album) {
 | 
						|
      if (_searchQuery.isEmpty) return true;
 | 
						|
      return album.name.toLowerCase().contains(_searchQuery.toLowerCase());
 | 
						|
    }).toList();
 | 
						|
 | 
						|
    final selectedBackupAlbums = albums.where((album) => album.backupSelection == BackupSelection.selected).toList();
 | 
						|
    final excludedBackupAlbums = albums.where((album) => album.backupSelection == BackupSelection.excluded).toList();
 | 
						|
 | 
						|
    return PopScope(
 | 
						|
      canPop: false,
 | 
						|
      onPopInvokedWithResult: (didPop, _) async {
 | 
						|
        if (!didPop) {
 | 
						|
          await _handlePagePopped();
 | 
						|
 | 
						|
          final user = ref.read(currentUserProvider);
 | 
						|
          if (user == null) {
 | 
						|
            return;
 | 
						|
          }
 | 
						|
 | 
						|
          final isBackupEnabled = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup);
 | 
						|
          await ref.read(driftBackupProvider.notifier).getBackupStatus(user.id);
 | 
						|
          final currentTotalAssetCount = ref.read(driftBackupProvider.select((p) => p.totalCount));
 | 
						|
          final totalChanged = currentTotalAssetCount != _initialTotalAssetCount;
 | 
						|
          final backupNotifier = ref.read(driftBackupProvider.notifier);
 | 
						|
          final backgroundSync = ref.read(backgroundSyncProvider);
 | 
						|
          final nativeSync = ref.read(nativeSyncApiProvider);
 | 
						|
          if (totalChanged) {
 | 
						|
            // Waits for hashing to be cancelled before starting a new one
 | 
						|
            unawaited(nativeSync.cancelHashing().whenComplete(() => backgroundSync.hashAssets()));
 | 
						|
            if (isBackupEnabled) {
 | 
						|
              unawaited(
 | 
						|
                backupNotifier.cancel().whenComplete(
 | 
						|
                  () => backgroundSync.syncRemote().then((success) {
 | 
						|
                    if (success) {
 | 
						|
                      return backupNotifier.startBackup(user.id);
 | 
						|
                    } else {
 | 
						|
                      Logger('DriftBackupAlbumSelectionPage').warning('Background sync failed, not starting backup');
 | 
						|
                    }
 | 
						|
                  }),
 | 
						|
                ),
 | 
						|
              );
 | 
						|
            }
 | 
						|
          }
 | 
						|
 | 
						|
          Navigator.of(context).pop();
 | 
						|
        }
 | 
						|
      },
 | 
						|
      child: Scaffold(
 | 
						|
        appBar: AppBar(
 | 
						|
          leading: IconButton(
 | 
						|
            onPressed: () async => await context.maybePop(),
 | 
						|
            icon: const Icon(Icons.arrow_back_ios_rounded),
 | 
						|
          ),
 | 
						|
          title: _isSearchMode
 | 
						|
              ? SearchField(
 | 
						|
                  hintText: 'search_albums'.t(context: context),
 | 
						|
                  autofocus: true,
 | 
						|
                  controller: _searchController,
 | 
						|
                  focusNode: _searchFocusNode,
 | 
						|
                  onChanged: (value) => setState(() => _searchQuery = value.trim()),
 | 
						|
                )
 | 
						|
              : const Text("backup_album_selection_page_select_albums").t(context: context),
 | 
						|
          actions: [
 | 
						|
            if (!_isSearchMode)
 | 
						|
              IconButton(
 | 
						|
                icon: const Icon(Icons.search),
 | 
						|
                onPressed: () => setState(() {
 | 
						|
                  _isSearchMode = true;
 | 
						|
                  _searchQuery = '';
 | 
						|
                }),
 | 
						|
              )
 | 
						|
            else
 | 
						|
              IconButton(
 | 
						|
                icon: const Icon(Icons.close),
 | 
						|
                onPressed: () => setState(() {
 | 
						|
                  _isSearchMode = false;
 | 
						|
                  _searchQuery = '';
 | 
						|
                  _searchController.clear();
 | 
						|
                }),
 | 
						|
              ),
 | 
						|
          ],
 | 
						|
          elevation: 0,
 | 
						|
        ),
 | 
						|
        body: Stack(
 | 
						|
          children: [
 | 
						|
            CustomScrollView(
 | 
						|
              physics: const ClampingScrollPhysics(),
 | 
						|
              slivers: [
 | 
						|
                SliverToBoxAdapter(
 | 
						|
                  child: Column(
 | 
						|
                    crossAxisAlignment: CrossAxisAlignment.start,
 | 
						|
                    children: [
 | 
						|
                      Padding(
 | 
						|
                        padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
 | 
						|
                        child: Text(
 | 
						|
                          "backup_album_selection_page_selection_info",
 | 
						|
                          style: context.textTheme.titleSmall,
 | 
						|
                        ).t(context: context),
 | 
						|
                      ),
 | 
						|
 | 
						|
                      // Selected Album Chips
 | 
						|
                      Padding(
 | 
						|
                        padding: const EdgeInsets.symmetric(horizontal: 16.0),
 | 
						|
                        child: Wrap(
 | 
						|
                          children: [
 | 
						|
                            _SelectedAlbumNameChips(selectedBackupAlbums: selectedBackupAlbums),
 | 
						|
                            _ExcludedAlbumNameChips(excludedBackupAlbums: excludedBackupAlbums),
 | 
						|
                          ],
 | 
						|
                        ),
 | 
						|
                      ),
 | 
						|
                      ListTile(
 | 
						|
                        title: Text(
 | 
						|
                          "albums_on_device_count".t(context: context, args: {'count': albumCount.toString()}),
 | 
						|
                          style: context.textTheme.titleSmall,
 | 
						|
                        ),
 | 
						|
                        subtitle: Padding(
 | 
						|
                          padding: const EdgeInsets.symmetric(vertical: 8.0),
 | 
						|
                          child: Text(
 | 
						|
                            "backup_album_selection_page_albums_tap",
 | 
						|
                            style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor),
 | 
						|
                          ).t(context: context),
 | 
						|
                        ),
 | 
						|
                        trailing: IconButton(
 | 
						|
                          splashRadius: 16,
 | 
						|
                          icon: Icon(Icons.info, size: 20, color: context.primaryColor),
 | 
						|
                          onPressed: () {
 | 
						|
                            showDialog(
 | 
						|
                              context: context,
 | 
						|
                              builder: (BuildContext context) {
 | 
						|
                                return AlertDialog(
 | 
						|
                                  shape: const RoundedRectangleBorder(
 | 
						|
                                    borderRadius: BorderRadius.all(Radius.circular(10)),
 | 
						|
                                  ),
 | 
						|
                                  elevation: 5,
 | 
						|
                                  title: Text(
 | 
						|
                                    'backup_album_selection_page_selection_info',
 | 
						|
                                    style: TextStyle(
 | 
						|
                                      fontSize: 16,
 | 
						|
                                      fontWeight: FontWeight.bold,
 | 
						|
                                      color: context.primaryColor,
 | 
						|
                                    ),
 | 
						|
                                  ).t(context: context),
 | 
						|
                                  content: SingleChildScrollView(
 | 
						|
                                    child: ListBody(
 | 
						|
                                      children: [
 | 
						|
                                        const Text(
 | 
						|
                                          'backup_album_selection_page_assets_scatter',
 | 
						|
                                          style: TextStyle(fontSize: 14),
 | 
						|
                                        ).t(context: context),
 | 
						|
                                      ],
 | 
						|
                                    ),
 | 
						|
                                  ),
 | 
						|
                                );
 | 
						|
                              },
 | 
						|
                            );
 | 
						|
                          },
 | 
						|
                        ),
 | 
						|
                      ),
 | 
						|
 | 
						|
                      if (Platform.isAndroid)
 | 
						|
                        _SelectAllButton(filteredAlbums: filteredAlbums, selectedBackupAlbums: selectedBackupAlbums),
 | 
						|
                    ],
 | 
						|
                  ),
 | 
						|
                ),
 | 
						|
                SliverLayoutBuilder(
 | 
						|
                  builder: (context, constraints) {
 | 
						|
                    if (constraints.crossAxisExtent > 600) {
 | 
						|
                      return _AlbumSelectionGrid(filteredAlbums: filteredAlbums, searchQuery: _searchQuery);
 | 
						|
                    } else {
 | 
						|
                      return _AlbumSelectionList(filteredAlbums: filteredAlbums, searchQuery: _searchQuery);
 | 
						|
                    }
 | 
						|
                  },
 | 
						|
                ),
 | 
						|
              ],
 | 
						|
            ),
 | 
						|
            if (_handleLinkedAlbumFuture != null)
 | 
						|
              FutureBuilder(
 | 
						|
                future: _handleLinkedAlbumFuture,
 | 
						|
                builder: (context, snapshot) {
 | 
						|
                  return SizedBox(
 | 
						|
                    height: double.infinity,
 | 
						|
                    width: double.infinity,
 | 
						|
                    child: Container(
 | 
						|
                      color: context.scaffoldBackgroundColor.withValues(alpha: 0.8),
 | 
						|
                      child: Center(
 | 
						|
                        child: Column(
 | 
						|
                          spacing: 16,
 | 
						|
                          crossAxisAlignment: CrossAxisAlignment.center,
 | 
						|
                          mainAxisAlignment: MainAxisAlignment.center,
 | 
						|
                          mainAxisSize: MainAxisSize.max,
 | 
						|
                          children: [
 | 
						|
                            const CircularProgressIndicator(strokeWidth: 4),
 | 
						|
                            Text('creating_linked_albums'.tr(), style: context.textTheme.labelLarge),
 | 
						|
                          ],
 | 
						|
                        ),
 | 
						|
                      ),
 | 
						|
                    ),
 | 
						|
                  );
 | 
						|
                },
 | 
						|
              ),
 | 
						|
          ],
 | 
						|
        ),
 | 
						|
      ),
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
class _AlbumSelectionList extends StatelessWidget {
 | 
						|
  final List<LocalAlbum> filteredAlbums;
 | 
						|
  final String searchQuery;
 | 
						|
 | 
						|
  const _AlbumSelectionList({required this.filteredAlbums, required this.searchQuery});
 | 
						|
 | 
						|
  @override
 | 
						|
  Widget build(BuildContext context) {
 | 
						|
    if (filteredAlbums.isEmpty && searchQuery.isNotEmpty) {
 | 
						|
      return SliverToBoxAdapter(
 | 
						|
        child: Center(
 | 
						|
          child: Padding(
 | 
						|
            padding: const EdgeInsets.all(24.0),
 | 
						|
            child: Text('album_search_not_found'.t(context: context)),
 | 
						|
          ),
 | 
						|
        ),
 | 
						|
      );
 | 
						|
    }
 | 
						|
 | 
						|
    if (filteredAlbums.isEmpty) {
 | 
						|
      return const SliverToBoxAdapter(child: Center(child: CircularProgressIndicator()));
 | 
						|
    }
 | 
						|
 | 
						|
    return SliverPadding(
 | 
						|
      padding: const EdgeInsets.symmetric(vertical: 12.0),
 | 
						|
      sliver: SliverList(
 | 
						|
        delegate: SliverChildBuilderDelegate(((context, index) {
 | 
						|
          return DriftAlbumInfoListTile(album: filteredAlbums[index]);
 | 
						|
        }), childCount: filteredAlbums.length),
 | 
						|
      ),
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
class _AlbumSelectionGrid extends StatelessWidget {
 | 
						|
  final List<LocalAlbum> filteredAlbums;
 | 
						|
  final String searchQuery;
 | 
						|
 | 
						|
  const _AlbumSelectionGrid({required this.filteredAlbums, required this.searchQuery});
 | 
						|
 | 
						|
  @override
 | 
						|
  Widget build(BuildContext context) {
 | 
						|
    if (filteredAlbums.isEmpty && searchQuery.isNotEmpty) {
 | 
						|
      return SliverToBoxAdapter(
 | 
						|
        child: Center(
 | 
						|
          child: Padding(
 | 
						|
            padding: const EdgeInsets.all(24.0),
 | 
						|
            child: Text('album_search_not_found'.t(context: context)),
 | 
						|
          ),
 | 
						|
        ),
 | 
						|
      );
 | 
						|
    }
 | 
						|
 | 
						|
    if (filteredAlbums.isEmpty) {
 | 
						|
      return const SliverToBoxAdapter(child: Center(child: CircularProgressIndicator()));
 | 
						|
    }
 | 
						|
 | 
						|
    return SliverPadding(
 | 
						|
      padding: const EdgeInsets.all(12.0),
 | 
						|
      sliver: SliverGrid.builder(
 | 
						|
        gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
 | 
						|
          maxCrossAxisExtent: 300,
 | 
						|
          mainAxisSpacing: 12,
 | 
						|
          crossAxisSpacing: 12,
 | 
						|
        ),
 | 
						|
        itemCount: filteredAlbums.length,
 | 
						|
        itemBuilder: ((context, index) {
 | 
						|
          return DriftAlbumInfoListTile(album: filteredAlbums[index]);
 | 
						|
        }),
 | 
						|
      ),
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
class _SelectedAlbumNameChips extends ConsumerWidget {
 | 
						|
  final List<LocalAlbum> selectedBackupAlbums;
 | 
						|
 | 
						|
  const _SelectedAlbumNameChips({required this.selectedBackupAlbums});
 | 
						|
 | 
						|
  @override
 | 
						|
  Widget build(BuildContext context, WidgetRef ref) {
 | 
						|
    return Wrap(
 | 
						|
      children: selectedBackupAlbums.asMap().entries.map((entry) {
 | 
						|
        final album = entry.value;
 | 
						|
 | 
						|
        void removeSelection() {
 | 
						|
          ref.read(backupAlbumProvider.notifier).deselectAlbum(album);
 | 
						|
        }
 | 
						|
 | 
						|
        return Padding(
 | 
						|
          padding: const EdgeInsets.only(right: 8.0),
 | 
						|
          child: GestureDetector(
 | 
						|
            onTap: removeSelection,
 | 
						|
            child: AnimatedContainer(
 | 
						|
              duration: const Duration(milliseconds: 200),
 | 
						|
              curve: Curves.easeInOut,
 | 
						|
              child: Chip(
 | 
						|
                label: Text(
 | 
						|
                  album.name,
 | 
						|
                  style: TextStyle(
 | 
						|
                    fontSize: 12,
 | 
						|
                    color: context.isDarkTheme ? Colors.black : Colors.white,
 | 
						|
                    fontWeight: FontWeight.bold,
 | 
						|
                  ),
 | 
						|
                ),
 | 
						|
                backgroundColor: context.primaryColor,
 | 
						|
                deleteIconColor: context.isDarkTheme ? Colors.black : Colors.white,
 | 
						|
                deleteIcon: const Icon(Icons.cancel_rounded, size: 15),
 | 
						|
                onDeleted: removeSelection,
 | 
						|
              ),
 | 
						|
            ),
 | 
						|
          ),
 | 
						|
        );
 | 
						|
      }).toList(),
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
class _ExcludedAlbumNameChips extends ConsumerWidget {
 | 
						|
  final List<LocalAlbum> excludedBackupAlbums;
 | 
						|
 | 
						|
  const _ExcludedAlbumNameChips({required this.excludedBackupAlbums});
 | 
						|
 | 
						|
  @override
 | 
						|
  Widget build(BuildContext context, WidgetRef ref) {
 | 
						|
    return Wrap(
 | 
						|
      children: excludedBackupAlbums.asMap().entries.map((entry) {
 | 
						|
        final album = entry.value;
 | 
						|
 | 
						|
        void removeSelection() {
 | 
						|
          ref.read(backupAlbumProvider.notifier).deselectAlbum(album);
 | 
						|
        }
 | 
						|
 | 
						|
        return GestureDetector(
 | 
						|
          onTap: removeSelection,
 | 
						|
          child: Padding(
 | 
						|
            padding: const EdgeInsets.only(right: 8.0),
 | 
						|
            child: AnimatedContainer(
 | 
						|
              duration: const Duration(milliseconds: 200),
 | 
						|
              curve: Curves.easeInOut,
 | 
						|
              child: Chip(
 | 
						|
                label: Text(
 | 
						|
                  album.name,
 | 
						|
                  style: TextStyle(fontSize: 12, color: context.scaffoldBackgroundColor, fontWeight: FontWeight.bold),
 | 
						|
                ),
 | 
						|
                backgroundColor: Colors.red[300],
 | 
						|
                deleteIconColor: context.scaffoldBackgroundColor,
 | 
						|
                deleteIcon: const Icon(Icons.cancel_rounded, size: 15),
 | 
						|
                onDeleted: removeSelection,
 | 
						|
              ),
 | 
						|
            ),
 | 
						|
          ),
 | 
						|
        );
 | 
						|
      }).toList(),
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
class _SelectAllButton extends ConsumerWidget {
 | 
						|
  final List<LocalAlbum> filteredAlbums;
 | 
						|
  final List<LocalAlbum> selectedBackupAlbums;
 | 
						|
 | 
						|
  const _SelectAllButton({required this.filteredAlbums, required this.selectedBackupAlbums});
 | 
						|
 | 
						|
  @override
 | 
						|
  Widget build(BuildContext context, WidgetRef ref) {
 | 
						|
    final canSelectAll = filteredAlbums.where((album) => album.backupSelection != BackupSelection.selected).isNotEmpty;
 | 
						|
 | 
						|
    return Padding(
 | 
						|
      padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
 | 
						|
      child: Row(
 | 
						|
        children: [
 | 
						|
          Expanded(
 | 
						|
            child: ElevatedButton.icon(
 | 
						|
              onPressed: canSelectAll
 | 
						|
                  ? () {
 | 
						|
                      for (final album in filteredAlbums) {
 | 
						|
                        if (album.backupSelection != BackupSelection.selected) {
 | 
						|
                          ref.read(backupAlbumProvider.notifier).selectAlbum(album);
 | 
						|
                        }
 | 
						|
                      }
 | 
						|
                    }
 | 
						|
                  : null,
 | 
						|
              icon: const Icon(Icons.select_all),
 | 
						|
              label: AnimatedSwitcher(
 | 
						|
                duration: const Duration(milliseconds: 200),
 | 
						|
                child: Text("select_all".t(context: context)),
 | 
						|
              ),
 | 
						|
              style: ElevatedButton.styleFrom(padding: const EdgeInsets.symmetric(vertical: 12.0)),
 | 
						|
            ),
 | 
						|
          ),
 | 
						|
          const SizedBox(width: 8.0),
 | 
						|
          Expanded(
 | 
						|
            child: OutlinedButton.icon(
 | 
						|
              onPressed: selectedBackupAlbums.isNotEmpty
 | 
						|
                  ? () {
 | 
						|
                      for (final album in filteredAlbums) {
 | 
						|
                        if (album.backupSelection == BackupSelection.selected) {
 | 
						|
                          ref.read(backupAlbumProvider.notifier).deselectAlbum(album);
 | 
						|
                        }
 | 
						|
                      }
 | 
						|
                    }
 | 
						|
                  : null,
 | 
						|
              icon: const Icon(Icons.deselect),
 | 
						|
              label: Text('deselect_all'.t(context: context)),
 | 
						|
              style: OutlinedButton.styleFrom(padding: const EdgeInsets.symmetric(vertical: 12.0)),
 | 
						|
            ),
 | 
						|
          ),
 | 
						|
        ],
 | 
						|
      ),
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 |