Skip to content

Commit 6b585d4

Browse files
committed
Fix account preferences logic
1 parent 5d8f7fd commit 6b585d4

6 files changed

Lines changed: 777 additions & 593 deletions

File tree

lib/src/model/account/account_preferences.dart

Lines changed: 76 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -13,37 +13,34 @@ import 'package:lichess_mobile/src/model/settings/preferences_storage.dart';
1313
part 'account_preferences.freezed.dart';
1414
part 'account_preferences.g.dart';
1515

16+
/// Represents the account preferences for a user.
17+
///
18+
/// If the user is authenticated, the preferences are fetched from the server. If the user is not authenticated, the preferences are fetched from local storage.
19+
/// Technically an anonymous user does not have account preferences, but we still store them locally for convenience, because some settings are useful even for anonymous users (e.g. piece notation, premoves, etc.).
1620
@Freezed(fromJson: true, toJson: true)
1721
sealed class AccountPrefState with _$AccountPrefState implements Serializable {
1822
const AccountPrefState._();
1923

2024
const factory AccountPrefState({
2125
// game display
22-
@JsonKey(name: 'zen', fromJson: _zenFromJson, toJson: _zenToJson) required Zen zenMode,
23-
@JsonKey(fromJson: _pieceNotationFromJson, toJson: _pieceNotationToJson)
24-
required PieceNotation pieceNotation,
25-
@JsonKey(name: 'ratings', fromJson: _showRatingsFromJson, toJson: _showRatingsToJson)
26-
required ShowRatings showRatings,
26+
@JsonKey(unknownEnumValue: Zen.no) required Zen zenMode,
27+
@JsonKey(unknownEnumValue: PieceNotation.symbol) required PieceNotation pieceNotation,
28+
@JsonKey(unknownEnumValue: ShowRatings.yes) required ShowRatings showRatings,
2729
// game behavior
28-
@JsonKey(fromJson: _premoveFromJson, toJson: _booleanPrefToJson) required BooleanPref premove,
29-
@JsonKey(fromJson: _autoQueenFromJson, toJson: _autoQueenToJson) required AutoQueen autoQueen,
30-
@JsonKey(fromJson: _autoThreefoldFromJson, toJson: _autoThreefoldToJson)
31-
required AutoThreefold autoThreefold,
32-
@JsonKey(fromJson: _takebackFromJson, toJson: _takebackToJson) required Takeback takeback,
33-
@JsonKey(fromJson: _confirmResignFromJson, toJson: _booleanPrefToJson)
30+
required BooleanPref premove,
31+
@JsonKey(unknownEnumValue: AutoQueen.premove) required AutoQueen autoQueen,
32+
@JsonKey(unknownEnumValue: AutoThreefold.always) required AutoThreefold autoThreefold,
33+
@JsonKey(unknownEnumValue: Takeback.always) required Takeback takeback,
3434
required BooleanPref confirmResign,
35-
@JsonKey(fromJson: _submitMoveFromJson, toJson: _submitMoveToJson)
3635
required SubmitMove submitMove,
3736
// clock
38-
@JsonKey(fromJson: _moretimeFromJson, toJson: _moretimeToJson) required Moretime moretime,
39-
@JsonKey(fromJson: _clockTenthsFromJson, toJson: _clockTenthsToJson)
40-
required ClockTenths clockTenths,
41-
@JsonKey(fromJson: _clockSoundFromJson, toJson: _booleanPrefToJson)
37+
@JsonKey(unknownEnumValue: Moretime.always) required Moretime moretime,
38+
@JsonKey(unknownEnumValue: ClockTenths.lessThan10s) required ClockTenths clockTenths,
4239
required BooleanPref clockSound,
4340
// privacy
44-
@JsonKey(fromJson: _followFromJson, toJson: _booleanPrefToJson) required BooleanPref follow,
45-
@JsonKey(fromJson: _challengeFromJson, toJson: _challengeToJson) required Challenge challenge,
46-
@JsonKey(fromJson: _messageFromJson, toJson: _messageToJson) required Message message,
41+
required BooleanPref follow,
42+
@JsonKey(unknownEnumValue: Challenge.registered) required Challenge challenge,
43+
@JsonKey(unknownEnumValue: Message.always) required Message message,
4744
}) = _AccountPrefState;
4845

4946
factory AccountPrefState.fromJson(Map<String, dynamic> json) {
@@ -55,6 +52,24 @@ sealed class AccountPrefState with _$AccountPrefState implements Serializable {
5552
}
5653
}
5754

55+
final defaultAccountPreferences = AccountPrefState(
56+
zenMode: Zen.no,
57+
pieceNotation: PieceNotation.symbol,
58+
showRatings: ShowRatings.yes,
59+
premove: const BooleanPref(true),
60+
autoQueen: AutoQueen.premove,
61+
autoThreefold: AutoThreefold.always,
62+
takeback: Takeback.always,
63+
moretime: Moretime.always,
64+
clockTenths: ClockTenths.lessThan10s,
65+
clockSound: const BooleanPref(true),
66+
confirmResign: const BooleanPref(true),
67+
submitMove: SubmitMove({SubmitMoveChoice.correspondence}),
68+
follow: const BooleanPref(true),
69+
challenge: Challenge.registered,
70+
message: Message.always,
71+
);
72+
5873
/// A provider that tells if the user wants to see ratings in the app.
5974
final showRatingsPrefProvider = FutureProvider<ShowRatings>((Ref ref) async {
6075
final prefs = await ref.watch(accountPreferencesProvider.future);
@@ -79,24 +94,6 @@ final clockTenthsProvider = FutureProvider<ClockTenths>((Ref ref) async {
7994
return prefs.clockTenths;
8095
});
8196

82-
final defaultAccountPreferences = AccountPrefState(
83-
zenMode: Zen.no,
84-
pieceNotation: PieceNotation.symbol,
85-
showRatings: ShowRatings.yes,
86-
premove: const BooleanPref(true),
87-
autoQueen: AutoQueen.premove,
88-
autoThreefold: AutoThreefold.always,
89-
takeback: Takeback.always,
90-
moretime: Moretime.always,
91-
clockTenths: ClockTenths.lessThan10s,
92-
clockSound: const BooleanPref(true),
93-
confirmResign: const BooleanPref(true),
94-
submitMove: SubmitMove({SubmitMoveChoice.correspondence}),
95-
follow: const BooleanPref(true),
96-
challenge: Challenge.registered,
97-
message: Message.always,
98-
);
99-
10097
/// A provider that gives the account preferences for the current user.
10198
final accountPreferencesProvider = AsyncNotifierProvider<AccountPreferences, AccountPrefState>(
10299
AccountPreferences.new,
@@ -107,6 +104,9 @@ final accountPreferencesProvider = AsyncNotifierProvider<AccountPreferences, Acc
107104
///
108105
/// The result is cached for the lifetime of the app, until refreshed.
109106
/// If the server returns an error, default values are returned.
107+
///
108+
/// If the user is authenticated, the preferences are fetched from the server. If the user is not authenticated, the preferences are fetched from local storage.
109+
/// See also [AccountPrefState] for more details.
110110
class AccountPreferences extends AsyncNotifier<AccountPrefState> {
111111
@override
112112
Future<AccountPrefState> build() async {
@@ -199,87 +199,6 @@ class AccountPreferences extends AsyncNotifier<AccountPrefState> {
199199
}
200200
}
201201

202-
Zen _zenFromJson(Object? value) =>
203-
Zen.fromInt(_intValue(value, defaultAccountPreferences.zenMode.value));
204-
205-
int _zenToJson(Zen value) => value.value;
206-
207-
PieceNotation _pieceNotationFromJson(Object? value) =>
208-
PieceNotation.fromInt(_intValue(value, defaultAccountPreferences.pieceNotation.value));
209-
210-
int _pieceNotationToJson(PieceNotation value) => value.value;
211-
212-
ShowRatings _showRatingsFromJson(Object? value) =>
213-
ShowRatings.fromInt(_intValue(value, defaultAccountPreferences.showRatings.value));
214-
215-
int _showRatingsToJson(ShowRatings value) => value.value;
216-
217-
BooleanPref _premoveFromJson(Object? value) =>
218-
BooleanPref(_boolValue(value, defaultAccountPreferences.premove.value));
219-
220-
AutoQueen _autoQueenFromJson(Object? value) =>
221-
AutoQueen.fromInt(_intValue(value, defaultAccountPreferences.autoQueen.value));
222-
223-
int _autoQueenToJson(AutoQueen value) => value.value;
224-
225-
AutoThreefold _autoThreefoldFromJson(Object? value) =>
226-
AutoThreefold.fromInt(_intValue(value, defaultAccountPreferences.autoThreefold.value));
227-
228-
int _autoThreefoldToJson(AutoThreefold value) => value.value;
229-
230-
Takeback _takebackFromJson(Object? value) =>
231-
Takeback.fromInt(_intValue(value, defaultAccountPreferences.takeback.value));
232-
233-
int _takebackToJson(Takeback value) => value.value;
234-
235-
BooleanPref _confirmResignFromJson(Object? value) =>
236-
BooleanPref(_boolValue(value, defaultAccountPreferences.confirmResign.value));
237-
238-
SubmitMove _submitMoveFromJson(Object? value) =>
239-
SubmitMove.fromInt(_intValue(value, defaultAccountPreferences.submitMove.value));
240-
241-
int _submitMoveToJson(SubmitMove value) => value.value;
242-
243-
Moretime _moretimeFromJson(Object? value) =>
244-
Moretime.fromInt(_intValue(value, defaultAccountPreferences.moretime.value));
245-
246-
int _moretimeToJson(Moretime value) => value.value;
247-
248-
ClockTenths _clockTenthsFromJson(Object? value) =>
249-
ClockTenths.fromInt(_intValue(value, defaultAccountPreferences.clockTenths.value));
250-
251-
int _clockTenthsToJson(ClockTenths value) => value.value;
252-
253-
BooleanPref _clockSoundFromJson(Object? value) =>
254-
BooleanPref(_boolValue(value, defaultAccountPreferences.clockSound.value));
255-
256-
BooleanPref _followFromJson(Object? value) =>
257-
BooleanPref(_boolValue(value, defaultAccountPreferences.follow.value));
258-
259-
Challenge _challengeFromJson(Object? value) =>
260-
Challenge.fromInt(_intValue(value, defaultAccountPreferences.challenge.value));
261-
262-
int _challengeToJson(Challenge value) => value.value;
263-
264-
Message _messageFromJson(Object? value) =>
265-
Message.fromInt(_intValue(value, defaultAccountPreferences.message.value));
266-
267-
int _messageToJson(Message value) => value.value;
268-
269-
bool _booleanPrefToJson(BooleanPref value) => value.value;
270-
271-
int _intValue(Object? value, int fallback) {
272-
return value is num ? value.toInt() : fallback;
273-
}
274-
275-
bool _boolValue(Object? value, bool fallback) {
276-
return switch (value) {
277-
final bool b => b,
278-
final num n => n != 0,
279-
_ => fallback,
280-
};
281-
}
282-
283202
abstract class AccountPref<T> {
284203
T get value;
285204
String get toFormData;
@@ -294,6 +213,10 @@ class BooleanPref implements AccountPref<bool> {
294213
@override
295214
String get toFormData => value ? '1' : '0';
296215

216+
factory BooleanPref.fromJson(bool json) => BooleanPref(json);
217+
218+
bool toJson() => value;
219+
297220
static BooleanPref fromInt(int value) {
298221
switch (value) {
299222
case 1:
@@ -307,8 +230,11 @@ class BooleanPref implements AccountPref<bool> {
307230
}
308231

309232
enum Zen implements AccountPref<int> {
233+
@JsonValue(0)
310234
no(0),
235+
@JsonValue(1)
311236
yes(1),
237+
@JsonValue(2)
312238
gameAuto(2);
313239

314240
const Zen(this.value);
@@ -345,8 +271,11 @@ enum Zen implements AccountPref<int> {
345271
}
346272

347273
enum ShowRatings implements AccountPref<int> {
274+
@JsonValue(0)
348275
no(0),
276+
@JsonValue(1)
349277
yes(1),
278+
@JsonValue(2)
350279
exceptInGame(2);
351280

352281
const ShowRatings(this.value);
@@ -383,7 +312,9 @@ enum ShowRatings implements AccountPref<int> {
383312
}
384313

385314
enum PieceNotation implements AccountPref<int> {
315+
@JsonValue(0)
386316
symbol(0),
317+
@JsonValue(1)
387318
letter(1);
388319

389320
const PieceNotation(this.value);
@@ -416,8 +347,11 @@ enum PieceNotation implements AccountPref<int> {
416347
}
417348

418349
enum AutoQueen implements AccountPref<int> {
350+
@JsonValue(1)
419351
never(1),
352+
@JsonValue(2)
420353
premove(2),
354+
@JsonValue(3)
421355
always(3);
422356

423357
const AutoQueen(this.value);
@@ -454,8 +388,11 @@ enum AutoQueen implements AccountPref<int> {
454388
}
455389

456390
enum AutoThreefold implements AccountPref<int> {
391+
@JsonValue(1)
457392
never(1),
393+
@JsonValue(2)
458394
time(2),
395+
@JsonValue(3)
459396
always(3);
460397

461398
const AutoThreefold(this.value);
@@ -492,8 +429,11 @@ enum AutoThreefold implements AccountPref<int> {
492429
}
493430

494431
enum Takeback implements AccountPref<int> {
432+
@JsonValue(1)
495433
never(1),
434+
@JsonValue(2)
496435
casual(2),
436+
@JsonValue(3)
497437
always(3);
498438

499439
const Takeback(this.value);
@@ -530,8 +470,11 @@ enum Takeback implements AccountPref<int> {
530470
}
531471

532472
enum Moretime implements AccountPref<int> {
473+
@JsonValue(1)
533474
never(1),
475+
@JsonValue(2)
534476
casual(2),
477+
@JsonValue(3)
535478
always(3);
536479

537480
const Moretime(this.value);
@@ -568,8 +511,11 @@ enum Moretime implements AccountPref<int> {
568511
}
569512

570513
enum ClockTenths implements AccountPref<int> {
514+
@JsonValue(0)
571515
never(0),
516+
@JsonValue(1)
572517
lessThan10s(1),
518+
@JsonValue(2)
573519
always(2);
574520

575521
const ClockTenths(this.value);
@@ -606,10 +552,15 @@ enum ClockTenths implements AccountPref<int> {
606552
}
607553

608554
enum Challenge implements AccountPref<int> {
555+
@JsonValue(1)
609556
never(1),
557+
@JsonValue(2)
610558
rating(2),
559+
@JsonValue(3)
611560
friends(3),
561+
@JsonValue(4)
612562
registered(4),
563+
@JsonValue(5)
613564
always(5);
614565

615566
const Challenge(this.value);
@@ -654,8 +605,11 @@ enum Challenge implements AccountPref<int> {
654605
}
655606

656607
enum Message implements AccountPref<int> {
608+
@JsonValue(1)
657609
never(1),
610+
@JsonValue(2)
658611
friends(2),
612+
@JsonValue(3)
659613
always(3);
660614

661615
const Message(this.value);
@@ -712,6 +666,10 @@ class SubmitMove implements AccountPref<int> {
712666

713667
factory SubmitMove.fromInt(int value) =>
714668
SubmitMove(SubmitMoveChoice.values.where((choice) => _bitPresent(value, choice.value)));
669+
670+
factory SubmitMove.fromJson(int json) => SubmitMove.fromInt(json);
671+
672+
int toJson() => value;
715673
}
716674

717675
enum SubmitMoveChoice {

lib/src/model/game/game_controller.dart

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ final gameControllerProvider = AsyncNotifierProvider.autoDispose
4747
name: 'GameControllerProvider',
4848
);
4949

50-
PlayableGame _withAccountPrefsFallback(PlayableGame game, AccountPrefState prefs) {
50+
/// Returns the [PlayableGame] with the local account preferences applied if the server didn't send any preferences.
51+
PlayableGame _withLocalAccountPrefsFallback(PlayableGame game, AccountPrefState prefs) {
5152
if (game.prefs != null) {
5253
return game;
5354
}
@@ -152,7 +153,7 @@ class GameController extends AsyncNotifier<GameState> with ChatMixin<GameState>
152153

153154
final game = fullEvent.game.prefs != null
154155
? fullEvent.game
155-
: _withAccountPrefsFallback(
156+
: _withLocalAccountPrefsFallback(
156157
fullEvent.game,
157158
await ref.read(accountPreferencesProvider.future),
158159
);
@@ -674,7 +675,7 @@ class GameController extends AsyncNotifier<GameState> with ChatMixin<GameState>
674675

675676
final curState = state.requireValue;
676677

677-
final newGame = _withAccountPrefsFallback(
678+
final newGame = _withLocalAccountPrefsFallback(
678679
fullEvent.game,
679680
ref.read(accountPreferencesProvider).value ?? defaultAccountPreferences,
680681
);

0 commit comments

Comments
 (0)