From 80da24b43c929e37c634eda504eacf0e74d191c6 Mon Sep 17 00:00:00 2001 From: Rui Miguel Alonso Date: Sun, 8 May 2022 19:28:03 +0200 Subject: [PATCH] feat: social networks `ShareDisplay` (#406) * feat: share screen and tests * chore: assets gen * feat: added share repository * feature: injecting sharerepository * feat: share url * feat: state for showing ShareDisplay at Backbox * test: tests for sharing links * test: backbox tests * Update lib/game/components/backbox/displays/share_display.dart Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * Update lib/game/components/backbox/displays/share_display.dart Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * Update lib/game/components/backbox/displays/share_display.dart Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * Update test/game/components/backbox/backbox_test.dart Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * Update test/game/components/backbox/displays/share_display_test.dart Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * Update test/game/components/backbox/displays/share_display_test.dart Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * Update test/game/components/backbox/backbox_test.dart Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * Update test/game/components/backbox/backbox_test.dart Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * refactor: moved down social networks icons * refactor: url and text for share Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> --- lib/app/view/app.dart | 5 + lib/game/components/backbox/backbox.dart | 24 ++ .../components/backbox/bloc/backbox_bloc.dart | 4 +- .../components/backbox/displays/displays.dart | 1 + .../backbox/displays/share_display.dart | 189 ++++++++++++++++ lib/game/game_assets.dart | 2 + lib/game/pinball_game.dart | 8 + lib/game/view/pinball_game_page.dart | 4 + lib/l10n/arb/app_en.arb | 24 +- lib/main.dart | 4 + .../assets/images/backbox/button/facebook.png | Bin 0 -> 3174 bytes .../assets/images/backbox/button/twitter.png | Bin 0 -> 3611 bytes .../lib/gen/assets.gen.dart | 15 ++ packages/pinball_components/pubspec.yaml | 1 + .../lib/src/share_repository.dart | 3 + test/app/view/app_test.dart | 6 + .../game/components/backbox/backbox_test.dart | 206 +++++++++++++++++- .../backbox/bloc/backbox_bloc_test.dart | 19 ++ .../backbox/bloc/backbox_state_test.dart | 18 ++ .../backbox/displays/share_display_test.dart | 112 ++++++++++ .../game_bloc_status_listener_test.dart | 27 ++- test/game/pinball_game_test.dart | 5 + test/game/view/pinball_game_page_test.dart | 4 + test/helpers/pump_app.dart | 7 + 24 files changed, 675 insertions(+), 13 deletions(-) create mode 100644 lib/game/components/backbox/displays/share_display.dart create mode 100644 packages/pinball_components/assets/images/backbox/button/facebook.png create mode 100644 packages/pinball_components/assets/images/backbox/button/twitter.png create mode 100644 test/game/components/backbox/displays/share_display_test.dart diff --git a/lib/app/view/app.dart b/lib/app/view/app.dart index 89f03dfa..f621f1e2 100644 --- a/lib/app/view/app.dart +++ b/lib/app/view/app.dart @@ -9,20 +9,24 @@ import 'package:pinball/select_character/select_character.dart'; import 'package:pinball/start_game/start_game.dart'; import 'package:pinball_audio/pinball_audio.dart'; import 'package:pinball_ui/pinball_ui.dart'; +import 'package:share_repository/share_repository.dart'; class App extends StatelessWidget { const App({ Key? key, required AuthenticationRepository authenticationRepository, required LeaderboardRepository leaderboardRepository, + required ShareRepository shareRepository, required PinballAudioPlayer pinballAudioPlayer, }) : _authenticationRepository = authenticationRepository, _leaderboardRepository = leaderboardRepository, + _shareRepository = shareRepository, _pinballAudioPlayer = pinballAudioPlayer, super(key: key); final AuthenticationRepository _authenticationRepository; final LeaderboardRepository _leaderboardRepository; + final ShareRepository _shareRepository; final PinballAudioPlayer _pinballAudioPlayer; @override @@ -31,6 +35,7 @@ class App extends StatelessWidget { providers: [ RepositoryProvider.value(value: _authenticationRepository), RepositoryProvider.value(value: _leaderboardRepository), + RepositoryProvider.value(value: _shareRepository), RepositoryProvider.value(value: _pinballAudioPlayer), ], child: MultiBlocProvider( diff --git a/lib/game/components/backbox/backbox.dart b/lib/game/components/backbox/backbox.dart index e455e89f..dad2c118 100644 --- a/lib/game/components/backbox/backbox.dart +++ b/lib/game/components/backbox/backbox.dart @@ -6,10 +6,13 @@ import 'package:leaderboard_repository/leaderboard_repository.dart'; import 'package:pinball/game/components/backbox/bloc/backbox_bloc.dart'; import 'package:pinball/game/components/backbox/displays/displays.dart'; import 'package:pinball/game/game.dart'; +import 'package:pinball/l10n/l10n.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_theme/pinball_theme.dart' hide Assets; +import 'package:pinball_ui/pinball_ui.dart'; import 'package:platform_helper/platform_helper.dart'; +import 'package:share_repository/share_repository.dart'; /// {@template backbox} /// The [Backbox] of the pinball machine. @@ -18,21 +21,26 @@ class Backbox extends PositionComponent with ZIndex, HasGameRef { /// {@macro backbox} Backbox({ required LeaderboardRepository leaderboardRepository, + required ShareRepository shareRepository, required List? entries, }) : _bloc = BackboxBloc( leaderboardRepository: leaderboardRepository, initialEntries: entries, ), + _shareRepository = shareRepository, _platformHelper = PlatformHelper(); /// {@macro backbox} @visibleForTesting Backbox.test({ required BackboxBloc bloc, + required ShareRepository shareRepository, required PlatformHelper platformHelper, }) : _bloc = bloc, + _shareRepository = shareRepository, _platformHelper = platformHelper; + final ShareRepository _shareRepository; late final Component _display; final BackboxBloc _bloc; final PlatformHelper _platformHelper; @@ -87,6 +95,8 @@ class Backbox extends PositionComponent with ZIndex, HasGameRef { ), ); } else if (state is InitialsSuccessState) { + gameRef.overlays.remove(PinballGame.mobileControlsOverlay); + _display.add( GameOverInfoDisplay( onShare: () { @@ -94,6 +104,20 @@ class Backbox extends PositionComponent with ZIndex, HasGameRef { }, ), ); + } else if (state is ShareState) { + _display.add( + ShareDisplay( + onShare: (platform) { + final message = readProvider() + .iGotScoreAtPinball(state.score); + final url = _shareRepository.shareText( + value: message, + platform: platform, + ); + openLink(url); + }, + ), + ); } else if (state is InitialsFailureState) { _display.add( InitialsSubmissionFailureDisplay( diff --git a/lib/game/components/backbox/bloc/backbox_bloc.dart b/lib/game/components/backbox/bloc/backbox_bloc.dart index 8a89b1bd..cfac4c6f 100644 --- a/lib/game/components/backbox/bloc/backbox_bloc.dart +++ b/lib/game/components/backbox/bloc/backbox_bloc.dart @@ -75,9 +75,7 @@ class BackboxBloc extends Bloc { Emitter emit, ) async { emit( - ShareState( - score: event.score, - ), + ShareState(score: event.score), ); } diff --git a/lib/game/components/backbox/displays/displays.dart b/lib/game/components/backbox/displays/displays.dart index 2b8a38ae..f80c0b54 100644 --- a/lib/game/components/backbox/displays/displays.dart +++ b/lib/game/components/backbox/displays/displays.dart @@ -5,3 +5,4 @@ export 'initials_submission_success_display.dart'; export 'leaderboard_display.dart'; export 'leaderboard_failure_display.dart'; export 'loading_display.dart'; +export 'share_display.dart'; diff --git a/lib/game/components/backbox/displays/share_display.dart b/lib/game/components/backbox/displays/share_display.dart new file mode 100644 index 00000000..ebaaac7e --- /dev/null +++ b/lib/game/components/backbox/displays/share_display.dart @@ -0,0 +1,189 @@ +import 'dart:async'; + +import 'package:flame/components.dart'; +import 'package:flame/input.dart'; +import 'package:flutter/material.dart'; +import 'package:pinball/l10n/l10n.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; +import 'package:pinball_ui/pinball_ui.dart'; +import 'package:share_repository/share_repository.dart'; + +/// Signature for the callback called when the user tries to share their score +/// on the [ShareDisplay]. +typedef OnSocialShareTap = void Function(SharePlatform); + +final _descriptionTextPaint = TextPaint( + style: const TextStyle( + fontSize: 1.6, + color: PinballColors.white, + fontFamily: PinballFonts.pixeloidSans, + ), +); + +/// {@template share_display} +/// Display that allows users to share their score to social networks. +/// {@endtemplate} +class ShareDisplay extends Component with HasGameRef { + /// {@macro share_display} + ShareDisplay({ + OnSocialShareTap? onShare, + }) : super( + children: [ + _ShareInstructionsComponent( + onShare: onShare, + ), + ], + ); +} + +class _ShareInstructionsComponent extends PositionComponent with HasGameRef { + _ShareInstructionsComponent({ + OnSocialShareTap? onShare, + }) : super( + anchor: Anchor.center, + position: Vector2(0, -25), + children: [ + _DescriptionComponent(), + _SocialNetworksComponent( + onShare: onShare, + ), + ], + ); +} + +class _DescriptionComponent extends PositionComponent with HasGameRef { + _DescriptionComponent() + : super( + anchor: Anchor.center, + position: Vector2.zero(), + children: [ + _LetEveryoneTextComponent(), + _SharingYourScoreTextComponent(), + _SocialMediaTextComponent(), + ], + ); +} + +class _LetEveryoneTextComponent extends TextComponent with HasGameRef { + _LetEveryoneTextComponent() + : super( + anchor: Anchor.center, + position: Vector2.zero(), + textRenderer: _descriptionTextPaint, + ); + + @override + Future onLoad() async { + await super.onLoad(); + text = readProvider().letEveryone; + } +} + +class _SharingYourScoreTextComponent extends TextComponent with HasGameRef { + _SharingYourScoreTextComponent() + : super( + anchor: Anchor.center, + position: Vector2(0, 2.5), + textRenderer: _descriptionTextPaint, + ); + + @override + Future onLoad() async { + await super.onLoad(); + text = readProvider().bySharingYourScore; + } +} + +class _SocialMediaTextComponent extends TextComponent with HasGameRef { + _SocialMediaTextComponent() + : super( + anchor: Anchor.center, + position: Vector2(0, 5), + textRenderer: _descriptionTextPaint, + ); + + @override + Future onLoad() async { + await super.onLoad(); + text = readProvider().socialMediaAccount; + } +} + +class _SocialNetworksComponent extends PositionComponent with HasGameRef { + _SocialNetworksComponent({ + OnSocialShareTap? onShare, + }) : super( + anchor: Anchor.center, + position: Vector2(0, 12), + children: [ + FacebookButtonComponent(onTap: onShare), + TwitterButtonComponent(onTap: onShare), + ], + ); +} + +/// {@template facebook_button_component} +/// Button for sharing on Facebook. +/// {@endtemplate} +class FacebookButtonComponent extends SpriteComponent + with HasGameRef, Tappable { + /// {@macro facebook_button_component} + FacebookButtonComponent({ + OnSocialShareTap? onTap, + }) : _onTap = onTap, + super( + anchor: Anchor.center, + position: Vector2(-5, 0), + ); + + final OnSocialShareTap? _onTap; + + @override + bool onTapDown(TapDownInfo info) { + _onTap?.call(SharePlatform.facebook); + return true; + } + + @override + Future onLoad() async { + await super.onLoad(); + final sprite = Sprite( + gameRef.images.fromCache(Assets.images.backbox.button.facebook.keyName), + ); + this.sprite = sprite; + size = sprite.originalSize / 25; + } +} + +/// {@template twitter_button_component} +/// Button for sharing on Twitter. +/// {@endtemplate} +class TwitterButtonComponent extends SpriteComponent with HasGameRef, Tappable { + /// {@macro twitter_button_component} + TwitterButtonComponent({ + OnSocialShareTap? onTap, + }) : _onTap = onTap, + super( + anchor: Anchor.center, + position: Vector2(5, 0), + ); + + final OnSocialShareTap? _onTap; + + @override + bool onTapDown(TapDownInfo info) { + _onTap?.call(SharePlatform.twitter); + return true; + } + + @override + Future onLoad() async { + await super.onLoad(); + final sprite = Sprite( + gameRef.images.fromCache(Assets.images.backbox.button.twitter.keyName), + ); + this.sprite = sprite; + size = sprite.originalSize / 25; + } +} diff --git a/lib/game/game_assets.dart b/lib/game/game_assets.dart index c10a11ed..2f386695 100644 --- a/lib/game/game_assets.dart +++ b/lib/game/game_assets.dart @@ -101,6 +101,8 @@ extension PinballGameAssetsX on PinballGame { images.load(components.Assets.images.sparky.bumper.c.dimmed.keyName), images.load(components.Assets.images.backbox.marquee.keyName), images.load(components.Assets.images.backbox.displayDivider.keyName), + images.load(components.Assets.images.backbox.button.facebook.keyName), + images.load(components.Assets.images.backbox.button.twitter.keyName), images.load( components.Assets.images.backbox.displayTitleDecoration.keyName, ), diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index 8c018085..8a213d31 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -14,12 +14,14 @@ import 'package:pinball/select_character/select_character.dart'; import 'package:pinball_audio/pinball_audio.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; +import 'package:share_repository/share_repository.dart'; class PinballGame extends PinballForge2DGame with HasKeyboardHandlerComponents, MultiTouchTapDetector, HasTappables { PinballGame({ required CharacterThemeCubit characterThemeBloc, required this.leaderboardRepository, + required this.shareRepository, required GameBloc gameBloc, required AppLocalizations l10n, required PinballAudioPlayer audioPlayer, @@ -51,6 +53,8 @@ class PinballGame extends PinballForge2DGame final LeaderboardRepository leaderboardRepository; + final ShareRepository shareRepository; + final AppLocalizations _l10n; final GameBloc _gameBloc; @@ -84,6 +88,7 @@ class PinballGame extends PinballForge2DGame providers: [ FlameProvider.value(_audioPlayer), FlameProvider.value(leaderboardRepository), + FlameProvider.value(shareRepository), FlameProvider.value(_l10n), ], children: [ @@ -106,6 +111,7 @@ class PinballGame extends PinballForge2DGame Boundaries(), Backbox( leaderboardRepository: leaderboardRepository, + shareRepository: shareRepository, entries: _entries, ), GoogleWord(position: Vector2(-4.45, 1.8)), @@ -188,6 +194,7 @@ class DebugPinballGame extends PinballGame with FPSCounter, PanDetector { DebugPinballGame({ required CharacterThemeCubit characterThemeBloc, required LeaderboardRepository leaderboardRepository, + required ShareRepository shareRepository, required AppLocalizations l10n, required PinballAudioPlayer audioPlayer, required GameBloc gameBloc, @@ -195,6 +202,7 @@ class DebugPinballGame extends PinballGame with FPSCounter, PanDetector { characterThemeBloc: characterThemeBloc, audioPlayer: audioPlayer, leaderboardRepository: leaderboardRepository, + shareRepository: shareRepository, l10n: l10n, gameBloc: gameBloc, ); diff --git a/lib/game/view/pinball_game_page.dart b/lib/game/view/pinball_game_page.dart index 6e910817..a114ec0b 100644 --- a/lib/game/view/pinball_game_page.dart +++ b/lib/game/view/pinball_game_page.dart @@ -12,6 +12,7 @@ import 'package:pinball/select_character/select_character.dart'; import 'package:pinball/start_game/start_game.dart'; import 'package:pinball_audio/pinball_audio.dart'; import 'package:pinball_ui/pinball_ui.dart'; +import 'package:share_repository/share_repository.dart'; class PinballGamePage extends StatelessWidget { const PinballGamePage({ @@ -26,12 +27,14 @@ class PinballGamePage extends StatelessWidget { final characterThemeBloc = context.read(); final audioPlayer = context.read(); final leaderboardRepository = context.read(); + final shareRepository = context.read(); final gameBloc = context.read(); final game = isDebugMode ? DebugPinballGame( characterThemeBloc: characterThemeBloc, audioPlayer: audioPlayer, leaderboardRepository: leaderboardRepository, + shareRepository: shareRepository, l10n: context.l10n, gameBloc: gameBloc, ) @@ -39,6 +42,7 @@ class PinballGamePage extends StatelessWidget { characterThemeBloc: characterThemeBloc, audioPlayer: audioPlayer, leaderboardRepository: leaderboardRepository, + shareRepository: shareRepository, l10n: context.l10n, gameBloc: gameBloc, ); diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 8573c585..a9b12291 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -189,7 +189,7 @@ "description": "Text shown on the mobile controls enter button" }, "initialsErrorTitle": "Uh-oh... well, that didn’t work", - "@enter": { + "@initialsErrorTitle": { "description": "Title shown when the initials submission fails" }, "initialsErrorMessage": "Please try a different combination of letters", @@ -200,4 +200,26 @@ "@leaderboardErrorMessage": { "description": "Text shown when the leaderboard had an error while loading" } + , + "letEveryone": "Let everyone know about I/O Pinball", + "@letEveryone": { + "description": "Text displayed on share screen for description" + }, + "bySharingYourScore": "by sharing your score to your preferred", + "@bySharingYourScore": { + "description": "Text displayed on share screen for description" + }, + "socialMediaAccount": "social media account!", + "@socialMediaAccount": { + "description": "Text displayed on share screen for description" + }, + "iGotScoreAtPinball": "I got {score} at the #IOPinball machine, can you beat my score? See you at #GoogleIO!", + "@iGotScoreAtPinball": { + "description": "Text to share score on Social Network", + "placeholders": { + "score": { + "type": "int" + } + } + } } diff --git a/lib/main.dart b/lib/main.dart index 158966c8..cb8c78da 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -6,10 +6,13 @@ import 'package:leaderboard_repository/leaderboard_repository.dart'; import 'package:pinball/app/app.dart'; import 'package:pinball/bootstrap.dart'; import 'package:pinball_audio/pinball_audio.dart'; +import 'package:share_repository/share_repository.dart'; void main() { bootstrap((firestore, firebaseAuth) async { final leaderboardRepository = LeaderboardRepository(firestore); + const shareRepository = + ShareRepository(appUrl: ShareRepository.pinballGameUrl); final authenticationRepository = AuthenticationRepository(firebaseAuth); final pinballAudioPlayer = PinballAudioPlayer(); unawaited( @@ -20,6 +23,7 @@ void main() { return App( authenticationRepository: authenticationRepository, leaderboardRepository: leaderboardRepository, + shareRepository: shareRepository, pinballAudioPlayer: pinballAudioPlayer, ); }); diff --git a/packages/pinball_components/assets/images/backbox/button/facebook.png b/packages/pinball_components/assets/images/backbox/button/facebook.png new file mode 100644 index 0000000000000000000000000000000000000000..f6d29ab28f01d7eaab310ecbad51fec8ce5b8910 GIT binary patch literal 3174 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 + const $AssetsImagesBackboxButtonGen(); + /// File path: assets/images/backbox/display_divider.png AssetGenImage get displayDivider => const AssetGenImage('assets/images/backbox/display_divider.png'); @@ -386,6 +389,18 @@ class $AssetsImagesAndroidSpaceshipGen { const AssetGenImage('assets/images/android/spaceship/saucer.png'); } +class $AssetsImagesBackboxButtonGen { + const $AssetsImagesBackboxButtonGen(); + + /// File path: assets/images/backbox/button/facebook.png + AssetGenImage get facebook => + const AssetGenImage('assets/images/backbox/button/facebook.png'); + + /// File path: assets/images/backbox/button/twitter.png + AssetGenImage get twitter => + const AssetGenImage('assets/images/backbox/button/twitter.png'); +} + class $AssetsImagesDashBumperGen { const $AssetsImagesDashBumperGen(); diff --git a/packages/pinball_components/pubspec.yaml b/packages/pinball_components/pubspec.yaml index c430d670..3301a0fc 100644 --- a/packages/pinball_components/pubspec.yaml +++ b/packages/pinball_components/pubspec.yaml @@ -90,6 +90,7 @@ flutter: - assets/images/multiplier/x6/ - assets/images/score/ - assets/images/backbox/ + - assets/images/backbox/button/ - assets/images/flapper/ - assets/images/skill_shot/ diff --git a/packages/share_repository/lib/src/share_repository.dart b/packages/share_repository/lib/src/share_repository.dart index e01f5746..16c29aee 100644 --- a/packages/share_repository/lib/src/share_repository.dart +++ b/packages/share_repository/lib/src/share_repository.dart @@ -17,6 +17,9 @@ class ShareRepository { /// Url to the Google IO Event. static const googleIOEvent = 'https://events.google.com/io/'; + /// Url to the Pinball game. + static const pinballGameUrl = 'https://ashehwkdkdjruejdnensjsjdne.web.app/#/'; + /// Returns a url to share the [value] on the given [platform]. /// /// The returned url can be opened using the [url_launcher](https://pub.dev/packages/url_launcher) package. diff --git a/test/app/view/app_test.dart b/test/app/view/app_test.dart index 4612641d..54814f36 100644 --- a/test/app/view/app_test.dart +++ b/test/app/view/app_test.dart @@ -5,6 +5,7 @@ import 'package:mocktail/mocktail.dart'; import 'package:pinball/app/app.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_audio/pinball_audio.dart'; +import 'package:share_repository/share_repository.dart'; class _MockAuthenticationRepository extends Mock implements AuthenticationRepository {} @@ -14,15 +15,19 @@ class _MockPinballAudioPlayer extends Mock implements PinballAudioPlayer {} class _MockLeaderboardRepository extends Mock implements LeaderboardRepository { } +class _MockShareRepository extends Mock implements ShareRepository {} + void main() { group('App', () { late AuthenticationRepository authenticationRepository; late LeaderboardRepository leaderboardRepository; + late ShareRepository shareRepository; late PinballAudioPlayer pinballAudioPlayer; setUp(() { authenticationRepository = _MockAuthenticationRepository(); leaderboardRepository = _MockLeaderboardRepository(); + shareRepository = _MockShareRepository(); pinballAudioPlayer = _MockPinballAudioPlayer(); when(pinballAudioPlayer.load).thenAnswer((_) => [Future.value()]); }); @@ -32,6 +37,7 @@ void main() { App( authenticationRepository: authenticationRepository, leaderboardRepository: leaderboardRepository, + shareRepository: shareRepository, pinballAudioPlayer: pinballAudioPlayer, ), ); diff --git a/test/game/components/backbox/backbox_test.dart b/test/game/components/backbox/backbox_test.dart index 65474cd2..b99b86ab 100644 --- a/test/game/components/backbox/backbox_test.dart +++ b/test/game/components/backbox/backbox_test.dart @@ -20,7 +20,10 @@ import 'package:pinball/l10n/l10n.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_theme/pinball_theme.dart' as theme; +import 'package:pinball_ui/pinball_ui.dart'; import 'package:platform_helper/platform_helper.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:share_repository/share_repository.dart'; class _TestGame extends Forge2DGame with HasKeyboardHandlerComponents, HasTappables { @@ -36,6 +39,8 @@ class _TestGame extends Forge2DGame character.leaderboardIcon.keyName, Assets.images.backbox.marquee.keyName, Assets.images.backbox.displayDivider.keyName, + Assets.images.backbox.button.facebook.keyName, + Assets.images.backbox.button.twitter.keyName, Assets.images.backbox.displayTitleDecoration.keyName, ]); } @@ -75,8 +80,14 @@ class _MockBackboxBloc extends Mock implements BackboxBloc {} class _MockLeaderboardRepository extends Mock implements LeaderboardRepository { } +class _MockShareRepository extends Mock implements ShareRepository {} + class _MockTapDownInfo extends Mock implements TapDownInfo {} +class _MockUrlLauncher extends Mock + with MockPlatformInterfaceMixin + implements UrlLauncherPlatform {} + class _MockAppLocalizations extends Mock implements AppLocalizations { @override String get score => ''; @@ -105,6 +116,15 @@ class _MockAppLocalizations extends Mock implements AppLocalizations { @override String get loading => ''; + @override + String get letEveryone => ''; + + @override + String get bySharingYourScore => ''; + + @override + String get socialMediaAccount => ''; + @override String get shareYourScore => ''; @@ -134,6 +154,9 @@ class _MockAppLocalizations extends Mock implements AppLocalizations { @override String get leaderboardErrorMessage => ''; + + @override + String iGotScoreAtPinball(int _) => ''; } void main() { @@ -143,6 +166,7 @@ void main() { late BackboxBloc bloc; late PlatformHelper platformHelper; + late UrlLauncherPlatform urlLauncher; setUp(() { bloc = _MockBackboxBloc(); @@ -161,6 +185,7 @@ void main() { (game) async { final backbox = Backbox.test( bloc: bloc, + shareRepository: _MockShareRepository(), platformHelper: platformHelper, ); await game.pump(backbox); @@ -178,6 +203,7 @@ void main() { await game.pump( Backbox.test( bloc: bloc, + shareRepository: _MockShareRepository(), platformHelper: platformHelper, ), ); @@ -199,6 +225,7 @@ void main() { leaderboardRepository: _MockLeaderboardRepository(), initialEntries: [LeaderboardEntryData.empty], ), + shareRepository: _MockShareRepository(), platformHelper: platformHelper, ); await game.pump(backbox); @@ -230,6 +257,7 @@ void main() { ); final backbox = Backbox.test( bloc: bloc, + shareRepository: _MockShareRepository(), platformHelper: platformHelper, ); await game.pump(backbox); @@ -258,6 +286,7 @@ void main() { ); final backbox = Backbox.test( bloc: bloc, + shareRepository: _MockShareRepository(), platformHelper: platformHelper, ); await game.pump(backbox); @@ -270,7 +299,8 @@ void main() { ); flameTester.test( - 'adds the mobile controls overlay when platform is mobile', + 'adds the mobile controls overlay ' + 'when platform is mobile at InitialsFormState', (game) async { final bloc = _MockBackboxBloc(); final platformHelper = _MockPlatformHelper(); @@ -286,6 +316,7 @@ void main() { when(() => platformHelper.isMobile).thenReturn(true); final backbox = Backbox.test( bloc: bloc, + shareRepository: _MockShareRepository(), platformHelper: platformHelper, ); await game.pump(backbox); @@ -297,6 +328,33 @@ void main() { }, ); + flameTester.test( + 'remove the mobile controls overlay ' + 'when InitialsSuccessState', + (game) async { + final bloc = _MockBackboxBloc(); + final platformHelper = _MockPlatformHelper(); + final state = InitialsSuccessState(score: 10); + whenListen( + bloc, + Stream.empty(), + initialState: state, + ); + when(() => platformHelper.isMobile).thenReturn(true); + final backbox = Backbox.test( + bloc: bloc, + shareRepository: _MockShareRepository(), + platformHelper: platformHelper, + ); + await game.pump(backbox); + + expect( + game.overlays.value, + isNot(contains(PinballGame.mobileControlsOverlay)), + ); + }, + ); + flameTester.test( 'adds InitialsSubmissionSuccessDisplay on InitialsSuccessState', (game) async { @@ -308,6 +366,7 @@ void main() { ); final backbox = Backbox.test( bloc: bloc, + shareRepository: _MockShareRepository(), platformHelper: platformHelper, ); await game.pump(backbox); @@ -330,6 +389,7 @@ void main() { ); final backbox = Backbox.test( bloc: bloc, + shareRepository: _MockShareRepository(), platformHelper: platformHelper, ); await game.pump(backbox); @@ -359,6 +419,7 @@ void main() { ); final backbox = Backbox.test( bloc: bloc, + shareRepository: _MockShareRepository(), platformHelper: platformHelper, ); await game.pump(backbox); @@ -373,6 +434,145 @@ void main() { }, ); + group('ShareDisplay', () { + setUp(() async { + urlLauncher = _MockUrlLauncher(); + UrlLauncherPlatform.instance = urlLauncher; + }); + + flameTester.test( + 'adds ShareDisplay on ShareState', + (game) async { + final state = ShareState(score: 100); + whenListen( + bloc, + const Stream.empty(), + initialState: state, + ); + final backbox = Backbox.test( + bloc: bloc, + shareRepository: _MockShareRepository(), + platformHelper: platformHelper, + ); + await game.pump(backbox); + + expect( + game.descendants().whereType().length, + equals(1), + ); + }, + ); + + flameTester.test( + 'opens Facebook link when sharing with Facebook', + (game) async { + when(() => urlLauncher.canLaunch(any())) + .thenAnswer((_) async => true); + when( + () => urlLauncher.launch( + any(), + useSafariVC: any(named: 'useSafariVC'), + useWebView: any(named: 'useWebView'), + enableJavaScript: any(named: 'enableJavaScript'), + enableDomStorage: any(named: 'enableDomStorage'), + universalLinksOnly: any(named: 'universalLinksOnly'), + headers: any(named: 'headers'), + ), + ).thenAnswer((_) async => true); + + final state = ShareState(score: 100); + whenListen( + bloc, + const Stream.empty(), + initialState: state, + ); + + final shareRepository = _MockShareRepository(); + const fakeUrl = 'http://fakeUrl'; + when( + () => shareRepository.shareText( + value: any(named: 'value'), + platform: SharePlatform.facebook, + ), + ).thenReturn(fakeUrl); + + final backbox = Backbox.test( + bloc: bloc, + shareRepository: shareRepository, + platformHelper: platformHelper, + ); + await game.pump(backbox); + + final facebookButton = + game.descendants().whereType().first; + facebookButton.onTapDown(_MockTapDownInfo()); + + await game.ready(); + + verify( + () => shareRepository.shareText( + value: any(named: 'value'), + platform: SharePlatform.facebook, + ), + ).called(1); + }, + ); + + flameTester.test( + 'opens Twitter link when sharing with Twitter', + (game) async { + final state = ShareState(score: 100); + whenListen( + bloc, + Stream.value(state), + initialState: state, + ); + + final shareRepository = _MockShareRepository(); + const fakeUrl = 'http://fakeUrl'; + when( + () => shareRepository.shareText( + value: any(named: 'value'), + platform: SharePlatform.twitter, + ), + ).thenReturn(fakeUrl); + when(() => urlLauncher.canLaunch(any())) + .thenAnswer((_) async => true); + when( + () => urlLauncher.launch( + any(), + useSafariVC: any(named: 'useSafariVC'), + useWebView: any(named: 'useWebView'), + enableJavaScript: any(named: 'enableJavaScript'), + enableDomStorage: any(named: 'enableDomStorage'), + universalLinksOnly: any(named: 'universalLinksOnly'), + headers: any(named: 'headers'), + ), + ).thenAnswer((_) async => true); + + final backbox = Backbox.test( + bloc: bloc, + shareRepository: shareRepository, + platformHelper: platformHelper, + ); + await game.pump(backbox); + + final facebookButton = + game.descendants().whereType().first; + facebookButton.onTapDown(_MockTapDownInfo()); + + await game.ready(); + + verify( + () => shareRepository.shareText( + value: any(named: 'value'), + platform: SharePlatform.twitter, + ), + ).called(1); + }, + ); + }); + flameTester.test( 'adds LeaderboardDisplay on LeaderboardSuccessState', (game) async { @@ -384,6 +584,7 @@ void main() { final backbox = Backbox.test( bloc: bloc, + shareRepository: _MockShareRepository(), platformHelper: platformHelper, ); await game.pump(backbox); @@ -406,6 +607,7 @@ void main() { final backbox = Backbox.test( bloc: bloc, + shareRepository: _MockShareRepository(), platformHelper: platformHelper, ); await game.pump(backbox); @@ -429,6 +631,7 @@ void main() { final backbox = Backbox.test( bloc: bloc, + shareRepository: _MockShareRepository(), platformHelper: platformHelper, ); await game.pump(backbox); @@ -469,6 +672,7 @@ void main() { final backbox = Backbox.test( bloc: bloc, + shareRepository: _MockShareRepository(), platformHelper: platformHelper, ); await game.pump(backbox); diff --git a/test/game/components/backbox/bloc/backbox_bloc_test.dart b/test/game/components/backbox/bloc/backbox_bloc_test.dart index aec46186..050307dc 100644 --- a/test/game/components/backbox/bloc/backbox_bloc_test.dart +++ b/test/game/components/backbox/bloc/backbox_bloc_test.dart @@ -137,6 +137,25 @@ void main() { ); }); + group('ShareScoreRequested', () { + blocTest( + 'emits ShareState', + setUp: () { + leaderboardRepository = _MockLeaderboardRepository(); + }, + build: () => BackboxBloc( + leaderboardRepository: leaderboardRepository, + initialEntries: emptyEntries, + ), + act: (bloc) => bloc.add( + ShareScoreRequested(score: 100), + ), + expect: () => [ + ShareState(score: 100), + ], + ); + }); + group('LeaderboardRequested', () { blocTest( 'adds [LoadingState, LeaderboardSuccessState] when request succeeds', diff --git a/test/game/components/backbox/bloc/backbox_state_test.dart b/test/game/components/backbox/bloc/backbox_state_test.dart index c41562b0..ba6d1c08 100644 --- a/test/game/components/backbox/bloc/backbox_state_test.dart +++ b/test/game/components/backbox/bloc/backbox_state_test.dart @@ -203,5 +203,23 @@ void main() { }); }); }); + + group('ShareState', () { + test('can be instantiated', () { + expect( + ShareState(score: 0), + isNotNull, + ); + }); + + test('supports value comparison', () { + expect( + ShareState(score: 0), + equals( + ShareState(score: 0), + ), + ); + }); + }); }); } diff --git a/test/game/components/backbox/displays/share_display_test.dart b/test/game/components/backbox/displays/share_display_test.dart new file mode 100644 index 00000000..1f882223 --- /dev/null +++ b/test/game/components/backbox/displays/share_display_test.dart @@ -0,0 +1,112 @@ +// ignore_for_file: cascade_invocations + +import 'package:flame/game.dart'; +import 'package:flame/input.dart'; +import 'package:flame_bloc/flame_bloc.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:pinball/game/bloc/game_bloc.dart'; +import 'package:pinball/game/components/backbox/displays/share_display.dart'; +import 'package:pinball/l10n/l10n.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; + +class _TestGame extends Forge2DGame with HasTappables { + @override + Future onLoad() async { + await super.onLoad(); + images.prefix = ''; + await images.loadAll( + [ + Assets.images.backbox.button.facebook.keyName, + Assets.images.backbox.button.twitter.keyName, + ], + ); + } + + Future pump(ShareDisplay component) { + return ensureAdd( + FlameBlocProvider.value( + value: GameBloc(), + children: [ + FlameProvider.value( + _MockAppLocalizations(), + children: [component], + ), + ], + ), + ); + } +} + +class _MockAppLocalizations extends Mock implements AppLocalizations { + @override + String get letEveryone => ''; + + @override + String get bySharingYourScore => ''; + + @override + String get socialMediaAccount => ''; +} + +class _MockTapDownInfo extends Mock implements TapDownInfo {} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + final flameTester = FlameTester(_TestGame.new); + + group('ShareDisplay', () { + flameTester.test( + 'loads correctly', + (game) async { + final component = ShareDisplay(); + await game.pump(component); + expect(game.descendants(), contains(component)); + }, + ); + + flameTester.test( + 'calls onShare when Facebook button is tapped', + (game) async { + var tapped = false; + + final tapDownInfo = _MockTapDownInfo(); + final component = ShareDisplay( + onShare: (_) => tapped = true, + ); + await game.pump(component); + + final facebookButton = + component.descendants().whereType().first; + + facebookButton.onTapDown(tapDownInfo); + + expect(tapped, isTrue); + }, + ); + + flameTester.test( + 'calls onShare when Twitter button is tapped', + (game) async { + var tapped = false; + + final tapDownInfo = _MockTapDownInfo(); + final component = ShareDisplay( + onShare: (_) => tapped = true, + ); + await game.pump(component); + + final twitterButton = + component.descendants().whereType().first; + + twitterButton.onTapDown(tapDownInfo); + + expect(tapped, isTrue); + }, + ); + }); +} diff --git a/test/game/components/game_bloc_status_listener_test.dart b/test/game/components/game_bloc_status_listener_test.dart index 3151e70b..cc1729b8 100644 --- a/test/game/components/game_bloc_status_listener_test.dart +++ b/test/game/components/game_bloc_status_listener_test.dart @@ -14,6 +14,7 @@ import 'package:pinball_audio/pinball_audio.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_theme/pinball_theme.dart' as theme; +import 'package:share_repository/share_repository.dart'; class _TestGame extends Forge2DGame { @override @@ -65,6 +66,8 @@ class _MockPinballAudioPlayer extends Mock implements PinballAudioPlayer {} class _MockLeaderboardRepository extends Mock implements LeaderboardRepository { } +class _MockShareRepository extends Mock implements ShareRepository {} + class _MockAppLocalizations extends Mock implements AppLocalizations { @override String get score => ''; @@ -149,9 +152,11 @@ void main() { 'changes the backbox display', (game) async { final component = GameBlocStatusListener(); - final repository = _MockLeaderboardRepository(); + final leaderboardRepository = _MockLeaderboardRepository(); + final shareRepository = _MockShareRepository(); final backbox = Backbox( - leaderboardRepository: repository, + leaderboardRepository: leaderboardRepository, + shareRepository: shareRepository, entries: const [], ); @@ -165,9 +170,11 @@ void main() { 'removes FlipperKeyControllingBehavior from Flipper', (game) async { final component = GameBlocStatusListener(); - final repository = _MockLeaderboardRepository(); + final leaderboardRepository = _MockLeaderboardRepository(); + final shareRepository = _MockShareRepository(); final backbox = Backbox( - leaderboardRepository: repository, + leaderboardRepository: leaderboardRepository, + shareRepository: shareRepository, entries: const [], ); final flipper = Flipper.test(side: BoardSide.left); @@ -193,9 +200,11 @@ void main() { (game) async { final audioPlayer = _MockPinballAudioPlayer(); final component = GameBlocStatusListener(); - final repository = _MockLeaderboardRepository(); + final leaderboardRepository = _MockLeaderboardRepository(); + final shareRepository = _MockShareRepository(); final backbox = Backbox( - leaderboardRepository: repository, + leaderboardRepository: leaderboardRepository, + shareRepository: shareRepository, entries: const [], ); await game.pump( @@ -245,9 +254,11 @@ void main() { 'adds key controlling behavior to Flippers when the game is started', (game) async { final component = GameBlocStatusListener(); - final repository = _MockLeaderboardRepository(); + final leaderboardRepository = _MockLeaderboardRepository(); + final shareRepository = _MockShareRepository(); final backbox = Backbox( - leaderboardRepository: repository, + leaderboardRepository: leaderboardRepository, + shareRepository: shareRepository, entries: const [], ); final flipper = Flipper.test(side: BoardSide.left); diff --git a/test/game/pinball_game_test.dart b/test/game/pinball_game_test.dart index d64333cc..19760f64 100644 --- a/test/game/pinball_game_test.dart +++ b/test/game/pinball_game_test.dart @@ -16,12 +16,14 @@ import 'package:pinball/game/game.dart'; import 'package:pinball/select_character/select_character.dart'; import 'package:pinball_audio/src/pinball_audio.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:share_repository/share_repository.dart'; class _TestPinballGame extends PinballGame { _TestPinballGame() : super( characterThemeBloc: CharacterThemeCubit(), leaderboardRepository: _MockLeaderboardRepository(), + shareRepository: _MockShareRepository(), gameBloc: GameBloc(), l10n: _MockAppLocalizations(), audioPlayer: _MockPinballAudioPlayer(), @@ -41,6 +43,7 @@ class _TestDebugPinballGame extends DebugPinballGame { : super( characterThemeBloc: CharacterThemeCubit(), leaderboardRepository: _MockLeaderboardRepository(), + shareRepository: _MockShareRepository(), gameBloc: GameBloc(), l10n: _MockAppLocalizations(), audioPlayer: _MockPinballAudioPlayer(), @@ -81,6 +84,8 @@ class _MockDragEndInfo extends Mock implements DragEndInfo {} class _MockLeaderboardRepository extends Mock implements LeaderboardRepository { } +class _MockShareRepository extends Mock implements ShareRepository {} + class _MockPinballAudioPlayer extends Mock implements PinballAudioPlayer {} void main() { diff --git a/test/game/view/pinball_game_page_test.dart b/test/game/view/pinball_game_page_test.dart index b2cde26e..74ef4fbb 100644 --- a/test/game/view/pinball_game_page_test.dart +++ b/test/game/view/pinball_game_page_test.dart @@ -16,6 +16,7 @@ import 'package:pinball/more_information/more_information.dart'; import 'package:pinball/select_character/select_character.dart'; import 'package:pinball/start_game/start_game.dart'; import 'package:pinball_audio/pinball_audio.dart'; +import 'package:share_repository/share_repository.dart'; import '../../helpers/helpers.dart'; @@ -24,6 +25,7 @@ class _TestPinballGame extends PinballGame { : super( characterThemeBloc: CharacterThemeCubit(), leaderboardRepository: _MockLeaderboardRepository(), + shareRepository: _MockShareRepository(), gameBloc: GameBloc(), l10n: _MockAppLocalizations(), audioPlayer: _MockPinballAudioPlayer(), @@ -60,6 +62,8 @@ class _MockPinballAudioPlayer extends Mock implements PinballAudioPlayer {} class _MockLeaderboardRepository extends Mock implements LeaderboardRepository { } +class _MockShareRepository extends Mock implements ShareRepository {} + void main() { final game = _TestPinballGame(); diff --git a/test/helpers/pump_app.dart b/test/helpers/pump_app.dart index 8be03f03..df75efae 100644 --- a/test/helpers/pump_app.dart +++ b/test/helpers/pump_app.dart @@ -12,12 +12,15 @@ import 'package:pinball/select_character/select_character.dart'; import 'package:pinball/start_game/start_game.dart'; import 'package:pinball_audio/pinball_audio.dart'; import 'package:pinball_ui/pinball_ui.dart'; +import 'package:share_repository/share_repository.dart'; class _MockAssetsManagerCubit extends Mock implements AssetsManagerCubit {} class _MockLeaderboardRepository extends Mock implements LeaderboardRepository { } +class _MockShareRepository extends Mock implements ShareRepository {} + class _MockCharacterThemeCubit extends Mock implements CharacterThemeCubit {} class _MockGameBloc extends Mock implements GameBloc {} @@ -55,6 +58,7 @@ extension PumpApp on WidgetTester { AssetsManagerCubit? assetsManagerCubit, CharacterThemeCubit? characterThemeCubit, LeaderboardRepository? leaderboardRepository, + ShareRepository? shareRepository, PinballAudioPlayer? pinballAudioPlayer, }) { return runAsync(() { @@ -64,6 +68,9 @@ extension PumpApp on WidgetTester { RepositoryProvider.value( value: leaderboardRepository ?? _MockLeaderboardRepository(), ), + RepositoryProvider.value( + value: shareRepository ?? _MockShareRepository(), + ), RepositoryProvider.value( value: pinballAudioPlayer ?? _buildDefaultPinballAudioPlayer(), ),