From 69d23b154f33ac4e42b631685e46c9c9633831da Mon Sep 17 00:00:00 2001 From: Mohamed Date: Sat, 1 Jun 2024 15:14:19 +0300 Subject: [PATCH] Compose side-effects fixes and remove unnecessary code --- .../app/omnivore/omnivore/MainActivity.kt | 24 +-- .../feature/notebook/NotebookViewModel.kt | 33 ++-- .../feature/reader/ReaderPreferencesView.kt | 47 ++--- .../omnivore/feature/reader/WebReader.kt | 9 +- .../reader/WebReaderLoadingContainer.kt | 166 ++++++++---------- .../feature/reader/WebReaderViewModel.kt | 62 +++---- 6 files changed, 161 insertions(+), 180 deletions(-) diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/MainActivity.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/MainActivity.kt index 108df99ea..bb376f099 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/MainActivity.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/MainActivity.kt @@ -1,7 +1,6 @@ package app.omnivore.omnivore import android.os.Bundle -import android.view.View import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge @@ -9,19 +8,14 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.ui.Modifier import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen -import androidx.core.view.ViewCompat -import androidx.core.view.WindowCompat -import androidx.core.view.WindowInsetsCompat +import androidx.lifecycle.lifecycleScope import app.omnivore.omnivore.feature.root.RootView import app.omnivore.omnivore.feature.theme.OmnivoreTheme import com.pspdfkit.PSPDFKit import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch -@OptIn(DelicateCoroutinesApi::class) @AndroidEntryPoint class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -30,15 +24,14 @@ class MainActivity : ComponentActivity() { super.onCreate(savedInstanceState) - val context = this - GlobalScope.launch(Dispatchers.IO) { + lifecycleScope.launch(Dispatchers.IO) { val licenseKey = getString(R.string.pspdfkit_license_key) if (licenseKey.length > 30) { - PSPDFKit.initialize(context, licenseKey) + PSPDFKit.initialize(this@MainActivity, licenseKey) } else { - PSPDFKit.initialize(context, null) + PSPDFKit.initialize(this@MainActivity, null) } } @@ -53,14 +46,5 @@ class MainActivity : ComponentActivity() { } } } - - // animate the view up when keyboard appears - WindowCompat.setDecorFitsSystemWindows(window, false) - val rootView = findViewById(android.R.id.content).rootView - ViewCompat.setOnApplyWindowInsetsListener(rootView) { _, insets -> - val imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom - rootView.setPadding(0, 0, 0, imeHeight) - insets - } } } diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/notebook/NotebookViewModel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/notebook/NotebookViewModel.kt index a785b5886..36539fa92 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/notebook/NotebookViewModel.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/notebook/NotebookViewModel.kt @@ -11,22 +11,21 @@ import app.omnivore.omnivore.core.database.entities.SavedItemWithLabelsAndHighli import com.apollographql.apollo3.api.Optional import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class NotebookViewModel @Inject constructor( private val networker: Networker, private val dataService: DataService, -): ViewModel() { +) : ViewModel() { var highlightUnderEdit: Highlight? = null fun getLibraryItemById(savedItemId: String): LiveData { return dataService.db.savedItemDao().getLibraryItemById(savedItemId) } - suspend fun addArticleNote(savedItemId: String, note: String) { - withContext(Dispatchers.IO) { + fun addArticleNote(savedItemId: String, note: String) = viewModelScope.launch(Dispatchers.IO) { val savedItem = dataService.db.savedItemDao().getById(savedItemId) savedItem?.let { item -> val noteHighlight = item.highlights.firstOrNull { it.type == "NOTE" } @@ -34,24 +33,26 @@ class NotebookViewModel @Inject constructor( dataService.db.highlightDao() .updateNote(highlightId = noteHighlight.highlightId, note = note) - networker.updateHighlight(input = UpdateHighlightInput( - highlightId = noteHighlight.highlightId, - annotation = Optional.presentIfNotNull(note), - )) + networker.updateHighlight( + input = UpdateHighlightInput( + highlightId = noteHighlight.highlightId, + annotation = Optional.presentIfNotNull(note), + ) + ) } ?: run { dataService.createNoteHighlight(savedItemId, note) } } } - } - suspend fun updateHighlightNote(highlightId: String, note: String?) { - withContext(Dispatchers.IO) { + fun updateHighlightNote(highlightId: String, note: String?) = + viewModelScope.launch(Dispatchers.IO) { dataService.db.highlightDao().updateNote(highlightId, note ?: "") - networker.updateHighlight(input = UpdateHighlightInput( - highlightId = highlightId, - annotation = Optional.presentIfNotNull(note), - )) + networker.updateHighlight( + input = UpdateHighlightInput( + highlightId = highlightId, + annotation = Optional.presentIfNotNull(note), + ) + ) } - } } diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/reader/ReaderPreferencesView.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/reader/ReaderPreferencesView.kt index c796e97a0..21795f4a4 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/reader/ReaderPreferencesView.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/reader/ReaderPreferencesView.kt @@ -13,7 +13,7 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.verticalScroll -import androidx.compose.material.Text +import androidx.compose.material3.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowDropDown import androidx.compose.material3.AssistChip @@ -23,9 +23,10 @@ import androidx.compose.material3.Checkbox import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Icon -import androidx.compose.material3.Slider import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue @@ -49,22 +50,26 @@ fun ReaderPreferencesView( webReaderViewModel: WebReaderViewModel ) { val isDark = isSystemInDarkTheme() - val currentWebPreferences = webReaderViewModel.storedWebPreferences(isDark) + val currentWebPreferences by remember { + derivedStateOf { + webReaderViewModel.storedWebPreferences(isDark) + } + } val isFontListExpanded = remember { mutableStateOf(false) } - val highContrastTextSwitchState = + var highContrastTextSwitchState by remember { mutableStateOf(currentWebPreferences.prefersHighContrastText) } - val justifyTextSwitchState = + var justifyTextSwitchState by remember { mutableStateOf(currentWebPreferences.prefersJustifyText) } - val selectedWebFontName = + var selectedWebFontName by remember { mutableStateOf(currentWebPreferences.fontFamily.displayText) } - var fontSizeSliderValue by remember { mutableStateOf(currentWebPreferences.textFontSize.toFloat()) } - var marginSliderValue by remember { mutableStateOf(currentWebPreferences.maxWidthPercentage.toFloat()) } - var lineSpacingSliderValue by remember { mutableStateOf(currentWebPreferences.lineHeight.toFloat()) } + var fontSizeSliderValue by remember { mutableFloatStateOf(currentWebPreferences.textFontSize.toFloat()) } + var marginSliderValue by remember { mutableFloatStateOf(currentWebPreferences.maxWidthPercentage.toFloat()) } + var lineSpacingSliderValue by remember { mutableFloatStateOf(currentWebPreferences.lineHeight.toFloat()) } - val themeState = remember { mutableStateOf(currentWebPreferences.storedThemePreference) } + var themeState by remember { mutableStateOf(currentWebPreferences.storedThemePreference) } val volumeForScrollState by webReaderViewModel.volumeRockerForScrollState.collectAsStateWithLifecycle() @@ -99,7 +104,7 @@ fun ReaderPreferencesView( onClick = { isFontListExpanded.value = true }, label = { Text( - selectedWebFontName.value, + selectedWebFontName, color = Color(red = 137, green = 137, blue = 137) ) }, @@ -129,7 +134,7 @@ fun ReaderPreferencesView( }, onClick = { webReaderViewModel.applyWebFont(it) - selectedWebFontName.value = it.displayText + selectedWebFontName = it.displayText isFontListExpanded.value = false }, ) @@ -211,13 +216,13 @@ fun ReaderPreferencesView( color = Color(red = 137, green = 137, blue = 137) ) ) - Checkbox(checked = themeState.value == "System", onCheckedChange = { + Checkbox(checked = themeState == "System", onCheckedChange = { if (it) { - themeState.value = "System" + themeState = "System" webReaderViewModel.updateStoredThemePreference("System") } else { val newThemeKey = if (isDark) "Black" else "Light" - themeState.value = newThemeKey + themeState = newThemeKey webReaderViewModel.updateStoredThemePreference(newThemeKey) } }) @@ -228,10 +233,10 @@ fun ReaderPreferencesView( ) { for (theme in Themes.entries) { if (theme.themeKey != "System") { - val isSelected = theme.themeKey == themeState.value + val isSelected = theme.themeKey == themeState Button( onClick = { - themeState.value = theme.themeKey + themeState = theme.themeKey webReaderViewModel.updateStoredThemePreference(theme.themeKey) }, shape = CircleShape, @@ -257,18 +262,18 @@ fun ReaderPreferencesView( // TODO : Use state flow SwitchPreferenceWidget( title = stringResource(R.string.reader_preferences_view_high_constrast_text), - checked = highContrastTextSwitchState.value, + checked = highContrastTextSwitchState, onCheckedChanged = { - highContrastTextSwitchState.value = it + highContrastTextSwitchState = it webReaderViewModel.updateHighContrastTextPreference(it) }, ) // TODO : Use state flow SwitchPreferenceWidget( title = stringResource(R.string.reader_preferences_view_justify_text), - checked = justifyTextSwitchState.value, + checked = justifyTextSwitchState, onCheckedChanged = { - justifyTextSwitchState.value = it + justifyTextSwitchState = it webReaderViewModel.updateJustifyText(it) }, ) diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/reader/WebReader.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/reader/WebReader.kt index 3e89a456c..e0e67ece5 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/reader/WebReader.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/reader/WebReader.kt @@ -18,9 +18,9 @@ import android.webkit.WebView import android.webkit.WebViewClient import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.* -import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.ui.Modifier import androidx.compose.ui.viewinterop.AndroidView import androidx.lifecycle.compose.collectAsStateWithLifecycle import app.omnivore.omnivore.R @@ -35,18 +35,17 @@ import java.util.* fun WebReader( styledContent: String, webReaderViewModel: WebReaderViewModel, - currentTheme: Themes? + currentTheme: Themes?, + modifier: Modifier = Modifier ) { val javascriptActionLoopUUID: UUID by webReaderViewModel.javascriptActionLoopUUIDLiveData.observeAsState( UUID.randomUUID() ) val isDarkMode = isSystemInDarkTheme() - WebView.setWebContentsDebuggingEnabled(true) - val volumeForScrollState by webReaderViewModel.volumeRockerForScrollState.collectAsStateWithLifecycle() - Box { + Box(modifier) { AndroidView(factory = { OmnivoreWebView(it).apply { viewModel = webReaderViewModel diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/reader/WebReaderLoadingContainer.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/reader/WebReaderLoadingContainer.kt index 3a80aeaf6..cdb28e963 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/reader/WebReaderLoadingContainer.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/reader/WebReaderLoadingContainer.kt @@ -2,7 +2,6 @@ package app.omnivore.omnivore.feature.reader import android.content.Intent import android.os.Bundle -import android.view.View import androidx.activity.ComponentActivity import androidx.activity.compose.LocalOnBackPressedDispatcherOwner import androidx.activity.compose.setContent @@ -11,10 +10,12 @@ import androidx.activity.viewModels import androidx.compose.foundation.background import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.shape.RoundedCornerShape @@ -32,11 +33,12 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -46,9 +48,6 @@ import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import androidx.core.view.ViewCompat -import androidx.core.view.WindowCompat -import androidx.core.view.WindowInsetsCompat import app.omnivore.omnivore.MainActivity import app.omnivore.omnivore.R import app.omnivore.omnivore.core.database.entities.SavedItemLabel @@ -62,7 +61,6 @@ import app.omnivore.omnivore.feature.notebook.NotebookViewModel import app.omnivore.omnivore.feature.savedItemViews.SavedItemContextMenu import app.omnivore.omnivore.feature.theme.OmnivoreTheme import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.launch import kotlin.math.roundToInt @AndroidEntryPoint @@ -87,6 +85,7 @@ class WebReaderLoadingContainerActivity : ComponentActivity() { modifier = Modifier .fillMaxSize() .background(color = if (isSystemInDarkTheme()) Color.Black else Color.White) + .imePadding() ) { if (viewModel.hasFetchError.value == true) { Text(stringResource(R.string.web_reader_loading_container_error_msg)) @@ -104,15 +103,6 @@ class WebReaderLoadingContainerActivity : ComponentActivity() { } } } - - // animate the view up when keyboard appears - WindowCompat.setDecorFitsSystemWindows(window, false) - val rootView = findViewById(android.R.id.content).rootView - ViewCompat.setOnApplyWindowInsetsListener(rootView) { _, insets -> - val imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom - rootView.setPadding(0, 0, 0, imeHeight) - insets - } } private fun startMainActivity() { @@ -143,9 +133,13 @@ fun WebReaderLoadingContainer( editInfoViewModel: EditInfoViewModel ) { val currentThemeKey = webReaderViewModel.currentThemeKey.observeAsState() - val currentTheme = Themes.values().find { it.themeKey == currentThemeKey.value } + val currentTheme by remember { + derivedStateOf { + Themes.entries.find { it.themeKey == currentThemeKey.value } + } + } val onBackPressedDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher - val bottomSheetState: BottomSheetState? by webReaderViewModel.bottomSheetStateLiveData.observeAsState( + val bottomSheetState: BottomSheetState? by webReaderViewModel.bottomSheetStateFlow.collectAsState( BottomSheetState.NONE ) @@ -162,15 +156,19 @@ fun WebReaderLoadingContainer( webReaderViewModel.maxToolbarHeightPx = with(LocalDensity.current) { maxToolbarHeight.roundToPx().toFloat() } - val coroutineScope = rememberCoroutineScope() + val darkTheme = isSystemInDarkTheme() - val styledContent = webReaderParams?.let { - val webReaderContent = WebReaderContent( - preferences = webReaderViewModel.storedWebPreferences(isSystemInDarkTheme()), - item = it.item, - articleContent = it.articleContent, - ) - webReaderContent.styledContent() + val styledContent by remember { + derivedStateOf { + webReaderParams?.let { + val webReaderContent = WebReaderContent( + preferences = webReaderViewModel.storedWebPreferences(darkTheme), + item = it.item, + articleContent = it.articleContent, + ) + webReaderContent.styledContent() + } + } } @@ -185,35 +183,28 @@ fun WebReaderLoadingContainer( } ) - val showMenu = { - coroutineScope.launch { - modalBottomSheetState.show() - } - } - - when (bottomSheetState) { - BottomSheetState.PREFERENCES -> { - coroutineScope.launch { + LaunchedEffect(bottomSheetState) { + when (bottomSheetState) { + BottomSheetState.PREFERENCES -> { if (!modalBottomSheetState.isVisible) { modalBottomSheetState.show() } } - } - BottomSheetState.NOTEBOOK, BottomSheetState.EDITNOTE, - BottomSheetState.HIGHLIGHTNOTE, BottomSheetState.LABELS, BottomSheetState.EDIT_INFO, - BottomSheetState.LINK, - -> { - showMenu() - } + BottomSheetState.NOTEBOOK, BottomSheetState.EDITNOTE, + BottomSheetState.HIGHLIGHTNOTE, BottomSheetState.LABELS, BottomSheetState.EDIT_INFO, + BottomSheetState.LINK, + -> { + modalBottomSheetState.show() + } - else -> { - coroutineScope.launch { + else -> { modalBottomSheetState.hide() } } } + ModalBottomSheetLayout( modifier = Modifier .statusBarsPadding(), @@ -246,24 +237,22 @@ fun WebReaderLoadingContainer( EditNoteModal( initialValue = notebookViewModel.highlightUnderEdit?.annotation, onDismiss = { save, note -> - coroutineScope.launch { - if (save) { - notebookViewModel.highlightUnderEdit?.let { highlight -> - notebookViewModel.updateHighlightNote( - highlight.highlightId, - note + if (save) { + notebookViewModel.highlightUnderEdit?.let { highlight -> + notebookViewModel.updateHighlightNote( + highlight.highlightId, + note + ) + } ?: run { + if (note != null) { + notebookViewModel.addArticleNote( + savedItemId = params.item.savedItemId, + note = note ) - } ?: run { - if (note != null) { - notebookViewModel.addArticleNote( - savedItemId = params.item.savedItemId, - note = note - ) - } } } - notebookViewModel.highlightUnderEdit = null } + notebookViewModel.highlightUnderEdit = null webReaderViewModel.setBottomSheet(BottomSheetState.NOTEBOOK) }) } @@ -273,14 +262,12 @@ fun WebReaderLoadingContainer( EditNoteModal( initialValue = webReaderViewModel.annotation, onDismiss = { save, note -> - coroutineScope.launch { - if (save) { - webReaderViewModel.saveAnnotation(note ?: "") - } else { - webReaderViewModel.cancelAnnotation() - } - webReaderViewModel.annotation = null + if (save) { + webReaderViewModel.saveAnnotation(note ?: "") + } else { + webReaderViewModel.cancelAnnotation() } + webReaderViewModel.annotation = null webReaderViewModel.resetBottomSheet() } ) @@ -293,21 +280,18 @@ fun WebReaderLoadingContainer( labelsViewModel = labelsViewModel, initialSelectedLabels = webReaderParams?.labels ?: listOf(), onCancel = { - coroutineScope.launch { - webReaderViewModel.resetBottomSheet() - } + webReaderViewModel.resetBottomSheet() }, isLibraryMode = false, onSave = { if (it != labels) { webReaderViewModel.updateSavedItemLabels( - savedItemID = webReaderParams?.item?.savedItemId ?: "", + savedItemID = webReaderParams?.item?.savedItemId + ?: "", labels = it ) } - coroutineScope.launch { - webReaderViewModel.resetBottomSheet() - } + webReaderViewModel.resetBottomSheet() }, onCreateLabel = { newLabelName, labelHexValue -> webReaderViewModel.createNewSavedItemLabel( @@ -328,15 +312,11 @@ fun WebReaderLoadingContainer( description = webReaderParams?.item?.descriptionText, viewModel = editInfoViewModel, onCancel = { - coroutineScope.launch { - webReaderViewModel.resetBottomSheet() - } + webReaderViewModel.resetBottomSheet() }, onUpdated = { - coroutineScope.launch { - webReaderViewModel.updateItemTitle() - webReaderViewModel.resetBottomSheet() - } + webReaderViewModel.updateItemTitle() + webReaderViewModel.resetBottomSheet() } ) } @@ -356,18 +336,20 @@ fun WebReaderLoadingContainer( } } - Spacer(modifier = Modifier.weight(1.0F)) } ) { Scaffold( topBar = { ReaderTopAppBar(webReaderViewModel, onLibraryIconTap) - }) { paddingValues -> - if (styledContent != null) { + }, + modifier = Modifier.statusBarsPadding() + ) { paddingValues -> + styledContent?.let { WebReader( - styledContent = styledContent, + styledContent = it, webReaderViewModel = webReaderViewModel, currentTheme = currentTheme, + modifier = Modifier.consumeWindowInsets(paddingValues) ) } @@ -390,7 +372,11 @@ fun ReaderTopAppBar( val isDarkMode = isSystemInDarkTheme() val currentThemeKey = webReaderViewModel.currentThemeKey.observeAsState() - val currentTheme = Themes.values().find { it.themeKey == currentThemeKey.value } + val currentTheme by remember { + derivedStateOf { + Themes.entries.find { it.themeKey == currentThemeKey.value } + } + } val toolbarHeightPx: Float by webReaderViewModel.currentToolbarHeightLiveData.observeAsState( 0.0f ) @@ -401,13 +387,13 @@ fun ReaderTopAppBar( val themeBackgroundColor = currentTheme?.let { if (it.themeKey == "System" && isDarkMode) { - Color(0xFF000000) + Color.Black } else if (it.themeKey == "System") { - Color(0xFFFFFFFF) + Color.White } else { Color(it.backgroundColor) } - } ?: Color(0xFFFFFFFF) + } ?: Color.White val themeTintColor = currentTheme?.let { if (it.themeKey == "System" && isDarkMode) { @@ -509,7 +495,11 @@ fun BottomSheetUI(content: @Composable () -> Unit) { .statusBarsPadding() ) { Scaffold { paddingValues -> - Box(modifier = Modifier.fillMaxSize()) { + Box( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues) + ) { content() } } diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/reader/WebReaderViewModel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/reader/WebReaderViewModel.kt index 6a84f8fca..1c11941e3 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/reader/WebReaderViewModel.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/reader/WebReaderViewModel.kt @@ -28,7 +28,6 @@ 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.followingTabActive import app.omnivore.omnivore.core.datastore.preferredTheme import app.omnivore.omnivore.core.datastore.preferredWebFontFamily import app.omnivore.omnivore.core.datastore.preferredWebFontSize @@ -52,10 +51,12 @@ import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext @@ -109,7 +110,7 @@ class WebReaderViewModel @Inject constructor( val savedItemLabelsLiveData = dataService.db.savedItemLabelDao().getSavedItemLabelsLiveData() var currentLink: Uri? = null - val bottomSheetStateLiveData = MutableLiveData(BottomSheetState.NONE) + val bottomSheetStateFlow = MutableStateFlow(BottomSheetState.NONE) var hasTappedExistingHighlight = false var lastTapCoordinates: TapCoordinates? = null @@ -137,12 +138,12 @@ class WebReaderViewModel @Inject constructor( onScrollChange(maxToolbarHeightPx) } - fun setBottomSheet(state: BottomSheetState) { - bottomSheetStateLiveData.postValue(state) + fun setBottomSheet(state: BottomSheetState) = viewModelScope.launch { + bottomSheetStateFlow.update { state } } - fun resetBottomSheet() { - bottomSheetStateLiveData.postValue(BottomSheetState.NONE) + fun resetBottomSheet() = viewModelScope.launch { + bottomSheetStateFlow.update { BottomSheetState.NONE } } fun showOpenLinkSheet(context: Context, uri: Uri) { @@ -151,7 +152,9 @@ class WebReaderViewModel @Inject constructor( openLink(context, uri) } else { currentLink = uri - bottomSheetStateLiveData.postValue(BottomSheetState.LINK) + viewModelScope.launch { + bottomSheetStateFlow.update { BottomSheetState.LINK } + } } } } @@ -172,7 +175,9 @@ class WebReaderViewModel @Inject constructor( currentLink?.let { openLink(context, it) } - bottomSheetStateLiveData.postValue(BottomSheetState.NONE) + viewModelScope.launch { + bottomSheetStateFlow.update { BottomSheetState.NONE } + } } private fun openLink(context: Context, uri: Uri) { @@ -180,20 +185,18 @@ class WebReaderViewModel @Inject constructor( startActivity(context, browserIntent, null) } - fun saveCurrentLink(context: Context) { + fun saveCurrentLink(context: Context) = viewModelScope.launch { currentLink?.let { - viewModelScope.launch { - val success = networker.saveUrl(it) - Toast.makeText( - context, - if (success) - context.getString(R.string.web_reader_view_model_save_link_success) else - context.getString(R.string.web_reader_view_model_save_link_error), - Toast.LENGTH_SHORT - ).show() - } + val success = networker.saveUrl(it) + Toast.makeText( + context, + if (success) + context.getString(R.string.web_reader_view_model_save_link_success) else + context.getString(R.string.web_reader_view_model_save_link_error), + Toast.LENGTH_SHORT + ).show() } - bottomSheetStateLiveData.postValue(BottomSheetState.NONE) + bottomSheetStateFlow.update { BottomSheetState.NONE } } fun copyCurrentLink(context: Context) { @@ -211,7 +214,7 @@ class WebReaderViewModel @Inject constructor( ).show() } } - bottomSheetStateLiveData.postValue(BottomSheetState.NONE) + bottomSheetStateFlow.update { BottomSheetState.NONE } } fun onScrollChange(delta: Float) { @@ -266,7 +269,8 @@ class WebReaderViewModel @Inject constructor( private suspend fun loadItemFromDB(slug: String) { withContext(Dispatchers.IO) { - val persistedItem = dataService.db.savedItemDao().getSavedItemWithLabelsAndHighlights(slug) + val persistedItem = + dataService.db.savedItemDao().getSavedItemWithLabelsAndHighlights(slug) val savedItemId = persistedItem?.savedItem?.savedItemId if (savedItemId != null) { val htmlContent = loadLibraryItemContent(applicationContext, savedItemId) @@ -342,11 +346,11 @@ class WebReaderViewModel @Inject constructor( } SavedItemAction.EditLabels -> { - bottomSheetStateLiveData.postValue(BottomSheetState.LABELS) + bottomSheetStateFlow.update { BottomSheetState.LABELS } } SavedItemAction.EditInfo -> { - bottomSheetStateLiveData.postValue(BottomSheetState.EDIT_INFO) + bottomSheetStateFlow.update { BottomSheetState.EDIT_INFO } } SavedItemAction.MarkRead -> { @@ -411,7 +415,7 @@ class WebReaderViewModel @Inject constructor( .fromJson(jsonString, AnnotationWebViewMessage::class.java) .annotation ?: "" annotation = annotationStr - bottomSheetStateLiveData.postValue(BottomSheetState.HIGHLIGHTNOTE) + bottomSheetStateFlow.update { BottomSheetState.HIGHLIGHTNOTE } } } @@ -436,7 +440,7 @@ class WebReaderViewModel @Inject constructor( javascriptDispatchQueue = mutableListOf() } - fun saveAnnotation(annotation: String) { + fun saveAnnotation(annotation: String) = viewModelScope.launch { val jsonAnnotation = Gson().toJson(annotation) val script = "var event = new Event('saveAnnotation');event.annotation = $jsonAnnotation;document.dispatchEvent(event);" @@ -447,7 +451,7 @@ class WebReaderViewModel @Inject constructor( cancelAnnotationEdit() } - fun cancelAnnotation() { + fun cancelAnnotation() = viewModelScope.launch { val script = "var event = new Event('dismissHighlight');document.dispatchEvent(event);" enqueueScript(script) @@ -612,9 +616,7 @@ class WebReaderViewModel @Inject constructor( // Send labels to webview val script = "var event = new Event('updateLabels');event.labels = ${Gson().toJson(labels)};document.dispatchEvent(event);" - CoroutineScope(Dispatchers.Main).launch { - enqueueScript(script) - } + enqueueScript(script) } } }