Skip to content

Commit

Permalink
refactor controller
Browse files Browse the repository at this point in the history
  • Loading branch information
julien4215 committed Dec 5, 2023
1 parent 08b6706 commit 58da96d
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 157 deletions.
4 changes: 2 additions & 2 deletions lib/src/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import 'package:flutter/material.dart';

const kLichessHost = String.fromEnvironment(
'LICHESS_HOST',
defaultValue: 'http://localhost:9663',
defaultValue: 'https://lichess.dev',
);

const kLichessWSHost = String.fromEnvironment(
'LICHESS_WS_HOST',
defaultValue: 'ws://localhost:9664',
defaultValue: 'wss://socket.lichess.dev',
);

const kLichessWSSecret = String.fromEnvironment(
Expand Down
118 changes: 118 additions & 0 deletions lib/src/model/game/chat_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import 'dart:async';

import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:lichess_mobile/src/model/auth/auth_socket.dart';
import 'package:lichess_mobile/src/model/common/socket.dart';
import 'package:lichess_mobile/src/model/game/game_socket_events.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'chat_controller.freezed.dart';
part 'chat_controller.g.dart';

@riverpod
class ChatController extends _$ChatController {
StreamSubscription<SocketEvent>? _socketSubscription;

int? _socketEventVersion;

AuthSocket get _socket => ref.read(authSocketProvider);

@override
Future<ChatState> build() {
_socketEventVersion = 0;
final stream = _socket.stream;
stream.listen(_handleSocketTopic);

ref.onDispose(() {
_socketSubscription?.cancel();
});

return stream.firstWhere((e) => e.topic == 'full').then(
(event) {
final fullEvent =
GameFullEvent.fromJson(event.data as Map<String, dynamic>);

_socketEventVersion = fullEvent.socketEventVersion;

return ChatState(
messages: fullEvent.game.messages,
unreadMessages: 0,
);
},
);
}

void onUserMessage(String message) {
_socket.send(
'talk',
message,
);
}

void resetUnreadMessages() {
final curState = state.requireValue;
state = AsyncValue.data(curState.copyWith(unreadMessages: 0));
}

void _handleSocketTopic(SocketEvent event) {
if (!state.hasValue) return;

switch (event.topic) {
// Full game data, received after a (re)connection to game socket
case 'full':
final fullEvent =
GameFullEvent.fromJson(event.data as Map<String, dynamic>);

if (_socketEventVersion != null &&
fullEvent.socketEventVersion < _socketEventVersion!) {
return;
}

_socketEventVersion = fullEvent.socketEventVersion;

state = AsyncValue.data(
ChatState(
messages: fullEvent.game.messages,
unreadMessages: state.requireValue.unreadMessages,
),
);

// Received when opponent send a message
case 'message':
final data = event.data as Map<String, dynamic>;
final message = data["t"] as String;
final username = data["u"] as String;
final curState = state.requireValue;
state = AsyncValue.data(
curState.copyWith(
messages: curState.messages.add(
Message(
message: message,
username: username,
),
),
unreadMessages: curState.unreadMessages + 1,
),
);
}
}
}

@freezed
class ChatState with _$ChatState {
const ChatState._();

const factory ChatState({
required IList<Message> messages,
required int unreadMessages,
}) = _ChatState;
}

@immutable
class Message {
final String username;
final String message;

const Message({required this.username, required this.message});
}
57 changes: 0 additions & 57 deletions lib/src/model/game/game_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import 'dart:async';
import 'package:chessground/chessground.dart' as cg;
import 'package:dartchess/dartchess.dart';
import 'package:deep_pick/deep_pick.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:flutter/services.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:lichess_mobile/src/model/account/account_preferences.dart';
Expand All @@ -25,7 +24,6 @@ import 'package:lichess_mobile/src/model/game/material_diff.dart';
import 'package:lichess_mobile/src/model/game/playable_game.dart';
import 'package:lichess_mobile/src/model/settings/board_preferences.dart';
import 'package:lichess_mobile/src/utils/rate_limit.dart';
import 'package:lichess_mobile/src/view/game/message.dart';
import 'package:logging/logging.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

Expand Down Expand Up @@ -82,8 +80,6 @@ class GameController extends _$GameController {
return GameState(
game: fullEvent.game,
stepCursor: fullEvent.game.steps.length - 1,
messages: IList(),
unreadMessages: 0,
stopClockWaitingForServerAck: false,
);
},
Expand Down Expand Up @@ -242,13 +238,6 @@ class GameController extends _$GameController {
);
}

void resetUnreadMessages() {
final curState = state.requireValue;
state = AsyncValue.data(
curState.copyWith(unreadMessages: 0),
);
}

void onFlag() {
_onFlagThrottler(() {
if (state.hasValue) {
Expand All @@ -257,10 +246,6 @@ class GameController extends _$GameController {
});
}

void onUserMessage(String message) {
_sendMessageToSocket(message);
}

void moreTime() {
_socket.send('moretime', null);
}
Expand Down Expand Up @@ -341,15 +326,6 @@ class GameController extends _$GameController {
_transientMoveTimer = Timer(const Duration(seconds: 10), _resyncGameData);
}

void _sendMessageToSocket(
String message,
) {
_socket.send(
'talk',
message,
);
}

/// Move feedback while playing
void _playMoveFeedback(SanMove sanMove, {bool skipAnimationDelay = false}) {
final animationDuration =
Expand Down Expand Up @@ -461,8 +437,6 @@ class GameController extends _$GameController {
GameState(
game: fullEvent.game,
stepCursor: fullEvent.game.steps.length - 1,
messages: state.requireValue.messages,
unreadMessages: state.requireValue.unreadMessages,
stopClockWaitingForServerAck: false,
// cancel the premove to avoid playing wrong premove when the full
// game data is reloaded
Expand Down Expand Up @@ -745,35 +719,6 @@ class GameController extends _$GameController {
),
);

// Event sent when a message is received
case 'message':
final data = event.data as Map<String, dynamic>;
final message = data["t"] as String;
final username = data["u"] as String;
final curState = state.requireValue;
final player = curState.game.me!.user?.name;
final opponent = curState.game.me!.user?.name;
final sender = username == "lichess"
? Sender.server
: username == player
? Sender.player
: username == opponent
? Sender.opponent
: null;
if (sender != null) {
state = AsyncValue.data(
curState.copyWith(
messages: curState.messages.add(
Message(
message: message,
sender: sender,
),
),
unreadMessages: curState.unreadMessages + 1,
),
);
}

// Event sent when a player adds or cancels a rematch offer
case 'rematchOffer':
final side = pick(event.data).asSideOrNull();
Expand Down Expand Up @@ -848,8 +793,6 @@ class GameState with _$GameState {
const factory GameState({
required PlayableGame game,
required int stepCursor,
required IList<Message> messages,
required int unreadMessages,
int? lastDrawOfferAtPly,
Duration? opponentLeftCountdown,
required bool stopClockWaitingForServerAck,
Expand Down
11 changes: 11 additions & 0 deletions lib/src/model/game/playable_game.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:lichess_mobile/src/model/common/chess.dart';
import 'package:lichess_mobile/src/model/common/id.dart';
import 'package:lichess_mobile/src/model/common/perf.dart';
import 'package:lichess_mobile/src/model/common/speed.dart';
import 'package:lichess_mobile/src/model/game/chat_controller.dart';
import 'package:lichess_mobile/src/model/game/material_diff.dart';
import 'package:lichess_mobile/src/model/user/user.dart';
import 'package:lichess_mobile/src/utils/json.dart';
Expand Down Expand Up @@ -38,6 +39,7 @@ class PlayableGame
required Player black,
required bool moretimeable,
required bool takebackable,
required IList<Message> messages,

/// The side that the current player is playing as. This is null if viewing
/// the game as a spectator.
Expand Down Expand Up @@ -141,6 +143,7 @@ PlayableGame _playableGameFromPick(RequiredPick pick) {
);
}
}
final messages = pick('chat', 'lines').asListOrThrow(_messageFromPick);

return PlayableGame(
id: requiredGamePick('id').asGameIdOrThrow(),
Expand Down Expand Up @@ -174,6 +177,7 @@ PlayableGame _playableGameFromPick(RequiredPick pick) {
},
),
rematch: pick('game', 'rematch').asGameIdOrNull(),
messages: messages.toIList(),
);
}

Expand Down Expand Up @@ -248,3 +252,10 @@ CorrespondenceClockData _correspondenceClockDataFromPick(RequiredPick pick) {
black: pick('black').asDurationFromSecondsOrThrow(),
);
}

Message _messageFromPick(RequiredPick pick) {
return Message(
message: pick('t').asStringOrThrow(),
username: pick('u').asStringOrThrow(),
);
}
64 changes: 39 additions & 25 deletions lib/src/view/game/game_body.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:lichess_mobile/src/model/account/account_repository.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/chat_controller.dart';
import 'package:lichess_mobile/src/model/game/game_controller.dart';
import 'package:lichess_mobile/src/model/game/game_repository_providers.dart';
import 'package:lichess_mobile/src/model/lobby/game_seek.dart';
Expand Down Expand Up @@ -79,6 +80,7 @@ class GameBody extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final ctrlProvider = gameControllerProvider(id);
ref.watch(chatControllerProvider);

ref.listen(
ctrlProvider,
Expand Down Expand Up @@ -533,37 +535,49 @@ class _GameBottomBar extends ConsumerWidget {
pushPlatformRoute(
context,
builder: (BuildContext context) {
return MessageScreen(
ctrlProvider: gameControllerProvider(id),
);
return MessageScreen(game: gameState.game);
},
);
},
icon: CupertinoIcons.chat_bubble,
),
if (gameState.unreadMessages > 0)
Positioned(
top: 9.0,
right: 6.0,
child: Stack(
alignment: Alignment.center,
children: [
const Icon(
Icons.brightness_1,
size: 16.0,
),
Text(
gameState.unreadMessages.toString(),
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.white,
fontSize: 8.0,
fontWeight: FontWeight.w500,
),
),
],
ref.watch(chatControllerProvider).when(
data: (data) {
if (data.unreadMessages > 0) {
return Positioned(
top: 9.0,
right: 6.0,
child: Stack(
alignment: Alignment.center,
children: [
const Icon(
Icons.brightness_1,
size: 16.0,
),
Text(
data.unreadMessages.toString(),
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.white,
fontSize: 8.0,
fontWeight: FontWeight.w500,
),
),
],
),
);
} else {
return const SizedBox.shrink();
}
},
loading: () => const SizedBox.shrink(),
error: (error, stackTrace) {
debugPrint(
'SEVERE: [ChatScreen] could not load chat; $error\n$stackTrace',
);
return const SizedBox.shrink();
},
),
),
],
),
RepeatButton(
Expand Down
Loading

0 comments on commit 58da96d

Please sign in to comment.