mirror of
https://github.com/immich-app/immich
synced 2025-11-07 17:27:20 +00:00
chore(mobile) Improve mobile UI (#1038)
This commit is contained in:
parent
1068c4ad23
commit
d31eddf32f
11 changed files with 267 additions and 106 deletions
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:cancellation_token_http/http.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/hive_box.dart';
|
||||
|
|
@ -68,6 +69,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|||
final AuthenticationState _authState;
|
||||
final BackgroundService _backgroundService;
|
||||
final Ref ref;
|
||||
var isGettingBackupInfo = false;
|
||||
|
||||
///
|
||||
/// UI INTERACTION
|
||||
|
|
@ -172,9 +174,10 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|||
/// Get all album on the device
|
||||
/// Get all selected and excluded album from the user's persistent storage
|
||||
/// If this is the first time performing backup - set the default selected album to be
|
||||
/// the one that has all assets (Recent on Android, Recents on iOS)
|
||||
/// the one that has all assets (`Recent` on Android, `Recents` on iOS)
|
||||
///
|
||||
Future<void> _getBackupAlbumsInfo() async {
|
||||
Stopwatch stopwatch = Stopwatch()..start();
|
||||
// Get all albums on the device
|
||||
List<AvailableAlbum> availableAlbums = [];
|
||||
List<AssetPathEntity> albums = await PhotoManager.getAssetPathList(
|
||||
|
|
@ -182,6 +185,8 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|||
type: RequestType.common,
|
||||
);
|
||||
|
||||
log.info('Found ${albums.length} local albums');
|
||||
|
||||
for (AssetPathEntity album in albums) {
|
||||
AvailableAlbum availableAlbum = AvailableAlbum(albumEntity: album);
|
||||
|
||||
|
|
@ -293,6 +298,8 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|||
} catch (e, stackTrace) {
|
||||
log.severe("Failed to generate album from id", e, stackTrace);
|
||||
}
|
||||
|
||||
debugPrint("_getBackupAlbumsInfo takes ${stopwatch.elapsedMilliseconds}ms");
|
||||
}
|
||||
|
||||
///
|
||||
|
|
@ -364,25 +371,29 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|||
return;
|
||||
}
|
||||
|
||||
///
|
||||
/// Get all necessary information for calculating the available albums,
|
||||
/// which albums are selected or excluded
|
||||
/// and then update the UI according to those information
|
||||
///
|
||||
Future<void> getBackupInfo() async {
|
||||
final bool isEnabled = await _backgroundService.isBackgroundBackupEnabled();
|
||||
state = state.copyWith(backgroundBackup: isEnabled);
|
||||
if (state.backupProgress != BackUpProgressEnum.inBackground) {
|
||||
await _getBackupAlbumsInfo();
|
||||
await _updateServerInfo();
|
||||
await _updateBackupAssetCount();
|
||||
if (!isGettingBackupInfo) {
|
||||
isGettingBackupInfo = true;
|
||||
|
||||
var isEnabled = await _backgroundService.isBackgroundBackupEnabled();
|
||||
|
||||
state = state.copyWith(backgroundBackup: isEnabled);
|
||||
|
||||
if (state.backupProgress != BackUpProgressEnum.inBackground) {
|
||||
await _getBackupAlbumsInfo();
|
||||
await _updateServerInfo();
|
||||
await _updateBackupAssetCount();
|
||||
}
|
||||
|
||||
isGettingBackupInfo = false;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Save user selection of selected albums and excluded albums to
|
||||
/// Hive database
|
||||
///
|
||||
void _updatePersistentAlbumsSelection() {
|
||||
final epoch = DateTime.fromMillisecondsSinceEpoch(0, isUtc: true);
|
||||
Box<HiveBackupAlbums> backupAlbumInfoBox =
|
||||
|
|
@ -402,9 +413,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// Invoke backup process
|
||||
///
|
||||
Future<void> startBackupProcess() async {
|
||||
assert(state.backupProgress == BackUpProgressEnum.idle);
|
||||
state = state.copyWith(backupProgress: BackUpProgressEnum.inProgress);
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ class AlbumPreviewPage extends HookConsumerWidget {
|
|||
title: Column(
|
||||
children: [
|
||||
Text(
|
||||
"${album.name} (${album.assetCountAsync})",
|
||||
album.name,
|
||||
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
|
||||
),
|
||||
Padding(
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/immich_colors.dart';
|
||||
import 'package:immich_mobile/modules/backup/models/available_album.model.dart';
|
||||
import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
|
||||
import 'package:immich_mobile/modules/backup/ui/album_info_card.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
|
|
@ -14,10 +15,13 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
|
|||
const BackupAlbumSelectionPage({Key? key}) : super(key: key);
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final availableAlbums = ref.watch(backupProvider).availableAlbums;
|
||||
// final availableAlbums = ref.watch(backupProvider).availableAlbums;
|
||||
final selectedBackupAlbums = ref.watch(backupProvider).selectedBackupAlbums;
|
||||
final excludedBackupAlbums = ref.watch(backupProvider).excludedBackupAlbums;
|
||||
final isDarkTheme = Theme.of(context).brightness == Brightness.dark;
|
||||
final albums = useState<List<AvailableAlbum>>(
|
||||
ref.watch(backupProvider).availableAlbums,
|
||||
);
|
||||
|
||||
useEffect(
|
||||
() {
|
||||
|
|
@ -28,7 +32,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
|
|||
);
|
||||
|
||||
buildAlbumSelectionList() {
|
||||
if (availableAlbums.isEmpty) {
|
||||
if (albums.value.isEmpty) {
|
||||
return const Center(
|
||||
child: ImmichLoadingIndicator(),
|
||||
);
|
||||
|
|
@ -38,17 +42,17 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
|
|||
height: 265,
|
||||
child: ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: availableAlbums.length,
|
||||
itemCount: albums.value.length,
|
||||
physics: const BouncingScrollPhysics(),
|
||||
itemBuilder: ((context, index) {
|
||||
var thumbnailData = availableAlbums[index].thumbnailData;
|
||||
var thumbnailData = albums.value[index].thumbnailData;
|
||||
return Padding(
|
||||
padding: index == 0
|
||||
? const EdgeInsets.only(left: 16.00)
|
||||
: const EdgeInsets.all(0),
|
||||
child: AlbumInfoCard(
|
||||
imageData: thumbnailData,
|
||||
albumInfo: availableAlbums[index],
|
||||
albumInfo: albums.value[index],
|
||||
),
|
||||
);
|
||||
}),
|
||||
|
|
@ -79,15 +83,13 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
|
|||
child: Chip(
|
||||
visualDensity: VisualDensity.compact,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
label: Text(
|
||||
album.name,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.black
|
||||
: Colors.white,
|
||||
color: isDarkTheme ? Colors.black : Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
|
|
@ -119,7 +121,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
|
|||
child: Chip(
|
||||
visualDensity: VisualDensity.compact,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
label: Text(
|
||||
album.name,
|
||||
|
|
@ -143,6 +145,46 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
|
|||
}).toSet();
|
||||
}
|
||||
|
||||
buildSearchBar() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 16.0, right: 16, bottom: 8.0),
|
||||
child: TextFormField(
|
||||
onChanged: (searchValue) {
|
||||
albums.value = ref
|
||||
.watch(backupProvider)
|
||||
.availableAlbums
|
||||
.where(
|
||||
(album) => album.name
|
||||
.toLowerCase()
|
||||
.contains(searchValue.toLowerCase()),
|
||||
)
|
||||
.toList();
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 8.0,
|
||||
vertical: 8.0,
|
||||
),
|
||||
hintText: "Search",
|
||||
hintStyle: TextStyle(
|
||||
color: isDarkTheme ? Colors.white : Colors.grey,
|
||||
fontSize: 14.0,
|
||||
),
|
||||
prefixIcon: const Icon(
|
||||
Icons.search,
|
||||
color: Colors.grey,
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
filled: true,
|
||||
fillColor: isDarkTheme ? Colors.white30 : Colors.grey[200],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
|
|
@ -188,7 +230,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
|
|||
child: Card(
|
||||
margin: const EdgeInsets.all(0),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
side: BorderSide(
|
||||
color: isDarkTheme
|
||||
? const Color.fromARGB(255, 0, 0, 0)
|
||||
|
|
@ -225,8 +267,11 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
|
|||
|
||||
ListTile(
|
||||
title: Text(
|
||||
"backup_album_selection_page_albums_device"
|
||||
.tr(args: [availableAlbums.length.toString()]),
|
||||
"backup_album_selection_page_albums_device".tr(
|
||||
args: [
|
||||
ref.watch(backupProvider).availableAlbums.length.toString()
|
||||
],
|
||||
),
|
||||
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
|
||||
),
|
||||
subtitle: Padding(
|
||||
|
|
@ -254,7 +299,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
|
|||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
elevation: 5,
|
||||
title: Text(
|
||||
|
|
@ -284,6 +329,8 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
|
|||
),
|
||||
),
|
||||
|
||||
buildSearchBar(),
|
||||
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16.0),
|
||||
child: buildAlbumSelectionList(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue