Skip to content

Commit

Permalink
refactor: show overnight upload in backup controller
Browse files Browse the repository at this point in the history
  • Loading branch information
shenlong-tanwen committed Apr 11, 2024
1 parent 545904c commit 1a6e85f
Show file tree
Hide file tree
Showing 5 changed files with 227 additions and 309 deletions.
129 changes: 129 additions & 0 deletions mobile/lib/modules/backup/ui/overnight_backup.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import 'dart:math';

import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/backup/models/backup_state.model.dart';
import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
import 'package:wakelock_plus/wakelock_plus.dart';

class OvernightBackup extends HookConsumerWidget {
final Function() stopOvernightBackup;

const OvernightBackup({required this.stopOvernightBackup, super.key});

@override
Widget build(BuildContext context, WidgetRef ref) {
final animationController =
useAnimationController(duration: const Duration(hours: 1));
final reset = useState(false);
final from = useRef<Alignment>(Alignment.center);
final to = useRef<Alignment>(Alignment.center);
final tween = AlignmentTween(begin: from.value, end: to.value);

ref.listen(
backupProvider.select((value) => value.backupProgress),
(prev, next) {
if (prev == BackUpProgressEnum.inProgress &&
next != BackUpProgressEnum.inProgress) {
stopOvernightBackup();
}
},
);

void randomizeAlignment() {
final random = Random();
from.value = to.value;
final currentAlign = to.value;
var newAlignment = currentAlign;
do {
newAlignment = switch (random.nextInt(9)) {
0 => Alignment.bottomCenter,
1 => Alignment.bottomLeft,
2 => Alignment.bottomRight,
3 => Alignment.center,
4 => Alignment.centerLeft,
5 => Alignment.centerRight,
6 => Alignment.topCenter,
7 => Alignment.topLeft,
8 => Alignment.topRight,
_ => Alignment.center,
};
} while (newAlignment == currentAlign);
to.value = newAlignment;

animationController.reset();
animationController.forward();
WakelockPlus.enable();
}

void onAnimationStateChange(AnimationStatus status) {
if (status == AnimationStatus.completed) {
/// This is used to force a rebuild of the widget to call the randomizeAlignment() method
/// through the useEffect hook which takes care of animating the icon to the new alignment
reset.value = !reset.value;
}
}

useEffect(
() {
WidgetsBinding.instance.addPostFrameCallback((_) {
animationController.addStatusListener(onAnimationStateChange);
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
// Start animating
reset.value = !reset.value;
});
return () {
WakelockPlus.disable();
animationController.removeStatusListener(onAnimationStateChange);
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
};
},
[],
);

/// The following effect is called on each rebuild of the widget and handles the starts the animation
/// This is also called on screen orientation change and handles updating the alignment and size of the icon
/// accordingly
useEffect(() {
randomizeAlignment();
return null;
});

return Stack(
children: [
Positioned.fill(
child: AlignTransition(
alignment: tween.animate(animationController),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.upload_rounded,
size: context.height / 4,
color: Colors.grey[850],
),
Text(
"overnight_upload_inprogress",
style: context.textTheme.titleLarge
?.copyWith(color: Colors.grey[800]),
).tr(),
const SizedBox(height: 10),
ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStatePropertyAll(Colors.grey[850]),
),
onPressed: stopOvernightBackup,
child: const Text("overnight_upload_stop").tr(),
),
],
),
),
),
],
);
}
}
131 changes: 98 additions & 33 deletions mobile/lib/modules/backup/views/backup_controller_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,73 @@ import 'package:immich_mobile/modules/backup/providers/manual_upload.provider.da
import 'package:immich_mobile/modules/backup/ui/current_backup_asset_info_box.dart';
import 'package:immich_mobile/modules/backup/models/backup_state.model.dart';
import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
import 'package:immich_mobile/modules/backup/ui/overnight_backup.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';

@RoutePage()
class BackupControllerPage extends HookConsumerWidget {
const BackupControllerPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final backupProgress =
ref.watch(backupProvider.select((value) => value.backupProgress));
final showOvernightBackup = useState(false);

void startBackup() {
ref.watch(errorBackupListProvider.notifier).empty();
if (ref.watch(backupProvider).backupProgress !=
BackUpProgressEnum.inBackground) {
ref.watch(backupProvider.notifier).startBackupProcess();
}
}

void stopBackup() {
if (backupProgress == BackUpProgressEnum.manualInProgress) {
ref.read(manualUploadProvider.notifier).cancelBackup();
} else {
ref.read(backupProvider.notifier).cancelBackup();
}
}

void startOvernightBackup() {
showOvernightBackup.value = true;
startBackup();
}

void stopOvernightBackup() {
showOvernightBackup.value = false;
stopBackup();
}

return ValueListenableBuilder(
valueListenable: showOvernightBackup,
child: PopScope(
canPop: false,
child: OvernightBackup(stopOvernightBackup: stopOvernightBackup),
),
builder: (_, show, child) => show
? child!
: _BackupController(
startBackup: startBackup,
stopBackup: stopBackup,
startOvernightBackup: startOvernightBackup,
),
);
}
}

class _BackupController extends HookConsumerWidget {
final void Function() startBackup;
final void Function() stopBackup;
final void Function() startOvernightBackup;

const _BackupController({
required this.startBackup,
required this.stopBackup,
required this.startOvernightBackup,
});

@override
Widget build(BuildContext context, WidgetRef ref) {
Expand Down Expand Up @@ -128,7 +188,7 @@ class BackupControllerPage extends HookConsumerWidget {
padding: const EdgeInsets.only(top: 8.0),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
borderRadius: const BorderRadius.all(Radius.circular(20)),
side: BorderSide(
color: context.isDarkTheme
? const Color.fromARGB(255, 56, 56, 56)
Expand Down Expand Up @@ -180,14 +240,6 @@ class BackupControllerPage extends HookConsumerWidget {
);
}

void startBackup() {
ref.watch(errorBackupListProvider.notifier).empty();
if (ref.watch(backupProvider).backupProgress !=
BackUpProgressEnum.inBackground) {
ref.watch(backupProvider.notifier).startBackupProcess();
}
}

Widget buildBackupButton() {
return Padding(
padding: const EdgeInsets.only(
Expand All @@ -203,14 +255,7 @@ class BackupControllerPage extends HookConsumerWidget {
backgroundColor: Colors.red[300],
// padding: const EdgeInsets.all(14),
),
onPressed: () {
if (backupState.backupProgress ==
BackUpProgressEnum.manualInProgress) {
ref.read(manualUploadProvider.notifier).cancelBackup();
} else {
ref.read(backupProvider.notifier).cancelBackup();
}
},
onPressed: stopBackup,
child: const Text(
"backup_controller_page_cancel",
style: TextStyle(
Expand All @@ -219,15 +264,43 @@ class BackupControllerPage extends HookConsumerWidget {
),
).tr(),
)
: ElevatedButton(
onPressed: shouldBackup ? startBackup : null,
child: const Text(
"backup_controller_page_start_backup",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (Platform.isIOS)
ElevatedButton(
onPressed: shouldBackup ? startOvernightBackup : null,
style: context.themeData.elevatedButtonTheme.style
?.copyWith(
backgroundColor:
MaterialStateProperty.resolveWith<Color?>(
(Set<MaterialState> states) {
if (!states.contains(MaterialState.disabled)) {
return context.colorScheme.secondary;
}
return null;
},
),
),
child: const Text(
"overnight_upload_start",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
).tr(),
),
ElevatedButton(
onPressed: shouldBackup ? startBackup : null,
child: const Text(
"backup_controller_page_start_backup",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
).tr(),
),
).tr(),
],
),
),
);
Expand Down Expand Up @@ -268,14 +341,6 @@ class BackupControllerPage extends HookConsumerWidget {
),
),
actions: [
if (Platform.isIOS)
IconButton(
onPressed: () => context.pushRoute(const OvernightUploadRoute()),
splashRadius: 24,
icon: const Icon(
Icons.bedtime_outlined,
),
),
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: IconButton(
Expand Down

0 comments on commit 1a6e85f

Please sign in to comment.