Skip to content

Commit 015872b

Browse files
committed
Implement addTime method and related UI for clock tool
1 parent 0a6508e commit 015872b

3 files changed

Lines changed: 131 additions & 55 deletions

File tree

lib/src/model/clock/clock_tool_controller.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,18 @@ class ClockToolController extends Notifier<ClockState> {
124124
_startActiveSide(playerType.opposite);
125125
}
126126

127+
void addTime(ClockSide playerType, Duration duration) {
128+
if (!state.started || state.flagged != null || duration <= Duration.zero) {
129+
return;
130+
}
131+
132+
final updatedTime = state.getDuration(playerType).value + duration;
133+
_clock.setTime(playerType.chessClockSide, updatedTime);
134+
if (updatedTime > _emergencyThresholds[playerType]!) {
135+
_hasPlayedLowTimeSound[playerType] = false;
136+
}
137+
}
138+
127139
void updateOptions(TimeIncrement timeIncrement) {
128140
final options = ClockOptions.fromTimeIncrement(timeIncrement, type: state.options.type);
129141
final threshold = _calculateEmergencyThreshold(Duration(seconds: timeIncrement.time));

lib/src/view/clock/clock_settings.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,18 @@ class ClockSettings extends ConsumerWidget {
7676
return ClockToolSettingsModal(
7777
clockType: clockType,
7878
timeIncrement: timeIncrement,
79+
started: state.started,
7980
onClockTypeSelected: (type) {
8081
ref.read(clockToolControllerProvider.notifier).updateClockType(type);
8182
},
8283
onTimeSelected: (choice) {
8384
ref.read(clockToolControllerProvider.notifier).updateOptions(choice);
8485
},
86+
onAddTime: (playerType, duration) {
87+
ref
88+
.read(clockToolControllerProvider.notifier)
89+
.addTime(playerType, duration);
90+
},
8591
);
8692
},
8793
);

lib/src/view/clock/clock_tool_settings_modal.dart

Lines changed: 113 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,25 @@ import 'package:lichess_mobile/src/widgets/list.dart';
1111
import 'package:lichess_mobile/src/widgets/non_linear_slider.dart';
1212
import 'package:lichess_mobile/src/widgets/settings.dart';
1313

14+
const _addTimeAmounts = [Duration(seconds: 10), Duration(seconds: 30), Duration(minutes: 1)];
15+
1416
class ClockToolSettingsModal extends StatefulWidget {
1517
const ClockToolSettingsModal({
1618
required this.clockType,
1719
required this.timeIncrement,
20+
required this.started,
1821
required this.onClockTypeSelected,
1922
required this.onTimeSelected,
23+
required this.onAddTime,
2024
super.key,
2125
});
2226

2327
final ClockTimeControlType clockType;
2428
final TimeIncrement timeIncrement;
29+
final bool started;
2530
final ValueSetter<ClockTimeControlType> onClockTypeSelected;
2631
final ValueSetter<TimeIncrement> onTimeSelected;
32+
final void Function(ClockSide playerType, Duration duration) onAddTime;
2733

2834
@override
2935
State<ClockToolSettingsModal> createState() => _ClockToolSettingsModalState();
@@ -32,6 +38,7 @@ class ClockToolSettingsModal extends StatefulWidget {
3238
class _ClockToolSettingsModalState extends State<ClockToolSettingsModal> {
3339
late ClockTimeControlType _clockType;
3440
late TimeIncrement _timeIncrement;
41+
ClockSide _playerType = ClockSide.bottom;
3542

3643
@override
3744
void initState() {
@@ -56,6 +63,11 @@ class _ClockToolSettingsModalState extends State<ClockToolSettingsModal> {
5663
});
5764
}
5865

66+
void _addTime(BuildContext context, ClockSide playerType, Duration duration) {
67+
widget.onAddTime(playerType, duration);
68+
Navigator.of(context).pop();
69+
}
70+
5971
void _submit(BuildContext context) {
6072
widget.onClockTypeSelected(_clockType);
6173
widget.onTimeSelected(_timeIncrement);
@@ -65,73 +77,119 @@ class _ClockToolSettingsModalState extends State<ClockToolSettingsModal> {
6577
@override
6678
Widget build(BuildContext context) {
6779
return BottomSheetScrollableContainer(
80+
padding: Styles.bodyPadding,
6881
children: [
69-
ListSection(
70-
materialFilledCard: true,
71-
children: [
72-
SettingsListTile(
73-
settingsLabel: Text(context.l10n.timeControl),
74-
settingsValue: _clockType.label(context.l10n),
75-
onTap: () {
76-
showChoicePicker<ClockTimeControlType>(
77-
context,
78-
title: Text(context.l10n.timeControl),
79-
choices: ClockTimeControlType.values,
80-
selectedItem: _clockType,
81-
labelBuilder: (type) => Text(type.label(context.l10n)),
82-
onSelectedItemChanged: _setClockType,
83-
);
84-
},
85-
),
86-
ListTile(
87-
title: Text.rich(
88-
TextSpan(
89-
text: '${context.l10n.minutesPerSide}: ',
90-
children: [
91-
TextSpan(
92-
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
93-
text: clockLabelInMinutes(_timeIncrement.time),
94-
),
95-
],
96-
),
82+
if (!widget.started)
83+
ListSection(
84+
materialFilledCard: true,
85+
children: [
86+
SettingsListTile(
87+
settingsLabel: Text(context.l10n.timeControl),
88+
settingsValue: _clockType.label(context.l10n),
89+
onTap: () {
90+
showChoicePicker<ClockTimeControlType>(
91+
context,
92+
title: Text(context.l10n.timeControl),
93+
choices: ClockTimeControlType.values,
94+
selectedItem: _clockType,
95+
labelBuilder: (type) => Text(type.label(context.l10n)),
96+
onSelectedItemChanged: _setClockType,
97+
);
98+
},
9799
),
98-
subtitle: NonLinearSlider(
99-
value: _timeIncrement.time,
100-
values: kAvailableTimesInSeconds,
101-
labelBuilder: clockLabelInMinutes,
102-
onChange: _setTotalTime,
103-
onChangeEnd: _setTotalTime,
100+
ListTile(
101+
title: Text.rich(
102+
TextSpan(
103+
text: '${context.l10n.minutesPerSide}: ',
104+
children: [
105+
TextSpan(
106+
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
107+
text: clockLabelInMinutes(_timeIncrement.time),
108+
),
109+
],
110+
),
111+
),
112+
subtitle: NonLinearSlider(
113+
value: _timeIncrement.time,
114+
values: kAvailableTimesInSeconds,
115+
labelBuilder: clockLabelInMinutes,
116+
onChange: _setTotalTime,
117+
onChangeEnd: _setTotalTime,
118+
),
104119
),
105-
),
106-
ListTile(
107-
title: Text.rich(
108-
TextSpan(
109-
text: '${_clockType.valueInSecondsLabel(context.l10n)}: ',
110-
children: [
111-
TextSpan(
112-
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
113-
text: _timeIncrement.increment.toString(),
114-
),
115-
],
120+
ListTile(
121+
title: Text.rich(
122+
TextSpan(
123+
text: '${_clockType.valueInSecondsLabel(context.l10n)}: ',
124+
children: [
125+
TextSpan(
126+
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
127+
text: _timeIncrement.increment.toString(),
128+
),
129+
],
130+
),
131+
),
132+
subtitle: NonLinearSlider(
133+
value: _timeIncrement.increment,
134+
values: kAvailableIncrementsInSeconds,
135+
onChange: _setIncrement,
136+
onChangeEnd: _setIncrement,
116137
),
117138
),
118-
subtitle: NonLinearSlider(
119-
value: _timeIncrement.increment,
120-
values: kAvailableIncrementsInSeconds,
121-
onChange: _setIncrement,
122-
onChangeEnd: _setIncrement,
139+
],
140+
),
141+
if (widget.started) ...[
142+
const SettingsSectionTitle('Add time'),
143+
ListSection(
144+
materialFilledCard: true,
145+
children: [
146+
SettingsListTile(
147+
settingsLabel: const Text('Player'),
148+
settingsValue: _playerLabel(context, _playerType),
149+
onTap: () {
150+
showChoicePicker<ClockSide>(
151+
context,
152+
title: const Text('Player'),
153+
choices: ClockSide.values,
154+
selectedItem: _playerType,
155+
labelBuilder: (playerType) => Text(_playerLabel(context, playerType)),
156+
onSelectedItemChanged: (playerType) {
157+
setState(() => _playerType = playerType);
158+
},
159+
);
160+
},
123161
),
124-
),
125-
],
126-
),
162+
for (final duration in _addTimeAmounts)
163+
ListTile(
164+
title: Text(_addTimeLabel(duration)),
165+
onTap: () => _addTime(context, _playerType, duration),
166+
),
167+
],
168+
),
169+
],
127170
Padding(
128171
padding: Styles.horizontalBodyPadding,
129172
child: FilledButton(
130-
onPressed: () => _submit(context),
131-
child: Text(context.l10n.mobileOkButton, style: Styles.bold),
173+
onPressed: widget.started ? () => Navigator.of(context).pop() : () => _submit(context),
174+
child: Text(
175+
widget.started ? context.l10n.close : context.l10n.mobileOkButton,
176+
style: Styles.bold,
177+
),
132178
),
133179
),
134180
],
135181
);
136182
}
137183
}
184+
185+
String _playerLabel(BuildContext context, ClockSide playerType) {
186+
return playerType == ClockSide.top ? 'Top' : 'Bottom';
187+
}
188+
189+
String _addTimeLabel(Duration duration) {
190+
final seconds = duration.inSeconds;
191+
if (seconds % 60 == 0) {
192+
return '+${seconds ~/ 60}m';
193+
}
194+
return '+${seconds}s';
195+
}

0 commit comments

Comments
 (0)