From a8a4965a198d58adae4b07a99b1b4517e9e15288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Vanden=20Bossche?= Date: Sun, 14 Jul 2024 20:25:03 +0200 Subject: [PATCH 1/7] Fixed leave event should disable postions tracking --- .../event/widgets/details/event_details_actions_menu.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/app/lib/event/widgets/details/event_details_actions_menu.dart b/packages/app/lib/event/widgets/details/event_details_actions_menu.dart index 465b42c1..b8e2fd5b 100644 --- a/packages/app/lib/event/widgets/details/event_details_actions_menu.dart +++ b/packages/app/lib/event/widgets/details/event_details_actions_menu.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:hollybike/event/types/event_status_state.dart'; +import 'package:hollybike/positions/bloc/my_position/my_position_bloc.dart'; +import 'package:hollybike/positions/bloc/my_position/my_position_event.dart'; import '../../bloc/event_details_bloc/event_details_bloc.dart'; import '../../bloc/event_details_bloc/event_details_event.dart'; @@ -172,5 +174,9 @@ class EventDetailsActionsMenu extends StatelessWidget { context.read().add( LeaveEvent(), ); + + context.read().add( + DisableSendPositions(), + ); } } From 0d1f10af31eba5099006e54fe3151832b7871481 Mon Sep 17 00:00:00 2001 From: enzo soares Date: Sun, 14 Jul 2024 21:56:46 +0200 Subject: [PATCH 2/7] Fixing deeplink redirection --- packages/app/lib/auth/screens/signup_screen.dart | 11 ++++++++--- packages/app/lib/auth/widgets/signup_link_dialog.dart | 4 ++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/app/lib/auth/screens/signup_screen.dart b/packages/app/lib/auth/screens/signup_screen.dart index fb030829..a591ccf2 100644 --- a/packages/app/lib/auth/screens/signup_screen.dart +++ b/packages/app/lib/auth/screens/signup_screen.dart @@ -1,6 +1,7 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:hollybike/app/app_router.gr.dart'; import 'package:hollybike/auth/bloc/auth_bloc.dart'; import 'package:hollybike/auth/types/form_texts.dart'; import 'package:hollybike/auth/types/signup_dto.dart'; @@ -17,10 +18,10 @@ class SignupScreen extends StatelessWidget { @override Widget build(BuildContext context) { - final canPop = context.routeData.queryParams.getBool("canPop", false); + final popContext = context.routeData.queryParams.getString("popContext", ""); return Scaffold( - floatingActionButton: canPop + floatingActionButton: popContext == "connected" ? FloatingActionButton.small( onPressed: () => context.router.maybePop(), child: const Icon(Icons.arrow_back), @@ -47,7 +48,11 @@ class SignupScreen extends StatelessWidget { host: context.routeData.queryParams.getString("host"), signupDto: SignupDto.fromMap(values), )); - if (canPop) context.router.maybePop(); + if (popContext == "connected") { + context.router.maybePop(); + } else if (popContext.isEmpty) { + context.router.replaceAll([const EventsRoute()]); + } }, formFields: { "username": FormFieldConfig( diff --git a/packages/app/lib/auth/widgets/signup_link_dialog.dart b/packages/app/lib/auth/widgets/signup_link_dialog.dart index ac16c649..b56c6f7b 100644 --- a/packages/app/lib/auth/widgets/signup_link_dialog.dart +++ b/packages/app/lib/auth/widgets/signup_link_dialog.dart @@ -114,8 +114,8 @@ class _SignupLinkDialogState extends State { void _handleSubmit() { if (isValid) { widget.canPop - ? context.router.replaceNamed("${_linkController.text}&canPop=true") - : context.router.pushNamed("${_linkController.text}&canPop=false"); + ? context.router.replaceNamed("${_linkController.text}&popContext=connected") + : context.router.pushNamed("${_linkController.text}&popContext=guard"); } } } From bc90a70729d43778e2523aea30c8f40015dc923d Mon Sep 17 00:00:00 2001 From: enzo soares Date: Sun, 14 Jul 2024 22:06:58 +0200 Subject: [PATCH 3/7] Removing disabled condition on signup link dialog confirm button --- packages/app/lib/auth/widgets/signup_link_dialog.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/lib/auth/widgets/signup_link_dialog.dart b/packages/app/lib/auth/widgets/signup_link_dialog.dart index b56c6f7b..3047b252 100644 --- a/packages/app/lib/auth/widgets/signup_link_dialog.dart +++ b/packages/app/lib/auth/widgets/signup_link_dialog.dart @@ -45,7 +45,7 @@ class _SignupLinkDialogState extends State { ), ), ElevatedButton( - onPressed: isValid ? _handleSubmit : null, + onPressed: _handleSubmit, style: ElevatedButton.styleFrom( padding: const EdgeInsets.all(18), shape: const RoundedRectangleBorder( From c7717e44278a76ee608d6808612a10169bb1e226 Mon Sep 17 00:00:00 2001 From: enzo soares Date: Sun, 14 Jul 2024 22:33:27 +0200 Subject: [PATCH 4/7] Redirect once auth session state changed and not in failure --- .../app/lib/auth/screens/signup_screen.dart | 114 ++++++++++-------- 1 file changed, 61 insertions(+), 53 deletions(-) diff --git a/packages/app/lib/auth/screens/signup_screen.dart b/packages/app/lib/auth/screens/signup_screen.dart index a591ccf2..be1bf7f0 100644 --- a/packages/app/lib/auth/screens/signup_screen.dart +++ b/packages/app/lib/auth/screens/signup_screen.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:hollybike/app/app_router.gr.dart'; import 'package:hollybike/auth/bloc/auth_bloc.dart'; +import 'package:hollybike/auth/types/auth_session.dart'; import 'package:hollybike/auth/types/form_texts.dart'; import 'package:hollybike/auth/types/signup_dto.dart'; import 'package:hollybike/auth/widgets/form_builder.dart'; @@ -18,67 +19,74 @@ class SignupScreen extends StatelessWidget { @override Widget build(BuildContext context) { - final popContext = context.routeData.queryParams.getString("popContext", ""); + final popContext = context.routeData.queryParams.getString( + "popContext", ""); return Scaffold( floatingActionButton: popContext == "connected" ? FloatingActionButton.small( - onPressed: () => context.router.maybePop(), - child: const Icon(Icons.arrow_back), - ) + onPressed: () => context.router.maybePop(), + child: const Icon(Icons.arrow_back), + ) : null, floatingActionButtonLocation: FloatingActionButtonLocation.miniStartTop, - body: BlocBuilder( - builder: (context, state) { - final error = state is AuthFailure ? state.message : null; - - return BannerDialog( - body: FormBuilder( - title: "Inscrivez-vous!", - description: "Saisissez les informations de votre nouveau compte.", - errorText: error, - formTexts: const FormTexts( - submit: "S'inscrire", - ), - onFormSubmit: (formValue) { - final values = Map.from(context.routeData.queryParams.rawMap); - values.addAll(formValue); + body: BlocListener( + listener: (context, state) { + if (state is! AuthFailure) { + if (popContext == "connected") { + context.router.maybePop(); + } else if (popContext.isEmpty) { + context.router.replaceAll([const EventsRoute()]); + } + } + }, + child: BlocBuilder( + builder: (context, state) { + final error = state is AuthFailure ? state.message : null; - BlocProvider.of(context).add(AuthSignup( - host: context.routeData.queryParams.getString("host"), - signupDto: SignupDto.fromMap(values), - )); - if (popContext == "connected") { - context.router.maybePop(); - } else if (popContext.isEmpty) { - context.router.replaceAll([const EventsRoute()]); - } - }, - formFields: { - "username": FormFieldConfig( - label: "Nom utilisateur", - validator: _inputValidator, - autofocus: true, - autofillHints: [AutofillHints.newUsername], - textInputType: TextInputType.name, - ), - "email": FormFieldConfig( - label: "Adresse mail", - validator: _inputValidator, - autofillHints: [AutofillHints.email], - textInputType: TextInputType.emailAddress, + return BannerDialog( + body: FormBuilder( + title: "Inscrivez-vous!", + description: "Saisissez les informations de votre nouveau compte.", + errorText: error, + formTexts: const FormTexts( + submit: "S'inscrire", ), - "password": FormFieldConfig( - label: "Mot de passe", - validator: _passwordInputValidator, - isSecured: true, - hasControlField: true, - autofillHints: [AutofillHints.newPassword], - ), - }, - ), - ); - }, + onFormSubmit: (formValue) { + final values = Map.from(context.routeData.queryParams.rawMap); + values.addAll(formValue); + + BlocProvider.of(context).add(AuthSignup( + host: context.routeData.queryParams.getString("host"), + signupDto: SignupDto.fromMap(values), + )); + }, + formFields: { + "username": FormFieldConfig( + label: "Nom utilisateur", + validator: _inputValidator, + autofocus: true, + autofillHints: [AutofillHints.newUsername], + textInputType: TextInputType.name, + ), + "email": FormFieldConfig( + label: "Adresse mail", + validator: _inputValidator, + autofillHints: [AutofillHints.email], + textInputType: TextInputType.emailAddress, + ), + "password": FormFieldConfig( + label: "Mot de passe", + validator: _passwordInputValidator, + isSecured: true, + hasControlField: true, + autofillHints: [AutofillHints.newPassword], + ), + }, + ), + ); + }, + ), ), ); } From 19c469cfc94ffdbfb1b7e509c7389241bb2c16df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Vanden=20Bossche?= Date: Sun, 14 Jul 2024 22:34:18 +0200 Subject: [PATCH 5/7] Fixed bottom bar & top bar --- .../app/lib/event/widgets/event_status.dart | 4 +- .../widgets/profile_bottom_bar_button.dart | 70 ++++--- .../lib/shared/widgets/bar/bottom_bar.dart | 193 +++++++++++++++--- .../widgets/bar/bottom_bar_icon_button.dart | 2 +- packages/app/lib/shared/widgets/hud/hud.dart | 6 +- 5 files changed, 217 insertions(+), 58 deletions(-) diff --git a/packages/app/lib/event/widgets/event_status.dart b/packages/app/lib/event/widgets/event_status.dart index 1ecc1a83..d4fadbd9 100644 --- a/packages/app/lib/event/widgets/event_status.dart +++ b/packages/app/lib/event/widgets/event_status.dart @@ -106,7 +106,9 @@ class _EventStatusIndicatorState extends State { size: 13, ), SizedBox(width: widget.separatorWidth), - _statusText, + Flexible( + child: _statusText, + ), ], ); } diff --git a/packages/app/lib/profile/widgets/profile_bottom_bar_button.dart b/packages/app/lib/profile/widgets/profile_bottom_bar_button.dart index 97debd61..76ca77b6 100644 --- a/packages/app/lib/profile/widgets/profile_bottom_bar_button.dart +++ b/packages/app/lib/profile/widgets/profile_bottom_bar_button.dart @@ -7,22 +7,20 @@ import '../../event/widgets/event_loading_profile_picture.dart'; import '../bloc/profile_bloc/profile_bloc.dart'; class ProfileBottomBarButton extends StatelessWidget { - const ProfileBottomBarButton({super.key}); + final bool isSelected; + final Color color; + final double size; + + const ProfileBottomBarButton({ + super.key, + this.isSelected = false, + required this.color, + required this.size, + }); @override Widget build(BuildContext context) { - return NavigationDestination( - selectedIcon: Container( - padding: const EdgeInsets.all(2), - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.onPrimary, - shape: BoxShape.circle, - ), - child: _renderProfilePicture(context), - ), - icon: _renderProfilePicture(context), - label: 'Profile', - ); + return _renderProfilePicture(context); } void _handleLongPress(context) { @@ -42,22 +40,48 @@ class ProfileBottomBarButton extends StatelessWidget { final currentProfile = bloc.currentProfile; if (currentProfile is ProfileLoadSuccessEvent) { - return UserProfilePicture( - url: currentProfile.profile.profilePicture, - profilePictureKey: currentProfile.profile.profilePictureKey, - radius: 12, - isLoading: false, + return _profilePictureContainer( + context, + UserProfilePicture( + url: currentProfile.profile.profilePicture, + profilePictureKey: currentProfile.profile.profilePictureKey, + radius: size * 0.5, + isLoading: false, + ), ); } - return const UserProfilePicture( - url: null, - profilePictureKey: null, - radius: 12, - isLoading: true, + return _profilePictureContainer( + context, + UserProfilePicture( + url: null, + profilePictureKey: null, + radius: size * 0.5, + isLoading: true, + ), ); }, ), ); } + + Widget _profilePictureContainer( + BuildContext context, + Widget child, + ) { + return Center( + child: Container( + height: size, + width: size, + decoration: BoxDecoration( + border: Border.all( + color: isSelected ? color : Colors.transparent, + width: 2, + ), + shape: BoxShape.circle, + ), + child: child, + ), + ); + } } diff --git a/packages/app/lib/shared/widgets/bar/bottom_bar.dart b/packages/app/lib/shared/widgets/bar/bottom_bar.dart index 93fbd535..ce350a24 100644 --- a/packages/app/lib/shared/widgets/bar/bottom_bar.dart +++ b/packages/app/lib/shared/widgets/bar/bottom_bar.dart @@ -2,7 +2,7 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:hollybike/profile/widgets/profile_bottom_bar_button.dart'; -import 'package:hollybike/shared/widgets/bar/bottom_bar_icon_button.dart'; +import 'package:hollybike/shared/utils/safe_set_state.dart'; import '../../../app/app_router.gr.dart'; import '../../../profile/bloc/profile_bloc/profile_bloc.dart'; @@ -25,6 +25,8 @@ class BottomBar extends StatefulWidget { } class _BottomBarState extends State { + late int _currentIndex; + final _routes = [ const NavRoute( routeName: EventsRoute.name, @@ -40,42 +42,177 @@ class _BottomBarState extends State { ), ]; - int _getCurrentPageIndex(BuildContext context) { - final currentRouteName = context.router.current.name; - final index = - _routes.indexWhere((route) => route.routeName == currentRouteName); - return index == -1 ? 0 : index; + @override + void initState() { + super.initState(); + _currentIndex = _getRouteIndex(context.router.current.name); + + try { + context.router.addListener(_routeListener); + } catch (e) { + // ignore: avoid_print + } + } + + @override + void dispose() { + if (context.mounted) { + context.router.removeListener(_routeListener); + } + super.dispose(); + } + + void _routeListener() { + try { + if (!context.mounted) return; + + final index = _getRouteIndex(context.router.current.name); + + if (index != -1) { + safeSetState(() { + _currentIndex = index; + }); + } + } catch (e) { + // ignore: avoid_print + } + } + + int _getRouteIndex(String routeName) { + try { + final currentRouteName = context.router.current.name; + final index = + _routes.indexWhere((route) => route.routeName == currentRouteName); + + if (index != -1) { + return index; + } + } catch (e) { + // ignore: avoid_print + } + + return 0; } @override Widget build(BuildContext context) { return BlocBuilder( builder: (context, state) { - return NavigationBar( - backgroundColor: Theme.of(context).scaffoldBackgroundColor, - indicatorColor: Colors.transparent, - labelBehavior: NavigationDestinationLabelBehavior.alwaysHide, - selectedIndex: _getCurrentPageIndex(context), - onDestinationSelected: (index) { - final currentRouteName = context.router.current.name; - if (_routes[index].routeName != currentRouteName) { - context.router.push(_routes[index].route); - } - }, - height: 60, - destinations: const [ - BottomBarIconButton( - icon: Icons.event, - label: 'Événements', - ), - BottomBarIconButton( - icon: Icons.search, - label: 'Recherche', + final selectedColor = Theme.of(context).colorScheme.secondary; + final unselectedColor = + Theme.of(context).colorScheme.onPrimary.withAlpha(100); + + const iconSize = 30.0; + + return SafeArea( + child: SizedBox( + height: 50, + child: NavBar( + currentIndex: _currentIndex, + onSelected: (index) { + final currentRouteName = context.router.current.name; + if (_routes[index].routeName != currentRouteName) { + context.router.removeWhere((route) { + if (route.name == _routes[index].routeName) { + return true; + } + + return false; + }); + + context.router.push(_routes[index].route); + } + }, + children: [ + NavBarItem( + selectedIcon: Icon( + Icons.event_rounded, + color: selectedColor, + size: iconSize, + ), + icon: Icon( + Icons.event_rounded, + color: unselectedColor, + size: iconSize, + ), + ), + NavBarItem( + selectedIcon: Icon( + Icons.search_rounded, + color: selectedColor, + size: iconSize, + ), + icon: Icon( + Icons.search_rounded, + color: unselectedColor, + size: iconSize, + ), + ), + NavBarItem( + selectedIcon: ProfileBottomBarButton( + isSelected: true, + size: iconSize, + color: selectedColor, + ), + icon: ProfileBottomBarButton( + isSelected: false, + size: iconSize, + color: unselectedColor, + ), + ), + ], ), - ProfileBottomBarButton(), - ], + ), ); }, ); } } + +class NavBarItem { + final Widget selectedIcon; + final Widget icon; + + const NavBarItem({ + required this.selectedIcon, + required this.icon, + }); +} + +class NavBar extends StatelessWidget { + final int currentIndex; + final void Function(int) onSelected; + final List children; + + const NavBar({ + super.key, + required this.currentIndex, + required this.onSelected, + required this.children, + }); + + @override + Widget build(BuildContext context) { + return Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ + for (var i = 0; i < children.length; i++) + Expanded( + child: GestureDetector( + onTap: () => onSelected(i), + child: _buildContainer(context, i), + ), + ), + ]); + } + + Widget _buildContainer(BuildContext context, int index) { + final selectedIcon = currentIndex == index + ? children[index].selectedIcon + : children[index].icon; + + return Container( + color: Colors.transparent, + height: double.infinity, + child: selectedIcon, + ); + } +} diff --git a/packages/app/lib/shared/widgets/bar/bottom_bar_icon_button.dart b/packages/app/lib/shared/widgets/bar/bottom_bar_icon_button.dart index 8f320b8a..940bc6da 100644 --- a/packages/app/lib/shared/widgets/bar/bottom_bar_icon_button.dart +++ b/packages/app/lib/shared/widgets/bar/bottom_bar_icon_button.dart @@ -13,7 +13,7 @@ class BottomBarIconButton extends StatelessWidget { @override Widget build(BuildContext context) { return NavigationDestination( - selectedIcon: Icon(icon), + selectedIcon: Icon(icon, color: Theme.of(context).colorScheme.secondary), icon: Icon( icon, color: Theme.of(context).colorScheme.onPrimary.withAlpha(100), diff --git a/packages/app/lib/shared/widgets/hud/hud.dart b/packages/app/lib/shared/widgets/hud/hud.dart index 77268409..ce506b0e 100644 --- a/packages/app/lib/shared/widgets/hud/hud.dart +++ b/packages/app/lib/shared/widgets/hud/hud.dart @@ -19,20 +19,16 @@ class Hud extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - extendBodyBehindAppBar: true, appBar: appBar == null ? null : PreferredSize( preferredSize: const Size.fromHeight(60), - child: appBar as Widget, + child: Center(child: appBar as Widget), ), floatingActionButton: floatingActionButton, body: Stack( children: [ Column(children: [ - const SizedBox.square( - dimension: 110, - ), Expanded( child: SafeArea( top: false, From e5c18ff3a2463165b04829fbd6a2cf3559aa0dc6 Mon Sep 17 00:00:00 2001 From: enzo soares Date: Sun, 14 Jul 2024 22:34:21 +0200 Subject: [PATCH 6/7] Fixing analyze --- packages/app/lib/auth/screens/signup_screen.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/app/lib/auth/screens/signup_screen.dart b/packages/app/lib/auth/screens/signup_screen.dart index be1bf7f0..d2e401cd 100644 --- a/packages/app/lib/auth/screens/signup_screen.dart +++ b/packages/app/lib/auth/screens/signup_screen.dart @@ -3,7 +3,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:hollybike/app/app_router.gr.dart'; import 'package:hollybike/auth/bloc/auth_bloc.dart'; -import 'package:hollybike/auth/types/auth_session.dart'; import 'package:hollybike/auth/types/form_texts.dart'; import 'package:hollybike/auth/types/signup_dto.dart'; import 'package:hollybike/auth/widgets/form_builder.dart'; From aae90f3d18ac0623a29ad0218407052832162e88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Vanden=20Bossche?= Date: Sun, 14 Jul 2024 22:35:46 +0200 Subject: [PATCH 7/7] Fixed 403 instead of 401 --- .../lib/profile/bloc/edit_profile_bloc/edit_profile_bloc.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/lib/profile/bloc/edit_profile_bloc/edit_profile_bloc.dart b/packages/app/lib/profile/bloc/edit_profile_bloc/edit_profile_bloc.dart index 2cbb76ea..4d5625cf 100644 --- a/packages/app/lib/profile/bloc/edit_profile_bloc/edit_profile_bloc.dart +++ b/packages/app/lib/profile/bloc/edit_profile_bloc/edit_profile_bloc.dart @@ -63,7 +63,7 @@ class EditProfileBloc extends Bloc { ); } catch (e) { if (e is DioException) { - if (e.response?.statusCode == 403) { + if (e.response?.statusCode == 401) { emit(EditProfileLoadFailure( state, errorMessage: 'Mot de passe incorrect.',