Skip to content

Commit

Permalink
Merge pull request #23 from dillonfagan/dev
Browse files Browse the repository at this point in the history
v1.2 - Bug Fixes, Code Cleanup
  • Loading branch information
dillonfagan authored Feb 8, 2024
2 parents c2e695f + a2796e5 commit c384043
Show file tree
Hide file tree
Showing 11 changed files with 219 additions and 119 deletions.
17 changes: 17 additions & 0 deletions lib/common/alarm.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/foundation.dart';

class Alarm {
static final AudioPlayer audioPlayer = AudioPlayer(playerId: '#alarm');

void play() async {
if (kDebugMode) {
return;
}

await audioPlayer.play(
UrlSource('sounds/alarm.mp3'),
volume: 1.0,
);
}
}
115 changes: 33 additions & 82 deletions lib/pages/timer/timer.dart
Original file line number Diff line number Diff line change
@@ -1,121 +1,72 @@
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mob_app/pages/timer/helpers/ticker.dart';
import 'package:mob_app/providers/break_provider.dart';
import 'package:mob_app/providers/driver_provider.dart';
import 'package:mob_app/providers/mob_controller_provider.dart';
import 'package:mob_app/providers/mob_state_provider.dart';
import 'package:mob_app/providers/turn_length_provider.dart';
import 'package:mob_app/providers/timer_provider.dart';

import 'widgets/appbar_factory.dart';
import 'widgets/break_button.dart';
import 'widgets/display.dart';
import 'widgets/next_button.dart';

class TimerPage extends ConsumerStatefulWidget {
TimerPage({super.key});

final AudioPlayer audioPlayer = AudioPlayer(playerId: '#timer');

@override
ConsumerState<TimerPage> createState() => _TimerPageState();
}

class _TimerPageState extends ConsumerState<TimerPage> {
late final _ticker = Ticker(
onTick: _update,
onStop: _playAlarm,
);
class TimerPage extends ConsumerWidget {
const TimerPage({super.key});

@override
void initState() {
super.initState();
_start();
}

@override
void dispose() {
_ticker.stop();
super.dispose();
}

void _update() {
setState(() {});
}

void _start({int? seconds}) {
setState(() => _ticker.start(seconds ?? ref.read(turnLengthProvider)));
}

void _stop() {
setState(() => _ticker.stop());
}

void _playAlarm() async {
await widget.audioPlayer.play(
UrlSource('sounds/alarm.mp3'),
volume: 1.0,
);
}

@override
Widget build(BuildContext context) {
final timeToBreak = ref.watch(breakProvider);
Widget build(BuildContext context, WidgetRef ref) {
final controller = ref.read(mobControllerProvider);
final mobState = ref.watch(mobStateProvider);
final driver = ref.watch(driverProvider);
final navigator = ref.watch(navigatorProvider);

String title = mobState == MobState.onBreak ? 'Break' : driver.name;

return Scaffold(
appBar: AppBarFactory.build(
context: context,
controller: ref.read(mobControllerProvider),
controller: controller,
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
TimerDisplay(seconds: _ticker.secondsRemaining),
TimerDisplay(seconds: ref.watch(secondsProvider)),
const SizedBox(height: 16),
_ticker.isActive
? Text(
title,
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 36),
)
: timeToBreak
? BreakButton(
onPressed: () {
ref
.read(mobStateProvider.notifier)
.update(MobState.onBreak);
_start(seconds: 600);
},
)
: NextButton(
labelText: navigator.name,
onPressed: () {
ref
.read(mobStateProvider.notifier)
.update(MobState.mobbing);
ref.read(driverIndexProvider.notifier).next();
_start();
},
),
if (mobState == MobState.initialLoad)
StartButton(
onPressed: () => controller.start(),
),
if (mobState == MobState.mobbing || mobState == MobState.onBreak)
Text(
title,
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 36),
),
if (mobState == MobState.timeToBreak)
BreakButton(
onPressed: () => controller.startBreak(),
),
if (mobState == MobState.waiting)
NextButton(
labelText: navigator.name,
onPressed: () => controller.startNextTurn(),
),
],
),
),
floatingActionButton: Visibility(
visible: _ticker.isActive,
visible: controller.isTurnSkippable,
child: FloatingActionButton(
onPressed: () {
_stop();
ref.read(mobStateProvider.notifier).update(MobState.waiting);
if (mobState == MobState.waiting) {
controller.skipTurn();
} else {
controller.endTurn();
}
},
backgroundColor: Colors.amber,
child: const Icon(
Icons.skip_next_rounded,
size: 36,
Expand Down
9 changes: 8 additions & 1 deletion lib/pages/timer/widgets/appbar_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,19 @@ class AppBarFactory {
elevation: 0,
leading: IconButton(
onPressed: () {
controller.reset();
controller.pause();

showDialog(
context: context,
builder: (context) {
return EndMobAlertDialog(
onCancel: () {
Navigator.of(context).pop();
controller.resume();
},
onConfirm: () {
controller.reset();

Navigator.of(context).pop();
Navigator.of(context).pop(MaterialPageRoute(
builder: (_) => const SetupPage(),
Expand Down
9 changes: 7 additions & 2 deletions lib/pages/timer/widgets/end_mob_alert.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import 'package:flutter/material.dart';

class EndMobAlertDialog extends StatelessWidget {
const EndMobAlertDialog({super.key, required this.onConfirm});
const EndMobAlertDialog({
super.key,
required this.onCancel,
required this.onConfirm,
});

final Function() onCancel;
final Function() onConfirm;

@override
Expand All @@ -14,7 +19,7 @@ class EndMobAlertDialog extends StatelessWidget {
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
onPressed: onCancel,
style: TextButton.styleFrom(foregroundColor: Colors.grey.shade200),
child: const Text(
'Cancel',
Expand Down
28 changes: 28 additions & 0 deletions lib/pages/timer/widgets/next_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,31 @@ class NextButton extends StatelessWidget {
);
}
}

class StartButton extends StatelessWidget {
const StartButton({
super.key,
required this.onPressed,
});

final Function() onPressed;

@override
Widget build(BuildContext context) {
return ElevatedButton.icon(
onPressed: onPressed,
icon: const Icon(
Icons.play_arrow,
size: 36,
),
label: const Padding(
padding: EdgeInsets.fromLTRB(8, 0, 8, 0),
child: Text(
'Start',
style: TextStyle(fontSize: 36),
),
),
style: ElevatedButton.styleFrom(shape: const StadiumBorder()),
);
}
}
76 changes: 73 additions & 3 deletions lib/providers/mob_controller_provider.dart
Original file line number Diff line number Diff line change
@@ -1,25 +1,95 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mob_app/common/alarm.dart';
import 'package:mob_app/providers/driver_provider.dart';
import 'package:mob_app/providers/mob_state_provider.dart';
import 'package:mob_app/providers/timer_provider.dart';
import 'package:mob_app/providers/turns_provider.dart';

import 'break_provider.dart';
import 'turn_length_provider.dart';

const int fortyFiveMinutes = 2700;

enum MobState {
initialLoad,
mobbing,
waiting,
timeToBreak,
onBreak,
}

class MobController {
const MobController(this.ref);
MobController(this.ref);

final Ref ref;

final _alarm = Alarm();

bool get isTurnSkippable {
final state = ref.read(mobStateProvider);
return state == MobState.mobbing ||
state == MobState.onBreak ||
state == MobState.waiting;
}

void start() {
ref.read(mobStateProvider.notifier).update(MobState.mobbing);
ref.read(secondsProvider.notifier).state = ref.read(turnLengthProvider);
resume();
}

void startNextTurn() {
ref.read(driverIndexProvider.notifier).next();
start();
}

void startBreak() {
ref.read(mobStateProvider.notifier).update(MobState.onBreak);
ref.read(secondsProvider.notifier).state = 600;
resume();
}

void skipTurn() {
ref.read(driverIndexProvider.notifier).next();
}

void pause() {
ref.read(timerProvider).cancel();
}

void resume() {
if (ref.read(mobStateProvider) == MobState.initialLoad) {
return;
}

ref.read(timerProvider.notifier).state = getTimer(ref, () {
_alarm.play();
endTurn();
});
}

void endTurn() {
ref.read(timerProvider).cancel();

if (ref.read(mobStateProvider) == MobState.onBreak) {
reset();
} else {
ref.read(turnsProvider.notifier).state += 1;
}

final timeToBreak = ref.read(breakProvider);
if (timeToBreak) {
ref.read(mobStateProvider.notifier).update(MobState.timeToBreak);
return;
}

ref.read(mobStateProvider.notifier).update(MobState.waiting);
}

void reset() {
ref.read(turnsProvider.notifier).reset();
ref.read(turnsProvider.notifier).state = 0;
ref.read(driverIndexProvider.notifier).reset();
ref.read(mobStateProvider.notifier).update(MobState.waiting);
ref.read(mobStateProvider.notifier).update(MobState.initialLoad);
}
}

Expand Down
2 changes: 1 addition & 1 deletion lib/providers/mob_state_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import 'package:mob_app/providers/mob_controller_provider.dart';
class MobStateNotifier extends Notifier<MobState> {
@override
MobState build() {
return MobState.waiting;
return MobState.initialLoad;
}

void update(MobState newState) {
Expand Down
25 changes: 25 additions & 0 deletions lib/providers/timer_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import 'dart:async';

import 'package:flutter_riverpod/flutter_riverpod.dart';

final secondsProvider = StateProvider<int>((ref) => 0);

final timerProvider = StateProvider<Timer>((ref) {
return Timer.periodic(const Duration(seconds: 1), (timer) {
timer.cancel();
});
});

Timer getTimer(Ref ref, Function() onStop) {
return Timer.periodic(
const Duration(seconds: 1),
(timer) {
if (ref.read(secondsProvider) <= 0) {
onStop();
return;
}

ref.read(secondsProvider.notifier).state -= 1;
},
);
}
13 changes: 1 addition & 12 deletions lib/providers/turns_provider.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,3 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';

class TurnsNotifier extends Notifier<int> {
@override
int build() {
return 0;
}

void reset() {
state = 0;
}
}

final turnsProvider = NotifierProvider<TurnsNotifier, int>(TurnsNotifier.new);
final turnsProvider = StateProvider<int>((ref) => 0);
Loading

0 comments on commit c384043

Please sign in to comment.