From ca3c523cea14f832a9e25d85d17355271056f9f4 Mon Sep 17 00:00:00 2001 From: Jaime <52668514+jsgalarraga@users.noreply.github.com> Date: Thu, 2 Nov 2023 19:44:08 +0100 Subject: [PATCH] feat: don't allow mobile web browser (#60) --- lib/game_intro/game_intro.dart | 1 + lib/game_intro/view/game_intro_page.dart | 216 +++++++++++++----- .../widgets/game_intro_buttons.dart | 58 +++++ lib/game_intro/widgets/widgets.dart | 1 + lib/l10n/arb/app_en.arb | 16 ++ macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.yaml | 1 + 7 files changed, 234 insertions(+), 61 deletions(-) create mode 100644 lib/game_intro/widgets/game_intro_buttons.dart create mode 100644 lib/game_intro/widgets/widgets.dart diff --git a/lib/game_intro/game_intro.dart b/lib/game_intro/game_intro.dart index 00ffcf94..3b46d13d 100644 --- a/lib/game_intro/game_intro.dart +++ b/lib/game_intro/game_intro.dart @@ -1 +1,2 @@ export 'view/view.dart'; +export 'widgets/widgets.dart'; diff --git a/lib/game_intro/view/game_intro_page.dart b/lib/game_intro/view/game_intro_page.dart index fa8b92e6..e1d52e96 100644 --- a/lib/game_intro/view/game_intro_page.dart +++ b/lib/game_intro/view/game_intro_page.dart @@ -1,18 +1,18 @@ import 'package:app_ui/app_ui.dart'; import 'package:dash_run/assets/assets.dart'; import 'package:dash_run/game/game.dart'; +import 'package:dash_run/game_intro/game_intro.dart'; import 'package:dash_run/l10n/l10n.dart'; -import 'package:dash_run/settings/settings.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:url_launcher/url_launcher_string.dart'; class GameIntroPage extends StatelessWidget { const GameIntroPage({super.key}); @override Widget build(BuildContext context) { - final l10n = context.l10n; - final theme = Theme.of(context); return Scaffold( body: Container( decoration: BoxDecoration( @@ -23,76 +23,170 @@ class GameIntroPage extends StatelessWidget { fit: BoxFit.cover, ), ), - child: Center( - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 390), - child: Column( - children: [ - const Spacer(), - Image.asset( - Assets.logo, - width: context.isSmall ? 282 : 380, - ), - const Spacer(flex: 4), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 32), - child: Text( - l10n.gameIntroPageHeadline, - textAlign: TextAlign.center, - style: theme.textTheme.titleMedium?.copyWith( - color: Colors.white, - fontWeight: FontWeight.w500, - ), - ), - ), - const SizedBox(height: 32), - GameElevatedButton( - label: l10n.gameIntroPagePlayButtonText, - onPressed: () => Navigator.of(context).push(GameView.route()), + child: isMobileWeb + ? const _MobileWebNotAvailableIntroPage() + : const _IntroPage(), + ), + ); + } + + bool get isMobileWeb => + kIsWeb && + (defaultTargetPlatform == TargetPlatform.android || + defaultTargetPlatform == TargetPlatform.iOS); +} + +class _IntroPage extends StatelessWidget { + const _IntroPage(); + + @override + Widget build(BuildContext context) { + final l10n = context.l10n; + final theme = Theme.of(context); + return Center( + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 390), + child: Column( + children: [ + const Spacer(), + Image.asset( + Assets.logo, + width: context.isSmall ? 282 : 380, + ), + const Spacer(flex: 4), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 32), + child: Text( + l10n.gameIntroPageHeadline, + textAlign: TextAlign.center, + style: theme.textTheme.titleMedium?.copyWith( + color: Colors.white, + fontWeight: FontWeight.w500, ), - const Spacer(), - const _Actions(), - const SizedBox(height: 32), + ), + ), + const SizedBox(height: 32), + GameElevatedButton( + label: l10n.gameIntroPagePlayButtonText, + onPressed: () => Navigator.of(context).push(GameView.route()), + ), + const Spacer(), + const Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + AudioButton(), + LeaderboardButton(), + InfoButton(), + HowToPlayButton(), ], ), - ), + const SizedBox(height: 32), + ], ), ), ); } } -class _Actions extends StatelessWidget { - const _Actions(); +class _MobileWebNotAvailableIntroPage extends StatelessWidget { + const _MobileWebNotAvailableIntroPage(); @override Widget build(BuildContext context) { - final settingsController = context.watch(); - return Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - ValueListenableBuilder( - valueListenable: settingsController.soundsOn, - builder: (context, soundsOn, child) => GameIconButton( - icon: soundsOn ? Icons.volume_up : Icons.volume_off, - onPressed: soundsOn - ? settingsController.toggleMuted - : settingsController.toggleMusicOn, - ), - ), - GameIconButton( - icon: Icons.leaderboard, - onPressed: () {}, - ), - GameIconButton( - onPressed: () {}, - icon: Icons.info, - ), - GameIconButton( - onPressed: () {}, - icon: Icons.help, + final l10n = context.l10n; + final theme = Theme.of(context); + return Center( + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 390), + child: Column( + children: [ + const Spacer(), + Image.asset(Assets.logo, width: 282), + const Spacer(flex: 4), + Container( + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: Color(0xFF0046ab), + ), + padding: const EdgeInsets.all(12), + child: const Icon( + Icons.mobile_off, + size: 24, + color: Colors.white, + ), + ), + const SizedBox(height: 24), + Text( + l10n.mobileModeUnavailable, + textAlign: TextAlign.center, + style: theme.textTheme.headlineSmall?.copyWith( + color: Colors.white, + fontWeight: FontWeight.w500, + ), + ), + const SizedBox(height: 24), + Text( + l10n.mobileModeUnavailableDescription, + textAlign: TextAlign.center, + style: theme.textTheme.titleMedium?.copyWith( + color: Colors.white, + fontWeight: FontWeight.w500, + ), + ), + const Spacer(), + const _BottomBar(), + const SizedBox(height: 32), + ], ), - ], + ), + ); + } +} + +class _BottomBar extends StatelessWidget { + const _BottomBar(); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final l10n = context.l10n; + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 32), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const AudioButton(), + Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + l10n.superDash, + style: theme.textTheme.titleMedium?.copyWith( + color: Colors.white, + fontWeight: FontWeight.w500, + ), + ), + const SizedBox(height: 4), + RichText( + text: TextSpan( + text: l10n.howItsMade, + style: theme.textTheme.titleMedium?.copyWith( + color: Colors.white, + fontWeight: FontWeight.w400, + decoration: TextDecoration.underline, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + // TODO(all): confirm url once is in google's org + launchUrlString('https://github.com/flutter/superdash'); + }, + ), + ), + ], + ), + const InfoButton(), + ], + ), ); } } diff --git a/lib/game_intro/widgets/game_intro_buttons.dart b/lib/game_intro/widgets/game_intro_buttons.dart new file mode 100644 index 00000000..a4ab8249 --- /dev/null +++ b/lib/game_intro/widgets/game_intro_buttons.dart @@ -0,0 +1,58 @@ +import 'package:app_ui/app_ui.dart'; +import 'package:dash_run/settings/settings_controller.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class AudioButton extends StatelessWidget { + const AudioButton({super.key}); + + @override + Widget build(BuildContext context) { + final settingsController = context.watch(); + return ValueListenableBuilder( + valueListenable: settingsController.soundsOn, + builder: (context, soundsOn, child) => GameIconButton( + icon: soundsOn ? Icons.volume_up : Icons.volume_off, + onPressed: soundsOn + ? settingsController.toggleMuted + : settingsController.toggleMusicOn, + ), + ); + } +} + +class LeaderboardButton extends StatelessWidget { + const LeaderboardButton({super.key}); + + @override + Widget build(BuildContext context) { + return GameIconButton( + icon: Icons.leaderboard, + onPressed: () {}, + ); + } +} + +class InfoButton extends StatelessWidget { + const InfoButton({super.key}); + + @override + Widget build(BuildContext context) { + return GameIconButton( + icon: Icons.info, + onPressed: () {}, + ); + } +} + +class HowToPlayButton extends StatelessWidget { + const HowToPlayButton({super.key}); + + @override + Widget build(BuildContext context) { + return GameIconButton( + icon: Icons.help, + onPressed: () {}, + ); + } +} diff --git a/lib/game_intro/widgets/widgets.dart b/lib/game_intro/widgets/widgets.dart new file mode 100644 index 00000000..28c30afb --- /dev/null +++ b/lib/game_intro/widgets/widgets.dart @@ -0,0 +1 @@ +export 'game_intro_buttons.dart'; diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 452653bb..0ece98a9 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -7,5 +7,21 @@ "gameIntroPagePlayButtonText": "Play", "@gameIntroPagePlayButtonText": { "description": "Text shown in the Play button of the Game Info Page" + }, + "mobileModeUnavailable": "Mobile mode unavailable", + "@mobileModeUnavailable": { + "description": "Title shown in the Game Info Page for mobile devices joining from a browser" + }, + "mobileModeUnavailableDescription": "Super Dash mobile is coming soon.\nOpen this link on a desktop web browser\nto play today.", + "@mobileModeUnavailableDescription": { + "description": "Description shown in the Game Info Page for mobile devices joining from a browser" + }, + "superDash": "Super Dash", + "@superDash": { + "description": "Game name" + }, + "howItsMade": "How it's made", + "@howItsMade": { + "description": "Text shown in the game info page" } } \ No newline at end of file diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 949e7be9..98f1b844 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -9,10 +9,12 @@ import audioplayers_darwin import file_selector_macos import path_provider_foundation import shared_preferences_foundation +import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin")) FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/pubspec.yaml b/pubspec.yaml index 0fd56d9f..0f4314ca 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -29,6 +29,7 @@ dependencies: pathxp: ^0.4.0 provider: ^6.0.5 shared_preferences: ^2.2.2 + url_launcher: ^6.2.1 dev_dependencies: bloc_test: ^9.1.2