saved song
continuous-integration/drone/push Build is passing Details

LIKES_POST_LDE-EKA
Lucas Delanier 2 years ago
parent 4036f02391
commit 5d254431db

@ -6,12 +6,14 @@ class User {
String _pp;
String _token;
List<String> _followers;
List<String> _musics_likes;
int _capsules;
List<String> _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 +24,12 @@ class User {
_pseudo = value;
}
List<String> get musics_likes => _musics_likes;
set musics_likes(List<String> value) {
_musics_likes = value;
}
String get uniquePseudo => _uniquePseudo;
set uniquePseudo(String value) {

@ -12,6 +12,7 @@ class UserMapper {
data?["picture"],
data?["token_notify"],
List<String>.from(data?["followers"] as List),
List<String>.from(data?["musics_likes"] as List),
data?["nbCapsules"] ?? 0,
List<String>.from(data?["followed"] as List));
}

@ -45,6 +45,10 @@ class _DetailPostScreenState extends State<DetailPostScreen> {
});
}
bool isSaved() {
return MyApp.userViewModel.userCurrent.musics_likes.contains(widget.post.music.id);
}
@override
void dispose() {
MyApp.audioPlayer.release();
@ -285,17 +289,45 @@ class _DetailPostScreenState extends State<DetailPostScreen> {
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<List<Comment>>(
future: MyApp.commentViewModel.getCommentsByPostId(widget.post.id),
builder:
(BuildContext context, AsyncSnapshot<List<Comment>> 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'),
@ -306,20 +338,23 @@ class _DetailPostScreenState extends State<DetailPostScreen> {
!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),
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>[
TextSpan(
text: "${widget.post.music.title}",
style: TextStyle(fontWeight: FontWeight.bold)),
TextSpan(text: " ajouté à votre collection"),
],
),
),
backgroundColor: primaryColor,
closeIconColor: Colors.white,
@ -327,28 +362,35 @@ class _DetailPostScreenState extends State<DetailPostScreen> {
)
: 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",
content: RichText(
textAlign: TextAlign.center,
style: GoogleFonts.plusJakartaSans(
color: Colors.white,
fontWeight: FontWeight.w400,
fontSize: 15),
maxLines: 1,
overflow: TextOverflow.ellipsis,
text: TextSpan(
style: GoogleFonts.plusJakartaSans(
color: Colors.white,
fontWeight: FontWeight.w400,
fontSize: 15,
),
children: <TextSpan>[
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')),
child: SvgPicture.asset(
"assets/images/save.svg",
semanticsLabel: 'Save Logo',
color: isSaved() ? primaryColor : Colors.white,
)),
SvgPicture.asset("assets/images/report.svg", semanticsLabel: 'Report Logo'),
],
),
@ -359,31 +401,6 @@ class _DetailPostScreenState extends State<DetailPostScreen> {
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),

@ -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<SearchSongScreen> {
final ScrollController _scrollController = ScrollController();
final TextEditingController _textEditingController = TextEditingController();
PageController controller = PageController();
int? playingIndex;
@ -81,6 +83,10 @@ class _SearchSongScreenState extends State<SearchSongScreen> {
}
}
Future<List<Music>> _fetchSavedSong() async {
return await MyApp.musicViewModel.getFavoriteMusicsByUserId(MyApp.userViewModel.userCurrent.id);
}
@override
void dispose() {
MyApp.audioPlayer.pause();
@ -139,6 +145,7 @@ class _SearchSongScreenState extends State<SearchSongScreen> {
filteredData = filteredData;
});
}
controller.animateTo(0, duration: Duration(milliseconds: 200), curve: Curves.easeIn);
},
cursorColor: Colors.white,
keyboardType: TextInputType.text,
@ -166,6 +173,7 @@ class _SearchSongScreenState extends State<SearchSongScreen> {
),
Flexible(
child: PageView(
controller: controller,
physics: BouncingScrollPhysics(),
children: [
ScrollConfiguration(
@ -207,40 +215,49 @@ class _SearchSongScreenState extends State<SearchSongScreen> {
),
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: FutureBuilder(
future: _fetchSavedSong(),
builder: (BuildContext context, AsyncSnapshot<List<Music>> 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();
}
},
),
)
],
))

@ -17,14 +17,17 @@ class MusicService {
var userRef = await FirebaseFirestore.instance.collection("users").doc(MyApp.userViewModel.userCurrent.id);
var response = await userRef.get();
List<dynamic> musicFavorite = List.from(response.get("saved_musics"));
List<String> musicFavorite = List.from(response.get("musics_likes"));
if (!musicFavorite.contains(id)) {
musicFavorite.add(id);
await userRef.update({"saved_musics": musicFavorite});
await userRef.update({"musics_likes": musicFavorite});
MyApp.userViewModel.userCurrent.musics_likes.add(id);
return false;
} else {
musicFavorite.remove(id);
await userRef.update({"saved_musics": musicFavorite});
await userRef.update({"musics_likes": musicFavorite});
MyApp.userViewModel.userCurrent.musics_likes.remove(id);
return true;
}
}

@ -2,6 +2,7 @@ import 'dart:convert';
import 'package:justmusic/view_model/TokenSpotify.dart';
import 'package:http/http.dart' as http;
import '../main.dart';
import '../model/Artist.dart';
import '../model/Music.dart';
import '../services/MusicService.dart';
@ -27,8 +28,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}');
}
}
@ -48,15 +48,12 @@ class MusicViewModel {
artists);
}
Future<List<Music>> getMusicsWithName(String name,
{int limit = 20, int offset = 0, String market = "FR"}) async {
Future<List<Music>> 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<String, dynamic> responseData = jsonDecode(response.body);
@ -64,20 +61,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<List<Music>> 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<String, dynamic> responseData = jsonDecode(response.body);
@ -85,24 +79,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<Artist> 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<Artist> artists =
List<Artist>.from(responseData['artists']['items'].map((artist) {
List<Artist> artists = List<Artist>.from(responseData['artists']['items'].map((artist) {
String image = '';
if (!artist['images'].isEmpty) {
image = artist['images'][0]['url'];
@ -118,25 +107,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<List<Artist>> getArtistsWithName(String name,
{int limit = 20, int offset = 0, String market = "FR"}) async {
Future<List<Artist>> 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<Artist> artists =
List<Artist>.from(responseData['artists']['items'].map((artist) {
List<Artist> artists = List<Artist>.from(responseData['artists']['items'].map((artist) {
String image = '';
if (!artist['images'].isEmpty) {
image = artist['images'][0]['url'];
@ -146,19 +131,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<List<Music>> getTopMusicsWithArtistId(String id,
{String market = "FR"}) async {
Future<List<Music>> 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<String, dynamic> responseData = jsonDecode(response.body);
@ -166,16 +147,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<List<Music>> getMusicsWithPlaylistId(String id,
{String market = "FR"}) async {
Future<List<Music>> 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',
});
@ -191,13 +169,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<List<Music>> getMusicsWithIds(List<String> ids,
{String market = "FR"}) async {
Future<List<Music>> getMusicsWithIds(List<String> ids, {String market = "FR"}) async {
var accessToken = await _token.getAccessToken();
String url = API_URL + '/tracks?market=$market&ids=';
@ -215,8 +191,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}');
}
}
@ -226,18 +201,21 @@ class MusicViewModel {
List<Music> 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<List<Music>> getFavoriteMusicsByUserId(String id) async {
try {
var idMusics = await _musicService.getFavoriteMusicsByUserId(id);
List<String> 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);

Loading…
Cancel
Save