Skip to content

Commit

Permalink
feat: use new InitialsPage (#229)
Browse files Browse the repository at this point in the history
* feat: define initials bloc

* refactor: removing duplicate

* feat: initials changes

* feat: use controller

* refactor: update game intro

* feat: updated initials

* test: update test files

* feat: simplified initialsBloc

* refactor: initials_error use error

* test: more and more tests

* feat: include createScore

* chore: redundant argument

* test: fix test

* test: add more initials state tests

* fix: test

* revert: pumpAndSettle
  • Loading branch information
alestiago authored Apr 9, 2024
1 parent 28b4570 commit 4ef2cac
Show file tree
Hide file tree
Showing 31 changed files with 911 additions and 1,187 deletions.
2 changes: 1 addition & 1 deletion lib/crossword/bloc/crossword_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ class CrosswordBloc extends Bloc<CrosswordEvent, CrosswordState> {
) async {
if (state is CrosswordLoaded) {
emit(
(state as CrosswordLoaded).copyWith(initials: event.initials.join()),
(state as CrosswordLoaded).copyWith(initials: event.initials),
);
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/crossword/bloc/crossword_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class BoardLoadingInfoFetched extends CrosswordEvent {
class InitialsSelected extends CrosswordEvent {
const InitialsSelected(this.initials);

final List<String> initials;
final String initials;

@override
List<Object> get props => [initials];
Expand Down
86 changes: 1 addition & 85 deletions lib/game_intro/bloc/game_intro_bloc.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import 'package:api_client/api_client.dart';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:game_domain/game_domain.dart';
Expand All @@ -7,40 +6,9 @@ part 'game_intro_event.dart';
part 'game_intro_state.dart';

class GameIntroBloc extends Bloc<GameIntroEvent, GameIntroState> {
GameIntroBloc({
required LeaderboardResource leaderboardResource,
}) : _leaderboardResource = leaderboardResource,
super(const GameIntroState()) {
on<BlacklistRequested>(_onBlacklistRequested);
on<WelcomeCompleted>(_onWelcomeCompleted);
GameIntroBloc() : super(const GameIntroState()) {
on<MascotUpdated>(_onMascotUpdated);
on<MascotSubmitted>(_onMascotSubmitted);
on<InitialsUpdated>(_onInitialsUpdated);
on<InitialsSubmitted>(_onInitialsSubmitted);
}

final LeaderboardResource _leaderboardResource;
final initialsRegex = RegExp('[A-Z]{3}');

Future<void> _onBlacklistRequested(
BlacklistRequested event,
Emitter<GameIntroState> emit,
) async {
try {
final blacklist = await _leaderboardResource.getInitialsBlacklist();
emit(state.copyWith(initialsBlacklist: blacklist));
} catch (e, s) {
addError(e, s);
}
}

void _onWelcomeCompleted(
WelcomeCompleted event,
Emitter<GameIntroState> emit,
) {
emit(
state.copyWith(status: GameIntroStatus.mascotSelection),
);
}

void _onMascotUpdated(
Expand All @@ -60,56 +28,4 @@ class GameIntroBloc extends Bloc<GameIntroEvent, GameIntroState> {
state.copyWith(status: GameIntroStatus.initialsInput),
);
}

void _onInitialsUpdated(
InitialsUpdated event,
Emitter<GameIntroState> emit,
) {
final initials = [...state.initials];
initials[event.index] = event.character;
final initialsStatus =
(state.initialsStatus == InitialsFormStatus.blacklisted)
? InitialsFormStatus.initial
: state.initialsStatus;
emit(state.copyWith(initials: initials, initialsStatus: initialsStatus));
}

Future<void> _onInitialsSubmitted(
InitialsSubmitted event,
Emitter<GameIntroState> emit,
) async {
if (!_hasValidPattern()) {
emit(state.copyWith(initialsStatus: InitialsFormStatus.invalid));
} else if (_isBlacklisted()) {
emit(state.copyWith(initialsStatus: InitialsFormStatus.blacklisted));
} else {
emit(state.copyWith(initialsStatus: InitialsFormStatus.loading));

try {
await _leaderboardResource.createScore(
initials: state.initials.join(),
mascot: state.selectedMascot,
);

emit(
state.copyWith(
initialsStatus: InitialsFormStatus.success,
isIntroCompleted: true,
),
);
} catch (e, s) {
addError(e, s);
emit(state.copyWith(initialsStatus: InitialsFormStatus.failure));
}
}
}

bool _hasValidPattern() {
final value = state.initials;
return value.isNotEmpty && initialsRegex.hasMatch(value.join());
}

bool _isBlacklisted() {
return state.initialsBlacklist.contains(state.initials.join());
}
}
31 changes: 0 additions & 31 deletions lib/game_intro/bloc/game_intro_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,6 @@ sealed class GameIntroEvent extends Equatable {
const GameIntroEvent();
}

class BlacklistRequested extends GameIntroEvent {
const BlacklistRequested();

@override
List<Object> get props => [];
}

class WelcomeCompleted extends GameIntroEvent {
const WelcomeCompleted();

@override
List<Object> get props => [];
}

class MascotUpdated extends GameIntroEvent {
const MascotUpdated(this.mascot);

Expand All @@ -33,20 +19,3 @@ class MascotSubmitted extends GameIntroEvent {
@override
List<Object> get props => [];
}

class InitialsUpdated extends GameIntroEvent {
const InitialsUpdated({required this.character, required this.index});

final String character;
final int index;

@override
List<Object> get props => [character, index];
}

class InitialsSubmitted extends GameIntroEvent {
const InitialsSubmitted();

@override
List<Object> get props => [];
}
36 changes: 1 addition & 35 deletions lib/game_intro/bloc/game_intro_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,59 +6,25 @@ enum GameIntroStatus {
initialsInput,
}

enum InitialsFormStatus {
initial,
loading,
success,
invalid,
failure,
blacklisted,
}

class GameIntroState extends Equatable {
const GameIntroState({
this.status = GameIntroStatus.welcome,
this.isIntroCompleted = false,
this.selectedMascot = Mascots.dash,
this.initials = const ['', '', ''],
this.initialsBlacklist = const [],
this.initialsStatus = InitialsFormStatus.initial,
});

final GameIntroStatus status;
final bool isIntroCompleted;
final Mascots selectedMascot;
final List<String> initials;
final List<String> initialsBlacklist;
final InitialsFormStatus initialsStatus;

GameIntroState copyWith({
GameIntroStatus? status,
bool? isIntroCompleted,
int? solvedWords,
int? totalWords,
Mascots? selectedMascot,
List<String>? initials,
List<String>? initialsBlacklist,
InitialsFormStatus? initialsStatus,
}) {
return GameIntroState(
status: status ?? this.status,
isIntroCompleted: isIntroCompleted ?? this.isIntroCompleted,
selectedMascot: selectedMascot ?? this.selectedMascot,
initials: initials ?? this.initials,
initialsBlacklist: initialsBlacklist ?? this.initialsBlacklist,
initialsStatus: initialsStatus ?? this.initialsStatus,
);
}

@override
List<Object?> get props => [
status,
isIntroCompleted,
selectedMascot,
initials,
initialsBlacklist,
initialsStatus,
];
List<Object?> get props => [status, selectedMascot];
}
42 changes: 28 additions & 14 deletions lib/game_intro/view/game_intro_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import 'package:api_client/api_client.dart';
import 'package:flow_builder/flow_builder.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:game_domain/game_domain.dart';
import 'package:io_crossword/about/about.dart';
import 'package:io_crossword/crossword/crossword.dart';
import 'package:io_crossword/game_intro/game_intro.dart';
import 'package:io_crossword/initials/view/initials_page.dart';
import 'package:io_crossword/welcome/view/welcome_page.dart';

class GameIntroPage extends StatelessWidget {
Expand All @@ -13,39 +15,51 @@ class GameIntroPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => GameIntroBloc(
leaderboardResource: context.read<LeaderboardResource>(),
)..add(const BlacklistRequested()),
create: (_) => GameIntroBloc(),
child: const GameIntroView(),
);
}
}

class GameIntroView extends StatelessWidget {
const GameIntroView({super.key});
const GameIntroView({
super.key,
@visibleForTesting FlowController<GameIntroState>? flowController,
}) : _flowController = flowController;

final FlowController<GameIntroState>? _flowController;

@override
Widget build(BuildContext context) {
return BlocListener<GameIntroBloc, GameIntroState>(
listenWhen: (previous, current) =>
(previous.status != current.status) || current.isIntroCompleted,
listenWhen: (previous, current) => previous.status != current.status,
listener: (context, state) {
if (state.status == GameIntroStatus.initialsInput) {
context
.read<CrosswordBloc>()
.add(MascotSelected(state.selectedMascot));
}
if (state.isIntroCompleted) {
context.read<CrosswordBloc>().add(InitialsSelected(state.initials));

Navigator.of(context).pushReplacement(CrosswordPage.route());
AboutView.showModal(context);
}
},
child: Material(
child: FlowBuilder<GameIntroState>(
state: context.select((GameIntroBloc bloc) => bloc.state),
controller: _flowController,
state: _flowController == null
? context.select((GameIntroBloc bloc) => bloc.state)
: null,
onGeneratePages: onGenerateGameIntroPages,
onComplete: (state) {
// coverage:ignore-start
// TODO(alestiago): Handle this creation.
// https://very-good-ventures-team.monday.com/boards/6004820050/pulses/6422014818
context.read<LeaderboardResource>().createScore(
initials: 'AAA',
mascot: Mascots.dash,
);
// coverage:ignore-end

Navigator.of(context).pushReplacement(CrosswordPage.route());
AboutView.showModal(context);
},
),
),
);
Expand All @@ -59,6 +73,6 @@ List<Page<void>> onGenerateGameIntroPages(
return switch (state.status) {
GameIntroStatus.welcome => [WelcomePage.page()],
GameIntroStatus.mascotSelection => [MascotSelectionView.page()],
GameIntroStatus.initialsInput => [InitialsInputView.page()],
GameIntroStatus.initialsInput => [InitialsPage.page()],
};
}
Loading

0 comments on commit 4ef2cac

Please sign in to comment.