diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/networking/HighlightMutations.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/networking/HighlightMutations.kt index f41344c7b..725c81011 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/networking/HighlightMutations.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/networking/HighlightMutations.kt @@ -3,7 +3,7 @@ package app.omnivore.omnivore.networking import android.util.Log import app.omnivore.omnivore.graphql.generated.CreateHighlightMutation import app.omnivore.omnivore.graphql.generated.type.CreateHighlightInput -import app.omnivore.omnivore.graphql.generated.type.Highlight +import app.omnivore.omnivore.models.Highlight import com.apollographql.apollo3.api.Optional import com.google.gson.Gson @@ -27,14 +27,30 @@ data class CreateHighlightParams( suspend fun Networker.createWebHighlight(jsonString: String): Boolean { val input = Gson().fromJson(jsonString, CreateHighlightParams::class.java).asCreateHighlightInput() - return createHighlight(input) + return createHighlight(input) != null } -suspend fun Networker.createHighlight(input: CreateHighlightInput): Boolean { +suspend fun Networker.createHighlight(input: CreateHighlightInput): Highlight? { Log.d("Loggo", "created highlight input: $input") val result = authenticatedApolloClient().mutation(CreateHighlightMutation(input)).execute() - val highlight = result.data?.createHighlight?.onCreateHighlightSuccess?.highlight - return highlight != null + val createdHighlight = result.data?.createHighlight?.onCreateHighlightSuccess?.highlight + + if (createdHighlight != null) { + return Highlight( + id = createdHighlight.highlightFields.id, + shortId = createdHighlight.highlightFields.shortId, + quote = createdHighlight.highlightFields.quote, + prefix = createdHighlight.highlightFields.prefix, + suffix = createdHighlight.highlightFields.suffix, + patch = createdHighlight.highlightFields.patch, + annotation = createdHighlight.highlightFields.annotation, + createdAt = null, // TODO: update gql query to get this + updatedAt = createdHighlight.highlightFields.updatedAt, + createdByMe = createdHighlight.highlightFields.createdByMe, + ) + } else { + return null + } } diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/PDFReader.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/PDFReader.kt index 615be9117..f436eca2a 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/PDFReader.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/PDFReader.kt @@ -16,6 +16,7 @@ import app.omnivore.omnivore.R import app.omnivore.omnivore.models.Highlight import com.pspdfkit.annotations.Annotation import com.pspdfkit.annotations.AnnotationProvider +import com.pspdfkit.annotations.HighlightAnnotation import com.pspdfkit.configuration.PdfConfiguration import com.pspdfkit.configuration.activity.ThumbnailBarMode import com.pspdfkit.configuration.page.PageScrollDirection @@ -93,7 +94,8 @@ class PDFReaderActivity: AppCompatActivity(), DocumentListener { } override fun onAnnotationUpdated(annotation: Annotation) { - viewModel.updateHighlight(annotation) + val highlightAnnotation = annotation as? HighlightAnnotation ?: return + viewModel.syncUpdatedAnnotationHighlight(highlightAnnotation, articleID = params.item.id) } override fun onAnnotationRemoved(annotation: Annotation) { diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/PDFReaderViewModel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/PDFReaderViewModel.kt index 776a6294c..5f45b49bd 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/PDFReaderViewModel.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/PDFReaderViewModel.kt @@ -15,11 +15,15 @@ import app.omnivore.omnivore.networking.* import com.apollographql.apollo3.api.Optional import com.google.gson.Gson import com.pspdfkit.annotations.Annotation +import com.pspdfkit.annotations.HighlightAnnotation import com.pspdfkit.document.download.DownloadJob import com.pspdfkit.document.download.DownloadRequest import com.pspdfkit.document.download.Progress import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.merge import kotlinx.coroutines.launch +import org.json.JSONArray +import org.json.JSONObject import java.io.File import java.util.* import javax.inject.Inject @@ -36,7 +40,8 @@ class PDFReaderViewModel @Inject constructor( private val networker: Networker ): ViewModel() { val pdfReaderParamsLiveData = MutableLiveData(null) - var annotations: List = listOf() + var annotations: List = listOf() + var documentHighlights: MutableList = mutableListOf() fun loadItem(slug: String, context: Context) { viewModelScope.launch { @@ -56,6 +61,8 @@ class PDFReaderViewModel @Inject constructor( } override fun onComplete(output: File) { + documentHighlights.addAll(0, articleQueryResult.highlights) + val articleContent = ArticleContent( title = article.title, htmlContent = article.content ?: "", @@ -79,58 +86,94 @@ class PDFReaderViewModel @Inject constructor( pdfReaderParamsLiveData.postValue(null) } - fun createHighlight(annotation: Annotation, articleID: String) { - // TODO: Check for overlapping highlights + fun createHighlight(annotation: Annotation, articleID: String, updateDoc: Boolean = true) { + val highlightID = UUID.randomUUID().toString() + val shortID = UUID.randomUUID().toString().replace("-","").substring(0,8) + val quote = annotation.contents ?: "" + + val jsonValues = JSONObject() + .put("id", highlightID) + .put("shortId", shortID) + .put("quote", quote) + .put("articleId", articleID) + + val omnivoreHighlight = JSONObject() + .put("omnivoreHighlight", jsonValues) + + annotation.customData = omnivoreHighlight + val createHighlightInput = CreateHighlightInput( annotation = Optional.presentIfNotNull(null), articleId = articleID, - id = UUID.randomUUID().toString(), + id = highlightID, patch = annotation.toInstantJson(), - quote = annotation.contents ?: "", - shortId = UUID.randomUUID().toString().replace("-","").substring(0,8), + quote = quote, + shortId = shortID, ) -// val ggg = overlappingHighlights(annotation) -// Log.d("annny", "has ${ggg.count()} overlapping highlights") - viewModelScope.launch { - val isHighlightSynced = networker.createHighlight(createHighlightInput) - Log.d("Network", "isHighlightSynced = $isHighlightSynced") + val highlight = networker.createHighlight(createHighlightInput) + if (highlight != null) { + documentHighlights.add(highlight) + } } } - fun updateHighlight(annotation: Annotation) { - Log.d("annny", "updated $annotation") + fun syncUpdatedAnnotationHighlight(annotation: HighlightAnnotation, articleID: String) { + val overlapList = overlappingHighlightIDs(annotation) + // TODO: Delete the overlap list + // Calling create highlight creates a loop... +// createHighlight(annotation, articleID) } fun deleteHighlight(annotation: Annotation) { Log.d("annny", "deleted $annotation") } - private fun overlappingHighlights(annotation: Annotation): List { - var result: MutableList = mutableListOf() + private fun overlappingHighlightIDs(annotation: HighlightAnnotation): List { + val result: MutableList = mutableListOf() + val highlightID = pluckHighlightID(annotation) ?: return listOf() - for (highlight in pdfReaderParamsLiveData.value?.articleContent?.highlights ?: listOf()) { - if (hasOverlappingHighlights(highlight, annotation)) { - result.add(highlight) + val pageHighlights = documentHighlights.filter { + Gson().fromJson(it.patch, HighlightPatch::class.java).pageIndex == annotation.pageIndex + } + + for (highlight in pageHighlights) { + if (highlight.id == highlightID) { + continue + } + + val rects = Gson().fromJson(highlight.patch, HighlightPatch::class.java).rects + if (hasOverlaps(annotation.rects, rects)) { + result.add(highlight.id) } } + result.add(highlightID) + return result } - private fun hasOverlappingHighlights(highlight: Highlight, annotation: Annotation): Boolean { - val highlightRects = Gson().fromJson(highlight.patch, HighlightRects::class.java).rects - - for (rect in highlightRects) { - if (rect.intersect(annotation.boundingBox)) { - return true + private fun hasOverlaps(leftRects: List, rightRects: List>): Boolean { + for (leftRect in leftRects) { + for (rightRect in rightRects) { + val transformedRect = RectF(rightRect[0].toFloat(), rightRect[1].toFloat(), rightRect[2].toFloat(), rightRect[3].toFloat()) + if (transformedRect.intersect(leftRect)) { + return true + } } } + return false } + + private fun pluckHighlightID(annotation: Annotation): String? { + val omnivoreHighlight = annotation.customData?.get("omnivoreHighlight") as? JSONObject + return omnivoreHighlight?.get("id") as? String + } } -data class HighlightRects( - val rects: List +data class HighlightPatch( + val rects: List>, + val pageIndex: Int )