Skip to content

Commit

Permalink
feat: add incorrect answer text (#368)
Browse files Browse the repository at this point in the history
  • Loading branch information
jsgalarraga authored Apr 23, 2024
1 parent f0fb5ad commit 9788e68
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 4 deletions.
4 changes: 4 additions & 0 deletions lib/l10n/arb/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@
"@typeToAnswer": {
"description": "Type to answer label"
},
"incorrectAnswer": "Oops, wrong word!",
"@incorrectAnswer": {
"description": "Incorrect answer label"
},
"wordSolved": "Word solved!",
"@wordSolved": {
"description": "Word solved label"
Expand Down
9 changes: 8 additions & 1 deletion lib/word_selection/bloc/word_selection_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,15 @@ class WordSelectionBloc extends Bloc<WordSelectionEvent, WordSelectionState> {
return;
}

if (state.status == WordSelectionStatus.solving ||
state.status == WordSelectionStatus.solved ||
state.status == WordSelectionStatus.validating) {
// Can't solve a word if it's already being solved, solved, or validating.
return;
}

emit(
WordSelectionState(
state.copyWith(
status: WordSelectionStatus.solving,
word: state.word,
),
Expand Down
41 changes: 39 additions & 2 deletions lib/word_selection/view/word_solving_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,16 @@ class WordSolvingLargeView extends StatelessWidget {

@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final selectedWord =
context.select((WordSelectionBloc bloc) => bloc.state.word);
if (selectedWord == null) return const SizedBox.shrink();
final isHintsEnabled =
context.select((HintBloc bloc) => bloc.state.isHintsEnabled);
final isIncorrectAnswer = context.select(
(WordSelectionBloc bloc) =>
bloc.state.status == WordSelectionStatus.incorrect,
);

return Column(
children: [
Expand All @@ -41,6 +46,12 @@ class WordSolvingLargeView extends StatelessWidget {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
isIncorrectAnswer ? context.l10n.incorrectAnswer : '',
style: IoCrosswordTextStyles.bodyMD.medium
?.copyWith(color: theme.colorScheme.error),
),
const SizedBox(height: 24),
Text(
selectedWord.word.clue,
style: IoCrosswordTextStyles.titleMD,
Expand Down Expand Up @@ -85,6 +96,18 @@ class WordSolvingSmallView extends StatefulWidget {

class _WordSolvingSmallViewState extends State<WordSolvingSmallView> {
final _controller = IoWordInputController();
String _lastWord = '';

@override
void initState() {
super.initState();
_controller.addListener(() {
if (_lastWord.length > _controller.word.length) {
context.read<WordSelectionBloc>().add(const WordSolveRequested());
}
_lastWord = _controller.word;
});
}

@override
void dispose() {
Expand All @@ -94,11 +117,16 @@ class _WordSolvingSmallViewState extends State<WordSolvingSmallView> {

@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final selectedWord =
context.select((WordSelectionBloc bloc) => bloc.state.word);
if (selectedWord == null) return const SizedBox.shrink();
final isHintsEnabled =
context.select((HintBloc bloc) => bloc.state.isHintsEnabled);
final isIncorrectAnswer = context.select(
(WordSelectionBloc bloc) =>
bloc.state.status == WordSelectionStatus.incorrect,
);

return Column(
children: [
Expand All @@ -108,7 +136,13 @@ class _WordSolvingSmallViewState extends State<WordSolvingSmallView> {
length: selectedWord.word.length,
controller: _controller,
),
const SizedBox(height: 24),
const SizedBox(height: 16),
Text(
isIncorrectAnswer ? context.l10n.incorrectAnswer : '',
style: IoCrosswordTextStyles.bodyMD.medium
?.copyWith(color: theme.colorScheme.error),
),
const SizedBox(height: 16),
Text(
selectedWord.word.clue,
style: IoCrosswordTextStyles.titleMD,
Expand All @@ -128,7 +162,10 @@ class _WordSolvingSmallViewState extends State<WordSolvingSmallView> {
return const Center(child: CircularProgressIndicator());
}

return const HintsSection();
return const Align(
alignment: Alignment.topCenter,
child: HintsSection(),
);
},
),
),
Expand Down
4 changes: 3 additions & 1 deletion packages/io_crossword_ui/lib/src/widgets/io_word_input.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'dart:collection';
import 'dart:math' as math;
import 'dart:ui';

import 'package:equatable/equatable.dart';
Expand Down Expand Up @@ -386,8 +387,9 @@ class _IoWordInputState extends State<IoWordInput> {
? (_) => widget.onSubmit!(_word)
: null,
onSelectionChanged: (selection, cause) {
final offset = math.min(1, controller.text.length);
controller.selection = TextSelection.fromPosition(
const TextPosition(offset: 1),
TextPosition(offset: offset),
);
},
),
Expand Down
52 changes: 52 additions & 0 deletions test/word_focused/view/word_solving_view_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import 'package:bloc_test/bloc_test.dart';
import 'package:flutter/material.dart' hide Axis;
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:game_domain/game_domain.dart';
Expand Down Expand Up @@ -148,6 +149,21 @@ void main() {
},
);

testWidgets(
'incorrectAnswer text when the status is incorrect',
(tester) async {
when(() => wordSelectionBloc.state).thenReturn(
WordSelectionState(
status: WordSelectionStatus.incorrect,
word: selectedWord,
),
);
await tester.pumpApp(widget);

expect(find.text(l10n.incorrectAnswer), findsOneWidget);
},
);

testWidgets(
'the $HintsSection when the status is not validating',
(tester) async {
Expand Down Expand Up @@ -236,6 +252,21 @@ void main() {
},
);

testWidgets(
'incorrectAnswer text when the status is incorrect',
(tester) async {
when(() => wordSelectionBloc.state).thenReturn(
WordSelectionState(
status: WordSelectionStatus.incorrect,
word: selectedWord,
),
);
await tester.pumpApp(widget);

expect(find.text(l10n.incorrectAnswer), findsOneWidget);
},
);

testWidgets(
'the $HintsSection when the status is not validating',
(tester) async {
Expand Down Expand Up @@ -304,6 +335,27 @@ void main() {
).called(1);
},
);

testWidgets(
'deleting a letter sends $WordSolveRequested',
(tester) async {
await tester.pumpApp(widget);
await tester.pumpAndSettle();

final editableTexts = find.byType(EditableText);
await tester.enterText(editableTexts.at(0), 'A');
await tester.enterText(editableTexts.at(1), 'N');
await tester.enterText(editableTexts.at(2), 'S');
await tester.enterText(editableTexts.at(3), '!'); // focus previous cell
await tester.pumpAndSettle();

await tester.sendKeyEvent(LogicalKeyboardKey.backspace);

verify(
() => wordSelectionBloc.add(const WordSolveRequested()),
).called(1);
},
);
});

group('$BottomPanel', () {
Expand Down

0 comments on commit 9788e68

Please sign in to comment.