mirror of
https://github.com/Dr-Blank/Vaani.git
synced 2025-12-09 04:29:29 +00:00
ui: better sleep timer ui in player and fix auto turn on settings (#43)
* refactor: enhance sleep timer functionality and improve duration formatting * refactor: update sleep timer settings handling * refactor: update cancel icon for sleep timer button * refactor: implement isBetween method for TimeOfDay and simplify sleep timer logic * refactor: update alwaysAutoTurnOnTimer default value and improve icon usage in settings * refactor: remove unused IconButton and update sleep timer preset durations
This commit is contained in:
parent
933bfc5750
commit
12100ffbcd
18 changed files with 755 additions and 383 deletions
|
|
@ -1,17 +1,11 @@
|
|||
import 'package:duration_picker/duration_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:miniplayer/miniplayer.dart';
|
||||
import 'package:vaani/constants/sizes.dart';
|
||||
import 'package:vaani/features/player/providers/currently_playing_provider.dart';
|
||||
import 'package:vaani/features/player/providers/player_form.dart';
|
||||
import 'package:vaani/features/player/view/audiobook_player.dart';
|
||||
import 'package:vaani/features/sleep_timer/core/sleep_timer.dart';
|
||||
import 'package:vaani/features/sleep_timer/providers/sleep_timer_provider.dart'
|
||||
show sleepTimerProvider;
|
||||
import 'package:vaani/settings/app_settings_provider.dart';
|
||||
import 'package:vaani/shared/extensions/duration_format.dart';
|
||||
import 'package:vaani/features/sleep_timer/view/sleep_timer_button.dart';
|
||||
import 'package:vaani/shared/extensions/inverse_lerp.dart';
|
||||
import 'package:vaani/shared/widgets/not_implemented.dart';
|
||||
|
||||
|
|
@ -246,13 +240,13 @@ class PlayerWhenExpanded extends HookConsumerWidget {
|
|||
// chapter list
|
||||
const ChapterSelectionButton(),
|
||||
// settings
|
||||
IconButton(
|
||||
icon: const Icon(Icons.more_horiz),
|
||||
onPressed: () {
|
||||
// show toast
|
||||
showNotImplementedToast(context);
|
||||
},
|
||||
),
|
||||
// IconButton(
|
||||
// icon: const Icon(Icons.more_horiz),
|
||||
// onPressed: () {
|
||||
// // show toast
|
||||
// showNotImplementedToast(context);
|
||||
// },
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
@ -261,128 +255,3 @@ class PlayerWhenExpanded extends HookConsumerWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SleepTimerButton extends HookConsumerWidget {
|
||||
const SleepTimerButton({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final sleepTimer = ref.watch(sleepTimerProvider);
|
||||
// if sleep timer is not active, show the button with the sleep timer icon
|
||||
// if the sleep timer is active, show the remaining time in a pill shaped container
|
||||
return Tooltip(
|
||||
message: 'Sleep Timer',
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
pendingPlayerModals++;
|
||||
// show the sleep timer dialog
|
||||
final resultingDuration = await showDurationPicker(
|
||||
context: context,
|
||||
initialTime: ref
|
||||
.watch(appSettingsProvider)
|
||||
.playerSettings
|
||||
.sleepTimerSettings
|
||||
.defaultDuration,
|
||||
);
|
||||
pendingPlayerModals--;
|
||||
if (resultingDuration != null) {
|
||||
// if 0 is selected, cancel the timer
|
||||
if (resultingDuration.inSeconds == 0) {
|
||||
ref.read(sleepTimerProvider.notifier).cancelTimer();
|
||||
} else {
|
||||
ref.read(sleepTimerProvider.notifier).setTimer(resultingDuration);
|
||||
}
|
||||
}
|
||||
},
|
||||
child: sleepTimer == null
|
||||
? Icon(
|
||||
Icons.timer_rounded,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
)
|
||||
: RemainingSleepTimeDisplay(
|
||||
timer: sleepTimer,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SleepTimerDialog extends HookConsumerWidget {
|
||||
const SleepTimerDialog({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final sleepTimer = ref.watch(sleepTimerProvider);
|
||||
final sleepTimerSettings =
|
||||
ref.watch(appSettingsProvider).playerSettings.sleepTimerSettings;
|
||||
final timerDurationController = useTextEditingController(
|
||||
text: sleepTimerSettings.defaultDuration.inMinutes.toString(),
|
||||
);
|
||||
|
||||
return AlertDialog(
|
||||
title: const Text('Sleep Timer'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text('Set the duration for the sleep timer'),
|
||||
TextField(
|
||||
controller: timerDurationController,
|
||||
keyboardType: TextInputType.number,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Duration in minutes',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
// sleepTimer.setTimer(
|
||||
// Duration(
|
||||
// minutes: int.tryParse(timerDurationController.text) ?? 0,
|
||||
// ),
|
||||
// );
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: const Text('Set Timer'),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RemainingSleepTimeDisplay extends HookConsumerWidget {
|
||||
const RemainingSleepTimeDisplay({
|
||||
super.key,
|
||||
required this.timer,
|
||||
});
|
||||
|
||||
final SleepTimer timer;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final remainingTime = useStream(timer.remainingTimeStream).data;
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 4,
|
||||
),
|
||||
child: Text(
|
||||
timer.timer == null
|
||||
? timer.duration.smartBinaryFormat
|
||||
: remainingTime?.smartBinaryFormat ?? '',
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
color: Theme.of(context).colorScheme.onPrimary,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||
import 'package:list_wheel_scroll_view_nls/list_wheel_scroll_view_nls.dart';
|
||||
import 'package:vaani/features/player/providers/audiobook_player.dart';
|
||||
import 'package:vaani/settings/app_settings_provider.dart';
|
||||
import 'package:vaani/shared/hooks.dart';
|
||||
|
||||
const double itemExtent = 25;
|
||||
|
||||
class SpeedSelector extends HookConsumerWidget {
|
||||
const SpeedSelector({
|
||||
|
|
@ -34,11 +37,11 @@ class SpeedSelector extends HookConsumerWidget {
|
|||
|
||||
// the speed options
|
||||
final minSpeed = min(
|
||||
speeds.reduce((minSpeedSoFar, element) => min(minSpeedSoFar, element)),
|
||||
speeds.reduce(min),
|
||||
playerSettings.minSpeed,
|
||||
);
|
||||
final maxSpeed = max(
|
||||
speeds.reduce((maxSpeedSoFar, element) => max(maxSpeedSoFar, element)),
|
||||
speeds.reduce(max),
|
||||
playerSettings.maxSpeed,
|
||||
);
|
||||
final speedIncrement = playerSettings.speedIncrement;
|
||||
|
|
@ -53,10 +56,9 @@ class SpeedSelector extends HookConsumerWidget {
|
|||
},
|
||||
);
|
||||
|
||||
final scrollController = FixedExtentScrollController(
|
||||
final scrollController = useFixedExtentScrollController(
|
||||
initialItem: availableSpeedsList.indexOf(currentSpeed),
|
||||
);
|
||||
const double itemExtent = 25;
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
|
|
@ -89,7 +91,6 @@ class SpeedSelector extends HookConsumerWidget {
|
|||
availableSpeedsList: availableSpeedsList,
|
||||
speedState: speedState,
|
||||
scrollController: scrollController,
|
||||
itemExtent: itemExtent,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -159,14 +160,12 @@ class SpeedWheel extends StatelessWidget {
|
|||
required this.availableSpeedsList,
|
||||
required this.speedState,
|
||||
required this.scrollController,
|
||||
required this.itemExtent,
|
||||
this.showIncrementButtons = true,
|
||||
});
|
||||
|
||||
final List<double> availableSpeedsList;
|
||||
final ValueNotifier<double> speedState;
|
||||
final FixedExtentScrollController scrollController;
|
||||
final double itemExtent;
|
||||
final bool showIncrementButtons;
|
||||
|
||||
@override
|
||||
|
|
@ -203,7 +202,7 @@ class SpeedWheel extends StatelessWidget {
|
|||
physics: const FixedExtentScrollPhysics(),
|
||||
children: availableSpeedsList
|
||||
.map(
|
||||
(speed) => SpeedLine(itemExtent: itemExtent, speed: speed),
|
||||
(speed) => SpeedLine(speed: speed),
|
||||
)
|
||||
.toList(),
|
||||
onSelectedItemChanged: (index) {
|
||||
|
|
@ -236,11 +235,9 @@ class SpeedWheel extends StatelessWidget {
|
|||
class SpeedLine extends StatelessWidget {
|
||||
const SpeedLine({
|
||||
super.key,
|
||||
required this.itemExtent,
|
||||
required this.speed,
|
||||
});
|
||||
|
||||
final double itemExtent;
|
||||
final double speed;
|
||||
|
||||
@override
|
||||
|
|
@ -250,7 +247,6 @@ class SpeedLine extends StatelessWidget {
|
|||
// a vertical line
|
||||
Expanded(
|
||||
child: Container(
|
||||
// height: itemExtent,
|
||||
// thick if multiple of 1, thin if multiple of 0.5 and transparent if multiple of 0.05
|
||||
width: speed % 0.5 == 0
|
||||
? 3
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue