Movies UI - Updates
Deleting from the display screens
Now that we have selectable lists inside the display screens, let's allow deletions.
First, let's add icons to the items in the display screens:
This allows clicking on the icons to select, in addition to long-pressing the cards.
Note
For the movie and actor display screens, the data we're displaying is role information,
and we're using the derived id for these objects in
RoleWithMovieDto and
RoleWithActorDto.
This will not work for RoleEntity
deletion. To properly
delete the roles we would need to track the actor id, movie id, and order in credits so we can
match them up for deletion. I'm leaving that as the obligatory "exercise for the interested
reader", and we'll only focus on deleting movies via the rating display screen.
To test deletion, we:
- Go to the ratings list
- Select PG-13
- Click on the icon next to "Hobbs and Shaw"
- Click the trash can on the top bar to delete the movie And... nothing happens! Or at least nothing seems to happen.
- But if we click on the ratings list
- Then go back into PG-13
We see that "Hobbs and Shaw" has indeed been deleted.
What's happening?
Once again, it's all down to where the state is being set/read.
- When we select PG-13, we pass the id
of the PG-13 entity into
RatingDisplayUi
RatingDisplayUi
fetches aRatingWithMoviesDto
using that id inside aLaunchedEffect
- When we delete a movie from the database, the only thing that changes is
the data in the
MovieEntity
table, emitting a newMovieList
to theFlow
for all movies. - We're not collecting that
Flow
for this screen; nothing triggers a refetch of the currently-displayed rating (the rating id hasn't changed) - We're stuck with the previously-fetched
RatingWithMoviesDto
.
How do we fix this? We need something to tell us that our local movie list has changed.
We can:
- Pass in the
RoleWithMoviesDto
instead of fetching it (and get rid of the fetcher function) - Create a DAO function to get a rating with its movies
using a
Flow
. - Forward that function through the repository, database repository implementation, and view model (automatically done via delegation)
- Collect the
Flow
and pass the result into the rating display
Poof! It works now!
The trick here was using data that's automatically updated, sending us a new
RoleWithMoviesDto
when a movie is deleted. We collect that flow in Ui
,
updating the value that we pass into the rating display, triggering recomposition.
The list will also display when the reset-database button on the top bar is pressed.
!
-
add edit screen with edit button for movie display
-
add edit screens for actor and rating
- use save button for actor
- use back nav for rating
- add + to lists to create new items
-
jumps to edit screen
-
skip adding roles/filmography for now
- do with dialogs?
Code Changes
CHANGED: /app/src/main/java/com/androidbyexample/movie/screens/ActorDisplayUi.kt
package com.androidbyexample.movie.screens import androidx.compose.foundation.clickableimport androidx.compose.foundation.layout.Rowimport androidx.compose.foundation.layout.paddingimport androidx.compose.material.icons.Iconsimport androidx.compose.material.icons.filled.Movieimport androidx.compose.material3.Iconimport androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignmentimport androidx.compose.ui.Modifierimport androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dpimport com.androidbyexample.movie.R import com.androidbyexample.movie.components.Display import com.androidbyexample.movie.components.Label import com.androidbyexample.movie.repository.ActorWithFilmographyDto import com.androidbyexample.movie.repository.MovieDto import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @Composable fun ActorDisplayUi( id: String, fetchActor: suspend (String) -> ActorWithFilmographyDto, onMovieClicked: (MovieDto) -> Unit, selectedIds: Set<String>, onSelectionToggle: (id: String) -> Unit, onClearSelections: () -> Unit, onDeleteSelectedMovies: () -> Unit, currentScreen: Screen, onSelectListScreen: (Screen) -> Unit, onResetDatabase: () -> Unit, ) { var actorWithFilmography by remember { mutableStateOf<ActorWithFilmographyDto?>(null) } LaunchedEffect(key1 = id) { withContext(Dispatchers.IO) { actorWithFilmography = fetchActor(id) } } ListScaffold( titleId = R.string.rating, items = actorWithFilmography?.filmography?.sortedBy { it.movie.title } ?: emptyList(), onItemClicked = { onMovieClicked(it.movie) }, selectedIds = selectedIds, onSelectionToggle = onSelectionToggle, onClearSelections = onClearSelections, onDeleteSelectedItems = onDeleteSelectedMovies, currentScreen = currentScreen, onSelectListScreen = onSelectListScreen, onResetDatabase = onResetDatabase, itemContent = { role ->Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(8.dp) ) { Icon( imageVector = Icons.Default.Movie, contentDescription = stringResource(id = R.string.movie), modifier = Modifier.clickable { onSelectionToggle(role.id) } ) Display(text = role.movie.title) }}, topContent = { Label(textId = R.string.name) Display(text = actorWithFilmography?.actor?.name ?: "") Label( text = stringResource( id = R.string.movies_starring, actorWithFilmography?.actor?.name ?: "" ) ) } ) }
CHANGED: /app/src/main/java/com/androidbyexample/movie/screens/MovieDisplayUi.kt
package com.androidbyexample.movie.screens import androidx.compose.foundation.clickableimport androidx.compose.foundation.layout.Rowimport androidx.compose.foundation.layout.paddingimport androidx.compose.material.icons.Iconsimport androidx.compose.material.icons.filled.Movieimport androidx.compose.material3.Iconimport androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignmentimport androidx.compose.ui.Modifierimport androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dpimport com.androidbyexample.movie.R import com.androidbyexample.movie.components.Display import com.androidbyexample.movie.components.Label import com.androidbyexample.movie.repository.MovieWithCastDto import com.androidbyexample.movie.repository.RoleWithActorDto import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @Composable fun MovieDisplayUi( id: String, fetchMovie: suspend (String) -> MovieWithCastDto, onActorClicked: (RoleWithActorDto) -> Unit, selectedIds: Set<String>, onSelectionToggle: (id: String) -> Unit, onClearSelections: () -> Unit, onDeleteSelectedMovies: () -> Unit, currentScreen: Screen, onSelectListScreen: (Screen) -> Unit, onResetDatabase: () -> Unit, ) { var movieWithCast by remember { mutableStateOf<MovieWithCastDto?>(null) } LaunchedEffect(key1 = id) { withContext(Dispatchers.IO) { movieWithCast = fetchMovie(id) } } ListScaffold( titleId = R.string.rating, items = movieWithCast?.cast?.sortedBy { it.orderInCredits } ?: emptyList(), onItemClicked = onActorClicked, selectedIds = selectedIds, onSelectionToggle = onSelectionToggle, onClearSelections = onClearSelections, onDeleteSelectedItems = onDeleteSelectedMovies, currentScreen = currentScreen, onSelectListScreen = onSelectListScreen, onResetDatabase = onResetDatabase, itemContent = { role ->Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(8.dp) ) { Icon( imageVector = Icons.Default.Movie, contentDescription = stringResource(id = R.string.movie), modifier = Modifier.clickable { onSelectionToggle(role.id) } ) Display( text = stringResource( R.string.cast_entry, role.character, role.actor.name, ) ) }}, topContent = { Label(textId = R.string.title) Display(text = movieWithCast?.movie?.title ?: "") Label(textId = R.string.description) Display(text = movieWithCast?.movie?.description ?: "") Label(textId = R.string.cast) } ) }
CHANGED: /app/src/main/java/com/androidbyexample/movie/screens/RatingDisplayUi.kt
package com.androidbyexample.movie.screens import androidx.compose.foundation.clickableimport androidx.compose.foundation.layout.Rowimport androidx.compose.foundation.layout.paddingimport androidx.compose.material.icons.Iconsimport androidx.compose.material.icons.filled.Movieimport androidx.compose.material3.Iconimport androidx.compose.runtime.Composable//import androidx.compose.runtime.LaunchedEffect//import androidx.compose.runtime.getValue//import androidx.compose.runtime.mutableStateOf//import androidx.compose.runtime.remember//import androidx.compose.runtime.setValueimport androidx.compose.ui.Alignmentimport androidx.compose.ui.Modifierimport androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dpimport com.androidbyexample.movie.R import com.androidbyexample.movie.components.Display import com.androidbyexample.movie.components.Label import com.androidbyexample.movie.repository.MovieDto import com.androidbyexample.movie.repository.RatingWithMoviesDto//import kotlinx.coroutines.Dispatchers//import kotlinx.coroutines.withContext@Composable fun RatingDisplayUi(onMovieClicked: (MovieDto) -> Unit,// id: String,// fetchRating: suspend (String) -> RatingWithMoviesDto,ratingWithMovies: RatingWithMoviesDto?,selectedIds: Set<String>, onSelectionToggle: (id: String) -> Unit, onClearSelections: () -> Unit, onDeleteSelectedMovies: () -> Unit,currentScreen: Screen, onSelectListScreen: (Screen) -> Unit, onResetDatabase: () -> Unit,) {// var ratingWithMovies by remember { mutableStateOf<RatingWithMoviesDto?>(null) }// LaunchedEffect(key1 = id) {// withContext(Dispatchers.IO) {// ratingWithMovies = fetchRating(id)// }// }ListScaffold( titleId = R.string.rating, items = ratingWithMovies?.movies?.sortedBy { it.title } ?: emptyList(), onItemClicked = onMovieClicked, selectedIds = selectedIds, onSelectionToggle = onSelectionToggle, onClearSelections = onClearSelections, onDeleteSelectedItems = onDeleteSelectedMovies, currentScreen = currentScreen, onSelectListScreen = onSelectListScreen, onResetDatabase = onResetDatabase, itemContent = { movie ->}, topContent = { ratingWithMovies?.let { ratingWithMovies -> Label(textId = R.string.name) Display(text = ratingWithMovies.rating.name) Label(textId = R.string.description) Display(text = ratingWithMovies.rating.description) Label( text = stringResource( id = R.string.movies_rated, ratingWithMovies.rating.name ) ) } } ) }// Display(// text = movie.titleRow( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(8.dp) ) { Icon( imageVector = Icons.Default.Movie, contentDescription = stringResource(id = R.string.movie), modifier = Modifier.clickable { onSelectionToggle(movie.id) } ) Display(text = movie.title) }
CHANGED: /app/src/main/java/com/androidbyexample/movie/screens/Ui.kt
package com.androidbyexample.movie.screens import androidx.activity.compose.BackHandler import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberCoroutineScope import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.androidbyexample.movie.MovieViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @Composable fun Ui( viewModel: MovieViewModel, onExit: () -> Unit, ) { BackHandler { viewModel.popScreen() } val scope = rememberCoroutineScope() when (val screen = viewModel.currentScreen) { null -> onExit() is MovieDisplay -> { val selectedIds by viewModel.selectedIdsFlow.collectAsStateWithLifecycle(initialValue = emptySet()) MovieDisplayUi( id = screen.id, fetchMovie = viewModel::getMovieWithCast, onActorClicked = { viewModel.pushScreen(ActorDisplay(it.actor.id)) }, selectedIds = selectedIds, onClearSelections = viewModel::clearSelectedIds, onSelectionToggle = viewModel::toggleSelection, onDeleteSelectedMovies = viewModel::deleteSelectedMovies, currentScreen = screen, onSelectListScreen = viewModel::setScreen, onResetDatabase = { scope.launch(Dispatchers.IO) { viewModel.resetDatabase() } } ) } is ActorDisplay -> { val selectedIds by viewModel.selectedIdsFlow.collectAsStateWithLifecycle(initialValue = emptySet()) ActorDisplayUi( id = screen.id, fetchActor = viewModel::getActorWithFilmography, onMovieClicked = { viewModel.pushScreen(MovieDisplay(it.id)) }, selectedIds = selectedIds, onClearSelections = viewModel::clearSelectedIds, onSelectionToggle = viewModel::toggleSelection, onDeleteSelectedMovies = viewModel::deleteSelectedMovies, currentScreen = screen, onSelectListScreen = viewModel::setScreen, onResetDatabase = { scope.launch(Dispatchers.IO) { viewModel.resetDatabase() } } ) } is RatingDisplay -> { val selectedIds by viewModel.selectedIdsFlow.collectAsStateWithLifecycle(initialValue = emptySet())val ratingWithMovies by viewModel.getRatingWithMoviesFlow(screen.id).collectAsStateWithLifecycle(initialValue = null) RatingDisplayUi(onMovieClicked = { viewModel.pushScreen(MovieDisplay(it.id)) },// id = screen.id,// fetchRating = viewModel::getRatingWithMovies,ratingWithMovies = ratingWithMovies,selectedIds = selectedIds, onClearSelections = viewModel::clearSelectedIds, onSelectionToggle = viewModel::toggleSelection, onDeleteSelectedMovies = viewModel::deleteSelectedMovies, currentScreen = screen, onSelectListScreen = viewModel::setScreen, onResetDatabase = { scope.launch(Dispatchers.IO) { viewModel.resetDatabase() } }) } MovieList -> { val movies by viewModel.moviesFlow.collectAsStateWithLifecycle(initialValue = emptyList()) val selectedIds by viewModel.selectedIdsFlow.collectAsStateWithLifecycle(initialValue = emptySet()) MovieListUi( movies = movies, onMovieClicked = { movie -> viewModel.pushScreen(MovieDisplay(movie.id)) }, selectedIds = selectedIds, onClearSelections = viewModel::clearSelectedIds, onSelectionToggle = viewModel::toggleSelection, onDeleteSelectedMovies = viewModel::deleteSelectedMovies, currentScreen = screen, onSelectListScreen = viewModel::setScreen, onResetDatabase = { scope.launch(Dispatchers.IO) { viewModel.resetDatabase() } } ) } ActorList -> { val actors by viewModel.actorsFlow.collectAsStateWithLifecycle(initialValue = emptyList()) val selectedIds by viewModel.selectedIdsFlow.collectAsStateWithLifecycle(initialValue = emptySet()) ActorListUi( actors = actors, onActorClicked = { actor -> viewModel.pushScreen(ActorDisplay(actor.id)) }, selectedIds = selectedIds, onClearSelections = viewModel::clearSelectedIds, onSelectionToggle = viewModel::toggleSelection, onDeleteSelectedActors = viewModel::deleteSelectedActors, currentScreen = screen, onSelectListScreen = viewModel::setScreen, onResetDatabase = { scope.launch(Dispatchers.IO) { viewModel.resetDatabase() } } ) } RatingList -> { val ratings by viewModel.ratingsFlow.collectAsStateWithLifecycle(initialValue = emptyList()) val selectedIds by viewModel.selectedIdsFlow.collectAsStateWithLifecycle(initialValue = emptySet()) RatingListUi( ratings = ratings,onRatingClicked = { rating -> viewModel.pushScreen(RatingDisplay(rating.id)) },selectedIds = selectedIds, onClearSelections = viewModel::clearSelectedIds, onSelectionToggle = viewModel::toggleSelection, onDeleteSelectedRatings = viewModel::deleteSelectedRatings, currentScreen = screen, onSelectListScreen = viewModel::setScreen, onResetDatabase = { scope.launch(Dispatchers.IO) { viewModel.resetDatabase() } } ) } } }
CHANGED: /data/src/main/java/com/androidbyexample/movie/data/MovieDao.kt
package com.androidbyexample.movie.data import androidx.room.Dao import androidx.room.Insert import androidx.room.Query import androidx.room.Transaction import kotlinx.coroutines.flow.Flow @Dao abstract class MovieDao { @Query("SELECT * FROM RatingEntity") abstract fun getRatingsFlow(): Flow<List<RatingEntity>> @Query("SELECT * FROM MovieEntity") abstract fun getMoviesFlow(): Flow<List<MovieEntity>> @Query("SELECT * FROM ActorEntity") abstract fun getActorsFlow(): Flow<List<ActorEntity>> @Transaction @Query("SELECT * FROM RatingEntity WHERE id = :id") abstract suspend fun getRatingWithMovies(id: String): RatingWithMovies@Transaction @Query("SELECT * FROM RatingEntity WHERE id = :id") abstract fun getRatingWithMoviesFlow(id: String): Flow<RatingWithMovies>@Transaction @Query("SELECT * FROM ActorEntity WHERE id = :id") abstract suspend fun getActorWithFilmography(id: String): ActorWithFilmography @Transaction @Query("SELECT * FROM MovieEntity WHERE id = :id") abstract suspend fun getMovieWithCast(id: String): MovieWithCast @Insert abstract suspend fun insert(vararg ratings: RatingEntity) @Insert abstract suspend fun insert(vararg movies: MovieEntity) @Insert abstract suspend fun insert(vararg actors: ActorEntity) @Insert abstract suspend fun insert(vararg roles: RoleEntity) @Query("DELETE FROM MovieEntity WHERE id IN (:ids)") abstract suspend fun deleteMoviesById(ids: Set<String>) @Query("DELETE FROM ActorEntity WHERE id IN (:ids)") abstract suspend fun deleteActorsById(ids: Set<String>) @Query("DELETE FROM RatingEntity WHERE id IN (:ids)") abstract suspend fun deleteRatingsById(ids: Set<String>) @Query("DELETE FROM MovieEntity") abstract suspend fun clearMovies() @Query("DELETE FROM ActorEntity") abstract suspend fun clearActors() @Query("DELETE FROM RatingEntity") abstract suspend fun clearRatings() @Query("DELETE FROM RoleEntity") abstract suspend fun clearRoles() @Transaction open suspend fun resetDatabase() { clearMovies() clearActors() clearRoles() clearRatings() insert( RatingEntity(id = "r0", name = "Not Rated", description = "Not yet rated"), RatingEntity(id = "r1", name = "G", description = "General Audiences"), RatingEntity(id = "r2", name = "PG", description = "Parental Guidance Suggested"), RatingEntity(id = "r3", name = "PG-13", description = "Unsuitable for those under 13"), RatingEntity(id = "r4", name = "R", description = "Restricted - 17 and older"), ) insert( MovieEntity("m1", "The Transporter", "Jason Statham kicks a guy in the face", "r3"), MovieEntity("m2", "Transporter 2", "Jason Statham kicks a bunch of guys in the face", "r4"), MovieEntity("m3", "Hobbs and Shaw", "Cars, Explosions and Stuff", "r3"), MovieEntity("m4", "Jumanji - Welcome to the Jungle", "The Rock smolders", "r3"), ) insert( ActorEntity("a1", "Jason Statham"), ActorEntity("a2", "The Rock"), ActorEntity("a3", "Shu Qi"), ActorEntity("a4", "Amber Valletta"), ActorEntity("a5", "Kevin Hart"), ) insert( RoleEntity("m1", "a1", "Frank Martin", 1), RoleEntity("m1", "a3", "Lai", 2), RoleEntity("m2", "a1", "Frank Martin", 1), RoleEntity("m2", "a4", "Audrey Billings", 2), RoleEntity("m3", "a2", "Hobbs", 1), RoleEntity("m3", "a1", "Shaw", 2), RoleEntity("m4", "a2", "Spencer", 1), RoleEntity("m4", "a5", "Fridge", 2), ) } }
CHANGED: /repository/src/main/java/com/androidbyexample/movie/repository/ActorDto.kt
package com.androidbyexample.movie.repository import com.androidbyexample.movie.data.ActorEntity import com.androidbyexample.movie.data.ActorWithFilmography import com.androidbyexample.movie.data.RoleWithMovie data class ActorDto( override val id: String, val name: String, ): HasId internal fun ActorEntity.toDto() = ActorDto(id = id, name = name) internal fun ActorDto.toEntity() = ActorEntity(id = id, name = name) data class ActorWithFilmographyDto( val actor: ActorDto, val filmography: List<RoleWithMovieDto>, ) data class RoleWithMovieDto( val movie: MovieDto, val character: String, val orderInCredits: Int, ): HasId {override val id: String get() = "${movie.id}:$character"} internal fun RoleWithMovie.toDto() = RoleWithMovieDto( movie = movie.toDto(), character = role.character, orderInCredits = role.orderInCredits, ) internal fun ActorWithFilmography.toDto() = ActorWithFilmographyDto( actor = actor.toDto(), filmography = rolesWithMovies.map { it.toDto() } )
CHANGED: /repository/src/main/java/com/androidbyexample/movie/repository/MovieDatabaseRepository.kt
package com.androidbyexample.movie.repository import android.content.Context import com.androidbyexample.movie.data.MovieDao import com.androidbyexample.movie.data.createDao import kotlinx.coroutines.flow.Flowimport kotlinx.coroutines.flow.map class MovieDatabaseRepository( private val dao: MovieDao ): MovieRepository { override val ratingsFlow = dao.getRatingsFlow() .map { ratings ->// for each List<RatingEntity> that's emitted // create a list of RatingDto ratings.map { rating -> rating.toDto() } // map each entity to Dto } override val moviesFlow = dao.getMoviesFlow() .map { movies -> movies.map { it.toDto() } } override val actorsFlow = dao.getActorsFlow() .map { actors -> actors.map { it.toDto() } }override fun getRatingWithMoviesFlow(id: String): Flow<RatingWithMoviesDto> = dao.getRatingWithMoviesFlow(id) .map { it.toDto() }override suspend fun getRatingWithMovies(id: String): RatingWithMoviesDto = dao.getRatingWithMovies(id).toDto() override suspend fun getMovieWithCast(id: String): MovieWithCastDto = dao.getMovieWithCast(id).toDto() override suspend fun getActorWithFilmography(id: String): ActorWithFilmographyDto = dao.getActorWithFilmography(id).toDto() override suspend fun insert(movie: MovieDto) = dao.insert(movie.toEntity()) override suspend fun insert(actor: ActorDto) = dao.insert(actor.toEntity()) override suspend fun insert(rating: RatingDto) = dao.insert(rating.toEntity()) override suspend fun deleteMoviesById(ids: Set<String>) = dao.deleteMoviesById(ids) override suspend fun deleteActorsById(ids: Set<String>) = dao.deleteActorsById(ids) override suspend fun deleteRatingsById(ids: Set<String>) = dao.deleteRatingsById(ids) override suspend fun resetDatabase() = dao.resetDatabase() companion object { fun create(context: Context) = MovieDatabaseRepository(createDao(context)) } }
CHANGED: /repository/src/main/java/com/androidbyexample/movie/repository/MovieDto.kt
package com.androidbyexample.movie.repository import com.androidbyexample.movie.data.MovieEntity import com.androidbyexample.movie.data.MovieWithCast import com.androidbyexample.movie.data.RoleWithActor data class MovieDto( override val id: String, val title: String, val description: String, val ratingId: String, ): HasId internal fun MovieEntity.toDto() = MovieDto(id = id, title = title, description = description, ratingId = ratingId) internal fun MovieDto.toEntity() = MovieEntity(id = id, title = title, description = description, ratingId = ratingId) data class MovieWithCastDto( val movie: MovieDto, val cast: List<RoleWithActorDto>, ) data class RoleWithActorDto( val actor: ActorDto, val character: String, val orderInCredits: Int, ): HasId {override val id: String get() = "${actor.id}:$character"} internal fun RoleWithActor.toDto() = RoleWithActorDto( actor = actor.toDto(), character = role.character, orderInCredits = role.orderInCredits, ) internal fun MovieWithCast.toDto() = MovieWithCastDto( movie = movie.toDto(), cast = rolesWithActors.map { it.toDto() } )
CHANGED: /repository/src/main/java/com/androidbyexample/movie/repository/MovieRepository.kt
package com.androidbyexample.movie.repository import kotlinx.coroutines.flow.Flow interface MovieRepository { val ratingsFlow: Flow<List<RatingDto>> val moviesFlow: Flow<List<MovieDto>> val actorsFlow: Flow<List<ActorDto>>fun getRatingWithMoviesFlow(id: String): Flow<RatingWithMoviesDto>suspend fun getRatingWithMovies(id: String): RatingWithMoviesDto suspend fun getMovieWithCast(id: String): MovieWithCastDto suspend fun getActorWithFilmography(id: String): ActorWithFilmographyDto suspend fun insert(movie: MovieDto) suspend fun insert(actor: ActorDto) suspend fun insert(rating: RatingDto) suspend fun deleteMoviesById(ids: Set<String>) suspend fun deleteActorsById(ids: Set<String>) suspend fun deleteRatingsById(ids: Set<String>) suspend fun resetDatabase() }