add following tab property in datastore

This commit is contained in:
Stefano Sansone
2024-04-25 14:24:16 +02:00
parent 4b402c8439
commit 2101815f36
13 changed files with 258 additions and 193 deletions

View File

@ -0,0 +1,18 @@
package app.omnivore.omnivore.core.datastore
const val omnivoreSelfHostedApiServer = "omnivoreSelfHostedAPIServer"
const val omnivoreSelfHostedWebServer = "omnivoreSelfHostedWebServer"
const val omnivoreAuthToken = "omnivoreAuthToken"
const val omnivoreAuthCookieString = "omnivoreAuthCookieString"
const val omnivorePendingUserToken = "omnivorePendingUserToken"
const val libraryLastSyncTimestamp = "libraryLastSyncTimestamp"
const val preferredWebFontSize = "preferredWebFontSize"
const val preferredWebLineHeight = "preferredWebLineHeight"
const val preferredWebMaxWidthPercentage = "preferredWebMaxWidthPercentage"
const val preferredWebFontFamily = "preferredWebFontFamily"
const val prefersWebHighContrastText = "prefersWebHighContrastText"
const val prefersJustifyText = "prefersJustifyText"
const val lastUsedSavedItemFilter = "lastUsedSavedItemFilter"
const val lastUsedSavedItemSortFilter = "lastUsedSavedItemSortFilter"
const val preferredTheme = "preferredTheme"
const val followingTabActive = "followingTabActive"

View File

@ -2,79 +2,96 @@ package app.omnivore.omnivore.core.datastore
import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.*
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import app.omnivore.omnivore.utils.Constants
import app.omnivore.omnivore.utils.DatastoreKeys
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import javax.inject.Inject
interface DatastoreRepository {
val hasAuthTokenFlow: Flow<Boolean>
val themeKeyFlow: Flow<String>
val hasAuthTokenFlow: Flow<Boolean>
val themeKeyFlow: Flow<String>
suspend fun clear()
suspend fun putString(key: String, value: String)
suspend fun putInt(key: String, value: Int)
suspend fun getString(key: String): String?
suspend fun getInt(key: String): Int?
suspend fun clearValue(key: String)
suspend fun clear()
suspend fun putBoolean(key: String, value: Boolean)
fun getBoolean(key: String): Flow<Boolean>
suspend fun putString(key: String, value: String)
suspend fun putInt(key: String, value: Int)
suspend fun getString(key: String): String?
suspend fun getInt(key: String): Int?
suspend fun clearValue(key: String)
}
class OmnivoreDatastore @Inject constructor(
private val context: Context
private val context: Context
) : DatastoreRepository {
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(
name = Constants.dataStoreName
)
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(
name = Constants.dataStoreName
)
override suspend fun putString(key: String, value: String) {
val preferencesKey = stringPreferencesKey(key)
context.dataStore.edit { preferences ->
preferences[preferencesKey] = value
}
}
override suspend fun putInt(key: String, value: Int) {
val preferencesKey = intPreferencesKey(key)
context.dataStore.edit { preferences ->
preferences[preferencesKey] = value
}
}
override suspend fun getString(key: String): String? {
val preferencesKey = stringPreferencesKey(key)
val preferences = context.dataStore.data.first()
return preferences[preferencesKey]
}
override suspend fun getInt(key: String): Int? {
val preferencesKey = intPreferencesKey(key)
val preferences = context.dataStore.data.first()
return preferences[preferencesKey]
}
override suspend fun clear() {
context.dataStore.edit { it.clear() }
}
override suspend fun clearValue(key: String) {
val preferencesKey = stringPreferencesKey(key)
context.dataStore.edit { it.remove(preferencesKey) }
}
override val hasAuthTokenFlow: Flow<Boolean> = context
.dataStore.data.map { preferences ->
val key = stringPreferencesKey(DatastoreKeys.omnivoreAuthToken)
val token = preferences[key]
token != null
override suspend fun putBoolean(key: String, value: Boolean) {
val preferencesKey = booleanPreferencesKey(key)
context.dataStore.edit { preferences ->
preferences[preferencesKey] = value
}
}
override val themeKeyFlow: Flow<String> = context
.dataStore.data.map { preferences ->
val key = stringPreferencesKey(DatastoreKeys.preferredTheme)
preferences[key] ?: "System"
override fun getBoolean(key: String): Flow<Boolean> {
val preferencesKey = booleanPreferencesKey(key)
return context.dataStore.data.map { preferences ->
preferences[preferencesKey] ?: false
}
}
override suspend fun putString(key: String, value: String) {
val preferencesKey = stringPreferencesKey(key)
context.dataStore.edit { preferences ->
preferences[preferencesKey] = value
}
}
override suspend fun putInt(key: String, value: Int) {
val preferencesKey = intPreferencesKey(key)
context.dataStore.edit { preferences ->
preferences[preferencesKey] = value
}
}
override suspend fun getString(key: String): String? {
val preferencesKey = stringPreferencesKey(key)
val preferences = context.dataStore.data.first()
return preferences[preferencesKey]
}
override suspend fun getInt(key: String): Int? {
val preferencesKey = intPreferencesKey(key)
val preferences = context.dataStore.data.first()
return preferences[preferencesKey]
}
override suspend fun clear() {
context.dataStore.edit { it.clear() }
}
override suspend fun clearValue(key: String) {
val preferencesKey = stringPreferencesKey(key)
context.dataStore.edit { it.remove(preferencesKey) }
}
override val hasAuthTokenFlow: Flow<Boolean> = context.dataStore.data.map { preferences ->
val key = stringPreferencesKey(omnivoreAuthToken)
val token = preferences[key]
token != null
}
override val themeKeyFlow: Flow<String> = context.dataStore.data.map { preferences ->
val key = stringPreferencesKey(preferredTheme)
preferences[key] ?: "System"
}
}

View File

@ -1,8 +1,9 @@
package app.omnivore.omnivore.core.network
import app.omnivore.omnivore.core.datastore.DatastoreRepository
import app.omnivore.omnivore.core.datastore.omnivoreAuthToken
import app.omnivore.omnivore.core.datastore.omnivoreSelfHostedApiServer
import app.omnivore.omnivore.utils.Constants
import app.omnivore.omnivore.utils.DatastoreKeys
import com.apollographql.apollo3.ApolloClient
import javax.inject.Inject
@ -10,10 +11,10 @@ class Networker @Inject constructor(
private val datastoreRepo: DatastoreRepository
) {
suspend fun baseUrl() =
datastoreRepo.getString(DatastoreKeys.omnivoreSelfHostedAPIServer) ?: Constants.apiURL
datastoreRepo.getString(omnivoreSelfHostedApiServer) ?: Constants.apiURL
private suspend fun serverUrl() = "${baseUrl()}/api/graphql"
private suspend fun authToken() = datastoreRepo.getString(DatastoreKeys.omnivoreAuthToken) ?: ""
private suspend fun authToken() = datastoreRepo.getString(omnivoreAuthToken) ?: ""
suspend fun authenticatedApolloClient() = ApolloClient.Builder().serverUrl(serverUrl())
.addHttpHeader("Authorization", value = authToken()).build()

View File

@ -5,11 +5,21 @@ import android.widget.Toast
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.*
import app.omnivore.omnivore.*
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import app.omnivore.omnivore.BuildConfig
import app.omnivore.omnivore.R
import app.omnivore.omnivore.core.analytics.EventTracker
import app.omnivore.omnivore.core.data.DataService
import app.omnivore.omnivore.core.datastore.DatastoreRepository
import app.omnivore.omnivore.core.datastore.omnivoreAuthCookieString
import app.omnivore.omnivore.core.datastore.omnivoreAuthToken
import app.omnivore.omnivore.core.datastore.omnivorePendingUserToken
import app.omnivore.omnivore.core.datastore.omnivoreSelfHostedApiServer
import app.omnivore.omnivore.core.datastore.omnivoreSelfHostedWebServer
import app.omnivore.omnivore.core.network.AuthProviderLoginSubmit
import app.omnivore.omnivore.core.network.CreateAccountParams
import app.omnivore.omnivore.core.network.CreateAccountSubmit
@ -17,24 +27,26 @@ import app.omnivore.omnivore.core.network.CreateEmailAccountSubmit
import app.omnivore.omnivore.core.network.EmailLoginCredentials
import app.omnivore.omnivore.core.network.EmailLoginSubmit
import app.omnivore.omnivore.core.network.EmailSignUpParams
import app.omnivore.omnivore.graphql.generated.ValidateUsernameQuery
import app.omnivore.omnivore.core.network.Networker
import app.omnivore.omnivore.core.network.PendingUserSubmit
import app.omnivore.omnivore.core.network.RetrofitHelper
import app.omnivore.omnivore.core.network.SignInParams
import app.omnivore.omnivore.core.network.UserProfile
import app.omnivore.omnivore.core.network.viewer
import app.omnivore.omnivore.utils.ResourceProvider
import app.omnivore.omnivore.graphql.generated.ValidateUsernameQuery
import app.omnivore.omnivore.utils.Constants
import app.omnivore.omnivore.utils.DatastoreKeys
import app.omnivore.omnivore.utils.ResourceProvider
import com.apollographql.apollo3.ApolloClient
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
import com.google.android.gms.common.api.ApiException
import com.google.android.gms.tasks.Task
import dagger.hilt.android.lifecycle.HiltViewModel
import io.intercom.android.sdk.Intercom
import kotlinx.coroutines.*
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.util.regex.Pattern
import javax.inject.Inject
@ -84,13 +96,13 @@ class LoginViewModel @Inject constructor(
val registrationStateLiveData = MutableLiveData(RegistrationState.SocialLogin)
fun getAuthCookieString(): String? = runBlocking {
datastoreRepo.getString(DatastoreKeys.omnivoreAuthCookieString)
datastoreRepo.getString(omnivoreAuthCookieString)
}
fun setSelfHostingDetails(context: Context, apiServer: String, webServer: String) {
viewModelScope.launch {
datastoreRepo.putString(DatastoreKeys.omnivoreSelfHostedAPIServer, apiServer)
datastoreRepo.putString(DatastoreKeys.omnivoreSelfHostedWebServer, webServer)
datastoreRepo.putString(omnivoreSelfHostedApiServer, apiServer)
datastoreRepo.putString(omnivoreSelfHostedWebServer, webServer)
Toast.makeText(
context,
context.getString(R.string.login_view_model_self_hosting_settings_updated),
@ -101,8 +113,8 @@ class LoginViewModel @Inject constructor(
fun resetSelfHostingDetails(context: Context) {
viewModelScope.launch {
datastoreRepo.clearValue(DatastoreKeys.omnivoreSelfHostedAPIServer)
datastoreRepo.clearValue(DatastoreKeys.omnivoreSelfHostedWebServer)
datastoreRepo.clearValue(omnivoreSelfHostedApiServer)
datastoreRepo.clearValue(omnivoreSelfHostedWebServer)
Toast.makeText(
context,
context.getString(R.string.login_view_model_self_hosting_settings_reset),
@ -136,7 +148,7 @@ class LoginViewModel @Inject constructor(
fun cancelNewUserSignUp() {
resetState()
viewModelScope.launch {
datastoreRepo.clearValue(DatastoreKeys.omnivorePendingUserToken)
datastoreRepo.clearValue(omnivorePendingUserToken)
}
showSocialLogin()
}
@ -235,7 +247,7 @@ class LoginViewModel @Inject constructor(
}
if (result.body()?.authToken != null) {
datastoreRepo.putString(DatastoreKeys.omnivoreAuthToken, result.body()?.authToken!!)
datastoreRepo.putString(omnivoreAuthToken, result.body()?.authToken!!)
} else {
errorMessage = resourceProvider.getString(
R.string.login_view_model_something_went_wrong_error_msg)
@ -243,7 +255,7 @@ class LoginViewModel @Inject constructor(
if (result.body()?.authCookieString != null) {
datastoreRepo.putString(
DatastoreKeys.omnivoreAuthCookieString, result.body()?.authCookieString!!
omnivoreAuthCookieString, result.body()?.authCookieString!!
)
}
}
@ -282,7 +294,7 @@ class LoginViewModel @Inject constructor(
}
private fun getPendingAuthToken(): String? = runBlocking {
datastoreRepo.getString(DatastoreKeys.omnivorePendingUserToken)
datastoreRepo.getString(omnivorePendingUserToken)
}
fun submitProfile(username: String, name: String) {
@ -305,7 +317,7 @@ class LoginViewModel @Inject constructor(
isLoading = false
if (result.body()?.authToken != null) {
datastoreRepo.putString(DatastoreKeys.omnivoreAuthToken, result.body()?.authToken!!)
datastoreRepo.putString(omnivoreAuthToken, result.body()?.authToken!!)
} else {
errorMessage = resourceProvider.getString(
R.string.login_view_model_something_went_wrong_error_msg)
@ -313,7 +325,7 @@ class LoginViewModel @Inject constructor(
if (result.body()?.authCookieString != null) {
datastoreRepo.putString(
DatastoreKeys.omnivoreAuthCookieString, result.body()?.authCookieString!!
omnivoreAuthCookieString, result.body()?.authCookieString!!
)
}
}
@ -371,11 +383,11 @@ class LoginViewModel @Inject constructor(
isLoading = false
if (result.body()?.authToken != null) {
datastoreRepo.putString(DatastoreKeys.omnivoreAuthToken, result.body()?.authToken!!)
datastoreRepo.putString(omnivoreAuthToken, result.body()?.authToken!!)
if (result.body()?.authCookieString != null) {
datastoreRepo.putString(
DatastoreKeys.omnivoreAuthCookieString, result.body()?.authCookieString!!
omnivoreAuthCookieString, result.body()?.authCookieString!!
)
}
} else {
@ -409,7 +421,7 @@ class LoginViewModel @Inject constructor(
if (result.body()?.pendingUserToken != null) {
datastoreRepo.putString(
DatastoreKeys.omnivorePendingUserToken, result.body()?.pendingUserToken!!
omnivorePendingUserToken, result.body()?.pendingUserToken!!
)
registrationStateLiveData.value = RegistrationState.PendingUser
} else {

View File

@ -6,13 +6,13 @@ import androidx.compose.runtime.setValue
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import app.omnivore.omnivore.utils.Constants
import app.omnivore.omnivore.utils.DatastoreKeys
import app.omnivore.omnivore.core.datastore.DatastoreRepository
import app.omnivore.omnivore.R
import app.omnivore.omnivore.core.data.DataService
import app.omnivore.omnivore.core.datastore.DatastoreRepository
import app.omnivore.omnivore.core.datastore.omnivoreAuthToken
import app.omnivore.omnivore.graphql.generated.UpdatePageMutation
import app.omnivore.omnivore.graphql.generated.type.UpdatePageInput
import app.omnivore.omnivore.utils.Constants
import app.omnivore.omnivore.utils.ResourceProvider
import com.apollographql.apollo3.ApolloClient
import com.apollographql.apollo3.api.Optional
@ -45,7 +45,7 @@ class EditInfoViewModel @Inject constructor(
private set
private fun getAuthToken(): String? = runBlocking {
datastoreRepo.getString(DatastoreKeys.omnivoreAuthToken)
datastoreRepo.getString(omnivoreAuthToken)
}
fun editInfo(itemId: String, title: String, author: String?, description: String?) {

View File

@ -13,12 +13,14 @@ import app.omnivore.omnivore.core.data.repository.LibraryRepository
import app.omnivore.omnivore.core.database.entities.SavedItemLabel
import app.omnivore.omnivore.core.database.entities.SavedItemWithLabelsAndHighlights
import app.omnivore.omnivore.core.datastore.DatastoreRepository
import app.omnivore.omnivore.core.datastore.lastUsedSavedItemFilter
import app.omnivore.omnivore.core.datastore.lastUsedSavedItemSortFilter
import app.omnivore.omnivore.core.datastore.libraryLastSyncTimestamp
import app.omnivore.omnivore.feature.library.LibraryBottomSheetState
import app.omnivore.omnivore.feature.library.SavedItemAction
import app.omnivore.omnivore.feature.library.SavedItemFilter
import app.omnivore.omnivore.feature.library.SavedItemSortFilter
import app.omnivore.omnivore.feature.library.SavedItemViewModel
import app.omnivore.omnivore.utils.DatastoreKeys
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
@ -118,7 +120,7 @@ class FollowingViewModel @Inject constructor(
}
private fun getLastSyncTime(): Instant? = runBlocking {
datastoreRepo.getString(DatastoreKeys.libraryLastSyncTimestamp)?.let {
datastoreRepo.getString(libraryLastSyncTimestamp)?.let {
try {
return@let Instant.parse(it)
} catch (e: Exception) {
@ -164,7 +166,7 @@ class FollowingViewModel @Inject constructor(
fun updateSavedItemFilter(filter: SavedItemFilter) {
viewModelScope.launch {
datastoreRepo.putString(DatastoreKeys.lastUsedSavedItemFilter, filter.rawValue)
datastoreRepo.putString(lastUsedSavedItemFilter, filter.rawValue)
appliedFilterState.value = filter
handleFilterChanges()
}
@ -172,7 +174,7 @@ class FollowingViewModel @Inject constructor(
fun updateSavedItemSortFilter(filter: SavedItemSortFilter) {
viewModelScope.launch {
datastoreRepo.putString(DatastoreKeys.lastUsedSavedItemSortFilter, filter.rawValue)
datastoreRepo.putString(lastUsedSavedItemSortFilter, filter.rawValue)
appliedSortFilterLiveData.value = filter
handleFilterChanges()
}
@ -225,7 +227,7 @@ class FollowingViewModel @Inject constructor(
_libraryQuery.value = LibraryQuery(
allowedArchiveStates = allowedArchiveStates,
sortKey = sortKey,
requiredLabels = requiredLabels + listOf("Newsletter", "RSS"),
requiredLabels = requiredLabels,
excludedLabels = excludeLabels,
allowedContentReaders = allowedContentReaders
)
@ -274,7 +276,7 @@ class FollowingViewModel @Inject constructor(
isInitialBatch = false
)
} else {
datastoreRepo.putString(DatastoreKeys.libraryLastSyncTimestamp, startTime)
datastoreRepo.putString(libraryLastSyncTimestamp, startTime)
}
}

View File

@ -13,7 +13,9 @@ import app.omnivore.omnivore.core.data.repository.LibraryRepository
import app.omnivore.omnivore.core.database.entities.SavedItemLabel
import app.omnivore.omnivore.core.database.entities.SavedItemWithLabelsAndHighlights
import app.omnivore.omnivore.core.datastore.DatastoreRepository
import app.omnivore.omnivore.utils.DatastoreKeys
import app.omnivore.omnivore.core.datastore.lastUsedSavedItemFilter
import app.omnivore.omnivore.core.datastore.lastUsedSavedItemSortFilter
import app.omnivore.omnivore.core.datastore.libraryLastSyncTimestamp
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
@ -112,7 +114,7 @@ class LibraryViewModel @Inject constructor(
}
private fun getLastSyncTime(): Instant? = runBlocking {
datastoreRepo.getString(DatastoreKeys.libraryLastSyncTimestamp)?.let {
datastoreRepo.getString(libraryLastSyncTimestamp)?.let {
try {
return@let Instant.parse(it)
} catch (e: Exception) {
@ -158,7 +160,7 @@ class LibraryViewModel @Inject constructor(
fun updateSavedItemFilter(filter: SavedItemFilter) {
viewModelScope.launch {
datastoreRepo.putString(DatastoreKeys.lastUsedSavedItemFilter, filter.rawValue)
datastoreRepo.putString(lastUsedSavedItemFilter, filter.rawValue)
appliedFilterState.value = filter
handleFilterChanges()
}
@ -166,7 +168,7 @@ class LibraryViewModel @Inject constructor(
fun updateSavedItemSortFilter(filter: SavedItemSortFilter) {
viewModelScope.launch {
datastoreRepo.putString(DatastoreKeys.lastUsedSavedItemSortFilter, filter.rawValue)
datastoreRepo.putString(lastUsedSavedItemSortFilter, filter.rawValue)
appliedSortFilterLiveData.value = filter
handleFilterChanges()
}
@ -182,49 +184,46 @@ class LibraryViewModel @Inject constructor(
private fun handleFilterChanges() {
librarySearchCursor = null
if (appliedSortFilterLiveData.value != null && appliedFilterState.value != null) {
val sortKey = when (appliedSortFilterLiveData.value) {
SavedItemSortFilter.NEWEST -> "newest"
SavedItemSortFilter.OLDEST -> "oldest"
SavedItemSortFilter.RECENTLY_READ -> "recentlyRead"
SavedItemSortFilter.RECENTLY_PUBLISHED -> "recentlyPublished"
else -> "newest"
}
val allowedArchiveStates = when (appliedFilterState.value) {
SavedItemFilter.ALL -> listOf(0, 1)
SavedItemFilter.ARCHIVED -> listOf(1)
else -> listOf(0)
}
val allowedContentReaders = when (appliedFilterState.value) {
SavedItemFilter.FILES -> listOf("PDF", "EPUB")
else -> listOf("WEB", "PDF", "EPUB")
}
var requiredLabels = when (appliedFilterState.value) {
SavedItemFilter.NEWSLETTERS -> listOf("Newsletter")
SavedItemFilter.FEEDS -> listOf("RSS")
else -> activeLabels.value.map { it.name }
}
activeLabels.value.let { it ->
requiredLabels = requiredLabels + it.map { it.name }
}
val excludeLabels = when (appliedFilterState.value) {
SavedItemFilter.NON_FEED -> listOf("Newsletter", "RSS")
else -> listOf("Newsletter", "RSS")
}
_libraryQuery.value = LibraryQuery(
allowedArchiveStates = allowedArchiveStates,
sortKey = sortKey,
requiredLabels = requiredLabels,
excludedLabels = excludeLabels,
allowedContentReaders = allowedContentReaders
)
val sortKey = when (appliedSortFilterLiveData.value) {
SavedItemSortFilter.NEWEST -> "newest"
SavedItemSortFilter.OLDEST -> "oldest"
SavedItemSortFilter.RECENTLY_READ -> "recentlyRead"
SavedItemSortFilter.RECENTLY_PUBLISHED -> "recentlyPublished"
}
val allowedArchiveStates = when (appliedFilterState.value) {
SavedItemFilter.ALL -> listOf(0, 1)
SavedItemFilter.ARCHIVED -> listOf(1)
else -> listOf(0)
}
val allowedContentReaders = when (appliedFilterState.value) {
SavedItemFilter.FILES -> listOf("PDF", "EPUB")
else -> listOf("WEB", "PDF", "EPUB")
}
var requiredLabels = when (appliedFilterState.value) {
SavedItemFilter.NEWSLETTERS -> listOf("Newsletter")
SavedItemFilter.FEEDS -> listOf("RSS")
else -> activeLabels.value.map { it.name }
}
activeLabels.value.let { it ->
requiredLabels = requiredLabels + it.map { it.name }
}
val excludeLabels = when (appliedFilterState.value) {
SavedItemFilter.NON_FEED -> listOf("Newsletter", "RSS")
else -> listOf("Newsletter", "RSS")
}
_libraryQuery.value = LibraryQuery(
allowedArchiveStates = allowedArchiveStates,
sortKey = sortKey,
requiredLabels = requiredLabels,
excludedLabels = excludeLabels,
allowedContentReaders = allowedContentReaders
)
}
private suspend fun syncItems() {
@ -270,7 +269,7 @@ class LibraryViewModel @Inject constructor(
isInitialBatch = false
)
} else {
datastoreRepo.putString(DatastoreKeys.libraryLastSyncTimestamp, startTime)
datastoreRepo.putString(libraryLastSyncTimestamp, startTime)
}
}
@ -360,7 +359,7 @@ class LibraryViewModel @Inject constructor(
private fun searchQueryString(): String {
var query =
"${appliedFilterState.value?.queryString} ${appliedSortFilterLiveData.value?.queryString}"
"${appliedFilterState.value.queryString} ${appliedSortFilterLiveData.value.queryString}"
activeLabels.value.let {
if (it.isNotEmpty()) {

View File

@ -7,9 +7,9 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import app.omnivore.omnivore.core.data.DataService
import app.omnivore.omnivore.core.datastore.DatastoreRepository
import app.omnivore.omnivore.core.datastore.libraryLastSyncTimestamp
import app.omnivore.omnivore.core.network.Networker
import app.omnivore.omnivore.core.network.viewer
import app.omnivore.omnivore.utils.DatastoreKeys
import dagger.hilt.android.lifecycle.HiltViewModel
import io.intercom.android.sdk.Intercom
import io.intercom.android.sdk.IntercomSpace
@ -33,7 +33,7 @@ class ProfileViewModel @Inject constructor(
isResettingData = true
viewModelScope.launch {
datastoreRepo.clearValue(DatastoreKeys.libraryLastSyncTimestamp)
datastoreRepo.clearValue(libraryLastSyncTimestamp)
dataService.clearDatabase()
delay(1000)
isResettingData = false

View File

@ -13,21 +13,18 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavHostController
import app.omnivore.omnivore.R
import app.omnivore.omnivore.core.designsystem.component.SwitchPreferenceWidget
import app.omnivore.omnivore.feature.profile.ProfileViewModel
@OptIn(ExperimentalMaterial3Api::class)
@Composable
internal fun FiltersScreen(
navController: NavHostController,
settingsViewModel: ProfileViewModel = hiltViewModel()
filtersViewModel: FiltersViewModel = hiltViewModel()
) {
Scaffold(
@ -52,12 +49,12 @@ internal fun FiltersScreen(
contentPadding = contentPadding,
) {
item {
var checked by remember { mutableStateOf(true) }
val followingTabActive by filtersViewModel.followingTabActiveState.collectAsStateWithLifecycle()
SwitchPreferenceWidget(
title = stringResource(R.string.hide_following_tab),
checked = checked,
onCheckedChanged = { checked = it },
checked = followingTabActive,
onCheckedChanged = { filtersViewModel.setFollowingTabActiveState(it) },
)
}
}

View File

@ -0,0 +1,30 @@
package app.omnivore.omnivore.feature.profile.filters
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import app.omnivore.omnivore.core.datastore.DatastoreRepository
import app.omnivore.omnivore.core.datastore.followingTabActive
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class FiltersViewModel @Inject constructor(
private val datastoreRepository: DatastoreRepository
) : ViewModel() {
val followingTabActiveState: StateFlow<Boolean> = datastoreRepository.getBoolean(followingTabActive).stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(),
initialValue = false
)
fun setFollowingTabActiveState(value: Boolean) {
viewModelScope.launch {
datastoreRepository.putBoolean(followingTabActive, value)
}
}
}

View File

@ -28,6 +28,13 @@ import app.omnivore.omnivore.core.database.dao.SavedItemDao
import app.omnivore.omnivore.core.database.entities.SavedItem
import app.omnivore.omnivore.core.database.entities.SavedItemLabel
import app.omnivore.omnivore.core.datastore.DatastoreRepository
import app.omnivore.omnivore.core.datastore.preferredTheme
import app.omnivore.omnivore.core.datastore.preferredWebFontFamily
import app.omnivore.omnivore.core.datastore.preferredWebFontSize
import app.omnivore.omnivore.core.datastore.preferredWebLineHeight
import app.omnivore.omnivore.core.datastore.preferredWebMaxWidthPercentage
import app.omnivore.omnivore.core.datastore.prefersJustifyText
import app.omnivore.omnivore.core.datastore.prefersWebHighContrastText
import app.omnivore.omnivore.core.network.Networker
import app.omnivore.omnivore.core.network.createNewLabel
import app.omnivore.omnivore.core.network.saveUrl
@ -35,7 +42,6 @@ import app.omnivore.omnivore.core.network.savedItem
import app.omnivore.omnivore.feature.components.HighlightColor
import app.omnivore.omnivore.feature.library.SavedItemAction
import app.omnivore.omnivore.graphql.generated.type.CreateLabelInput
import app.omnivore.omnivore.utils.DatastoreKeys
import com.apollographql.apollo3.api.Optional.Companion.presentIfNotNull
import com.google.gson.Gson
import dagger.hilt.android.lifecycle.HiltViewModel
@ -454,21 +460,21 @@ class WebReaderViewModel @Inject constructor(
.asLiveData()
fun storedWebPreferences(isDarkMode: Boolean): WebPreferences = runBlocking {
val storedFontSize = datastoreRepo.getInt(DatastoreKeys.preferredWebFontSize)
val storedLineHeight = datastoreRepo.getInt(DatastoreKeys.preferredWebLineHeight)
val storedMaxWidth = datastoreRepo.getInt(DatastoreKeys.preferredWebMaxWidthPercentage)
val storedFontSize = datastoreRepo.getInt(preferredWebFontSize)
val storedLineHeight = datastoreRepo.getInt(preferredWebLineHeight)
val storedMaxWidth = datastoreRepo.getInt(preferredWebMaxWidthPercentage)
val storedFontFamily =
datastoreRepo.getString(DatastoreKeys.preferredWebFontFamily) ?: WebFont.SYSTEM.rawValue
datastoreRepo.getString(preferredWebFontFamily) ?: WebFont.SYSTEM.rawValue
val storedThemePreference =
datastoreRepo.getString(DatastoreKeys.preferredTheme) ?: "System"
datastoreRepo.getString(preferredTheme) ?: "System"
val storedWebFont =
WebFont.entries.firstOrNull { it.rawValue == storedFontFamily } ?: WebFont.entries
.first()
val prefersHighContrastFont =
datastoreRepo.getString(DatastoreKeys.prefersWebHighContrastText) == "true"
val prefersJustifyText = datastoreRepo.getString(DatastoreKeys.prefersJustifyText) == "true"
datastoreRepo.getString(prefersWebHighContrastText) == "true"
val prefersJustifyText = datastoreRepo.getString(prefersJustifyText) == "true"
WebPreferences(
textFontSize = storedFontSize ?: 12,
@ -494,7 +500,7 @@ class WebReaderViewModel @Inject constructor(
Log.d("theme", "Setting theme key: $newThemeKey")
runBlocking {
datastoreRepo.putString(DatastoreKeys.preferredTheme, newThemeKey)
datastoreRepo.putString(preferredTheme, newThemeKey)
}
val script =
@ -504,7 +510,7 @@ class WebReaderViewModel @Inject constructor(
fun setFontSize(newFontSize: Int) {
runBlocking {
datastoreRepo.putInt(DatastoreKeys.preferredWebFontSize, newFontSize)
datastoreRepo.putInt(preferredWebFontSize, newFontSize)
}
val script =
"var event = new Event('updateFontSize');event.fontSize = '$newFontSize';document.dispatchEvent(event);"
@ -514,7 +520,7 @@ class WebReaderViewModel @Inject constructor(
fun setMaxWidthPercentage(newMaxWidthPercentageValue: Int) {
runBlocking {
datastoreRepo.putInt(
DatastoreKeys.preferredWebMaxWidthPercentage,
preferredWebMaxWidthPercentage,
newMaxWidthPercentageValue
)
}
@ -525,7 +531,7 @@ class WebReaderViewModel @Inject constructor(
fun setLineHeight(newLineHeight: Int) {
runBlocking {
datastoreRepo.putInt(DatastoreKeys.preferredWebLineHeight, newLineHeight)
datastoreRepo.putInt(preferredWebLineHeight, newLineHeight)
}
val script =
"var event = new Event('updateLineHeight');event.lineHeight = '$newLineHeight';document.dispatchEvent(event);"
@ -535,7 +541,7 @@ class WebReaderViewModel @Inject constructor(
fun updateHighContrastTextPreference(prefersHighContrastText: Boolean) {
runBlocking {
datastoreRepo.putString(
DatastoreKeys.prefersWebHighContrastText,
prefersWebHighContrastText,
prefersHighContrastText.toString()
)
}
@ -547,7 +553,7 @@ class WebReaderViewModel @Inject constructor(
fun updateJustifyText(justifyText: Boolean) {
runBlocking {
datastoreRepo.putString(DatastoreKeys.prefersJustifyText, justifyText.toString())
datastoreRepo.putString(prefersJustifyText, justifyText.toString())
}
val script =
"var event = new Event('updateJustifyText');event.justifyText = $justifyText;document.dispatchEvent(event);"
@ -556,7 +562,7 @@ class WebReaderViewModel @Inject constructor(
fun applyWebFont(font: WebFont) {
runBlocking {
datastoreRepo.putString(DatastoreKeys.preferredWebFontFamily, font.rawValue)
datastoreRepo.putString(preferredWebFontFamily, font.rawValue)
}
val script =

View File

@ -8,19 +8,20 @@ import androidx.compose.ui.text.intl.Locale
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import app.omnivore.omnivore.utils.Constants
import app.omnivore.omnivore.utils.DatastoreKeys
import app.omnivore.omnivore.core.datastore.DatastoreRepository
import app.omnivore.omnivore.R
import app.omnivore.omnivore.core.datastore.DatastoreRepository
import app.omnivore.omnivore.core.datastore.omnivoreAuthToken
import app.omnivore.omnivore.graphql.generated.SaveUrlMutation
import app.omnivore.omnivore.graphql.generated.type.SaveUrlInput
import app.omnivore.omnivore.utils.Constants
import app.omnivore.omnivore.utils.ResourceProvider
import com.apollographql.apollo3.ApolloClient
import com.apollographql.apollo3.api.Optional
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.util.*
import java.util.TimeZone
import java.util.UUID
import java.util.regex.Pattern
import javax.inject.Inject
@ -48,7 +49,7 @@ class SaveViewModel @Inject constructor(
private set
private fun getAuthToken(): String? = runBlocking {
datastoreRepo.getString(DatastoreKeys.omnivoreAuthToken)
datastoreRepo.getString(omnivoreAuthToken)
}
/**

View File

@ -3,31 +3,13 @@ package app.omnivore.omnivore.utils
import app.omnivore.omnivore.BuildConfig
object Constants {
const val apiURL = BuildConfig.OMNIVORE_API_URL
const val dataStoreName = "omnivore-datastore"
}
object DatastoreKeys {
const val omnivoreSelfHostedAPIServer = "omnivoreSelfHostedAPIServer"
const val omnivoreSelfHostedWebServer = "omnivoreSelfHostedWebServer"
const val omnivoreAuthToken = "omnivoreAuthToken"
const val omnivoreAuthCookieString = "omnivoreAuthCookieString"
const val omnivorePendingUserToken = "omnivorePendingUserToken"
const val libraryLastSyncTimestamp = "libraryLastSyncTimestamp"
const val preferredWebFontSize = "preferredWebFontSize"
const val preferredWebLineHeight = "preferredWebLineHeight"
const val preferredWebMaxWidthPercentage = "preferredWebMaxWidthPercentage"
const val preferredWebFontFamily = "preferredWebFontFamily"
const val prefersWebHighContrastText = "prefersWebHighContrastText"
const val prefersJustifyText = "prefersJustifyText"
const val lastUsedSavedItemFilter = "lastUsedSavedItemFilter"
const val lastUsedSavedItemSortFilter = "lastUsedSavedItemSortFilter"
const val preferredTheme = "preferredTheme"
const val apiURL = BuildConfig.OMNIVORE_API_URL
const val dataStoreName = "omnivore-datastore"
}
object AppleConstants {
const val clientId = "app.omnivore"
const val redirectURI = BuildConfig.OMNIVORE_API_URL + "/api/mobile-auth/android-apple-redirect"
const val scope = "name%20email"
const val authUrl = "https://appleid.apple.com/auth/authorize"
const val clientId = "app.omnivore"
const val redirectURI = BuildConfig.OMNIVORE_API_URL + "/api/mobile-auth/android-apple-redirect"
const val scope = "name%20email"
const val authUrl = "https://appleid.apple.com/auth/authorize"
}