Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Time handicap in clock tool #685

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 50 additions & 12 deletions lib/src/model/clock/clock_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,15 @@ part 'clock_controller.g.dart';
class ClockController extends _$ClockController {
@override
ClockState build() {
const time = Duration(minutes: 10);
const increment = Duration.zero;
return ClockState.fromOptions(
const ClockOptions(time: Duration(minutes: 10), increment: Duration.zero),
const ClockOptions(
timePlayerTop: time,
timePlayerBottom: time,
incrementPlayerTop: increment,
incrementPlayerBottom: increment,
),
);
}

Expand Down Expand Up @@ -39,16 +46,25 @@ class ClockController extends _$ClockController {
}

if (playerType == ClockPlayerType.top) {
state = state.copyWith(playerTopTime: duration + state.options.increment);
state = state.copyWith(
playerTopTime: duration + state.options.incrementPlayerTop,
);
} else {
state =
state.copyWith(playerBottomTime: duration + state.options.increment);
state = state.copyWith(
playerBottomTime: duration + state.options.incrementPlayerBottom,
);
}
}

void updateOptions(TimeIncrement timeIncrement) =>
state = ClockState.fromTimeIncrement(timeIncrement);

void updateOptionsCustom(
TimeIncrement playerTop,
TimeIncrement playerBottom,
) =>
state = ClockState.fromSeparateTimeIncrements(playerTop, playerBottom);

void setLoser(ClockPlayerType playerType) =>
state = state.copyWith(currentPlayer: null, loser: playerType);

Expand All @@ -66,8 +82,10 @@ class ClockOptions with _$ClockOptions {
const ClockOptions._();

const factory ClockOptions({
required Duration time,
required Duration increment,
required Duration timePlayerTop,
required Duration timePlayerBottom,
required Duration incrementPlayerTop,
required Duration incrementPlayerBottom,
}) = _ClockOptions;
}

Expand All @@ -90,24 +108,44 @@ class ClockState with _$ClockState {

factory ClockState.fromTimeIncrement(TimeIncrement timeIncrement) {
final options = ClockOptions(
time: Duration(seconds: timeIncrement.time),
increment: Duration(seconds: timeIncrement.increment),
timePlayerTop: Duration(seconds: timeIncrement.time),
timePlayerBottom: Duration(seconds: timeIncrement.time),
incrementPlayerTop: Duration(seconds: timeIncrement.increment),
incrementPlayerBottom: Duration(seconds: timeIncrement.increment),
);

return ClockState(
id: DateTime.now().millisecondsSinceEpoch,
options: options,
playerTopTime: options.time,
playerBottomTime: options.time,
playerTopTime: options.timePlayerTop,
playerBottomTime: options.timePlayerBottom,
);
}

factory ClockState.fromSeparateTimeIncrements(
TimeIncrement playerTop,
TimeIncrement playerBottom,
) {
final options = ClockOptions(
timePlayerTop: Duration(seconds: playerTop.time),
timePlayerBottom: Duration(seconds: playerBottom.time),
incrementPlayerTop: Duration(seconds: playerTop.increment),
incrementPlayerBottom: Duration(seconds: playerBottom.increment),
);
return ClockState(
id: DateTime.now().millisecondsSinceEpoch,
options: options,
playerTopTime: options.timePlayerTop,
playerBottomTime: options.timePlayerBottom,
);
}

factory ClockState.fromOptions(ClockOptions options) {
return ClockState(
id: DateTime.now().millisecondsSinceEpoch,
options: options,
playerTopTime: options.time,
playerBottomTime: options.time,
playerTopTime: options.timePlayerTop,
playerBottomTime: options.timePlayerBottom,
);
}

Expand Down
16 changes: 13 additions & 3 deletions lib/src/view/clock/clock_settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,23 @@ class ClockSettings extends ConsumerWidget {
);
return TimeControlModal(
excludeUltraBullet: true,
value: TimeIncrement(
options.time.inSeconds,
options.increment.inSeconds,
topPlayer: TimeIncrement(
options.timePlayerTop.inSeconds,
options.incrementPlayerTop.inSeconds,
),
bottomPlayer: TimeIncrement(
options.timePlayerBottom.inSeconds,
options.incrementPlayerBottom.inSeconds,
),
onSelected: (choice) {
controller.updateOptions(choice);
},
onSelectedCustom: (playerTop, playerBottom) {
controller.updateOptionsCustom(
playerTop,
playerBottom,
);
},
);
},
);
Expand Down
3 changes: 2 additions & 1 deletion lib/src/view/home/quick_game_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ class QuickGameButton extends ConsumerWidget {
),
builder: (BuildContext context) {
return TimeControlModal(
value: playPrefs.timeIncrement,
topPlayer: playPrefs.timeIncrement,
bottomPlayer: playPrefs.timeIncrement,
onSelected: (choice) {
ref
.read(gameSetupPreferencesProvider.notifier)
Expand Down
212 changes: 212 additions & 0 deletions lib/src/view/play/create_custom_time_control_screen.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:lichess_mobile/src/model/common/time_increment.dart';
import 'package:lichess_mobile/src/model/lobby/game_setup.dart';
import 'package:lichess_mobile/src/styles/styles.dart';
import 'package:lichess_mobile/src/utils/l10n_context.dart';
import 'package:lichess_mobile/src/widgets/buttons.dart';
import 'package:lichess_mobile/src/widgets/list.dart';
import 'package:lichess_mobile/src/widgets/non_linear_slider.dart';
import 'package:lichess_mobile/src/widgets/platform.dart';

class CreateCustomTimeControlScreen extends StatelessWidget {
final void Function(TimeIncrement playerTop, TimeIncrement playerBottom)
onSubmit;
final TimeIncrement topPlayer;
final TimeIncrement bottomPlayer;

const CreateCustomTimeControlScreen({
required this.onSubmit,
required this.topPlayer,
required this.bottomPlayer,
});

@override
Widget build(BuildContext context) {
return PlatformWidget(
androidBuilder: _androidBuilder,
iosBuilder: _iosBuilder,
);
}

Widget _androidBuilder(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(context.l10n.custom)),
body: _Body(
onSubmit: onSubmit,
topPlayer: topPlayer,
bottomPlayer: bottomPlayer,
),
);
}

Widget _iosBuilder(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(),
child: _Body(
onSubmit: onSubmit,
topPlayer: topPlayer,
bottomPlayer: bottomPlayer,
),
);
}
}

class _Body extends StatefulWidget {
final void Function(
TimeIncrement timeTopPlayer,
TimeIncrement timeBottomPlayer,
) onSubmit;
final TimeIncrement topPlayer;
final TimeIncrement bottomPlayer;

const _Body({
required this.onSubmit,
required this.topPlayer,
required this.bottomPlayer,
});

@override
State<_Body> createState() => _BodyState();
}

class _BodyState extends State<_Body> {
late int timeTopPlayer;
late int incrementTopPlayer;
late int timeBottomPlayer;
late int incrementBottomPlayer;

@override
void initState() {
timeTopPlayer = widget.topPlayer.time;
timeBottomPlayer = widget.bottomPlayer.time;
incrementTopPlayer = widget.topPlayer.increment;
incrementBottomPlayer = widget.bottomPlayer.increment;
super.initState();
}

@override
Widget build(BuildContext context) {
void onSubmit(TimeIncrement topPlayer, TimeIncrement bottomPlayer) {
Navigator.pop(context);
Navigator.pop(context);
widget.onSubmit(topPlayer, bottomPlayer);
}

return Padding(
padding: Styles.bodyPadding,
child: ListView(
children: [
_PlayerTimeSlider(
playerNr: 1,
timeSec: timeTopPlayer,
incrementSec: incrementTopPlayer,
updateTime: (int time) => setState(() => timeTopPlayer = time),
updateIncrement: (int increment) =>
setState(() => incrementTopPlayer = increment),
),
_PlayerTimeSlider(
playerNr: 2,
timeSec: timeBottomPlayer,
incrementSec: incrementBottomPlayer,
updateTime: (int time) => setState(() => timeBottomPlayer = time),
updateIncrement: (int increment) =>
setState(() => incrementBottomPlayer = increment),
),
FatButton(
semanticsLabel: context.l10n.apply,
child: Text(context.l10n.apply),
onPressed: () => onSubmit(
TimeIncrement(timeTopPlayer, incrementTopPlayer),
TimeIncrement(timeBottomPlayer, incrementBottomPlayer),
),
),
],
),
);
}
}

class _PlayerTimeSlider extends StatelessWidget {
const _PlayerTimeSlider({
required this.playerNr,
required this.timeSec,
required this.incrementSec,
required this.updateTime,
required this.updateIncrement,
});

final int playerNr;
final int timeSec;
final int incrementSec;
final void Function(int time) updateTime;
final void Function(int time) updateIncrement;

@override
Widget build(BuildContext context) {
return ListView(
shrinkWrap: true,
children: [
Text(
'${context.l10n.player} $playerNr',
style: Styles.title,
),
PlatformListTile(
padding: EdgeInsets.zero,
title: Text(
'${context.l10n.time}: ${timeSec < 60 ? context.l10n.nbSeconds(timeSec) : context.l10n.nbMinutes(_secToMin(timeSec))}',
),
subtitle: NonLinearSlider(
value: timeSec,
values: kAvailableTimesInSeconds,
labelBuilder: _clockTimeLabel,
onChange: Theme.of(context).platform == TargetPlatform.iOS
? (num value) {
updateTime(value.toInt());
}
: null,
onChangeEnd: (num value) {
updateTime(value.toInt());
},
),
),
PlatformListTile(
padding: EdgeInsets.zero,
title: Text(
'${context.l10n.increment}: ${context.l10n.nbSeconds(incrementSec)}',
),
subtitle: NonLinearSlider(
value: incrementSec,
values: kAvailableIncrementsInSeconds,
labelBuilder: (num sec) => sec.toString(),
onChange: Theme.of(context).platform == TargetPlatform.iOS
? (num value) {
updateIncrement(value.toInt());
}
: null,
onChangeEnd: (num value) {
updateIncrement(value.toInt());
},
),
),
],
);
}
}

int _secToMin(num sec) => sec ~/ 60;

String _clockTimeLabel(num seconds) {
switch (seconds) {
case 0:
return '0';
case 45:
return '¾';
case 30:
return '½';
case 15:
return '¼';
default:
return _secToMin(seconds).toString();
}
}
Loading