Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed caching of settings values in multiplatform settings #110

Merged
merged 12 commits into from
Jan 3, 2024
Merged
2 changes: 1 addition & 1 deletion appDesktop/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ dependencies {

compose.desktop {
application {
mainClass = "com.vickbt.notflix.NotflixApplicationKt"
mainClass = "NotflixApplicationKt"
}
}
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ kotlinxDateTime = "0.5.0"
napier = "2.6.1"
ktor = "2.3.7"
sqlDelight = "2.0.1"
multiplatformSettings = "0.8.1"
multiplatformSettings = "1.1.1"
kmpNativeCoroutines = "0.12.1-new-mm"
buildKonfig = "0.13.3"
kover = "0.6.1"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package com.vickbt.shared.utils

import android.content.Context
import com.russhwolf.settings.AndroidSettings
import com.russhwolf.settings.ObservableSettings
import com.russhwolf.settings.SharedPreferencesSettings

actual class MultiplatformSettingsWrapper {
val context = ContextUtils.context
private val context = ContextUtils.context

actual fun createSettings(): ObservableSettings {
val sharedPreferences =
context.getSharedPreferences("notflix_preferences", Context.MODE_PRIVATE)
return AndroidSettings(delegate = sharedPreferences)
return SharedPreferencesSettings(delegate = sharedPreferences)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import com.vickbt.shared.domain.models.Cast
import com.vickbt.shared.domain.models.Movie
import com.vickbt.shared.domain.models.MovieDetails
import com.vickbt.shared.domain.repositories.MovieDetailsRepository
import com.vickbt.shared.utils.NetworkResultState
import com.vickbt.shared.utils.ResultState
import com.vickbt.shared.utils.toBoolean
import io.ktor.client.HttpClient
import io.ktor.client.call.body
Expand All @@ -24,15 +24,15 @@ class MovieDetailsRepositoryImpl(
private val favoriteMovieDao: FavoriteMovieDao
) : MovieDetailsRepository {

override suspend fun fetchMovieDetails(movieId: Int): Flow<NetworkResultState<MovieDetails>> {
override suspend fun fetchMovieDetails(movieId: Int): Flow<ResultState<MovieDetails>> {
val isMovieCached = isMovieFavorite(movieId = movieId)

return if (isMovieCached == true) {
try {
val cachedFavoriteMovie = getFavoriteMovie(movieId = movieId)
flowOf(NetworkResultState.Success(data = cachedFavoriteMovie))
flowOf(ResultState.Success(data = cachedFavoriteMovie))
} catch (e: Exception) {
flowOf(NetworkResultState.Failure(exception = e))
flowOf(ResultState.Failure(exception = e))
}
} else {
flowOf(
Expand All @@ -45,7 +45,7 @@ class MovieDetailsRepositoryImpl(
}
}

override suspend fun fetchMovieCast(movieId: Int): Flow<NetworkResultState<Cast>> {
override suspend fun fetchMovieCast(movieId: Int): Flow<ResultState<Cast>> {
return flowOf(
safeApiCall {
val response = httpClient.get(urlString = "movie/$movieId/credits").body<CastDto>()
Expand All @@ -58,7 +58,7 @@ class MovieDetailsRepositoryImpl(
override suspend fun fetchSimilarMovies(
movieId: Int,
page: Int
): Flow<NetworkResultState<List<Movie>?>> {
): Flow<ResultState<List<Movie>?>> {
return flowOf(
safeApiCall {
val response = httpClient.get(urlString = "movie/$movieId/similar") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import com.vickbt.shared.data.network.models.MovieResultsDto
import com.vickbt.shared.data.network.utils.safeApiCall
import com.vickbt.shared.domain.models.Movie
import com.vickbt.shared.domain.repositories.MoviesRepository
import com.vickbt.shared.utils.NetworkResultState
import com.vickbt.shared.utils.ResultState
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.get
Expand All @@ -17,7 +17,7 @@ class MoviesRepositoryImpl constructor(
private val httpClient: HttpClient
) : MoviesRepository {

override suspend fun fetchNowPlayingMovies(page: Int): Flow<NetworkResultState<List<Movie>?>> {
override suspend fun fetchNowPlayingMovies(page: Int): Flow<ResultState<List<Movie>?>> {
return flowOf(
safeApiCall {
val response = httpClient.get(urlString = "movie/now_playing") {
Expand All @@ -33,7 +33,7 @@ class MoviesRepositoryImpl constructor(
mediaType: String,
timeWindow: String,
page: Int
): Flow<NetworkResultState<List<Movie>?>> {
): Flow<ResultState<List<Movie>?>> {
return flowOf(
safeApiCall {
val response = httpClient.get(urlString = "trending/$mediaType/$timeWindow") {
Expand All @@ -45,7 +45,7 @@ class MoviesRepositoryImpl constructor(
)
}

override suspend fun fetchPopularMovies(page: Int): Flow<NetworkResultState<List<Movie>?>> {
override suspend fun fetchPopularMovies(page: Int): Flow<ResultState<List<Movie>?>> {
return flowOf(
safeApiCall {
val response = httpClient.get(urlString = "movie/popular") {
Expand All @@ -57,7 +57,7 @@ class MoviesRepositoryImpl constructor(
)
}

override suspend fun fetchUpcomingMovies(page: Int): Flow<NetworkResultState<List<Movie>?>> {
override suspend fun fetchUpcomingMovies(page: Int): Flow<ResultState<List<Movie>?>> {
return flowOf(
safeApiCall {
val response = httpClient.get(urlString = "movie/upcoming") {
Expand All @@ -82,7 +82,7 @@ class MoviesRepositoryImpl constructor(
override suspend fun searchMovie(
movieName: String,
page: Int
): Flow<NetworkResultState<List<Movie>?>> {
): Flow<ResultState<List<Movie>?>> {
return flowOf(
safeApiCall {
val response = httpClient.get(urlString = "search/movie") {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
package com.vickbt.shared.data.datasources

import com.russhwolf.settings.ExperimentalSettingsApi
import com.russhwolf.settings.ObservableSettings
import com.russhwolf.settings.coroutines.getIntOrNullFlow
import com.russhwolf.settings.coroutines.getIntFlow
import com.vickbt.shared.domain.repositories.SettingsRepository
import com.vickbt.shared.domain.utils.Constants.KEY_IMAGE_QUALITY
import com.vickbt.shared.domain.utils.Constants.KEY_THEME
import kotlinx.coroutines.flow.Flow

class SettingsRepositoryImpl constructor(private val observableSettings: ObservableSettings) :
@ExperimentalSettingsApi
class SettingsRepositoryImpl(private val observableSettings: ObservableSettings) :
SettingsRepository {

override suspend fun savePreferenceSelection(key: String, selection: Int) =
observableSettings.putInt(key = key, value = selection)

override suspend fun getThemePreference(): Flow<Int?> {
return observableSettings.getIntOrNullFlow(key = KEY_THEME)
override suspend fun getThemePreference(): Flow<Int> {
return observableSettings.getIntFlow(key = KEY_THEME, defaultValue = 2)
}

override suspend fun getImageQualityPreference(): Flow<Int?> {
return observableSettings.getIntOrNullFlow(key = KEY_IMAGE_QUALITY)
override suspend fun getImageQualityPreference(): Flow<Int> {
return observableSettings.getIntFlow(key = KEY_IMAGE_QUALITY, defaultValue = 1)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,34 @@ package com.vickbt.shared.data.network.utils
import com.vickbt.shared.data.mappers.toDomain
import com.vickbt.shared.data.network.models.ErrorResponseDto
import com.vickbt.shared.domain.models.ErrorResponse
import com.vickbt.shared.utils.NetworkResultState
import com.vickbt.shared.utils.ResultState
import io.ktor.client.call.body
import io.ktor.client.plugins.ClientRequestException
import io.ktor.client.plugins.RedirectResponseException
import io.ktor.client.plugins.ServerResponseException
import io.ktor.client.statement.HttpResponse
import io.ktor.util.network.UnresolvedAddressException

suspend fun <T : Any?> safeApiCall(apiCall: suspend () -> T): NetworkResultState<T> {
suspend fun <T : Any?> safeApiCall(apiCall: suspend () -> T): ResultState<T> {
return try {
NetworkResultState.Loading
ResultState.Loading

NetworkResultState.Success(apiCall.invoke())
ResultState.Success(apiCall.invoke())
} catch (e: RedirectResponseException) {
val error = parseNetworkError(e.response.body())
NetworkResultState.Failure(exception = error)
ResultState.Failure(exception = error)
} catch (e: ClientRequestException) {
val error = parseNetworkError(e.response.body())
NetworkResultState.Failure(exception = error)
ResultState.Failure(exception = error)
} catch (e: ServerResponseException) {
val error = parseNetworkError(e.response.body())
NetworkResultState.Failure(exception = error)
ResultState.Failure(exception = error)
} catch (e: UnresolvedAddressException) {
val error = parseNetworkError(exception = e)
NetworkResultState.Failure(exception = error)
ResultState.Failure(exception = error)
} catch (e: Exception) {
val error = parseNetworkError(exception = e)
NetworkResultState.Failure(exception = error)
ResultState.Failure(exception = error)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import com.vickbt.shared.domain.utils.Constants.BASE_URL
import com.vickbt.shared.domain.utils.Constants.URL_PATH
import com.vickbt.shared.presentation.ui.screens.home.HomeViewModel
import com.vickbt.shared.presentation.ui.screens.main.MainViewModel
import com.vickbt.shared.presentation.ui.screens.settings.SettingsViewModel
import com.vickbt.shared.ui.screens.settings.SettingsViewModel
import com.vickbt.shared.ui.screens.details.DetailsViewModel
import com.vickbt.shared.ui.screens.favorites.FavoritesViewModel
import io.github.aakira.napier.DebugAntilog
Expand Down
2 changes: 1 addition & 1 deletion shared/src/commonMain/kotlin/com/vickbt/shared/di/Koin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.vickbt.shared.di

import com.vickbt.shared.presentation.ui.screens.home.HomeViewModel
import com.vickbt.shared.presentation.ui.screens.main.MainViewModel
import com.vickbt.shared.presentation.ui.screens.settings.SettingsViewModel
import com.vickbt.shared.ui.screens.settings.SettingsViewModel
import org.koin.core.Koin
import org.koin.core.KoinApplication
import org.koin.core.context.startKoin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,22 @@ import com.vickbt.shared.domain.models.Cast
import com.vickbt.shared.domain.models.Movie
import com.vickbt.shared.domain.models.MovieDetails
import com.vickbt.shared.domain.utils.Constants.STARTING_PAGE_INDEX
import com.vickbt.shared.utils.NetworkResultState
import com.vickbt.shared.utils.ResultState
import kotlinx.coroutines.flow.Flow

interface MovieDetailsRepository {

/**Fetch movie details from network source*/
suspend fun fetchMovieDetails(movieId: Int): Flow<NetworkResultState<MovieDetails>>
suspend fun fetchMovieDetails(movieId: Int): Flow<ResultState<MovieDetails>>

/**Fetch movie cast from network source*/
suspend fun fetchMovieCast(movieId: Int): Flow<NetworkResultState<Cast>>
suspend fun fetchMovieCast(movieId: Int): Flow<ResultState<Cast>>

/** Fetches similar movies from network source*/
suspend fun fetchSimilarMovies(
movieId: Int,
page: Int = STARTING_PAGE_INDEX
): Flow<NetworkResultState<List<Movie>?>>
): Flow<ResultState<List<Movie>?>>

/**Save movie details to local cache*/
suspend fun saveFavoriteMovie(movie: MovieDetails)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,26 @@ package com.vickbt.shared.domain.repositories

import com.vickbt.shared.domain.models.Movie
import com.vickbt.shared.domain.utils.Constants.STARTING_PAGE_INDEX
import com.vickbt.shared.utils.NetworkResultState
import com.vickbt.shared.utils.ResultState
import kotlinx.coroutines.flow.Flow

interface MoviesRepository {

/** Fetch Now Playing movies from data source*/
suspend fun fetchNowPlayingMovies(page: Int = STARTING_PAGE_INDEX): Flow<NetworkResultState<List<Movie>?>>
suspend fun fetchNowPlayingMovies(page: Int = STARTING_PAGE_INDEX): Flow<ResultState<List<Movie>?>>

/** Fetch Trending movies from data source*/
suspend fun fetchTrendingMovies(
mediaType: String = "movie",
timeWindow: String = "week",
page: Int = STARTING_PAGE_INDEX
): Flow<NetworkResultState<List<Movie>?>>
): Flow<ResultState<List<Movie>?>>

/** Fetch Popular movies from data source*/
suspend fun fetchPopularMovies(page: Int = STARTING_PAGE_INDEX): Flow<NetworkResultState<List<Movie>?>>
suspend fun fetchPopularMovies(page: Int = STARTING_PAGE_INDEX): Flow<ResultState<List<Movie>?>>

/** Fetch Upcoming movies from data source*/
suspend fun fetchUpcomingMovies(page: Int = STARTING_PAGE_INDEX): Flow<NetworkResultState<List<Movie>?>>
suspend fun fetchUpcomingMovies(page: Int = STARTING_PAGE_INDEX): Flow<ResultState<List<Movie>?>>

/** Get movies based on category from cache*/
/*@Deprecated("Pending caching implementation")
Expand All @@ -31,5 +31,5 @@ interface MoviesRepository {
suspend fun searchMovie(
movieName: String,
page: Int = STARTING_PAGE_INDEX
): Flow<NetworkResultState<List<Movie>?>>
): Flow<ResultState<List<Movie>?>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ interface SettingsRepository {

suspend fun savePreferenceSelection(key: String, selection: Int)

suspend fun getThemePreference(): Flow<Int?>
suspend fun getThemePreference(): Flow<Int>

suspend fun getImageQualityPreference(): Flow<Int?>
suspend fun getImageQualityPreference(): Flow<Int>
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,11 @@ fun HomeScreen(
) {
//region Search
SearchBar(
modifier = Modifier.fillMaxWidth().background(MaterialTheme.colorScheme.surface),
modifier = Modifier.background(
MaterialTheme.colorScheme.surface
).also {
if (activeState) it.fillMaxWidth() else it.fillMaxWidth(.85f)
},
query = searchQuery,
onQueryChange = { searchQuery = it },
onSearch = { viewModel.searchMovie(movieName = it) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,15 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Image
import androidx.compose.material.icons.rounded.Lightbulb
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import com.vickbt.shared.domain.utils.Constants.KEY_IMAGE_QUALITY
import com.vickbt.shared.domain.utils.Constants.KEY_THEME
import com.vickbt.shared.presentation.ui.screens.settings.SettingsViewModel
import com.vickbt.shared.ui.components.appbars.AppBar
import com.vickbt.shared.ui.components.preferences.DialogPreferenceSelection
import com.vickbt.shared.ui.components.preferences.PreferencesGroup
Expand All @@ -26,14 +23,8 @@ import org.koin.compose.koinInject
private val themeLabels = listOf("Light", "Dark", "System Default")
private val imageQualityLabels = listOf("High Quality", "Low Quality")

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SettingsScreen(viewModel: SettingsViewModel = koinInject()) {
LaunchedEffect(key1 = viewModel) {
viewModel.getThemePreference()
viewModel.getImageQualityPreference()
}

val settingsUiState = viewModel.settingsUiState.collectAsState().value

val showThemeDialog = remember { mutableStateOf(false) }
Expand Down
Loading
Loading