diff --git a/Sources/justMUSIC/assets/images/key_icon.png b/Sources/justMUSIC/assets/images/key_icon.png new file mode 100644 index 0000000..b869f0e Binary files /dev/null and b/Sources/justMUSIC/assets/images/key_icon.png differ diff --git a/Sources/justMUSIC/assets/images/password_icon.png b/Sources/justMUSIC/assets/images/password_icon.png new file mode 100644 index 0000000..769b019 Binary files /dev/null and b/Sources/justMUSIC/assets/images/password_icon.png differ diff --git a/Sources/justMUSIC/lib/components/comment_component.dart b/Sources/justMUSIC/lib/components/comment_component.dart index 36d9c95..a2c8965 100644 --- a/Sources/justMUSIC/lib/components/comment_component.dart +++ b/Sources/justMUSIC/lib/components/comment_component.dart @@ -32,6 +32,7 @@ class CommentComponent extends StatelessWidget { // Image radius child: FadeInImage.assetNetwork( image: comment.user.pp, + fit: BoxFit.cover, fadeInDuration: const Duration(milliseconds: 100), placeholder: "assets/images/loadingPlaceholder.gif", ), diff --git a/Sources/justMUSIC/lib/components/historic_component.dart b/Sources/justMUSIC/lib/components/historic_component.dart new file mode 100644 index 0000000..7d0e758 --- /dev/null +++ b/Sources/justMUSIC/lib/components/historic_component.dart @@ -0,0 +1,43 @@ +import 'package:flutter/Material.dart'; + +class HistoricComponent extends StatefulWidget { + final int month; + const HistoricComponent({super.key, required this.month}); + + @override + State createState() => _HistoricComponentState(); +} + +class _HistoricComponentState extends State { + int getNumberOfDaysInMonth(int year, int month) { + if (month < 1 || month > 12) { + throw ArgumentError("Le numéro de mois doit être compris entre 1 et 12."); + } + + return DateTime(year, month + 1, 0).day; + } + + @override + Widget build(BuildContext context) { + return Wrap( + spacing: 14, + runSpacing: 14, + children: List.generate(getNumberOfDaysInMonth(DateTime.now().year, widget.month), (index) { + // Generate widgets + return LimitedBox( + maxWidth: MediaQuery.of(context).size.width - 40 / 5, + child: Container( + decoration: BoxDecoration( + gradient: LinearGradient(colors: [ + Color(0xFF1E1E1E).withOpacity(0.7), + Color(0xFF1E1E1E).withOpacity(0), + ], begin: Alignment.topCenter, end: Alignment.bottomCenter), + borderRadius: BorderRadius.circular(3)), + height: 60, + width: 60, + ), + ); + }), + ); + } +} diff --git a/Sources/justMUSIC/lib/components/login_button.dart b/Sources/justMUSIC/lib/components/login_button.dart index 6a41d9c..aa71087 100644 --- a/Sources/justMUSIC/lib/components/login_button.dart +++ b/Sources/justMUSIC/lib/components/login_button.dart @@ -4,8 +4,9 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; class LoginButton extends StatefulWidget { final Function callback; + final String text; - const LoginButton({Key? key, required this.callback}) : super(key: key); + const LoginButton({Key? key, required this.callback, required this.text}) : super(key: key); @override State createState() => _LoginButtonState(); @@ -57,7 +58,7 @@ class _LoginButtonState extends State { ), alignment: Alignment.center, child: Text( - "Se connecter", + widget.text, textAlign: TextAlign.center, style: TextStyle(color: Colors.white, fontSize: 18), ), diff --git a/Sources/justMUSIC/lib/components/profil_picture_component.dart b/Sources/justMUSIC/lib/components/profil_picture_component.dart index 01f8b3f..48b734a 100644 --- a/Sources/justMUSIC/lib/components/profil_picture_component.dart +++ b/Sources/justMUSIC/lib/components/profil_picture_component.dart @@ -24,7 +24,7 @@ class ProfilPictureComponent extends StatelessWidget { // Image radius child: FadeInImage.assetNetwork( image: user.pp, - fit: BoxFit.fill, + fit: BoxFit.cover, fadeInDuration: const Duration(milliseconds: 100), placeholder: "assets/images/loadingPlaceholder.gif", ), diff --git a/Sources/justMUSIC/lib/config/routes.dart b/Sources/justMUSIC/lib/config/routes.dart index 1c4ed4b..4b36efa 100644 --- a/Sources/justMUSIC/lib/config/routes.dart +++ b/Sources/justMUSIC/lib/config/routes.dart @@ -1,5 +1,7 @@ import 'package:flutter/Material.dart'; import 'package:justmusic/screens/add_friend_screen.dart'; +import 'package:justmusic/screens/capsule_historic_screen.dart'; +import 'package:justmusic/screens/change_password_screen.dart'; import 'package:justmusic/screens/feed_screen.dart'; import 'package:justmusic/screens/profile_screen.dart'; import 'package:justmusic/screens/user_screen.dart'; @@ -71,3 +73,39 @@ Route routeUser(User user) { }, ); } + +Route routePassword() { + return PageRouteBuilder( + pageBuilder: (context, animation, secondaryAnimation) => const ChangePasswordScreen(), + transitionsBuilder: (context, animation, secondaryAnimation, child) { + const begin = Offset(1.0, 0.0); + const end = Offset.zero; + const curve = Curves.ease; + + var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve)); + + return SlideTransition( + position: animation.drive(tween), + child: child, + ); + }, + ); +} + +Route routeHistoric() { + return PageRouteBuilder( + pageBuilder: (context, animation, secondaryAnimation) => const CapsuleHistoricScreen(), + transitionsBuilder: (context, animation, secondaryAnimation, child) { + const begin = Offset(1.0, 0.0); + const end = Offset.zero; + const curve = Curves.ease; + + var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve)); + + return SlideTransition( + position: animation.drive(tween), + child: child, + ); + }, + ); +} diff --git a/Sources/justMUSIC/lib/main.dart b/Sources/justMUSIC/lib/main.dart index 45e9b19..4ac0733 100644 --- a/Sources/justMUSIC/lib/main.dart +++ b/Sources/justMUSIC/lib/main.dart @@ -11,6 +11,7 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:justmusic/screens/add_friend_screen.dart'; import 'package:justmusic/screens/explanations_screen.dart'; import 'package:justmusic/screens/feed_screen.dart'; +import 'package:justmusic/screens/forget_password_screen.dart'; import 'package:justmusic/screens/login_screen.dart'; import 'package:justmusic/screens/launching_rocker_screen.dart'; import 'package:justmusic/screens/post_screen.dart'; @@ -102,6 +103,7 @@ class _MyAppState extends State { '/addFriend': (context) => const AddFriendScreen(), '/launchingRocket': (context) => const LaunchingRocketScreen(), '/verifyEmail': (context) => const VerifyEmailScreen(), + '/forgetPassword': (context) => const ForgetPasswordScreen(), }, debugShowCheckedModeBanner: false, theme: ThemeData( diff --git a/Sources/justMUSIC/lib/screens/capsule_historic_screen.dart b/Sources/justMUSIC/lib/screens/capsule_historic_screen.dart new file mode 100644 index 0000000..d6fe2c7 --- /dev/null +++ b/Sources/justMUSIC/lib/screens/capsule_historic_screen.dart @@ -0,0 +1,94 @@ +import 'package:flutter/Material.dart'; +import 'package:google_fonts/google_fonts.dart'; + +import '../components/historic_component.dart'; +import '../values/constants.dart'; + +class CapsuleHistoricScreen extends StatefulWidget { + const CapsuleHistoricScreen({super.key}); + + @override + State createState() => _CapsuleHistoricScreenState(); +} + +class _CapsuleHistoricScreenState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: PreferredSize( + preferredSize: Size(double.infinity, 58), + child: Container( + height: double.infinity, + color: bgAppBar, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: defaultPadding), + child: Stack( + alignment: Alignment.centerLeft, + children: [ + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + Navigator.pop(context, true); + }, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 10), + height: 30, + width: 30, + child: Image( + image: AssetImage("assets/images/return_icon.png"), + height: 8, + ), + )), + Align( + child: Text( + "Historique des capsules", + style: GoogleFonts.plusJakartaSans(color: Colors.white, fontSize: 14, fontWeight: FontWeight.bold), + ), + ) + ], + ), + ), + ), + ), + body: Container( + width: double.infinity, + height: double.infinity, + color: bgColor, + child: SingleChildScrollView( + physics: const BouncingScrollPhysics(decelerationRate: ScrollDecelerationRate.fast), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: settingPadding), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.only(top: 30, bottom: 40), + child: SizedBox( + width: double.infinity, + child: Stack( + alignment: Alignment.center, + children: [ + Container( + padding: EdgeInsets.symmetric(horizontal: 15), + constraints: BoxConstraints(maxWidth: 600), + child: Column( + children: [ + HistoricComponent( + month: DateTime.now().month, + ), + ], + ), + ) + ], + ), + ), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/Sources/justMUSIC/lib/screens/change_password_screen.dart b/Sources/justMUSIC/lib/screens/change_password_screen.dart new file mode 100644 index 0000000..d9d8beb --- /dev/null +++ b/Sources/justMUSIC/lib/screens/change_password_screen.dart @@ -0,0 +1,311 @@ +import 'package:firebase_auth/firebase_auth.dart'; + +import 'package:flutter/Material.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:justmusic/main.dart'; + +import '../values/constants.dart'; + +class ChangePasswordScreen extends StatefulWidget { + const ChangePasswordScreen({super.key}); + + @override + State createState() => _ChangePasswordScreenState(); +} + +class _ChangePasswordScreenState extends State { + final _currentPasswordTextField = TextEditingController(); + final _newPasswordTextField = TextEditingController(); + final _confirmPasswordTextField = TextEditingController(); + final _formKey = GlobalKey(); + Future resetFullScreen() async { + await SystemChannels.platform.invokeMethod( + 'SystemChrome.restoreSystemUIOverlays', + ); + } + + handleChange() async { + print("test"); + if (_formKey.currentState!.validate()) { + var error; + try { + await FirebaseAuth.instance.signInWithEmailAndPassword( + email: MyApp.userViewModel.userCurrent.mail, + password: _currentPasswordTextField.text, + ); + if (_newPasswordTextField.text == _confirmPasswordTextField.text) { + await FirebaseAuth.instance.currentUser?.updatePassword(_confirmPasswordTextField.text); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + "Mot de passe mis à jour", + style: GoogleFonts.plusJakartaSans(color: Colors.white, fontWeight: FontWeight.w400, fontSize: 15.h), + ), + backgroundColor: primaryColor, + closeIconColor: Colors.white, + ), + ); + } else { + throw FirebaseAuthException(code: "not-same", message: "Les mots de passe ne correspondent pas"); + } + } on FirebaseAuthException catch (e) { + if (e.code == "wrong-password") { + error = "Mot de passe incorrect"; + } else if (e.code == "too-many-requests") { + error = "Trop de tentatives infructueuses. Veuillez réessayer plus tard"; + } else if (e.code == "channel-error") { + error = "Impossible de vérifier le mot de passe"; + } else if (e.code == "weak-password") { + error = "Le mot de passe doit contenir 6 caractères minimum"; + } + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + error ?? e.message, + style: GoogleFonts.plusJakartaSans(color: Colors.white, fontWeight: FontWeight.w400, fontSize: 15.h), + ), + backgroundColor: Colors.red, + closeIconColor: Colors.white, + ), + ); + } + setState(() { + _currentPasswordTextField.clear(); + _newPasswordTextField.clear(); + _confirmPasswordTextField.clear(); + }); + } + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + FocusScopeNode currentFocus = FocusScope.of(context); + if (!currentFocus.hasPrimaryFocus) { + currentFocus.unfocus(); + resetFullScreen(); + } + }, + child: Scaffold( + appBar: PreferredSize( + preferredSize: Size(double.infinity, 58), + child: Container( + height: double.infinity, + color: bgAppBar, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: defaultPadding), + child: Stack( + alignment: Alignment.centerLeft, + children: [ + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + Navigator.pop(context, true); + }, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 10), + height: 30, + width: 30, + child: Image( + image: AssetImage("assets/images/return_icon.png"), + height: 8, + ), + )), + Align( + child: Text( + "Mettre le mot de passe à jour", + style: + GoogleFonts.plusJakartaSans(color: Colors.white, fontSize: 14, fontWeight: FontWeight.bold), + ), + ) + ], + ), + ), + ), + ), + body: Container( + width: double.infinity, + height: double.infinity, + color: bgColor, + child: SingleChildScrollView( + physics: const BouncingScrollPhysics(decelerationRate: ScrollDecelerationRate.fast), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: settingPadding), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.only(top: 30, bottom: 40), + child: SizedBox( + width: double.infinity, + child: Form( + key: _formKey, + child: Stack( + alignment: Alignment.center, + children: [ + Container( + constraints: BoxConstraints(maxWidth: 600), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Padding( + padding: const EdgeInsets.only(right: 10), + child: Text( + "Mot de passe actuel", + style: GoogleFonts.plusJakartaSans( + color: Colors.white, fontWeight: FontWeight.w800, fontSize: 16), + ), + )), + Expanded( + child: TextFormField( + controller: _currentPasswordTextField, + decoration: InputDecoration( + enabledBorder: OutlineInputBorder( + borderSide: const BorderSide(color: Colors.transparent), + ), + // Hides the border when you click the TextField + focusedBorder: OutlineInputBorder( + borderSide: const BorderSide(color: Colors.transparent), + ), + // Hides the border when the TextField is disabled + disabledBorder: OutlineInputBorder( + borderSide: const BorderSide(color: Colors.transparent), + ), + filled: true, + hintText: '6 caractères minimum', + hintStyle: GoogleFonts.plusJakartaSans(color: strokeTextField)), + maxLines: 1, + obscureText: true, + cursorColor: primaryColor, + style: GoogleFonts.plusJakartaSans( + color: grayText, fontWeight: FontWeight.w400, fontSize: 16), + ), + ), + ], + ), + SizedBox( + height: 10, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Padding( + padding: const EdgeInsets.only(right: 10), + child: Text( + "Nouveau mot de passe", + style: GoogleFonts.plusJakartaSans( + color: Colors.white, fontWeight: FontWeight.w800, fontSize: 16), + ), + )), + Expanded( + child: TextField( + controller: _newPasswordTextField, + decoration: InputDecoration( + enabledBorder: OutlineInputBorder( + borderSide: const BorderSide(color: Colors.transparent), + ), + // Hides the border when you click the TextField + focusedBorder: OutlineInputBorder( + borderSide: const BorderSide(color: Colors.transparent), + ), + // Hides the border when the TextField is disabled + disabledBorder: OutlineInputBorder( + borderSide: const BorderSide(color: Colors.transparent), + ), + filled: true, + hintText: '6 caractères minimum', + hintStyle: GoogleFonts.plusJakartaSans(color: strokeTextField)), + maxLines: 1, + obscureText: true, + cursorColor: primaryColor, + style: GoogleFonts.plusJakartaSans( + color: grayText, fontWeight: FontWeight.w400, fontSize: 16), + ), + ), + ], + ), + SizedBox( + height: 10, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Padding( + padding: const EdgeInsets.only(right: 10), + child: Text( + "Confirmer", + style: GoogleFonts.plusJakartaSans( + color: Colors.white, fontWeight: FontWeight.w800, fontSize: 16), + ), + )), + Expanded( + child: TextField( + obscureText: true, + controller: _confirmPasswordTextField, + decoration: InputDecoration( + enabledBorder: OutlineInputBorder( + borderSide: const BorderSide(color: Colors.transparent), + ), + // Hides the border when you click the TextField + focusedBorder: OutlineInputBorder( + borderSide: const BorderSide(color: Colors.transparent), + ), + // Hides the border when the TextField is disabled + disabledBorder: OutlineInputBorder( + borderSide: const BorderSide(color: Colors.transparent), + ), + filled: true, + hintText: '6 caractères minimum', + hintStyle: GoogleFonts.plusJakartaSans(color: strokeTextField)), + maxLines: 1, + cursorColor: primaryColor, + style: GoogleFonts.plusJakartaSans( + color: grayText, fontWeight: FontWeight.w400, fontSize: 16), + ), + ), + ], + ), + ], + ), + ) + ], + ), + ), + ), + ), + GestureDetector( + onTap: handleChange, + child: Align( + child: Container( + height: 35, + width: 160, + decoration: + BoxDecoration(color: primaryColor, borderRadius: BorderRadius.all(Radius.circular(10))), + child: Center( + child: Text( + "Mettre à jour", + style: GoogleFonts.plusJakartaSans(color: Colors.white), + ), + ), + ), + ), + ) + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/Sources/justMUSIC/lib/screens/forget_password_screen.dart b/Sources/justMUSIC/lib/screens/forget_password_screen.dart new file mode 100644 index 0000000..f43c889 --- /dev/null +++ b/Sources/justMUSIC/lib/screens/forget_password_screen.dart @@ -0,0 +1,234 @@ +import 'dart:async'; + +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter/Material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:google_fonts/google_fonts.dart'; + +import '../components/login_button.dart'; +import '../values/constants.dart'; + +class ForgetPasswordScreen extends StatefulWidget { + const ForgetPasswordScreen({Key? key}) : super(key: key); + + @override + State createState() => _ForgetPasswordScreenState(); +} + +class _ForgetPasswordScreenState extends State { + bool canResendEmail = true; + Timer? timer; + final _formKey = GlobalKey(); + final _mailTextField = TextEditingController(); + + @override + void dispose() { + timer?.cancel(); + + super.dispose(); + } + + Future sendForgetPasswordEmail() async { + if (_formKey.currentState!.validate()) { + var error; + try { + await FirebaseAuth.instance + .sendPasswordResetEmail(email: _mailTextField.text); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + "Un e-mail de réinitialisation a été envoyé. Veuillez patienter pendant 30 secondes avant la prochaine utilisation.", + style: GoogleFonts.plusJakartaSans( + color: Colors.white, + fontWeight: FontWeight.w400, + fontSize: 15.h), + ), + backgroundColor: primaryColor, + ), + ); + setState(() => canResendEmail = false); + await Future.delayed(Duration(minutes: 1)); + setState(() => canResendEmail = true); + } on FirebaseAuthException catch (e) { + if (e.code == "invalid-email") { + error = "Mail incorrect"; + } else if (e.code == "user-not-found") { + error = "Format de mail incorrect"; + } else if (e.code == "too-many-requests") { + error = + "Trop de tentatives. Veuillez réessayer plus tard"; + } + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + error, + style: GoogleFonts.plusJakartaSans( + color: Colors.white, + fontWeight: FontWeight.w400, + fontSize: 20.h), + ), + backgroundColor: Colors.red, + ), + ); + } + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: bgColor, + body: Form( + key: _formKey, + child: Stack( + children: [ + SingleChildScrollView( + child: SizedBox( + width: double.infinity, + child: Column( + children: [ + Padding( + padding: EdgeInsets.only(top: 185.h), + child: Align( + child: SizedBox( + width: 56.h, + child: Image( + image: AssetImage( + "assets/images/key_icon.png")), + ), + )), + Padding( + padding: EdgeInsets.only(top: 28.h), + child: AutoSizeText( + "Mot de passe oublié", + style: GoogleFonts.plusJakartaSans( + color: Colors.white, + fontWeight: FontWeight.w600, + fontSize: 24.w), + maxLines: 1, + maxFontSize: 30, + overflow: TextOverflow.fade, + ), + ), + Padding( + padding: + EdgeInsets.symmetric(horizontal: defaultPadding), + child: Padding( + padding: EdgeInsets.only(bottom: 20.h), + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + SizedBox( + height: 15.h, + ), + SizedBox( + width: 346.h, + child: AutoSizeText( + "Afin de procéder à la récupération de votre mot de passe, veuillez renseigner votre adresse mail correspondant a votre compte JustMusic.", + style: GoogleFonts.plusJakartaSans( + color: Colors.white, + fontWeight: FontWeight.w100, + fontSize: 16.w), + maxFontSize: 20, + textAlign: TextAlign.center, + ), + ), + ], + ), + ), + ), + ConstrainedBox( + constraints: BoxConstraints(maxWidth: 600), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.only( + bottom: 68.h, + left: defaultPadding, + right: defaultPadding), + child: TextFormField( + controller: _mailTextField, + keyboardAppearance: Brightness.dark, + validator: (value) { + if (value == null || value.isEmpty) { + return 'entrez un email valide'; + } + return null; + }, + cursorColor: primaryColor, + keyboardType: + TextInputType.emailAddress, + style: GoogleFonts.plusJakartaSans( + color: primaryColor), + decoration: InputDecoration( + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + width: 1, + color: strokeTextField), + borderRadius: BorderRadius.all( + Radius.circular(10))), + prefix: const Padding( + padding: EdgeInsets.only( + left: 20.0)), + suffix: const Padding( + padding: EdgeInsets.only( + left: 20.0)), + fillColor: bgTextField, + filled: true, + focusColor: Color.fromRGBO( + 255, 255, 255, 0.30), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + width: 1, + color: strokeTextField), + borderRadius: BorderRadius.all( + Radius.circular(10))), + hintText: 'Email', + hintStyle: GoogleFonts.plusJakartaSans(color: strokeTextField)), + )), + Padding( + padding: EdgeInsets.symmetric( + horizontal: defaultPadding), + child: SizedBox( + width: 600, + child: LoginButton( + callback: () { + canResendEmail + ? sendForgetPasswordEmail() + : null; + }, + text: "Envoyer", + )), + ), + ])), + Align( + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + Navigator.pushNamed(context, '/login'); + }, + child: Padding( + padding: EdgeInsets.only(top: 101), + child: RichText( + textAlign: TextAlign.center, + text: TextSpan( + text: 'Revenir a l’étape précédente', + style: GoogleFonts.plusJakartaSans( + fontSize: 15, + fontWeight: FontWeight.w400, + color: primaryColor), + ), + ), + ), + ), + ), + ], + ), + ), + ), + ], + ))); + } +} diff --git a/Sources/justMUSIC/lib/screens/login_screen.dart b/Sources/justMUSIC/lib/screens/login_screen.dart index 0cbddf1..10d3be2 100644 --- a/Sources/justMUSIC/lib/screens/login_screen.dart +++ b/Sources/justMUSIC/lib/screens/login_screen.dart @@ -1,4 +1,3 @@ -import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -27,14 +26,18 @@ class _LoginScreenState extends State { handleLogin() async { if (_formKey.currentState!.validate()) { try { - await MyApp.userViewModel.login(_userMailTextField.text, _passwordTextField.text); + await MyApp.userViewModel + .login(_userMailTextField.text, _passwordTextField.text); Navigator.pushNamed(context, '/feed'); } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( - e.toString() ?? "", - style: GoogleFonts.plusJakartaSans(color: Colors.white, fontWeight: FontWeight.w400, fontSize: 20.h), + e.toString(), + style: GoogleFonts.plusJakartaSans( + color: Colors.white, + fontWeight: FontWeight.w400, + fontSize: 20.h), ), backgroundColor: Colors.red, ), @@ -63,19 +66,25 @@ class _LoginScreenState extends State { child: Form( key: _formKey, child: Column( - crossAxisAlignment: CrossAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.center, children: [ Flexible( flex: 4, child: Padding( padding: EdgeInsets.only(bottom: 60), child: Column( - mainAxisAlignment: MainAxisAlignment.end, + mainAxisAlignment: + MainAxisAlignment.end, children: [ Text( "Te revoilà!", - style: GoogleFonts.plusJakartaSans( - color: Colors.white, fontWeight: FontWeight.w600, fontSize: 38.h), + style: + GoogleFonts.plusJakartaSans( + color: Colors.white, + fontWeight: + FontWeight.w600, + fontSize: 38.h), ), SizedBox( height: 10, @@ -84,8 +93,12 @@ class _LoginScreenState extends State { width: 230.w, child: Text( "Bon retour parmis nous tu nous as manqué!", - style: GoogleFonts.plusJakartaSans( - color: Colors.white, fontWeight: FontWeight.w400, fontSize: 20.h), + style: GoogleFonts + .plusJakartaSans( + color: Colors.white, + fontWeight: + FontWeight.w400, + fontSize: 20.h), textAlign: TextAlign.center, ), ), @@ -96,33 +109,51 @@ class _LoginScreenState extends State { Expanded( flex: 5, child: Column( - crossAxisAlignment: CrossAxisAlignment.end, - mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.end, + mainAxisAlignment: + MainAxisAlignment.center, children: [ TextFormField( controller: _userMailTextField, - keyboardAppearance: Brightness.dark, + keyboardAppearance: + Brightness.dark, validator: (value) { - if (value == null || value.isEmpty) { + if (value == null || + value.isEmpty) { return 'entrez un email valide'; } return null; }, cursorColor: primaryColor, - keyboardType: TextInputType.emailAddress, - style: GoogleFonts.plusJakartaSans(color: primaryColor), + keyboardType: + TextInputType.emailAddress, + style: + GoogleFonts.plusJakartaSans( + color: primaryColor), decoration: InputDecoration( focusedBorder: OutlineInputBorder( - borderSide: BorderSide(width: 1, color: strokeTextField), - borderRadius: BorderRadius.all(Radius.circular(10))), - prefix: const Padding(padding: EdgeInsets.only(left: 20.0)), - suffix: const Padding(padding: EdgeInsets.only(left: 20.0)), + borderSide: BorderSide( + width: 1, + color: + strokeTextField), + borderRadius: BorderRadius.all( + Radius.circular(10))), + prefix: const Padding( + padding: EdgeInsets.only( + left: 20.0)), + suffix: const Padding( + padding: EdgeInsets.only( + left: 20.0)), fillColor: bgTextField, filled: true, - errorStyle: TextStyle(fontSize: 9, height: 0.3), - focusColor: Color.fromRGBO(255, 255, 255, 0.30), + errorStyle: TextStyle( + fontSize: 9, height: 0.3), + focusColor: Color.fromRGBO( + 255, 255, 255, 0.30), enabledBorder: OutlineInputBorder( - borderSide: BorderSide(width: 1, color: strokeTextField), + borderSide: + BorderSide(width: 1, color: strokeTextField), borderRadius: BorderRadius.all(Radius.circular(10))), hintText: 'Email', hintStyle: GoogleFonts.plusJakartaSans(color: strokeTextField)), @@ -132,31 +163,55 @@ class _LoginScreenState extends State { ), TextFormField( controller: _passwordTextField, - keyboardAppearance: Brightness.dark, + keyboardAppearance: + Brightness.dark, obscureText: passenable, validator: (value) { - if (value == null || value.isEmpty) { + if (value == null || + value.isEmpty) { return 'entrez un mot de passe valide'; } return null; }, cursorColor: primaryColor, - style: GoogleFonts.plusJakartaSans(color: primaryColor), + style: + GoogleFonts.plusJakartaSans( + color: primaryColor), decoration: InputDecoration( - focusedBorder: OutlineInputBorder( - borderSide: BorderSide(width: 1, color: strokeTextField), - borderRadius: BorderRadius.all(Radius.circular(10))), + focusedBorder: + OutlineInputBorder( + borderSide: BorderSide( + width: 1, + color: + strokeTextField), + borderRadius: + BorderRadius.all( + Radius.circular( + 10))), fillColor: bgTextField, filled: true, - focusColor: Color.fromRGBO(255, 255, 255, 0.30), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide(width: 1, color: strokeTextField), - borderRadius: BorderRadius.all(Radius.circular(10))), + focusColor: Color.fromRGBO( + 255, 255, 255, 0.30), + enabledBorder: + OutlineInputBorder( + borderSide: BorderSide( + width: 1, + color: + strokeTextField), + borderRadius: + BorderRadius.all( + Radius.circular( + 10))), hintText: 'Mot de passe', - hintStyle: GoogleFonts.plusJakartaSans(color: strokeTextField), - prefix: const Padding(padding: EdgeInsets.only(left: 20.0)), + hintStyle: + GoogleFonts.plusJakartaSans( + color: strokeTextField), + prefix: const Padding( + padding: EdgeInsets.only( + left: 20.0)), suffixIcon: Container( - padding: EdgeInsets.only(right: 10), + padding: EdgeInsets.only( + right: 10), margin: EdgeInsets.all(5), height: 3, child: InkWell( @@ -170,25 +225,40 @@ class _LoginScreenState extends State { }); }, // Image tapped - splashColor: Colors.white10, + splashColor: + Colors.white10, // Splash color over image child: Image( image: passenable - ? AssetImage("assets/images/show_icon.png") - : AssetImage("assets/images/hide_icon.png"), + ? AssetImage( + "assets/images/show_icon.png") + : AssetImage( + "assets/images/hide_icon.png"), height: 2, ), )), - errorStyle: TextStyle(fontSize: 9, height: 0.3), - ), - ), - Padding( - padding: EdgeInsets.only(top: 10), - child: Text( - "Mot de passe oublié?", - style: GoogleFonts.plusJakartaSans(color: Colors.white), + errorStyle: TextStyle( + fontSize: 9, height: 0.3), ), ), + GestureDetector( + behavior: + HitTestBehavior.translucent, + onTap: () { + Navigator.pushNamed( + context, '/forgetPassword'); + }, + child: Padding( + padding: + EdgeInsets.only(top: 10), + child: Text( + "Mot de passe oublié?", + style: GoogleFonts + .plusJakartaSans( + color: + Colors.white), + ), + )), SizedBox( height: defaultPadding, ), @@ -196,30 +266,44 @@ class _LoginScreenState extends State { width: 600, child: LoginButton( callback: handleLogin, + text: "Se connecter", )), Align( child: GestureDetector( - behavior: HitTestBehavior.translucent, + behavior: + HitTestBehavior.translucent, onTap: () { - Navigator.pushNamed(context, '/register'); + Navigator.pushNamed( + context, '/register'); }, child: Padding( - padding: EdgeInsets.only(top: 20), + padding: + EdgeInsets.only(top: 20), child: RichText( textAlign: TextAlign.center, text: TextSpan( - text: 'Pas encore inscrit?', - style: GoogleFonts.plusJakartaSans( - color: Colors.white, - fontWeight: FontWeight.w400, - fontSize: 15), + text: + 'Pas encore inscrit?', + style: GoogleFonts + .plusJakartaSans( + color: + Colors.white, + fontWeight: + FontWeight + .w400, + fontSize: 15), children: [ TextSpan( text: " S’inscire", - style: GoogleFonts.plusJakartaSans( - fontSize: 15, - fontWeight: FontWeight.w400, - color: primaryColor)), + style: GoogleFonts + .plusJakartaSans( + fontSize: + 15, + fontWeight: + FontWeight + .w400, + color: + primaryColor)), ], ), ), @@ -234,38 +318,55 @@ class _LoginScreenState extends State { child: Padding( padding: EdgeInsets.only(top: 20), child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.center, children: [ ConstrainedBox( - constraints: BoxConstraints(maxWidth: 600), + constraints: BoxConstraints( + maxWidth: 600), child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisAlignment: + MainAxisAlignment + .spaceEvenly, children: [ Expanded( child: Container( - color: Color(0xFF3D3D3D), + color: + Color(0xFF3D3D3D), height: 1, ), ), Padding( - padding: const EdgeInsets.only( - left: defaultPadding, right: defaultPadding), + padding: const EdgeInsets + .only( + left: + defaultPadding, + right: + defaultPadding), child: Text( 'Ou', - style: GoogleFonts.plusJakartaSans( - color: Colors.white, fontWeight: FontWeight.bold), + style: GoogleFonts + .plusJakartaSans( + color: Colors + .white, + fontWeight: + FontWeight + .bold), ), ), Expanded( child: Container( height: 1, - color: Color(0xFF3D3D3D), + color: + Color(0xFF3D3D3D), )), ], ), ), - SizedBox(height: defaultPadding), + SizedBox( + height: defaultPadding), SignInButton( Buttons.Google, text: "Login with Google", diff --git a/Sources/justMUSIC/lib/screens/profile_screen.dart b/Sources/justMUSIC/lib/screens/profile_screen.dart index 1a5fafe..54cf845 100644 --- a/Sources/justMUSIC/lib/screens/profile_screen.dart +++ b/Sources/justMUSIC/lib/screens/profile_screen.dart @@ -26,10 +26,18 @@ class _ProfileScreenState extends State { Navigator.pushNamed(context, '/welcome'); } + void _openHistoric() { + Navigator.of(context).push(routeHistoric()); + } + void _openDetail() { Navigator.of(context).push(routeUser(MyApp.userViewModel.userCurrent)); } + void _openPassword() { + Navigator.of(context).push(routePassword()); + } + return Scaffold( appBar: PreferredSize( preferredSize: Size(double.infinity, 58), @@ -103,16 +111,21 @@ class _ProfileScreenState extends State { label: 'Compte', action: _openDetail, ), - const SettingPartComponent( + SettingPartComponent( icon: JustMusicIcon.history, label: 'Historiques des capsules', - action: null, + action: _openHistoric, ), const SettingPartComponent( icon: JustMusicIcon.spotify, label: 'Lier un compte Spotify', action: null, ), + SettingPartComponent( + icon: JustMusicIcon.password, + label: 'Modifier mon mot de passe', + action: _openPassword, + ), const SettingPartComponent( icon: JustMusicIcon.trash, label: 'Supprimer mon compte', diff --git a/Sources/justMUSIC/lib/screens/registration_screen.dart b/Sources/justMUSIC/lib/screens/registration_screen.dart index 9ff5692..9d03be5 100644 --- a/Sources/justMUSIC/lib/screens/registration_screen.dart +++ b/Sources/justMUSIC/lib/screens/registration_screen.dart @@ -36,7 +36,7 @@ class _RegistrationScreenState extends State { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( - e.toString() ?? "", + e.toString(), style: GoogleFonts.plusJakartaSans(color: Colors.white, fontWeight: FontWeight.w400, fontSize: 20.h), ), backgroundColor: Colors.red, @@ -276,6 +276,7 @@ class _RegistrationScreenState extends State { width: 600, child: LoginButton( callback: handleRegister, + text: "Continuer", )), ), Align( diff --git a/Sources/justMUSIC/lib/screens/user_screen.dart b/Sources/justMUSIC/lib/screens/user_screen.dart index 4ee6494..30eb974 100644 --- a/Sources/justMUSIC/lib/screens/user_screen.dart +++ b/Sources/justMUSIC/lib/screens/user_screen.dart @@ -1,5 +1,7 @@ import 'dart:io'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/Material.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; @@ -10,11 +12,11 @@ import 'package:image_picker/image_picker.dart'; import '../components/profile_component.dart'; import '../components/recap_component.dart'; import '../main.dart'; -import '../model/User.dart'; +import '../model/User.dart' as UserJstMusic; import '../values/constants.dart'; class UserScreen extends StatefulWidget { - final User user; + final UserJstMusic.User user; const UserScreen({super.key, required this.user}); @override diff --git a/Sources/justMUSIC/lib/screens/verify_email_screen.dart b/Sources/justMUSIC/lib/screens/verify_email_screen.dart index 6d964e9..63b0e16 100644 --- a/Sources/justMUSIC/lib/screens/verify_email_screen.dart +++ b/Sources/justMUSIC/lib/screens/verify_email_screen.dart @@ -75,7 +75,7 @@ class _VerifyEmailScreenState extends State { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( - e.toString() ?? "", + e.toString(), style: GoogleFonts.plusJakartaSans(color: Colors.white, fontWeight: FontWeight.w400, fontSize: 20.h), ), backgroundColor: Colors.red, @@ -144,6 +144,7 @@ class _VerifyEmailScreenState extends State { width: 600, child: LoginButton( callback: checkEmailVerified, + text: "Continuer", )), ), Align( diff --git a/Sources/justMUSIC/lib/values/icons.dart b/Sources/justMUSIC/lib/values/icons.dart index 1cd01d1..f08140b 100644 --- a/Sources/justMUSIC/lib/values/icons.dart +++ b/Sources/justMUSIC/lib/values/icons.dart @@ -5,7 +5,8 @@ enum JustMusicIcon { cross, history, theme, - notification + notification, + password, } extension MyIconExtension on JustMusicIcon { @@ -25,7 +26,8 @@ extension MyIconExtension on JustMusicIcon { return 'assets/images/theme_icon.png'; case JustMusicIcon.notification: return 'assets/images/notification_icon.png'; - + case JustMusicIcon.password: + return 'assets/images/password_icon.png'; default: throw 'assets/images/unknown.png'; }