forked from lichess-org/mobile
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgame_screen_providers.dart
More file actions
216 lines (185 loc) · 7.25 KB
/
Copy pathgame_screen_providers.dart
File metadata and controls
216 lines (185 loc) · 7.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
import 'package:dartchess/dartchess.dart' show Side;
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:lichess_mobile/src/model/challenge/challenge.dart';
import 'package:lichess_mobile/src/model/common/id.dart';
import 'package:lichess_mobile/src/model/common/speed.dart';
import 'package:lichess_mobile/src/model/game/game.dart';
import 'package:lichess_mobile/src/model/game/game_controller.dart';
import 'package:lichess_mobile/src/model/lobby/create_game_service.dart';
import 'package:lichess_mobile/src/model/lobby/game_seek.dart';
import 'package:lichess_mobile/src/view/game/game_screen.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'game_screen_providers.g.dart';
part 'game_screen_providers.freezed.dart';
/// The state of the [GameScreen].
///
/// It can be:
/// - [GameCreatedState]: A game has been created or loaded.
/// - [ChallengeDeclinedState]: A real time challenge has been declined.
/// - [SeekCancelledState]: A game seek has been cancelled.
/// - [ChallengeCancelledState]: A real time challenge has been cancelled.
sealed class GameScreenState {}
/// Game screen state when a game has been created or loaded.
///
/// This is used in the following cases:
/// - A game that had already been created is loaded.
/// - A game has been created from a lobby seek.
/// - A challenge has been accepted and a game has been created from it.
@freezed
sealed class GameCreatedState with _$GameCreatedState implements GameScreenState {
const GameCreatedState._();
const factory GameCreatedState(GameFullId createdGameId) = _GameCreatedState;
}
/// A real time challenge has been declined.
@freezed
sealed class ChallengeDeclinedState with _$ChallengeDeclinedState implements GameScreenState {
const ChallengeDeclinedState._();
const factory ChallengeDeclinedState(ChallengeResponseDeclined response) =
_ChallengeDeclinedState;
}
/// A game seek has been cancelled.
@freezed
sealed class SeekCancelledState with _$SeekCancelledState implements GameScreenState {
const SeekCancelledState._();
const factory SeekCancelledState() = _SeekCancelledState;
}
/// A real time challenge has been cancelled.
@freezed
sealed class ChallengeCancelledState with _$ChallengeCancelledState implements GameScreenState {
const ChallengeCancelledState._();
const factory ChallengeCancelledState() = _ChallengeCancelledState;
}
/// The source from which the [GameScreen] was opened.
///
/// It can be:
/// - An existing game, see [ExistingGameSource].
/// - A lobby seek, see [LobbySource].
/// - A user challenge, see [UserChallengeSource].
///
/// In case of a lobby seek or a user challenge, a new game will be created and the screen will show
/// a loading indicator until the game is created.
sealed class GameScreenSource {}
/// An existing game source for [GameScreen], identified by its [GameFullId].
@freezed
sealed class ExistingGameSource with _$ExistingGameSource implements GameScreenSource {
const ExistingGameSource._();
const factory ExistingGameSource(GameFullId id) = _ExistingGameSource;
}
/// A lobby source for [GameScreen], identified by the [GameSeek] from which the game will be created.
@freezed
sealed class LobbySource with _$LobbySource implements GameScreenSource {
const LobbySource._();
const factory LobbySource(GameSeek seek) = _LobbySource;
}
/// A user challenge source for [GameScreen], identified by the [ChallengeRequest] from which the game will be created.
@freezed
sealed class UserChallengeSource with _$UserChallengeSource implements GameScreenSource {
const UserChallengeSource._();
const factory UserChallengeSource(ChallengeRequest challengeRequest) = _UserChallengeSource;
}
/// A provider that loads or creates a game for the [GameScreen].
@riverpod
class GameScreenLoader extends _$GameScreenLoader {
@override
Future<GameScreenState> build(GameScreenSource source) {
final service = ref.watch(createGameServiceProvider);
return switch (source) {
LobbySource(:final seek) => service.newLobbyGame(seek).then((id) => GameCreatedState(id)),
UserChallengeSource(:final challengeRequest) =>
service
.newRealTimeChallenge(challengeRequest)
.then(
(data) => switch (data) {
ChallengeResponseAccepted(:final gameFullId) => GameCreatedState(gameFullId),
ChallengeResponseDeclined() => ChallengeDeclinedState(data),
},
),
ExistingGameSource(:final id) => Future.value(GameCreatedState(id)),
};
}
/// Search for a new opponent (lobby only).
Future<void> newOpponent() async {
if (source case LobbySource(:final seek)) {
final service = ref.read(createGameServiceProvider);
state = const AsyncValue.loading();
state = AsyncValue.data(await service.newLobbyGame(seek).then((id) => GameCreatedState(id)));
}
}
/// Load a game from its id.
void loadGame(GameFullId id) {
state = AsyncValue.data(GameCreatedState(id));
}
void cancelSeek() {
state = const AsyncValue.data(SeekCancelledState());
}
void cancelChallenge() {
state = const AsyncValue.data(ChallengeCancelledState());
}
}
@riverpod
class IsBoardTurned extends _$IsBoardTurned {
@override
bool build() {
return false;
}
void toggle() {
state = !state;
}
}
@riverpod
Future<bool> isGameBookmarked(Ref ref, GameFullId gameId) {
return ref.watch(
gameControllerProvider(gameId).selectAsync((state) => state.game.bookmarked ?? false),
);
}
@riverpod
Future<({bool finished, Side? pov})> gameShareData(Ref ref, GameFullId gameId) {
return ref.watch(
gameControllerProvider(
gameId,
).selectAsync((state) => (finished: state.game.finished, pov: state.game.youAre)),
);
}
@riverpod
Future<bool> isRealTimePlayableGame(Ref ref, GameFullId gameId) {
return ref.watch(
gameControllerProvider(
gameId,
).selectAsync((state) => state.game.meta.speed != Speed.correspondence && state.game.playable),
);
}
/// User game preferences, defined server-side.
@riverpod
Future<({ServerGamePrefs? prefs, bool shouldConfirmMove, bool isZenModeEnabled, bool canAutoQueen})>
userGamePrefs(Ref ref, GameFullId gameId) async {
final prefs = await ref.watch(
gameControllerProvider(gameId).selectAsync((state) => state.game.prefs),
);
final shouldConfirmMove = await ref.watch(
gameControllerProvider(gameId).selectAsync((state) => state.shouldConfirmMove),
);
final isZenModeEnabled = await ref.watch(
gameControllerProvider(gameId).selectAsync((state) => state.isZenModeEnabled),
);
final canAutoQueen = await ref.watch(
gameControllerProvider(gameId).selectAsync((state) => state.canAutoQueen),
);
return (
prefs: prefs,
shouldConfirmMove: shouldConfirmMove,
isZenModeEnabled: isZenModeEnabled,
canAutoQueen: canAutoQueen,
);
}
/// Returns the [PlayableGameMeta].
///
/// This is data that won't change during the game.
@riverpod
Future<GameMeta> gameMeta(Ref ref, GameFullId gameId) async {
return await ref.watch(gameControllerProvider(gameId).selectAsync((state) => state.game.meta));
}
@riverpod
Future<TournamentMeta?> gameTournament(Ref ref, GameFullId gameId) async {
return await ref.watch(gameControllerProvider(gameId).selectAsync((state) => state.tournament));
}