mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Merge pull request #712 from omnivore-app/fix/ios-pdf-handling
Better PDF upload handling on iOS
This commit is contained in:
commit
987336f438
36 changed files with 17013 additions and 16839 deletions
|
|
@ -59,11 +59,17 @@ final class ShareExtensionViewModel: ObservableObject {
|
|||
return
|
||||
}
|
||||
|
||||
let backgroundTask = UIApplication.shared.beginBackgroundTask(withName: requestId)
|
||||
let saveLinkPublisher: AnyPublisher<Void, SaveArticleError> = {
|
||||
if pageScrapePayload.contentType == .pdf {
|
||||
return services.dataService.uploadPDFPublisher(pageScrapePayload: pageScrapePayload, requestId: requestId)
|
||||
} else if pageScrapePayload.html != nil {
|
||||
return services.dataService.savePagePublisher(pageScrapePayload: pageScrapePayload, requestId: requestId)
|
||||
if case let .pdf(data) = pageScrapePayload.contentType {
|
||||
return services.dataService.uploadPDFPublisher(pageScrapePayload: pageScrapePayload,
|
||||
data: data,
|
||||
requestId: requestId)
|
||||
} else if case let .html(html, title) = pageScrapePayload.contentType {
|
||||
return services.dataService.savePagePublisher(pageScrapePayload: pageScrapePayload,
|
||||
html: html,
|
||||
title: title,
|
||||
requestId: requestId)
|
||||
} else {
|
||||
return services.dataService.saveUrlPublisher(pageScrapePayload: pageScrapePayload, requestId: requestId)
|
||||
}
|
||||
|
|
@ -74,8 +80,10 @@ final class ShareExtensionViewModel: ObservableObject {
|
|||
guard case let .failure(error) = completion else { return }
|
||||
self?.debugText = "saveArticleError: \(error)"
|
||||
self?.status = .failed(error: error)
|
||||
UIApplication.shared.endBackgroundTask(backgroundTask)
|
||||
} receiveValue: { [weak self] _ in
|
||||
self?.status = .success
|
||||
UIApplication.shared.endBackgroundTask(backgroundTask)
|
||||
}
|
||||
.store(in: &subscriptions)
|
||||
|
||||
|
|
|
|||
|
|
@ -9,28 +9,34 @@ import UniformTypeIdentifiers
|
|||
let URLREGEX = #"[(http(s)?):\/\/(www\.)?a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)"#
|
||||
|
||||
public struct PageScrapePayload {
|
||||
public enum ContentType {
|
||||
case html
|
||||
case pdf
|
||||
public struct HTMLPayload {
|
||||
let url: String
|
||||
let title: String?
|
||||
let html: String
|
||||
}
|
||||
|
||||
public enum ContentType {
|
||||
case none
|
||||
case html(html: String, title: String?)
|
||||
case pdf(data: Data)
|
||||
}
|
||||
|
||||
public let title: String?
|
||||
public let html: String?
|
||||
public let url: String
|
||||
public let contentType: ContentType
|
||||
|
||||
init(url: String, title: String?, html: String?, contentType: String?) {
|
||||
init(url: String) {
|
||||
self.url = url
|
||||
self.title = title
|
||||
self.html = html
|
||||
self.contentType = .none
|
||||
}
|
||||
|
||||
// If the content type was specified and we know its PDF, use that
|
||||
// otherwise fallback to using file extensions.
|
||||
if let contentType = contentType, contentType.contains("pdf") {
|
||||
self.contentType = .pdf
|
||||
} else {
|
||||
self.contentType = url.hasSuffix(".pdf") ? .pdf : .html
|
||||
}
|
||||
init(url: String, pdfData: Data) {
|
||||
self.url = url
|
||||
self.contentType = .pdf(data: pdfData)
|
||||
}
|
||||
|
||||
init(url: String, title: String?, html: String) {
|
||||
self.url = url
|
||||
self.contentType = .html(html: html, title: title)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -52,10 +58,25 @@ public enum PageScraper {
|
|||
}
|
||||
|
||||
var pageScrapePayload: PageScrapePayload?
|
||||
let PDFKey = UTType.pdf.identifier
|
||||
let publicFileKey = UTType.fileURL.identifier
|
||||
let propertyListKey = UTType.propertyList.identifier
|
||||
|
||||
let group = DispatchGroup()
|
||||
|
||||
for attachment in attachments where attachment.hasItemConformingToTypeIdentifier(PDFKey) {
|
||||
group.enter()
|
||||
attachment.loadItem(
|
||||
forTypeIdentifier: PDFKey,
|
||||
options: nil
|
||||
) { item, _ in
|
||||
if let payload = PageScrapePayload.make(item: item) {
|
||||
pageScrapePayload = payload
|
||||
}
|
||||
group.leave()
|
||||
}
|
||||
}
|
||||
|
||||
for attachment in attachments where attachment.hasItemConformingToTypeIdentifier(propertyListKey) {
|
||||
group.enter()
|
||||
attachment.loadItem(
|
||||
|
|
@ -214,17 +235,55 @@ public enum PageScraper {
|
|||
private extension PageScrapePayload {
|
||||
static func make(url: URL?) -> PageScrapePayload? {
|
||||
guard let url = url else { return nil }
|
||||
return PageScrapePayload(url: url.absoluteString, title: nil, html: nil, contentType: nil)
|
||||
return PageScrapePayload(url: url.absoluteString)
|
||||
}
|
||||
|
||||
static func make(item: NSSecureCoding?) -> PageScrapePayload? {
|
||||
let dictionary = item as? NSDictionary
|
||||
let results = dictionary?[NSExtensionJavaScriptPreprocessingResultsKey] as? NSDictionary
|
||||
if let dictionary = item as? NSDictionary {
|
||||
return makeFromDictionary(dictionary)
|
||||
}
|
||||
if let url = item as? NSURL {
|
||||
return makeFromURL(url as URL)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
static func makeFromURL(_ url: URL) -> PageScrapePayload? {
|
||||
if url.isFileURL {
|
||||
let type = try? url.resourceValues(forKeys: [.typeIdentifierKey]).typeIdentifier
|
||||
if type == UTType.pdf.identifier, let data = try? Data(contentsOf: url) {
|
||||
return PageScrapePayload(url: url.absoluteString, pdfData: data)
|
||||
}
|
||||
// Don't try to handle file URLs that are not PDFs.
|
||||
// In the future we can add image and other file type support here
|
||||
return nil
|
||||
}
|
||||
return PageScrapePayload(url: url.absoluteString)
|
||||
}
|
||||
|
||||
static func makeFromDictionary(_ dictionary: NSDictionary) -> PageScrapePayload? {
|
||||
let results = dictionary[NSExtensionJavaScriptPreprocessingResultsKey] as? NSDictionary
|
||||
guard let url = results?["url"] as? String else { return nil }
|
||||
let html = results?["documentHTML"] as? String
|
||||
let title = results?["title"] as? String
|
||||
let contentType = results?["contentType"] as? String
|
||||
|
||||
return PageScrapePayload(url: url, title: title, html: html, contentType: contentType)
|
||||
// If we were not able to capture any HTML, treat this as a URL and
|
||||
// see if the backend can do better.
|
||||
if html == nil || html!.isEmpty {
|
||||
return PageScrapePayload(url: url)
|
||||
}
|
||||
|
||||
// If its a PDF that we opened through Safari we don't have access to the
|
||||
// file content, so pass the URL to the backend and let it download it.
|
||||
if contentType == "application/pdf" {
|
||||
return PageScrapePayload(url: url)
|
||||
}
|
||||
|
||||
if let html = html {
|
||||
return PageScrapePayload(url: url, title: title, html: html)
|
||||
}
|
||||
|
||||
return PageScrapePayload(url: url)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -24,16 +24,16 @@ extension DataService {
|
|||
|
||||
let selection = Selection<MutationResult, Unions.ArchiveLinkResult> {
|
||||
try $0.on(
|
||||
archiveLinkError: .init { .error(errorCode: try $0.errorCodes().first ?? .badRequest) },
|
||||
archiveLinkSuccess: .init { .success(linkId: try $0.linkId()) }
|
||||
archiveLinkSuccess: .init { .success(linkId: try $0.linkId()) },
|
||||
archiveLinkError: .init { .error(errorCode: try $0.errorCodes().first ?? .badRequest) }
|
||||
)
|
||||
}
|
||||
|
||||
let mutation = Selection.Mutation {
|
||||
try $0.setLinkArchived(
|
||||
input: InputObjects.ArchiveLinkInput(
|
||||
archived: archived,
|
||||
linkId: itemID
|
||||
linkId: itemID,
|
||||
archived: archived
|
||||
),
|
||||
selection: selection
|
||||
)
|
||||
|
|
|
|||
|
|
@ -40,22 +40,22 @@ extension DataService {
|
|||
|
||||
let selection = Selection<MutationResult, Unions.CreateHighlightResult> {
|
||||
try $0.on(
|
||||
createHighlightError: .init { .error(errorCode: try $0.errorCodes().first ?? .badData) },
|
||||
createHighlightSuccess: .init {
|
||||
.saved(highlight: try $0.highlight(selection: highlightSelection))
|
||||
}
|
||||
},
|
||||
createHighlightError: .init { .error(errorCode: try $0.errorCodes().first ?? .badData) }
|
||||
)
|
||||
}
|
||||
|
||||
let mutation = Selection.Mutation {
|
||||
try $0.createHighlight(
|
||||
input: InputObjects.CreateHighlightInput(
|
||||
annotation: OptionalArgument(highlight.annotation),
|
||||
articleId: articleId,
|
||||
id: highlight.id,
|
||||
shortId: highlight.shortId,
|
||||
articleId: articleId,
|
||||
patch: highlight.patch,
|
||||
quote: highlight.quote,
|
||||
shortId: highlight.shortId
|
||||
annotation: OptionalArgument(highlight.annotation)
|
||||
),
|
||||
selection: selection
|
||||
)
|
||||
|
|
|
|||
|
|
@ -34,17 +34,17 @@ extension DataService {
|
|||
|
||||
let selection = Selection<MutationResult, Unions.CreateLabelResult> {
|
||||
try $0.on(
|
||||
createLabelError: .init { .error(errorCode: try $0.errorCodes().first ?? .badRequest) },
|
||||
createLabelSuccess: .init { .saved(label: try $0.label(selection: feedItemLabelSelection)) }
|
||||
createLabelSuccess: .init { .saved(label: try $0.label(selection: feedItemLabelSelection)) },
|
||||
createLabelError: .init { .error(errorCode: try $0.errorCodes().first ?? .badRequest) }
|
||||
)
|
||||
}
|
||||
|
||||
let mutation = Selection.Mutation {
|
||||
try $0.createLabel(
|
||||
input: InputObjects.CreateLabelInput(
|
||||
name: label.name,
|
||||
color: label.color,
|
||||
description: OptionalArgument(label.labelDescription),
|
||||
name: label.name
|
||||
description: OptionalArgument(label.labelDescription)
|
||||
),
|
||||
selection: selection
|
||||
)
|
||||
|
|
|
|||
|
|
@ -12,9 +12,6 @@ public extension DataService {
|
|||
|
||||
let selection = Selection<MutationResult, Unions.CreateNewsletterEmailResult> {
|
||||
try $0.on(
|
||||
createNewsletterEmailError: .init {
|
||||
.error(errorCode: try $0.errorCodes().first ?? .badRequest)
|
||||
},
|
||||
createNewsletterEmailSuccess: .init {
|
||||
.saved(newsletterEmail: try $0.newsletterEmail(selection: Selection.NewsletterEmail {
|
||||
InternalNewsletterEmail(
|
||||
|
|
@ -23,6 +20,9 @@ public extension DataService {
|
|||
confirmationCode: try $0.confirmationCode()
|
||||
)
|
||||
}))
|
||||
},
|
||||
createNewsletterEmailError: .init {
|
||||
.error(errorCode: try $0.errorCodes().first ?? .badRequest)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,21 +37,21 @@ public extension DataService {
|
|||
|
||||
let selection = Selection<MutationResult, Unions.CreateReminderResult> {
|
||||
try $0.on(
|
||||
createReminderError: .init { .error(errorCode: try $0.errorCodes().first ?? .badRequest) },
|
||||
createReminderSuccess: .init {
|
||||
.complete(id: try $0.reminder(selection: Selection.Reminder { try $0.id() }))
|
||||
}
|
||||
},
|
||||
createReminderError: .init { .error(errorCode: try $0.errorCodes().first ?? .badRequest) }
|
||||
)
|
||||
}
|
||||
|
||||
let mutation = Selection.Mutation {
|
||||
try $0.createReminder(
|
||||
input: InputObjects.CreateReminderInput(
|
||||
archiveUntil: true,
|
||||
clientRequestId: OptionalArgument(reminderItemId.clientRequestId),
|
||||
linkId: OptionalArgument(reminderItemId.linkId),
|
||||
remindAt: DateTime(from: remindAt),
|
||||
sendNotification: true
|
||||
clientRequestId: OptionalArgument(reminderItemId.clientRequestId),
|
||||
archiveUntil: true,
|
||||
sendNotification: true,
|
||||
remindAt: DateTime(from: remindAt)
|
||||
),
|
||||
selection: selection
|
||||
)
|
||||
|
|
|
|||
|
|
@ -30,10 +30,10 @@ public extension DataService {
|
|||
|
||||
let selection = Selection<MutationResult, Unions.DeleteHighlightResult> {
|
||||
try $0.on(
|
||||
deleteHighlightError: .init { .error(errorCode: try $0.errorCodes().first ?? .unauthorized) },
|
||||
deleteHighlightSuccess: .init {
|
||||
.saved(id: try $0.highlight(selection: Selection.Highlight { try $0.id() }))
|
||||
}
|
||||
},
|
||||
deleteHighlightError: .init { .error(errorCode: try $0.errorCodes().first ?? .unauthorized) }
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ public extension DataService {
|
|||
|
||||
let selection = Selection<MutationResult, Unions.UnsubscribeResult> {
|
||||
try $0.on(
|
||||
unsubscribeError: .init { .error(errorMessage: (try $0.errorCodes().first ?? .unauthorized).rawValue) },
|
||||
unsubscribeSuccess: .init { .success(id: try $0.subscription(selection: Selection.Subscription { try $0.id() })) }
|
||||
unsubscribeSuccess: .init { .success(id: try $0.subscription(selection: Selection.Subscription { try $0.id() })) },
|
||||
unsubscribeError: .init { .error(errorMessage: (try $0.errorCodes().first ?? .unauthorized).rawValue) }
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,10 +34,10 @@ public extension DataService {
|
|||
|
||||
let selection = Selection<MutationResult, Unions.SetDeviceTokenResult> {
|
||||
try $0.on(
|
||||
setDeviceTokenError: .init { .error(errorCode: try $0.errorCodes().first ?? .badRequest) },
|
||||
setDeviceTokenSuccess: .init {
|
||||
.saved(id: try $0.deviceToken(selection: Selection.DeviceToken { try $0.id() }))
|
||||
}
|
||||
},
|
||||
setDeviceTokenError: .init { .error(errorCode: try $0.errorCodes().first ?? .badRequest) }
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,25 +46,25 @@ extension DataService {
|
|||
|
||||
let selection = Selection<MutationResult, Unions.MergeHighlightResult> {
|
||||
try $0.on(
|
||||
mergeHighlightError: .init { .error(errorCode: try $0.errorCodes().first ?? .badData) },
|
||||
mergeHighlightSuccess: .init {
|
||||
.saved(highlight: try $0.highlight(selection: highlightSelection))
|
||||
}
|
||||
},
|
||||
mergeHighlightError: .init { .error(errorCode: try $0.errorCodes().first ?? .badData) }
|
||||
)
|
||||
}
|
||||
|
||||
let mutation = Selection.Mutation {
|
||||
try $0.mergeHighlight(
|
||||
input: InputObjects.MergeHighlightInput(
|
||||
annotation: .absent(),
|
||||
articleId: articleId,
|
||||
id: highlight.id,
|
||||
overlapHighlightIdList: overlapHighlightIdList,
|
||||
patch: highlight.patch,
|
||||
prefix: .absent(),
|
||||
quote: highlight.quote,
|
||||
shortId: highlight.shortId,
|
||||
suffix: .absent()
|
||||
articleId: articleId,
|
||||
patch: highlight.patch,
|
||||
quote: highlight.quote,
|
||||
prefix: .absent(),
|
||||
suffix: .absent(),
|
||||
annotation: .absent(),
|
||||
overlapHighlightIdList: overlapHighlightIdList
|
||||
),
|
||||
selection: selection
|
||||
)
|
||||
|
|
|
|||
|
|
@ -23,10 +23,10 @@ extension DataService {
|
|||
|
||||
let selection = Selection<MutationResult, Unions.DeleteLabelResult> {
|
||||
try $0.on(
|
||||
deleteLabelError: .init { .error(errorCode: try $0.errorCodes().first ?? .badRequest) },
|
||||
deleteLabelSuccess: .init {
|
||||
.success(labelID: try $0.label(selection: Selection.Label { try $0.id() }))
|
||||
}
|
||||
},
|
||||
deleteLabelError: .init { .error(errorCode: try $0.errorCodes().first ?? .badRequest) }
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,14 +24,14 @@ extension DataService {
|
|||
|
||||
let selection = Selection<MutationResult, Unions.SetBookmarkArticleResult> {
|
||||
try $0.on(
|
||||
setBookmarkArticleError: .init { .error(errorCode: try $0.errorCodes().first ?? .notFound) },
|
||||
setBookmarkArticleSuccess: .init {
|
||||
.success(
|
||||
linkId: try $0.bookmarkedArticle(selection: Selection.Article {
|
||||
try $0.id()
|
||||
})
|
||||
)
|
||||
}
|
||||
},
|
||||
setBookmarkArticleError: .init { .error(errorCode: try $0.errorCodes().first ?? .notFound) }
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ public extension Networker {
|
|||
|
||||
let selection = Selection<QueryResult, Unions.ArticleSavingRequestResult> {
|
||||
try $0.on(
|
||||
articleSavingRequestError: .init { .error(errorCode: (try? $0.errorCodes().first) ?? .notFound) },
|
||||
articleSavingRequestSuccess: .init {
|
||||
.saved(
|
||||
status: try $0.articleSavingRequest(
|
||||
|
|
@ -41,7 +40,8 @@ public extension Networker {
|
|||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
articleSavingRequestError: .init { .error(errorCode: (try? $0.errorCodes().first) ?? .notFound) }
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -92,23 +92,25 @@ public extension DataService {
|
|||
}
|
||||
|
||||
let preparedDocument: InputObjects.PreparedDocumentInput? = {
|
||||
guard let html = pageScrapePayload.html, let title = pageScrapePayload.title else { return nil }
|
||||
return InputObjects.PreparedDocumentInput(
|
||||
document: html,
|
||||
pageInfo: InputObjects.PageInfoInput(title: OptionalArgument(title))
|
||||
)
|
||||
if case let .html(html, title) = pageScrapePayload.contentType {
|
||||
return InputObjects.PreparedDocumentInput(
|
||||
document: html,
|
||||
pageInfo: InputObjects.PageInfoInput(title: OptionalArgument(title))
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}()
|
||||
|
||||
let input = InputObjects.CreateArticleInput(
|
||||
url: pageScrapePayload.url,
|
||||
preparedDocument: OptionalArgument(preparedDocument),
|
||||
uploadFileId: uploadFileId != nil ? .present(uploadFileId!) : .null(),
|
||||
url: pageScrapePayload.url
|
||||
uploadFileId: uploadFileId != nil ? .present(uploadFileId!) : .null()
|
||||
)
|
||||
|
||||
let selection = Selection<MutationResult, Unions.CreateArticleResult> {
|
||||
try $0.on(
|
||||
createArticleError: .init { .error(errorCode: (try? $0.errorCodes().first) ?? .unableToParse) },
|
||||
createArticleSuccess: .init { .saved(created: try $0.created()) }
|
||||
createArticleSuccess: .init { .saved(created: try $0.created()) },
|
||||
createArticleError: .init { .error(errorCode: (try? $0.errorCodes().first) ?? .unableToParse) }
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -161,7 +163,6 @@ public extension DataService {
|
|||
|
||||
let selection = Selection<MutationResult, Unions.CreateArticleSavingRequestResult> {
|
||||
try $0.on(
|
||||
createArticleSavingRequestError: .init { .error(errorCode: (try? $0.errorCodes().first) ?? .badData) },
|
||||
createArticleSavingRequestSuccess: .init {
|
||||
.saved(
|
||||
status: try $0.articleSavingRequest(
|
||||
|
|
@ -173,7 +174,8 @@ public extension DataService {
|
|||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
createArticleSavingRequestError: .init { .error(errorCode: (try? $0.errorCodes().first) ?? .badData) }
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@ import SwiftGraphQL
|
|||
public extension DataService {
|
||||
func uploadPDFPublisher(
|
||||
pageScrapePayload: PageScrapePayload,
|
||||
data: Data,
|
||||
requestId: String
|
||||
) -> AnyPublisher<Void, SaveArticleError> {
|
||||
uploadFileRequestPublisher(pageScrapePayload: pageScrapePayload)
|
||||
.flatMap { self.uploadFilePublisher(fileUploadConfig: $0, fileURLString: pageScrapePayload.url) }
|
||||
uploadFileRequestPublisher(pageScrapePayload: pageScrapePayload, requestId: requestId)
|
||||
.flatMap { self.uploadFilePublisher(fileUploadConfig: $0, data: data) }
|
||||
.flatMap { self.saveFilePublisher(pageScrapePayload: pageScrapePayload, uploadFileId: $0, requestId: requestId) }
|
||||
.catch { _ in self.saveUrlPublisher(pageScrapePayload: pageScrapePayload, requestId: requestId) }
|
||||
.receive(on: DispatchQueue.main)
|
||||
|
|
@ -25,20 +26,21 @@ private struct UploadFileRequestPayload {
|
|||
|
||||
private extension DataService {
|
||||
// swiftlint:disable:next line_length
|
||||
func uploadFileRequestPublisher(pageScrapePayload: PageScrapePayload) -> AnyPublisher<UploadFileRequestPayload, SaveArticleError> {
|
||||
func uploadFileRequestPublisher(pageScrapePayload: PageScrapePayload, requestId: String?) -> AnyPublisher<UploadFileRequestPayload, SaveArticleError> {
|
||||
enum MutationResult {
|
||||
case success(payload: UploadFileRequestPayload)
|
||||
case error(errorCode: Enums.UploadFileRequestErrorCode?)
|
||||
}
|
||||
|
||||
let input = InputObjects.UploadFileRequestInput(
|
||||
url: pageScrapePayload.url,
|
||||
contentType: "application/pdf",
|
||||
url: pageScrapePayload.url
|
||||
createPageEntry: OptionalArgument(true),
|
||||
clientRequestId: OptionalArgument(requestId)
|
||||
)
|
||||
|
||||
let selection = Selection<MutationResult, Unions.UploadFileRequestResult> {
|
||||
try $0.on(
|
||||
uploadFileRequestError: .init { .error(errorCode: try? $0.errorCodes().first) },
|
||||
uploadFileRequestSuccess: .init {
|
||||
.success(
|
||||
payload: UploadFileRequestPayload(
|
||||
|
|
@ -47,7 +49,8 @@ private extension DataService {
|
|||
urlString: try $0.uploadSignedUrl()
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
uploadFileRequestError: .init { .error(errorCode: try? $0.errorCodes().first) }
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -88,16 +91,14 @@ private extension DataService {
|
|||
}
|
||||
|
||||
// swiftlint:disable:next line_length
|
||||
func uploadFilePublisher(fileUploadConfig: UploadFileRequestPayload, fileURLString: String) -> AnyPublisher<String, SaveArticleError> {
|
||||
let pdfData = URL(string: fileURLString).flatMap { try? Data(contentsOf: $0) }
|
||||
|
||||
func uploadFilePublisher(fileUploadConfig: UploadFileRequestPayload, data: Data) -> AnyPublisher<String, SaveArticleError> {
|
||||
let url = fileUploadConfig.urlString.flatMap { URL(string: $0) }
|
||||
guard let url = url, let pdfData = pdfData else { return Future { $0(.failure(.badData)) }.eraseToAnyPublisher() }
|
||||
guard let url = url else { return Future { $0(.failure(.badData)) }.eraseToAnyPublisher() }
|
||||
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "PUT"
|
||||
request.addValue("application/pdf", forHTTPHeaderField: "content-type")
|
||||
request.httpBody = pdfData
|
||||
request.httpBody = data
|
||||
|
||||
return networker.urlSession.dataTaskPublisher(for: request)
|
||||
.tryMap { data, response -> String in
|
||||
|
|
@ -132,16 +133,16 @@ private extension DataService {
|
|||
}
|
||||
|
||||
let input = InputObjects.SaveFileInput(
|
||||
clientRequestId: requestId,
|
||||
url: pageScrapePayload.url,
|
||||
source: "ios-file",
|
||||
uploadFileId: uploadFileId,
|
||||
url: pageScrapePayload.url
|
||||
clientRequestId: requestId,
|
||||
uploadFileId: uploadFileId
|
||||
)
|
||||
|
||||
let selection = Selection<MutationResult, Unions.SaveResult> {
|
||||
try $0.on(
|
||||
saveError: .init { .error(errorCode: (try? $0.errorCodes().first) ?? .unknown) },
|
||||
saveSuccess: .init { .saved(requestId: requestId, url: (try? $0.url()) ?? "") }
|
||||
saveSuccess: .init { .saved(requestId: requestId, url: (try? $0.url()) ?? "") },
|
||||
saveError: .init { .error(errorCode: (try? $0.errorCodes().first) ?? .unknown) }
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,24 +5,23 @@ import SwiftGraphQL
|
|||
|
||||
public extension DataService {
|
||||
// swiftlint:disable:next line_length
|
||||
func savePagePublisher(pageScrapePayload: PageScrapePayload, requestId: String) -> AnyPublisher<Void, SaveArticleError> {
|
||||
func savePagePublisher(pageScrapePayload: PageScrapePayload, html: String, title: String?, requestId: String) -> AnyPublisher<Void, SaveArticleError> {
|
||||
enum MutationResult {
|
||||
case saved(requestId: String, url: String)
|
||||
case error(errorCode: Enums.SaveErrorCode)
|
||||
}
|
||||
|
||||
let input = InputObjects.SavePageInput(
|
||||
clientRequestId: requestId,
|
||||
originalContent: pageScrapePayload.html ?? "",
|
||||
source: "ios-page",
|
||||
title: OptionalArgument(pageScrapePayload.title),
|
||||
url: pageScrapePayload.url
|
||||
url: requestId,
|
||||
source: html,
|
||||
clientRequestId: "ios-page",
|
||||
title: OptionalArgument(title),
|
||||
originalContent: pageScrapePayload.url
|
||||
)
|
||||
|
||||
let selection = Selection<MutationResult, Unions.SaveResult> {
|
||||
try $0.on(
|
||||
saveError: .init { .error(errorCode: (try? $0.errorCodes().first) ?? .unknown) },
|
||||
saveSuccess: .init { .saved(requestId: requestId, url: (try? $0.url()) ?? "") }
|
||||
saveSuccess: .init { .saved(requestId: requestId, url: (try? $0.url()) ?? "") }, saveError: .init { .error(errorCode: (try? $0.errorCodes().first) ?? .unknown) }
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,15 +12,15 @@ public extension DataService {
|
|||
}
|
||||
|
||||
let input = InputObjects.SaveUrlInput(
|
||||
clientRequestId: requestId,
|
||||
url: pageScrapePayload.url,
|
||||
source: "ios-url",
|
||||
url: pageScrapePayload.url
|
||||
clientRequestId: requestId
|
||||
)
|
||||
|
||||
let selection = Selection<MutationResult, Unions.SaveResult> {
|
||||
try $0.on(
|
||||
saveError: .init { .error(errorCode: (try? $0.errorCodes().first) ?? .unknown) },
|
||||
saveSuccess: .init { .saved(requestId: requestId, url: (try? $0.url()) ?? "") }
|
||||
saveSuccess: .init { .saved(requestId: requestId, url: (try? $0.url()) ?? "") },
|
||||
saveError: .init { .error(errorCode: (try? $0.errorCodes().first) ?? .unknown) }
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,16 +32,16 @@ extension DataService {
|
|||
|
||||
let selection = Selection<MutationResult, Unions.SetLabelsResult> {
|
||||
try $0.on(
|
||||
setLabelsError: .init { .error(errorCode: try $0.errorCodes().first ?? .badRequest) },
|
||||
setLabelsSuccess: .init { .saved(feedItem: try $0.labels(selection: feedItemLabelSelection.list)) }
|
||||
setLabelsSuccess: .init { .saved(feedItem: try $0.labels(selection: feedItemLabelSelection.list)) },
|
||||
setLabelsError: .init { .error(errorCode: try $0.errorCodes().first ?? .badRequest) }
|
||||
)
|
||||
}
|
||||
|
||||
let mutation = Selection.Mutation {
|
||||
try $0.setLabels(
|
||||
input: InputObjects.SetLabelsInput(
|
||||
labelIds: labelIDs,
|
||||
pageId: itemID
|
||||
pageId: itemID,
|
||||
labelIds: labelIDs
|
||||
),
|
||||
selection: selection
|
||||
)
|
||||
|
|
|
|||
|
|
@ -33,12 +33,12 @@ extension DataService {
|
|||
|
||||
let selection = Selection<MutationResult, Unions.SaveArticleReadingProgressResult> {
|
||||
try $0.on(
|
||||
saveArticleReadingProgressError: .init { .error(errorCode: try $0.errorCodes().first ?? .badData) },
|
||||
saveArticleReadingProgressSuccess: .init {
|
||||
.saved(
|
||||
readingProgress: try $0.updatedArticle(selection: Selection.Article { try $0.readingProgressPercent() })
|
||||
)
|
||||
}
|
||||
},
|
||||
saveArticleReadingProgressError: .init { .error(errorCode: try $0.errorCodes().first ?? .badData) }
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -46,8 +46,8 @@ extension DataService {
|
|||
try $0.saveArticleReadingProgress(
|
||||
input: InputObjects.SaveArticleReadingProgressInput(
|
||||
id: itemID,
|
||||
readingProgressAnchorIndex: anchorIndex,
|
||||
readingProgressPercent: readingProgress
|
||||
readingProgressPercent: readingProgress,
|
||||
readingProgressAnchorIndex: anchorIndex
|
||||
),
|
||||
selection: selection
|
||||
)
|
||||
|
|
|
|||
|
|
@ -31,18 +31,18 @@ extension DataService {
|
|||
|
||||
let selection = Selection<MutationResult, Unions.UpdateHighlightResult> {
|
||||
try $0.on(
|
||||
updateHighlightError: .init { .error(errorCode: try $0.errorCodes().first ?? .badData) },
|
||||
updateHighlightSuccess: .init {
|
||||
.saved(highlight: try $0.highlight(selection: highlightSelection))
|
||||
}
|
||||
},
|
||||
updateHighlightError: .init { .error(errorCode: try $0.errorCodes().first ?? .badData) }
|
||||
)
|
||||
}
|
||||
|
||||
let mutation = Selection.Mutation {
|
||||
try $0.updateHighlight(
|
||||
input: InputObjects.UpdateHighlightInput(
|
||||
annotation: OptionalArgument(annotation),
|
||||
highlightId: highlightID,
|
||||
annotation: OptionalArgument(annotation),
|
||||
sharedAt: OptionalArgument(nil)
|
||||
),
|
||||
selection: selection
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ extension DataService {
|
|||
case error(error: String)
|
||||
}
|
||||
|
||||
let articleSelection = Selection.Article {
|
||||
let articleContentSelection = Selection.Article {
|
||||
ArticleProps(
|
||||
item: InternalLinkedItem(
|
||||
id: try $0.id(),
|
||||
|
|
@ -121,18 +121,17 @@ extension DataService {
|
|||
|
||||
let selection = Selection<QueryResult, Unions.ArticleResult> {
|
||||
try $0.on(
|
||||
articleSuccess: .init {
|
||||
QueryResult.success(result: try $0.article(selection: articleContentSelection))
|
||||
},
|
||||
articleError: .init {
|
||||
QueryResult.error(error: try $0.errorCodes().description)
|
||||
},
|
||||
articleSuccess: .init {
|
||||
QueryResult.success(result: try $0.article(selection: articleSelection))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
let query = Selection.Query {
|
||||
// backend has a hack that allows us to pass in itemID in place of slug
|
||||
try $0.article(slug: itemID, username: username, selection: selection)
|
||||
try $0.article(username: username, slug: itemID, selection: selection)
|
||||
}
|
||||
|
||||
let path = appEnvironment.graphqlPath
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@ public extension DataService {
|
|||
|
||||
let selection = Selection<QueryResult, Unions.LabelsResult> {
|
||||
try $0.on(
|
||||
labelsError: .init {
|
||||
QueryResult.error(error: try $0.errorCodes().description)
|
||||
},
|
||||
labelsSuccess: .init {
|
||||
QueryResult.success(result: try $0.labels(selection: feedItemLabelSelection.list))
|
||||
},
|
||||
labelsError: .init {
|
||||
QueryResult.error(error: try $0.errorCodes().description)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,9 +24,6 @@ public extension DataService {
|
|||
|
||||
let selection = Selection<QueryResult, Unions.ArticlesResult> {
|
||||
try $0.on(
|
||||
articlesError: .init {
|
||||
QueryResult.error(error: try $0.errorCodes().description)
|
||||
},
|
||||
articlesSuccess: .init {
|
||||
QueryResult.success(
|
||||
result: InternalHomeFeedData(
|
||||
|
|
@ -36,22 +33,25 @@ public extension DataService {
|
|||
})
|
||||
)
|
||||
)
|
||||
},
|
||||
articlesError: .init {
|
||||
QueryResult.error(error: try $0.errorCodes().description)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
let query = Selection.Query {
|
||||
try $0.articles(
|
||||
after: OptionalArgument(cursor),
|
||||
first: OptionalArgument(limit),
|
||||
includePending: OptionalArgument(true),
|
||||
query: OptionalArgument(searchQuery),
|
||||
sharedOnly: .present(false),
|
||||
sort: OptionalArgument(
|
||||
InputObjects.SortParams(
|
||||
by: .updatedTime, order: .present(.descending)
|
||||
order: .present(.descending), by: .updatedTime
|
||||
)
|
||||
),
|
||||
after: OptionalArgument(cursor),
|
||||
first: OptionalArgument(limit),
|
||||
query: OptionalArgument(searchQuery),
|
||||
includePending: OptionalArgument(true),
|
||||
selection: selection
|
||||
)
|
||||
}
|
||||
|
|
@ -90,20 +90,44 @@ public extension DataService {
|
|||
case error(error: String)
|
||||
}
|
||||
|
||||
let articleSelection = Selection.Article {
|
||||
InternalLinkedItem(
|
||||
id: try $0.id(),
|
||||
title: try $0.title(),
|
||||
createdAt: try $0.createdAt().value ?? Date(),
|
||||
savedAt: try $0.savedAt().value ?? Date(),
|
||||
readingProgress: try $0.readingProgressPercent(),
|
||||
readingProgressAnchor: try $0.readingProgressAnchorIndex(),
|
||||
imageURLString: try $0.image(),
|
||||
onDeviceImageURLString: nil,
|
||||
documentDirectoryPath: nil,
|
||||
pageURLString: try $0.url(),
|
||||
descriptionText: try $0.description(),
|
||||
publisherURLString: try $0.originalArticleUrl(),
|
||||
siteName: try $0.siteName(),
|
||||
author: try $0.author(),
|
||||
publishDate: try $0.publishedAt()?.value,
|
||||
slug: try $0.slug(),
|
||||
isArchived: try $0.isArchived(),
|
||||
contentReader: try $0.contentReader().rawValue,
|
||||
labels: try $0.labels(selection: feedItemLabelSelection.list.nullable) ?? []
|
||||
)
|
||||
}
|
||||
|
||||
let selection = Selection<QueryResult, Unions.ArticleResult> {
|
||||
try $0.on(
|
||||
articleError: .init {
|
||||
QueryResult.error(error: try $0.errorCodes().description)
|
||||
},
|
||||
articleSuccess: .init {
|
||||
QueryResult.success(result: try $0.article(selection: articleSelection))
|
||||
},
|
||||
articleError: .init {
|
||||
QueryResult.error(error: try $0.errorCodes().description)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
let query = Selection.Query {
|
||||
// backend has a hack that allows us to pass in itemID in place of slug
|
||||
try $0.article(slug: itemID, username: username, selection: selection)
|
||||
try $0.article(username: username, slug: itemID, selection: selection)
|
||||
}
|
||||
|
||||
let path = appEnvironment.graphqlPath
|
||||
|
|
@ -130,7 +154,7 @@ public extension DataService {
|
|||
}
|
||||
}
|
||||
|
||||
private let articleSelection = Selection.Article {
|
||||
private let libraryArticleSelection = Selection.Article {
|
||||
InternalLinkedItem(
|
||||
id: try $0.id(),
|
||||
title: try $0.title(),
|
||||
|
|
@ -155,5 +179,5 @@ private let articleSelection = Selection.Article {
|
|||
}
|
||||
|
||||
private let articleEdgeSelection = Selection.ArticleEdge {
|
||||
try $0.node(selection: articleSelection)
|
||||
try $0.node(selection: libraryArticleSelection)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,9 +20,6 @@ extension DataService {
|
|||
|
||||
let selection = Selection<QueryResult, Unions.SearchResult> {
|
||||
try $0.on(
|
||||
searchError: .init {
|
||||
QueryResult.error(error: try $0.errorCodes().description)
|
||||
},
|
||||
searchSuccess: .init {
|
||||
QueryResult.success(
|
||||
result: LinkedItemIDFetchResult(
|
||||
|
|
@ -32,6 +29,9 @@ extension DataService {
|
|||
})
|
||||
)
|
||||
)
|
||||
},
|
||||
searchError: .init {
|
||||
QueryResult.error(error: try $0.errorCodes().description)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,11 +20,11 @@ public extension DataService {
|
|||
|
||||
let selection = Selection<QueryResult, Unions.NewsletterEmailsResult> {
|
||||
try $0.on(
|
||||
newsletterEmailsError: .init {
|
||||
QueryResult.error(error: try $0.errorCodes().description)
|
||||
},
|
||||
newsletterEmailsSuccess: .init {
|
||||
QueryResult.success(result: try $0.newsletterEmails(selection: newsletterEmailSelection.list))
|
||||
},
|
||||
newsletterEmailsError: .init {
|
||||
QueryResult.error(error: try $0.errorCodes().description)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,11 +26,11 @@ public extension DataService {
|
|||
|
||||
let selection = Selection<QueryResult, Unions.SubscriptionsResult> {
|
||||
try $0.on(
|
||||
subscriptionsError: .init {
|
||||
QueryResult.error(error: try $0.errorCodes().description)
|
||||
},
|
||||
subscriptionsSuccess: .init {
|
||||
QueryResult.success(result: try $0.subscriptions(selection: subsciptionSelection.list))
|
||||
},
|
||||
subscriptionsError: .init {
|
||||
QueryResult.error(error: try $0.errorCodes().description)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2057,7 +2057,9 @@ export enum UploadFileRequestErrorCode {
|
|||
}
|
||||
|
||||
export type UploadFileRequestInput = {
|
||||
clientRequestId?: InputMaybe<Scalars['String']>;
|
||||
contentType: Scalars['String'];
|
||||
createPageEntry?: InputMaybe<Scalars['Boolean']>;
|
||||
url: Scalars['String'];
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1581,7 +1581,9 @@ enum UploadFileRequestErrorCode {
|
|||
}
|
||||
|
||||
input UploadFileRequestInput {
|
||||
clientRequestId: String
|
||||
contentType: String!
|
||||
createPageEntry: Boolean
|
||||
url: String!
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ import {
|
|||
stringToHash,
|
||||
userDataToUser,
|
||||
validatedDate,
|
||||
titleForFilePath,
|
||||
} from '../../utils/helpers'
|
||||
import {
|
||||
ParsedContentPuppeteer,
|
||||
|
|
@ -166,6 +167,7 @@ export const createArticleResolver = authorized<
|
|||
.join('.')
|
||||
).replace(/_/gi, ' ')
|
||||
|
||||
let title: string | undefined
|
||||
let parsedContent: Readability.ParseResult | null = null
|
||||
let canonicalUrl
|
||||
let userArticleUrl: string | null = null
|
||||
|
|
@ -219,6 +221,7 @@ export const createArticleResolver = authorized<
|
|||
userArticleUrl = uploadFileDetails.fileUrl
|
||||
canonicalUrl = uploadFile.url
|
||||
pageType = PageType.File
|
||||
title = titleForFilePath(uploadFile.url)
|
||||
} else if (
|
||||
source !== 'puppeteer-parse' &&
|
||||
FORCE_PUPPETEER_URLS.some((regex) => regex.test(url))
|
||||
|
|
@ -252,6 +255,7 @@ export const createArticleResolver = authorized<
|
|||
content: parsedContent?.content || '',
|
||||
description: parsedContent?.excerpt || '',
|
||||
title:
|
||||
title ||
|
||||
parsedContent?.title ||
|
||||
preparedDocument?.pageInfo.title ||
|
||||
croppedPathname,
|
||||
|
|
@ -333,7 +337,6 @@ export const createArticleResolver = authorized<
|
|||
} else {
|
||||
// update existing page's state from processing to succeeded
|
||||
articleToSave.archivedAt = archive ? saveTime : undefined
|
||||
articleToSave.url = uploadFileUrlOverride || articleToSave.url
|
||||
const updated = await updatePage(pageId, articleToSave, {
|
||||
...ctx,
|
||||
uid,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import path from 'path'
|
|||
import normalizeUrl from 'normalize-url'
|
||||
import { analytics } from '../../utils/analytics'
|
||||
import { env } from '../../env'
|
||||
import { createPage } from '../../elastic/pages'
|
||||
import { createPage, getPageByParam, updatePage } from '../../elastic/pages'
|
||||
import { PageType } from '../../elastic/types'
|
||||
import { generateSlug } from '../../utils/helpers'
|
||||
|
||||
|
|
@ -82,28 +82,44 @@ export const uploadFileRequestResolver: ResolverFn<
|
|||
input.contentType
|
||||
)
|
||||
|
||||
const pageId = await createPage(
|
||||
{
|
||||
id: '',
|
||||
url: input.url,
|
||||
if (input.createPageEntry) {
|
||||
const page = await getPageByParam({
|
||||
userId: claims.uid,
|
||||
title: title,
|
||||
hash: uploadFilePathName,
|
||||
content: '',
|
||||
pageType: PageType.File,
|
||||
uploadFileId: uploadFileData.id,
|
||||
slug: generateSlug(uploadFilePathName),
|
||||
createdAt: new Date(),
|
||||
savedAt: new Date(),
|
||||
readingProgressPercent: 0,
|
||||
readingProgressAnchorIndex: 0,
|
||||
state: ArticleSavingRequestStatus.Processing,
|
||||
},
|
||||
ctx
|
||||
)
|
||||
|
||||
if (!pageId) {
|
||||
return { errorCodes: [UploadFileRequestErrorCode.FailedCreate] }
|
||||
url: input.url,
|
||||
})
|
||||
if (page) {
|
||||
await updatePage(
|
||||
page.id,
|
||||
{
|
||||
savedAt: new Date(),
|
||||
archivedAt: null,
|
||||
},
|
||||
ctx
|
||||
)
|
||||
} else {
|
||||
const pageId = await createPage(
|
||||
{
|
||||
url: input.url,
|
||||
id: input.clientRequestId || '',
|
||||
userId: claims.uid,
|
||||
title: title,
|
||||
hash: uploadFilePathName,
|
||||
content: '',
|
||||
pageType: PageType.File,
|
||||
uploadFileId: uploadFileData.id,
|
||||
slug: generateSlug(uploadFilePathName),
|
||||
createdAt: new Date(),
|
||||
savedAt: new Date(),
|
||||
readingProgressPercent: 0,
|
||||
readingProgressAnchorIndex: 0,
|
||||
state: ArticleSavingRequestStatus.Processing,
|
||||
},
|
||||
ctx
|
||||
)
|
||||
if (!pageId) {
|
||||
return { errorCodes: [UploadFileRequestErrorCode.FailedCreate] }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { id: uploadFileData.id, uploadSignedUrl }
|
||||
|
|
|
|||
|
|
@ -416,7 +416,10 @@ const schema = gql`
|
|||
input UploadFileRequestInput {
|
||||
url: String!
|
||||
contentType: String!
|
||||
createPageEntry: Boolean
|
||||
clientRequestId: String
|
||||
}
|
||||
|
||||
enum UploadFileRequestErrorCode {
|
||||
UNAUTHORIZED
|
||||
BAD_INPUT
|
||||
|
|
|
|||
|
|
@ -83,13 +83,14 @@ export const createPageSaveRequest = async (
|
|||
priority = priority || (await getPriorityByRateLimit(userId))
|
||||
|
||||
// look for existing page
|
||||
url = normalizeUrl(url, {
|
||||
const normalizedUrl = normalizeUrl(url, {
|
||||
stripHash: true,
|
||||
stripWWW: false,
|
||||
})
|
||||
|
||||
let page = await getPageByParam({
|
||||
userId,
|
||||
url,
|
||||
url: normalizedUrl,
|
||||
})
|
||||
if (page) {
|
||||
console.log('Page already exists', page)
|
||||
|
|
|
|||
|
|
@ -2,17 +2,9 @@ import Knex from 'knex'
|
|||
import { PubsubClient } from '../datalayer/pubsub'
|
||||
import { UserData } from '../datalayer/user/model'
|
||||
import { homePageURL } from '../env'
|
||||
import {
|
||||
PageType,
|
||||
SaveErrorCode,
|
||||
SaveFileInput,
|
||||
SaveResult,
|
||||
} from '../generated/graphql'
|
||||
import { SaveErrorCode, SaveFileInput, SaveResult } from '../generated/graphql'
|
||||
import { DataModels } from '../resolvers/types'
|
||||
import { generateSlug } from '../utils/helpers'
|
||||
import { getStorageFileDetails, makeStorageFilePublic } from '../utils/uploads'
|
||||
import { createPage, getPageByParam, updatePage } from '../elastic/pages'
|
||||
import { ArticleSavingRequestStatus } from '../elastic/types'
|
||||
import { getStorageFileDetails } from '../utils/uploads'
|
||||
|
||||
type SaveContext = {
|
||||
pubsub: PubsubClient
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ import { Merge } from '../util'
|
|||
import { CreateArticlesSuccessPartial } from '../resolvers'
|
||||
import { ArticleSavingRequestStatus, Page } from '../elastic/types'
|
||||
import { updatePage } from '../elastic/pages'
|
||||
import path from 'path'
|
||||
import normalizeUrl from 'normalize-url'
|
||||
|
||||
interface InputObject {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
|
@ -230,3 +232,13 @@ export const validatedDate = (
|
|||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
export const titleForFilePath = (url: string): string => {
|
||||
try {
|
||||
const title = decodeURI(path.basename(new URL(url).pathname, '.pdf'))
|
||||
return title
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ const uploadToSignedUrl = async ({ id, uploadSignedUrl }, contentType, contentOb
|
|||
})
|
||||
};
|
||||
|
||||
const getUploadIdAndSignedUrl = async (userId, url) => {
|
||||
const getUploadIdAndSignedUrl = async (userId, url, articleSavingRequestId) => {
|
||||
const auth = await signToken({ uid: userId }, process.env.JWT_SECRET);
|
||||
const data = JSON.stringify({
|
||||
query: `mutation UploadFileRequest($input: UploadFileRequestInput!) {
|
||||
|
|
@ -112,6 +112,7 @@ const getUploadIdAndSignedUrl = async (userId, url) => {
|
|||
input: {
|
||||
url,
|
||||
contentType: 'application/pdf',
|
||||
clientRequestId: articleSavingRequestId,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -126,10 +127,10 @@ const getUploadIdAndSignedUrl = async (userId, url) => {
|
|||
return response.data.data.uploadFileRequest;
|
||||
};
|
||||
|
||||
const uploadPdf = async (url, userId) => {
|
||||
const uploadPdf = async (url, userId, articleSavingRequestId) => {
|
||||
validateUrlString(url);
|
||||
|
||||
const uploadResult = await getUploadIdAndSignedUrl(userId, url);
|
||||
const uploadResult = await getUploadIdAndSignedUrl(userId, url, articleSavingRequestId);
|
||||
await uploadToSignedUrl(uploadResult, 'application/pdf', url);
|
||||
return uploadResult.id;
|
||||
};
|
||||
|
|
@ -282,7 +283,7 @@ async function fetchContent(req, res) {
|
|||
|
||||
try {
|
||||
if (contentType === 'application/pdf') {
|
||||
const uploadedFileId = await uploadPdf(finalUrl, userId);
|
||||
const uploadedFileId = await uploadPdf(finalUrl, userId, articleSavingRequestId);
|
||||
const l = await saveUploadedPdf(userId, finalUrl, uploadedFileId, articleSavingRequestId);
|
||||
} else {
|
||||
if (!content || !title) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue