Compose side-effects fixes and remove unnecessary code
This commit is contained in:
@ -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<View>(android.R.id.content).rootView
|
||||
ViewCompat.setOnApplyWindowInsetsListener(rootView) { _, insets ->
|
||||
val imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
|
||||
rootView.setPadding(0, 0, 0, imeHeight)
|
||||
insets
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<SavedItemWithLabelsAndHighlights> {
|
||||
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),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
},
|
||||
)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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<View>(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()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user