-
-
Notifications
You must be signed in to change notification settings - Fork 217
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
Introduce preset messages into the app #825
base: main
Are you sure you want to change the base?
Changes from 1 commit
c54f395
211ca61
281a607
04a192d
8a3146f
c38eff9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
import 'package:freezed_annotation/freezed_annotation.dart'; | ||
import 'package:lichess_mobile/src/model/common/id.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/message_presets.dart'; | ||
import 'package:riverpod_annotation/riverpod_annotation.dart'; | ||
|
||
part 'chat_presets_controller.freezed.dart'; | ||
part 'chat_presets_controller.g.dart'; | ||
|
||
@riverpod | ||
class ChatPresetsController extends _$ChatPresetsController { | ||
late GameFullId _gameId; | ||
|
||
static const Map<PresetMessageGroup, List<PresetMessage>> _presetMessages = { | ||
PresetMessageGroup.start: [ | ||
(label: 'HI', value: 'Hello'), | ||
(label: 'GL', value: 'Good luck'), | ||
(label: 'HF', value: 'Have fun!'), | ||
(label: 'U2', value: 'You too!'), | ||
], | ||
PresetMessageGroup.end: [ | ||
(label: 'GG', value: 'Good game'), | ||
(label: 'WP', value: 'Well played'), | ||
(label: 'TY', value: 'Thank you'), | ||
(label: 'GTG', value: "I've got to go"), | ||
(label: 'BYE', value: 'Bye!'), | ||
], | ||
}; | ||
|
||
@override | ||
Future<ChatPresetsState> build(GameFullId id) async { | ||
_gameId = id; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure to understand why you need a private local var here. |
||
|
||
final gameState = ref.read(gameControllerProvider(_gameId)).value; | ||
|
||
ref.listen(gameControllerProvider(_gameId), _handleGameStateChange); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here I don't think Since we want to act upon the game state, |
||
|
||
if (gameState != null) { | ||
final presetMessageGroup = PresetMessageGroup.fromGame(gameState.game); | ||
|
||
const List<PresetMessage> alreadySaid = []; | ||
|
||
final initialState = ChatPresetsState( | ||
presets: _presetMessages, | ||
gameId: _gameId, | ||
alreadySaid: alreadySaid, | ||
currentPresetMessageGroup: presetMessageGroup, | ||
); | ||
|
||
return initialState; | ||
} else { | ||
return ChatPresetsState( | ||
presets: _presetMessages, | ||
gameId: _gameId, | ||
alreadySaid: [], | ||
currentPresetMessageGroup: null, | ||
); | ||
} | ||
} | ||
|
||
void _handleGameStateChange( | ||
AsyncValue<GameState>? previousGame, | ||
AsyncValue<GameState> currentGame, | ||
) { | ||
final newGameState = currentGame.value; | ||
|
||
if (newGameState != null) { | ||
final newMessageGroup = PresetMessageGroup.fromGame(newGameState.game); | ||
|
||
final currentMessageGroup = state.value?.currentPresetMessageGroup; | ||
|
||
if (newMessageGroup != currentMessageGroup) { | ||
state = state.whenData((s) { | ||
final newState = s.copyWith( | ||
currentPresetMessageGroup: newMessageGroup, | ||
alreadySaid: [], | ||
); | ||
|
||
return newState; | ||
}); | ||
} | ||
} | ||
} | ||
|
||
void sendPreset(PresetMessage message) { | ||
final chatController = ref.read(chatControllerProvider(_gameId).notifier); | ||
chatController.sendMessage(message.value); | ||
|
||
state = state.whenData((s) { | ||
final state = s.copyWith(alreadySaid: [...s.alreadySaid, message]); | ||
return state; | ||
}); | ||
} | ||
} | ||
|
||
@freezed | ||
class ChatPresetsState with _$ChatPresetsState { | ||
const ChatPresetsState._(); | ||
|
||
const factory ChatPresetsState({ | ||
required Map<PresetMessageGroup, List<PresetMessage>> presets, | ||
required GameFullId gameId, | ||
required List<PresetMessage> alreadySaid, | ||
required PresetMessageGroup? currentPresetMessageGroup, | ||
}) = _ChatPresetsState; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import 'package:lichess_mobile/src/model/game/game_status.dart'; | ||
import 'package:lichess_mobile/src/model/game/playable_game.dart'; | ||
|
||
enum PresetMessageGroup { | ||
start, | ||
end; | ||
|
||
static PresetMessageGroup? fromString(String groupName) { | ||
Happy0 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (groupName == 'start') { | ||
return start; | ||
} else if (groupName == 'end') { | ||
return end; | ||
} else { | ||
return null; | ||
} | ||
} | ||
|
||
static PresetMessageGroup? fromGame(PlayableGame game) { | ||
if (game.status.value <= GameStatus.mate.value && game.steps.length < 4) { | ||
return start; | ||
} else if (game.status.value >= GameStatus.mate.value) { | ||
return end; | ||
} else { | ||
return null; | ||
} | ||
} | ||
} | ||
|
||
typedef PresetMessage = ({String label, String value}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import 'package:flutter/cupertino.dart'; | ||
import 'package:flutter_riverpod/flutter_riverpod.dart'; | ||
import 'package:lichess_mobile/src/model/common/id.dart'; | ||
import 'package:lichess_mobile/src/model/game/message_presets.dart'; | ||
import 'package:lichess_mobile/src/widgets/buttons.dart'; | ||
|
||
class PresetMessages extends ConsumerWidget { | ||
final GameFullId gameId; | ||
final List<PresetMessage> alreadySaid; | ||
final PresetMessageGroup? presetMessageGroup; | ||
final Map<PresetMessageGroup, List<PresetMessage>> presetMessages; | ||
final void Function(PresetMessage presetMessage) sendChatPreset; | ||
|
||
const PresetMessages({ | ||
required this.gameId, | ||
required this.alreadySaid, | ||
required this.presetMessageGroup, | ||
required this.presetMessages, | ||
required this.sendChatPreset, | ||
}); | ||
|
||
@override | ||
Widget build(BuildContext context, WidgetRef ref) { | ||
final messages = presetMessages[presetMessageGroup] ?? []; | ||
|
||
if (messages.isEmpty || alreadySaid.length >= 2) { | ||
return const SizedBox.shrink(); | ||
} | ||
|
||
final notAlreadySaid = | ||
messages.where((message) => !alreadySaid.contains(message)); | ||
|
||
return Row( | ||
children: notAlreadySaid | ||
.map((preset) => _renderPresetMessageButton(preset, ref)) | ||
.toList(), | ||
); | ||
} | ||
|
||
Widget _renderPresetMessageButton(PresetMessage preset, WidgetRef ref) { | ||
return Padding( | ||
padding: const EdgeInsets.all(4.0), | ||
child: SecondaryButton( | ||
semanticsLabel: preset.label, | ||
onPressed: () { | ||
sendChatPreset(preset); | ||
}, | ||
child: Text( | ||
preset.label, | ||
textAlign: TextAlign.center, | ||
), | ||
), | ||
); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok now I understand why the state is invalidated.
I think the easiest way to fix this is to remove the
chatPresetController
and put its logic in thechatController
. This will simplify things hopefully as this logic belongs to the chat controller, and you won't have to worry about state invalidation anymore.