Skip to content

Commit

Permalink
Merge branch 'deeepLink' of https://github.com/tom-anders/lichess-mobile
Browse files Browse the repository at this point in the history
 into theme_fixes
  • Loading branch information
veloce committed Feb 9, 2025
2 parents 8e563b7 + 935adf2 commit fad63ca
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 68 deletions.
19 changes: 19 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,25 @@
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>

<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data android:scheme="https" android:host="lichess.org" />

<data android:pathPattern="/training/....." />
<data android:pathPattern="/study/........" />

<data android:pathPattern="/storm" />
<data android:pathPattern="/streak" />

<!-- Either game or challenge -->
<data android:pathPattern="/........" />
<data android:pathPattern="/......../black" />
<data android:pathPattern="/......../white" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
Expand Down
14 changes: 12 additions & 2 deletions lib/src/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:lichess_mobile/l10n/l10n.dart';
import 'package:lichess_mobile/src/app_links.dart';
import 'package:lichess_mobile/src/constants.dart';
import 'package:lichess_mobile/src/model/account/account_repository.dart';
import 'package:lichess_mobile/src/model/challenge/challenge_service.dart';
Expand All @@ -16,8 +17,8 @@ import 'package:lichess_mobile/src/network/http.dart';
import 'package:lichess_mobile/src/network/socket.dart';
import 'package:lichess_mobile/src/styles/styles.dart';
import 'package:lichess_mobile/src/theme.dart';
import 'package:lichess_mobile/src/utils/navigation.dart';
import 'package:lichess_mobile/src/utils/screen.dart';
import 'package:lichess_mobile/src/widgets/background.dart';

/// Application initialization and main entry point.
class AppInitializationScreen extends ConsumerWidget {
Expand Down Expand Up @@ -151,7 +152,16 @@ class _AppState extends ConsumerState<Application> {
child: Material(color: Colors.transparent, child: child),
)
: null,
home: const FullScreenBackground(child: BottomNavScaffold()),
onGenerateRoute:
(settings) =>
settings.name != null ? resolveAppLinkUri(context, Uri.parse(settings.name!)) : null,
onGenerateInitialRoutes: (initialRoute) {
final homeRoute = createPlatformRoute(context, screen: const BottomNavScaffold());
return <Route<dynamic>?>[
homeRoute,
resolveAppLinkUri(context, Uri.parse(initialRoute)),
].nonNulls.toList(growable: false);
},
navigatorObservers: [rootNavPageRouteObserver],
);
}
Expand Down
51 changes: 51 additions & 0 deletions lib/src/app_links.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import 'package:dartchess/dartchess.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:flutter/widgets.dart';
import 'package:lichess_mobile/src/model/common/id.dart';
import 'package:lichess_mobile/src/model/puzzle/puzzle_angle.dart';
import 'package:lichess_mobile/src/utils/navigation.dart';
import 'package:lichess_mobile/src/view/game/archived_game_screen.dart';
import 'package:lichess_mobile/src/view/puzzle/puzzle_screen.dart';
import 'package:lichess_mobile/src/view/puzzle/storm_screen.dart';
import 'package:lichess_mobile/src/view/puzzle/streak_screen.dart';
import 'package:lichess_mobile/src/view/study/study_screen.dart';

Route<dynamic>? resolveAppLinkUri(BuildContext context, Uri appLinkUri) {
if (appLinkUri.pathSegments.length < 2 || appLinkUri.pathSegments[1].isEmpty) {
return null;
}

final id = appLinkUri.pathSegments[1];

switch (appLinkUri.pathSegments[0]) {
case 'streak':
return createPlatformRoute(context, screen: const StreakScreen());
case 'storm':
return createPlatformRoute(context, screen: const StormScreen());
case 'study':
return createPlatformRoute(context, screen: StudyScreen(id: StudyId(id)));
case 'training':
return createPlatformRoute(
context,
screen: PuzzleScreen(angle: PuzzleAngle.fromKey('mix'), puzzleId: PuzzleId(id)),
);
case _:
{
final gameId = GameId(appLinkUri.pathSegments[0]);
final orientation = appLinkUri.pathSegments.getOrNull(2);
if (gameId.isValid) {
return createPlatformRoute(
context,
screen: ArchivedGameScreen(
gameId: gameId,
orientation: orientation == 'black' ? Side.black : Side.white,
),
);
} else {
// TODO if it's not a game, it's a challenge.
// So we should show a accept/decline screen here.
return null;
}
}
}
}
66 changes: 36 additions & 30 deletions lib/src/navigation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:lichess_mobile/src/view/puzzle/puzzle_tab_screen.dart';
import 'package:lichess_mobile/src/view/settings/settings_tab_screen.dart';
import 'package:lichess_mobile/src/view/tools/tools_tab_screen.dart';
import 'package:lichess_mobile/src/view/watch/watch_tab_screen.dart';
import 'package:lichess_mobile/src/widgets/background.dart';
import 'package:lichess_mobile/src/widgets/feedback.dart';

enum BottomTab {
Expand Down Expand Up @@ -155,40 +156,45 @@ class BottomNavScaffold extends ConsumerWidget {

switch (Theme.of(context).platform) {
case TargetPlatform.android:
return Scaffold(
body: _TabSwitchingView(currentTab: currentTab, tabBuilder: _androidTabBuilder),
bottomNavigationBar: Consumer(
builder: (context, ref, _) {
final isOnline = ref.watch(connectivityChangesProvider).valueOrNull?.isOnline ?? true;
return NavigationBar(
selectedIndex: currentTab.index,
destinations: [
for (final tab in BottomTab.values)
NavigationDestination(
icon: Icon(tab == currentTab ? tab.activeIcon : tab.icon),
label: tab.label(context.l10n),
),
],
onDestinationSelected: (i) => _onItemTapped(ref, i, isOnline: isOnline),
);
},
return FullScreenBackground(
child: Scaffold(
body: _TabSwitchingView(currentTab: currentTab, tabBuilder: _androidTabBuilder),
bottomNavigationBar: Consumer(
builder: (context, ref, _) {
final isOnline =
ref.watch(connectivityChangesProvider).valueOrNull?.isOnline ?? true;
return NavigationBar(
selectedIndex: currentTab.index,
destinations: [
for (final tab in BottomTab.values)
NavigationDestination(
icon: Icon(tab == currentTab ? tab.activeIcon : tab.icon),
label: tab.label(context.l10n),
),
],
onDestinationSelected: (i) => _onItemTapped(ref, i, isOnline: isOnline),
);
},
),
),
);
case TargetPlatform.iOS:
final isOnline = ref.watch(connectivityChangesProvider).valueOrNull?.isOnline ?? true;
return CupertinoTabScaffold(
tabBuilder: _iOSTabBuilder,
controller: _cupertinoTabController,
tabBar: CupertinoTabBar(
currentIndex: currentTab.index,
items: [
for (final tab in BottomTab.values)
BottomNavigationBarItem(
icon: Icon(tab == currentTab ? tab.activeIcon : tab.icon),
label: tab.label(context.l10n),
),
],
onTap: (i) => _onItemTapped(ref, i, isOnline: isOnline),
return FullScreenBackground(
child: CupertinoTabScaffold(
tabBuilder: _iOSTabBuilder,
controller: _cupertinoTabController,
tabBar: CupertinoTabBar(
currentIndex: currentTab.index,
items: [
for (final tab in BottomTab.values)
BottomNavigationBarItem(
icon: Icon(tab == currentTab ? tab.activeIcon : tab.icon),
label: tab.label(context.l10n),
),
],
onTap: (i) => _onItemTapped(ref, i, isOnline: isOnline),
),
),
);
default:
Expand Down
79 changes: 43 additions & 36 deletions lib/src/utils/navigation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,7 @@ class CupertinoScreenRoute<T extends Object?> extends CupertinoPageRoute<T>

/// Push a new route using Navigator.
///
/// Either [builder] or [screen] must be provided.
///
/// If [builder] if provided, it will return a [MaterialPageRoute] on Android and
/// a [CupertinoPageRoute] on iOS.
///
/// If [screen] is provided, it will return a [MaterialScreenRoute] on Android and
/// a [CupertinoScreenRoute] on iOS.
/// {@macro lichess.navigation.builder_or_screen}
Future<void> pushPlatformRoute(
BuildContext context, {
Widget? screen,
Expand All @@ -63,29 +57,19 @@ Future<void> pushPlatformRoute(
assert(screen != null || builder != null, 'Either screen or builder must be provided.');

return Navigator.of(context, rootNavigator: rootNavigator).push<void>(
Theme.of(context).platform == TargetPlatform.iOS
? builder != null
? CupertinoPageRoute(builder: builder, title: title, fullscreenDialog: fullscreenDialog)
: CupertinoScreenRoute(
screen: screen!,
title: title,
fullscreenDialog: fullscreenDialog,
)
: builder != null
? MaterialPageRoute(builder: builder, fullscreenDialog: fullscreenDialog)
: MaterialScreenRoute(screen: screen!, fullscreenDialog: fullscreenDialog),
createPlatformRoute(
context,
builder: builder,
screen: screen,
fullscreenDialog: fullscreenDialog,
title: title,
),
);
}

/// Push a new route using Navigator and replace the current route.
///
/// Either [builder] or [screen] must be provided.
///
/// If [builder] if provided, it will return a [MaterialPageRoute] on Android and
/// a [CupertinoPageRoute] on iOS.
///
/// If [screen] is provided, it will return a [MaterialScreenRoute] on Android and
/// a [CupertinoScreenRoute] on iOS.
/// {@macro lichess.navigation.builder_or_screen}
Future<void> pushReplacementPlatformRoute(
BuildContext context, {
WidgetBuilder? builder,
Expand All @@ -95,16 +79,39 @@ Future<void> pushReplacementPlatformRoute(
String? title,
}) {
return Navigator.of(context, rootNavigator: rootNavigator).pushReplacement<void, void>(
Theme.of(context).platform == TargetPlatform.iOS
? builder != null
? CupertinoPageRoute(builder: builder, title: title, fullscreenDialog: fullscreenDialog)
: CupertinoScreenRoute(
screen: screen!,
title: title,
fullscreenDialog: fullscreenDialog,
)
: builder != null
? MaterialPageRoute(builder: builder, fullscreenDialog: fullscreenDialog)
: MaterialScreenRoute(screen: screen!, fullscreenDialog: fullscreenDialog),
createPlatformRoute(
context,
builder: builder,
screen: screen,
fullscreenDialog: fullscreenDialog,
title: title,
),
);
}

/// Create a route from either [builder] or [screen].
///
/// {@template lichess.navigation.builder_or_screen}
/// If [builder] is provided, it will return a [MaterialPageRoute] on Android and
/// a [CupertinoPageRoute] on iOS.
///
/// If [screen] is provided, it will return a [MaterialScreenRoute] on Android and
/// a [CupertinoScreenRoute] on iOS.
/// {@endtemplate}
Route<dynamic> createPlatformRoute(
BuildContext context, {
Widget? screen,
WidgetBuilder? builder,
bool fullscreenDialog = false,
String? title,
}) {
assert(screen != null || builder != null, 'Either screen or builder must be provided.');

return Theme.of(context).platform == TargetPlatform.iOS
? builder != null
? CupertinoPageRoute(builder: builder, title: title, fullscreenDialog: fullscreenDialog)
: CupertinoScreenRoute(screen: screen!, title: title, fullscreenDialog: fullscreenDialog)
: builder != null
? MaterialPageRoute(builder: builder, fullscreenDialog: fullscreenDialog)
: MaterialScreenRoute(screen: screen!, fullscreenDialog: fullscreenDialog);
}

0 comments on commit fad63ca

Please sign in to comment.