Skip to content

Commit

Permalink
database-design branch - feat: implement a feature that allows adding…
Browse files Browse the repository at this point in the history
… song to the albums
  • Loading branch information
thebeo2004 committed Nov 30, 2024
1 parent 1ea68e1 commit def4068
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 13 deletions.
2 changes: 2 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ dependencies {
implementation ("com.squareup.retrofit2:retrofit:2.9.0")
implementation ("com.squareup.retrofit2:converter-gson:2.9.0")

implementation ("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")

// // Hilt Testing dependencies
// androidTestImplementation("androidx.hilt:hilt-testing:1.0.0")
// androidTestImplementation("com.google.dagger:hilt-android-testing:2.43.2")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import android.util.Log
import com.example.harmonyhub.domain.repository.UserDataRepo
import com.example.harmonyhub.presentation.viewmodel.DataFetchingState
import com.example.harmonyhub.presentation.viewmodel.FavoriteSongFetchingState
import com.example.harmonyhub.presentation.viewmodel.PlaylistSongFetchingState
import com.example.harmonyhub.ui.components.Song
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.firestore.CollectionReference
Expand Down Expand Up @@ -44,6 +45,16 @@ class UserDataRepoImpl @Inject constructor(
return favoriteSongsRef
}

fun getPlaylistRef(userId: String?, playlistName: String): DocumentReference {
val playlistRef = firestore.collection("users").document(userId.toString()).collection("albums").document(playlistName)
return playlistRef
}

fun getPlaylistSongRef(userId: String?, playlistName: String, url: String): DocumentReference {
val playlistSongRef = getPlaylistRef(userId, playlistName).collection("songs").document(url)
return playlistSongRef
}

override fun getUserInfor(callback: (String?, String?) -> Unit) {
val userId = auth.currentUser?.uid
val userRef = getUserDataRef(userId)
Expand Down Expand Up @@ -242,4 +253,31 @@ class UserDataRepoImpl @Inject constructor(
callback(FavoriteSongFetchingState.Error("Failed to get favorite songs"))
}
}

override fun addSongToPlayList(
song: Song,
playlistName: String,
callback: (PlaylistSongFetchingState) -> Unit
) {
val userId = auth.currentUser?.uid
val encodedUrl = URLEncoder.encode(song.url, StandardCharsets.UTF_8.toString())
val playlistSongsRef = getPlaylistSongRef(userId, playlistName, encodedUrl)

val songMap = hashMapOf(
"songName" to song.name,
"artist" to song.artist,
"imageResId" to song.imageResId,
"url" to song.url
)

playlistSongsRef.set(songMap)
.addOnSuccessListener {
Log.d(TAG, "DocumentSnapshot successfully written!")
callback(PlaylistSongFetchingState.Success("Successfully added song to ${playlistName}"))
}
.addOnFailureListener { e ->
Log.w(TAG, "Error writing document", e)
callback(PlaylistSongFetchingState.Error("Failed to add song to ${playlistName}"))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,22 @@ package com.example.harmonyhub.domain.repository

import com.example.harmonyhub.presentation.viewmodel.DataFetchingState
import com.example.harmonyhub.presentation.viewmodel.FavoriteSongFetchingState
import com.example.harmonyhub.presentation.viewmodel.PlaylistSongFetchingState
import com.example.harmonyhub.ui.components.Song

interface UserDataRepo {
fun getUserInfor(callback: (String?, String?) -> Unit)
fun setUserInfor(userName: String, email: String, userId: String?)

fun getAlbums(callback: (DataFetchingState) -> Unit)
fun setAlbum(albumName: String, callback: (DataFetchingState) -> Unit)

fun addFavoriteSong(song: Song, callback: (FavoriteSongFetchingState) -> Unit)
fun removeFavoriteSong(song: Song, callback: (FavoriteSongFetchingState) -> Unit)
fun getFavoriteSongs(callback: (FavoriteSongFetchingState) -> Unit)

fun addSongToPlayList(song: Song, playlistName: String, callback: (PlaylistSongFetchingState) -> Unit)
// fun removeSongFromPlayList()
// fun getSongFromPlayList()
// fun deletePlayList()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.example.harmonyhub.presentation.viewmodel

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.example.harmonyhub.domain.repository.UserDataRepo
import com.example.harmonyhub.ui.components.Song
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject

@HiltViewModel
class PlaylistViewModel @Inject constructor(
private val userRepo: UserDataRepo,
): ViewModel() {
private val _dataFetchingState = MutableLiveData<PlaylistSongFetchingState>()
val dataFetchingState: MutableLiveData<PlaylistSongFetchingState> get() = _dataFetchingState

init {
resetDataFetchingState()
}

fun addSongToPlayList(song: Song, playlistName: String) {
userRepo.addSongToPlayList(song, playlistName, callback = {
_dataFetchingState.value = it
})
}

fun resetDataFetchingState() {
_dataFetchingState.value = PlaylistSongFetchingState.Pending
}
}

sealed class PlaylistSongFetchingState {
object Pending : PlaylistSongFetchingState()
data class Success(val data: Any) : PlaylistSongFetchingState()
data class Error(val message: String) : PlaylistSongFetchingState()
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package com.example.harmonyhub.ui.library

import android.util.Log
import android.widget.Toast
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
Expand All @@ -22,7 +21,6 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Clear
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
Expand Down Expand Up @@ -52,19 +50,22 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import coil.compose.rememberAsyncImagePainter
import androidx.lifecycle.lifecycleScope
import com.example.harmonyhub.R
import com.example.harmonyhub.presentation.viewmodel.DataFetchingState
import com.example.harmonyhub.presentation.viewmodel.PlaylistSongFetchingState
import com.example.harmonyhub.presentation.viewmodel.PlaylistViewModel
import com.example.harmonyhub.presentation.viewmodel.UserDataViewModel
import com.example.harmonyhub.ui.components.Playlist
import com.example.harmonyhub.ui.components.contains
import com.example.harmonyhub.ui.components.Song
import com.example.harmonyhub.ui.theme.NotoSans
import kotlinx.coroutines.launch

private val gradientBackground = Brush.verticalGradient(
colors = listOf(Color(0xFF04A8A3), Color(0xFF0A91BD))
Expand All @@ -74,23 +75,33 @@ private val gradientBackground = Brush.verticalGradient(
@Composable
fun AddToPlaylistFromSongScreen(
onBackButtonClicked: () -> Unit,
userDataViewModel: UserDataViewModel = hiltViewModel()
userDataViewModel: UserDataViewModel = hiltViewModel(),
playlistViewModel: PlaylistViewModel = hiltViewModel(),
song: Song = Song("1", "Song 1", "Artist 1", "Album 1", "Genre 1")
) {
val focusManager = LocalFocusManager.current

var query by remember { mutableStateOf("") }
var showDialog by remember { mutableStateOf(false) }
var newPlaylistName by remember { mutableStateOf("") }

var selectedPlaylists by remember { mutableStateOf(setOf<String>())}

val albumsFetchingState = userDataViewModel.dataFetchingState.observeAsState()
val context = LocalContext.current

val allPlaylists = remember { mutableListOf<String>() }

val lifecycleOwner = LocalLifecycleOwner.current

LaunchedEffect(Unit) {
userDataViewModel.getAlbums()
}

LaunchedEffect(selectedPlaylists) {
Log.d("SelectedPlaylists", "Playlist size: ${selectedPlaylists.size}")
}

LaunchedEffect(albumsFetchingState.value) {
when (albumsFetchingState.value) {
is DataFetchingState.Success -> {
Expand Down Expand Up @@ -209,7 +220,14 @@ fun AddToPlaylistFromSongScreen(
items(allPlaylists) { playlist ->
PlaylistItem(
playlistName = playlist,
songCount = "2 bài hát"
songCount = "2 bài hát",
onPlaylistClicked = {
if (selectedPlaylists.contains(playlist)) {
selectedPlaylists -= playlist
} else {
selectedPlaylists += playlist
}
}
)
}
}
Expand All @@ -219,8 +237,23 @@ fun AddToPlaylistFromSongScreen(
// Nút hoàn tất
Button(
onClick = {
onBackButtonClicked()
/* Todo */
// Launch a coroutine to handle the asynchronous operations
selectedPlaylists.forEach { playlist ->
lifecycleOwner.lifecycleScope.launch {
playlistViewModel.addSongToPlayList(song, playlist)

when (val state = playlistViewModel.dataFetchingState.value) {
is PlaylistSongFetchingState.Error -> {
val message = state.message
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
playlistViewModel.resetDataFetchingState()
}
else -> {}
}
}

}
// onBackButtonClicked()
},
colors = ButtonDefaults.buttonColors(
containerColor = Color.Transparent
Expand Down Expand Up @@ -322,7 +355,10 @@ fun AddToPlaylistFromSongScreen(
}

@Composable
fun PlaylistItem(playlistName: String, songCount: String) {
fun PlaylistItem(playlistName: String,
songCount: String,
onPlaylistClicked: () -> Unit = { }
) {
var isSelected by remember { mutableStateOf(false) }
Row(
verticalAlignment = Alignment.CenterVertically,
Expand Down Expand Up @@ -353,8 +389,10 @@ fun AddToPlaylistFromSongScreen(
Spacer(modifier = Modifier.weight(1f))
RadioButton(
selected = isSelected,
onClick = { isSelected = !isSelected },
onClick = { isSelected = !isSelected
onPlaylistClicked()
},
colors = RadioButtonDefaults.colors(selectedColor = Color(0xFF0A91BD))
)
}
}
}

0 comments on commit def4068

Please sign in to comment.