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 4ac0733..a82c39d 100644 --- a/Sources/justMUSIC/lib/main.dart +++ b/Sources/justMUSIC/lib/main.dart @@ -29,6 +29,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/model/User.dart b/Sources/justMUSIC/lib/model/User.dart index d88a05e..8ee6e5e 100644 --- a/Sources/justMUSIC/lib/model/User.dart +++ b/Sources/justMUSIC/lib/model/User.dart @@ -6,12 +6,13 @@ class User { String _pp; String _token; List _followers; + List _musics_likes; int _capsules; List _followed; // Constructor - User(this._id, this._pseudo, this._uniquePseudo, this._mail, this._pp, - this._token, this._followers, this._capsules, this._followed); + User(this._id, this._pseudo, this._uniquePseudo, this._mail, this._pp, this._token, this._followers, + this._musics_likes, this._capsules, this._followed); //Getters and setters String get id => _id; @@ -22,6 +23,12 @@ class User { _pseudo = value; } + List get musics_likes => _musics_likes; + + set musics_likes(List value) { + _musics_likes = value; + } + String get uniquePseudo => _uniquePseudo; set uniquePseudo(String value) { diff --git a/Sources/justMUSIC/lib/model/mapper/UserMapper.dart b/Sources/justMUSIC/lib/model/mapper/UserMapper.dart index 7d0c5ab..e21ff5c 100644 --- a/Sources/justMUSIC/lib/model/mapper/UserMapper.dart +++ b/Sources/justMUSIC/lib/model/mapper/UserMapper.dart @@ -12,6 +12,7 @@ class UserMapper { data?["picture"], data?["token_notify"], List.from(data?["followers"] as List), + List.from(data?["musics_likes"] as List), data?["nbCapsules"] ?? 0, List.from(data?["followed"] as List)); } diff --git a/Sources/justMUSIC/lib/screens/detail_post_screen.dart b/Sources/justMUSIC/lib/screens/detail_post_screen.dart index 47b6f8a..373ce89 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'; @@ -44,6 +45,10 @@ class _DetailPostScreenState extends State { }); } + bool isSaved() { + return MyApp.userViewModel.userCurrent.musics_likes.contains(widget.post.music.id); + } + @override void dispose() { MyApp.audioPlayer.release(); @@ -89,154 +94,164 @@ class _DetailPostScreenState extends State { child: Container( height: 400, width: double.infinity, - child: FadeInImage.assetNetwork( - fit: BoxFit.cover, - image: choice ? widget.post.selfie! : widget.post.music.cover!, - fadeInDuration: const Duration(milliseconds: 100), - placeholder: "assets/images/loadingPlaceholder.gif", - ), + 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)), - ), - ), - ], + ], + ), ), - ), - ], + ], + ), ), ), ), @@ -274,21 +289,108 @@ class _DetailPostScreenState extends State { child: Column( children: [ Padding( - padding: EdgeInsets.symmetric(vertical: 20), + padding: EdgeInsets.only(top: 30, bottom: 20), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.start, children: [ SvgPicture.asset("assets/images/heart.svg", semanticsLabel: 'Like Logo'), - GestureDetector( - onTap: () { - myFocusNode.requestFocus(); - }, - child: SvgPicture.asset("assets/images/chat.svg", - semanticsLabel: 'Chat Logo'), + Column( + children: [ + GestureDetector( + onTap: () { + myFocusNode.requestFocus(); + }, + child: SvgPicture.asset("assets/images/chat.svg", + semanticsLabel: 'Chat Logo'), + ), + Container( + padding: EdgeInsets.only(top: 8), + height: 30, + child: FutureBuilder>( + future: MyApp.commentViewModel.getCommentsByPostId(widget.post.id), + builder: + (BuildContext context, AsyncSnapshot> snapshot) { + if (snapshot.hasData) { + return Text(snapshot.data!.length.toString(), + style: GoogleFonts.plusJakartaSans( + color: Colors.white, + fontWeight: FontWeight.w800, + )); + } else { + return Container( + child: Center( + child: CupertinoActivityIndicator(), + ), + ); + } + }, + ), + ) + ], ), SvgPicture.asset("assets/images/add.svg", semanticsLabel: 'Add playlist Logo'), - SvgPicture.asset("assets/images/save.svg", semanticsLabel: 'Save Logo'), + GestureDetector( + onTap: () async { + var bool = await MyApp.musicViewModel + .addOrDeleteFavoriteMusic(widget.post.music.id); + !bool + ? ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: RichText( + textAlign: TextAlign.center, + maxLines: 1, + overflow: TextOverflow.ellipsis, + text: TextSpan( + style: GoogleFonts.plusJakartaSans( + color: Colors.white, + fontWeight: FontWeight.w400, + fontSize: 15, + ), + children: [ + TextSpan( + text: "${widget.post.music.title}", + style: TextStyle(fontWeight: FontWeight.bold)), + TextSpan(text: " ajouté à votre collection"), + ], + ), + ), + backgroundColor: primaryColor, + closeIconColor: Colors.white, + ), + ) + : ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: RichText( + textAlign: TextAlign.center, + maxLines: 1, + overflow: TextOverflow.ellipsis, + text: TextSpan( + style: GoogleFonts.plusJakartaSans( + color: Colors.white, + fontWeight: FontWeight.w400, + fontSize: 15, + ), + children: [ + TextSpan( + text: "${widget.post.music.title}", + style: TextStyle(fontWeight: FontWeight.bold)), + TextSpan(text: " retiré de votre collection"), + ], + ), + ), + backgroundColor: Colors.red, + closeIconColor: Colors.white, + ), + ); + setState(() {}); + }, + child: SvgPicture.asset( + "assets/images/save.svg", + semanticsLabel: 'Save Logo', + color: isSaved() ? primaryColor : Colors.white, + )), SvgPicture.asset("assets/images/report.svg", semanticsLabel: 'Report Logo'), ], ), @@ -299,31 +401,6 @@ class _DetailPostScreenState extends State { if (snapshot.hasData) { return Column( children: [ - snapshot.data!.length > 0 - ? Padding( - padding: const EdgeInsets.all(15.0), - child: RichText( - text: TextSpan( - text: snapshot.data!.length.toString(), - style: GoogleFonts.plusJakartaSans( - color: Colors.white, - fontWeight: FontWeight.w800, - ), - children: [ - TextSpan( - text: snapshot.data!.length > 1 - ? " commentaires" - : " commentaire", - style: GoogleFonts.plusJakartaSans( - color: Colors.white, - fontWeight: FontWeight.w400, - ), - ), - ], - ), - ), - ) - : Container(), snapshot.data!.length > 0 ? Padding( padding: const EdgeInsets.fromLTRB(20, 0, 20, 20), 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..05353df 100644 --- a/Sources/justMUSIC/lib/screens/search_song_screen.dart +++ b/Sources/justMUSIC/lib/screens/search_song_screen.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:ui'; import 'package:flutter/Material.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:justmusic/model/Music.dart'; @@ -21,6 +22,7 @@ class SearchSongScreen extends StatefulWidget { class _SearchSongScreenState extends State { final ScrollController _scrollController = ScrollController(); final TextEditingController _textEditingController = TextEditingController(); + PageController controller = PageController(); int? playingIndex; @@ -81,6 +83,10 @@ class _SearchSongScreenState extends State { } } + Future> _fetchSavedSong() async { + return await MyApp.musicViewModel.getFavoriteMusicsByUserId(MyApp.userViewModel.userCurrent.id); + } + @override void dispose() { MyApp.audioPlayer.pause(); @@ -124,7 +130,6 @@ class _SearchSongScreenState extends State { child: SizedBox( height: 40, child: TextField( - autofocus: true, controller: _textEditingController, keyboardAppearance: Brightness.dark, onEditingComplete: resetFullScreen, @@ -132,11 +137,15 @@ class _SearchSongScreenState extends State { if (_textEditingController.text.isEmpty) { fetchTrendingMusic(); } else { + setState(() { + filteredData = []; + }); filteredData = await MyApp.musicViewModel.getMusicsWithNameOrArtistName(value); setState(() { filteredData = filteredData; }); } + controller.animateTo(0, duration: Duration(milliseconds: 200), curve: Curves.easeIn); }, cursorColor: Colors.white, keyboardType: TextInputType.text, @@ -163,42 +172,94 @@ 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( + controller: controller, + 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: FutureBuilder( + 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) { + return InkWell( + onTap: () { + widget.callback(filteredData[index]); + }, + 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((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(); + } + }, + ), + ) + ], )) ], ), diff --git a/Sources/justMUSIC/lib/services/AuthService.dart b/Sources/justMUSIC/lib/services/AuthService.dart index f4a1fdd..20de554 100644 --- a/Sources/justMUSIC/lib/services/AuthService.dart +++ b/Sources/justMUSIC/lib/services/AuthService.dart @@ -88,10 +88,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++; @@ -103,8 +101,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'); @@ -137,8 +134,7 @@ 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(); diff --git a/Sources/justMUSIC/lib/services/MusicService.dart b/Sources/justMUSIC/lib/services/MusicService.dart index 8d4f4f6..307177e 100644 --- a/Sources/justMUSIC/lib/services/MusicService.dart +++ b/Sources/justMUSIC/lib/services/MusicService.dart @@ -5,45 +5,30 @@ 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("musics_likes")); + if (!musicFavorite.contains(id)) { musicFavorite.add(id); await userRef.update({"musics_likes": musicFavorite}); + MyApp.userViewModel.userCurrent.musics_likes.add(id); return false; } else { musicFavorite.remove(id); await userRef.update({"musics_likes": musicFavorite}); + MyApp.userViewModel.userCurrent.musics_likes.remove(id); return true; } } diff --git a/Sources/justMUSIC/lib/view_model/MusicViewModel.dart b/Sources/justMUSIC/lib/view_model/MusicViewModel.dart index 1e2c531..57fa601 100644 --- a/Sources/justMUSIC/lib/view_model/MusicViewModel.dart +++ b/Sources/justMUSIC/lib/view_model/MusicViewModel.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:justmusic/view_model/TokenSpotify.dart'; import 'package:http/http.dart' as http; import 'package:tuple/tuple.dart'; +import '../main.dart'; import '../model/Artist.dart'; import '../model/Music.dart'; import '../services/MusicService.dart'; @@ -28,8 +29,7 @@ class MusicViewModel { return _getMusicFromResponse(responseData); } else { - throw Exception( - 'Error retrieving music information : ${response.statusCode} ${response.reasonPhrase}'); + throw Exception('Error retrieving music information : ${response.statusCode} ${response.reasonPhrase}'); } } @@ -49,15 +49,12 @@ class MusicViewModel { artists); } - Future> getMusicsWithName(String name, - {int limit = 20, int offset = 0, String market = "FR"}) async { + Future> getMusicsWithName(String name, {int limit = 20, int offset = 0, String market = "FR"}) async { var accessToken = await _token.getAccessToken(); - var response = await http.get( - Uri.parse( - '$API_URL/search?q=track%3A$name&type=track&market=fr&limit=$limit&offset=$offset'), - headers: { - 'Authorization': 'Bearer $accessToken', - }); + var response = await http + .get(Uri.parse('$API_URL/search?q=track%3A$name&type=track&market=fr&limit=$limit&offset=$offset'), headers: { + 'Authorization': 'Bearer $accessToken', + }); if (response.statusCode == 200) { Map responseData = jsonDecode(response.body); @@ -65,20 +62,17 @@ class MusicViewModel { return _getMusicFromResponse(track); })); } else { - throw Exception( - 'Error while retrieving music : ${response.statusCode} ${response.reasonPhrase}'); + throw Exception('Error while retrieving music : ${response.statusCode} ${response.reasonPhrase}'); } } Future> getMusicsWithArtistName(String name, {int limit = 20, int offset = 0, String market = "FR"}) async { var accessToken = await _token.getAccessToken(); - var response = await http.get( - Uri.parse( - '$API_URL/search?q=artist%3A$name&type=track&market=fr&limit=$limit&offset=$offset'), - headers: { - 'Authorization': 'Bearer $accessToken', - }); + var response = await http + .get(Uri.parse('$API_URL/search?q=artist%3A$name&type=track&market=fr&limit=$limit&offset=$offset'), headers: { + 'Authorization': 'Bearer $accessToken', + }); if (response.statusCode == 200) { Map responseData = jsonDecode(response.body); @@ -86,24 +80,19 @@ class MusicViewModel { return _getMusicFromResponse(track); })); } else { - throw Exception( - 'Error while retrieving music : ${response.statusCode} ${response.reasonPhrase}'); + throw Exception('Error while retrieving music : ${response.statusCode} ${response.reasonPhrase}'); } } Future getArtistWithName(String name, {String market = "FR"}) async { var accessToken = await _token.getAccessToken(); - var response = await http.get( - Uri.parse( - '$API_URL/search?q=artist%3A$name&type=artist&market=$market'), - headers: { - 'Authorization': 'Bearer $accessToken', - }); + var response = await http.get(Uri.parse('$API_URL/search?q=artist%3A$name&type=artist&market=$market'), headers: { + 'Authorization': 'Bearer $accessToken', + }); if (response.statusCode == 200) { final responseData = jsonDecode(response.body); - List artists = - List.from(responseData['artists']['items'].map((artist) { + List artists = List.from(responseData['artists']['items'].map((artist) { String image = ''; if (!artist['images'].isEmpty) { image = artist['images'][0]['url']; @@ -119,25 +108,21 @@ class MusicViewModel { throw Exception('Artist not found : ${name}'); } else { - throw Exception( - 'Error retrieving artist information : ${response.statusCode} ${response.reasonPhrase}'); + throw Exception('Error retrieving artist information : ${response.statusCode} ${response.reasonPhrase}'); } } - Future> getArtistsWithName(String name, - {int limit = 20, int offset = 0, String market = "FR"}) async { + Future> getArtistsWithName(String name, {int limit = 20, int offset = 0, String market = "FR"}) async { var accessToken = await _token.getAccessToken(); var response = await http.get( - Uri.parse( - '$API_URL/search?q=artist%3A$name&type=artist&market=$market&limit=$limit&offset=$offset'), + Uri.parse('$API_URL/search?q=artist%3A$name&type=artist&market=$market&limit=$limit&offset=$offset'), headers: { 'Authorization': 'Bearer $accessToken', }); if (response.statusCode == 200) { final responseData = jsonDecode(response.body); - List artists = - List.from(responseData['artists']['items'].map((artist) { + List artists = List.from(responseData['artists']['items'].map((artist) { String image = ''; if (!artist['images'].isEmpty) { image = artist['images'][0]['url']; @@ -147,19 +132,15 @@ class MusicViewModel { return artists; } else { - throw Exception( - 'Error while retrieving artist : ${response.statusCode} ${response.reasonPhrase}'); + throw Exception('Error while retrieving artist : ${response.statusCode} ${response.reasonPhrase}'); } } - Future> getTopMusicsWithArtistId(String id, - {String market = "FR"}) async { + Future> getTopMusicsWithArtistId(String id, {String market = "FR"}) async { var accessToken = await _token.getAccessToken(); - var response = await http.get( - Uri.parse('$API_URL/artists/$id/top-tracks?market=$market'), - headers: { - 'Authorization': 'Bearer $accessToken', - }); + var response = await http.get(Uri.parse('$API_URL/artists/$id/top-tracks?market=$market'), headers: { + 'Authorization': 'Bearer $accessToken', + }); if (response.statusCode == 200) { Map responseData = jsonDecode(response.body); @@ -167,16 +148,13 @@ class MusicViewModel { return _getMusicFromResponse(track); })); } else { - throw Exception( - 'Error while retrieving music : ${response.statusCode} ${response.reasonPhrase}'); + throw Exception('Error while retrieving music : ${response.statusCode} ${response.reasonPhrase}'); } } - Future> getMusicsWithPlaylistId(String id, - {String market = "FR"}) async { + Future> getMusicsWithPlaylistId(String id, {String market = "FR"}) async { var accessToken = await _token.getAccessToken(); - var response = await http - .get(Uri.parse('$API_URL/playlists/$id?market=$market'), headers: { + var response = await http.get(Uri.parse('$API_URL/playlists/$id?market=$market'), headers: { 'Authorization': 'Bearer $accessToken', }); @@ -192,13 +170,11 @@ class MusicViewModel { return musics; } else { - throw Exception( - 'Error while retrieving music : ${response.statusCode} ${response.reasonPhrase}'); + throw Exception('Error while retrieving music : ${response.statusCode} ${response.reasonPhrase}'); } } - Future> getMusicsWithIds(List ids, - {String market = "FR"}) async { + Future> getMusicsWithIds(List ids, {String market = "FR"}) async { var accessToken = await _token.getAccessToken(); String url = API_URL + '/tracks?market=$market&ids='; @@ -216,8 +192,7 @@ class MusicViewModel { return _getMusicFromResponse(track); })); } else { - throw Exception( - 'Error while retrieving music : ${response.statusCode} ${response.reasonPhrase}'); + throw Exception('Error while retrieving music : ${response.statusCode} ${response.reasonPhrase}'); } } @@ -227,18 +202,21 @@ class MusicViewModel { List musics = []; Artist artist = await getArtistWithName(name, market: market); musics.addAll(await getTopMusicsWithArtistId(artist.id)); - musics.addAll(await getMusicsWithName(name, - limit: limit, offset: offset, market: market)); + musics.addAll(await getMusicsWithName(name, limit: limit, offset: offset, market: market)); return musics; } catch (e) { - return await getMusicsWithName(name, - limit: limit, offset: offset, market: market); + return await getMusicsWithName(name, limit: limit, offset: offset, market: market); } } Future> getFavoriteMusicsByUserId(String id) async { try { - var idMusics = await _musicService.getFavoriteMusicsByUserId(id); + List idMusics = []; + if (id == MyApp.userViewModel.userCurrent.id) { + idMusics = MyApp.userViewModel.userCurrent.musics_likes; + } else { + idMusics = await _musicService.getFavoriteMusicsByUserId(id); + } return await getMusicsWithIds(idMusics); } catch (e) { print(e);