mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
load web reader container from read now button action
This commit is contained in:
parent
3bf74d1f75
commit
00dfb9386c
6 changed files with 234 additions and 120 deletions
|
|
@ -51,5 +51,11 @@
|
|||
android:name=".ui.reader.PDFReaderActivity"
|
||||
android:theme="@style/Theme.AppCompat.NoActionBar"
|
||||
android:windowSoftInputMode="adjustNothing" />
|
||||
|
||||
<activity
|
||||
android:name=".ui.reader.WebReaderLoadingContainerActivity"
|
||||
android:theme="@style/Theme.AppCompat.NoActionBar"
|
||||
android:windowSoftInputMode="adjustNothing" />
|
||||
|
||||
</application>
|
||||
</manifest>
|
||||
|
|
|
|||
|
|
@ -41,123 +41,6 @@ import kotlinx.coroutines.launch
|
|||
import java.util.*
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
||||
@Composable
|
||||
fun WebReaderLoadingContainer(slug: String, webReaderViewModel: WebReaderViewModel) {
|
||||
val onBackPressedDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher
|
||||
|
||||
var isMenuExpanded by remember { mutableStateOf(false) }
|
||||
var showWebPreferencesDialog by remember { mutableStateOf(false ) }
|
||||
|
||||
val webReaderParams: WebReaderParams? by webReaderViewModel.webReaderParamsLiveData.observeAsState(null)
|
||||
val annotation: String? by webReaderViewModel.annotationLiveData.observeAsState(null)
|
||||
val shouldPopView: Boolean by webReaderViewModel.shouldPopViewLiveData.observeAsState(false)
|
||||
|
||||
val maxToolbarHeight = 48.dp
|
||||
val maxToolbarHeightPx = with(LocalDensity.current) { maxToolbarHeight.roundToPx().toFloat() }
|
||||
val toolbarHeightPx = remember { mutableStateOf(maxToolbarHeightPx) }
|
||||
|
||||
// Create a connection to the nested scroll system and listen to the scroll happening inside child Column
|
||||
val nestedScrollConnection = remember {
|
||||
object : NestedScrollConnection {
|
||||
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
|
||||
val delta = available.y
|
||||
val newHeight = toolbarHeightPx.value + delta
|
||||
toolbarHeightPx.value = newHeight.coerceIn(0f, maxToolbarHeightPx)
|
||||
return Offset.Zero
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (webReaderParams == null) {
|
||||
webReaderViewModel.loadItem(slug = slug)
|
||||
}
|
||||
|
||||
if (webReaderParams != null) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.nestedScroll(nestedScrollConnection)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(webReaderViewModel.scrollState)
|
||||
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.requiredHeight(height = maxToolbarHeight)
|
||||
) {
|
||||
}
|
||||
WebReader(webReaderParams!!, webReaderViewModel.storedWebPreferences(isSystemInDarkTheme()), webReaderViewModel)
|
||||
}
|
||||
|
||||
TopAppBar(
|
||||
modifier = Modifier
|
||||
.height(height = with(LocalDensity.current) {
|
||||
webReaderViewModel.currentToolbarHeight = toolbarHeightPx.value.toInt()
|
||||
toolbarHeightPx.value.roundToInt().toDp()
|
||||
} ),
|
||||
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||
title = {},
|
||||
actions = {
|
||||
// Disabling menu until we implement local persistence
|
||||
IconButton(onClick = { isMenuExpanded = true }) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Menu,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
IconButton(onClick = { showWebPreferencesDialog = true }) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Settings, // TODO: set a better icon
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
SavedItemContextMenu(
|
||||
isExpanded = isMenuExpanded,
|
||||
isArchived = webReaderParams!!.item.isArchived,
|
||||
onDismiss = { isMenuExpanded = false },
|
||||
actionHandler = { webReaderViewModel.handleSavedItemAction(webReaderParams!!.item.savedItemId, it) }
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
if (showWebPreferencesDialog) {
|
||||
WebPreferencesDialog(
|
||||
onDismiss = {
|
||||
showWebPreferencesDialog = false
|
||||
},
|
||||
webReaderViewModel = webReaderViewModel
|
||||
)
|
||||
}
|
||||
|
||||
if (annotation != null) {
|
||||
AnnotationEditView(
|
||||
initialAnnotation = annotation!!,
|
||||
onSave = {
|
||||
webReaderViewModel.saveAnnotation(it)
|
||||
},
|
||||
onCancel = {
|
||||
webReaderViewModel.cancelAnnotationEdit()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(shouldPopView) {
|
||||
if (shouldPopView) {
|
||||
onBackPressedDispatcher?.onBackPressed()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// TODO: add a proper loading view
|
||||
Text("Loading...")
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
@Composable
|
||||
fun WebReader(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,200 @@
|
|||
package app.omnivore.omnivore.ui.reader
|
||||
|
||||
import android.graphics.PointF
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.TopAppBar
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Menu
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
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.R
|
||||
import app.omnivore.omnivore.ui.root.RootView
|
||||
import app.omnivore.omnivore.ui.savedItemViews.SavedItemContextMenu
|
||||
import app.omnivore.omnivore.ui.theme.OmnivoreTheme
|
||||
import com.pspdfkit.annotations.Annotation
|
||||
import com.pspdfkit.annotations.HighlightAnnotation
|
||||
import com.pspdfkit.configuration.PdfConfiguration
|
||||
import com.pspdfkit.configuration.page.PageScrollDirection
|
||||
import com.pspdfkit.listeners.DocumentListener
|
||||
import com.pspdfkit.listeners.OnPreparePopupToolbarListener
|
||||
import com.pspdfkit.ui.PdfFragment
|
||||
import com.pspdfkit.ui.PdfThumbnailBar
|
||||
import com.pspdfkit.ui.search.PdfSearchViewModular
|
||||
import com.pspdfkit.ui.special_mode.controller.TextSelectionController
|
||||
import com.pspdfkit.ui.special_mode.manager.TextSelectionManager
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
||||
@AndroidEntryPoint
|
||||
class WebReaderLoadingContainerActivity: AppCompatActivity() {
|
||||
val viewModel: WebReaderViewModel by viewModels()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val requestID = intent.getStringExtra("SAVED_ITEM_REQUEST_ID") ?: ""
|
||||
|
||||
setContent {
|
||||
OmnivoreTheme {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(color = Color.Black)
|
||||
) {
|
||||
WebReaderLoadingContainer(requestID = requestID, webReaderViewModel = viewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null, webReaderViewModel: WebReaderViewModel) {
|
||||
val onBackPressedDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher
|
||||
|
||||
var isMenuExpanded by remember { mutableStateOf(false) }
|
||||
var showWebPreferencesDialog by remember { mutableStateOf(false ) }
|
||||
|
||||
val webReaderParams: WebReaderParams? by webReaderViewModel.webReaderParamsLiveData.observeAsState(null)
|
||||
val annotation: String? by webReaderViewModel.annotationLiveData.observeAsState(null)
|
||||
val shouldPopView: Boolean by webReaderViewModel.shouldPopViewLiveData.observeAsState(false)
|
||||
|
||||
val maxToolbarHeight = 48.dp
|
||||
val maxToolbarHeightPx = with(LocalDensity.current) { maxToolbarHeight.roundToPx().toFloat() }
|
||||
val toolbarHeightPx = remember { mutableStateOf(maxToolbarHeightPx) }
|
||||
|
||||
// Create a connection to the nested scroll system and listen to the scroll happening inside child Column
|
||||
val nestedScrollConnection = remember {
|
||||
object : NestedScrollConnection {
|
||||
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
|
||||
val delta = available.y
|
||||
val newHeight = toolbarHeightPx.value + delta
|
||||
toolbarHeightPx.value = newHeight.coerceIn(0f, maxToolbarHeightPx)
|
||||
return Offset.Zero
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (webReaderParams == null) {
|
||||
webReaderViewModel.loadItem(slug = slug, requestID = requestID)
|
||||
}
|
||||
|
||||
if (webReaderParams != null) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.nestedScroll(nestedScrollConnection)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(webReaderViewModel.scrollState)
|
||||
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.requiredHeight(height = maxToolbarHeight)
|
||||
) {
|
||||
}
|
||||
WebReader(webReaderParams!!, webReaderViewModel.storedWebPreferences(isSystemInDarkTheme()), webReaderViewModel)
|
||||
}
|
||||
|
||||
TopAppBar(
|
||||
modifier = Modifier
|
||||
.height(height = with(LocalDensity.current) {
|
||||
webReaderViewModel.currentToolbarHeight = toolbarHeightPx.value.toInt()
|
||||
toolbarHeightPx.value.roundToInt().toDp()
|
||||
} ),
|
||||
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||
title = {},
|
||||
actions = {
|
||||
// Disabling menu until we implement local persistence
|
||||
IconButton(onClick = { isMenuExpanded = true }) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Menu,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
IconButton(onClick = { showWebPreferencesDialog = true }) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Settings, // TODO: set a better icon
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
SavedItemContextMenu(
|
||||
isExpanded = isMenuExpanded,
|
||||
isArchived = webReaderParams!!.item.isArchived,
|
||||
onDismiss = { isMenuExpanded = false },
|
||||
actionHandler = { webReaderViewModel.handleSavedItemAction(webReaderParams!!.item.savedItemId, it) }
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
if (showWebPreferencesDialog) {
|
||||
WebPreferencesDialog(
|
||||
onDismiss = {
|
||||
showWebPreferencesDialog = false
|
||||
},
|
||||
webReaderViewModel = webReaderViewModel
|
||||
)
|
||||
}
|
||||
|
||||
if (annotation != null) {
|
||||
AnnotationEditView(
|
||||
initialAnnotation = annotation!!,
|
||||
onSave = {
|
||||
webReaderViewModel.saveAnnotation(it)
|
||||
},
|
||||
onCancel = {
|
||||
webReaderViewModel.cancelAnnotationEdit()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(shouldPopView) {
|
||||
if (shouldPopView) {
|
||||
onBackPressedDispatcher?.onBackPressed()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// TODO: add a proper loading view
|
||||
Text("Loading...")
|
||||
}
|
||||
}
|
||||
|
|
@ -45,7 +45,12 @@ class WebReaderViewModel @Inject constructor(
|
|||
var hasTappedExistingHighlight = false
|
||||
var lastTapCoordinates: TapCoordinates? = null
|
||||
|
||||
fun loadItem(slug: String) {
|
||||
fun loadItem(slug: String?, requestID: String?) {
|
||||
slug?.let { loadItemUsingSlug(it) }
|
||||
requestID?.let { loadItemUsingRequestID(it) }
|
||||
}
|
||||
|
||||
private fun loadItemUsingSlug(slug: String) {
|
||||
viewModelScope.launch {
|
||||
val webReaderParams = loadItemFromServer(slug)
|
||||
|
||||
|
|
@ -58,6 +63,10 @@ class WebReaderViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun loadItemUsingRequestID(requestID: String) {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
private suspend fun loadItemFromDB(slug: String) {
|
||||
withContext(Dispatchers.IO) {
|
||||
val persistedItem = dataService.db.savedItemDao().getSavedItemWithLabelsAndHighlights(slug)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package app.omnivore.omnivore.ui.save
|
||||
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.MaterialTheme
|
||||
|
|
@ -11,13 +13,18 @@ import androidx.compose.material.*
|
|||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.omnivore.omnivore.MainActivity
|
||||
import app.omnivore.omnivore.ui.reader.PDFReaderActivity
|
||||
import app.omnivore.omnivore.ui.reader.WebReaderLoadingContainerActivity
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
fun SaveContent(viewModel: SaveViewModel, modalBottomSheetState: ModalBottomSheetState, modifier: Modifier) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val context = LocalContext.current
|
||||
|
||||
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colors.background) {
|
||||
Column(
|
||||
|
|
@ -34,7 +41,11 @@ fun SaveContent(viewModel: SaveViewModel, modalBottomSheetState: ModalBottomShee
|
|||
onClick = {
|
||||
coroutineScope.launch {
|
||||
modalBottomSheetState.hide()
|
||||
// TODO: open app
|
||||
viewModel.clientRequestID?.let {
|
||||
val intent = Intent(context, WebReaderLoadingContainerActivity::class.java)
|
||||
intent.putExtra("SAVED_ITEM_REQUEST_ID", it)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
},
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ class SaveViewModel @Inject constructor(
|
|||
var message by mutableStateOf<String?>(null)
|
||||
private set
|
||||
|
||||
var clientRequestID by mutableStateOf<String?>(null)
|
||||
private set
|
||||
|
||||
private fun getAuthToken(): String? = runBlocking {
|
||||
datastoreRepo.getString(DatastoreKeys.omnivoreAuthToken)
|
||||
}
|
||||
|
|
@ -52,10 +55,12 @@ class SaveViewModel @Inject constructor(
|
|||
.build()
|
||||
|
||||
try {
|
||||
clientRequestID = UUID.randomUUID().toString()
|
||||
|
||||
val response = apolloClient.mutation(
|
||||
SaveUrlMutation(
|
||||
SaveUrlInput(
|
||||
clientRequestId = UUID.randomUUID().toString(),
|
||||
clientRequestId = clientRequestID!!,
|
||||
source = "android",
|
||||
url = url
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue