diff --git a/lib/leaderboard/bloc/leaderboard_bloc.dart b/lib/leaderboard/bloc/leaderboard_bloc.dart index 6542548d..aad01516 100644 --- a/lib/leaderboard/bloc/leaderboard_bloc.dart +++ b/lib/leaderboard/bloc/leaderboard_bloc.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:leaderboard_repository/leaderboard_repository.dart'; +import 'package:pinball_theme/pinball_theme.dart'; part 'leaderboard_event.dart'; part 'leaderboard_state.dart'; @@ -30,6 +31,12 @@ class LeaderboardBloc extends Bloc { try { final top10Leaderboard = await _leaderboardRepository.fetchTop10Leaderboard(); + + final leaderboardEntries = []; + top10Leaderboard.asMap().forEach( + (index, value) => leaderboardEntries.add(value.toEntry(index + 1)), + ); + emit( state.copyWith( status: LeaderboardStatus.success, @@ -62,3 +69,79 @@ class LeaderboardBloc extends Bloc { } } } + +/// {@template leaderboard_entry_data} +/// A model representing a leaderboard entry containing the ranking position, +/// player's initials, score, and chosen character. +/// +/// {@endtemplate} +class LeaderboardEntry { + /// {@macro leaderboard_entry_data} + LeaderboardEntry({ + required this.rank, + required this.playerInitials, + required this.score, + required this.character, + }); + + /// Position at ranking for [LeaderboardEntry]. + final String rank; + + /// Player's chosen initials for [LeaderboardEntry]. + final String playerInitials; + + /// Score for [LeaderboardEntry]. + final int score; + + /// [CharacterTheme] for [LeaderboardEntry]. + final CharacterTheme character; +} + +extension on LeaderboardEntryData { + LeaderboardEntry toEntry(int position) { + return LeaderboardEntry( + rank: position.toString(), + playerInitials: playerInitials, + score: score, + character: character.toTheme, + ); + } +} + +/// Converts [CharacterType] to [CharacterTheme] to show on UI character theme +/// from repository. +extension CharacterTypeX on CharacterType { + /// Conversion method to [CharacterTheme] + CharacterTheme get toTheme { + switch (this) { + case CharacterType.dash: + return const DashTheme(); + case CharacterType.sparky: + return const SparkyTheme(); + case CharacterType.android: + return const AndroidTheme(); + case CharacterType.dino: + return const DinoTheme(); + } + } +} + +/// Converts [CharacterTheme] to [CharacterType] to persist at repository the +/// character theme from UI. +extension CharacterThemeX on CharacterTheme { + /// Conversion method to [CharacterType] + CharacterType get toType { + switch (runtimeType) { + case DashTheme: + return CharacterType.dash; + case SparkyTheme: + return CharacterType.sparky; + case AndroidTheme: + return CharacterType.android; + case DinoTheme: + return CharacterType.dino; + default: + return CharacterType.dash; + } + } +} diff --git a/test/helpers/mocks.dart b/test/helpers/mocks.dart index e1bd8a0c..1e6b7289 100644 --- a/test/helpers/mocks.dart +++ b/test/helpers/mocks.dart @@ -2,6 +2,7 @@ import 'package:flame/input.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; +import 'package:leaderboard_repository/leaderboard_repository.dart'; import 'package:mocktail/mocktail.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball/theme/theme.dart'; @@ -32,6 +33,8 @@ class MockGameState extends Mock implements GameState {} class MockThemeCubit extends Mock implements ThemeCubit {} +class MockLeaderboardRepository extends Mock implements LeaderboardRepository {} + class MockRawKeyDownEvent extends Mock implements RawKeyDownEvent { @override String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { diff --git a/test/leaderboard/bloc/leaderboard_bloc_test.dart b/test/leaderboard/bloc/leaderboard_bloc_test.dart index 72db27e4..2b217704 100644 --- a/test/leaderboard/bloc/leaderboard_bloc_test.dart +++ b/test/leaderboard/bloc/leaderboard_bloc_test.dart @@ -5,8 +5,9 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:leaderboard_repository/leaderboard_repository.dart'; import 'package:mocktail/mocktail.dart'; import 'package:pinball/leaderboard/leaderboard.dart'; +import 'package:pinball_theme/pinball_theme.dart'; -class MockLeaderboardRepository extends Mock implements LeaderboardRepository {} +import '../../helpers/helpers.dart'; void main() { group('LeaderboardBloc', () { @@ -163,4 +164,40 @@ void main() { ); }); }); + + group('CharacterTypeX', () { + test('converts CharacterType.android to AndroidTheme', () { + expect(CharacterType.android.toTheme, equals(AndroidTheme())); + }); + + test('converts CharacterType.dash to DashTheme', () { + expect(CharacterType.dash.toTheme, equals(DashTheme())); + }); + + test('converts CharacterType.dino to DinoTheme', () { + expect(CharacterType.dino.toTheme, equals(DinoTheme())); + }); + + test('converts CharacterType.sparky to SparkyTheme', () { + expect(CharacterType.sparky.toTheme, equals(SparkyTheme())); + }); + }); + + group('CharacterThemeX', () { + test('converts AndroidTheme to CharacterType.android', () { + expect(AndroidTheme().toType, equals(CharacterType.android)); + }); + + test('converts DashTheme to CharacterType.dash', () { + expect(DashTheme().toType, equals(CharacterType.dash)); + }); + + test('converts DinoTheme to CharacterType.dino', () { + expect(DinoTheme().toType, equals(CharacterType.dino)); + }); + + test('converts SparkyTheme to CharacterType.sparky', () { + expect(SparkyTheme().toType, equals(CharacterType.sparky)); + }); + }); }