diff --git a/android/Omnivore/app/build.gradle b/android/Omnivore/app/build.gradle index 1578a16ba..df4cac0c6 100644 --- a/android/Omnivore/app/build.gradle +++ b/android/Omnivore/app/build.gradle @@ -17,8 +17,8 @@ android { applicationId "app.omnivore.omnivore" minSdk 26 targetSdk 33 - versionCode 124 - versionName "0.0.124" + versionCode 126 + versionName "0.0.126" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { diff --git a/android/Omnivore/app/src/main/graphql/schema.graphqls b/android/Omnivore/app/src/main/graphql/schema.graphqls index 988946951..335c93409 100644 --- a/android/Omnivore/app/src/main/graphql/schema.graphqls +++ b/android/Omnivore/app/src/main/graphql/schema.graphqls @@ -100,7 +100,7 @@ type Article { title: String! unsubHttpUrl: String unsubMailTo: String - updatedAt: Date! + updatedAt: Date uploadFileId: ID url: String! wordsCount: Int @@ -134,7 +134,7 @@ type ArticleSavingRequest { id: ID! slug: String! status: ArticleSavingRequestStatus! - updatedAt: Date! + updatedAt: Date url: String! user: User! userId: ID! @deprecated(reason: "userId has been replaced with user") @@ -638,7 +638,7 @@ type Feature { id: ID! name: String! token: String! - updatedAt: Date! + updatedAt: Date } type FeedArticle { @@ -677,12 +677,14 @@ type FeedArticlesSuccess { type Filter { category: String! createdAt: Date! + defaultFilter: Boolean description: String filter: String! id: ID! name: String! position: Int! - updatedAt: Date! + updatedAt: Date + visible: Boolean } type FiltersError { @@ -823,7 +825,7 @@ type Highlight { shortId: String! suffix: String type: HighlightType! - updatedAt: Date! + updatedAt: Date user: User! } @@ -832,7 +834,7 @@ type HighlightReply { highlight: Highlight! id: ID! text: String! - updatedAt: Date! + updatedAt: Date user: User! } @@ -861,6 +863,13 @@ type ImportFromIntegrationSuccess { success: Boolean! } +enum ImportItemState { + ALL + ARCHIVED + UNARCHIVED + UNREAD +} + type Integration { createdAt: Date! enabled: Boolean! @@ -869,7 +878,7 @@ type Integration { taskName: String token: String! type: IntegrationType! - updatedAt: Date! + updatedAt: Date } enum IntegrationType { @@ -962,7 +971,7 @@ type Link { shareInfo: LinkShareInfo! shareStats: ShareStats! slug: String! - updatedAt: Date! + updatedAt: Date url: String! } @@ -1105,20 +1114,14 @@ type Mutation { createArticleSavingRequest(input: CreateArticleSavingRequestInput!): CreateArticleSavingRequestResult! createGroup(input: CreateGroupInput!): CreateGroupResult! createHighlight(input: CreateHighlightInput!): CreateHighlightResult! - createHighlightReply(input: CreateHighlightReplyInput!): CreateHighlightReplyResult! createLabel(input: CreateLabelInput!): CreateLabelResult! createNewsletterEmail: CreateNewsletterEmailResult! - createReaction(input: CreateReactionInput!): CreateReactionResult! - createReminder(input: CreateReminderInput!): CreateReminderResult! deleteAccount(userID: ID!): DeleteAccountResult! deleteFilter(id: ID!): DeleteFilterResult! deleteHighlight(highlightId: ID!): DeleteHighlightResult! - deleteHighlightReply(highlightReplyId: ID!): DeleteHighlightReplyResult! deleteIntegration(id: ID!): DeleteIntegrationResult! deleteLabel(id: ID!): DeleteLabelResult! deleteNewsletterEmail(newsletterEmailId: ID!): DeleteNewsletterEmailResult! - deleteReaction(id: ID!): DeleteReactionResult! - deleteReminder(id: ID!): DeleteReminderResult! deleteRule(id: ID!): DeleteRuleResult! deleteWebhook(id: ID!): DeleteWebhookResult! generateApiKey(input: GenerateApiKeyInput!): GenerateApiKeyResult! @@ -1145,25 +1148,20 @@ type Mutation { setBookmarkArticle(input: SetBookmarkArticleInput!): SetBookmarkArticleResult! setDeviceToken(input: SetDeviceTokenInput!): SetDeviceTokenResult! setFavoriteArticle(id: ID!): SetFavoriteArticleResult! - setFollow(input: SetFollowInput!): SetFollowResult! setIntegration(input: SetIntegrationInput!): SetIntegrationResult! setLabels(input: SetLabelsInput!): SetLabelsResult! setLabelsForHighlight(input: SetLabelsForHighlightInput!): SetLabelsResult! setLinkArchived(input: ArchiveLinkInput!): ArchiveLinkResult! setRule(input: SetRuleInput!): SetRuleResult! - setShareArticle(input: SetShareArticleInput!): SetShareArticleResult! - setShareHighlight(input: SetShareHighlightInput!): SetShareHighlightResult! setUserPersonalization(input: SetUserPersonalizationInput!): SetUserPersonalizationResult! setWebhook(input: SetWebhookInput!): SetWebhookResult! subscribe(input: SubscribeInput!): SubscribeResult! unsubscribe(name: String!, subscriptionId: ID): UnsubscribeResult! + updateEmail(input: UpdateEmailInput!): UpdateEmailResult! + updateFilter(input: UpdateFilterInput!): UpdateFilterResult! updateHighlight(input: UpdateHighlightInput!): UpdateHighlightResult! - updateHighlightReply(input: UpdateHighlightReplyInput!): UpdateHighlightReplyResult! updateLabel(input: UpdateLabelInput!): UpdateLabelResult! - updateLinkShareInfo(input: UpdateLinkShareInfoInput!): UpdateLinkShareInfoResult! updatePage(input: UpdatePageInput!): UpdatePageResult! - updateReminder(input: UpdateReminderInput!): UpdateReminderResult! - updateSharedComment(input: UpdateSharedCommentInput!): UpdateSharedCommentResult! updateSubscription(input: UpdateSubscriptionInput!): UpdateSubscriptionResult! updateUser(input: UpdateUserInput!): UpdateUserResult! updateUserProfile(input: UpdateUserProfileInput!): UpdateUserProfileResult! @@ -1226,7 +1224,7 @@ type Page { readableHtml: String! title: String! type: PageType! - updatedAt: Date! + updatedAt: Date url: String! } @@ -1293,12 +1291,8 @@ type Query { apiKeys: ApiKeysResult! article(format: String, slug: String!, username: String!): ArticleResult! articleSavingRequest(id: ID, url: String): ArticleSavingRequestResult! - articles(after: String, first: Int, includePending: Boolean, query: String, sharedOnly: Boolean, sort: SortParams): ArticlesResult! deviceTokens: DeviceTokensResult! - feedArticles(after: String, first: Int, sharedByUser: ID, sort: SortParams): FeedArticlesResult! filters: FiltersResult! - getFollowers(userId: ID): GetFollowersResult! - getFollowing(userId: ID): GetFollowingResult! getUserPersonalization: GetUserPersonalizationResult! groups: GroupsResult! hello: String @@ -1308,11 +1302,9 @@ type Query { newsletterEmails: NewsletterEmailsResult! recentEmails: RecentEmailsResult! recentSearches: RecentSearchesResult! - reminder(linkId: ID!): ReminderResult! rules(enabled: Boolean): RulesResult! search(after: String, first: Int, format: String, includeContent: Boolean, query: String): SearchResult! sendInstallInstructions: SendInstallInstructionsResult! - sharedArticle(selectedHighlightId: String, slug: String!, username: String!): SharedArticleResult! subscriptions(sort: SortParams, type: SubscriptionType): SubscriptionsResult! typeaheadSearch(first: Int, query: String!): TypeaheadSearchResult! updatesSince(after: String, first: Int, since: Date!, sort: SortParams): UpdatesSinceResult! @@ -1459,7 +1451,7 @@ type RecommendationGroup { members: [User!]! name: String! topics: [String!] - updatedAt: Date! + updatedAt: Date } type RecommendingUser { @@ -1535,7 +1527,7 @@ type Rule { filter: String! id: ID! name: String! - updatedAt: Date! + updatedAt: Date } type RuleAction { @@ -1586,8 +1578,9 @@ enum SaveArticleReadingProgressErrorCode { } input SaveArticleReadingProgressInput { + force: Boolean id: ID! - readingProgressAnchorIndex: Int! + readingProgressAnchorIndex: Int readingProgressPercent: Float! readingProgressTopPercent: Float } @@ -1629,11 +1622,11 @@ enum SaveFilterErrorCode { } input SaveFilterInput { - category: String! + category: String description: String filter: String! - id: ID name: String! + position: Int } union SaveFilterResult = SaveFilterError | SaveFilterSuccess @@ -1810,7 +1803,7 @@ enum SetFavoriteArticleErrorCode { union SetFavoriteArticleResult = SetFavoriteArticleError | SetFavoriteArticleSuccess type SetFavoriteArticleSuccess { - favoriteArticle: Article! + success: Boolean! } type SetFollowError { @@ -1848,7 +1841,9 @@ enum SetIntegrationErrorCode { input SetIntegrationInput { enabled: Boolean! id: ID + importItemState: ImportItemState name: String! + syncedAt: Date token: String! type: IntegrationType } @@ -2073,9 +2068,8 @@ enum SubscribeErrorCode { } input SubscribeInput { - name: String subscriptionType: SubscriptionType - url: String + url: String! } union SubscribeResult = SubscribeError | SubscribeSuccess @@ -2097,7 +2091,7 @@ type Subscription { type: SubscriptionType! unsubscribeHttpUrl: String unsubscribeMailTo: String - updatedAt: Date! + updatedAt: Date url: String } @@ -2174,6 +2168,53 @@ type UnsubscribeSuccess { subscription: Subscription! } +type UpdateEmailError { + errorCodes: [UpdateEmailErrorCode!]! +} + +enum UpdateEmailErrorCode { + BAD_REQUEST + EMAIL_ALREADY_EXISTS + UNAUTHORIZED +} + +input UpdateEmailInput { + email: String! +} + +union UpdateEmailResult = UpdateEmailError | UpdateEmailSuccess + +type UpdateEmailSuccess { + email: String! + verificationEmailSent: Boolean +} + +type UpdateFilterError { + errorCodes: [UpdateFilterErrorCode!]! +} + +enum UpdateFilterErrorCode { + BAD_REQUEST + NOT_FOUND + UNAUTHORIZED +} + +input UpdateFilterInput { + category: String + description: String + filter: String + id: String! + name: String + position: Int + visible: Boolean +} + +union UpdateFilterResult = UpdateFilterError | UpdateFilterSuccess + +type UpdateFilterSuccess { + filter: Filter! +} + type UpdateHighlightError { errorCodes: [UpdateHighlightErrorCode!]! } @@ -2359,7 +2400,9 @@ input UpdateSubscriptionInput { description: String id: ID! lastFetchedAt: Date + lastFetchedChecksum: String name: String + scheduledAt: Date status: SubscriptionStatus } @@ -2485,9 +2528,11 @@ enum UploadImportFileType { } type User { + email: String followersCount: Int friendsCount: Int id: ID! + intercomHash: String isFriend: Boolean @deprecated(reason: "isFriend has been replaced with viewerIsFollowing") isFullUser: Boolean name: String! @@ -2497,6 +2542,7 @@ type User { sharedArticlesCount: Int sharedHighlightsCount: Int sharedNotesCount: Int + source: String viewerIsFollowing: Boolean } @@ -2551,7 +2597,7 @@ type Webhook { eventTypes: [WebhookEvent!]! id: ID! method: String! - updatedAt: Date! + updatedAt: Date url: String! } diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/dataService/SyncOfflineChanges.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/dataService/SyncOfflineChanges.kt index b1c949474..6110b91e4 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/dataService/SyncOfflineChanges.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/dataService/SyncOfflineChanges.kt @@ -66,6 +66,7 @@ private suspend fun DataService.syncSavedItem(item: SavedItem) { val isReadingProgressSynced = networker.updateReadingProgress( ReadingProgressParams( id = item.savedItemId, + force = item.contentReader == "PDF", readingProgressPercent = item.readingProgress, readingProgressAnchorIndex = item.readingProgressAnchor ) diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/networking/ReadingProgressMutations.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/networking/ReadingProgressMutations.kt index 2fb86e691..2ca820474 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/networking/ReadingProgressMutations.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/networking/ReadingProgressMutations.kt @@ -5,17 +5,20 @@ import app.omnivore.omnivore.graphql.generated.type.SaveArticleReadingProgressIn import android.util.Log +import com.apollographql.apollo3.api.Optional import com.google.gson.Gson data class ReadingProgressParams( val id: String?, val readingProgressPercent: Double?, - val readingProgressAnchorIndex: Int? + val readingProgressAnchorIndex: Int?, + val force: Boolean? ) { fun asSaveReadingProgressInput() = SaveArticleReadingProgressInput( id = id ?: "", + force = Optional.presentIfNotNull(force), readingProgressPercent = readingProgressPercent ?: 0.0, - readingProgressAnchorIndex = readingProgressAnchorIndex ?: 0 + readingProgressAnchorIndex = Optional.presentIfNotNull(readingProgressAnchorIndex ?: 0) ) } 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 e674b89ac..4410e077e 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 @@ -131,16 +131,15 @@ class PDFReaderViewModel @Inject constructor( fun syncPageChange(currentPageIndex: Int, totalPages: Int) { val rawProgress = ((currentPageIndex + 1).toDouble() / totalPages.toDouble()) * 100 val percent = min(100.0, max(0.0, rawProgress)) - if (percent > currentReadingProgress) { - currentReadingProgress = percent - viewModelScope.launch { - val params = ReadingProgressParams( - id = pdfReaderParamsLiveData.value?.item?.savedItemId, - readingProgressPercent = percent, - readingProgressAnchorIndex = currentPageIndex - ) - networker.updateReadingProgress(params) - } + currentReadingProgress = percent + viewModelScope.launch { + val params = ReadingProgressParams( + id = pdfReaderParamsLiveData.value?.item?.savedItemId, + readingProgressPercent = percent, + readingProgressAnchorIndex = currentPageIndex, + force = true + ) + networker.updateReadingProgress(params) } }