Skip to content

Commit

Permalink
feat: add player stats with cloud firestore
Browse files Browse the repository at this point in the history
  • Loading branch information
thisissandipp committed Aug 4, 2024
1 parent 19e7222 commit b726f3c
Show file tree
Hide file tree
Showing 33 changed files with 2,453 additions and 86 deletions.
1,091 changes: 1,091 additions & 0 deletions ios/Podfile.lock

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion lib/app/view/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@ class App extends StatelessWidget {
required SudokuAPI apiClient,
required PuzzleRepository puzzleRepository,
required AuthenticationRepository authenticationRepository,
required PlayerRepository playerRepository,
super.key,
}) : _apiClient = apiClient,
_puzzleRepository = puzzleRepository,
_authenticationRepository = authenticationRepository;
_authenticationRepository = authenticationRepository,
_playerRepository = playerRepository;

final SudokuAPI _apiClient;
final PuzzleRepository _puzzleRepository;
final AuthenticationRepository _authenticationRepository;
final PlayerRepository _playerRepository;

@override
Widget build(BuildContext context) {
Expand All @@ -34,6 +37,9 @@ class App extends StatelessWidget {
RepositoryProvider<AuthenticationRepository>.value(
value: _authenticationRepository,
),
RepositoryProvider<PlayerRepository>.value(
value: _playerRepository,
),
],
child: const AppView(),
);
Expand Down
9 changes: 8 additions & 1 deletion lib/bootstrap.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:developer';

import 'package:bloc/bloc.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
Expand All @@ -11,6 +12,7 @@ import 'package:sudoku/app_bloc_observer.dart';
/// The type definition for the builder widget.
typedef BootstrapBuilder = FutureOr<Widget> Function(
FirebaseAuth firebaseAuth,
FirebaseFirestore firestore,
);

/// Bootstrap is responsible for any common setup and calls
Expand All @@ -37,7 +39,12 @@ Future<void> bootstrap(BootstrapBuilder builder) async {
await runZonedGuarded(
() async {
Bloc.observer = const AppBlocObserver();
runApp(await builder(FirebaseAuth.instance));
runApp(
await builder(
FirebaseAuth.instance,
FirebaseFirestore.instance,
),
);
},
(error, stackTrace) => log(error.toString(), stackTrace: stackTrace),
);
Expand Down
4 changes: 2 additions & 2 deletions lib/colors/colors.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ abstract class SudokuColors {
static const lightPurple = Color(0xFF9089FC);

/// Dark Pink
static const darkPink = Color(0xFFFF38B0);
static const darkPink = Color(0xFFFC1FA4);

/// Dark Purple
static const darkPurple = Color(0xFF7A57FD);
static const darkPurple = Color(0xFF5E33FD);

/// Green
static const green = Color(0xFF388E3C);
Expand Down
39 changes: 37 additions & 2 deletions lib/home/bloc/home_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,24 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
HomeBloc({
required SudokuAPI apiClient,
required PuzzleRepository puzzleRepository,
required AuthenticationRepository authenticationRepository,
required PlayerRepository playerRepository,
}) : _apiClient = apiClient,
_puzzleRepository = puzzleRepository,
_authenticationRepository = authenticationRepository,
_playerRepository = playerRepository,
super(const HomeState()) {
on<SudokuCreationRequested>(_onSudokuCreationRequested);
on<UnfinishedPuzzleSubscriptionRequested>(_onSubscriptionRequested);
on<UnfinishedPuzzleSubscriptionRequested>(_onPuzzleSubscriptionRequested);
on<UnfinishedPuzzleResumed>(_onUnfinishedPuzzleResumed);
on<PlayerSubscriptionRequested>(_onPlayerSubscriptionRequested);
on<NewPuzzleAttempted>(_onNewPuzzleAttempted);
}

final SudokuAPI _apiClient;
final PuzzleRepository _puzzleRepository;
final AuthenticationRepository _authenticationRepository;
final PlayerRepository _playerRepository;

FutureOr<void> _onSudokuCreationRequested(
SudokuCreationRequested event,
Expand Down Expand Up @@ -73,7 +81,7 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
}
}

FutureOr<void> _onSubscriptionRequested(
FutureOr<void> _onPuzzleSubscriptionRequested(
UnfinishedPuzzleSubscriptionRequested event,
Emitter<HomeState> emit,
) async {
Expand Down Expand Up @@ -114,4 +122,31 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
),
);
}

FutureOr<void> _onPlayerSubscriptionRequested(
PlayerSubscriptionRequested event,
Emitter<HomeState> emit,
) async {
final userId = _authenticationRepository.currentUser.id;
await emit.forEach(
_playerRepository.getPlayer(userId),
onData: (player) => state.copyWith(
player: () => player,
),
onError: (_, __) => state.copyWith(
player: () => Player.empty,
),
);
}

FutureOr<void> _onNewPuzzleAttempted(
NewPuzzleAttempted event,
Emitter<HomeState> emit,
) async {
final userId = _authenticationRepository.currentUser.id;
final updatedPlayer = state.player.updateAttemptCount(event.difficulty);
try {
await _playerRepository.updatePlayer(userId, updatedPlayer);
} catch (_) {}
}
}
13 changes: 13 additions & 0 deletions lib/home/bloc/home_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,16 @@ final class UnfinishedPuzzleSubscriptionRequested extends HomeEvent {
final class UnfinishedPuzzleResumed extends HomeEvent {
const UnfinishedPuzzleResumed();
}

final class PlayerSubscriptionRequested extends HomeEvent {
const PlayerSubscriptionRequested();
}

final class NewPuzzleAttempted extends HomeEvent {
const NewPuzzleAttempted(this.difficulty);

final Difficulty difficulty;

@override
List<Object?> get props => [difficulty];
}
5 changes: 5 additions & 0 deletions lib/home/bloc/home_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,30 @@ class HomeState extends Equatable {
this.sudokuCreationStatus = SudokuCreationStatus.initial,
this.sudokuCreationError,
this.unfinishedPuzzle,
this.player = Player.empty,
});

final Difficulty? difficulty;
final SudokuCreationStatus sudokuCreationStatus;
final SudokuCreationErrorType? sudokuCreationError;
final Puzzle? unfinishedPuzzle;
final Player player;

@override
List<Object?> get props => [
difficulty,
sudokuCreationStatus,
sudokuCreationError,
unfinishedPuzzle,
player,
];

HomeState copyWith({
Difficulty? Function()? difficulty,
SudokuCreationStatus Function()? sudokuCreationStatus,
SudokuCreationErrorType? Function()? sudokuCreationError,
Puzzle? Function()? unfinishedPuzzle,
Player Function()? player,
}) {
return HomeState(
difficulty: difficulty != null ? difficulty() : this.difficulty,
Expand All @@ -42,6 +46,7 @@ class HomeState extends Equatable {
: this.sudokuCreationError,
unfinishedPuzzle:
unfinishedPuzzle != null ? unfinishedPuzzle() : this.unfinishedPuzzle,
player: player != null ? player() : this.player,
);
}
}
1 change: 1 addition & 0 deletions lib/home/home.dart
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export 'bloc/home_bloc.dart';
export 'view/home_page.dart';
export 'widgets/widgets.dart';
56 changes: 28 additions & 28 deletions lib/home/view/home_page.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import 'dart:developer';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
Expand All @@ -26,8 +24,12 @@ class HomePage extends StatelessWidget {
return BlocProvider<HomeBloc>(
create: (context) => HomeBloc(
apiClient: context.read<SudokuAPI>(),
authenticationRepository: context.read<AuthenticationRepository>(),
puzzleRepository: context.read<PuzzleRepository>(),
)..add(const UnfinishedPuzzleSubscriptionRequested()),
playerRepository: context.read<PlayerRepository>(),
)
..add(const UnfinishedPuzzleSubscriptionRequested())
..add(const PlayerSubscriptionRequested()),
child: const HomeView(),
);
}
Expand Down Expand Up @@ -282,16 +284,6 @@ class HighlightedSection extends StatelessWidget {
(HomeBloc bloc) => bloc.state.unfinishedPuzzle,
);

final dailyChallengeWidget = HighlightedSectionItem(
key: const Key('daily_challenge_widget'),
elevatedButtonkey: const Key('daily_challenge_widget_elevated_button'),
iconAsset: Assets.dailyChallengeIcon,
title: l10n.dailyChallengeTitle,
subtitle: l10n.dailyChallengeSubtitle,
buttonText: 'Play',
onButtonPressed: () => log('daily_challenge'),
);

final resumePuzzleWidget = HighlightedSectionItem(
key: const Key('resume_puzzle_widget'),
elevatedButtonkey: const Key('resume_puzzle_widget_elevated_button'),
Expand All @@ -313,7 +305,7 @@ class HighlightedSection extends StatelessWidget {
),
child: Row(
children: [
Expanded(child: dailyChallengeWidget),
const Expanded(child: PlayerInfoWidget()),
const SizedBox(width: 16),
Expanded(child: resumePuzzleWidget),
],
Expand All @@ -328,7 +320,7 @@ class HighlightedSection extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
dailyChallengeWidget,
const PlayerInfoWidget(),
const SizedBox(height: 24),
resumePuzzleWidget,
],
Expand All @@ -339,7 +331,7 @@ class HighlightedSection extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 32),
child: Row(
children: [
Expanded(child: dailyChallengeWidget),
const Expanded(child: PlayerInfoWidget()),
const SizedBox(width: 32),
Expanded(child: resumePuzzleWidget),
],
Expand Down Expand Up @@ -537,39 +529,47 @@ class CreateGameSection extends StatelessWidget {
iconAsset: Assets.easyPuzzleIcon,
title: l10n.createEasyGameTitle,
caption: l10n.createEasyGameCaption,
onButtonPressed: () => context.read<HomeBloc>().add(
const SudokuCreationRequested(Difficulty.easy),
),
onButtonPressed: () {
context.read<HomeBloc>()
..add(const SudokuCreationRequested(Difficulty.easy))
..add(const NewPuzzleAttempted(Difficulty.easy));
},
),
CreateGameSectionItem(
key: const Key('create_game_medium_mode'),
textButtonkey: const Key('create_game_medium_mode_text_button'),
iconAsset: Assets.mediumPuzzleIcon,
title: l10n.createMediumGameTitle,
caption: l10n.createMediumGameCaption,
onButtonPressed: () => context.read<HomeBloc>().add(
const SudokuCreationRequested(Difficulty.medium),
),
onButtonPressed: () {
context.read<HomeBloc>()
..add(const SudokuCreationRequested(Difficulty.medium))
..add(const NewPuzzleAttempted(Difficulty.medium));
},
),
CreateGameSectionItem(
key: const Key('create_game_difficult_mode'),
textButtonkey: const Key('create_game_difficult_mode_text_button'),
iconAsset: Assets.difficultPuzzleIcon,
title: l10n.createDifficultGameTitle,
caption: l10n.createDifficultGameCaption,
onButtonPressed: () => context.read<HomeBloc>().add(
const SudokuCreationRequested(Difficulty.difficult),
),
onButtonPressed: () {
context.read<HomeBloc>()
..add(const SudokuCreationRequested(Difficulty.difficult))
..add(const NewPuzzleAttempted(Difficulty.difficult));
},
),
CreateGameSectionItem(
key: const Key('create_game_expert_mode'),
textButtonkey: const Key('create_game_expert_mode_text_button'),
iconAsset: Assets.expertPuzzleIcon,
title: l10n.createExpertGameTitle,
caption: l10n.createExpertGameCaption,
onButtonPressed: () => context.read<HomeBloc>().add(
const SudokuCreationRequested(Difficulty.expert),
),
onButtonPressed: () {
context.read<HomeBloc>()
..add(const SudokuCreationRequested(Difficulty.expert))
..add(const NewPuzzleAttempted(Difficulty.expert));
},
),
];

Expand Down
Loading

0 comments on commit b726f3c

Please sign in to comment.