-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #67 from GabinL21/add-session-stats
feat(stats): add session stats
- Loading branch information
Showing
24 changed files
with
800 additions
and
60 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import 'package:bloc/bloc.dart'; | ||
import 'package:equatable/equatable.dart'; | ||
import 'package:kubrs_app/solve/bloc/solve_bloc.dart'; | ||
import 'package:kubrs_app/solve/model/solve.dart'; | ||
import 'package:kubrs_app/solve/repository/solve_repository.dart'; | ||
|
||
part 'session_event.dart'; | ||
part 'session_state.dart'; | ||
|
||
class SessionBloc extends Bloc<SessionEvent, SessionState> { | ||
SessionBloc({required this.solveBloc, required this.solveRepository}) | ||
: super(SessionInitial()) { | ||
solveRepository | ||
.getStreamOfSolvesSince(sessionStart) | ||
.listen((_) => add(const RefreshSessionSolves())); | ||
on<RefreshSessionSolves>((event, emit) async { | ||
emit(SessionLoading(state.solves)); | ||
final sessionSolves = await solveRepository.getSolvesSince( | ||
sessionStart, | ||
offline: true, // Limit Firestore server calls | ||
); | ||
emit(SessionLoaded(sessionSolves)); | ||
}); | ||
} | ||
final SolveBloc solveBloc; | ||
final SolveRepository solveRepository; | ||
final DateTime sessionStart = DateTime.now(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
part of 'session_bloc.dart'; | ||
|
||
abstract class SessionEvent extends Equatable { | ||
const SessionEvent(); | ||
|
||
@override | ||
List<Object> get props => []; | ||
} | ||
|
||
class RefreshSessionSolves extends SessionEvent { | ||
const RefreshSessionSolves(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
part of 'session_bloc.dart'; | ||
|
||
abstract class SessionState extends Equatable { | ||
const SessionState(this.solves); | ||
|
||
final List<Solve> solves; | ||
|
||
@override | ||
List<Object> get props => [solves]; | ||
} | ||
|
||
class SessionInitial extends SessionState { | ||
SessionInitial() : super(List.empty()); | ||
} | ||
|
||
class SessionLoading extends SessionState { | ||
const SessionLoading(super.solves); | ||
} | ||
|
||
class SessionLoaded extends SessionState { | ||
const SessionLoaded(super.solves); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:flutter_bloc/flutter_bloc.dart'; | ||
import 'package:kubrs_app/session/bloc/session_bloc.dart'; | ||
import 'package:kubrs_app/solve/model/solve.dart'; | ||
import 'package:kubrs_app/stats/utils/stats_calculator.dart'; | ||
|
||
class SessionStats extends StatelessWidget { | ||
const SessionStats({super.key}); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return BlocBuilder<SessionBloc, SessionState>( | ||
builder: (context, timerState) { | ||
final solves = timerState.solves; | ||
final textStyle = _getTextStyle(context); | ||
return Column( | ||
crossAxisAlignment: CrossAxisAlignment.start, | ||
children: [ | ||
_getSolveCountStatText(solves, textStyle), | ||
_getBestSolveText(solves, textStyle), | ||
_getWorstSolveText(solves, textStyle), | ||
_getSolveMeanText(solves, textStyle), | ||
_getLastAverageText(solves, textStyle), | ||
], | ||
); | ||
}, | ||
); | ||
} | ||
|
||
TextStyle? _getTextStyle(BuildContext context) { | ||
return Theme.of(context).textTheme.displaySmall?.copyWith( | ||
color: Theme.of(context).colorScheme.secondary, | ||
); | ||
} | ||
|
||
Widget _getSolveCountStatText(List<Solve> solves, TextStyle? textStyle) { | ||
final count = solves.length; | ||
return Text( | ||
'Count: $count', | ||
style: textStyle, | ||
); | ||
} | ||
|
||
Widget _getBestSolveText(List<Solve> solves, TextStyle? textStyle) { | ||
final bestStat = StatsCalculator.computeBest(solves); | ||
final statName = bestStat.displayedName; | ||
final statValue = bestStat.displayedValue; | ||
return Text( | ||
'$statName: $statValue', | ||
style: textStyle, | ||
); | ||
} | ||
|
||
Widget _getWorstSolveText(List<Solve> solves, TextStyle? textStyle) { | ||
final worstStat = StatsCalculator.computeWorst(solves); | ||
final statName = worstStat.displayedName; | ||
final statValue = worstStat.displayedValue; | ||
return Text( | ||
'$statName: $statValue', | ||
style: textStyle, | ||
); | ||
} | ||
|
||
Widget _getSolveMeanText(List<Solve> solves, TextStyle? textStyle) { | ||
final meanStat = StatsCalculator.computeMean(solves); | ||
final statValue = meanStat.displayedValue; | ||
return Text( | ||
'Mean: $statValue', | ||
style: textStyle, | ||
); | ||
} | ||
|
||
Widget _getLastAverageText(List<Solve> solves, TextStyle? textStyle) { | ||
final averageStat = StatsCalculator.computeAverage(solves, 5); | ||
final statName = averageStat.displayedName; | ||
final statValue = averageStat.displayedValue; | ||
return Text( | ||
'Last $statName: $statValue', | ||
style: textStyle, | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,24 @@ | ||
class DurationFormatter { | ||
static String format(Duration duration) { | ||
final minutesStr = duration.inMinutes.toString(); | ||
final roundedDuration = _roundDuration(duration); | ||
final minutesStr = roundedDuration.inMinutes.toString(); | ||
final secondsStr = | ||
duration.inSeconds.remainder(60).toString().padLeft(2, '0'); | ||
final millisecondsStr = (duration.inMilliseconds.remainder(1000) / 10) | ||
.floor() | ||
.toString() | ||
.padLeft(2, '0'); | ||
roundedDuration.inSeconds.remainder(60).toString().padLeft(2, '0'); | ||
final millisecondsStr = | ||
(roundedDuration.inMilliseconds.remainder(1000) / 10) | ||
.round() | ||
.toString() | ||
.padLeft(2, '0'); | ||
var textStr = '$secondsStr.$millisecondsStr'; | ||
if (duration >= const Duration(minutes: 1)) { | ||
textStr = '$minutesStr:$textStr'; | ||
} | ||
return textStr; | ||
} | ||
|
||
static Duration _roundDuration(Duration duration) { | ||
final milliseconds = duration.inMilliseconds; | ||
final roundedMilliseconds = (milliseconds / 10).round() * 10; | ||
return Duration(milliseconds: roundedMilliseconds); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import 'package:equatable/equatable.dart'; | ||
import 'package:kubrs_app/solve/utils/duration_formatter.dart'; | ||
import 'package:kubrs_app/stats/model/stat.dart'; | ||
|
||
class AverageStat extends Stat with EquatableMixin { | ||
AverageStat(this._value, this._nbSolves) : _dnf = false; | ||
AverageStat.empty(this._nbSolves) | ||
: _value = null, | ||
_dnf = false; | ||
AverageStat.dnf(this._nbSolves) | ||
: _value = null, | ||
_dnf = true; | ||
|
||
final int? _value; | ||
final int _nbSolves; | ||
final bool _dnf; | ||
|
||
@override | ||
String get displayedName { | ||
return 'Ao$_nbSolves'; | ||
} | ||
|
||
@override | ||
String get displayedValue { | ||
if (_dnf) return 'DNF'; | ||
if (_value == null) return '-'; | ||
final duration = Duration(milliseconds: _value!); | ||
return DurationFormatter.format(duration); | ||
} | ||
|
||
@override | ||
List<Object?> get props => [_nbSolves, _value, _dnf]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import 'package:equatable/equatable.dart'; | ||
import 'package:kubrs_app/solve/utils/duration_formatter.dart'; | ||
import 'package:kubrs_app/stats/model/stat.dart'; | ||
|
||
class BestStat extends Stat with EquatableMixin { | ||
BestStat(this._value) : _dnf = false; | ||
BestStat.empty() | ||
: _value = null, | ||
_dnf = false; | ||
BestStat.dnf() | ||
: _value = null, | ||
_dnf = true; | ||
|
||
final int? _value; | ||
final bool _dnf; | ||
|
||
@override | ||
String get displayedName { | ||
return 'Best'; | ||
} | ||
|
||
@override | ||
String get displayedValue { | ||
if (_dnf) return 'DNF'; | ||
if (_value == null) return '-'; | ||
final duration = Duration(milliseconds: _value!); | ||
return DurationFormatter.format(duration); | ||
} | ||
|
||
@override | ||
List<Object?> get props => [_value, _dnf]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import 'package:equatable/equatable.dart'; | ||
import 'package:kubrs_app/solve/utils/duration_formatter.dart'; | ||
import 'package:kubrs_app/stats/model/stat.dart'; | ||
|
||
class MeanStat extends Stat with EquatableMixin { | ||
MeanStat(this._value, this._nbSolves) : _dnf = false; | ||
MeanStat.empty(this._nbSolves) | ||
: _value = null, | ||
_dnf = false; | ||
MeanStat.dnf(this._nbSolves) | ||
: _value = null, | ||
_dnf = true; | ||
|
||
final int? _value; | ||
final int _nbSolves; | ||
final bool _dnf; | ||
|
||
@override | ||
String get displayedName { | ||
return 'Mo$_nbSolves'; | ||
} | ||
|
||
@override | ||
String get displayedValue { | ||
if (_dnf) return 'DNF'; | ||
if (_value == null) return '-'; | ||
final duration = Duration(milliseconds: _value!); | ||
return DurationFormatter.format(duration); | ||
} | ||
|
||
@override | ||
List<Object?> get props => [_nbSolves, _value, _dnf]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
abstract class Stat { | ||
String get displayedName; | ||
String get displayedValue; | ||
|
||
@override | ||
String toString() { | ||
final name = displayedName; | ||
final score = displayedValue; | ||
return '$name: $score'; | ||
} | ||
} |
Oops, something went wrong.