diff --git a/Sources/justMUSIC/assets/images/saved.png b/Sources/justMUSIC/assets/images/saved.png new file mode 100644 index 0000000..1fe5ae9 Binary files /dev/null and b/Sources/justMUSIC/assets/images/saved.png differ diff --git a/Sources/justMUSIC/lib/main.dart b/Sources/justMUSIC/lib/main.dart index 391db40..2da3a66 100644 --- a/Sources/justMUSIC/lib/main.dart +++ b/Sources/justMUSIC/lib/main.dart @@ -11,13 +11,11 @@ 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/loading_screen.dart'; import 'package:justmusic/screens/login_screen.dart'; import 'package:justmusic/screens/launching_rocker_screen.dart'; import 'package:justmusic/screens/post_screen.dart'; import 'package:justmusic/screens/profile_screen.dart'; import 'package:justmusic/screens/registration_screen.dart'; -import 'package:justmusic/screens/user_screen.dart'; import 'package:justmusic/screens/welcome_screen.dart'; import 'package:justmusic/view_model/CommentViewModel.dart'; import 'package:justmusic/view_model/MusicViewModel.dart'; @@ -29,6 +27,7 @@ import 'package:timezone/data/latest.dart' as tz; Future main() async { tz.initializeTimeZones(); + Paint.enableDithering = true; WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, diff --git a/Sources/justMUSIC/lib/model/Music.dart b/Sources/justMUSIC/lib/model/Music.dart index ed9d9f4..48862ab 100644 --- a/Sources/justMUSIC/lib/model/Music.dart +++ b/Sources/justMUSIC/lib/model/Music.dart @@ -14,11 +14,11 @@ class Music { List _artists; // Constructor - Music(this._id, this._title, this._cover, this._previewUrl, this._date, - this._duration, this._explicit, this._artists); + Music( + this._id, this._title, this._cover, this._previewUrl, this._date, this._duration, this._explicit, this._artists); //Getters and setters - String? get id => _id; + String get id => _id; String? get title => _title; diff --git a/Sources/justMUSIC/lib/screens/detail_post_screen.dart b/Sources/justMUSIC/lib/screens/detail_post_screen.dart index 59298f4..fd9372d 100644 --- a/Sources/justMUSIC/lib/screens/detail_post_screen.dart +++ b/Sources/justMUSIC/lib/screens/detail_post_screen.dart @@ -5,6 +5,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_svg/svg.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:pinch_zoom/pinch_zoom.dart'; import 'package:text_scroll/text_scroll.dart'; import 'package:zoom_tap_animation/zoom_tap_animation.dart'; import '../components/button_play_component.dart'; @@ -89,154 +90,164 @@ class _DetailPostScreenState extends State { child: Container( height: 400, width: double.infinity, - child: FadeInImage.assetNetwork( - placeholder: "assets/images/loadingPlaceholder.gif", - image: choice ? widget.post.selfie! : widget.post.music.cover!, - width: double.infinity, - fit: BoxFit.cover, - ), + child: PinchZoom( + resetDuration: const Duration(milliseconds: 400), + maxScale: 2.5, + child: FadeInImage.assetNetwork( + placeholder: "assets/images/loadingPlaceholder.gif", + image: choice ? widget.post.selfie! : widget.post.music.cover!, + width: double.infinity, + fit: BoxFit.cover, + )), ), ), Column( children: [ - Container( - height: 200, - margin: EdgeInsets.only(top: 230), - width: double.infinity, - decoration: const BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [Colors.transparent, bgModal], - stops: [0, 0.8], + IgnorePointer( + child: Container( + height: 200, + margin: EdgeInsets.only(top: 230), + width: double.infinity, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.transparent, + bgModal.withOpacity(0.5), + bgModal.withOpacity(0.75), + bgModal + ], + stops: [0, 0.2, 0.4, 0.8], + ), ), - ), - child: Padding( - padding: const EdgeInsets.fromLTRB(20, 0, 20, 10), - child: Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Padding( - padding: const EdgeInsets.only(right: 10), - child: choice - ? Padding( - padding: const EdgeInsets.all(4), - child: ClipOval( - child: SizedBox.fromSize( - // Image radius - child: ProfilPictureComponent(user: widget.post.user), + child: Padding( + padding: const EdgeInsets.fromLTRB(20, 0, 20, 10), + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Padding( + padding: const EdgeInsets.only(right: 10), + child: choice + ? Padding( + padding: const EdgeInsets.all(4), + child: ClipOval( + child: SizedBox.fromSize( + // Image radius + child: ProfilPictureComponent(user: widget.post.user), + ), ), + ) + : widget.post.music.previewUrl != null + ? ButtonPlayComponent(music: widget.post.music) + : Container(), + ), + Flexible( + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Flexible( + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Expanded( + child: ScrollConfiguration( + behavior: ScrollBehavior().copyWith(scrollbars: false), + child: TextScroll( + choice ? widget.post.user.pseudo : widget.post.music.title!, + style: GoogleFonts.plusJakartaSans( + height: 1, + color: Colors.white, + fontWeight: FontWeight.w800, + fontSize: 22, + ), + mode: TextScrollMode.endless, + pauseBetween: Duration(milliseconds: 500), + velocity: Velocity(pixelsPerSecond: Offset(20, 0)), + ), + ), + ), + Padding( + padding: const EdgeInsets.only(left: 20.0), + child: choice + ? DateTime(today.year, today.month, today.day) + .isAtSameMomentAs( + DateTime( + widget.post.date.year, + widget.post.date.month, + widget.post.date.day, + ), + ) + ? Text( + "Aujourd'hui, ${widget.post.date.hour}:${widget.post.date.minute}", + style: GoogleFonts.plusJakartaSans( + height: 1, + color: Colors.white, + fontWeight: FontWeight.w900, + fontSize: 18, + ), + ) + : Text( + "hier, ${widget.post.date.hour}:${widget.post.date.minute}", + style: GoogleFonts.plusJakartaSans( + height: 1, + color: Colors.white, + fontWeight: FontWeight.w900, + fontSize: 18, + ), + ) + : Text( + widget.post.music.date.toString(), + style: GoogleFonts.plusJakartaSans( + height: 1, + color: Colors.white, + fontWeight: FontWeight.w900, + fontSize: 18, + ), + ), + ), + ], ), - ) - : widget.post.music.previewUrl != null - ? ButtonPlayComponent(music: widget.post.music) - : Container(), - ), - Flexible( - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Flexible( - child: Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Expanded( - child: ScrollConfiguration( + ), + choice + ? widget.post.location.item2 != null + ? Text( + "${widget.post.location.item1}, ${widget.post.location.item2}", + style: GoogleFonts.plusJakartaSans( + color: Colors.white.withOpacity(0.5), + fontWeight: FontWeight.w400, + fontSize: 15, + ), + ) + : Text( + "", + style: GoogleFonts.plusJakartaSans( + color: Colors.white.withOpacity(0.4), + fontWeight: FontWeight.w300, + fontSize: 13, + ), + ) + : ScrollConfiguration( behavior: ScrollBehavior().copyWith(scrollbars: false), child: TextScroll( - choice ? widget.post.user.pseudo : widget.post.music.title!, + widget.post.music.artists.first.name!, style: GoogleFonts.plusJakartaSans( height: 1, color: Colors.white, - fontWeight: FontWeight.w800, - fontSize: 22, + fontWeight: FontWeight.w500, + fontSize: 17, ), mode: TextScrollMode.endless, pauseBetween: Duration(milliseconds: 500), velocity: Velocity(pixelsPerSecond: Offset(20, 0)), ), ), - ), - Padding( - padding: const EdgeInsets.only(left: 20.0), - child: choice - ? DateTime(today.year, today.month, today.day) - .isAtSameMomentAs( - DateTime( - widget.post.date.year, - widget.post.date.month, - widget.post.date.day, - ), - ) - ? Text( - "Aujourd'hui, ${widget.post.date.hour}:${widget.post.date.minute}", - style: GoogleFonts.plusJakartaSans( - height: 1, - color: Colors.white, - fontWeight: FontWeight.w900, - fontSize: 18, - ), - ) - : Text( - "hier, ${widget.post.date.hour}:${widget.post.date.minute}", - style: GoogleFonts.plusJakartaSans( - height: 1, - color: Colors.white, - fontWeight: FontWeight.w900, - fontSize: 18, - ), - ) - : Text( - widget.post.music.date.toString(), - style: GoogleFonts.plusJakartaSans( - height: 1, - color: Colors.white, - fontWeight: FontWeight.w900, - fontSize: 18, - ), - ), - ), - ], - ), - ), - choice - ? widget.post.location.item2 != null - ? Text( - "${widget.post.location.item1}, ${widget.post.location.item2}", - style: GoogleFonts.plusJakartaSans( - color: Colors.white.withOpacity(0.5), - fontWeight: FontWeight.w400, - fontSize: 15, - ), - ) - : Text( - "", - style: GoogleFonts.plusJakartaSans( - color: Colors.white.withOpacity(0.4), - fontWeight: FontWeight.w300, - fontSize: 13, - ), - ) - : ScrollConfiguration( - behavior: ScrollBehavior().copyWith(scrollbars: false), - child: TextScroll( - widget.post.music.artists.first.name!, - style: GoogleFonts.plusJakartaSans( - height: 1, - color: Colors.white, - fontWeight: FontWeight.w500, - fontSize: 17, - ), - mode: TextScrollMode.endless, - pauseBetween: Duration(milliseconds: 500), - velocity: Velocity(pixelsPerSecond: Offset(20, 0)), - ), - ), - ], + ], + ), ), - ), - ], + ], + ), ), ), ), @@ -289,8 +300,52 @@ class _DetailPostScreenState extends State { SvgPicture.asset("assets/images/add.svg", semanticsLabel: 'Add playlist Logo'), GestureDetector( - onTap: () { - MyApp.musicViewModel. + onTap: () async { + var bool = await MyApp.musicViewModel + .addOrDeleteFavoriteMusic(widget.post.music.id); + !bool + ? ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + shape: RoundedRectangleBorder( + borderRadius: + new BorderRadius.all(new Radius.circular(300.0)), + ), + behavior: SnackBarBehavior.floating, + content: Text( + "${widget.post.music.title} ajouté à votre collection", + style: GoogleFonts.plusJakartaSans( + color: Colors.white, + fontWeight: FontWeight.w400, + fontSize: 15), + textAlign: TextAlign.center, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + backgroundColor: primaryColor, + closeIconColor: Colors.white, + ), + ) + : ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + behavior: SnackBarBehavior.floating, + shape: RoundedRectangleBorder( + borderRadius: + new BorderRadius.all(new Radius.circular(300.0)), + ), + content: Text( + "${widget.post.music.title} retiré de votre collection", + textAlign: TextAlign.center, + style: GoogleFonts.plusJakartaSans( + color: Colors.white, + fontWeight: FontWeight.w400, + fontSize: 15), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + backgroundColor: Colors.red, + closeIconColor: Colors.white, + ), + ); }, child: SvgPicture.asset("assets/images/save.svg", semanticsLabel: 'Save Logo')), diff --git a/Sources/justMUSIC/lib/screens/feed_screen.dart b/Sources/justMUSIC/lib/screens/feed_screen.dart index 4382192..4ad8120 100644 --- a/Sources/justMUSIC/lib/screens/feed_screen.dart +++ b/Sources/justMUSIC/lib/screens/feed_screen.dart @@ -91,7 +91,14 @@ class _FeedScreenState extends State with SingleTickerProviderStateM builder: ((BuildContext context) { return ClipRRect( borderRadius: BorderRadius.only(topLeft: Radius.circular(20), topRight: Radius.circular(20)), - child: DetailPostScreen(post: post)); + child: SizedBox( + height: 760.h, + child: Scaffold( + primary: false, + extendBody: false, + backgroundColor: Colors.transparent, + body: DetailPostScreen(post: post)), + )); }), ); } diff --git a/Sources/justMUSIC/lib/screens/post_screen.dart b/Sources/justMUSIC/lib/screens/post_screen.dart index c7ffce9..fb6dbed 100644 --- a/Sources/justMUSIC/lib/screens/post_screen.dart +++ b/Sources/justMUSIC/lib/screens/post_screen.dart @@ -11,7 +11,6 @@ import '../components/editable_post_component.dart'; import '../components/post_button_component.dart'; import '../main.dart'; import '../model/Music.dart'; -import '../model/Post.dart'; import '../values/constants.dart'; class PostScreen extends StatefulWidget { @@ -93,7 +92,7 @@ class _PostScreenState extends State with SingleTickerProviderStateM } handleSubmit() async { - MyApp.postViewModel.addPost(description, (selectedMusic?.id)!, selectedImage, selectedCity); + MyApp.postViewModel.addPost(description, selectedMusic!.id, selectedImage, selectedCity); quit(); } diff --git a/Sources/justMUSIC/lib/screens/search_song_screen.dart b/Sources/justMUSIC/lib/screens/search_song_screen.dart index 8b702de..5972629 100644 --- a/Sources/justMUSIC/lib/screens/search_song_screen.dart +++ b/Sources/justMUSIC/lib/screens/search_song_screen.dart @@ -124,7 +124,6 @@ class _SearchSongScreenState extends State { child: SizedBox( height: 40, child: TextField( - autofocus: true, controller: _textEditingController, keyboardAppearance: Brightness.dark, onEditingComplete: resetFullScreen, @@ -132,6 +131,9 @@ class _SearchSongScreenState extends State { if (_textEditingController.text.isEmpty) { fetchTrendingMusic(); } else { + setState(() { + filteredData = []; + }); filteredData = await MyApp.musicViewModel.getMusicsWithNameOrArtistName(value); setState(() { filteredData = filteredData; @@ -163,42 +165,84 @@ class _SearchSongScreenState extends State { ), ), Flexible( - child: ScrollConfiguration( - behavior: ScrollBehavior().copyWith(scrollbars: true), - child: ListView.builder( - physics: const BouncingScrollPhysics(decelerationRate: ScrollDecelerationRate.fast), - controller: _scrollController, - itemCount: filteredData.length, - itemBuilder: (context, index) { - if (playingIndex == index) { - return InkWell( - onTap: () { - widget.callback(filteredData[index]); - }, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: MusicListComponent( - music: filteredData[index], - playing: true, - callback: playMusic, - index: index, - ), - )); - } - return InkWell( - onTap: () { - widget.callback(filteredData[index]); - }, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: MusicListComponent( - music: filteredData[index], - playing: false, - callback: playMusic, - index: index, - ), - )); - }), + child: PageView( + physics: BouncingScrollPhysics(), + children: [ + ScrollConfiguration( + behavior: ScrollBehavior().copyWith(scrollbars: true), + child: ListView.builder( + physics: const BouncingScrollPhysics(decelerationRate: ScrollDecelerationRate.fast), + controller: _scrollController, + itemCount: filteredData.length, + itemBuilder: (context, index) { + if (playingIndex == index) { + return InkWell( + onTap: () { + widget.callback(filteredData[index]); + }, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: MusicListComponent( + music: filteredData[index], + playing: true, + callback: playMusic, + index: index, + ), + )); + } + return InkWell( + onTap: () { + widget.callback(filteredData[index]); + }, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: MusicListComponent( + music: filteredData[index], + playing: false, + callback: playMusic, + index: index, + ), + )); + }), + ), + ScrollConfiguration( + behavior: ScrollBehavior().copyWith(scrollbars: true), + child: ListView.builder( + physics: const BouncingScrollPhysics(decelerationRate: ScrollDecelerationRate.fast), + controller: _scrollController, + itemCount: filteredData.length, + itemBuilder: (context, index) { + if (playingIndex == index) { + return InkWell( + onTap: () { + widget.callback(filteredData[index]); + }, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: MusicListComponent( + music: filteredData[index], + playing: true, + callback: playMusic, + index: index, + ), + )); + } + return InkWell( + onTap: () { + widget.callback(filteredData[index]); + }, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: MusicListComponent( + music: filteredData[index], + playing: false, + callback: playMusic, + index: index, + ), + )); + }), + ) + ], )) ], ), diff --git a/Sources/justMUSIC/lib/services/AuthService.dart b/Sources/justMUSIC/lib/services/AuthService.dart index 79bb717..c11be8e 100644 --- a/Sources/justMUSIC/lib/services/AuthService.dart +++ b/Sources/justMUSIC/lib/services/AuthService.dart @@ -29,7 +29,7 @@ class AuthService { "nbCapsules": 0, "followers": [], "token_notify": token, - "musics_likes": [], + "saved_musics": [], "picture": "https://firebasestorage.googleapis.com/v0/b/justmusic-435d5.appspot.com/o/justMusicDefaultImage.png?alt=media&token=020d0fcb-b7df-4d4d-b380-e99597293fcc" }; @@ -55,10 +55,8 @@ class AuthService { Future generateUniqueId(String pseudo) async { String uniqueId = '$pseudo#0001'; int suffix = 1; - final CollectionReference usersCollection = - FirebaseFirestore.instance.collection("users"); - final QuerySnapshot querySnapshot = - await usersCollection.where('pseudo', isEqualTo: pseudo).get(); + final CollectionReference usersCollection = FirebaseFirestore.instance.collection("users"); + final QuerySnapshot querySnapshot = await usersCollection.where('pseudo', isEqualTo: pseudo).get(); querySnapshot.docs.forEach((snapshot) { suffix++; @@ -70,8 +68,7 @@ class AuthService { login(String email, String password) async { try { - await FirebaseAuth.instance - .signInWithEmailAndPassword(email: email, password: password); + await FirebaseAuth.instance.signInWithEmailAndPassword(email: email, password: password); } on FirebaseAuthException catch (e) { if (e.code == 'user-not-found') { throw ('Mail incorrect'); @@ -98,12 +95,10 @@ class AuthService { .doc(currentUser?.uid) .delete() .then((value) => print("Firestore deleted user")) - .catchError( - (error) => print("Error deleting user from Firestore: $error")); + .catchError((error) => print("Error deleting user from Firestore: $error")); await currentUser?.delete(); await FirebaseAuth.instance.signOut(); - } on FirebaseAuthException catch (e) { if (e.code == 'requires-recent-login') { throw ('Please log in again to delete your account'); diff --git a/Sources/justMUSIC/lib/services/MusicService.dart b/Sources/justMUSIC/lib/services/MusicService.dart index a100258..46c4ffa 100644 --- a/Sources/justMUSIC/lib/services/MusicService.dart +++ b/Sources/justMUSIC/lib/services/MusicService.dart @@ -4,45 +4,27 @@ import '../main.dart'; class MusicService { Future getFavoriteMusicsByUserId(String id) async { - var response = - await FirebaseFirestore.instance.collection("users").doc(id).get(); + var response = await FirebaseFirestore.instance.collection("users").doc(id).get(); if (response.exists) { - var musicFavorite = response.get("musics_likes"); + var musicFavorite = response.get("saved_musics"); return List.from(musicFavorite); } else { return []; } } - deleteFavoriteMusic(String id) async { - var userRef = await FirebaseFirestore.instance - .collection("users") - .doc(MyApp.userViewModel.userCurrent.id); - var response = await userRef.get(); - - List musicFavorite = List.from(response.get("musics_likes")); - if (!musicFavorite.contains(id)) { - musicFavorite.remove(id); - await userRef.update({"musics_likes": musicFavorite}); - } else { - print("Delete error: The music is not in the user's favorite music list"); - } - } - Future addOrDeleteFavoriteMusic(String id) async { - var userRef = await FirebaseFirestore.instance - .collection("users") - .doc(MyApp.userViewModel.userCurrent.id); + var userRef = await FirebaseFirestore.instance.collection("users").doc(MyApp.userViewModel.userCurrent.id); var response = await userRef.get(); - List musicFavorite = List.from(response.get("musics_likes")); + List musicFavorite = List.from(response.get("saved_musics")); if (!musicFavorite.contains(id)) { musicFavorite.add(id); - await userRef.update({"musics_likes": musicFavorite}); + await userRef.update({"saved_musics": musicFavorite}); return false; } else { musicFavorite.remove(id); - await userRef.update({"musics_likes": musicFavorite}); + await userRef.update({"saved_musics": musicFavorite}); return true; } }