Skip to content

Commit

Permalink
Merge pull request #61 from RajashekarRaju/feat/implement_favorite_sc…
Browse files Browse the repository at this point in the history
…reen_for_actors

Implemented favorite screen for Actors
  • Loading branch information
RajashekarRaju committed Apr 22, 2023
2 parents 4609faa + d6498d2 commit 22663c1
Show file tree
Hide file tree
Showing 24 changed files with 420 additions and 92 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
- [x] Separate the ViewModel param from Screen and UI composables to see previews for all screens.
- [ ] Break composables into smaller for previewing.
- [x] Break composables into smaller and move them to separate composables packages in each screen.
- [ ] Add feature for adding actors to favorites like movies.
- [ ] Add bottom sheet to home screen to support navigation for various screens.
- [x] Add feature for adding actors to favorites like movies.
- [x] Add bottom sheet to home screen to support navigation for various screens.
- [x] Implement paging to upcoming lazy list movies in home tab.
- [x] Setup project dependencies, resources for initial testing, write simple test.
- [ ] Write tests for app navigation for all composable destinations.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import com.developersbreach.composeactors.data.datasource.database.entity.Favori

@Database(
entities = [FavoriteActorsEntity::class, FavoriteMoviesEntity::class],
version = 2,
version = 3,
exportSchema = false
)
abstract class AppDatabase : RoomDatabase() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@ package com.developersbreach.composeactors.data.datasource.database

import androidx.lifecycle.LiveData
import androidx.lifecycle.map
import com.developersbreach.composeactors.data.model.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import com.developersbreach.composeactors.data.model.FavoriteActor
import com.developersbreach.composeactors.data.model.Movie
import com.developersbreach.composeactors.data.model.actorAsDatabaseModel
import com.developersbreach.composeactors.data.model.actorAsDomainModel
import com.developersbreach.composeactors.data.model.movieAsDatabaseModel
import com.developersbreach.composeactors.data.model.movieAsDomainModel
import javax.inject.Inject
import javax.inject.Singleton
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext


@Singleton
Expand All @@ -22,7 +27,7 @@ class DatabaseDataSource @Inject constructor(
}
}

fun getAllFavoriteActors(): LiveData<List<Actor>> {
fun getAllFavoriteActors(): LiveData<List<FavoriteActor>> {
val allFavoriteActors = database.favoriteActorsDao.getAllFavoriteActors()
// Change to distinct until changed
return allFavoriteActors.map { favEntityList ->
Expand All @@ -47,9 +52,9 @@ class DatabaseDataSource @Inject constructor(
}

suspend fun addActorToFavorites(
actor: Actor
favoriteActor: FavoriteActor
) = withContext(Dispatchers.IO) {
with(actor.actorAsDatabaseModel()) {
with(favoriteActor.actorAsDatabaseModel()) {
database.favoriteActorsDao.addActorToFavorites(favoriteActorsEntity = this)
}
}
Expand All @@ -63,9 +68,9 @@ class DatabaseDataSource @Inject constructor(
}

suspend fun deleteSelectedFavoriteActor(
actor: Actor
favoriteActor: FavoriteActor
) = withContext(Dispatchers.IO) {
with(actor.actorAsDatabaseModel()) {
with(favoriteActor.actorAsDatabaseModel()) {
database.favoriteActorsDao.deleteSelectedFavoriteActor(favoriteActorsEntity = this)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,8 @@ data class FavoriteActorsEntity(
val actorName: String,

@ColumnInfo(name = "column_actor_profileUrl")
val actorProfileUrl: String
val actorProfileUrl: String,

@ColumnInfo(name = "column_actor_placeOfBirth")
val actorPlaceOfBirth: String
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ package com.developersbreach.composeactors.data.datasource.network

import com.developersbreach.composeactors.data.PagedResponse
import com.developersbreach.composeactors.data.model.*
import org.json.JSONObject
import javax.inject.Inject
import javax.inject.Singleton
import org.json.JSONObject

/**
* @property urls for low and high resolution images.
Expand Down Expand Up @@ -70,6 +70,7 @@ class JsonRemoteData @Inject constructor(
): ActorDetail {

val jsonObject = JSONObject(response)
val actorId = jsonObject.getInt("id")
val actorName = jsonObject.getString("name")
val biography = jsonObject.getString("biography")
val dateOfBirth = jsonObject.getString("birthday")
Expand All @@ -78,7 +79,15 @@ class JsonRemoteData @Inject constructor(
val profilePathUrl = jsonObject.getString("profile_path")
val profilePath = "${HIGH_RES_IMAGE}$profilePathUrl"

return ActorDetail(actorName, profilePath, biography, dateOfBirth, placeOfBirth, popularity)
return ActorDetail(
actorId,
actorName,
profilePath,
biography,
dateOfBirth,
placeOfBirth,
popularity
)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,10 @@ package com.developersbreach.composeactors.data.model

import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import com.developersbreach.composeactors.data.datasource.database.entity.FavoriteActorsEntity

@Immutable
data class Actor(
@Stable val actorId: Int,
val actorName: String,
val profileUrl: String
)

fun Actor.actorAsDatabaseModel(): FavoriteActorsEntity {
return FavoriteActorsEntity(
actorId = this.actorId,
actorName = this.actorName,
actorProfileUrl = this.profileUrl
)
}

fun List<FavoriteActorsEntity>.actorAsDomainModel(): List<Actor> {
return map {
Actor(
actorId = it.actorId,
actorName = it.actorName,
profileUrl = it.actorProfileUrl
)
}
}
)
Original file line number Diff line number Diff line change
@@ -1,13 +1,43 @@
package com.developersbreach.composeactors.data.model

import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import com.developersbreach.composeactors.data.datasource.database.entity.FavoriteActorsEntity

@Immutable
data class ActorDetail(
@Stable val actorId: Int,
val actorName: String,
val profileUrl: String,
val biography: String,
val dateOfBirth: String,
val placeOfBirth: String,
val popularity: Double
)

fun ActorDetail.toFavoriteActor() = FavoriteActor(
actorId = this.actorId,
actorName = this.actorName,
profileUrl = this.profileUrl,
placeOfBirth = this.placeOfBirth
)

fun FavoriteActor.actorAsDatabaseModel(): FavoriteActorsEntity {
return FavoriteActorsEntity(
actorId = this.actorId,
actorName = this.actorName,
actorProfileUrl = this.profileUrl,
actorPlaceOfBirth = this.placeOfBirth
)
}

fun List<FavoriteActorsEntity>.actorAsDomainModel(): List<FavoriteActor> {
return map {
FavoriteActor(
actorId = it.actorId,
actorName = it.actorName,
profileUrl = it.actorProfileUrl,
placeOfBirth = it.actorPlaceOfBirth
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.developersbreach.composeactors.data.model

import androidx.compose.runtime.Stable

data class FavoriteActor(
@Stable val actorId: Int,
val actorName: String,
val profileUrl: String,
val placeOfBirth: String
)
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package com.developersbreach.composeactors.data.repository.actor

import androidx.lifecycle.LiveData
import com.developersbreach.composeactors.data.datasource.database.DatabaseDataSource
import com.developersbreach.composeactors.data.datasource.network.NetworkDataSource
import com.developersbreach.composeactors.data.model.Actor
import com.developersbreach.composeactors.data.model.ActorDetail
import com.developersbreach.composeactors.data.model.FavoriteActor
import com.developersbreach.composeactors.data.model.Movie
import com.developersbreach.composeactors.data.datasource.network.NetworkDataSource
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class ActorRepository @Inject constructor(
private val networkDataSource: NetworkDataSource
private val networkDataSource: NetworkDataSource,
private val databaseDataSource: DatabaseDataSource
) {
suspend fun getPopularActorsData(): List<Actor> {
return networkDataSource.getPopularActorsData()
Expand All @@ -30,4 +34,20 @@ class ActorRepository @Inject constructor(
suspend fun getCastData(actorInt: Int): List<Movie> {
return networkDataSource.getCastData(actorInt)
}

fun isFavoriteActor(actorId: Int): LiveData<Int> {
return databaseDataSource.checkIfActorIsFavorite(actorId)
}

suspend fun addActorsToFavorite(favoriteActor: FavoriteActor) {
databaseDataSource.addActorToFavorites(favoriteActor)
}

suspend fun deleteSelectedFavoriteActor(favoriteActor: FavoriteActor) {
databaseDataSource.deleteSelectedFavoriteActor(favoriteActor)
}

fun getAllFavoriteActors(): LiveData<List<FavoriteActor>> {
return databaseDataSource.getAllFavoriteActors()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.developersbreach.composeactors.domain.useCase

import com.developersbreach.composeactors.data.model.FavoriteActor
import com.developersbreach.composeactors.data.repository.actor.ActorRepository
import javax.inject.Inject

class RemoveActorsFromFavoritesUseCase @Inject constructor(
private val actorRepository: ActorRepository
) {
suspend operator fun invoke(favoriteActor: FavoriteActor) {
actorRepository.deleteSelectedFavoriteActor(
favoriteActor = favoriteActor
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.developersbreach.composeactors.ui.screens.actorDetails.ActorDetailsViewModel
import com.developersbreach.composeactors.ui.screens.actorDetails.ActorDetailsScreen
import com.developersbreach.composeactors.ui.screens.actorDetails.ActorDetailsViewModel
import com.developersbreach.composeactors.ui.screens.favorites.FavoriteViewModel
import com.developersbreach.composeactors.ui.screens.favorites.FavoritesScreen
import com.developersbreach.composeactors.ui.screens.home.HomeScreen
import com.developersbreach.composeactors.ui.screens.home.HomeViewModel
import com.developersbreach.composeactors.ui.screens.movieDetail.MovieDetailScreen
import com.developersbreach.composeactors.ui.screens.movieDetail.MovieDetailViewModel
import com.developersbreach.composeactors.ui.screens.search.SearchScreen
import com.developersbreach.composeactors.ui.screens.search.SearchViewModel
import com.developersbreach.composeactors.ui.screens.search.SearchType
import com.developersbreach.composeactors.ui.screens.search.SearchViewModel


/**
Expand Down Expand Up @@ -133,6 +133,7 @@ fun AppNavigation(
FavoritesScreen(
navigateUp = actions.navigateUp,
selectedMovie = actions.selectedMovie,
selectedActor = actions.selectedActor,
favoriteViewModel = favoriteViewModel
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.developersbreach.composeactors.ui.screens.actorDetails.composables.ActorBiography
Expand All @@ -19,17 +21,22 @@ internal fun ActorDetailsContent(
navigateUp: () -> Unit,
detailUIState: ActorDetailsUIState,
openActorDetailsBottomSheet: () -> Job,
getSelectedMovieDetails: (Int) -> Unit
getSelectedMovieDetails: (Int) -> Unit,
showFab: MutableState<Boolean>
) {
val actorData = detailUIState.actorData
val listState = rememberLazyListState()

/** Sticky actor details content */
Spacer(modifier = Modifier.padding(top = 16.dp))
ActorRoundProfile("${actorData?.profileUrl}")
Spacer(modifier = Modifier.padding(vertical = 8.dp))

showFab.value = !listState.isScrollInProgress

/** Scrollable actor details content */
LazyColumn(
state = listState,
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
item {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
package com.developersbreach.composeactors.ui.screens.actorDetails

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import com.developersbreach.composeactors.ui.screens.home.HomeScreen
import com.developersbreach.composeactors.ui.screens.modalSheets.manageModalBottomSheet
import com.developersbreach.composeactors.ui.screens.modalSheets.modalBottomSheetState
import com.developersbreach.composeactors.ui.screens.movieDetail.composables.FloatingAddToFavoritesButton
import com.developersbreach.composeactors.ui.screens.search.SearchScreen


Expand All @@ -26,22 +34,36 @@ internal fun ActorDetailsScreen(
val detailUIState = viewModel.detailUIState
val sheetUIState = viewModel.sheetUIState
val actorProfileUrl = "${detailUIState.actorData?.profileUrl}"
val movieId by viewModel.isFavoriteMovie.observeAsState()

val modalSheetState = modalBottomSheetState()
val openActorDetailsBottomSheet = manageModalBottomSheet(
modalSheetState = modalSheetState
)

ActorDetailsUI(
detailUIState = detailUIState,
sheetUIState = sheetUIState,
actorProfileUrl = actorProfileUrl,
modalSheetState = modalSheetState,
selectedMovie = selectedMovie,
navigateUp = navigateUp,
openActorDetailsBottomSheet = openActorDetailsBottomSheet,
getSelectedMovieDetails = { movieId ->
viewModel.getSelectedMovieDetails(movieId)
val showFab = rememberSaveable { mutableStateOf(true) }

Box(modifier = Modifier.fillMaxSize()) {
ActorDetailsUI(
detailUIState = detailUIState,
sheetUIState = sheetUIState,
actorProfileUrl = actorProfileUrl,
modalSheetState = modalSheetState,
selectedMovie = selectedMovie,
navigateUp = navigateUp,
openActorDetailsBottomSheet = openActorDetailsBottomSheet,
showFab = showFab,
getSelectedMovieDetails = { movieId ->
viewModel.getSelectedMovieDetails(movieId)
}
)

if (showFab.value) {
FloatingAddToFavoritesButton(
isFavorite = movieId != 0 && movieId != null,
addToFavorites = { viewModel.addActorToFavorites() },
removeFromFavorites = { viewModel.removeActorFromFavorites() }
)
}
)
}
}

0 comments on commit 22663c1

Please sign in to comment.