Repository: PHS-TSA/nexus
Pull request #152: Responsive UI
Author: login: MattsAttack
Created: 2025-02-25T03:15:32Z | Merged: 2025-03-22T02:26:27Z



## Description

Reworked UI to not look terrible and made it responsive.


## Type of Change


- ✨ New feature
- 🎨 Style

## Checklist

> Before submitting the PR, please make sure you do the following:

- [ ] Read the Contributing Guidelines.
- [ ] Check that there isn't already a PR that solves the problem the same way to avoid creating a duplicate.
- [ ] Fill out this template.
- [ ] Log your hours.
- [ ] Check that commits follow the Angular commit convention, more or less.
- [ ] Ideally, include relevant tests that fail without this PR but pass with it (if applicable).

## Tested on


- macOS 14
- Windows 10




Diff:
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index a1ad3473..7b807566 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -49,19 +49,17 @@ jobs:
       - name: 🌐 Disable analytics
         run: flutter --disable-analytics
       - name: ⚙️ Cache generated files
-        uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
+        uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
         with:
           path: |
             .dart_tool/
-            lib/src/gen/*.gen.dart
-            lib/src/features/**/*.g.dart
-            lib/src/features/**/*.freezed.dart
-            lib/src/utils/*.g.dart
-            lib/src/utils/*.freezed.dart
-            lib/src/app/*.gr.dart
-            lib/src/app/*.gm.dart
-            lib/src/l10n/app_localizations.dart
-            lib/src/l10n/app_localizations_*.dart
+            packages/*/lib/src/gen/*.gen.dart
+            packages/*/lib/src/**/*.g.dart
+            packages/*/lib/src/**/*.freezed.dart
+            packages/*/lib/src/app/*.gr.dart
+            packages/*/lib/src/app/*.gm.dart
+            packages/*/lib/src/l10n/app_localizations.dart
+            packages/*/lib/src/l10n/app_localizations_*.dart
           key: ${{ runner.os }}-${{ steps.flutter.outputs.CHANNEL }}-dart-${{ hashFiles('**/build.yaml') }}
       - name: 📦 Install dependencies
         uses: bluefireteam/melos-action@c7dcb921b23cc520cace360b95d02b37bf09cdaa # v3
@@ -130,19 +128,17 @@ jobs:
       - name: 🌐 Disable analytics
         run: flutter --disable-analytics
       - name: ⚙️ Cache generated files
-        uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
+        uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
         with:
           path: |
             .dart_tool/
-            lib/src/gen/*.gen.dart
-            lib/src/features/**/*.g.dart
-            lib/src/features/**/*.freezed.dart
-            lib/src/utils/*.g.dart
-            lib/src/utils/*.freezed.dart
-            lib/src/app/*.gr.dart
-            lib/src/app/*.gm.dart
-            lib/src/l10n/app_localizations.dart
-            lib/src/l10n/app_localizations_*.dart
+            packages/*/lib/src/gen/*.gen.dart
+            packages/*/lib/src/**/*.g.dart
+            packages/*/lib/src/**/*.freezed.dart
+            packages/*/lib/src/app/*.gr.dart
+            packages/*/lib/src/app/*.gm.dart
+            packages/*/lib/src/l10n/app_localizations.dart
+            packages/*/lib/src/l10n/app_localizations_*.dart
           key: ${{ runner.os }}-${{ steps.flutter.outputs.CHANNEL }}-dart-${{ hashFiles('**/build.yaml') }}
       - name: 📦 Install dependencies
         uses: bluefireteam/melos-action@c7dcb921b23cc520cace360b95d02b37bf09cdaa # v3
@@ -185,19 +181,17 @@ jobs:
       - name: 🌐 Disable analytics
         run: flutter --disable-analytics
       - name: ⚙️ Cache generated files
-        uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
+        uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
         with:
           path: |
             .dart_tool/
-            lib/src/gen/*.gen.dart
-            lib/src/features/**/*.g.dart
-            lib/src/features/**/*.freezed.dart
-            lib/src/utils/*.g.dart
-            lib/src/utils/*.freezed.dart
-            lib/src/app/*.gr.dart
-            lib/src/app/*.gm.dart
-            lib/src/l10n/app_localizations.dart
-            lib/src/l10n/app_localizations_*.dart
+            packages/*/lib/src/gen/*.gen.dart
+            packages/*/lib/src/**/*.g.dart
+            packages/*/lib/src/**/*.freezed.dart
+            packages/*/lib/src/app/*.gr.dart
+            packages/*/lib/src/app/*.gm.dart
+            packages/*/lib/src/l10n/app_localizations.dart
+            packages/*/lib/src/l10n/app_localizations_*.dart
           key: ${{ runner.os }}-${{ steps.flutter.outputs.CHANNEL }}-dart-${{ hashFiles('**/build.yaml') }}
       - name: 📦 Install dependencies
         uses: bluefireteam/melos-action@c7dcb921b23cc520cace360b95d02b37bf09cdaa # v3
@@ -233,19 +227,17 @@ jobs:
       - name: 🌐 Disable analytics
         run: flutter --disable-analytics
       - name: ⚙️ Cache generated files
-        uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
+        uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
         with:
           path: |
             .dart_tool/
-            lib/src/gen/*.gen.dart
-            lib/src/features/**/*.g.dart
-            lib/src/features/**/*.freezed.dart
-            lib/src/utils/*.g.dart
-            lib/src/utils/*.freezed.dart
-            lib/src/app/*.gr.dart
-            lib/src/app/*.gm.dart
-            lib/src/l10n/app_localizations.dart
-            lib/src/l10n/app_localizations_*.dart
+            packages/*/lib/src/gen/*.gen.dart
+            packages/*/lib/src/**/*.g.dart
+            packages/*/lib/src/**/*.freezed.dart
+            packages/*/lib/src/app/*.gr.dart
+            packages/*/lib/src/app/*.gm.dart
+            packages/*/lib/src/l10n/app_localizations.dart
+            packages/*/lib/src/l10n/app_localizations_*.dart
           key: ${{ runner.os }}-${{ steps.flutter.outputs.CHANNEL }}-dart-${{ hashFiles('**/build.yaml') }}
           restore-keys: |
             ${{ runner.os }}-${{ steps.flutter.outputs.CHANNEL }}-dart-
diff --git a/packages/app/lib/main.dart b/packages/app/lib/main.dart
index bf66f48b..6f765e2b 100644
--- a/packages/app/lib/main.dart
+++ b/packages/app/lib/main.dart
@@ -1,8 +1,11 @@
 /// This library contains the app's primary entrypoint.
 library;
 
+import 'dart:io';
+
 import 'package:flutter/widgets.dart';
 import 'package:shared_preferences/shared_preferences.dart';
+import 'package:window_manager/window_manager.dart';
 
 import 'src/app/app.dart';
 import 'src/app/bootstrap.dart';
@@ -11,6 +14,16 @@ import 'src/app/bootstrap.dart';
 ///
 /// This uses [Bootstrap] to launch [App].
 Future<void> main() async {
+  //Set minimum window size for desktop
+  WidgetsFlutterBinding.ensureInitialized();
+
+  // Only initialize window manager on desktop platforms
+  if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) {
+    await windowManager.ensureInitialized();
+    await WindowManager.instance.setMinimumSize(const Size(1200, 600));
+    await WindowManager.instance.setMaximumSize(const Size(1920, 1080));
+  }
+
   await const App().bootstrap((
     runApp: runApp,
     getSharedPreferences: SharedPreferencesWithCache.create,
diff --git a/packages/app/lib/src/app/wrapper_page.dart b/packages/app/lib/src/app/wrapper_page.dart
index b01209f7..95a7aa0f 100644
--- a/packages/app/lib/src/app/wrapper_page.dart
+++ b/packages/app/lib/src/app/wrapper_page.dart
@@ -32,6 +32,106 @@ class WrapperPage extends ConsumerWidget {
   /// Construct a new [WrapperPage] widget.
   const WrapperPage({super.key});
 
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    return LayoutBuilder(
+      builder: (context, constraints) {
+        if (constraints.maxWidth > 680) {
+          return const _DesktopWrapper();
+        } else {
+          return const _MobileWrapper();
+        }
+      },
+    );
+  }
+}
+
+class _DesktopWrapper extends ConsumerWidget {
+  const _DesktopWrapper({super.key});
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    return AutoTabsRouter(
+      routes: const [
+        FeedRoutingRoute(),
+        MapRoute(),
+        SettingsRoute(), // Make a new feed route page that has an app bar that routes between local and world
+      ],
+      builder: (context, child) {
+        final autoRouter = AutoTabsRouter.of(context);
+        return Scaffold(
+          body: Row(
+            children: [
+              NavigationRail(
+                selectedIndex: autoRouter.activeIndex,
+                onDestinationSelected: autoRouter.setActiveIndex,
+                extended:
+                    MediaQuery.sizeOf(context).width >=
+                    800, // Might need to change to constraints
+                minExtendedWidth: 200,
+                destinations: const [
+                  // TODO(MattsAttack): increase size of tabs
+                  NavigationRailDestination(
+                    icon: Icon(Icons.feed),
+                    label: Text('Feeds'),
+                  ),
+                  NavigationRailDestination(
+                    icon: Icon(Icons.map_outlined),
+                    label: Text('Discover'),
+                  ),
+                  NavigationRailDestination(
+                    icon: Icon(Icons.settings),
+                    label: Text('Settings'),
+                  ),
+                ],
+              ),
+              Expanded(child: child),
+            ],
+          ), // Implement rail here similar to google article
+          appBar: AppBar(
+            elevation: 0,
+            // backgroundColor: Theme.of(context).colorScheme.primary,
+            shadowColor: Theme.of(context).colorScheme.surface,
+            backgroundColor: Theme.of(context).colorScheme.surface,
+            scrolledUnderElevation: 0,
+            title: Text(autoRouter.current.title(context)),
+            automaticallyImplyLeading: false,
+            // bottom: switch (autoRouter.current.path) {
+            //   '/' => TabBar(
+            //     onTap: autoRouter.setActiveIndex,
+            //     automaticIndicatorColorAdjustment: false,
+            //     overlayColor: WidgetStateProperty.all(
+            //       Theme.of(context).colorScheme.surface,
+            //     ),
+
+            //     // labelColor: Colors.blue,
+            //     // unselectedLabelColor: Colors.blue,
+            //     // indicatorColor: Colors.blue,
+            //     tabs: const [
+            //       Tab(icon: Icon(Icons.my_location), text: 'Local'),
+            //       Tab(icon: Icon(Icons.public), text: 'World'),
+            //     ],
+            //   ),
+            //   _ => null,
+            // },
+          ),
+          floatingActionButton: FloatingActionButton(
+            onPressed:
+                () async => showDialog<void>(
+                  context: context,
+                  builder: (context) => const _Dialog(),
+                ),
+            child: const Icon(Icons.create),
+          ), // Change to form on top of feed for desktop
+        );
+      },
+    );
+  }
+}
+
+class _MobileWrapper extends ConsumerWidget {
+  const _MobileWrapper({super.key});
+
   @override
   Widget build(BuildContext context, WidgetRef ref) {
     return AutoTabsScaffold(
@@ -51,6 +151,9 @@ class WrapperPage extends ConsumerWidget {
       appBarBuilder: (context, autoRouter) {
         return AppBar(
           title: Text(autoRouter.current.title(context)),
+          shadowColor: Theme.of(context).colorScheme.surface,
+          backgroundColor: Theme.of(context).colorScheme.surface,
+          scrolledUnderElevation: 0,
           automaticallyImplyLeading: false,
           bottom: switch (autoRouter.current.path) {
             // FIXME(lishaduck): This needs some work.
@@ -143,21 +246,44 @@ class _Dialog extends HookConsumerWidget {
         context,
       ).showSnackBar(const SnackBar(content: Text('Post Created!')));
     }, [formKey]);
-
+    final responsivePadding =
+        MediaQuery.sizeOf(context).width > 680 ? 16.0 : 0.0;
     return Dialog(
-      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
+      shape: RoundedRectangleBorder(
+        borderRadius: BorderRadius.circular(responsivePadding),
+      ),
+      insetPadding: EdgeInsets.symmetric(
+        horizontal: responsivePadding * 4,
+        vertical: responsivePadding * 3,
+      ),
       child: Padding(
-        padding: const EdgeInsets.all(16),
+        padding: EdgeInsets.all(responsivePadding),
         child: Form(
           key: formKey,
           child: Padding(
             padding: const EdgeInsets.all(16),
             child: Column(
               children: [
-                Text(
-                  'Create a new post',
-                  style: Theme.of(context).textTheme.headlineSmall,
-                ),
+                if (responsivePadding == 0)
+                  Row(
+                    children: [
+                      IconButton(
+                        onPressed: () async {
+                          await context.router.maybePop();
+                        },
+                        icon: const Icon(Icons.close),
+                      ),
+                      Text(
+                        'Create a new post',
+                        style: Theme.of(context).textTheme.headlineSmall,
+                      ),
+                    ],
+                  )
+                else
+                  Text(
+                    'Create a new post',
+                    style: Theme.of(context).textTheme.headlineSmall,
+                  ),
                 const SizedBox(height: 16),
 
                 // TODO(MattsAttack): guard against creating empty posts.
@@ -232,6 +358,8 @@ class _Dialog extends HookConsumerWidget {
           ),
         ),
       ),
+      //   );
+      // },
     );
   }
 }
@@ -244,7 +372,7 @@ class _UploadedImagesView extends HookConsumerWidget {
     final uploadedImages = ref.watch(uploadedImagesServiceProvider);
 
     return SizedBox(
-      height: 150,
+      height: 200,
       child: ListView.builder(
         scrollDirection: Axis.horizontal,
         padding: const EdgeInsets.all(8),
diff --git a/packages/app/lib/src/features/auth/presentation/auth/log_in_page.dart b/packages/app/lib/src/features/auth/presentation/auth/log_in_page.dart
index 93cc0c89..61172e14 100644
--- a/packages/app/lib/src/features/auth/presentation/auth/log_in_page.dart
+++ b/packages/app/lib/src/features/auth/presentation/auth/log_in_page.dart
@@ -2,7 +2,6 @@
 library;
 
 import 'package:auto_route/auto_route.dart';
-import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_hooks/flutter_hooks.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
@@ -26,6 +25,26 @@ class LogInPage extends HookConsumerWidget {
 
   final AuthCallback? _onResult;
 
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    // TODO(lishaduck): Figure out how to remove nested scaffolds.
+    return LayoutBuilder(
+      builder: (context, constraints) {
+        if (constraints.maxWidth > 680) {
+          return _DesktopLogInPage(onResult: _onResult);
+        } else {
+          return _MobileLogInPage(onResult: _onResult);
+        }
+      },
+    );
+  }
+}
+
+class _DesktopLogInPage extends HookConsumerWidget {
+  const _DesktopLogInPage({super.key, AuthCallback? onResult})
+    : _onResult = onResult;
+
+  final AuthCallback? _onResult;
   @override
   Widget build(BuildContext context, WidgetRef ref) {
     final formKey = useGlobalKey<FormState>();
@@ -62,8 +81,7 @@ class LogInPage extends HookConsumerWidget {
         }
       }
     }, [formKey]);
-
-    // TODO(lishaduck): Figure out how to remove nested scaffolds.
+    // TODO(MattsAttack): implement build.
     return Scaffold(
       body: Container(
         padding: EdgeInsets.only(
@@ -88,8 +106,6 @@ class LogInPage extends HookConsumerWidget {
                 color: Theme.of(context).colorScheme.primaryContainer,
                 borderRadius: BorderRadius.circular(15),
               ),
-              //               constraints: const BoxConstraints(maxWidth: 200,
-              // maxHeight              : 200,),
               child: Form(
                 key: formKey,
                 child: Column(
@@ -165,15 +181,147 @@ class LogInPage extends HookConsumerWidget {
       ),
     );
   }
+}
+
+class _MobileLogInPage extends HookConsumerWidget {
+  const _MobileLogInPage({super.key, AuthCallback? onResult})
+    : _onResult = onResult;
 
-  // coverage:ignore-start
+  final AuthCallback? _onResult;
   @override
-  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
-    super.debugFillProperties(properties);
-    properties.add(
-      ObjectFlagProperty<AuthCallback?>.has('onResult', _onResult),
+  Widget build(BuildContext context, WidgetRef ref) {
+    final formKey = useGlobalKey<FormState>();
+
+    final email = useState('');
+    final password = useState('');
+
+    final handleSubmit = useCallback(() async {
+      if (formKey.currentState?.validate() ?? false) {
+        formKey.currentState?.save();
+
+        // Log in the user.
+        await ref
+            .read(authServiceProvider.notifier)
+            .logInUser(email.value, password.value);
+
+        // Check that the widget still exists after the async operation.
+        if (!context.mounted) return;
+
+        if (ref.read(authServiceProvider).requireValue != null) {
+          if (_onResult != null) {
+            // Navigate the page the user wanted to go to.
+            // Runs the function passed in by the guard and brings user back to previous page.
+            _onResult(didLogIn: true);
+          } else {
+            // Replace the route so user won't come back to login.
+            await context.router.replace(const LocalFeedRoute());
+          }
+        } else {
+          // TODO(lishaduck): Move this to the guard.
+          context.showSnackBar(
+            content: const Text('Invalid username and password'),
+          );
+        }
+      }
+    }, [formKey]);
+    // TODO(MattsAttack): implement build.
+    return Scaffold(
+      body: SingleChildScrollView(
+        child: Container(
+          padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 20),
+          decoration: BoxDecoration(
+            image: DecorationImage(
+              image: Assets.pictures.loginImage.provider(),
+              fit: BoxFit.cover,
+            ),
+          ),
+          constraints: BoxConstraints(
+            minHeight: MediaQuery.of(context).size.height,
+          ),
+          child: SafeArea(
+            child: Column(
+              mainAxisAlignment: MainAxisAlignment.center,
+              children: [
+                Container(
+                  padding: const EdgeInsets.all(16),
+                  decoration: BoxDecoration(
+                    color: Theme.of(context).colorScheme.primaryContainer,
+                    borderRadius: BorderRadius.circular(15),
+                  ),
+                  child: Form(
+                    key: formKey,
+                    child: Column(
+                      children: [
+                        Text(
+                          'Welcome to Town Talk!',
+                          style: TextStyle(
+                            fontSize: 24,
+                            color:
+                                Theme.of(
+                                  context,
+                                ).colorScheme.onPrimaryContainer,
+                          ),
+                          textAlign: TextAlign.center,
+                        ),
+                        const SizedBox(height: 24),
+                        TextFormField(
+                          initialValue: email.value,
+                          onSaved: (value) {
+                            if (value == null) return;
+                            email.value = value;
+                          },
+                          keyboardType: TextInputType.emailAddress,
+                          decoration: const InputDecoration(
+                            border: OutlineInputBorder(),
+                            labelText: 'Email',
+                          ),
+                        ),
+                        const SizedBox(height: 16),
+                        TextFormField(
+                          initialValue: password.value,
+                          onSaved: (value) {
+                            if (value == null) return;
+                            password.value = value;
+                          },
+                          obscureText: true,
+                          enableSuggestions: false,
+                          autocorrect: false,
+                          keyboardType: TextInputType.visiblePassword,
+                          decoration: const InputDecoration(
+                            border: OutlineInputBorder(),
+                            labelText: 'Password',
+                          ),
+                        ),
+                        const SizedBox(height: 16),
+                        SizedBox(
+                          width: double.infinity,
+                          child: ElevatedButton(
+                            onPressed: handleSubmit,
+                            child: const Text('Log in'),
+                          ),
+                        ),
+                        Padding(
+                          padding: const EdgeInsets.only(top: 16),
+                          child: TextButton(
+                            onPressed: () async {
+                              await context.router.push(
+                                SignUpRoute(onResult: _onResult),
+                              );
+                            },
+                            child: const Text(
+                              "Don't have an account? Sign up!",
+                            ),
+                          ),
+                        ),
+                      ],
+                    ),
+                  ),
+                ),
+              ],
+            ),
+          ),
+        ),
+      ),
     );
   }
-
-  // coverage:ignore-end
 }
diff --git a/packages/app/lib/src/features/home/presentation/home/feed.dart b/packages/app/lib/src/features/home/presentation/home/feed.dart
index 176094b5..7dc22029 100644
--- a/packages/app/lib/src/features/home/presentation/home/feed.dart
+++ b/packages/app/lib/src/features/home/presentation/home/feed.dart
@@ -42,7 +42,7 @@ class Feed extends ConsumerWidget {
 
               // If we have none, return a placeholder.
               AsyncData() when index == 0 => const Expanded(
-                child: Center(child: Text('No posts yet!')),
+                child: Center(child: Text('No posts yet! Make the first!')),
               ),
               // If we run out of items, return null.
               AsyncData() => null,
diff --git a/packages/app/lib/src/features/home/presentation/home/feed_routing_page.dart b/packages/app/lib/src/features/home/presentation/home/feed_routing_page.dart
index ddec5531..1cdf74d8 100644
--- a/packages/app/lib/src/features/home/presentation/home/feed_routing_page.dart
+++ b/packages/app/lib/src/features/home/presentation/home/feed_routing_page.dart
@@ -23,11 +23,14 @@ class FeedRoutingPage extends ConsumerWidget {
       routes: const [LocalFeedRoute(), WorldFeedRoute()],
       appBarBuilder: (context, autoRouter) {
         return AppBar(
+          scrolledUnderElevation: 0,
+          backgroundColor: Theme.of(context).colorScheme.surface,
           automaticallyImplyLeading: false,
           title: NavigationBar(
             selectedIndex: autoRouter.activeIndex,
             onDestinationSelected: autoRouter.setActiveIndex,
-            backgroundColor: const Color.fromARGB(0, 0, 0, 0),
+            elevation: 0,
+            backgroundColor: Theme.of(context).colorScheme.surface,
             destinations: const [
               NavigationDestination(icon: Icon(Icons.pin_drop), label: 'Local'),
               NavigationDestination(icon: Icon(Icons.public), label: 'World'),
diff --git a/packages/app/lib/src/features/home/presentation/home/post.dart b/packages/app/lib/src/features/home/presentation/home/post.dart
index b6a34d15..45ad2366 100644
--- a/packages/app/lib/src/features/home/presentation/home/post.dart
+++ b/packages/app/lib/src/features/home/presentation/home/post.dart
@@ -187,7 +187,6 @@ class _PostImages extends ConsumerWidget {
 
     return SizedBox(
       height: 500,
-      width: MediaQuery.sizeOf(context).width / 1.5,
       child: ListView.builder(
         scrollDirection: Axis.horizontal,
         padding: const EdgeInsets.all(8),
@@ -204,7 +203,9 @@ class _PostImages extends ConsumerWidget {
               ),
             ),
             AsyncError() => const Text('Error loading image'),
-            _ => const CircularProgressIndicator(),
+            _ => CircularProgressIndicator(
+              constraints: BoxConstraints.tight(const Size.square(300)),
+            ),
           };
         },
       ),
diff --git a/packages/app/lib/src/features/home/presentation/home/post_view_page.dart b/packages/app/lib/src/features/home/presentation/home/post_view_page.dart
index 023cc5a5..eb70cb65 100644
--- a/packages/app/lib/src/features/home/presentation/home/post_view_page.dart
+++ b/packages/app/lib/src/features/home/presentation/home/post_view_page.dart
@@ -29,16 +29,20 @@ class PostViewPage extends ConsumerWidget {
         // TODO(MattsAttack): Could update this to be more intresting.
         title: const Text('Post View'),
       ),
-      body: Center(
-        child: Container(
-          constraints: const BoxConstraints(maxWidth: 1000),
-          child: Column(
-            children: [
-              Post(postId: postId),
-              // TODO(MattsAttack): Comments will go here.
-            ],
+      body: ListView(
+        children: [
+          Center(
+            child: Container(
+              constraints: const BoxConstraints(maxWidth: 1000),
+              child: Column(
+                children: [
+                  Post(postId: postId),
+                  // TODO(MattsAttack): Comments will go here.
+                ],
+              ),
+            ),
           ),
-        ),
+        ],
       ),
     );
   }
diff --git a/packages/app/linux/flutter/generated_plugin_registrant.cc b/packages/app/linux/flutter/generated_plugin_registrant.cc
index 33272872..68796484 100644
--- a/packages/app/linux/flutter/generated_plugin_registrant.cc
+++ b/packages/app/linux/flutter/generated_plugin_registrant.cc
@@ -8,6 +8,7 @@
 
 #include <desktop_webview_window/desktop_webview_window_plugin.h>
 #include <file_selector_linux/file_selector_plugin.h>
+#include <screen_retriever_linux/screen_retriever_linux_plugin.h>
 #include <url_launcher_linux/url_launcher_plugin.h>
 #include <window_to_front/window_to_front_plugin.h>
 
@@ -18,6 +19,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
   g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
       fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
   file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
+  g_autoptr(FlPluginRegistrar) screen_retriever_linux_registrar =
+      fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverLinuxPlugin");
+  screen_retriever_linux_plugin_register_with_registrar(screen_retriever_linux_registrar);
   g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
       fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
   url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
diff --git a/packages/app/linux/flutter/generated_plugins.cmake b/packages/app/linux/flutter/generated_plugins.cmake
index e0b3f1ac..eb1d44b7 100644
--- a/packages/app/linux/flutter/generated_plugins.cmake
+++ b/packages/app/linux/flutter/generated_plugins.cmake
@@ -5,7 +5,9 @@
 list(APPEND FLUTTER_PLUGIN_LIST
   desktop_webview_window
   file_selector_linux
+  screen_retriever_linux
   url_launcher_linux
+  window_manager
   window_to_front
 )
 
diff --git a/packages/app/macos/Flutter/GeneratedPluginRegistrant.swift b/packages/app/macos/Flutter/GeneratedPluginRegistrant.swift
index a42d1836..4e2996b1 100644
--- a/packages/app/macos/Flutter/GeneratedPluginRegistrant.swift
+++ b/packages/app/macos/Flutter/GeneratedPluginRegistrant.swift
@@ -13,8 +13,10 @@ import flutter_web_auth_2
 import geolocator_apple
 import package_info_plus
 import path_provider_foundation
+import screen_retriever_macos
 import shared_preferences_foundation
 import url_launcher_macos
+import window_manager
 import window_to_front
 
 func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
@@ -26,7 +28,9 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
   GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
   FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
   PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
+  ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin"))
   SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
   UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
+  WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
   WindowToFrontPlugin.register(with: registry.registrar(forPlugin: "WindowToFrontPlugin"))
 }
diff --git a/packages/app/pubspec.yaml b/packages/app/pubspec.yaml
index 96bc644f..04e81289 100644
--- a/packages/app/pubspec.yaml
+++ b/packages/app/pubspec.yaml
@@ -68,6 +68,7 @@ dependencies:
   riverpod_annotation: ^2.6.1
   shared_preferences: ^2.5.2
   url_launcher: ^6.3.1
+  window_manager: ^0.4.3
 
 dev_dependencies:
   _analysis_options: 0.0.0
diff --git a/packages/app/windows/flutter/generated_plugin_registrant.cc b/packages/app/windows/flutter/generated_plugin_registrant.cc
index bc22a41b..83154409 100644
--- a/packages/app/windows/flutter/generated_plugin_registrant.cc
+++ b/packages/app/windows/flutter/generated_plugin_registrant.cc
@@ -9,7 +9,9 @@
 #include <desktop_webview_window/desktop_webview_window_plugin.h>
 #include <file_selector_windows/file_selector_windows.h>
 #include <geolocator_windows/geolocator_windows.h>
+#include <screen_retriever_windows/screen_retriever_windows_plugin_c_api.h>
 #include <url_launcher_windows/url_launcher_windows.h>
+#include <window_manager/window_manager_plugin.h>
 #include <window_to_front/window_to_front_plugin.h>
 
 void RegisterPlugins(flutter::PluginRegistry* registry) {
@@ -19,8 +21,12 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
       registry->GetRegistrarForPlugin("FileSelectorWindows"));
   GeolocatorWindowsRegisterWithRegistrar(
       registry->GetRegistrarForPlugin("GeolocatorWindows"));
+  ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar(
+      registry->GetRegistrarForPlugin("ScreenRetrieverWindowsPluginCApi"));
   UrlLauncherWindowsRegisterWithRegistrar(
       registry->GetRegistrarForPlugin("UrlLauncherWindows"));
+  WindowManagerPluginRegisterWithRegistrar(
+      registry->GetRegistrarForPlugin("WindowManagerPlugin"));
   WindowToFrontPluginRegisterWithRegistrar(
       registry->GetRegistrarForPlugin("WindowToFrontPlugin"));
 }
diff --git a/packages/app/windows/flutter/generated_plugins.cmake b/packages/app/windows/flutter/generated_plugins.cmake
index 18bdc460..540ddd47 100644
--- a/packages/app/windows/flutter/generated_plugins.cmake
+++ b/packages/app/windows/flutter/generated_plugins.cmake
@@ -6,7 +6,9 @@ list(APPEND FLUTTER_PLUGIN_LIST
   desktop_webview_window
   file_selector_windows
   geolocator_windows
+  screen_retriever_windows
   url_launcher_windows
+  window_manager
   window_to_front
 )
 
diff --git a/pubspec.lock b/pubspec.lock
index dd3c11f2..972bfa29 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -1413,6 +1413,46 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.1.2"
+  screen_retriever:
+    dependency: transitive
+    description:
+      name: screen_retriever
+      sha256: "570dbc8e4f70bac451e0efc9c9bb19fa2d6799a11e6ef04f946d7886d2e23d0c"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.2.0"
+  screen_retriever_linux:
+    dependency: transitive
+    description:
+      name: screen_retriever_linux
+      sha256: f7f8120c92ef0784e58491ab664d01efda79a922b025ff286e29aa123ea3dd18
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.2.0"
+  screen_retriever_macos:
+    dependency: transitive
+    description:
+      name: screen_retriever_macos
+      sha256: "71f956e65c97315dd661d71f828708bd97b6d358e776f1a30d5aa7d22d78a149"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.2.0"
+  screen_retriever_platform_interface:
+    dependency: transitive
+    description:
+      name: screen_retriever_platform_interface
+      sha256: ee197f4581ff0d5608587819af40490748e1e39e648d7680ecf95c05197240c0
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.2.0"
+  screen_retriever_windows:
+    dependency: transitive
+    description:
+      name: screen_retriever_windows
+      sha256: "449ee257f03ca98a57288ee526a301a430a344a161f9202b4fcc38576716fe13"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.2.0"
   shared_preferences:
     dependency: transitive
     description:
@@ -1858,6 +1898,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.1.5"
+  window_manager:
+    dependency: transitive
+    description:
+      name: window_manager
+      sha256: "732896e1416297c63c9e3fb95aea72d0355f61390263982a47fd519169dc5059"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.4.3"
   window_to_front:
     dependency: transitive
     description:


Reviews:
user: login: lishaduck
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-25T03:54:53Z
user: login: MattsAttack
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-25T04:20:39Z
user: login: lishaduck
body: Starting to shape up, looking forward to it!
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-03-20T20:02:05Z
user: login: lishaduck
body: I guess I'll give [ZeroMQ-style optimistic merging](https://hintjens.gitbooks.io/social-architecture/content/chapter4.html) a chance.
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-22T02:13:03Z
user: login: lishaduck
body: I guess I'll give [ZeroMQ-style optimistic merging](https://hintjens.gitbooks.io/social-architecture/content/chapter4.html) a chance.
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-22T02:13:28Z

Review comments:
diff_hunk: @@ -204,7 +204,9 @@ class _PostImages extends ConsumerWidget {
               ),
             ),
             AsyncError() => const Text('Error loading image'),
-            _ => const CircularProgressIndicator(),
+            _ => const CircularProgressIndicator(
+              constraints: BoxConstraints(minWidth: 300, minHeight: 300),
path: packages/app/lib/src/features/home/presentation/home/post.dart
user: login: lishaduck
body: Should this be
```suggestion
              constraints: BoxConstraints.tight(Size.square(300)),
```

IDK, I don't want to _too_ big.

(Obv. haven't tested that)
created_at: 2025-02-25T03:54:52Z
author_association: MEMBER
diff_hunk: @@ -204,7 +204,9 @@ class _PostImages extends ConsumerWidget {
               ),
             ),
             AsyncError() => const Text('Error loading image'),
-            _ => const CircularProgressIndicator(),
+            _ => const CircularProgressIndicator(
+              constraints: BoxConstraints(minWidth: 300, minHeight: 300),
path: packages/app/lib/src/features/home/presentation/home/post.dart
user: login: MattsAttack
body: Yeah, that looks better. I just wanted to change it so it wasn't weird and vertical anymore
created_at: 2025-02-25T04:20:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,8 +1,11 @@
 /// This library contains the app's primary entrypoint.
 library;
 
+import 'dart:io';
path: packages/app/lib/main.dart
user: login: lishaduck
body: `dart:io` doesn't work on Web, this'll crash.
Try using [os_detect](https://pub.dev/packages/os_detect) instead.

created_at: 2025-03-20T19:59:58Z
author_association: MEMBER
diff_hunk: @@ -11,6 +14,16 @@ import 'src/app/bootstrap.dart';
 ///
 /// This uses [Bootstrap] to launch [App].
 Future<void> main() async {
+  //Set minimum window size for desktop
+  WidgetsFlutterBinding.ensureInitialized();
path: packages/app/lib/main.dart
user: login: lishaduck
body: This is already called in `App#bootstrap()`, could you move the rest of the code over there?
created_at: 2025-03-20T20:00:39Z
author_association: MEMBER
diff_hunk: @@ -32,6 +32,106 @@ class WrapperPage extends ConsumerWidget {
   /// Construct a new [WrapperPage] widget.
   const WrapperPage({super.key});
 
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    return LayoutBuilder(
+      builder: (context, constraints) {
+        if (constraints.maxWidth > 680) {
+          return const _DesktopWrapper();
+        } else {
+          return const _MobileWrapper();
+        }
+      },
+    );
+  }
+}
+
+class _DesktopWrapper extends ConsumerWidget {
+  const _DesktopWrapper({super.key});
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    return AutoTabsRouter(
+      routes: const [
+        FeedRoutingRoute(),
+        MapRoute(),
+        SettingsRoute(), // Make a new feed route page that has an app bar that routes between local and world
+      ],
+      builder: (context, child) {
+        final autoRouter = AutoTabsRouter.of(context);
+        return Scaffold(
+          body: Row(
+            children: [
+              NavigationRail(
+                selectedIndex: autoRouter.activeIndex,
+                onDestinationSelected: autoRouter.setActiveIndex,
+                extended:
+                    MediaQuery.sizeOf(context).width >=
+                    800, // Might need to change to constraints
+                minExtendedWidth: 200,
+                destinations: const [
+                  //Todo increase size oftabs
+                  NavigationRailDestination(
+                    icon: Icon(Icons.feed),
+                    label: Text('Feeds'),
+                  ),
+                  NavigationRailDestination(
+                    icon: Icon(Icons.map_outlined),
+                    label: Text('Discover'),
+                  ),
+                  NavigationRailDestination(
+                    icon: Icon(Icons.settings),
+                    label: Text('Settings'),
+                  ),
+                ],
+              ),
+              Expanded(child: child),
+            ],
+          ), // Implement rail here similar to google article
+          appBar: AppBar(
+            elevation: 0,
+            // backgroundColor: Theme.of(context).colorScheme.primary,
+            shadowColor: Theme.of(context).colorScheme.surface,
+            backgroundColor: Theme.of(context).colorScheme.surface,
+            scrolledUnderElevation: 0,
+            title: Text(autoRouter.current.title(context)),
+            automaticallyImplyLeading: false,
+            // bottom: switch (autoRouter.current.path) {
+            //   '/' => TabBar(
+            //     onTap: autoRouter.setActiveIndex,
+            //     automaticIndicatorColorAdjustment: false,
+            //     overlayColor: WidgetStateProperty.all(
+            //       Theme.of(context).colorScheme.surface,
+            //     ),
+
+            //     // labelColor: Colors.blue,
+            //     // unselectedLabelColor: Colors.blue,
+            //     // indicatorColor: Colors.blue,
+            //     tabs: const [
+            //       Tab(icon: Icon(Icons.my_location), text: 'Local'),
+            //       Tab(icon: Icon(Icons.public), text: 'World'),
+            //     ],
+            //   ),
+            //   _ => null,
+            // },
+          ),
path: packages/app/lib/src/app/wrapper_page.dart
user: login: lishaduck
body: Remove?
created_at: 2025-03-20T20:01:12Z
author_association: MEMBER

PR comments:
user: login: MattsAttack
created_at: 2025-02-25T03:16:31Z
body: @lishaduck whenever you get a chance could you take a look at this bug? I'm trying to get the background of the tab bar not to change when the user scrolls but it's just a real pain. I've tried a couple methods but to no luck
![image](https://github.com/user-attachments/assets/f8d94f3d-326f-4c71-82b4-8c779b51fb01)


author_association: CONTRIBUTOR
user: login: lishaduck
created_at: 2025-02-25T03:18:24Z
body: The solution is not to go nested scaffold.
author_association: MEMBER
user: login: codecov[bot]
created_at: 2025-02-25T03:19:19Z
body: ## [Codecov](https://app.codecov.io/gh/PHS-TSA/nexus/pull/152?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PHS-TSA) Report
Attention: Patch coverage is `21.73913%` with `90 lines` in your changes missing coverage. Please review.
> Project coverage is 25.58%. Comparing base [(`45b3fc6`)](https://app.codecov.io/gh/PHS-TSA/nexus/commit/45b3fc6180f11a8326158ef9e357d03853e97c36?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PHS-TSA) to head [(`ad9221a`)](https://app.codecov.io/gh/PHS-TSA/nexus/commit/ad9221af6018bdf0e53d50d20ed778774ef592af?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PHS-TSA).
> Report is 1 commits behind head on main.

| [Files with missing lines](https://app.codecov.io/gh/PHS-TSA/nexus/pull/152?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PHS-TSA) | Patch % | Lines |
|---|---|---|
| [...c/features/auth/presentation/auth/log\_in\_page.dart](https://app.codecov.io/gh/PHS-TSA/nexus/pull/152?src=pr&el=tree&filepath=packages%2Fapp%2Flib%2Fsrc%2Ffeatures%2Fauth%2Fpresentation%2Fauth%2Flog_in_page.dart&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PHS-TSA#diff-cGFja2FnZXMvYXBwL2xpYi9zcmMvZmVhdHVyZXMvYXV0aC9wcmVzZW50YXRpb24vYXV0aC9sb2dfaW5fcGFnZS5kYXJ0) | 0.00% | [60 Missing :warning: ](https://app.codecov.io/gh/PHS-TSA/nexus/pull/152?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PHS-TSA) |
| [packages/app/lib/src/app/wrapper\_page.dart](https://app.codecov.io/gh/PHS-TSA/nexus/pull/152?src=pr&el=tree&filepath=packages%2Fapp%2Flib%2Fsrc%2Fapp%2Fwrapper_page.dart&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PHS-TSA#diff-cGFja2FnZXMvYXBwL2xpYi9zcmMvYXBwL3dyYXBwZXJfcGFnZS5kYXJ0) | 52.27% | [21 Missing :warning: ](https://app.codecov.io/gh/PHS-TSA/nexus/pull/152?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PHS-TSA) |
| [...eatures/home/presentation/home/post\_view\_page.dart](https://app.codecov.io/gh/PHS-TSA/nexus/pull/152?src=pr&el=tree&filepath=packages%2Fapp%2Flib%2Fsrc%2Ffeatures%2Fhome%2Fpresentation%2Fhome%2Fpost_view_page.dart&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PHS-TSA#diff-cGFja2FnZXMvYXBwL2xpYi9zcmMvZmVhdHVyZXMvaG9tZS9wcmVzZW50YXRpb24vaG9tZS9wb3N0X3ZpZXdfcGFnZS5kYXJ0) | 0.00% | [7 Missing :warning: ](https://app.codecov.io/gh/PHS-TSA/nexus/pull/152?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PHS-TSA) |
| [.../lib/src/features/home/presentation/home/post.dart](https://app.codecov.io/gh/PHS-TSA/nexus/pull/152?src=pr&el=tree&filepath=packages%2Fapp%2Flib%2Fsrc%2Ffeatures%2Fhome%2Fpresentation%2Fhome%2Fpost.dart&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PHS-TSA#diff-cGFja2FnZXMvYXBwL2xpYi9zcmMvZmVhdHVyZXMvaG9tZS9wcmVzZW50YXRpb24vaG9tZS9wb3N0LmRhcnQ=) | 0.00% | [2 Missing :warning: ](https://app.codecov.io/gh/PHS-TSA/nexus/pull/152?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PHS-TSA) |

| [Files with missing lines](https://app.codecov.io/gh/PHS-TSA/nexus/pull/152?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PHS-TSA) | Coverage Δ | |
|---|---|---|
| [.../lib/src/features/home/presentation/home/feed.dart](https://app.codecov.io/gh/PHS-TSA/nexus/pull/152?src=pr&el=tree&filepath=packages%2Fapp%2Flib%2Fsrc%2Ffeatures%2Fhome%2Fpresentation%2Fhome%2Ffeed.dart&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PHS-TSA#diff-cGFja2FnZXMvYXBwL2xpYi9zcmMvZmVhdHVyZXMvaG9tZS9wcmVzZW50YXRpb24vaG9tZS9mZWVkLmRhcnQ=) | `61.11% <ø> (ø)` | |
| [...ures/home/presentation/home/feed\_routing\_page.dart](https://app.codecov.io/gh/PHS-TSA/nexus/pull/152?src=pr&el=tree&filepath=packages%2Fapp%2Flib%2Fsrc%2Ffeatures%2Fhome%2Fpresentation%2Fhome%2Ffeed_routing_page.dart&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PHS-TSA#diff-cGFja2FnZXMvYXBwL2xpYi9zcmMvZmVhdHVyZXMvaG9tZS9wcmVzZW50YXRpb24vaG9tZS9mZWVkX3JvdXRpbmdfcGFnZS5kYXJ0) | `100.00% <100.00%> (ø)` | |
| [.../lib/src/features/home/presentation/home/post.dart](https://app.codecov.io/gh/PHS-TSA/nexus/pull/152?src=pr&el=tree&filepath=packages%2Fapp%2Flib%2Fsrc%2Ffeatures%2Fhome%2Fpresentation%2Fhome%2Fpost.dart&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PHS-TSA#diff-cGFja2FnZXMvYXBwL2xpYi9zcmMvZmVhdHVyZXMvaG9tZS9wcmVzZW50YXRpb24vaG9tZS9wb3N0LmRhcnQ=) | `0.00% <0.00%> (ø)` | |
| [...eatures/home/presentation/home/post\_view\_page.dart](https://app.codecov.io/gh/PHS-TSA/nexus/pull/152?src=pr&el=tree&filepath=packages%2Fapp%2Flib%2Fsrc%2Ffeatures%2Fhome%2Fpresentation%2Fhome%2Fpost_view_page.dart&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PHS-TSA#diff-cGFja2FnZXMvYXBwL2xpYi9zcmMvZmVhdHVyZXMvaG9tZS9wcmVzZW50YXRpb24vaG9tZS9wb3N0X3ZpZXdfcGFnZS5kYXJ0) | `0.00% <0.00%> (ø)` | |
| [packages/app/lib/src/app/wrapper\_page.dart](https://app.codecov.io/gh/PHS-TSA/nexus/pull/152?src=pr&el=tree&filepath=packages%2Fapp%2Flib%2Fsrc%2Fapp%2Fwrapper_page.dart&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PHS-TSA#diff-cGFja2FnZXMvYXBwL2xpYi9zcmMvYXBwL3dyYXBwZXJfcGFnZS5kYXJ0) | `17.10% <52.27%> (+3.21%)` | :arrow_up: |
| [...c/features/auth/presentation/auth/log\_in\_page.dart](https://app.codecov.io/gh/PHS-TSA/nexus/pull/152?src=pr&el=tree&filepath=packages%2Fapp%2Flib%2Fsrc%2Ffeatures%2Fauth%2Fpresentation%2Fauth%2Flog_in_page.dart&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PHS-TSA#diff-cGFja2FnZXMvYXBwL2xpYi9zcmMvZmVhdHVyZXMvYXV0aC9wcmVzZW50YXRpb24vYXV0aC9sb2dfaW5fcGFnZS5kYXJ0) | `0.00% <0.00%> (ø)` | |

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: lishaduck
created_at: 2025-02-25T03:20:05Z
body: > The solution is not to go nested scaffold.

Also, was reading up on it, and I think I've figured out our wrapper woes. I'm just too much of a webdev/nested layouts guy, but the approach I used for Ducky should work better for this (Wrapper widget rather than Wrapper route).
author_association: MEMBER
user: login: MattsAttack
created_at: 2025-02-25T03:29:00Z
body: > The solution is not to go nested scaffold.

When there's a will there's a way (I should really start using the widget inspector more. I fixed it as soon I used started using it)
![image](https://github.com/user-attachments/assets/b0d086ad-1aec-4bc3-8e8c-116501d90741)

author_association: CONTRIBUTOR
user: login: lishaduck
created_at: 2025-02-25T03:33:31Z
body: > When there's a will there's a way

Fair enough.

> (I should really start using the widget inspector more. I fixed it as soon I used started using it)

True statement.
author_association: MEMBER
user: login: lishaduck
created_at: 2025-02-25T03:51:49Z
body: Do we want to drop the title from the navbar?
author_association: MEMBER
user: login: MattsAttack
created_at: 2025-02-25T04:21:10Z
body: 
> Do we want to drop the title from the navbar?

Maybe. I might change it to the app name or something

author_association: CONTRIBUTOR
user: login: MattsAttack
created_at: 2025-03-20T04:45:53Z
body: I would rather work in a coal mine than in dev ops
author_association: CONTRIBUTOR
user: login: lishaduck
created_at: 2025-03-20T12:50:52Z
body: > I would rather work in a coal mine than in dev ops

I would rather work in DevOps without retirement than work in a coal mine a week.
author_association: MEMBER
user: login: MattsAttack
created_at: 2025-03-20T13:37:33Z
body: > I would rather work in DevOps without retirement than work in a coal mine a week.

Valid but man this rebase is messed up. I don’t even know how I did it

author_association: CONTRIBUTOR
user: login: MattsAttack
created_at: 2025-03-20T20:12:59Z
body: @lishaduck I'll implement those changes. But, do you know why setup is failing?
author_association: CONTRIBUTOR
user: login: lishaduck
created_at: 2025-03-20T20:14:26Z
body: > do you know why setup is failing?

I'm not sure. I was looking into it and it looks a like a bug in GHA to me. I need to go on my run, I'll investigate more later.
author_association: MEMBER
user: login: MattsAttack
created_at: 2025-03-20T20:15:26Z
body: > I'm not sure. I was looking into it and it looks a like a bug in GHA to me. I need to go on my run, I'll investigate more later.

Cool beans. I might go and make a couple more ui changes that I forgot about. Have fun on that fartlek. 
author_association: CONTRIBUTOR
user: login: lishaduck
created_at: 2025-03-22T00:31:19Z
body: @MattsAttack, are you still working on this, or can I mess with it?
author_association: MEMBER
user: login: MattsAttack
created_at: 2025-03-22T00:41:21Z
body: @lishaduck pleade mess with it. I have no idea why set up is failing and I want to work on other stuff
author_association: CONTRIBUTOR
user: login: lishaduck
created_at: 2025-03-22T00:51:27Z
body: > @lishaduck pleade mess with it. I have no idea why set up is failing and I want to work on other stuff

👍, you go write Pineapple Pier.
author_association: MEMBER