diff --git a/ios/SolianShareExtension/ShareViewController.swift b/ios/SolianShareExtension/ShareViewController.swift index f8a9b1c..c5ed850 100644 --- a/ios/SolianShareExtension/ShareViewController.swift +++ b/ios/SolianShareExtension/ShareViewController.swift @@ -9,6 +9,6 @@ import receive_sharing_intent class ShareViewController: RSIShareViewController { override func shouldAutoRedirect() -> Bool { - return false + return true } } diff --git a/lib/main.dart b/lib/main.dart index 7adc46c..99c445f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -18,6 +18,7 @@ import 'package:bitsdojo_window/bitsdojo_window.dart'; import 'package:island/pods/userinfo.dart'; import 'package:island/pods/websocket.dart'; import 'package:island/route.dart'; +import 'package:island/screens/tabs.dart'; import 'package:island/services/notify.dart'; import 'package:island/services/timezone.dart'; import 'package:island/widgets/alert.dart'; @@ -186,7 +187,16 @@ class IslandApp extends HookConsumerWidget { theme: theme?.light, darkTheme: theme?.dark, themeMode: ThemeMode.system, - routerConfig: appRouter.config(), + routerConfig: appRouter.config( + navigatorObservers: + () => [ + TabNavigationObserver( + onChange: (route) { + ref.read(currentRouteProvider.notifier).state = route; + }, + ), + ], + ), supportedLocales: context.supportedLocales, localizationsDelegates: [ ...context.localizationDelegates, diff --git a/lib/route.dart b/lib/route.dart index 5d75879..e6d51e1 100644 --- a/lib/route.dart +++ b/lib/route.dart @@ -12,10 +12,13 @@ class AppRouter extends RootStackRouter { ]; List get _appRoutes => [ + // Standalone routes without bottom navigation AutoRoute(page: PostComposeRoute.page, path: 'posts/compose'), AutoRoute(page: PostEditRoute.page, path: 'posts/:id/edit'), AutoRoute(page: CallRoute.page, path: 'chat/:id/call'), AutoRoute(page: EventCalanderRoute.page, path: 'account/:name/calendar'), + + // Main tabs with bottom navigation and shell routes for desktop layout AutoRoute( page: TabsRoute.page, path: '', diff --git a/lib/screens/tabs.dart b/lib/screens/tabs.dart index 932bb7c..ff74b5e 100644 --- a/lib/screens/tabs.dart +++ b/lib/screens/tabs.dart @@ -1,3 +1,4 @@ +import 'dart:developer'; import 'dart:ui'; import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -6,8 +7,40 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:island/route.gr.dart'; import 'package:island/screens/notification.dart'; import 'package:island/services/responsive.dart'; +import 'package:island/widgets/navigation/conditional_bottom_nav.dart'; import 'package:material_symbols_icons/symbols.dart'; +final currentRouteProvider = StateProvider((ref) => null); + +class TabNavigationObserver extends AutoRouterObserver { + Function(String?) onChange; + TabNavigationObserver({required this.onChange}); + + @override + void didPush(Route route, Route? previousRoute) { + log('pushed ${previousRoute?.settings.name} -> ${route.settings.name}'); + if (route is DialogRoute) return; + final name = route.settings.name; + if (name == null) return; + if (name.contains('Shell')) return; + Future(() { + onChange(name); + }); + } + + @override + void didPop(Route route, Route? previousRoute) { + log('popped ${route.settings.name} -> ${previousRoute?.settings.name}'); + if (previousRoute is DialogRoute) return; + final name = previousRoute?.settings.name; + if (name == null) return; + if (name.contains('Shell')) return; + Future(() { + onChange(name); + }); + } +} + @RoutePage() class TabsScreen extends HookConsumerWidget { const TabsScreen({super.key}); @@ -41,10 +74,10 @@ class TabsScreen extends HookConsumerWidget { ]; final routes = [ - ExploreRoute(), - ChatListRoute(), + ExploreShellRoute(), + ChatShellRoute(), RealmListRoute(), - AccountRoute(), + AccountShellRoute(), ]; return AutoTabsRouter.tabBar( @@ -83,31 +116,33 @@ class TabsScreen extends HookConsumerWidget { left: 0, right: 0, bottom: 0, - child: ClipRRect( - child: BackdropFilter( - filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), - child: Container( - decoration: BoxDecoration( - color: Theme.of( - context, - ).colorScheme.surface.withOpacity(0.8), - ), - child: MediaQuery.removePadding( - context: context, - removeTop: true, - child: NavigationBar( - backgroundColor: Colors.transparent, - shadowColor: Colors.transparent, - overlayColor: WidgetStatePropertyAll( - Colors.transparent, + child: ConditionalBottomNav( + child: ClipRRect( + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), + child: Container( + decoration: BoxDecoration( + color: Theme.of( + context, + ).colorScheme.surface.withOpacity(0.8), + ), + child: MediaQuery.removePadding( + context: context, + removeTop: true, + child: NavigationBar( + backgroundColor: Colors.transparent, + shadowColor: Colors.transparent, + overlayColor: const WidgetStatePropertyAll( + Colors.transparent, + ), + surfaceTintColor: Colors.transparent, + height: 56, + labelBehavior: + NavigationDestinationLabelBehavior.alwaysHide, + selectedIndex: tabsRouter.activeIndex, + onDestinationSelected: tabsRouter.setActiveIndex, + destinations: destinations, ), - surfaceTintColor: Colors.transparent, - height: 56, - labelBehavior: - NavigationDestinationLabelBehavior.alwaysHide, - selectedIndex: tabsRouter.activeIndex, - onDestinationSelected: tabsRouter.setActiveIndex, - destinations: destinations, ), ), ), diff --git a/lib/services/sharing_intent.dart b/lib/services/sharing_intent.dart index 45d87de..b77bac6 100644 --- a/lib/services/sharing_intent.dart +++ b/lib/services/sharing_intent.dart @@ -1,4 +1,6 @@ import 'dart:async'; +import 'dart:io'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:receive_sharing_intent/receive_sharing_intent.dart'; import 'package:island/widgets/share/share_sheet.dart'; @@ -15,6 +17,7 @@ class SharingIntentService { /// Initialize the sharing intent service void initialize(BuildContext context) { + if (kIsWeb || !(Platform.isIOS || Platform.isAndroid)) return; debugPrint("SharingIntentService: Initializing with context"); _context = context; _setupSharingListeners(); diff --git a/lib/widgets/navigation/conditional_bottom_nav.dart b/lib/widgets/navigation/conditional_bottom_nav.dart new file mode 100644 index 0000000..7013f67 --- /dev/null +++ b/lib/widgets/navigation/conditional_bottom_nav.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:island/route.gr.dart'; +import 'package:island/screens/tabs.dart'; + +class ConditionalBottomNav extends HookConsumerWidget { + final Widget child; + + const ConditionalBottomNav({super.key, required this.child}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final currentRouteName = ref.watch(currentRouteProvider); + + const mainTabRoutes = { + ExploreRoute.name, + ChatListRoute.name, + RealmListRoute.name, + AccountRoute.name, + }; + + debugPrint(currentRouteName); + final shouldShowBottomNav = mainTabRoutes.contains(currentRouteName); + + return shouldShowBottomNav ? child : const SizedBox.shrink(); + } +} diff --git a/lib/widgets/post/post_item.dart b/lib/widgets/post/post_item.dart index 01a102f..0ea64a9 100644 --- a/lib/widgets/post/post_item.dart +++ b/lib/widgets/post/post_item.dart @@ -733,12 +733,16 @@ class _PostTruncateHint extends StatelessWidget { color: Theme.of(context).colorScheme.secondary, ), SizedBox(width: isCompact ? 4 : 6), - Text( - 'postTruncated'.tr(), - style: TextStyle( - fontSize: isCompact ? 10 : 12, - color: Theme.of(context).colorScheme.secondary, - fontStyle: FontStyle.italic, + Flexible( + child: Text( + 'postTruncated'.tr(), + style: TextStyle( + fontSize: isCompact ? 10 : 12, + color: Theme.of(context).colorScheme.secondary, + fontStyle: FontStyle.italic, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, ), ), SizedBox(width: isCompact ? 3 : 4),