Skip to content

Commit 5d5dbec

Browse files
committed
✅ create Music Play Page 100%
1 parent 044b685 commit 5d5dbec

File tree

13 files changed

+253
-28
lines changed

13 files changed

+253
-28
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import 'package:flutter_bloc/flutter_bloc.dart';
2+
import 'package:spotify_with_flutter/common/bloc/favorite_button/favorite_button_state.dart';
3+
import 'package:spotify_with_flutter/domain/usecase/song/add_or_remove_favorite_song.dart';
4+
import 'package:spotify_with_flutter/service_locator.dart';
5+
6+
class FavoriteButtonCubit extends Cubit<FavoriteButtonState> {
7+
FavoriteButtonCubit() : super(FavoriteButtonInitial());
8+
9+
Future<void> favoriteButtonUpdated(String songId) async {
10+
var result = await sl<AddOrRemoveFavoriteSongUseCase>().call(
11+
params: songId,
12+
);
13+
result.fold(
14+
(l) {},
15+
(isFavorite) {
16+
emit(FavoriteButtonUpdated(
17+
isFavorite: isFavorite,
18+
));
19+
},
20+
);
21+
}
22+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
abstract class FavoriteButtonState {}
2+
3+
class FavoriteButtonInitial extends FavoriteButtonState {}
4+
5+
class FavoriteButtonUpdated extends FavoriteButtonState {
6+
final bool isFavorite;
7+
8+
FavoriteButtonUpdated({
9+
required this.isFavorite,
10+
});
11+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_bloc/flutter_bloc.dart';
3+
import 'package:spotify_with_flutter/common/bloc/favorite_button/favorite_button_cubit.dart';
4+
import 'package:spotify_with_flutter/common/bloc/favorite_button/favorite_button_state.dart';
5+
import 'package:spotify_with_flutter/core/configs/theme/app_color.dart';
6+
import 'package:spotify_with_flutter/domain/entities/songs/songs.dart';
7+
8+
class FavoriteButton extends StatelessWidget {
9+
final SongEntity songEntity;
10+
final double sizeIcons;
11+
12+
const FavoriteButton({
13+
super.key,
14+
required this.songEntity,
15+
this.sizeIcons = 25,
16+
});
17+
18+
@override
19+
Widget build(BuildContext context) {
20+
return BlocProvider(
21+
create: (context) => FavoriteButtonCubit(),
22+
child: BlocBuilder<FavoriteButtonCubit, FavoriteButtonState>(
23+
builder: (context, state) {
24+
if (state is FavoriteButtonInitial) {
25+
return IconButton(
26+
onPressed: () async {
27+
await context.read<FavoriteButtonCubit>().favoriteButtonUpdated(
28+
songEntity.songId,
29+
);
30+
},
31+
icon: Icon(
32+
songEntity.isFavorite ? Icons.favorite : Icons.favorite_outline_outlined,
33+
size: sizeIcons,
34+
color: AppColors.darkGrey,
35+
),
36+
);
37+
}
38+
39+
if (state is FavoriteButtonUpdated) {
40+
return IconButton(
41+
onPressed: () {
42+
context.read<FavoriteButtonCubit>().favoriteButtonUpdated(
43+
songEntity.songId,
44+
);
45+
},
46+
icon: Icon(
47+
state.isFavorite ? Icons.favorite : Icons.favorite_outline_outlined,
48+
size: sizeIcons,
49+
color: AppColors.darkGrey,
50+
),
51+
);
52+
}
53+
54+
return Container();
55+
},
56+
),
57+
);
58+
}
59+
}

lib/data/models/songs/songs.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@ class SongModel {
77
num? duration;
88
Timestamp? releaseDate;
99
num? idImg;
10+
bool? isFavorite;
11+
String? songId;
1012

1113
SongModel.fromJson(Map<String, dynamic> data) {
1214
title = data['title'];
1315
artist = data['artist'];
1416
duration = data['duration'];
1517
releaseDate = data['releaseDate'];
1618
idImg = data['idImg'];
19+
isFavorite = data['isFavorite'];
1720
}
1821
}
1922

@@ -25,6 +28,8 @@ extension SongModelX on SongModel {
2528
duration: duration!,
2629
releaseDate: releaseDate!,
2730
idImg: idImg!,
31+
isFavorite: isFavorite!,
32+
songId: songId!,
2833
);
2934
}
3035
}

lib/data/repository/song/song_repository_impl.dart

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,12 @@ class SongRepositoryImpl extends SongsRepository {
1515
}
1616

1717
@override
18-
Future<Either> addOrRemoveFavoriteSongs() {
19-
// TODO: implement addOrRemoveFavoriteSongs
20-
throw UnimplementedError();
18+
Future<Either> addOrRemoveFavoriteSong(String songId) async {
19+
return await sl<SongFirebaseService>().addOrRemoveFavoriteSong(songId);
20+
}
21+
22+
@override
23+
Future<bool> isFavoriteSong(String songId) async {
24+
return await sl<SongFirebaseService>().isFavoriteSong(songId);
2125
}
2226
}

lib/data/sources/song/song_firebase_service.dart

Lines changed: 103 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,40 @@ import 'package:firebase_auth/firebase_auth.dart';
44
import 'package:flutter/foundation.dart';
55
import 'package:spotify_with_flutter/data/models/songs/songs.dart';
66
import 'package:spotify_with_flutter/domain/entities/songs/songs.dart';
7+
import 'package:spotify_with_flutter/domain/usecase/song/is_favorite_song.dart';
8+
import 'package:spotify_with_flutter/service_locator.dart';
79

810
abstract class SongFirebaseService {
911
Future<Either> getNewsSongs();
1012
Future<Either> getPlayList();
11-
Future<Either> addOrRemoveFavoriteSong();
13+
Future<Either> addOrRemoveFavoriteSong(String songId);
14+
Future<bool> isFavoriteSong(String songId);
1215
}
1316

1417
class SongFirebaseServiceImpl extends SongFirebaseService {
1518
@override
1619
Future<Either> getNewsSongs() async {
1720
try {
1821
List<SongEntity> songs = [];
19-
var data = await FirebaseFirestore.instance.collection('Songs').orderBy('releaseDate', descending: true).limit(3).get();
22+
var data = await FirebaseFirestore.instance
23+
.collection('Songs')
24+
.orderBy(
25+
'releaseDate',
26+
descending: true,
27+
)
28+
.limit(3)
29+
.get();
2030

2131
for (var element in data.docs) {
2232
var songModel = SongModel.fromJson(element.data());
23-
songs.add(songModel.toEntity());
33+
bool isFavorite = await sl<IsFavoriteSongUseCase>().call(
34+
params: element.reference.id,
35+
);
36+
songModel.isFavorite = isFavorite;
37+
songModel.songId = element.reference.id;
38+
songs.add(
39+
songModel.toEntity(),
40+
);
2441
}
2542

2643
return right(songs);
@@ -36,11 +53,26 @@ class SongFirebaseServiceImpl extends SongFirebaseService {
3653
Future<Either> getPlayList() async {
3754
try {
3855
List<SongEntity> songs = [];
39-
var data = await FirebaseFirestore.instance.collection('Songs').orderBy('releaseDate', descending: true).get();
56+
var data = await FirebaseFirestore.instance
57+
.collection('Songs')
58+
.orderBy(
59+
'releaseDate',
60+
descending: true,
61+
)
62+
.get();
4063

4164
for (var element in data.docs) {
4265
var songModel = SongModel.fromJson(element.data());
43-
songs.add(songModel.toEntity());
66+
67+
bool isFavorite = await sl<IsFavoriteSongUseCase>().call(
68+
params: element.reference.id,
69+
);
70+
songModel.isFavorite = isFavorite;
71+
songModel.songId = element.reference.id;
72+
73+
songs.add(
74+
songModel.toEntity(),
75+
);
4476
}
4577

4678
return right(songs);
@@ -53,15 +85,75 @@ class SongFirebaseServiceImpl extends SongFirebaseService {
5385
}
5486

5587
@override
56-
Future<Either> addOrRemoveFavoriteSong() async {
57-
final FirebaseAuth firebaseAuth = FirebaseAuth.instance;
88+
Future<Either> addOrRemoveFavoriteSong(String songId) async {
89+
try {
90+
final FirebaseAuth firebaseAuth = FirebaseAuth.instance;
91+
92+
final FirebaseFirestore firebaseFirestore = FirebaseFirestore.instance;
93+
94+
late bool isFavorite;
5895

59-
final FirebaseFirestore firebaseFirestore = FirebaseFirestore.instance;
96+
var user = await firebaseAuth.currentUser;
6097

61-
var user = await firebaseAuth.currentUser;
98+
String uID = user!.uid;
6299

63-
String uID = us
100+
QuerySnapshot favoriteSongs = await firebaseFirestore
101+
.collection('Users')
102+
.doc(uID)
103+
.collection('Favorites')
104+
.where(
105+
'songId',
106+
isEqualTo: songId,
107+
)
108+
.get();
109+
110+
if (favoriteSongs.docs.isNotEmpty) {
111+
await favoriteSongs.docs.first.reference.delete();
112+
isFavorite = false;
113+
} else {
114+
await firebaseFirestore.collection('Users').doc(uID).collection('Favorites').add(
115+
{
116+
'songId': songId,
117+
'addedDate': Timestamp.now(),
118+
},
119+
);
120+
isFavorite = true;
121+
}
122+
123+
return Right(isFavorite);
124+
} catch (e) {
125+
return const Left('An error occurred');
126+
}
127+
}
64128

65-
129+
@override
130+
Future<bool> isFavoriteSong(String songId) async {
131+
try {
132+
final FirebaseAuth firebaseAuth = FirebaseAuth.instance;
133+
134+
final FirebaseFirestore firebaseFirestore = FirebaseFirestore.instance;
135+
136+
var user = await firebaseAuth.currentUser;
137+
138+
String uID = user!.uid;
139+
140+
QuerySnapshot favoriteSongs = await firebaseFirestore
141+
.collection('Users')
142+
.doc(uID)
143+
.collection('Favorites')
144+
.where(
145+
'songId',
146+
isEqualTo: songId,
147+
)
148+
.get();
149+
150+
if (favoriteSongs.docs.isNotEmpty) {
151+
return true;
152+
} else {
153+
return false;
154+
}
155+
} catch (e) {
156+
return false;
157+
}
66158
}
67159
}

lib/domain/entities/songs/songs.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@ class SongEntity {
66
final num duration;
77
final Timestamp releaseDate;
88
final num idImg;
9+
final bool isFavorite;
10+
final String songId;
911

1012
SongEntity({
1113
required this.title,
1214
required this.artist,
1315
required this.duration,
1416
required this.releaseDate,
1517
required this.idImg,
18+
required this.isFavorite,
19+
required this.songId,
1620
});
1721
}

lib/domain/repository/song/song.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@ import 'package:dartz/dartz.dart';
33
abstract class SongsRepository {
44
Future<Either> getNewsSongs();
55
Future<Either> getPlayList();
6+
Future<Either> addOrRemoveFavoriteSong(String songId);
7+
Future<bool> isFavoriteSong(String songId);
68
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import 'package:dartz/dartz.dart';
2+
import 'package:spotify_with_flutter/core/usecase/usecase.dart';
3+
import 'package:spotify_with_flutter/domain/repository/song/song.dart';
4+
import 'package:spotify_with_flutter/service_locator.dart';
5+
6+
class AddOrRemoveFavoriteSongUseCase implements UseCase<Either, String> {
7+
@override
8+
Future<Either> call({String? params}) async {
9+
return await sl<SongsRepository>().addOrRemoveFavoriteSong(params!);
10+
}
11+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import 'package:spotify_with_flutter/core/usecase/usecase.dart';
2+
import 'package:spotify_with_flutter/domain/repository/song/song.dart';
3+
import 'package:spotify_with_flutter/service_locator.dart';
4+
5+
class IsFavoriteSongUseCase implements UseCase<bool, String> {
6+
@override
7+
Future<bool> call({String? params}) async {
8+
return sl<SongsRepository>().isFavoriteSong(params!);
9+
}
10+
}

0 commit comments

Comments
 (0)