load web reader container from read now button action

This commit is contained in:
Satindar Dhillon 2023-01-30 15:22:30 -08:00
parent 3bf74d1f75
commit 00dfb9386c
6 changed files with 234 additions and 120 deletions

View file

@ -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>

View file

@ -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(

View file

@ -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...")
}
}

View file

@ -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)

View file

@ -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(

View file

@ -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
)