mirror of https://github.com/flutter/pinball.git
feat: adding leaderboard display (#352)
* feat: adding leaderboard display * feat: pr suggestions * fix: conflicts * lint Co-authored-by: Alejandro Santiago <dev@alestiago.com>pull/367/head
parent
2f94cf84d6
commit
c8a2150787
@ -1,4 +1,5 @@
|
|||||||
export 'initials_input_display.dart';
|
export 'initials_input_display.dart';
|
||||||
export 'initials_submission_failure_display.dart';
|
export 'initials_submission_failure_display.dart';
|
||||||
export 'initials_submission_success_display.dart';
|
export 'initials_submission_success_display.dart';
|
||||||
|
export 'leaderboard_display.dart';
|
||||||
export 'loading_display.dart';
|
export 'loading_display.dart';
|
||||||
|
@ -0,0 +1,120 @@
|
|||||||
|
import 'package:flame/components.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:leaderboard_repository/leaderboard_repository.dart';
|
||||||
|
import 'package:pinball/l10n/l10n.dart';
|
||||||
|
import 'package:pinball/leaderboard/models/leader_board_entry.dart';
|
||||||
|
import 'package:pinball_components/pinball_components.dart';
|
||||||
|
import 'package:pinball_flame/pinball_flame.dart';
|
||||||
|
import 'package:pinball_ui/pinball_ui.dart';
|
||||||
|
|
||||||
|
final _titleTextPaint = TextPaint(
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 2,
|
||||||
|
color: PinballColors.red,
|
||||||
|
fontFamily: PinballFonts.pixeloidSans,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final _bodyTextPaint = TextPaint(
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 1.8,
|
||||||
|
color: PinballColors.white,
|
||||||
|
fontFamily: PinballFonts.pixeloidSans,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
/// {@template leaderboard_display}
|
||||||
|
/// Component that builds the leaderboard list of the Backbox.
|
||||||
|
/// {@endtemplate}
|
||||||
|
class LeaderboardDisplay extends PositionComponent with HasGameRef {
|
||||||
|
/// {@macro leaderboard_display}
|
||||||
|
LeaderboardDisplay({required List<LeaderboardEntryData> entries})
|
||||||
|
: _entries = entries;
|
||||||
|
|
||||||
|
final List<LeaderboardEntryData> _entries;
|
||||||
|
|
||||||
|
double _calcY(int i) => (i * 3.2) + 3.2;
|
||||||
|
|
||||||
|
static const _columns = [-15.0, 0.0, 15.0];
|
||||||
|
|
||||||
|
String _rank(int number) {
|
||||||
|
switch (number) {
|
||||||
|
case 1:
|
||||||
|
return '${number}st';
|
||||||
|
case 2:
|
||||||
|
return '${number}nd';
|
||||||
|
case 3:
|
||||||
|
return '${number}rd';
|
||||||
|
default:
|
||||||
|
return '${number}th';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> onLoad() async {
|
||||||
|
position = Vector2(0, -30);
|
||||||
|
|
||||||
|
final l10n = readProvider<AppLocalizations>();
|
||||||
|
final ranking = _entries.take(5).toList();
|
||||||
|
await add(
|
||||||
|
PositionComponent(
|
||||||
|
position: Vector2(0, 4),
|
||||||
|
children: [
|
||||||
|
PositionComponent(
|
||||||
|
children: [
|
||||||
|
TextComponent(
|
||||||
|
text: l10n.rank,
|
||||||
|
textRenderer: _titleTextPaint,
|
||||||
|
position: Vector2(_columns[0], 0),
|
||||||
|
anchor: Anchor.center,
|
||||||
|
),
|
||||||
|
TextComponent(
|
||||||
|
text: l10n.score,
|
||||||
|
textRenderer: _titleTextPaint,
|
||||||
|
position: Vector2(_columns[1], 0),
|
||||||
|
anchor: Anchor.center,
|
||||||
|
),
|
||||||
|
TextComponent(
|
||||||
|
text: l10n.name,
|
||||||
|
textRenderer: _titleTextPaint,
|
||||||
|
position: Vector2(_columns[2], 0),
|
||||||
|
anchor: Anchor.center,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
for (var i = 0; i < ranking.length; i++)
|
||||||
|
PositionComponent(
|
||||||
|
children: [
|
||||||
|
TextComponent(
|
||||||
|
text: _rank(i + 1),
|
||||||
|
textRenderer: _bodyTextPaint,
|
||||||
|
position: Vector2(_columns[0], _calcY(i)),
|
||||||
|
anchor: Anchor.center,
|
||||||
|
),
|
||||||
|
TextComponent(
|
||||||
|
text: ranking[i].score.formatScore(),
|
||||||
|
textRenderer: _bodyTextPaint,
|
||||||
|
position: Vector2(_columns[1], _calcY(i)),
|
||||||
|
anchor: Anchor.center,
|
||||||
|
),
|
||||||
|
SpriteComponent.fromImage(
|
||||||
|
gameRef.images.fromCache(
|
||||||
|
ranking[i].character.toTheme.leaderboardIcon.keyName,
|
||||||
|
),
|
||||||
|
anchor: Anchor.center,
|
||||||
|
size: Vector2(1.8, 1.8),
|
||||||
|
position: Vector2(_columns[2] - 2.5, _calcY(i) + .25),
|
||||||
|
),
|
||||||
|
TextComponent(
|
||||||
|
text: ranking[i].playerInitials,
|
||||||
|
textRenderer: _bodyTextPaint,
|
||||||
|
position: Vector2(_columns[2] + 1, _calcY(i)),
|
||||||
|
anchor: Anchor.center,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,109 @@
|
|||||||
|
// ignore_for_file: cascade_invocations
|
||||||
|
|
||||||
|
import 'package:flame/components.dart';
|
||||||
|
import 'package:flame_forge2d/forge2d_game.dart';
|
||||||
|
import 'package:flame_test/flame_test.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:leaderboard_repository/leaderboard_repository.dart';
|
||||||
|
import 'package:mocktail/mocktail.dart';
|
||||||
|
import 'package:pinball/game/components/backbox/displays/leaderboard_display.dart';
|
||||||
|
import 'package:pinball/l10n/l10n.dart';
|
||||||
|
import 'package:pinball_flame/pinball_flame.dart';
|
||||||
|
import 'package:pinball_theme/pinball_theme.dart';
|
||||||
|
|
||||||
|
class _MockAppLocalizations extends Mock implements AppLocalizations {
|
||||||
|
@override
|
||||||
|
String get rank => 'rank';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get score => 'score';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get name => 'name';
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TestGame extends Forge2DGame {
|
||||||
|
@override
|
||||||
|
Future<void> onLoad() async {
|
||||||
|
await super.onLoad();
|
||||||
|
images.prefix = '';
|
||||||
|
await images.load(const AndroidTheme().leaderboardIcon.keyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> pump(LeaderboardDisplay component) {
|
||||||
|
return ensureAdd(
|
||||||
|
FlameProvider.value(
|
||||||
|
_MockAppLocalizations(),
|
||||||
|
children: [component],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('LeaderboardDisplay', () {
|
||||||
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
final flameTester = FlameTester(_TestGame.new);
|
||||||
|
|
||||||
|
flameTester.test('renders the titles', (game) async {
|
||||||
|
await game.pump(LeaderboardDisplay(entries: const []));
|
||||||
|
|
||||||
|
final textComponents =
|
||||||
|
game.descendants().whereType<TextComponent>().toList();
|
||||||
|
expect(textComponents.length, equals(3));
|
||||||
|
expect(textComponents[0].text, equals('rank'));
|
||||||
|
expect(textComponents[1].text, equals('score'));
|
||||||
|
expect(textComponents[2].text, equals('name'));
|
||||||
|
});
|
||||||
|
|
||||||
|
flameTester.test('renders the entries', (game) async {
|
||||||
|
await game.pump(
|
||||||
|
LeaderboardDisplay(
|
||||||
|
entries: const [
|
||||||
|
LeaderboardEntryData(
|
||||||
|
playerInitials: 'AAA',
|
||||||
|
score: 123,
|
||||||
|
character: CharacterType.android,
|
||||||
|
),
|
||||||
|
LeaderboardEntryData(
|
||||||
|
playerInitials: 'BBB',
|
||||||
|
score: 1234,
|
||||||
|
character: CharacterType.android,
|
||||||
|
),
|
||||||
|
LeaderboardEntryData(
|
||||||
|
playerInitials: 'CCC',
|
||||||
|
score: 12345,
|
||||||
|
character: CharacterType.android,
|
||||||
|
),
|
||||||
|
LeaderboardEntryData(
|
||||||
|
playerInitials: 'DDD',
|
||||||
|
score: 12346,
|
||||||
|
character: CharacterType.android,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
for (final text in [
|
||||||
|
'AAA',
|
||||||
|
'BBB',
|
||||||
|
'CCC',
|
||||||
|
'DDD',
|
||||||
|
'1st',
|
||||||
|
'2nd',
|
||||||
|
'3rd',
|
||||||
|
'4th'
|
||||||
|
]) {
|
||||||
|
expect(
|
||||||
|
game
|
||||||
|
.descendants()
|
||||||
|
.whereType<TextComponent>()
|
||||||
|
.where((textComponent) => textComponent.text == text)
|
||||||
|
.length,
|
||||||
|
equals(1),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in new issue