diff --git a/.idea/libraries/Dart_Packages.xml b/.idea/libraries/Dart_Packages.xml index 36e15e8..70bceed 100644 --- a/.idea/libraries/Dart_Packages.xml +++ b/.idea/libraries/Dart_Packages.xml @@ -387,34 +387,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -611,132 +583,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -905,20 +751,6 @@ - - - - - - - - - - - - @@ -1017,41 +849,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1101,34 +898,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Sources/justMUSIC/assets/images/empty_collection.png b/Sources/justMUSIC/assets/images/empty_collection.png new file mode 100644 index 0000000..c140ccf Binary files /dev/null and b/Sources/justMUSIC/assets/images/empty_collection.png differ diff --git a/Sources/justMUSIC/lib/main.dart b/Sources/justMUSIC/lib/main.dart index 1cf347a..3c5ae57 100644 --- a/Sources/justMUSIC/lib/main.dart +++ b/Sources/justMUSIC/lib/main.dart @@ -57,26 +57,10 @@ class MyApp extends StatefulWidget { class _MyAppState extends State { late StreamSubscription user; - Stream userCurrent = Stream.empty(); @override void initState() { super.initState(); - checkSignIn(); - } - - Future checkSignIn() async { - user = FirebaseAuth.instance.authStateChanges().listen((user) async { - if (user == null) { - print('User is currently signed out!'); - return null; - } else { - MyApp.userViewModel.userCurrent = (await (MyApp.userViewModel.getUser(user.uid)))!; - userCurrent = Stream.value(MyApp.userViewModel.userCurrent); - print('User is signed in!'); - } - }); - return null; } @override @@ -94,25 +78,68 @@ class _MyAppState extends State { return ScreenUtilInit( useInheritedMediaQuery: true, builder: (context, child) { - return MaterialApp( - routes: { - '/welcome': (context) => const WellcomeScreen(), - '/feed': (context) => const FeedScreen(), - '/login': (context) => const LoginScreen(), - '/register': (context) => const RegistrationScreen(), - '/post': (context) => const PostScreen(), - '/profile': (context) => const ProfileScreen(), - '/explanation': (context) => const ExplanationsScreen(), - '/addFriend': (context) => const AddFriendScreen(), - '/launchingRocket': (context) => const LaunchingRocketScreen(), - '/verifyEmail': (context) => const VerifyEmailScreen(), - '/forgetPassword': (context) => const ForgetPasswordScreen(), - }, - debugShowCheckedModeBanner: false, - theme: ThemeData( - primarySwatch: Colors.blue, - ), - home: WellcomeScreen()); + return StreamBuilder( + stream: FirebaseAuth.instance.authStateChanges(), + builder: (context, snapshot) { + if (ConnectionState.waiting == snapshot.connectionState) { + return const CupertinoActivityIndicator(); + } + if (snapshot.hasData) { + return FutureBuilder( + future: MyApp.userViewModel.getUser(snapshot.data!.uid), + builder: (context, userSnapshot) { + if (userSnapshot.connectionState == ConnectionState.waiting) { + return const CupertinoActivityIndicator(); + } else { + if (userSnapshot.hasData) { + MyApp.userViewModel.userCurrent = userSnapshot.data!; + return MaterialApp( + routes: { + '/welcome': (context) => const WellcomeScreen(), + '/feed': (context) => const FeedScreen(), + '/login': (context) => const LoginScreen(), + '/register': (context) => const RegistrationScreen(), + '/post': (context) => const PostScreen(), + '/profile': (context) => const ProfileScreen(), + '/explanation': (context) => const ExplanationsScreen(), + '/addFriend': (context) => const AddFriendScreen(), + '/launchingRocket': (context) => const LaunchingRocketScreen(), + '/verifyEmail': (context) => const VerifyEmailScreen(), + '/forgetPassword': (context) => const ForgetPasswordScreen(), + }, + debugShowCheckedModeBanner: false, + theme: ThemeData( + primarySwatch: Colors.blue, + ), + home: FeedScreen()); + } else { + return const Text('User data not found'); + } + } + }, + ); + } else { + return MaterialApp( + routes: { + '/welcome': (context) => const WellcomeScreen(), + '/feed': (context) => const FeedScreen(), + '/login': (context) => const LoginScreen(), + '/register': (context) => const RegistrationScreen(), + '/post': (context) => const PostScreen(), + '/profile': (context) => const ProfileScreen(), + '/explanation': (context) => const ExplanationsScreen(), + '/addFriend': (context) => const AddFriendScreen(), + '/launchingRocket': (context) => const LaunchingRocketScreen(), + '/verifyEmail': (context) => const VerifyEmailScreen(), + '/forgetPassword': (context) => const ForgetPasswordScreen(), + }, + debugShowCheckedModeBanner: false, + theme: ThemeData( + primarySwatch: Colors.blue, + ), + home: WellcomeScreen()); + } + }); }, designSize: Size(390, 844), ); diff --git a/Sources/justMUSIC/lib/screens/login_screen.dart b/Sources/justMUSIC/lib/screens/login_screen.dart index eb44ba8..01280f8 100644 --- a/Sources/justMUSIC/lib/screens/login_screen.dart +++ b/Sources/justMUSIC/lib/screens/login_screen.dart @@ -27,18 +27,14 @@ 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), + style: GoogleFonts.plusJakartaSans(color: Colors.white, fontWeight: FontWeight.w400, fontSize: 20.h), ), backgroundColor: Colors.red, ), @@ -79,25 +75,19 @@ 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, @@ -106,12 +96,8 @@ 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, ), ), @@ -122,51 +108,33 @@ 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)), @@ -176,55 +144,31 @@ 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( @@ -238,38 +182,28 @@ 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), + errorStyle: TextStyle(fontSize: 9, height: 0.3), ), ), GestureDetector( - behavior: - HitTestBehavior.translucent, + behavior: HitTestBehavior.translucent, onTap: () { - Navigator.pushNamed( - context, '/forgetPassword'); + Navigator.pushNamed(context, '/forgetPassword'); }, child: Padding( - padding: - EdgeInsets.only(top: 10), + padding: EdgeInsets.only(top: 10), child: Text( "Mot de passe oublié?", - style: GoogleFonts - .plusJakartaSans( - color: - Colors.white), + style: GoogleFonts.plusJakartaSans(color: Colors.white), ), )), SizedBox( @@ -283,40 +217,27 @@ class _LoginScreenState extends State { )), 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)), ], ), ), @@ -331,59 +252,51 @@ 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), - SignInButton( - Buttons.Google, - text: "Login with Google", - onPressed: signInWithGoogle, + SizedBox(height: defaultPadding), + ConstrainedBox( + constraints: BoxConstraints(maxWidth: 540), + child: SizedBox( + width: 300.sp, + height: 50, + child: SignInButton( + Buttons.Google, + text: "Login with Google", + onPressed: signInWithGoogle, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(20))), + ), + ), ), ], ), diff --git a/Sources/justMUSIC/lib/screens/search_song_screen.dart b/Sources/justMUSIC/lib/screens/search_song_screen.dart index 017e5c2..aac095b 100644 --- a/Sources/justMUSIC/lib/screens/search_song_screen.dart +++ b/Sources/justMUSIC/lib/screens/search_song_screen.dart @@ -269,40 +269,139 @@ class _SearchSongScreenState extends State { future: _fetchSavedSong(), builder: (BuildContext context, AsyncSnapshot> snapshot) { if (snapshot.hasData) { - return ListView.builder( - physics: const BouncingScrollPhysics(decelerationRate: ScrollDecelerationRate.fast), - controller: _scrollController, - itemCount: snapshot.data?.length, - itemBuilder: (context, index) { - if (playingIndex == index) { + if (snapshot.data?.length == 0) { + return Container( + width: double.infinity, + height: double.infinity, + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + "Votre collection est vide.", + style: GoogleFonts.plusJakartaSans( + color: Colors.white, fontWeight: FontWeight.w800, fontSize: 18), + ), + ), + Image.asset( + "assets/images/empty_collection.png", + width: 300, + ) + ], + ), + ); + } else { + return ListView.builder( + physics: const BouncingScrollPhysics(decelerationRate: ScrollDecelerationRate.fast), + controller: _scrollController, + itemCount: snapshot.data?.length, + itemBuilder: (context, index) { + if (playingIndex == index) { + return InkWell( + onTap: () { + widget.callback((snapshot.data?[index])!); + }, + onLongPress: () { + showCupertinoModalPopup( + context: context, + builder: (BuildContext context) => CupertinoAlertDialog( + title: const Text('Supprimer la musique'), + content: Text( + 'Etes-vous sur de vouloir supprimer ${(snapshot.data?[index])!.title} de votre collection?'), + actions: [ + CupertinoDialogAction( + /// This parameter indicates this action is the default, + /// and turns the action's text to bold text. + isDefaultAction: true, + onPressed: () { + Navigator.pop(context); + }, + child: const Text('Annuler'), + ), + CupertinoDialogAction( + /// This parameter indicates the action would perform + /// a destructive action such as deletion, and turns + /// the action's text color to red. + isDestructiveAction: true, + onPressed: () async { + Navigator.pop(context); + await MyApp.musicViewModel + .addOrDeleteFavoriteMusic((snapshot.data?[index])!.id); + MyApp.userViewModel.userCurrent.musics_likes + .remove((snapshot.data?[index])!.id); + + MyApp.audioPlayer.release(); + setState(() { + playingIndex = null; + }); + }, + child: const Text('Supprimer'), + ), + ], + ), + ); + }, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: MusicListComponent( + music: (snapshot.data?[index])!, + playing: true, + callback: playMusic, + index: index, + ), + )); + } return InkWell( onTap: () { - widget.callback(filteredData[index]); + widget.callback((snapshot.data?[index])!); + }, + onLongPress: () { + showCupertinoModalPopup( + context: context, + builder: (BuildContext context) => CupertinoAlertDialog( + title: const Text('Supprimer la musique'), + content: Text( + 'Etes-vous sur de vouloir supprimer ${(snapshot.data?[index])!.title} de votre collection?'), + actions: [ + CupertinoDialogAction( + /// This parameter indicates this action is the default, + /// and turns the action's text to bold text. + isDefaultAction: true, + onPressed: () { + Navigator.pop(context); + }, + child: const Text('Annuler'), + ), + CupertinoDialogAction( + /// This parameter indicates the action would perform + /// a destructive action such as deletion, and turns + /// the action's text color to red. + isDestructiveAction: true, + onPressed: () async { + Navigator.pop(context); + await MyApp.musicViewModel + .addOrDeleteFavoriteMusic((snapshot.data?[index])!.id); + MyApp.userViewModel.userCurrent.musics_likes + .remove((snapshot.data?[index])!.id); + setState(() {}); + }, + child: const Text('Supprimer'), + ), + ], + ), + ); }, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 20), child: MusicListComponent( music: (snapshot.data?[index])!, - playing: true, + playing: false, callback: playMusic, index: index, ), )); - } - return InkWell( - onTap: () { - widget.callback((snapshot.data?[index])!); - }, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: MusicListComponent( - music: (snapshot.data?[index])!, - playing: false, - callback: playMusic, - index: index, - ), - )); - }); + }); + } } else { return CupertinoActivityIndicator(); }