Skip to content

Commit

Permalink
Added displaying comprehensive actor data on movie detail
Browse files Browse the repository at this point in the history
  • Loading branch information
diareuse committed Nov 11, 2023
1 parent 10f38df commit aa2ff6a
Show file tree
Hide file tree
Showing 14 changed files with 151 additions and 45 deletions.
6 changes: 5 additions & 1 deletion app/src/main/java/movie/metropolis/app/di/FacadeModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import movie.metropolis.app.presentation.detail.MovieFacadeFromFeature
import movie.metropolis.app.presentation.detail.MovieFacadeRating
import movie.metropolis.app.presentation.detail.MovieFacadeReactive
import movie.metropolis.app.presentation.detail.MovieFacadeRecover
import movie.metropolis.app.presentation.detail.MovieFacadeWithActors
import movie.metropolis.app.presentation.home.HomeFacade
import movie.metropolis.app.presentation.home.HomeFacadeFromFeature
import movie.metropolis.app.presentation.listing.ListingFacade
Expand Down Expand Up @@ -73,6 +74,7 @@ import movie.metropolis.app.presentation.ticket.TicketFacade
import movie.metropolis.app.presentation.ticket.TicketFacadeCinemaFromFeature
import movie.metropolis.app.presentation.ticket.TicketFacadeFilter
import movie.metropolis.app.presentation.ticket.TicketFacadeMovieFromFeature
import movie.rating.ActorProvider
import movie.rating.MetadataProvider

@Module
Expand Down Expand Up @@ -121,11 +123,13 @@ class FacadeModule {
showings: EventShowingsFeature.Factory,
detail: EventDetailFeature,
favorite: FavoriteFeature,
rating: MetadataProvider
rating: MetadataProvider,
actors: ActorProvider
): MovieFacade.Factory = MovieFacade.Factory {
var facade: MovieFacade
facade = MovieFacadeFromFeature(it, showings, detail, favorite)
facade = MovieFacadeRating(facade, rating)
facade = MovieFacadeWithActors(facade, actors)
facade = MovieFacadeReactive(facade)
facade = MovieFacadeRecover(facade)
facade = MovieFacadeFilterable(facade)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ interface MovieDetailView {
val releasedAt: String
val duration: String
val countryOfOrigin: String
val cast: List<String>
val directors: List<String>
val cast: List<PersonView>
val directors: List<PersonView>
val description: String
val availableFrom: String
val poster: ImageView?
Expand Down
11 changes: 11 additions & 0 deletions app/src/main/java/movie/metropolis/app/model/PersonView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package movie.metropolis.app.model

import androidx.compose.runtime.*

@Immutable
interface PersonView {
val name: String
val popularity: Int
val image: String
val starredInMovies: Int
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import movie.core.model.Media
import movie.core.model.MovieDetail
import movie.metropolis.app.model.ImageView
import movie.metropolis.app.model.MovieDetailView
import movie.metropolis.app.model.PersonView
import movie.metropolis.app.model.VideoView
import movie.metropolis.app.util.toStringComponents
import java.text.DateFormat
Expand All @@ -29,10 +30,10 @@ data class MovieDetailViewFromFeature(
get() = movie.duration.toStringComponents()
override val countryOfOrigin: String
get() = movie.countryOfOrigin.orEmpty()
override val cast: List<String>
get() = movie.cast.toList()
override val directors: List<String>
get() = movie.directors.toList()
override val cast: List<PersonView>
get() = movie.cast.map(::PersonViewFromName)
override val directors: List<PersonView>
get() = movie.directors.map(::PersonViewFromName)
override val description: String
get() = movie.description
override val availableFrom: String
Expand All @@ -51,4 +52,12 @@ data class MovieDetailViewFromFeature(
return movie
}

data class PersonViewFromName(
override val name: String
) : PersonView {
override val popularity: Int = -1
override val image: String = ""
override val starredInMovies: Int = -1
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import java.util.Date

interface MovieFacade {

val movie: Flow<Result<MovieDetailView>>
val movie: Flow<MovieDetailView>
val favorite: Flow<Boolean>
val availability: Flow<Date>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package movie.metropolis.app.presentation.detail

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
Expand All @@ -25,18 +26,14 @@ class MovieFacadeFromFeature(

private val model = MovieFromId(id)
private val detailFlow = flow {
emit(detail.get(model))
emit(detail.get(model).getOrThrow())
}

override val movie: Flow<Result<MovieDetailView>> = detailFlow.map {
it.map(::MovieDetailViewFromFeature)
}
override val movie: Flow<MovieDetailView> = detailFlow.map(::MovieDetailViewFromFeature)
override val favorite = flow {
emit(favorites.isFavorite(model).getOrDefault(false))
}
override val availability = detailFlow.map { result ->
result.fold({ it.screeningFrom }, { Date() }).coerceAtLeast(Date())
}
override val availability = detailFlow.map { it.screeningFrom }.catch { emit(Date()) }

override fun showings(
date: Date,
Expand All @@ -54,7 +51,7 @@ class MovieFacadeFromFeature(
}

override suspend fun toggleFavorite() {
val detail = detailFlow.first().getOrNull() ?: return
val detail = detailFlow.first()
val preview = MoviePreviewFromDetail(detail)
favorites.toggle(preview)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package movie.metropolis.app.presentation.detail

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import movie.metropolis.app.model.MovieDetailView
import movie.metropolis.app.model.adapter.MovieDetailViewWithRating
import movie.metropolis.app.util.flatMapResult
import movie.rating.MetadataProvider
import movie.rating.MovieDescriptor
import movie.rating.MovieMetadata
Expand All @@ -16,19 +15,22 @@ class MovieFacadeRating(
private val rating: MetadataProvider
) : MovieFacade by origin {

override val movie: Flow<Result<MovieDetailView>> = origin.movie.flatMapResult {
private var metadata: MovieMetadata? = null

override val movie: Flow<MovieDetailView> = origin.movie.flatMapLatest {
flow {
emit(it)
if (metadata == null) emit(it)
val year = Calendar.getInstance().apply { time = it.base().releasedAt }[Calendar.YEAR]
val descriptors = arrayOf(
MovieDescriptor.Original(it.nameOriginal, year),
MovieDescriptor.Local(it.name, year)
)
val rating = descriptors.fold(null as MovieMetadata?) { acc, it ->
val rating = metadata ?: descriptors.fold(null as MovieMetadata?) { acc, it ->
acc ?: rating.get(it)
}
metadata = rating
emit(MovieDetailViewWithRating(it, rating))
}.map(Result.Companion::success)
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,12 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import movie.log.logSevere
import movie.metropolis.app.model.CinemaBookingView
import movie.metropolis.app.model.MovieDetailView
import java.util.Date

class MovieFacadeRecover(
private val origin: MovieFacade
) : MovieFacade by origin {

override val movie: Flow<Result<MovieDetailView>> = origin.movie
.catch { emit(Result.failure(it)) }
override val favorite: Flow<Boolean> = origin.favorite
.catch { emit(false) }
override val availability: Flow<Date> = origin.availability
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package movie.metropolis.app.presentation.detail

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import movie.metropolis.app.model.MovieDetailView
import movie.metropolis.app.model.PersonView
import movie.rating.Actor
import movie.rating.ActorProvider

class MovieFacadeWithActors(
private val origin: MovieFacade,
provider: ActorProvider
) : MovieFacade by origin {

private val actors = ActorProviderCaching(provider)

override val movie: Flow<MovieDetailView> = origin.movie.flatMapLatest {
channelFlow {
val it = MovieDetailReplacing(it)
send(it)
val directors = it.directors.toMutableList()
val cast = it.cast.toMutableList()
val directorsMutex = Mutex()
val castMutex = Mutex()
for ((index, d) in directors.withIndex()) launch {
val actor = actors.runCatching { get(d.name) }.getOrNull() ?: return@launch
val out = directorsMutex.withLock {
directors[index] = PersonViewFromActor(actor)
directors.toList()
}
send(it.copy(directors = out))
}
for ((index, c) in cast.withIndex()) launch {
val actor = actors.runCatching { get(c.name) }.getOrNull() ?: return@launch
val out = castMutex.withLock {
cast[index] = PersonViewFromActor(actor)
cast.toList()
}
send(it.copy(cast = out))
}
}
}

class ActorProviderCaching(
private val origin: ActorProvider
) : ActorProvider {
private val cache = mutableMapOf<String, Actor>()
private val mutex = Mutex()
override suspend fun get(query: String): Actor {
return cache[query] ?: mutex.withLock { cache[query] } ?: origin.get(query).also {
mutex.withLock {
cache[query] = it
}
}
}
}

data class MovieDetailReplacing(
private val origin: MovieDetailView,
override val directors: List<PersonView> = origin.directors,
override val cast: List<PersonView> = origin.cast
) : MovieDetailView by origin

data class PersonViewFromActor(
private val actor: Actor
) : PersonView {
override val name: String
get() = actor.name
override val popularity: Int
get() = actor.popularity
override val image: String
get() = actor.image
override val starredInMovies: Int
get() = actor.movies.size
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import movie.metropolis.app.model.CinemaView
import movie.metropolis.app.model.Filter
import movie.metropolis.app.model.ImageView
import movie.metropolis.app.model.MovieDetailView
import movie.metropolis.app.model.PersonView
import movie.metropolis.app.model.VideoView
import movie.metropolis.app.presentation.Loadable
import movie.metropolis.app.presentation.map
Expand Down Expand Up @@ -359,8 +360,8 @@ class MovieDetailViewProvider : CollectionPreviewParameterProvider<MovieDetailVi
override val releasedAt: String = "2022",
override val duration: String = "1h 34m",
override val countryOfOrigin: String = "USA",
override val cast: List<String> = listOf("Foo Bar", "Bar Foo-Foo"),
override val directors: List<String> = listOf("Foofoo Barbar"),
override val cast: List<PersonView> = listOf(Person("Foo Bar"), Person("Bar Foo-Foo")),
override val directors: List<PersonView> = listOf(Person("Foofoo Barbar")),
override val description: String = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam vel finibus augue. Praesent porta, nibh rhoncus ultrices tempus, metus lacus facilisis lorem, id venenatis nisl mi non massa. Vestibulum eu ipsum leo. Mauris et sagittis tortor. Fusce dictum cursus quam in ornare. Curabitur posuere ligula sem, et tincidunt lorem commodo vitae. Fusce mollis elementum dignissim. Fusce suscipit massa maximus metus gravida, vitae posuere sem semper. Nullam auctor venenatis elementum. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Phasellus nibh sem, volutpat nec egestas convallis, ultricies quis massa. Duis quis placerat neque, eu bibendum arcu. ",
override val availableFrom: String = "23. 4. 2022",
override val poster: ImageView? = null,
Expand All @@ -370,6 +371,13 @@ class MovieDetailViewProvider : CollectionPreviewParameterProvider<MovieDetailVi
override fun base(): MovieDetail = throw NotImplementedError()
}

private data class Person(
override val name: String,
override val popularity: Int = -1,
override val image: String = "",
override val starredInMovies: Int = -1
) : PersonView

}

class CinemaBookingViewProvider :
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ class MovieViewModel private constructor(
val location = MutableStateFlow(null as AndroidLocation?)

val detail = facade.movie
.map { it.asLoadable() }
.map { Loadable.success(it) }
.onStart { emit(Loadable.loading()) }
.retainStateIn(viewModelScope)
val startDate = facade.availability
.onEach { selectedDate.compareAndSet(null, it) }
Expand Down
22 changes: 10 additions & 12 deletions app/src/main/java/movie/metropolis/app/screen2/movie/MovieScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -131,14 +131,13 @@ fun MovieScreen(
flingBehavior = rememberSnapFlingBehavior(state)
) {
items(movie.directors) {
val state =
rememberPaletteImageState(url = "https://image.tmdb.org/t/p/w500/80DH2zWgZiXHehH7TLe6HKDldyl.jpg")
val state = rememberPaletteImageState(url = it.image)
ActorRow(
color = state.palette.color,
image = { Image(state) },
name = { Text(it) },
popularity = { Text("n/a") },
movieCount = { Text("n/a") }
name = { Text(it.name) },
popularity = { Text(if (it.popularity > 0) it.popularity.toString() else "n/a") },
movieCount = { Text(if (it.starredInMovies > 0) it.starredInMovies.toString() else "n/a") },
color = state.palette.color
)
}
}
Expand All @@ -162,14 +161,13 @@ fun MovieScreen(
flingBehavior = rememberSnapFlingBehavior(state)
) {
items(movie.cast) {
val state =
rememberPaletteImageState(url = "https://image.tmdb.org/t/p/w500/80DH2zWgZiXHehH7TLe6HKDldyl.jpg")
val state = rememberPaletteImageState(url = it.image)
ActorRow(
color = state.palette.color,
image = { Image(state) },
name = { Text(it) },
popularity = { Text("n/a") },
movieCount = { Text("n/a") }
name = { Text(it.name) },
popularity = { Text(if (it.popularity > 0) it.popularity.toString() else "n/a") },
movieCount = { Text(if (it.starredInMovies > 0) it.starredInMovies.toString() else "n/a") },
color = state.palette.color
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.mapNotNull
import movie.core.UserDataFeature
import movie.metropolis.app.presentation.detail.MovieFacade
import movie.metropolis.app.screen.Route
Expand Down Expand Up @@ -39,7 +38,7 @@ class MovieViewModel private constructor(
args.upcoming
)

val movie = facade.movie.mapNotNull { it.getOrNull() }
val movie = facade.movie
.retainStateIn(viewModelScope, null)

}
Loading

0 comments on commit aa2ff6a

Please sign in to comment.