add updatesSince API to Android

This commit is contained in:
Satindar Dhillon
2023-01-09 15:07:37 -08:00
parent 270412bc49
commit 2bb2e806ff
12 changed files with 176 additions and 10 deletions

View File

@ -0,0 +1,56 @@
query UpdatesSince($after: String, $first: Int, $since: Date!) {
updatesSince(after: $after, first: $first, since: $since) {
... on UpdatesSinceSuccess {
edges {
cursor
itemID
updateReason
node {
id
title
slug
url
pageType
contentReader
createdAt
isArchived
readingProgressPercent
readingProgressAnchorIndex
author
image
description
publishedAt
ownedByViewer
originalArticleUrl
uploadFileId
labels {
id
name
color
}
pageId
shortId
quote
annotation
state
siteName
subscription
readAt
savedAt
updatedAt
language
}
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
totalCount
}
}
... on UpdatesSinceError {
errorCodes
}
}
}

View File

@ -12,6 +12,7 @@ object DatastoreKeys {
const val omnivoreAuthToken = "omnivoreAuthToken"
const val omnivoreAuthCookieString = "omnivoreAuthCookieString"
const val omnivorePendingUserToken = "omnivorePendingUserToken"
const val libraryLastSyncTimestamp = "libraryLastSyncTimestamp"
const val preferredWebFontSize = "preferredWebFontSize"
const val preferredWebLineHeight = "preferredWebLineHeight"
const val preferredWebMaxWidthPercentage = "preferredWebMaxWidthPercentage"

View File

@ -15,3 +15,7 @@ class DataService @Inject constructor(
AppDatabase::class.java, "omnivore-database"
).build()
}
//suspend fun DataService.sync(): Boolean {
//
//}

View File

@ -0,0 +1,11 @@
package app.omnivore.omnivore.models
public enum class ServerSyncStatus(
public val rawValue: Int,
) {
IS_SYNCED(0),
IS_SYNCING(1),
NEEDS_DELETION(2),
NEEDS_CREATION(3),
NEEDS_UPDATE(4)
}

View File

@ -8,6 +8,7 @@ import app.omnivore.omnivore.graphql.generated.UpdateHighlightMutation
import app.omnivore.omnivore.graphql.generated.type.CreateHighlightInput
import app.omnivore.omnivore.graphql.generated.type.MergeHighlightInput
import app.omnivore.omnivore.graphql.generated.type.UpdateHighlightInput
import app.omnivore.omnivore.models.ServerSyncStatus
import app.omnivore.omnivore.persistence.entities.Highlight
import com.apollographql.apollo3.api.Optional
import com.google.gson.Gson
@ -150,8 +151,7 @@ suspend fun Networker.createHighlight(input: CreateHighlightInput): Highlight? {
createdAt = null, // TODO: update gql query to get this
updatedAt = null, // TODO: fix updatedAtString?.let { LocalDate.parse(it) },
createdByMe = createdHighlight.highlightFields.createdByMe,
markedForDeletion = false,
serverSyncStatus = 1 // TODO: create enum for this
markedForDeletion = false
)
} else {
return null

View File

@ -53,7 +53,6 @@ suspend fun Networker.savedItem(slug: String): SavedItemQueryResponse {
updatedAt = null, //updatedAtString?.let { str -> LocalDate.parse(str) }, TODO: fix date parsing
createdByMe = it.highlightFields.createdByMe,
markedForDeletion = false,
serverSyncStatus = 1 // TODO: create enum for this
)
}

View File

@ -2,6 +2,8 @@ package app.omnivore.omnivore.networking
import app.omnivore.omnivore.graphql.generated.SearchQuery
import app.omnivore.omnivore.graphql.generated.TypeaheadSearchQuery
import app.omnivore.omnivore.graphql.generated.UpdatesSinceQuery
import app.omnivore.omnivore.graphql.generated.type.UpdateReason
import app.omnivore.omnivore.persistence.entities.SavedItem
import app.omnivore.omnivore.persistence.entities.SavedItemCardData
import com.apollographql.apollo3.api.Optional
@ -11,6 +13,14 @@ data class SearchQueryResponse(
val cardsData: List<SavedItemCardData>
)
data class SavedItemUpdatesQueryResponse(
val cursor: String?,
val hasMoreItems: Boolean,
val totalCount: Int,
val deletedItemIDs: List<String>,
val items: List<SavedItem>
)
suspend fun Networker.typeaheadSearch(
query: String
): SearchQueryResponse {
@ -55,7 +65,6 @@ suspend fun Networker.search(
)
).execute()
val newCursor = result.data?.search?.onSearchSuccess?.pageInfo?.endCursor
val itemList = result.data?.search?.onSearchSuccess?.edges ?: listOf()
@ -78,3 +87,65 @@ suspend fun Networker.search(
return SearchQueryResponse(null, listOf())
}
}
suspend fun Networker.savedItemUpdates(
cursor: String? = null,
limit: Int = 15,
since: String
): SavedItemUpdatesQueryResponse? {
try {
val result = authenticatedApolloClient().query(
UpdatesSinceQuery(
after = Optional.presentIfNotNull(cursor),
first = Optional.presentIfNotNull(limit),
since = since
)
).execute()
val payload = result.data?.updatesSince?.onUpdatesSinceSuccess ?: return null
val itemNodes: MutableList<UpdatesSinceQuery.Node> = mutableListOf()
val deletedItemIDs: MutableList<String> = mutableListOf()
for (edge in payload.edges) {
if (edge.updateReason == UpdateReason.DELETED) {
deletedItemIDs.add(edge.itemID)
} else if (edge.node != null) {
itemNodes.add(edge.node)
}
}
val savedItems = itemNodes.map {
SavedItem(
id = it.id,
title = it.title,
createdAt = it.createdAt as String,
savedAt = it.savedAt as String,
readAt = it.readAt as String?,
updatedAt = it.updatedAt as String?,
readingProgress = it.readingProgressPercent,
readingProgressAnchor = it.readingProgressAnchorIndex,
imageURLString = it.image,
pageURLString = it.url,
descriptionText = it.description,
publisherURLString = it.originalArticleUrl,
siteName = it.siteName,
author = it.author,
publishDate = it.publishedAt as String?,
slug = it.slug,
isArchived = it.isArchived,
contentReader = it.contentReader.rawValue,
content = null
)
}
return SavedItemUpdatesQueryResponse(
cursor = payload.pageInfo.endCursor,
hasMoreItems = payload.pageInfo.hasNextPage,
totalCount = savedItems.size,
deletedItemIDs = deletedItemIDs,
items = savedItems
)
} catch (e: java.lang.Exception) {
return null
}
}

View File

@ -2,6 +2,7 @@ package app.omnivore.omnivore.persistence.entities
import androidx.room.Entity
import androidx.room.PrimaryKey
import app.omnivore.omnivore.models.ServerSyncStatus
import java.time.LocalDate
import java.util.Date

View File

@ -2,6 +2,7 @@ package app.omnivore.omnivore.persistence.entities
import androidx.core.net.toUri
import androidx.room.*
import app.omnivore.omnivore.models.ServerSyncStatus
import app.omnivore.omnivore.persistence.BaseDao
@Entity
@ -35,7 +36,7 @@ data class SavedItem(
val onDeviceImageURLString: String? = null,
val originalHtml: String? = null,
@ColumnInfo(typeAffinity = ColumnInfo.BLOB) val pdfData: ByteArray? = null,
val serverSyncStatus: Int = 0, // TODO: implement,
val serverSyncStatus: Int = 0,
val tempPDFURL: String? = null
// hasMany highlights

View File

@ -2,6 +2,7 @@ package app.omnivore.omnivore.persistence.entities
import androidx.room.Entity
import androidx.room.PrimaryKey
import app.omnivore.omnivore.models.ServerSyncStatus
@Entity
data class SavedItemLabel(

View File

@ -8,19 +8,20 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import app.omnivore.omnivore.DataService
import app.omnivore.omnivore.DatastoreKeys
import app.omnivore.omnivore.DatastoreRepository
import app.omnivore.omnivore.networking.*
import app.omnivore.omnivore.persistence.entities.SavedItemCardData
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.*
import java.time.LocalDateTime
import javax.inject.Inject
@HiltViewModel
class LibraryViewModel @Inject constructor(
private val networker: Networker,
private val dataService: DataService
private val dataService: DataService,
private val datastoreRepo: DatastoreRepository
): ViewModel() {
private var cursor: String? = null
private var items: List<SavedItemCardData> = listOf()
@ -51,6 +52,19 @@ class LibraryViewModel @Inject constructor(
load(true)
}
fun getLastSyncTime(): LocalDateTime? = runBlocking {
datastoreRepo.getString(DatastoreKeys.libraryLastSyncTimestamp)?.let {
LocalDateTime.parse(it)
}
}
fun syncItems() {
val syncStart = LocalDateTime.now()
val lastSyncDate = getLastSyncTime() ?: LocalDateTime.MIN
// try? await dataService.syncOfflineItemsWithServerIfNeeded()
}
fun load(clearPreviousSearch: Boolean = false) {
if (clearPreviousSearch) {
cursor = null