Merge pull request #2266 from omnivore-app/feat/android-notebooks-ux
WIP: Android notebooks
This commit is contained in:
@ -17,8 +17,8 @@ android {
|
||||
applicationId "app.omnivore.omnivore"
|
||||
minSdk 26
|
||||
targetSdk 33
|
||||
versionCode 80
|
||||
versionName "0.0.80"
|
||||
versionCode 82
|
||||
versionName "0.0.82"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
@ -148,6 +148,7 @@ dependencies {
|
||||
|
||||
// Room Deps
|
||||
implementation "androidx.room:room-runtime:$room_version"
|
||||
implementation "androidx.room:room-ktx:$room_version"
|
||||
annotationProcessor "androidx.room:room-compiler:$room_version"
|
||||
kapt "androidx.room:room-compiler:$room_version"
|
||||
|
||||
|
||||
@ -53,6 +53,7 @@ fragment HighlightFields on Highlight {
|
||||
patch
|
||||
annotation
|
||||
createdByMe
|
||||
createdAt
|
||||
updatedAt
|
||||
sharedAt
|
||||
}
|
||||
|
||||
@ -1,4 +1,9 @@
|
||||
directive @sanitize(allowedTags: [String], maxLength: Int, minLength: Int, pattern: String) on INPUT_FIELD_DEFINITION
|
||||
directive @sanitize(
|
||||
allowedTags: [String]
|
||||
maxLength: Int
|
||||
minLength: Int
|
||||
pattern: String
|
||||
) on INPUT_FIELD_DEFINITION
|
||||
|
||||
type AddPopularReadError {
|
||||
errorCodes: [AddPopularReadErrorCode!]!
|
||||
@ -150,7 +155,9 @@ enum ArticleSavingRequestErrorCode {
|
||||
UNAUTHORIZED
|
||||
}
|
||||
|
||||
union ArticleSavingRequestResult = ArticleSavingRequestError | ArticleSavingRequestSuccess
|
||||
union ArticleSavingRequestResult =
|
||||
ArticleSavingRequestError
|
||||
| ArticleSavingRequestSuccess
|
||||
|
||||
enum ArticleSavingRequestStatus {
|
||||
ARCHIVED
|
||||
@ -248,7 +255,9 @@ input CreateArticleSavingRequestInput {
|
||||
url: String!
|
||||
}
|
||||
|
||||
union CreateArticleSavingRequestResult = CreateArticleSavingRequestError | CreateArticleSavingRequestSuccess
|
||||
union CreateArticleSavingRequestResult =
|
||||
CreateArticleSavingRequestError
|
||||
| CreateArticleSavingRequestSuccess
|
||||
|
||||
type CreateArticleSavingRequestSuccess {
|
||||
articleSavingRequest: ArticleSavingRequest!
|
||||
@ -329,7 +338,9 @@ input CreateHighlightReplyInput {
|
||||
text: String!
|
||||
}
|
||||
|
||||
union CreateHighlightReplyResult = CreateHighlightReplyError | CreateHighlightReplySuccess
|
||||
union CreateHighlightReplyResult =
|
||||
CreateHighlightReplyError
|
||||
| CreateHighlightReplySuccess
|
||||
|
||||
type CreateHighlightReplySuccess {
|
||||
highlightReply: HighlightReply!
|
||||
@ -373,7 +384,9 @@ enum CreateNewsletterEmailErrorCode {
|
||||
UNAUTHORIZED
|
||||
}
|
||||
|
||||
union CreateNewsletterEmailResult = CreateNewsletterEmailError | CreateNewsletterEmailSuccess
|
||||
union CreateNewsletterEmailResult =
|
||||
CreateNewsletterEmailError
|
||||
| CreateNewsletterEmailSuccess
|
||||
|
||||
type CreateNewsletterEmailSuccess {
|
||||
newsletterEmail: NewsletterEmail!
|
||||
@ -481,7 +494,9 @@ enum DeleteHighlightReplyErrorCode {
|
||||
UNAUTHORIZED
|
||||
}
|
||||
|
||||
union DeleteHighlightReplyResult = DeleteHighlightReplyError | DeleteHighlightReplySuccess
|
||||
union DeleteHighlightReplyResult =
|
||||
DeleteHighlightReplyError
|
||||
| DeleteHighlightReplySuccess
|
||||
|
||||
type DeleteHighlightReplySuccess {
|
||||
highlightReply: HighlightReply!
|
||||
@ -503,7 +518,9 @@ enum DeleteIntegrationErrorCode {
|
||||
UNAUTHORIZED
|
||||
}
|
||||
|
||||
union DeleteIntegrationResult = DeleteIntegrationError | DeleteIntegrationSuccess
|
||||
union DeleteIntegrationResult =
|
||||
DeleteIntegrationError
|
||||
| DeleteIntegrationSuccess
|
||||
|
||||
type DeleteIntegrationSuccess {
|
||||
integration: Integration!
|
||||
@ -535,7 +552,9 @@ enum DeleteNewsletterEmailErrorCode {
|
||||
UNAUTHORIZED
|
||||
}
|
||||
|
||||
union DeleteNewsletterEmailResult = DeleteNewsletterEmailError | DeleteNewsletterEmailSuccess
|
||||
union DeleteNewsletterEmailResult =
|
||||
DeleteNewsletterEmailError
|
||||
| DeleteNewsletterEmailSuccess
|
||||
|
||||
type DeleteNewsletterEmailSuccess {
|
||||
newsletterEmail: NewsletterEmail!
|
||||
@ -752,7 +771,9 @@ enum GetUserPersonalizationErrorCode {
|
||||
UNAUTHORIZED
|
||||
}
|
||||
|
||||
union GetUserPersonalizationResult = GetUserPersonalizationError | GetUserPersonalizationSuccess
|
||||
union GetUserPersonalizationResult =
|
||||
GetUserPersonalizationError
|
||||
| GetUserPersonalizationSuccess
|
||||
|
||||
type GetUserPersonalizationSuccess {
|
||||
userPersonalization: UserPersonalization
|
||||
@ -848,7 +869,9 @@ enum ImportFromIntegrationErrorCode {
|
||||
UNAUTHORIZED
|
||||
}
|
||||
|
||||
union ImportFromIntegrationResult = ImportFromIntegrationError | ImportFromIntegrationSuccess
|
||||
union ImportFromIntegrationResult =
|
||||
ImportFromIntegrationError
|
||||
| ImportFromIntegrationSuccess
|
||||
|
||||
type ImportFromIntegrationSuccess {
|
||||
success: Boolean!
|
||||
@ -1092,10 +1115,14 @@ type Mutation {
|
||||
addPopularRead(name: String!): AddPopularReadResult!
|
||||
bulkAction(action: BulkActionType!, query: String): BulkActionResult!
|
||||
createArticle(input: CreateArticleInput!): CreateArticleResult!
|
||||
createArticleSavingRequest(input: CreateArticleSavingRequestInput!): CreateArticleSavingRequestResult!
|
||||
createArticleSavingRequest(
|
||||
input: CreateArticleSavingRequestInput!
|
||||
): CreateArticleSavingRequestResult!
|
||||
createGroup(input: CreateGroupInput!): CreateGroupResult!
|
||||
createHighlight(input: CreateHighlightInput!): CreateHighlightResult!
|
||||
createHighlightReply(input: CreateHighlightReplyInput!): CreateHighlightReplyResult!
|
||||
createHighlightReply(
|
||||
input: CreateHighlightReplyInput!
|
||||
): CreateHighlightReplyResult!
|
||||
createLabel(input: CreateLabelInput!): CreateLabelResult!
|
||||
createNewsletterEmail: CreateNewsletterEmailResult!
|
||||
createReaction(input: CreateReactionInput!): CreateReactionResult!
|
||||
@ -1124,10 +1151,14 @@ type Mutation {
|
||||
moveLabel(input: MoveLabelInput!): MoveLabelResult!
|
||||
optInFeature(input: OptInFeatureInput!): OptInFeatureResult!
|
||||
recommend(input: RecommendInput!): RecommendResult!
|
||||
recommendHighlights(input: RecommendHighlightsInput!): RecommendHighlightsResult!
|
||||
recommendHighlights(
|
||||
input: RecommendHighlightsInput!
|
||||
): RecommendHighlightsResult!
|
||||
reportItem(input: ReportItemInput!): ReportItemResult!
|
||||
revokeApiKey(id: ID!): RevokeApiKeyResult!
|
||||
saveArticleReadingProgress(input: SaveArticleReadingProgressInput!): SaveArticleReadingProgressResult!
|
||||
saveArticleReadingProgress(
|
||||
input: SaveArticleReadingProgressInput!
|
||||
): SaveArticleReadingProgressResult!
|
||||
saveFile(input: SaveFileInput!): SaveResult!
|
||||
saveFilter(input: SaveFilterInput!): SaveFilterResult!
|
||||
savePage(input: SavePageInput!): SaveResult!
|
||||
@ -1142,21 +1173,32 @@ type Mutation {
|
||||
setRule(input: SetRuleInput!): SetRuleResult!
|
||||
setShareArticle(input: SetShareArticleInput!): SetShareArticleResult!
|
||||
setShareHighlight(input: SetShareHighlightInput!): SetShareHighlightResult!
|
||||
setUserPersonalization(input: SetUserPersonalizationInput!): SetUserPersonalizationResult!
|
||||
setUserPersonalization(
|
||||
input: SetUserPersonalizationInput!
|
||||
): SetUserPersonalizationResult!
|
||||
setWebhook(input: SetWebhookInput!): SetWebhookResult!
|
||||
subscribe(name: String!): SubscribeResult!
|
||||
unsubscribe(name: String!): UnsubscribeResult!
|
||||
updateHighlight(input: UpdateHighlightInput!): UpdateHighlightResult!
|
||||
updateHighlightReply(input: UpdateHighlightReplyInput!): UpdateHighlightReplyResult!
|
||||
updateHighlightReply(
|
||||
input: UpdateHighlightReplyInput!
|
||||
): UpdateHighlightReplyResult!
|
||||
updateLabel(input: UpdateLabelInput!): UpdateLabelResult!
|
||||
updateLinkShareInfo(input: UpdateLinkShareInfoInput!): UpdateLinkShareInfoResult!
|
||||
updateLinkShareInfo(
|
||||
input: UpdateLinkShareInfoInput!
|
||||
): UpdateLinkShareInfoResult!
|
||||
updatePage(input: UpdatePageInput!): UpdatePageResult!
|
||||
updateReminder(input: UpdateReminderInput!): UpdateReminderResult!
|
||||
updateSharedComment(input: UpdateSharedCommentInput!): UpdateSharedCommentResult!
|
||||
updateSharedComment(
|
||||
input: UpdateSharedCommentInput!
|
||||
): UpdateSharedCommentResult!
|
||||
updateUser(input: UpdateUserInput!): UpdateUserResult!
|
||||
updateUserProfile(input: UpdateUserProfileInput!): UpdateUserProfileResult!
|
||||
uploadFileRequest(input: UploadFileRequestInput!): UploadFileRequestResult!
|
||||
uploadImportFile(contentType: String!, type: UploadImportFileType!): UploadImportFileResult!
|
||||
uploadImportFile(
|
||||
contentType: String!
|
||||
type: UploadImportFileType!
|
||||
): UploadImportFileResult!
|
||||
}
|
||||
|
||||
type NewsletterEmail {
|
||||
@ -1281,9 +1323,21 @@ 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!
|
||||
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!
|
||||
feedArticles(
|
||||
after: String
|
||||
first: Int
|
||||
sharedByUser: ID
|
||||
sort: SortParams
|
||||
): FeedArticlesResult!
|
||||
filters: FiltersResult!
|
||||
getFollowers(userId: ID): GetFollowersResult!
|
||||
getFollowing(userId: ID): GetFollowingResult!
|
||||
@ -1298,12 +1352,27 @@ type Query {
|
||||
recentSearches: RecentSearchesResult!
|
||||
reminder(linkId: ID!): ReminderResult!
|
||||
rules(enabled: Boolean): RulesResult!
|
||||
search(after: String, first: Int, format: String, includeContent: Boolean, query: String): SearchResult!
|
||||
search(
|
||||
after: String
|
||||
first: Int
|
||||
format: String
|
||||
includeContent: Boolean
|
||||
query: String
|
||||
): SearchResult!
|
||||
sendInstallInstructions: SendInstallInstructionsResult!
|
||||
sharedArticle(selectedHighlightId: String, slug: String!, username: String!): SharedArticleResult!
|
||||
sharedArticle(
|
||||
selectedHighlightId: String
|
||||
slug: String!
|
||||
username: String!
|
||||
): SharedArticleResult!
|
||||
subscriptions(sort: SortParams): SubscriptionsResult!
|
||||
typeaheadSearch(first: Int, query: String!): TypeaheadSearchResult!
|
||||
updatesSince(after: String, first: Int, since: Date!, sort: SortParams): UpdatesSinceResult!
|
||||
updatesSince(
|
||||
after: String
|
||||
first: Int
|
||||
since: Date!
|
||||
sort: SortParams
|
||||
): UpdatesSinceResult!
|
||||
user(userId: ID, username: String): UserResult!
|
||||
users: UsersResult!
|
||||
validateUsername(username: String!): Boolean!
|
||||
@ -1409,7 +1478,9 @@ input RecommendHighlightsInput {
|
||||
pageId: ID!
|
||||
}
|
||||
|
||||
union RecommendHighlightsResult = RecommendHighlightsError | RecommendHighlightsSuccess
|
||||
union RecommendHighlightsResult =
|
||||
RecommendHighlightsError
|
||||
| RecommendHighlightsSuccess
|
||||
|
||||
type RecommendHighlightsSuccess {
|
||||
success: Boolean!
|
||||
@ -1574,7 +1645,9 @@ input SaveArticleReadingProgressInput {
|
||||
readingProgressTopPercent: Float
|
||||
}
|
||||
|
||||
union SaveArticleReadingProgressResult = SaveArticleReadingProgressError | SaveArticleReadingProgressSuccess
|
||||
union SaveArticleReadingProgressResult =
|
||||
SaveArticleReadingProgressError
|
||||
| SaveArticleReadingProgressSuccess
|
||||
|
||||
type SaveArticleReadingProgressSuccess {
|
||||
updatedArticle: Article!
|
||||
@ -1721,7 +1794,9 @@ enum SendInstallInstructionsErrorCode {
|
||||
UNAUTHORIZED
|
||||
}
|
||||
|
||||
union SendInstallInstructionsResult = SendInstallInstructionsError | SendInstallInstructionsSuccess
|
||||
union SendInstallInstructionsResult =
|
||||
SendInstallInstructionsError
|
||||
| SendInstallInstructionsSuccess
|
||||
|
||||
type SendInstallInstructionsSuccess {
|
||||
sent: Boolean!
|
||||
@ -1741,7 +1816,9 @@ input SetBookmarkArticleInput {
|
||||
bookmark: Boolean!
|
||||
}
|
||||
|
||||
union SetBookmarkArticleResult = SetBookmarkArticleError | SetBookmarkArticleSuccess
|
||||
union SetBookmarkArticleResult =
|
||||
SetBookmarkArticleError
|
||||
| SetBookmarkArticleSuccess
|
||||
|
||||
type SetBookmarkArticleSuccess {
|
||||
bookmarkedArticle: Article!
|
||||
@ -1904,7 +1981,9 @@ input SetShareHighlightInput {
|
||||
share: Boolean!
|
||||
}
|
||||
|
||||
union SetShareHighlightResult = SetShareHighlightError | SetShareHighlightSuccess
|
||||
union SetShareHighlightResult =
|
||||
SetShareHighlightError
|
||||
| SetShareHighlightSuccess
|
||||
|
||||
type SetShareHighlightSuccess {
|
||||
highlight: Highlight!
|
||||
@ -1932,7 +2011,9 @@ input SetUserPersonalizationInput {
|
||||
theme: String
|
||||
}
|
||||
|
||||
union SetUserPersonalizationResult = SetUserPersonalizationError | SetUserPersonalizationSuccess
|
||||
union SetUserPersonalizationResult =
|
||||
SetUserPersonalizationError
|
||||
| SetUserPersonalizationSuccess
|
||||
|
||||
type SetUserPersonalizationSuccess {
|
||||
updatedUserPersonalization: UserPersonalization!
|
||||
@ -2144,7 +2225,9 @@ input UpdateHighlightReplyInput {
|
||||
text: String!
|
||||
}
|
||||
|
||||
union UpdateHighlightReplyResult = UpdateHighlightReplyError | UpdateHighlightReplySuccess
|
||||
union UpdateHighlightReplyResult =
|
||||
UpdateHighlightReplyError
|
||||
| UpdateHighlightReplySuccess
|
||||
|
||||
type UpdateHighlightReplySuccess {
|
||||
highlightReply: HighlightReply!
|
||||
@ -2195,7 +2278,9 @@ input UpdateLinkShareInfoInput {
|
||||
title: String!
|
||||
}
|
||||
|
||||
union UpdateLinkShareInfoResult = UpdateLinkShareInfoError | UpdateLinkShareInfoSuccess
|
||||
union UpdateLinkShareInfoResult =
|
||||
UpdateLinkShareInfoError
|
||||
| UpdateLinkShareInfoSuccess
|
||||
|
||||
type UpdateLinkShareInfoSuccess {
|
||||
message: String!
|
||||
@ -2271,7 +2356,9 @@ input UpdateSharedCommentInput {
|
||||
sharedComment: String!
|
||||
}
|
||||
|
||||
union UpdateSharedCommentResult = UpdateSharedCommentError | UpdateSharedCommentSuccess
|
||||
union UpdateSharedCommentResult =
|
||||
UpdateSharedCommentError
|
||||
| UpdateSharedCommentSuccess
|
||||
|
||||
type UpdateSharedCommentSuccess {
|
||||
articleID: ID!
|
||||
@ -2313,7 +2400,9 @@ input UpdateUserProfileInput {
|
||||
username: String
|
||||
}
|
||||
|
||||
union UpdateUserProfileResult = UpdateUserProfileError | UpdateUserProfileSuccess
|
||||
union UpdateUserProfileResult =
|
||||
UpdateUserProfileError
|
||||
| UpdateUserProfileSuccess
|
||||
|
||||
type UpdateUserProfileSuccess {
|
||||
user: User!
|
||||
@ -2357,7 +2446,9 @@ input UploadFileRequestInput {
|
||||
url: String!
|
||||
}
|
||||
|
||||
union UploadFileRequestResult = UploadFileRequestError | UploadFileRequestSuccess
|
||||
union UploadFileRequestResult =
|
||||
UploadFileRequestError
|
||||
| UploadFileRequestSuccess
|
||||
|
||||
type UploadFileRequestSuccess {
|
||||
createdPageId: String
|
||||
@ -2397,7 +2488,8 @@ type User {
|
||||
followersCount: Int
|
||||
friendsCount: Int
|
||||
id: ID!
|
||||
isFriend: Boolean @deprecated(reason: "isFriend has been replaced with viewerIsFollowing")
|
||||
isFriend: Boolean
|
||||
@deprecated(reason: "isFriend has been replaced with viewerIsFollowing")
|
||||
isFullUser: Boolean
|
||||
name: String!
|
||||
picture: String
|
||||
|
||||
@ -1,12 +1,16 @@
|
||||
package app.omnivore.omnivore.dataService
|
||||
|
||||
import app.omnivore.omnivore.graphql.generated.type.CreateHighlightInput
|
||||
import app.omnivore.omnivore.graphql.generated.type.HighlightType
|
||||
import app.omnivore.omnivore.models.ServerSyncStatus
|
||||
import app.omnivore.omnivore.networking.*
|
||||
import app.omnivore.omnivore.persistence.entities.Highlight
|
||||
import app.omnivore.omnivore.persistence.entities.SavedItemAndHighlightCrossRef
|
||||
import com.apollographql.apollo3.api.Optional
|
||||
import com.google.gson.Gson
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.*
|
||||
|
||||
suspend fun DataService.createWebHighlight(jsonString: String) {
|
||||
val createHighlightInput = Gson().fromJson(jsonString, CreateHighlightParams::class.java).asCreateHighlightInput()
|
||||
@ -44,6 +48,53 @@ suspend fun DataService.createWebHighlight(jsonString: String) {
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun DataService.createNoteHighlight(savedItemId: String, note: String): String {
|
||||
val shortId = UUID.randomUUID().toString()
|
||||
val createHighlightId = UUID.randomUUID().toString()
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
val highlight = Highlight(
|
||||
type = "NOTE",
|
||||
highlightId = createHighlightId,
|
||||
shortId = shortId,
|
||||
quote = null,
|
||||
prefix = null,
|
||||
suffix = null,
|
||||
patch =null,
|
||||
annotation = note,
|
||||
createdAt = null,
|
||||
updatedAt = null,
|
||||
createdByMe = true
|
||||
)
|
||||
|
||||
highlight.serverSyncStatus = ServerSyncStatus.NEEDS_CREATION.rawValue
|
||||
|
||||
val crossRef = SavedItemAndHighlightCrossRef(
|
||||
highlightId = createHighlightId,
|
||||
savedItemId = savedItemId
|
||||
)
|
||||
|
||||
db.highlightDao().insertAll(listOf(highlight))
|
||||
db.savedItemAndHighlightCrossRefDao().insertAll(listOf(crossRef))
|
||||
|
||||
val newHighlight = networker.createHighlight(input = CreateHighlightParams(
|
||||
type = HighlightType.NOTE,
|
||||
articleId = savedItemId,
|
||||
id = createHighlightId,
|
||||
shortId = shortId,
|
||||
quote = null,
|
||||
patch = null,
|
||||
annotation = note,
|
||||
).asCreateHighlightInput())
|
||||
|
||||
newHighlight?.let {
|
||||
db.highlightDao().update(it)
|
||||
}
|
||||
}
|
||||
|
||||
return createHighlightId
|
||||
}
|
||||
|
||||
suspend fun DataService.mergeWebHighlights(jsonString: String) {
|
||||
val mergeHighlightInput = Gson().fromJson(jsonString, MergeHighlightsParams::class.java).asMergeHighlightInput()
|
||||
|
||||
|
||||
@ -34,6 +34,10 @@ suspend fun DataService.sync(since: String, cursor: String?, limit: Int = 20): S
|
||||
val syncResult = networker.savedItemUpdates(cursor = cursor, limit = limit, since = since)
|
||||
?: return SavedItemSyncResult.errorResult
|
||||
|
||||
if (syncResult.deletedItemIDs.isNotEmpty()) {
|
||||
db.savedItemDao().deleteByIds(syncResult.deletedItemIDs)
|
||||
}
|
||||
|
||||
val savedItems = syncResult.items.map {
|
||||
val savedItem = SavedItem(
|
||||
savedItemId = it.id,
|
||||
|
||||
@ -6,6 +6,7 @@ import app.omnivore.omnivore.graphql.generated.DeleteHighlightMutation
|
||||
import app.omnivore.omnivore.graphql.generated.MergeHighlightMutation
|
||||
import app.omnivore.omnivore.graphql.generated.UpdateHighlightMutation
|
||||
import app.omnivore.omnivore.graphql.generated.type.CreateHighlightInput
|
||||
import app.omnivore.omnivore.graphql.generated.type.HighlightType
|
||||
import app.omnivore.omnivore.graphql.generated.type.MergeHighlightInput
|
||||
import app.omnivore.omnivore.graphql.generated.type.UpdateHighlightInput
|
||||
import app.omnivore.omnivore.persistence.entities.Highlight
|
||||
@ -13,6 +14,7 @@ import com.apollographql.apollo3.api.Optional
|
||||
import com.google.gson.Gson
|
||||
|
||||
data class CreateHighlightParams(
|
||||
val type: HighlightType,
|
||||
val shortId: String?,
|
||||
val id: String?,
|
||||
val quote: String?,
|
||||
@ -21,7 +23,8 @@ data class CreateHighlightParams(
|
||||
val `annotation`: String?
|
||||
) {
|
||||
fun asCreateHighlightInput() = CreateHighlightInput(
|
||||
annotation = Optional.presentIfNotNull(`annotation`),
|
||||
type = Optional.presentIfNotNull(type),
|
||||
annotation = Optional.presentIfNotNull(annotation),
|
||||
articleId = articleId ?: "",
|
||||
id = id ?: "",
|
||||
patch = Optional.presentIfNotNull(patch),
|
||||
@ -136,8 +139,6 @@ suspend fun Networker.createHighlight(input: CreateHighlightInput): Highlight? {
|
||||
val createdHighlight = result.data?.createHighlight?.onCreateHighlightSuccess?.highlight
|
||||
|
||||
if (createdHighlight != null) {
|
||||
// val updatedAtString = createdHighlight.highlightFields.updatedAt as? String
|
||||
|
||||
return Highlight(
|
||||
type = createdHighlight.highlightFields.type.toString(),
|
||||
highlightId = createdHighlight.highlightFields.id,
|
||||
@ -147,8 +148,8 @@ suspend fun Networker.createHighlight(input: CreateHighlightInput): Highlight? {
|
||||
suffix = createdHighlight.highlightFields.suffix,
|
||||
patch = createdHighlight.highlightFields.patch,
|
||||
annotation = createdHighlight.highlightFields.annotation,
|
||||
createdAt = null, // TODO: update gql query to get this
|
||||
updatedAt = null, // TODO: fix updatedAtString?.let { LocalDate.parse(it) },
|
||||
createdAt = createdHighlight.highlightFields.createdAt.toString(),
|
||||
updatedAt = createdHighlight.highlightFields.updatedAt.toString(),
|
||||
createdByMe = createdHighlight.highlightFields.createdByMe
|
||||
)
|
||||
} else {
|
||||
|
||||
@ -51,8 +51,8 @@ suspend fun Networker.savedItem(slug: String): SavedItemQueryResponse {
|
||||
suffix = it.highlightFields.suffix,
|
||||
patch = it.highlightFields.patch,
|
||||
annotation = it.highlightFields.annotation,
|
||||
createdAt = null, // TODO: update gql query to get this
|
||||
updatedAt = null, //updatedAtString?.let { str -> LocalDate.parse(str) }, TODO: fix date parsing
|
||||
createdAt = it.highlightFields.createdAt as String?,
|
||||
updatedAt = it.highlightFields.updatedAt as String?,
|
||||
createdByMe = it.highlightFields.createdByMe
|
||||
)
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ suspend fun Networker.search(
|
||||
savedItemLabelId = label.labelFields.id,
|
||||
name = label.labelFields.name,
|
||||
color = label.labelFields.color,
|
||||
createdAt = null,
|
||||
createdAt = label.labelFields.createdAt as String?,
|
||||
labelDescription = null
|
||||
)
|
||||
},
|
||||
@ -81,7 +81,7 @@ suspend fun Networker.search(
|
||||
shortId = highlight.highlightFields.shortId,
|
||||
suffix = highlight.highlightFields.suffix,
|
||||
updatedAt = highlight.highlightFields.updatedAt as String?,
|
||||
createdAt = null,
|
||||
createdAt = highlight.highlightFields.createdAt as String?,
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package app.omnivore.omnivore.persistence.entities
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.*
|
||||
import app.omnivore.omnivore.models.ServerSyncStatus
|
||||
import com.google.gson.annotations.SerializedName
|
||||
@ -12,7 +13,7 @@ data class Highlight(
|
||||
val type: String,
|
||||
var annotation: String?,
|
||||
val createdAt: String?,
|
||||
val createdByMe: Boolean,
|
||||
val createdByMe: Boolean = true,
|
||||
val markedForDeletion: Boolean = false,
|
||||
var patch: String?,
|
||||
var prefix: String?,
|
||||
@ -21,10 +22,6 @@ data class Highlight(
|
||||
var shortId: String,
|
||||
val suffix: String?,
|
||||
val updatedAt: String?
|
||||
|
||||
// has many SavedItemLabels (inverse: labels have many highlights)
|
||||
// has one savedItem (inverse: savedItem has many highlights
|
||||
// has a UserProfile (no inverse)
|
||||
)
|
||||
|
||||
@Entity(
|
||||
@ -90,6 +87,10 @@ interface HighlightDao {
|
||||
@Query("SELECT * FROM highlight WHERE highlightId = :highlightId")
|
||||
fun findById(highlightId: String): Highlight?
|
||||
|
||||
// Server sync status is passed in here to work around Room compile-time query rules, but should always be NEEDS_UPDATE
|
||||
@Query("UPDATE highlight SET annotation = :note, serverSyncStatus = :serverSyncStatus WHERE highlightId = :highlightId")
|
||||
fun updateNote(highlightId: String, note: String, serverSyncStatus: Int = ServerSyncStatus.NEEDS_UPDATE.rawValue)
|
||||
|
||||
@Update
|
||||
fun update(highlight: Highlight)
|
||||
}
|
||||
|
||||
@ -142,6 +142,9 @@ interface SavedItemDao {
|
||||
@Query("DELETE FROM savedItem WHERE savedItemId = :itemID")
|
||||
fun deleteById(itemID: String)
|
||||
|
||||
@Query("DELETE FROM savedItem WHERE savedItemId in (:itemIDs)")
|
||||
fun deleteByIds(itemIDs: List<String>)
|
||||
|
||||
@Update
|
||||
fun update(savedItem: SavedItem)
|
||||
|
||||
@ -161,6 +164,22 @@ interface SavedItemDao {
|
||||
)
|
||||
fun getLibraryItemById(savedItemId: String): LiveData<SavedItemWithLabelsAndHighlights>
|
||||
|
||||
@Transaction
|
||||
@Query(
|
||||
"SELECT ${SavedItemQueryConstants.libraryColumns} " +
|
||||
"FROM SavedItem " +
|
||||
"LEFT OUTER JOIN SavedItemAndSavedItemLabelCrossRef on SavedItem.savedItemId = SavedItemAndSavedItemLabelCrossRef.savedItemId " +
|
||||
"LEFT OUTER JOIN SavedItemAndHighlightCrossRef on SavedItem.savedItemId = SavedItemAndHighlightCrossRef.savedItemId " +
|
||||
|
||||
"LEFT OUTER JOIN SavedItemLabel on SavedItemLabel.savedItemLabelId = SavedItemAndSavedItemLabelCrossRef.savedItemLabelId " +
|
||||
"LEFT OUTER JOIN Highlight on highlight.highlightId = SavedItemAndHighlightCrossRef.highlightId " +
|
||||
|
||||
"WHERE SavedItem.savedItemId = :savedItemId " +
|
||||
|
||||
"GROUP BY SavedItem.savedItemId "
|
||||
)
|
||||
suspend fun getById(savedItemId: String): SavedItemWithLabelsAndHighlights?
|
||||
|
||||
@Transaction
|
||||
@Query(
|
||||
"SELECT ${SavedItemQueryConstants.libraryColumns} " +
|
||||
|
||||
@ -3,7 +3,6 @@ package app.omnivore.omnivore.ui.notebook
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
@ -19,12 +18,16 @@ import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.drawWithCache
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@ -38,7 +41,7 @@ import app.omnivore.omnivore.ui.library.*
|
||||
import dev.jeziellago.compose.markdowntext.MarkdownText
|
||||
import kotlinx.coroutines.launch
|
||||
import app.omnivore.omnivore.persistence.entities.Highlight
|
||||
|
||||
import app.omnivore.omnivore.ui.theme.OmnivoreTheme
|
||||
|
||||
|
||||
fun notebookMD(notes: List<Highlight>, highlights: List<Highlight>): String {
|
||||
@ -68,15 +71,12 @@ fun notebookMD(notes: List<Highlight>, highlights: List<Highlight>): String {
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun NotebookView(savedItemId: String, viewModel: NotebookViewModel) {
|
||||
fun NotebookView(savedItemId: String, viewModel: NotebookViewModel, onEditNote: (note: Highlight?) -> Unit) {
|
||||
var isMenuOpen by remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
val savedItem = viewModel.getLibraryItemById(savedItemId).observeAsState()
|
||||
val scrollState = rememberScrollState()
|
||||
val modalBottomSheetState = rememberModalBottomSheetState(
|
||||
ModalBottomSheetValue.Hidden,
|
||||
)
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val snackBarHostState = remember { SnackbarHostState() }
|
||||
val clipboard: ClipboardManager? =
|
||||
@ -85,69 +85,59 @@ fun NotebookView(savedItemId: String, viewModel: NotebookViewModel) {
|
||||
val notes = savedItem.value?.highlights?.filter { it.type == "NOTE" } ?: listOf()
|
||||
val highlights = savedItem.value?.highlights?.filter { it.type == "HIGHLIGHT" } ?: listOf()
|
||||
|
||||
ModalBottomSheetLayout(
|
||||
sheetBackgroundColor = Color.Transparent,
|
||||
sheetState = modalBottomSheetState,
|
||||
sheetContent = {
|
||||
// EditNoteModal()
|
||||
Spacer(modifier = Modifier.weight(1.0F))
|
||||
}
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Notebook") },
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.background
|
||||
),
|
||||
actions = {
|
||||
Box {
|
||||
IconButton(onClick = {
|
||||
isMenuOpen = true
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.MoreVert,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
if (isMenuOpen) {
|
||||
DropdownMenu(
|
||||
expanded = isMenuOpen,
|
||||
onDismissRequest = { isMenuOpen = false }
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = { Text("Copy") },
|
||||
onClick = {
|
||||
val clip = ClipData.newPlainText("notebook", notebookMD(notes, highlights))
|
||||
clipboard?.let {
|
||||
clipboard?.setPrimaryClip(clip)
|
||||
} ?: run {
|
||||
coroutineScope.launch {
|
||||
snackBarHostState
|
||||
.showSnackbar("Notebook copied")
|
||||
}
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Notebook") },
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.background
|
||||
),
|
||||
actions = {
|
||||
Box {
|
||||
IconButton(onClick = {
|
||||
isMenuOpen = true
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.MoreVert,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
if (isMenuOpen) {
|
||||
DropdownMenu(
|
||||
expanded = isMenuOpen,
|
||||
onDismissRequest = { isMenuOpen = false }
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = { Text("Copy") },
|
||||
onClick = {
|
||||
val clip = ClipData.newPlainText("notebook", notebookMD(notes, highlights))
|
||||
clipboard?.let {
|
||||
clipboard?.setPrimaryClip(clip)
|
||||
} ?: run {
|
||||
coroutineScope.launch {
|
||||
snackBarHostState
|
||||
.showSnackbar("Notebook copied")
|
||||
}
|
||||
isMenuOpen = false
|
||||
}
|
||||
)
|
||||
}
|
||||
isMenuOpen = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(scrollState)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
savedItem.value?.let {
|
||||
if (notes.isNotEmpty()) {
|
||||
ArticleNotes(it)
|
||||
}
|
||||
HighlightsList(it)
|
||||
}
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(scrollState)
|
||||
.fillMaxSize()
|
||||
.padding(top = paddingValues.calculateTopPadding())
|
||||
) {
|
||||
savedItem.value?.let {
|
||||
ArticleNotes(viewModel, it, onEditNote)
|
||||
HighlightsList(it, onEditNote)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -155,28 +145,31 @@ fun NotebookView(savedItemId: String, viewModel: NotebookViewModel) {
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun EditNoteModal() {
|
||||
val onBackPressedDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher
|
||||
val annotation = remember { mutableStateOf("") }
|
||||
fun EditNoteModal(initialValue: String?, onDismiss: (save: Boolean, text: String?) -> Unit) {
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val annotation = remember { mutableStateOf(initialValue ?: "") }
|
||||
|
||||
BottomSheetUI() {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
CenterAlignedTopAppBar(
|
||||
title = { Text("Note") },
|
||||
modifier = Modifier.statusBarsPadding(),
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.background
|
||||
),
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
onBackPressedDispatcher?.onBackPressed()
|
||||
TextButton(onClick = {
|
||||
onDismiss(false, initialValue)
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = androidx.compose.material.icons.Icons.Filled.ArrowBack,
|
||||
modifier = Modifier,
|
||||
contentDescription = "Back"
|
||||
)
|
||||
Text(text = "Cancel")
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
TextButton(onClick = {
|
||||
onDismiss(true, annotation.value)
|
||||
}) {
|
||||
Text(text = "Save")
|
||||
}
|
||||
}
|
||||
)
|
||||
@ -185,26 +178,30 @@ fun EditNoteModal() {
|
||||
TextField(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.focusRequester(focusRequester)
|
||||
.fillMaxSize(),
|
||||
value = annotation.value, onValueChange = { annotation.value = it }
|
||||
value = annotation.value, onValueChange = { annotation.value = it },
|
||||
colors = TextFieldDefaults.textFieldColors(
|
||||
textColor = Color.White
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun ArticleNotes(item: SavedItemWithLabelsAndHighlights) {
|
||||
fun ArticleNotes(viewModel: NotebookViewModel, item: SavedItemWithLabelsAndHighlights, onEditNote: (note: Highlight?) -> Unit) {
|
||||
val notes = item.highlights?.filter { it.type == "NOTE" } ?: listOf()
|
||||
val showDialog = remember { mutableStateOf(false) }
|
||||
val modalBottomSheetState = rememberModalBottomSheetState(
|
||||
ModalBottomSheetValue.Expanded,
|
||||
)
|
||||
val annotation = remember { mutableStateOf("") }
|
||||
|
||||
Column(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 15.dp)
|
||||
.padding(top = 20.dp, bottom = 50.dp)
|
||||
) {
|
||||
Text("Article Notes")
|
||||
Divider(modifier = Modifier.padding(bottom= 15.dp))
|
||||
@ -214,14 +211,13 @@ fun ArticleNotes(item: SavedItemWithLabelsAndHighlights) {
|
||||
fontSize = 14.sp,
|
||||
style = TextStyle(lineHeight = 18.sp),
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
onClick = { onEditNote(note) }
|
||||
)
|
||||
}
|
||||
if (notes.isEmpty()) {
|
||||
Button(
|
||||
onClick = {
|
||||
// viewModelScope.launch {
|
||||
// datastoreRepo.clearValue(DatastoreKeys.omnivorePendingUserToken)
|
||||
// }
|
||||
onEditNote(null)
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(0.dp, end = 15.dp)
|
||||
@ -232,12 +228,12 @@ fun ArticleNotes(item: SavedItemWithLabelsAndHighlights) {
|
||||
containerColor = MaterialTheme.colorScheme.surfaceVariant
|
||||
)
|
||||
) {
|
||||
// Text(
|
||||
// text = "Add Notes...",
|
||||
// style = androidx.compose.material.MaterialTheme.typography.subtitle2,
|
||||
// modifier = Modifier
|
||||
// .padding(vertical = 2.dp, horizontal = 0.dp),
|
||||
// )
|
||||
Text(
|
||||
text = "Add Notes...",
|
||||
style = androidx.compose.material.MaterialTheme.typography.subtitle2,
|
||||
modifier = Modifier
|
||||
.padding(vertical = 2.dp, horizontal = 0.dp),
|
||||
)
|
||||
Spacer(Modifier.weight(1f))
|
||||
}
|
||||
}
|
||||
@ -246,7 +242,7 @@ fun ArticleNotes(item: SavedItemWithLabelsAndHighlights) {
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun HighlightsList(item: SavedItemWithLabelsAndHighlights) {
|
||||
fun HighlightsList(item: SavedItemWithLabelsAndHighlights, onEditNote: (note: Highlight?) -> Unit) {
|
||||
val highlights = item.highlights?.filter { it.type == "HIGHLIGHT" } ?: listOf()
|
||||
val yellowColor = colorResource(R.color.cta_yellow)
|
||||
|
||||
@ -263,98 +259,107 @@ fun HighlightsList(item: SavedItemWithLabelsAndHighlights) {
|
||||
Text("Highlights")
|
||||
Divider(modifier = Modifier.padding(bottom= 10.dp))
|
||||
highlights.forEach { highlight ->
|
||||
var isMenuOpen by remember { mutableStateOf(false) }
|
||||
var isMenuOpen by remember { mutableStateOf(false) }
|
||||
|
||||
Row(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.align(Alignment.End)
|
||||
.padding(0.dp)
|
||||
) {
|
||||
Spacer(Modifier.weight(1f))
|
||||
Box {
|
||||
IconButton(onClick = { isMenuOpen = true }) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.MoreVert,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
if (isMenuOpen) {
|
||||
DropdownMenu(
|
||||
expanded = isMenuOpen,
|
||||
onDismissRequest = { isMenuOpen = false }
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = { Text("Copy") },
|
||||
onClick = {
|
||||
val clip = ClipData.newPlainText("highlight", highlight.quote)
|
||||
clipboard?.let {
|
||||
clipboard?.setPrimaryClip(clip)
|
||||
} ?: run {
|
||||
coroutineScope.launch {
|
||||
snackBarHostState
|
||||
.showSnackbar("Highlight copied")
|
||||
}
|
||||
}
|
||||
isMenuOpen = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
highlight.quote?.let {
|
||||
Row(modifier = Modifier
|
||||
.padding(start = 2.dp, end = 15.dp)
|
||||
.fillMaxWidth()
|
||||
.drawWithCache {
|
||||
onDrawWithContent {
|
||||
// draw behind the content the vertical line on the left
|
||||
drawLine(
|
||||
color = yellowColor,
|
||||
start = Offset.Zero,
|
||||
end = Offset(0f, this.size.height),
|
||||
strokeWidth = 10f
|
||||
)
|
||||
|
||||
// draw the content
|
||||
drawContent()
|
||||
}
|
||||
}) {
|
||||
|
||||
MarkdownText(
|
||||
modifier = Modifier
|
||||
.padding(start = 15.dp, end = 15.dp),
|
||||
markdown = it,
|
||||
fontSize = 14.sp,
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
Row(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.align(Alignment.End)
|
||||
.padding(0.dp)
|
||||
) {
|
||||
Spacer(Modifier.weight(1f))
|
||||
Box {
|
||||
IconButton(onClick = { isMenuOpen = true }) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.MoreVert,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
if (isMenuOpen) {
|
||||
DropdownMenu(
|
||||
expanded = isMenuOpen,
|
||||
onDismissRequest = { isMenuOpen = false }
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = { Text("Copy") },
|
||||
onClick = {
|
||||
val clip = ClipData.newPlainText("highlight", highlight.quote)
|
||||
clipboard?.let {
|
||||
clipboard?.setPrimaryClip(clip)
|
||||
} ?: run {
|
||||
coroutineScope.launch {
|
||||
snackBarHostState
|
||||
.showSnackbar("Highlight copied")
|
||||
}
|
||||
}
|
||||
isMenuOpen = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
highlight.annotation?.let {
|
||||
}
|
||||
|
||||
highlight.quote?.let {
|
||||
Row(modifier = Modifier
|
||||
.padding(start = 2.dp, end = 15.dp)
|
||||
.fillMaxWidth()
|
||||
.drawWithCache {
|
||||
onDrawWithContent {
|
||||
// draw behind the content the vertical line on the left
|
||||
drawLine(
|
||||
color = yellowColor,
|
||||
start = Offset.Zero,
|
||||
end = Offset(0f, this.size.height),
|
||||
strokeWidth = 10f
|
||||
)
|
||||
|
||||
// draw the content
|
||||
drawContent()
|
||||
}
|
||||
}) {
|
||||
|
||||
MarkdownText(
|
||||
// modifier = Modifier.padding(paddingValues),
|
||||
modifier = Modifier
|
||||
.padding(start = 15.dp, end = 15.dp),
|
||||
markdown = it,
|
||||
fontSize = 14.sp,
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
)
|
||||
} ?: run {
|
||||
// Surface(
|
||||
// modifier = Modifier
|
||||
// .padding(0.dp, end = 15.dp, top = 15.dp, bottom = 30.dp)
|
||||
// .fillMaxWidth(),
|
||||
// shape = androidx.compose.material.MaterialTheme.shapes.medium,
|
||||
// color = MaterialTheme.colorScheme.surfaceVariant
|
||||
// ) {
|
||||
// Row {
|
||||
// Text(
|
||||
// text = "Add Notes...",
|
||||
// style = androidx.compose.material.MaterialTheme.typography.subtitle2,
|
||||
// modifier = Modifier.padding(vertical = 10.dp, horizontal = 10.dp)
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
)
|
||||
}
|
||||
}
|
||||
highlight.annotation?.let {
|
||||
MarkdownText(
|
||||
// modifier = Modifier.padding(paddingValues),
|
||||
markdown = it,
|
||||
fontSize = 14.sp,
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
onClick = { onEditNote(highlight) },
|
||||
modifier = Modifier
|
||||
.padding(top = 15.dp),
|
||||
)
|
||||
} ?: run {
|
||||
Button(
|
||||
onClick = {
|
||||
onEditNote(highlight)
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(0.dp, top = 15.dp, end = 15.dp)
|
||||
.fillMaxWidth(),
|
||||
shape = androidx.compose.material.MaterialTheme.shapes.medium,
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
containerColor = MaterialTheme.colorScheme.surfaceVariant
|
||||
)
|
||||
) {
|
||||
Text(
|
||||
text = "Add Note...",
|
||||
style = androidx.compose.material.MaterialTheme.typography.subtitle2,
|
||||
modifier = Modifier
|
||||
.padding(vertical = 2.dp, horizontal = 0.dp),
|
||||
)
|
||||
Spacer(Modifier.weight(1f))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (highlights.isEmpty()) {
|
||||
Text(
|
||||
@ -366,18 +371,25 @@ fun HighlightsList(item: SavedItemWithLabelsAndHighlights) {
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
private fun BottomSheetUI(content: @Composable () -> Unit) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.wrapContentHeight()
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(topEnd = 20.dp, topStart = 20.dp))
|
||||
.background(Color.White)
|
||||
.statusBarsPadding()
|
||||
.padding(top = 20.dp)
|
||||
) {
|
||||
content()
|
||||
fun BottomSheetUI(content: @Composable () -> Unit) {
|
||||
OmnivoreTheme() {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.wrapContentHeight()
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(topEnd = 20.dp, topStart = 20.dp))
|
||||
.background(Color.Transparent)
|
||||
.statusBarsPadding()
|
||||
) {
|
||||
Scaffold(
|
||||
) { paddingValues ->
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,13 +1,23 @@
|
||||
package app.omnivore.omnivore.ui.notebook
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.*
|
||||
import androidx.room.Query
|
||||
import app.omnivore.omnivore.DatastoreRepository
|
||||
import app.omnivore.omnivore.dataService.DataService
|
||||
import app.omnivore.omnivore.dataService.createNoteHighlight
|
||||
import app.omnivore.omnivore.dataService.updateWebHighlight
|
||||
import app.omnivore.omnivore.graphql.generated.type.UpdateHighlightInput
|
||||
import app.omnivore.omnivore.models.ServerSyncStatus
|
||||
import app.omnivore.omnivore.networking.Networker
|
||||
import app.omnivore.omnivore.networking.updateHighlight
|
||||
import app.omnivore.omnivore.persistence.entities.Highlight
|
||||
import app.omnivore.omnivore.persistence.entities.SavedItemWithLabelsAndHighlights
|
||||
import app.omnivore.omnivore.ui.library.SavedItemViewModel
|
||||
import com.apollographql.apollo3.api.Optional
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
@ -16,8 +26,39 @@ class NotebookViewModel @Inject constructor(
|
||||
private val dataService: DataService,
|
||||
private val datastoreRepo: DatastoreRepository
|
||||
): ViewModel() {
|
||||
var highlightUnderEdit: Highlight? = null
|
||||
|
||||
fun getLibraryItemById(savedItemId: String): LiveData<SavedItemWithLabelsAndHighlights> {
|
||||
return dataService.db.savedItemDao().getLibraryItemById(savedItemId)
|
||||
}
|
||||
|
||||
suspend fun addArticleNote(savedItemId: String, note: String) {
|
||||
withContext(Dispatchers.IO) {
|
||||
val item = dataService.db.savedItemDao().getById(savedItemId)
|
||||
item?.let { item ->
|
||||
val noteHighlight = item.highlights.firstOrNull { it.type == "NOTE" }
|
||||
noteHighlight?.let {
|
||||
dataService.db.highlightDao()
|
||||
.updateNote(highlightId = noteHighlight.highlightId, note = note)
|
||||
|
||||
networker.updateHighlight(input = UpdateHighlightInput(
|
||||
highlightId = noteHighlight.highlightId,
|
||||
annotation = Optional.presentIfNotNull(note),
|
||||
))
|
||||
} ?: run {
|
||||
dataService.createNoteHighlight(savedItemId, note)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun updateHighlightNote(highlightId: String, note: String?) {
|
||||
withContext(Dispatchers.IO) {
|
||||
dataService.db.highlightDao().updateNote(highlightId, note ?: "")
|
||||
networker.updateHighlight(input = UpdateHighlightInput(
|
||||
highlightId = highlightId,
|
||||
annotation = Optional.presentIfNotNull(note),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,37 +1,28 @@
|
||||
package app.omnivore.omnivore.ui.reader
|
||||
|
||||
import android.content.ClipData
|
||||
import android.R
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.foundation.background
|
||||
import android.view.WindowManager
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import app.omnivore.omnivore.ui.notebook.ArticleNotes
|
||||
import app.omnivore.omnivore.ui.notebook.HighlightsList
|
||||
import app.omnivore.omnivore.ui.notebook.notebookMD
|
||||
import app.omnivore.omnivore.ui.notebook.EditNoteModal
|
||||
import app.omnivore.omnivore.ui.theme.OmnivoreTheme
|
||||
import kotlinx.coroutines.launch
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
|
||||
|
||||
class AnnotationEditFragment : DialogFragment() {
|
||||
private var onSave: (String) -> Unit = {}
|
||||
@ -48,24 +39,38 @@ class AnnotationEditFragment : DialogFragment() {
|
||||
this.onCancel = onCancel
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
|
||||
return ComposeView(requireContext()).apply {
|
||||
// Dispose of the Composition when the view's LifecycleOwner
|
||||
// is destroyed
|
||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||
|
||||
(dialog as? BottomSheetDialog)?.let {
|
||||
it.behavior.skipCollapsed = true
|
||||
it.behavior.state = STATE_EXPANDED
|
||||
}
|
||||
|
||||
dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN)
|
||||
|
||||
|
||||
|
||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||
setContent {
|
||||
OmnivoreTheme {
|
||||
AnnotationEditView(
|
||||
initialAnnotation,
|
||||
onSave,
|
||||
onCancel,
|
||||
// dismissAction = { dismiss() }
|
||||
)
|
||||
}
|
||||
val annotation = remember { mutableStateOf(initialAnnotation ?: "") }
|
||||
|
||||
OmnivoreTheme {
|
||||
EditNoteModal(initialValue = initialAnnotation, onDismiss = { save, text ->
|
||||
if (save) {
|
||||
onSave(text ?: "")
|
||||
} else {
|
||||
onCancel()
|
||||
}
|
||||
dismissNow()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -75,117 +80,3 @@ class AnnotationEditFragment : DialogFragment() {
|
||||
super.onDismiss(dialog)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AnnotationEditView(
|
||||
initialAnnotation: String,
|
||||
onSave: (String) -> Unit,
|
||||
onCancel: () -> Unit,
|
||||
) {
|
||||
val annotation = remember { mutableStateOf(initialAnnotation) }
|
||||
val focusRequester = FocusRequester()
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Notebook") },
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.background
|
||||
),
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
onCancel()
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ArrowBack,
|
||||
modifier = Modifier,
|
||||
contentDescription = "Back",
|
||||
)
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
onSave(annotation.value)
|
||||
}
|
||||
) {
|
||||
Text("Save")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
TextField(
|
||||
value = annotation.value,
|
||||
onValueChange = { annotation.value = it },
|
||||
modifier = Modifier
|
||||
.focusRequester(focusRequester)
|
||||
.fillMaxSize()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// Column(
|
||||
// modifier = Modifier.padding(16.dp),
|
||||
// horizontalAlignment = Alignment.CenterHorizontally,
|
||||
// ) {
|
||||
// Row {
|
||||
// TextButton(
|
||||
// onClick = {
|
||||
// onCancel()
|
||||
// dismissAction()
|
||||
// }
|
||||
// ) {
|
||||
// Text("Cancel")
|
||||
// }
|
||||
//
|
||||
// Spacer(modifier = Modifier.weight(1.0F))
|
||||
//
|
||||
// Text(text = "Note")
|
||||
//
|
||||
// Spacer(modifier = Modifier.weight(1.0F))
|
||||
//
|
||||
// TextButton(
|
||||
// onClick = {
|
||||
// onSave(annotation.value)
|
||||
// dismissAction()
|
||||
// }
|
||||
// ) {
|
||||
// Text("Save")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Spacer(modifier = Modifier.height(8.dp))
|
||||
//
|
||||
//
|
||||
//
|
||||
// Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
//
|
||||
// LaunchedEffect(Unit) {
|
||||
// focusRequester.requestFocus()
|
||||
// }
|
||||
|
||||
// Row {
|
||||
// Spacer(modifier = Modifier.weight(0.1F))
|
||||
// TextField(
|
||||
// value = annotation.value,
|
||||
// onValueChange = { annotation.value = it },
|
||||
// modifier = Modifier
|
||||
// .width(IntrinsicSize.Max)
|
||||
// .height(IntrinsicSize.Max)
|
||||
// .weight(1.0F)
|
||||
// )
|
||||
// Spacer(modifier = Modifier.weight(0.1F))
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // }
|
||||
//}
|
||||
|
||||
@ -4,22 +4,19 @@ import android.annotation.SuppressLint
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.graphics.Rect
|
||||
import android.util.Log
|
||||
import android.view.*
|
||||
import android.view.View.OnScrollChangeListener
|
||||
import android.view.ViewTreeObserver.OnScrollChangedListener
|
||||
import android.webkit.JavascriptInterface
|
||||
import android.webkit.WebResourceRequest
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import app.omnivore.omnivore.R
|
||||
import com.google.gson.Gson
|
||||
@ -32,7 +29,8 @@ import java.util.*
|
||||
@Composable
|
||||
fun WebReader(
|
||||
styledContent: String,
|
||||
webReaderViewModel: WebReaderViewModel
|
||||
webReaderViewModel: WebReaderViewModel,
|
||||
currentTheme: Themes?
|
||||
) {
|
||||
val javascriptActionLoopUUID: UUID by webReaderViewModel
|
||||
.javascriptActionLoopUUIDLiveData
|
||||
@ -55,15 +53,13 @@ fun WebReader(
|
||||
settings.allowFileAccess = true
|
||||
settings.domStorageEnabled = true
|
||||
|
||||
alpha = 0.0f
|
||||
alpha = 1.0f
|
||||
viewModel?.showNavBar()
|
||||
currentTheme?.let { theme ->
|
||||
setBackgroundColor(theme.backgroundColor.toInt());
|
||||
}
|
||||
|
||||
webViewClient = object : WebViewClient() {
|
||||
override fun onPageFinished(view: WebView?, url: String?) {
|
||||
super.onPageFinished(view, url)
|
||||
viewModel?.showNavBar()
|
||||
view?.animate()?.alpha(1.0f)?.duration = 200
|
||||
}
|
||||
|
||||
override fun shouldOverrideUrlLoading(
|
||||
view: WebView?,
|
||||
request: WebResourceRequest?
|
||||
|
||||
@ -3,7 +3,6 @@ package app.omnivore.omnivore.ui.reader
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
|
||||
import androidx.activity.compose.setContent
|
||||
@ -15,11 +14,8 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material.icons.filled.Home
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
@ -48,7 +44,7 @@ import kotlinx.coroutines.launch
|
||||
import kotlin.math.roundToInt
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
import app.omnivore.omnivore.ui.notebook.EditNoteModal
|
||||
|
||||
@AndroidEntryPoint
|
||||
class WebReaderLoadingContainerActivity: ComponentActivity() {
|
||||
@ -115,6 +111,7 @@ enum class BottomSheetState(
|
||||
NONE(),
|
||||
PREFERENCES(),
|
||||
NOTEBOOK(),
|
||||
EDITNOTE(),
|
||||
HIGHLIGHTNOTE(),
|
||||
LABELS(),
|
||||
LINK()
|
||||
@ -127,13 +124,14 @@ fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null,
|
||||
onLibraryIconTap: (() -> Unit)? = null,
|
||||
webReaderViewModel: WebReaderViewModel,
|
||||
notebookViewModel: NotebookViewModel) {
|
||||
val currentThemeKey = webReaderViewModel.currentThemeKey.observeAsState()
|
||||
val currentTheme = Themes.values().find { it.themeKey == currentThemeKey.value }
|
||||
val onBackPressedDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher
|
||||
val bottomSheetState: BottomSheetState? by webReaderViewModel.bottomSheetStateLiveData.observeAsState(BottomSheetState.NONE)
|
||||
|
||||
val webReaderParams: WebReaderParams? by webReaderViewModel.webReaderParamsLiveData.observeAsState(null)
|
||||
val shouldPopView: Boolean by webReaderViewModel.shouldPopViewLiveData.observeAsState(false)
|
||||
|
||||
|
||||
val labels: List<SavedItemLabel> by webReaderViewModel.savedItemLabelsLiveData.observeAsState(listOf())
|
||||
|
||||
val maxToolbarHeight = 48.dp
|
||||
@ -151,9 +149,11 @@ fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null,
|
||||
webReaderContent.styledContent()
|
||||
} ?: null
|
||||
|
||||
|
||||
val modalBottomSheetState = rememberModalBottomSheetState(
|
||||
initialValue = ModalBottomSheetValue.Hidden,
|
||||
confirmStateChange = {
|
||||
skipHalfExpanded = bottomSheetState == BottomSheetState.EDITNOTE || bottomSheetState == BottomSheetState.HIGHLIGHTNOTE,
|
||||
confirmValueChange = {
|
||||
if (it == ModalBottomSheetValue.Hidden) {
|
||||
webReaderViewModel.resetBottomSheet()
|
||||
}
|
||||
@ -161,6 +161,11 @@ fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null,
|
||||
}
|
||||
)
|
||||
|
||||
val showMenu = {
|
||||
coroutineScope.launch {
|
||||
modalBottomSheetState.show()
|
||||
}
|
||||
}
|
||||
|
||||
when (bottomSheetState) {
|
||||
BottomSheetState.PREFERENCES -> {
|
||||
@ -170,30 +175,10 @@ fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null,
|
||||
}
|
||||
}
|
||||
}
|
||||
BottomSheetState.NOTEBOOK -> {
|
||||
coroutineScope.launch {
|
||||
modalBottomSheetState.show()
|
||||
}
|
||||
}
|
||||
BottomSheetState.HIGHLIGHTNOTE -> {
|
||||
coroutineScope.launch {
|
||||
modalBottomSheetState.show()
|
||||
}
|
||||
}
|
||||
BottomSheetState.LABELS -> {
|
||||
coroutineScope.launch {
|
||||
modalBottomSheetState.show()
|
||||
}
|
||||
}
|
||||
BottomSheetState.LINK -> {
|
||||
coroutineScope.launch {
|
||||
modalBottomSheetState.show()
|
||||
}
|
||||
}
|
||||
BottomSheetState.NONE -> {
|
||||
coroutineScope.launch {
|
||||
modalBottomSheetState.hide()
|
||||
}
|
||||
BottomSheetState.NOTEBOOK, BottomSheetState.EDITNOTE,
|
||||
BottomSheetState.HIGHLIGHTNOTE, BottomSheetState.LABELS,
|
||||
BottomSheetState.LINK, -> {
|
||||
showMenu()
|
||||
}
|
||||
else -> {
|
||||
coroutineScope.launch {
|
||||
@ -217,31 +202,53 @@ fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null,
|
||||
BottomSheetState.NOTEBOOK -> {
|
||||
webReaderParams?.let { params ->
|
||||
BottomSheetUI(title = "Notebook") {
|
||||
NotebookView(savedItemId = params.item.savedItemId, viewModel = notebookViewModel)
|
||||
NotebookView(savedItemId = params.item.savedItemId, viewModel = notebookViewModel, onEditNote = {
|
||||
notebookViewModel.highlightUnderEdit = it
|
||||
webReaderViewModel.setBottomSheet(BottomSheetState.EDITNOTE)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
BottomSheetState.HIGHLIGHTNOTE -> {
|
||||
webReaderViewModel.annotation?.let { annotation ->
|
||||
BottomSheetUI(title = "Note") {
|
||||
AnnotationEditView(
|
||||
initialAnnotation = annotation,
|
||||
onSave = {
|
||||
webReaderViewModel.saveAnnotation(it)
|
||||
coroutineScope.launch {
|
||||
webReaderViewModel.resetBottomSheet()
|
||||
}
|
||||
},
|
||||
onCancel = {
|
||||
webReaderViewModel.cancelAnnotationEdit()
|
||||
coroutineScope.launch {
|
||||
webReaderViewModel.resetBottomSheet()
|
||||
BottomSheetState.EDITNOTE -> {
|
||||
webReaderParams?.let { params ->
|
||||
EditNoteModal(
|
||||
initialValue = notebookViewModel.highlightUnderEdit?.annotation,
|
||||
onDismiss = { save, note ->
|
||||
coroutineScope.launch {
|
||||
if (save) {
|
||||
notebookViewModel.highlightUnderEdit?.let { highlight ->
|
||||
notebookViewModel.updateHighlightNote(highlight.highlightId, note)
|
||||
} ?: run {
|
||||
if (note != null) {
|
||||
notebookViewModel.addArticleNote(
|
||||
savedItemId = params.item.savedItemId,
|
||||
note = note
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
notebookViewModel.highlightUnderEdit = null
|
||||
}
|
||||
)
|
||||
}
|
||||
webReaderViewModel.setBottomSheet(BottomSheetState.NOTEBOOK)
|
||||
})
|
||||
}
|
||||
}
|
||||
BottomSheetState.HIGHLIGHTNOTE -> {
|
||||
EditNoteModal(
|
||||
initialValue = webReaderViewModel.annotation,
|
||||
onDismiss = { save, note ->
|
||||
coroutineScope.launch {
|
||||
if (save) {
|
||||
webReaderViewModel.saveAnnotation(note ?: "")
|
||||
} else {
|
||||
webReaderViewModel.cancelAnnotation()
|
||||
}
|
||||
webReaderViewModel.annotation = null
|
||||
}
|
||||
webReaderViewModel.resetBottomSheet()
|
||||
}
|
||||
)
|
||||
}
|
||||
BottomSheetState.LABELS -> {
|
||||
BottomSheetUI(title = "Notebook") {
|
||||
LabelsSelectionSheetContent(
|
||||
@ -291,7 +298,8 @@ fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null,
|
||||
if (styledContent != null) {
|
||||
WebReader(
|
||||
styledContent = styledContent,
|
||||
webReaderViewModel = webReaderViewModel
|
||||
webReaderViewModel = webReaderViewModel,
|
||||
currentTheme = currentTheme,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -319,7 +319,18 @@ class WebReaderViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
fun saveAnnotation(annotation: String) {
|
||||
val script = "var event = new Event('saveAnnotation');event.annotation = '$annotation';document.dispatchEvent(event);"
|
||||
val jsonAnnotation = Gson().toJson(annotation)
|
||||
val script = "var event = new Event('saveAnnotation');event.annotation = $jsonAnnotation;document.dispatchEvent(event);"
|
||||
|
||||
Log.d("loggo", script)
|
||||
|
||||
enqueueScript(script)
|
||||
cancelAnnotationEdit()
|
||||
}
|
||||
|
||||
fun cancelAnnotation() {
|
||||
val script = "var event = new Event('dismissHighlight');document.dispatchEvent(event);"
|
||||
|
||||
enqueueScript(script)
|
||||
cancelAnnotationEdit()
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<FrameLayout
|
||||
android:id="@+id/fragmentContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<com.pspdfkit.ui.PdfThumbnailBar
|
||||
android:id="@+id/thumbnailBar"
|
||||
|
||||
Reference in New Issue
Block a user