Merge pull request #1530 from omnivore-app/feature/android-home-refresh

Pull to Refresh - Android
This commit is contained in:
Satindar Dhillon 2022-12-13 21:02:56 -08:00 committed by GitHub
commit bc3e8853e8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 445 additions and 74 deletions

View file

@ -17,8 +17,8 @@ android {
applicationId "app.omnivore.omnivore"
minSdk 23
targetSdk 33
versionCode 9
versionName "0.0.10"
versionCode 15
versionName "0.0.15"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
@ -140,6 +140,9 @@ dependencies {
implementation 'com.google.code.gson:gson:2.8.9'
implementation 'com.pspdfkit:pspdfkit:8.4.1'
implementation 'com.segment.analytics.kotlin:android:1.10.0'
implementation 'io.intercom.android:intercom-sdk-base:14.0.0'
}
apollo {

View file

@ -4,6 +4,8 @@
package="app.omnivore.omnivore">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="com.google.android.gms.permission.AD_ID"/>
<application
android:name=".OmnivoreApplication"

View file

@ -0,0 +1,12 @@
mutation SetLinkArchived($input: ArchiveLinkInput!) {
setLinkArchived(input: $input) {
... on ArchiveLinkSuccess {
linkId
message
}
... on ArchiveLinkError {
message
errorCodes
}
}
}

View file

@ -0,0 +1,12 @@
mutation SetBookmarkArticle($input: SetBookmarkArticleInput!) {
setBookmarkArticle(input: $input) {
... on SetBookmarkArticleSuccess {
bookmarkedArticle {
id
}
}
... on SetBookmarkArticleError {
errorCodes
}
}
}

View file

@ -0,0 +1,13 @@
query Viewer {
me {
id
name
isFullUser
profile {
id
username
pictureUrl
bio
}
}
}

View file

@ -22,4 +22,8 @@ object AppModule {
@Singleton
@Provides
fun provideNetworker(datastore: DatastoreRepository) = Networker(datastore)
@Singleton
@Provides
fun provideAnalytics(@ApplicationContext app: Context) = EventTracker(app)
}

View file

@ -0,0 +1,36 @@
package app.omnivore.omnivore
import android.content.Context
import com.segment.analytics.kotlin.android.Analytics
import com.segment.analytics.kotlin.core.*
import io.intercom.android.sdk.Intercom
import io.intercom.android.sdk.identity.Registration
import org.json.JSONObject
import javax.inject.Inject
class EventTracker @Inject constructor(val app: Context) {
val segmentAnalytics: Analytics
init {
val writeKey = app.getString(R.string.segment_write_key)
segmentAnalytics = Analytics(writeKey, app.applicationContext) {
trackApplicationLifecycleEvents = true
application = app.applicationContext
useLifecycleObserver = true
}
}
fun registerUser(userID: String) {
segmentAnalytics.identify(userID)
Intercom.client().loginIdentifiedUser(Registration.create().withUserId(userID))
}
fun debugMessage(message: String) {
track(message)
}
fun track(eventName: String, jsonObject: JSONObject = JSONObject()) {
segmentAnalytics.track(eventName, jsonObject)
}
}

View file

@ -2,7 +2,17 @@ package app.omnivore.omnivore
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
import io.intercom.android.sdk.Intercom
@HiltAndroidApp
class OmnivoreApplication: Application() {
override fun onCreate() {
super.onCreate()
Intercom.initialize(
this,
this.getString(R.string.intercom_api_key),
this.getString(R.string.intercom_app_id)
)
}
}

View file

@ -0,0 +1,9 @@
package app.omnivore.omnivore.models
data class Viewer(
val id: String,
val name: String,
val username: String,
val pictureUrl: String?,
)

View file

@ -0,0 +1,26 @@
package app.omnivore.omnivore.networking
import app.omnivore.omnivore.graphql.generated.SetBookmarkArticleMutation
import app.omnivore.omnivore.graphql.generated.SetLinkArchivedMutation
import app.omnivore.omnivore.graphql.generated.type.ArchiveLinkInput
import app.omnivore.omnivore.graphql.generated.type.SetBookmarkArticleInput
suspend fun Networker.deleteLinkedItem(itemID: String): Boolean {
val input = SetBookmarkArticleInput(itemID, false)
val result = authenticatedApolloClient().mutation(SetBookmarkArticleMutation(input)).execute()
return result.data?.setBookmarkArticle?.onSetBookmarkArticleSuccess?.bookmarkedArticle?.id != null
}
suspend fun Networker.archiveLinkedItem(itemID: String): Boolean {
return updateArchiveStatusLinkedItem(itemID, true)
}
suspend fun Networker.unarchiveLinkedItem(itemID: String): Boolean {
return updateArchiveStatusLinkedItem(itemID, false)
}
private suspend fun Networker.updateArchiveStatusLinkedItem(itemID: String, setAsArchived: Boolean): Boolean {
val input = ArchiveLinkInput(setAsArchived, itemID)
val result = authenticatedApolloClient().mutation(SetLinkArchivedMutation(input)).execute()
return result.data?.setLinkArchived?.onArchiveLinkSuccess?.linkId != null
}

View file

@ -0,0 +1,20 @@
package app.omnivore.omnivore.networking
import app.omnivore.omnivore.graphql.generated.ViewerQuery
import app.omnivore.omnivore.models.Viewer
suspend fun Networker.viewer(): Viewer? {
val result = authenticatedApolloClient().query(ViewerQuery()).execute()
val me = result.data?.me
return if (me != null) {
Viewer(
id = me.id,
name = me.name,
username = me.profile.username,
pictureUrl = me.profile.pictureUrl
)
} else {
null
}
}

View file

@ -8,12 +8,15 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import app.omnivore.omnivore.*
import app.omnivore.omnivore.graphql.generated.SearchQuery
import app.omnivore.omnivore.graphql.generated.ValidateUsernameQuery
import app.omnivore.omnivore.networking.Networker
import app.omnivore.omnivore.networking.viewer
import com.apollographql.apollo3.ApolloClient
import com.apollographql.apollo3.api.Optional
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.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.distinctUntilChanged
@ -36,7 +39,9 @@ data class PendingEmailUserCreds(
@HiltViewModel
class LoginViewModel @Inject constructor(
private val datastoreRepo: DatastoreRepository
private val datastoreRepo: DatastoreRepository,
private val eventTracker: EventTracker,
private val networker: Networker
): ViewModel() {
private var validateUsernameJob: Job? = null
@ -90,6 +95,15 @@ class LoginViewModel @Inject constructor(
showSocialLogin()
}
fun registerUser() {
viewModelScope.launch {
val viewer = networker.viewer()
viewer?.let {
eventTracker.registerUser(viewer.id)
}
}
}
private fun resetState() {
validateUsernameJob = null
isLoading = false
@ -255,6 +269,7 @@ class LoginViewModel @Inject constructor(
fun logout() {
viewModelScope.launch {
datastoreRepo.clear()
Intercom.client().logout()
}
}

View file

@ -7,6 +7,10 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState
@ -17,6 +21,7 @@ import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import app.omnivore.omnivore.Routes
import app.omnivore.omnivore.models.LinkedItem
import app.omnivore.omnivore.ui.linkedItemViews.LinkedItemCard
import app.omnivore.omnivore.ui.reader.PDFReaderActivity
import kotlinx.coroutines.flow.distinctUntilChanged
@ -50,6 +55,7 @@ fun HomeView(
}
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun HomeViewContent(
homeViewModel: HomeViewModel,
@ -58,35 +64,54 @@ fun HomeViewContent(
) {
val context = LocalContext.current
val listState = rememberLazyListState()
val pullRefreshState = rememberPullRefreshState(
refreshing = homeViewModel.isRefreshing,
onRefresh = { homeViewModel.refresh() }
)
val linkedItems: List<LinkedItem> by homeViewModel.itemsLiveData.observeAsState(listOf())
LazyColumn(
state = listState,
verticalArrangement = Arrangement.Top,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier
.background(MaterialTheme.colorScheme.background)
Box(
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 6.dp)
.pullRefresh(pullRefreshState)
) {
items(linkedItems) { item ->
LinkedItemCard(
item = item,
onClickHandler = {
if (item.isPDF()) {
val intent = Intent(context, PDFReaderActivity::class.java)
intent.putExtra("LINKED_ITEM_SLUG", item.slug)
context.startActivity(intent)
} else {
navController.navigate("WebReader/${item.slug}")
}
}
)
LazyColumn(
state = listState,
verticalArrangement = Arrangement.Top,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier
.background(MaterialTheme.colorScheme.background)
.fillMaxSize()
.padding(horizontal = 6.dp)
) {
items(linkedItems) { item ->
LinkedItemCard(
item = item,
onClickHandler = {
if (item.isPDF()) {
val intent = Intent(context, PDFReaderActivity::class.java)
intent.putExtra("LINKED_ITEM_SLUG", item.slug)
context.startActivity(intent)
} else {
navController.navigate("WebReader/${item.slug}")
}
},
actionHandler = { homeViewModel.handleLinkedItemAction(item.id, it) }
)
}
}
}
InfiniteListHandler(listState = listState) {
homeViewModel.load()
InfiniteListHandler(listState = listState) {
homeViewModel.load()
}
PullRefreshIndicator(
refreshing = homeViewModel.isRefreshing,
state = pullRefreshState,
modifier = Modifier.align(Alignment.TopCenter)
)
}
}

View file

@ -1,18 +1,24 @@
package app.omnivore.omnivore.ui.home
import android.util.Log
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import app.omnivore.omnivore.models.LinkedItem
import app.omnivore.omnivore.networking.Networker
import app.omnivore.omnivore.networking.search
import app.omnivore.omnivore.networking.*
import com.pspdfkit.analytics.Analytics
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class HomeViewModel @Inject constructor(
private val networker: Networker
private val networker: Networker,
): ViewModel() {
private var cursor: String? = null
private var items: List<LinkedItem> = listOf()
@ -26,6 +32,7 @@ class HomeViewModel @Inject constructor(
// Live Data
val searchTextLiveData = MutableLiveData("")
val itemsLiveData = MutableLiveData<List<LinkedItem>>(listOf())
var isRefreshing by mutableStateOf(false)
fun updateSearchText(text: String) {
searchTextLiveData.value = text
@ -37,6 +44,11 @@ class HomeViewModel @Inject constructor(
}
}
fun refresh() {
isRefreshing = true
load(true)
}
fun load(clearPreviousSearch: Boolean = false) {
if (clearPreviousSearch) {
cursor = null
@ -61,14 +73,49 @@ class HomeViewModel @Inject constructor(
receivedIdx = thisSearchIdx
cursor = searchResult.cursor
if (searchTextLiveData.value != "") {
if (searchTextLiveData.value != "" || clearPreviousSearch) {
val previousItems = if (clearPreviousSearch) listOf() else searchedItems
searchedItems = previousItems.plus(searchResult.items)
itemsLiveData.value = searchedItems
itemsLiveData.postValue(searchedItems)
} else {
items = items.plus(searchResult.items)
itemsLiveData.value = items
itemsLiveData.postValue(items)
}
CoroutineScope(Dispatchers.Main).launch {
isRefreshing = false
}
}
}
fun handleLinkedItemAction(itemID: String, action: LinkedItemAction) {
when (action) {
LinkedItemAction.Delete -> {
removeItemFromList(itemID)
viewModelScope.launch {
networker.deleteLinkedItem(itemID)
}
}
LinkedItemAction.Archive -> {
removeItemFromList(itemID)
viewModelScope.launch {
networker.archiveLinkedItem(itemID)
}
}
LinkedItemAction.Unarchive -> {
removeItemFromList(itemID)
viewModelScope.launch {
networker.unarchiveLinkedItem(itemID)
}
}
}
}
private fun removeItemFromList(itemID: String) {
itemsLiveData.value?.let {
val newList = it.filter { item -> item.id != itemID }
itemsLiveData.postValue(newList)
}
}
@ -82,3 +129,9 @@ class HomeViewModel @Inject constructor(
return query
}
}
enum class LinkedItemAction {
Delete,
Archive,
Unarchive
}

View file

@ -1,13 +1,10 @@
package app.omnivore.omnivore.ui.home
package app.omnivore.omnivore.ui.linkedItemViews
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Divider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@ -16,10 +13,13 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import app.omnivore.omnivore.models.LinkedItem
import app.omnivore.omnivore.ui.home.LinkedItemAction
import coil.compose.rememberAsyncImagePainter
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun LinkedItemCard(item: LinkedItem, onClickHandler: () -> Unit) {
fun LinkedItemCard(item: LinkedItem, onClickHandler: () -> Unit, actionHandler: (LinkedItemAction) -> Unit) {
var isMenuExpanded by remember { mutableStateOf(false) }
val publisherDisplayName = item.publisherDisplayName()
Column {
@ -29,7 +29,11 @@ fun LinkedItemCard(item: LinkedItem, onClickHandler: () -> Unit) {
modifier = Modifier
.fillMaxWidth()
.padding(12.dp)
.clickable(onClick = onClickHandler)
.combinedClickable(
onClick = onClickHandler,
onLongClick = { isMenuExpanded = true }
)
.background(if (isMenuExpanded) Color.LightGray else Color.Transparent)
) {
Column(
verticalArrangement = Arrangement.spacedBy(2.dp),
@ -75,5 +79,12 @@ fun LinkedItemCard(item: LinkedItem, onClickHandler: () -> Unit) {
}
Divider(color = MaterialTheme.colorScheme.outlineVariant, thickness = 1.dp)
LinkedItemContextMenu(
isExpanded = isMenuExpanded,
isArchived = item.isArchived,
onDismiss = { isMenuExpanded = false },
actionHandler = actionHandler
)
}
}

View file

@ -0,0 +1,52 @@
package app.omnivore.omnivore.ui.linkedItemViews
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material.icons.outlined.List
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import app.omnivore.omnivore.ui.home.LinkedItemAction
@Composable
fun LinkedItemContextMenu(
isExpanded: Boolean,
isArchived: Boolean,
onDismiss: () -> Unit,
actionHandler: (LinkedItemAction) -> Unit
) {
DropdownMenu(
expanded = isExpanded,
onDismissRequest = onDismiss
) {
DropdownMenuItem(
text = { Text(if (isArchived) "Unarchive" else "Archive") },
onClick = {
val action = if (isArchived) LinkedItemAction.Unarchive else LinkedItemAction.Archive
actionHandler(action)
onDismiss()
},
leadingIcon = {
Icon(
Icons.Outlined.List, // TODO: use more appropriate icon
contentDescription = null
)
}
)
DropdownMenuItem(
text = { Text("Remove Item") },
onClick = {
actionHandler(LinkedItemAction.Delete)
onDismiss()
},
leadingIcon = {
Icon(
Icons.Outlined.Delete,
contentDescription = null
)
}
)
}
}

View file

@ -322,12 +322,13 @@ class PDFReaderActivity: AppCompatActivity(), DocumentListener, TextSelectionMan
p0.dismiss()
return@OnPopupToolbarItemClickedListener true
}
// 2 -> {
// Log.d("pdf", "user selected annotate action")
2 -> {
Log.d("pdf", "user selected annotate action")
showAnnotationView("")
// textSelectionController?.textSelection = null
// p0.dismiss()
// return@OnPopupToolbarItemClickedListener true
// }
p0.dismiss()
return@OnPopupToolbarItemClickedListener true
}
3 -> {
val text = textSelectionController?.textSelection?.text ?: ""
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
@ -349,7 +350,7 @@ class PDFReaderActivity: AppCompatActivity(), DocumentListener, TextSelectionMan
p0.menuItems = listOf(
PopupToolbarMenuItem(1, R.string.pdf_highlight_menu_action),
// PopupToolbarMenuItem(2, R.string.annotate_menu_action),
PopupToolbarMenuItem(2, R.string.annotate_menu_action),
PopupToolbarMenuItem(3, R.string.pdf_highlight_copy),
)
}
@ -432,6 +433,7 @@ class PDFReaderActivity: AppCompatActivity(), DocumentListener, TextSelectionMan
actionMode = null
clickedHighlight = null
clickedHighlightPosition = null
textSelectionController?.textSelection = null
viewModel.annotationUnderNoteEdit = null
}
@ -443,8 +445,15 @@ class PDFReaderActivity: AppCompatActivity(), DocumentListener, TextSelectionMan
val annotationEditFragment = AnnotationEditFragment()
annotationEditFragment.configure(
onSave = { newNote ->
clickedHighlight?.let { highlight ->
viewModel.updateHighlightNote(highlight, newNote)
if (clickedHighlight != null) {
viewModel.updateHighlightNote(clickedHighlight!!, newNote)
} else {
pendingHighlightAnnotation?.let { annotation ->
val quote = textSelectionController?.textSelection?.text ?: ""
fragment.addAnnotationToPage(annotation, false) {
viewModel.syncHighlightUpdates(annotation, quote, listOf(), newNote)
}
}
}
resetHighlightTap()
},

View file

@ -101,7 +101,7 @@ class PDFReaderViewModel @Inject constructor(
}
}
fun syncHighlightUpdates(newAnnotation: Annotation, quote: String, overlapIds: List<String>) {
fun syncHighlightUpdates(newAnnotation: Annotation, quote: String, overlapIds: List<String>, note: String? = null) {
val itemID = pdfReaderParamsLiveData.value?.item?.id ?: return
val highlightID = UUID.randomUUID().toString()
val shortID = UUID.randomUUID().toString().replace("-","").substring(0,8)
@ -130,7 +130,7 @@ class PDFReaderViewModel @Inject constructor(
}
} else {
val createHighlightInput = CreateHighlightInput(
annotation = Optional.presentIfNotNull(null),
annotation = Optional.presentIfNotNull(note),
articleId = itemID,
id = highlightID,
patch = newAnnotation.toInstantJson(),
@ -141,17 +141,16 @@ class PDFReaderViewModel @Inject constructor(
viewModelScope.launch {
networker.createHighlight(createHighlightInput)
}
if (note != null) {
storeUpdatedNoteLocally(newAnnotation, note!!)
}
}
}
fun updateHighlightNote(annotation: Annotation, note: String) {
// Save the updated note locally
val omnivoreHighlight = annotation.customData?.get("omnivoreHighlight") as? JSONObject
omnivoreHighlight?.put("editedNote", note)
omnivoreHighlight?.let {
Log.d("pdf", "setting custom data: $omnivoreHighlight")
annotation.customData = JSONObject().put("omnivoreHighlight", it)
}
storeUpdatedNoteLocally(annotation, note)
// Sync update with data service
viewModelScope.launch {
@ -165,6 +164,15 @@ class PDFReaderViewModel @Inject constructor(
}
}
private fun storeUpdatedNoteLocally(annotation: Annotation, note: String) {
val omnivoreHighlight = annotation.customData?.get("omnivoreHighlight") as? JSONObject
omnivoreHighlight?.put("editedNote", note)
omnivoreHighlight?.let {
Log.d("pdf", "setting custom data: $omnivoreHighlight")
annotation.customData = JSONObject().put("omnivoreHighlight", it)
}
}
fun deleteHighlight(annotation: Annotation) {
val highlightID = pluckHighlightID(annotation) ?: return
viewModelScope.launch {

View file

@ -14,6 +14,7 @@ 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.*
import androidx.compose.runtime.*
@ -28,6 +29,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.ContextCompat.getSystemService
import app.omnivore.omnivore.R
import app.omnivore.omnivore.ui.linkedItemViews.LinkedItemContextMenu
import com.google.gson.Gson
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@ -39,6 +41,7 @@ import kotlin.math.roundToInt
@Composable
fun WebReaderLoadingContainer(slug: String, webReaderViewModel: WebReaderViewModel) {
var isMenuExpanded by remember { mutableStateOf(false) }
var showWebPreferencesDialog by remember { mutableStateOf(false ) }
val webReaderParams: WebReaderParams? by webReaderViewModel.webReaderParamsLiveData.observeAsState(null)
@ -94,12 +97,25 @@ fun WebReaderLoadingContainer(slug: String, webReaderViewModel: WebReaderViewMod
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,
imageVector = Icons.Filled.Settings, // TODO: set a better icon
contentDescription = null
)
}
LinkedItemContextMenu(
isExpanded = isMenuExpanded,
isArchived = webReaderParams!!.item.isArchived,
onDismiss = { isMenuExpanded = false },
actionHandler = { webReaderViewModel.handleLinkedItemAction(webReaderParams!!.item.id, it) }
)
}
)
@ -254,7 +270,7 @@ class OmnivoreWebView(context: Context) : WebView(context) {
// Called when the user selects a contextual menu item
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
return when (item.itemId) {
R.id.annotateHighlight -> {
R.id.annotateHighlight, R.id.annotate -> {
val script = "var event = new Event('annotate');document.dispatchEvent(event);"
evaluateJavascript(script) {
mode.finish()

View file

@ -10,8 +10,11 @@ import app.omnivore.omnivore.DatastoreKeys
import app.omnivore.omnivore.DatastoreRepository
import app.omnivore.omnivore.models.LinkedItem
import app.omnivore.omnivore.networking.*
import app.omnivore.omnivore.ui.home.LinkedItemAction
import com.google.gson.Gson
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.util.*
@ -62,6 +65,36 @@ class WebReaderViewModel @Inject constructor(
}
}
fun handleLinkedItemAction(itemID: String, action: LinkedItemAction) {
when (action) {
LinkedItemAction.Delete -> {
viewModelScope.launch {
networker.deleteLinkedItem(itemID)
popToHomeView(itemID)
}
}
LinkedItemAction.Archive -> {
viewModelScope.launch {
networker.archiveLinkedItem(itemID)
popToHomeView(itemID)
}
}
LinkedItemAction.Unarchive -> {
viewModelScope.launch {
networker.unarchiveLinkedItem(itemID)
popToHomeView(itemID)
}
}
}
}
private fun popToHomeView(itemID: String) {
CoroutineScope(Dispatchers.Main).launch {
// TODO: pop to home
Log.d("maxx", "should pop to home and remove item with ID: $itemID")
}
}
fun handleIncomingWebMessage(actionID: String, jsonString: String) {
when (actionID) {
"createHighlight" -> {

View file

@ -53,6 +53,13 @@ fun RootView(
} else {
WelcomeScreen(viewModel = loginViewModel)
}
DisposableEffect(hasAuthToken) {
if (hasAuthToken) {
loginViewModel.registerUser()
}
onDispose {}
}
}
}

View file

@ -16,6 +16,8 @@ import app.omnivore.omnivore.Routes
import app.omnivore.omnivore.ui.auth.LoginViewModel
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
import io.intercom.android.sdk.Intercom
import io.intercom.android.sdk.IntercomSpace
@OptIn(ExperimentalMaterial3Api::class)
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@ -51,6 +53,11 @@ fun SettingsView(
.padding(horizontal = 16.dp)
) {
LogoutButton { loginViewModel.logout() }
Button(onClick = {
Intercom.client().present(space = IntercomSpace.Messages)
}) {
Text(text = "Open Help Center")
}
}
}
}

View file

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/createHighlight"
android:title="@string/pdf_highlight_menu_action"
app:showAsAction="always">
</item>
<item
android:id="@+id/copyPdfHighlight"
android:title="@string/pdf_highlight_copy"
app:showAsAction="always">
</item>
</menu>

View file

@ -1,4 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pspdfkit_license_key">unset</string>
<string name="segment_write_key">unset</string>
<string name="intercom_api_key">unset</string>
<string name="intercom_app_id">unset</string>
</resources>