diff --git a/apple/Omnivore.xcodeproj/project.pbxproj b/apple/Omnivore.xcodeproj/project.pbxproj index b852d8c5e..8186a649d 100644 --- a/apple/Omnivore.xcodeproj/project.pbxproj +++ b/apple/Omnivore.xcodeproj/project.pbxproj @@ -1400,7 +1400,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 12.0; - MARKETING_VERSION = 1.30.0; + MARKETING_VERSION = 1.31.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = app.omnivore.app; @@ -1435,7 +1435,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 12.0; - MARKETING_VERSION = 1.30.0; + MARKETING_VERSION = 1.31.0; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = app.omnivore.app; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1490,7 +1490,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.30.0; + MARKETING_VERSION = 1.31.0; PRODUCT_BUNDLE_IDENTIFIER = app.omnivore.app; PRODUCT_NAME = Omnivore; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1831,7 +1831,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.30.0; + MARKETING_VERSION = 1.31.0; PRODUCT_BUNDLE_IDENTIFIER = app.omnivore.app; PRODUCT_NAME = Omnivore; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/apple/OmnivoreKit/Package.swift b/apple/OmnivoreKit/Package.swift index cc04187a2..afe997291 100644 --- a/apple/OmnivoreKit/Package.swift +++ b/apple/OmnivoreKit/Package.swift @@ -47,7 +47,7 @@ let package = Package( .target( name: "Utils", dependencies: [ - .product(name: "PostHog", package: "posthog-ios") + .product(name: "PostHog", package: "posthog-ios", condition: .when(platforms: [.iOS])) ], resources: [.process("Resources")] ), @@ -58,7 +58,7 @@ let package = Package( var appPackageDependencies: [Target.Dependency] { var deps: [Target.Dependency] = ["Views", "Services", "Models", "Utils"] // Comment out following line for macOS build - deps.append(.product(name: "PSPDFKit", package: "PSPDFKit-SP")) + deps.append(.product(name: "PSPDFKit", package: "PSPDFKit-SP", condition: .when(platforms: [.iOS]))) return deps } diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift b/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift index 8eeb2e4e6..37865d853 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift @@ -12,18 +12,15 @@ struct MacFeedCardNavigationLink: View { @ObservedObject var viewModel: HomeFeedViewModel var body: some View { - NavigationLink( - destination: LinkItemDetailView( + ZStack { + LibraryItemCard(item: item, viewer: dataService.currentViewer) + NavigationLink(destination: LinkItemDetailView( linkedItemObjectID: item.objectID, isPDF: item.isPDF - ), - tag: item.objectID, - selection: $viewModel.selectedLinkItem - ) { - LibraryItemCard(item: item, viewer: dataService.currentViewer) + ), label: { + EmptyView() + }).opacity(0) } - .opacity(0) - .buttonStyle(PlainButtonStyle()) .onAppear { Task { await viewModel.itemAppeared(item: item, dataService: dataService) } } diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/FilterSelectorView.swift b/apple/OmnivoreKit/Sources/App/Views/Home/FilterSelectorView.swift index 04ad57902..614a714cd 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/FilterSelectorView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/FilterSelectorView.swift @@ -3,7 +3,6 @@ import Introspect import Models import Services import SwiftUI -import UIKit import Views @MainActor final class FilterSelectorViewModel: NSObject, ObservableObject { @@ -44,9 +43,11 @@ struct FilterSelectorView: View { .listStyle(.plain) #endif } - .navigationBarTitle("Library") - .navigationBarTitleDisplayMode(.inline) - .navigationBarItems(trailing: doneButton) + #if os(iOS) + .navigationBarTitle("Library") + .navigationBarTitleDisplayMode(.inline) + .navigationBarItems(trailing: doneButton) + #endif } private var innerBody: some View { diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewMac.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewMac.swift index 78f2e1556..b04cd20af 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewMac.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewMac.swift @@ -15,6 +15,8 @@ import Views @State private var confirmationShown = false @State private var presentProfileSheet = false + @Namespace var mainNamespace + @ObservedObject var viewModel: HomeFeedViewModel func loadItems(isRefresh: Bool) { @@ -26,6 +28,95 @@ import Views } } + func menuItems(_ item: LinkedItem) -> some View { + Group { + Button( + action: { viewModel.itemUnderTitleEdit = item }, + label: { Label("Edit Info", systemImage: "info.circle") } + ) + Button( + action: { viewModel.itemUnderLabelEdit = item }, + label: { Label(item.labels?.count == 0 ? "Add Labels" : "Edit Labels", systemImage: "tag") } + ) + Button(action: { + withAnimation(.linear(duration: 0.4)) { + viewModel.setLinkArchived( + dataService: dataService, + objectID: item.objectID, + archived: !item.isArchived + ) + } + }, label: { + Label( + item.isArchived ? "Unarchive" : "Archive", + systemImage: item.isArchived ? "tray.and.arrow.down.fill" : "archivebox" + ) + }) + Button( + action: { + itemToRemove = item + confirmationShown = true + }, + label: { Label("Remove", systemImage: "trash") } + ) + } + } + + var refreshButton: some View { + Button( + action: { + loadItems(isRefresh: true) + }, + label: { Label("Refresh Feed", systemImage: "arrow.clockwise") } + ) + .disabled(viewModel.isLoading) + .opacity(viewModel.isLoading ? 0 : 1) + .overlay { + if viewModel.isLoading { + ProgressView() + .controlSize(.small) + } + } + } + + var itemsList: some View { + VStack { + MacSearchBar(searchTerm: $viewModel.searchTerm) + .padding(.leading, 10) + .padding(.trailing, 10) + .prefersDefaultFocus(false, in: mainNamespace) + + List { + ForEach(viewModel.items) { item in + MacFeedCardNavigationLink( + item: item, + viewModel: viewModel + ) + .listRowInsets(EdgeInsets()) + .contextMenu { + menuItems(item) + } + Divider().padding(5) + } + .prefersDefaultFocus(true, in: mainNamespace) + + if viewModel.isLoading { + LoadingSection() + } + } + .padding(0) + .listStyle(InsetListStyle()) + .navigationTitle("Library") + .onChange(of: viewModel.searchTerm) { _ in + loadItems(isRefresh: true) + } + .toolbar { + Spacer() + refreshButton + } + } + } + var body: some View { ZStack { if let linkRequest = viewModel.linkRequest { @@ -37,97 +128,7 @@ import Views EmptyView() } } - List { - Spacer(minLength: 10) - - ForEach(viewModel.items) { item in - MacFeedCardNavigationLink( - item: item, - viewModel: viewModel - ) - .listRowInsets(EdgeInsets()) - .contextMenu { - Button( - action: { viewModel.itemUnderTitleEdit = item }, - label: { Label("Edit Info", systemImage: "info.circle") } - ) - Button( - action: { viewModel.itemUnderLabelEdit = item }, - label: { Label(item.labels?.count == 0 ? "Add Labels" : "Edit Labels", systemImage: "tag") } - ) - Button(action: { - withAnimation(.linear(duration: 0.4)) { - viewModel.setLinkArchived( - dataService: dataService, - objectID: item.objectID, - archived: !item.isArchived - ) - } - }, label: { - Label( - item.isArchived ? "Unarchive" : "Archive", - systemImage: item.isArchived ? "tray.and.arrow.down.fill" : "archivebox" - ) - }) - Button( - action: { - itemToRemove = item - confirmationShown = true - }, - label: { Label("Remove", systemImage: "trash") } - ) - if FeatureFlag.enableSnooze { - Button { - viewModel.itemToSnoozeID = item.id - viewModel.snoozePresented = true - } label: { - Label { Text(LocalText.genericSnooze) } icon: { Image.moon } - } - } - } - Divider().padding(5) - } - - if viewModel.isLoading { - LoadingSection() - } - } - .listStyle(InsetListStyle()) - .navigationTitle("Library") - .searchable( - text: $viewModel.searchTerm, - placement: .toolbar - ) { - if viewModel.searchTerm.isEmpty { - Text(LocalText.inboxGeneric).searchCompletion("in:inbox ") - Text(LocalText.allGeneric).searchCompletion("in:all ") - Text(LocalText.archivedGeneric).searchCompletion("in:archive ") - Text(LocalText.filesGeneric).searchCompletion("type:file ") - } - } - .onChange(of: viewModel.searchTerm) { _ in - loadItems(isRefresh: true) - } - .onSubmit(of: .search) { - loadItems(isRefresh: true) - } - .toolbar { - ToolbarItem { - Button( - action: { - loadItems(isRefresh: true) - }, - label: { Label("Refresh Feed", systemImage: "arrow.clockwise") } - ) - .disabled(viewModel.isLoading) - .opacity(viewModel.isLoading ? 0 : 1) - .overlay { - if viewModel.isLoading { - ProgressView() - } - } - } - } + itemsList } .alert("Are you sure?", isPresented: $confirmationShown) { Button("Remove Link", role: .destructive) { @@ -160,6 +161,7 @@ import Views loadItems(isRefresh: true) } } + .focusScope(mainNamespace) .handlesExternalEvents(preferring: Set(["shareExtensionRequestID"]), allowing: Set(["*"])) .onOpenURL { url in viewModel.linkRequest = nil diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/LibraryAddLinkView.swift b/apple/OmnivoreKit/Sources/App/Views/Home/LibraryAddLinkView.swift index 1755f0c6d..7fb03a786 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/LibraryAddLinkView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/LibraryAddLinkView.swift @@ -1,81 +1,93 @@ -#if os(iOS) - import Introspect - import Models - import Services - import SwiftUI - import UIKit - import Views - @MainActor final class LibraryAddLinkViewModel: NSObject, ObservableObject { - @Published var isLoading = false - @Published var errorMessage: String = "" - @Published var showErrorMessage: Bool = false +import Introspect +import Models +import Services +import SwiftUI +import Views - func addLink(dataService: DataService, newLinkURL: String, dismiss: DismissAction) { - isLoading = true - Task { - if URL(string: newLinkURL) == nil { - error("Invalid link") +@MainActor final class LibraryAddLinkViewModel: NSObject, ObservableObject { + @Published var isLoading = false + @Published var errorMessage: String = "" + @Published var showErrorMessage: Bool = false + + func addLink(dataService: DataService, newLinkURL: String, dismiss: DismissAction) { + isLoading = true + Task { + if URL(string: newLinkURL) == nil { + error("Invalid link") + } else { + let result = try? await dataService.saveURL(id: UUID().uuidString, url: newLinkURL) + if result == nil { + error("Error adding link") } else { - let result = try? await dataService.saveURL(id: UUID().uuidString, url: newLinkURL) - if result == nil { - error("Error adding link") - } else { - dismiss() - } + dismiss() } - isLoading = false } - } - - func error(_ msg: String) { - errorMessage = msg - showErrorMessage = true isLoading = false } } - struct LibraryAddLinkView: View { - @StateObject var viewModel = LibraryAddLinkViewModel() + func error(_ msg: String) { + errorMessage = msg + showErrorMessage = true + isLoading = false + } +} - @State var newLinkURL: String = "" - @EnvironmentObject var dataService: DataService - @Environment(\.dismiss) private var dismiss +struct LibraryAddLinkView: View { + @StateObject var viewModel = LibraryAddLinkViewModel() - enum FocusField: Hashable { - case addLinkEditor - } + @State var newLinkURL: String = "" + @EnvironmentObject var dataService: DataService + @Environment(\.dismiss) private var dismiss - @FocusState private var focusedField: FocusField? + enum FocusField: Hashable { + case addLinkEditor + } - var body: some View { - innerBody - .navigationTitle("Add Link") - .navigationBarTitleDisplayMode(.inline) - .onAppear { - focusedField = .addLinkEditor - } - } + @FocusState private var focusedField: FocusField? - var innerBody: some View { - Form { - TextField("Add Link", text: $newLinkURL) - .keyboardType(.URL) - .autocorrectionDisabled(true) - .textFieldStyle(StandardTextFieldStyle()) - .focused($focusedField, equals: .addLinkEditor) - - Button(action: { - if let url = UIPasteboard.general.url { - newLinkURL = url.absoluteString - } else { - viewModel.error("No URL on pasteboard") - } - }, label: { - Text("Get from pasteboard") - }) - } + var body: some View { + innerBody .navigationTitle("Add Link") + #if os(iOS) + .navigationBarTitleDisplayMode(.inline) + #endif + .onAppear { + focusedField = .addLinkEditor + } + } + + var pasteboardString: String? { + #if os(iOS) + UIPasteboard.general.url?.absoluteString + #else + NSPasteboard.general.string(forType: NSPasteboard.PasteboardType.URL) + #endif + } + + var innerBody: some View { + Form { + TextField("Add Link", text: $newLinkURL) + #if os(iOS) + .keyboardType(.URL) + #endif + .autocorrectionDisabled(true) + .textFieldStyle(StandardTextFieldStyle()) + .focused($focusedField, equals: .addLinkEditor) + + Button(action: { + if let url = pasteboardString { + newLinkURL = url + } else { + viewModel.error("No URL on pasteboard") + } + }, label: { + Text("Get from pasteboard") + }) + } + .navigationTitle("Add Link") + #if os(iOS) .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .navigationBarLeading) { @@ -85,28 +97,28 @@ viewModel.isLoading ? AnyView(ProgressView()) : AnyView(addButton) } } - .alert(viewModel.errorMessage, - isPresented: $viewModel.showErrorMessage) { - Button(LocalText.genericOk, role: .cancel) { viewModel.showErrorMessage = false } - } - } - - var addButton: some View { - Button( - action: { - viewModel.addLink(dataService: dataService, newLinkURL: newLinkURL, dismiss: dismiss) - }, - label: { Text("Add").bold() } - ) - .disabled(viewModel.isLoading) - } - - var dismissButton: some View { - Button( - action: { dismiss() }, - label: { Text(LocalText.genericClose) } - ) - .disabled(viewModel.isLoading) + #endif + .alert(viewModel.errorMessage, + isPresented: $viewModel.showErrorMessage) { + Button(LocalText.genericOk, role: .cancel) { viewModel.showErrorMessage = false } } } -#endif + + var addButton: some View { + Button( + action: { + viewModel.addLink(dataService: dataService, newLinkURL: newLinkURL, dismiss: dismiss) + }, + label: { Text("Add").bold() } + ) + .disabled(viewModel.isLoading) + } + + var dismissButton: some View { + Button( + action: { dismiss() }, + label: { Text(LocalText.genericClose) } + ) + .disabled(viewModel.isLoading) + } +} diff --git a/apple/OmnivoreKit/Sources/App/Views/LibraryTabView.swift b/apple/OmnivoreKit/Sources/App/Views/LibraryTabView.swift index 9d3347b7a..90e5f9c2c 100644 --- a/apple/OmnivoreKit/Sources/App/Views/LibraryTabView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/LibraryTabView.swift @@ -43,14 +43,9 @@ struct LibraryTabView: View { ) var body: some View { - if #available(iOS 16.0, *) { - NavigationView { - HomeView(viewModel: libraryViewModel) - .navigationBarHidden(false) - } - } else { - // Fallback on earlier versions - EmptyView() + NavigationView { + HomeView(viewModel: libraryViewModel) + .navigationBarTitleDisplayMode(.inline) } } } diff --git a/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift b/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift index 83422da7f..70ef246e1 100644 --- a/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift @@ -89,10 +89,12 @@ struct LinkItemDetailView: View { pdfContainerView } else if let item = viewModel.item { WebReaderContainerView(item: item, pop: { dismiss() }) + #if os(iOS) .navigationBarHidden(true) .lazyPop(pop: { dismiss() }, isEnabled: $isEnabled) + #endif } } .task { diff --git a/apple/OmnivoreKit/Sources/App/Views/PrimaryContentView.swift b/apple/OmnivoreKit/Sources/App/Views/PrimaryContentView.swift index 483664bd0..e6ba752d3 100644 --- a/apple/OmnivoreKit/Sources/App/Views/PrimaryContentView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/PrimaryContentView.swift @@ -9,6 +9,8 @@ import Views PrimaryContentCategory.profile ] + @State var searchTerm: String = "" + public var body: some View { innerBody } @@ -18,9 +20,10 @@ import Views if UIDevice.isIPad { return AnyView(splitView) } else { - return AnyView(LibraryTabView()) - // .navigationViewStyle(.stack) - // .navigationBarTitleDisplayMode(.inline) + return AnyView( + LibraryTabView() + .navigationViewStyle(.stack) + ) } #else return AnyView(splitView) @@ -33,7 +36,6 @@ import Views PrimaryContentCategory.feed.destinationView Text(LocalText.navigationSelectLink) } - .accentColor(.appGrayTextContrast) } #endif @@ -90,6 +92,7 @@ import Views .sheet(isPresented: $addLinkPresented) { NavigationView { LibraryAddLinkView() + .navigationBarTitleDisplayMode(.inline) } } } diff --git a/apple/OmnivoreKit/Sources/App/Views/SlideAnimatedTransitioning.swift b/apple/OmnivoreKit/Sources/App/Views/SlideAnimatedTransitioning.swift index 164cae639..d2a9a6cfe 100644 --- a/apple/OmnivoreKit/Sources/App/Views/SlideAnimatedTransitioning.swift +++ b/apple/OmnivoreKit/Sources/App/Views/SlideAnimatedTransitioning.swift @@ -6,60 +6,64 @@ // Copyright © 2016 Warif Akhand Rishi. All rights reserved. // -import UIKit +#if os(iOS) -class SlideAnimatedTransitioning: NSObject {} + import UIKit -extension SlideAnimatedTransitioning: UIViewControllerAnimatedTransitioning { - func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { - let containerView = transitionContext.containerView - guard - let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from), - let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) - else { - return + class SlideAnimatedTransitioning: NSObject {} + + extension SlideAnimatedTransitioning: UIViewControllerAnimatedTransitioning { + func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { + let containerView = transitionContext.containerView + guard + let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from), + let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) + else { + return + } + + let width = containerView.frame.width + + var offsetLeft = fromVC.view?.frame + offsetLeft?.origin.x = width + + var offscreenRight = fromVC.view?.frame + offscreenRight?.origin.x = -width / 3.33 + + toVC.view?.frame = offscreenRight! + + fromVC.view?.layer.shadowRadius = 5.0 + fromVC.view?.layer.shadowOpacity = 1.0 + toVC.view?.layer.opacity = 0.9 + + transitionContext.containerView.addSubview(toVC.view) + transitionContext.containerView.addSubview(fromVC.view) + + UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0, options: .curveLinear, animations: { + toVC.view?.frame = (fromVC.view?.frame)! + fromVC.view?.frame = offsetLeft! + + toVC.view?.layer.opacity = 1.0 + fromVC.view?.layer.shadowOpacity = 0.1 + + }, completion: { _ in + if !transitionContext.transitionWasCancelled { + toVC.view?.layer.opacity = 1.0 + toVC.view?.layer.shadowOpacity = 0 + fromVC.view?.layer.opacity = 1.0 + fromVC.view?.layer.shadowOpacity = 0 + + fromVC.view.removeFromSuperview() + } + // when cancelling or completing the animation, ios simulator seems to sometimes flash black backgrounds during the animation. on devices, this doesn't seem to happen though. + // containerView.backgroundColor = [UIColor whiteColor]; + transitionContext.completeTransition(!transitionContext.transitionWasCancelled) + }) } - let width = containerView.frame.width - - var offsetLeft = fromVC.view?.frame - offsetLeft?.origin.x = width - - var offscreenRight = fromVC.view?.frame - offscreenRight?.origin.x = -width / 3.33 - - toVC.view?.frame = offscreenRight! - - fromVC.view?.layer.shadowRadius = 5.0 - fromVC.view?.layer.shadowOpacity = 1.0 - toVC.view?.layer.opacity = 0.9 - - transitionContext.containerView.addSubview(toVC.view) - transitionContext.containerView.addSubview(fromVC.view) - - UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0, options: .curveLinear, animations: { - toVC.view?.frame = (fromVC.view?.frame)! - fromVC.view?.frame = offsetLeft! - - toVC.view?.layer.opacity = 1.0 - fromVC.view?.layer.shadowOpacity = 0.1 - - }, completion: { _ in - if !transitionContext.transitionWasCancelled { - toVC.view?.layer.opacity = 1.0 - toVC.view?.layer.shadowOpacity = 0 - fromVC.view?.layer.opacity = 1.0 - fromVC.view?.layer.shadowOpacity = 0 - - fromVC.view.removeFromSuperview() - } - // when cancelling or completing the animation, ios simulator seems to sometimes flash black backgrounds during the animation. on devices, this doesn't seem to happen though. - // containerView.backgroundColor = [UIColor whiteColor]; - transitionContext.completeTransition(!transitionContext.transitionWasCancelled) - }) + func transitionDuration(using _: UIViewControllerContextTransitioning?) -> TimeInterval { + 0.3 + } } - func transitionDuration(using _: UIViewControllerContextTransitioning?) -> TimeInterval { - 0.3 - } -} +#endif diff --git a/apple/OmnivoreKit/Sources/App/Views/SwipeRightToPopViewController.swift b/apple/OmnivoreKit/Sources/App/Views/SwipeRightToPopViewController.swift index 3a2fb967b..48e9ca2c8 100644 --- a/apple/OmnivoreKit/Sources/App/Views/SwipeRightToPopViewController.swift +++ b/apple/OmnivoreKit/Sources/App/Views/SwipeRightToPopViewController.swift @@ -10,144 +10,146 @@ // Copyright © 2019 Joseph Hinkle. All rights reserved. // -import SwiftUI -private func < (lhs: T?, rhs: T?) -> Bool { - switch (lhs, rhs) { - case let (lll?, rrr?): - return lll < rrr - case (nil, _?): - return true - default: - return false - } -} +#if os(iOS) -private func > (lhs: T?, rhs: T?) -> Bool { - switch (lhs, rhs) { - case let (lll?, rrr?): - return lll > rrr - default: - return rhs < lhs - } -} - -class SwipeRightToPopViewController: UIHostingController, UINavigationControllerDelegate where Content: View { - fileprivate var pop: (() -> Void)? - fileprivate var lazyPopContent: LazyPop? - private var percentDrivenInteractiveTransition: UIPercentDrivenInteractiveTransition? - private var panGestureRecognizer: UIPanGestureRecognizer! - private var parentNavigationControllerToUse: UINavigationController? - private var gestureAdded = false - - override func viewDidLayoutSubviews() { - // You need to add gesture events after every subview layout to protect against weird edge cases - // One notable edgecase is if you are in a splitview in landscape. In this case, there will be - // no nav controller with 2 vcs, so our addGesture will fail. After rotating back to portrait, - // the splitview will combine into one view with the details pushed on top. So only then would - // would the addGesture find a parent nav controller with 2 view controllers. I don't know if - // there are other edge cases, but running addGesture on every viewDidLayoutSubviews seems safe. - addGesture() + import SwiftUI + private func < (lhs: T?, rhs: T?) -> Bool { + switch (lhs, rhs) { + case let (lll?, rrr?): + return lll < rrr + case (nil, _?): + return true + default: + return false + } } - public func addGesture() { - if !gestureAdded { - // attempt to find a parent navigationController - var currentVc: UIViewController = self - while true { - if currentVc.navigationController != nil, - currentVc.navigationController?.viewControllers.count > 1 - { - parentNavigationControllerToUse = currentVc.navigationController - break + private func > (lhs: T?, rhs: T?) -> Bool { + switch (lhs, rhs) { + case let (lll?, rrr?): + return lll > rrr + default: + return rhs < lhs + } + } + + class SwipeRightToPopViewController: UIHostingController, UINavigationControllerDelegate where Content: View { + fileprivate var pop: (() -> Void)? + fileprivate var lazyPopContent: LazyPop? + private var percentDrivenInteractiveTransition: UIPercentDrivenInteractiveTransition? + private var panGestureRecognizer: UIPanGestureRecognizer! + private var parentNavigationControllerToUse: UINavigationController? + private var gestureAdded = false + + override func viewDidLayoutSubviews() { + // You need to add gesture events after every subview layout to protect against weird edge cases + // One notable edgecase is if you are in a splitview in landscape. In this case, there will be + // no nav controller with 2 vcs, so our addGesture will fail. After rotating back to portrait, + // the splitview will combine into one view with the details pushed on top. So only then would + // would the addGesture find a parent nav controller with 2 view controllers. I don't know if + // there are other edge cases, but running addGesture on every viewDidLayoutSubviews seems safe. + addGesture() + } + + public func addGesture() { + if !gestureAdded { + // attempt to find a parent navigationController + var currentVc: UIViewController = self + while true { + if currentVc.navigationController != nil, + currentVc.navigationController?.viewControllers.count > 1 + { + parentNavigationControllerToUse = currentVc.navigationController + break + } + guard let parent = currentVc.parent else { + return + } + currentVc = parent } - guard let parent = currentVc.parent else { + guard parentNavigationControllerToUse?.viewControllers.count > 1 else { return } - currentVc = parent - } - guard parentNavigationControllerToUse?.viewControllers.count > 1 else { - return - } - panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(SwipeRightToPopViewController.handlePanGesture(_:))) - view.addGestureRecognizer(panGestureRecognizer) - gestureAdded = true + panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(SwipeRightToPopViewController.handlePanGesture(_:))) + view.addGestureRecognizer(panGestureRecognizer) + gestureAdded = true + } } - } - @objc func handlePanGesture(_ panGesture: UIPanGestureRecognizer) { - // if the parentNavigationControllerToUse has a width value, use that because it's more accurate. Otherwise use this view's width as a backup - let total = parentNavigationControllerToUse?.view.frame.width ?? view.frame.width - let percent = max(panGesture.translation(in: view).x, 0) / total + @objc func handlePanGesture(_ panGesture: UIPanGestureRecognizer) { + // if the parentNavigationControllerToUse has a width value, use that because it's more accurate. Otherwise use this view's width as a backup + let total = parentNavigationControllerToUse?.view.frame.width ?? view.frame.width + let percent = max(panGesture.translation(in: view).x, 0) / total - switch panGesture.state { - case .began: - if lazyPopContent?.isEnabled == true { - parentNavigationControllerToUse?.delegate = self - if let pop = self.pop { - pop() + switch panGesture.state { + case .began: + if lazyPopContent?.isEnabled == true { + parentNavigationControllerToUse?.delegate = self + if let pop = self.pop { + pop() + } } - } - case .changed: - if let percentDrivenInteractiveTransition = percentDrivenInteractiveTransition { - percentDrivenInteractiveTransition.update(percent) - } + case .changed: + if let percentDrivenInteractiveTransition = percentDrivenInteractiveTransition { + percentDrivenInteractiveTransition.update(percent) + } - case .ended: - let velocity = panGesture.velocity(in: view).x + case .ended: + let velocity = panGesture.velocity(in: view).x - // Continue if drag more than 50% of screen width or velocity is higher than 100 - if percent > 0.5 || velocity > 100 { - percentDrivenInteractiveTransition?.finish() - } else { + // Continue if drag more than 50% of screen width or velocity is higher than 100 + if percent > 0.5 || velocity > 100 { + percentDrivenInteractiveTransition?.finish() + } else { + percentDrivenInteractiveTransition?.cancel() + } + + case .cancelled, .failed: percentDrivenInteractiveTransition?.cancel() + + default: + break + } + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + } + + func navigationController(_: UINavigationController, + animationControllerFor _: UINavigationController.Operation, + from _: UIViewController, + to _: UIViewController) -> UIViewControllerAnimatedTransitioning? + { + if #available(iOS 17.0, *) { + return nil + } else if UIDevice.isIPad { + return nil + } else { + return SlideAnimatedTransitioning() + } + } + + func navigationController(_: UINavigationController, + interactionControllerFor _: UIViewControllerAnimatedTransitioning) + -> UIViewControllerInteractiveTransitioning? + { + parentNavigationControllerToUse?.delegate = nil + // navigationController.delegate = nil + + if panGestureRecognizer.state == .began { + percentDrivenInteractiveTransition = UIPercentDrivenInteractiveTransition() + percentDrivenInteractiveTransition?.completionCurve = .easeOut + } else { + percentDrivenInteractiveTransition = nil } - case .cancelled, .failed: - percentDrivenInteractiveTransition?.cancel() - - default: - break + return percentDrivenInteractiveTransition } } - override func didReceiveMemoryWarning() { - super.didReceiveMemoryWarning() - } - - func navigationController(_: UINavigationController, - animationControllerFor _: UINavigationController.Operation, - from _: UIViewController, - to _: UIViewController) -> UIViewControllerAnimatedTransitioning? - { - if #available(iOS 17.0, *) { - return nil - } else if UIDevice.isIPad { - return nil - } else { - return SlideAnimatedTransitioning() - } - } - - func navigationController(_: UINavigationController, - interactionControllerFor _: UIViewControllerAnimatedTransitioning) - -> UIViewControllerInteractiveTransitioning? - { - parentNavigationControllerToUse?.delegate = nil - // navigationController.delegate = nil - - if panGestureRecognizer.state == .began { - percentDrivenInteractiveTransition = UIPercentDrivenInteractiveTransition() - percentDrivenInteractiveTransition?.completionCurve = .easeOut - } else { - percentDrivenInteractiveTransition = nil - } - - return percentDrivenInteractiveTransition - } -} - // // Lazy Pop SwiftUI Component // @@ -155,33 +157,35 @@ class SwipeRightToPopViewController: UIHostingController, UINa // Copyright © 2019 Joseph Hinkle. All rights reserved. // -private struct LazyPop: UIViewControllerRepresentable { - let rootView: Content - let pop: () -> Void - @Binding var isEnabled: Bool + private struct LazyPop: UIViewControllerRepresentable { + let rootView: Content + let pop: () -> Void + @Binding var isEnabled: Bool - init(_ rootView: Content, pop: @escaping () -> Void, isEnabled: (Binding)? = nil) { - self.rootView = rootView - self.pop = pop - self._isEnabled = isEnabled ?? Binding(get: { true }, set: { _ in }) - } + init(_ rootView: Content, pop: @escaping () -> Void, isEnabled: (Binding)? = nil) { + self.rootView = rootView + self.pop = pop + self._isEnabled = isEnabled ?? Binding(get: { true }, set: { _ in }) + } - func makeUIViewController(context _: Context) -> UIViewController { - let vc = SwipeRightToPopViewController(rootView: rootView) - vc.pop = pop - vc.lazyPopContent = self - return vc - } + func makeUIViewController(context _: Context) -> UIViewController { + let vc = SwipeRightToPopViewController(rootView: rootView) + vc.pop = pop + vc.lazyPopContent = self + return vc + } - func updateUIViewController(_ uiViewController: UIViewController, context _: Context) { - if let host = uiViewController as? UIHostingController { - host.rootView = rootView + func updateUIViewController(_ uiViewController: UIViewController, context _: Context) { + if let host = uiViewController as? UIHostingController { + host.rootView = rootView + } } } -} -public extension View { - func lazyPop(pop: @escaping () -> Void, isEnabled: (Binding)? = nil) -> some View { - LazyPop(self, pop: pop, isEnabled: isEnabled) + public extension View { + func lazyPop(pop: @escaping () -> Void, isEnabled: (Binding)? = nil) -> some View { + LazyPop(self, pop: pop, isEnabled: isEnabled) + } } -} + +#endif diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift index d6e6528fc..f8a5ed8a2 100644 --- a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift +++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift @@ -427,7 +427,9 @@ struct WebReaderContainerView: View { showHighlightAnnotationModal: $showHighlightAnnotationModal ) .background(ThemeManager.currentBgColor) - .statusBar(hidden: prefersHideStatusBarInReader) + #if os(iOS) + .statusBar(hidden: prefersHideStatusBarInReader) + #endif .onAppear { if item.isUnread { dataService.updateLinkReadingProgress(itemID: item.unwrappedID, readingProgress: 0.1, anchorIndex: 0) diff --git a/apple/OmnivoreKit/Sources/App/Views/WelcomeView.swift b/apple/OmnivoreKit/Sources/App/Views/WelcomeView.swift index ae7fc5622..413176650 100644 --- a/apple/OmnivoreKit/Sources/App/Views/WelcomeView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/WelcomeView.swift @@ -7,7 +7,6 @@ import Views struct WelcomeView: View { @EnvironmentObject var dataService: DataService @EnvironmentObject var authenticator: Authenticator - @Environment(\.horizontalSizeClass) var horizontalSizeClass @Environment(\.openURL) var openURL @StateObject private var viewModel = RegistrationViewModel() diff --git a/apple/OmnivoreKit/Sources/Utils/EventTracking/EventTracker.swift b/apple/OmnivoreKit/Sources/Utils/EventTracking/EventTracker.swift index 0dc18838f..245cd8094 100644 --- a/apple/OmnivoreKit/Sources/Utils/EventTracking/EventTracker.swift +++ b/apple/OmnivoreKit/Sources/Utils/EventTracking/EventTracker.swift @@ -1,24 +1,29 @@ import Foundation -import PostHog + +#if os(iOS) + import PostHog +#endif public enum EventTracker { - public static var posthog: PHGPostHog? = { - guard let writeKey = AppKeys.sharedInstance?.posthogClientKey else { - return nil - } + #if os(iOS) + public static var posthog: PHGPostHog? = { + guard let writeKey = AppKeys.sharedInstance?.posthogClientKey else { + return nil + } - guard let posthogInstanceAddress = AppKeys.sharedInstance?.posthogInstanceAddress else { - return nil - } + guard let posthogInstanceAddress = AppKeys.sharedInstance?.posthogInstanceAddress else { + return nil + } - let configuration = PHGPostHogConfiguration(apiKey: writeKey, host: posthogInstanceAddress) + let configuration = PHGPostHogConfiguration(apiKey: writeKey, host: posthogInstanceAddress) - configuration.recordScreenViews = false - configuration.captureApplicationLifecycleEvents = true + configuration.recordScreenViews = false + configuration.captureApplicationLifecycleEvents = true - PHGPostHog.setup(with: configuration) - return PHGPostHog.shared() - }() + PHGPostHog.setup(with: configuration) + return PHGPostHog.shared() + }() + #endif public static func trackForDebugging(_ message: String) { #if DEBUG @@ -27,14 +32,20 @@ public enum EventTracker { } public static func track(_ event: TrackableEvent) { - posthog?.capture(event.name, properties: event.properties) + #if os(iOS) + posthog?.capture(event.name, properties: event.properties) + #endif } public static func registerUser(userID: String) { - posthog?.identify(userID) + #if os(iOS) + posthog?.identify(userID) + #endif } public static func reset() { - posthog?.reset() + #if os(iOS) + posthog?.reset() + #endif } } diff --git a/apple/OmnivoreKit/Sources/Views/MacSearchBar.swift b/apple/OmnivoreKit/Sources/Views/MacSearchBar.swift new file mode 100644 index 000000000..1dc915a9b --- /dev/null +++ b/apple/OmnivoreKit/Sources/Views/MacSearchBar.swift @@ -0,0 +1,57 @@ +import SwiftUI + +#if os(macOS) + public struct MacSearchBar: NSViewRepresentable { + @Binding var searchTerm: String + + public init( + searchTerm: Binding + ) { + self._searchTerm = searchTerm + } + + public func makeNSView(context _: Context) -> NSSearchField { + let searchField = NSSearchField(frame: .zero) + searchField.translatesAutoresizingMaskIntoConstraints = false + searchField.heightAnchor.constraint(greaterThanOrEqualToConstant: 28).isActive = true + searchField.resignFirstResponder() + + return searchField + } + + func changeSearchFieldItem(searchField: NSSearchField, sender: AnyObject) -> NSSearchField { + // Based on the Menu item selection in the search field the placeholder string is set + (searchField.cell as? NSSearchFieldCell)?.placeholderString = sender.title + return searchField + } + + public func updateNSView(_ searchField: NSSearchField, context: Context) { + searchField.font = searchField.font?.withSize(14) + searchField.stringValue = searchTerm + searchField.delegate = context.coordinator + searchField.resignFirstResponder() + } + + public func makeCoordinator() -> Coordinator { + let coordinator = Coordinator(searchTerm: $searchTerm) + return coordinator + } + + public class Coordinator: NSObject, NSSearchFieldDelegate { + var searchTerm: Binding + + init(searchTerm: Binding) { + self.searchTerm = searchTerm + super.init() + } + + public func controlTextDidChange(_ notification: Notification) { + guard let searchField = notification.object as? NSSearchField else { + // log.error("Unexpected control in update notification", source: .ui) + return + } + searchTerm.wrappedValue = searchField.stringValue + } + } + } +#endif diff --git a/apple/OmnivoreKit/Sources/Views/Utils/MacOSSizeClass.swift b/apple/OmnivoreKit/Sources/Views/Utils/MacOSSizeClass.swift index f53d08f91..b20f5d533 100644 --- a/apple/OmnivoreKit/Sources/Views/Utils/MacOSSizeClass.swift +++ b/apple/OmnivoreKit/Sources/Views/Utils/MacOSSizeClass.swift @@ -8,10 +8,10 @@ import SwiftUI // https://stackoverflow.com/questions/63526478/swiftui-userinterfacesizeclass-for-universal-macos-ios-views #if os(macOS) - public enum UserInterfaceSizeClass { - case compact - case regular - } +// public enum UserInterfaceSizeClass { +// case compact +// case regular +// } public struct HorizontalSizeClassEnvironmentKey: EnvironmentKey { public static let defaultValue: UserInterfaceSizeClass = .regular diff --git a/apple/Sources/SafariExtension/Resources/images/extension/icon-128.png b/apple/Sources/SafariExtension/Resources/images/extension/icon-128.png index 7608e31eb..d6df58ae7 100644 Binary files a/apple/Sources/SafariExtension/Resources/images/extension/icon-128.png and b/apple/Sources/SafariExtension/Resources/images/extension/icon-128.png differ diff --git a/apple/Sources/SafariExtension/Resources/images/extension/icon-16.png b/apple/Sources/SafariExtension/Resources/images/extension/icon-16.png index 26d201618..5c0b8ad30 100644 Binary files a/apple/Sources/SafariExtension/Resources/images/extension/icon-16.png and b/apple/Sources/SafariExtension/Resources/images/extension/icon-16.png differ diff --git a/apple/Sources/SafariExtension/Resources/images/extension/icon-24.png b/apple/Sources/SafariExtension/Resources/images/extension/icon-24.png index 218786d43..a0654130b 100644 Binary files a/apple/Sources/SafariExtension/Resources/images/extension/icon-24.png and b/apple/Sources/SafariExtension/Resources/images/extension/icon-24.png differ diff --git a/apple/Sources/SafariExtension/Resources/images/extension/icon-256.png b/apple/Sources/SafariExtension/Resources/images/extension/icon-256.png index 36840b8af..a4413ba25 100644 Binary files a/apple/Sources/SafariExtension/Resources/images/extension/icon-256.png and b/apple/Sources/SafariExtension/Resources/images/extension/icon-256.png differ diff --git a/apple/Sources/SafariExtension/Resources/images/extension/icon-32.png b/apple/Sources/SafariExtension/Resources/images/extension/icon-32.png index e599d94e2..2270c0762 100644 Binary files a/apple/Sources/SafariExtension/Resources/images/extension/icon-32.png and b/apple/Sources/SafariExtension/Resources/images/extension/icon-32.png differ diff --git a/apple/Sources/SafariExtension/Resources/images/extension/icon-48.png b/apple/Sources/SafariExtension/Resources/images/extension/icon-48.png index 568703dfc..dda13bd4e 100644 Binary files a/apple/Sources/SafariExtension/Resources/images/extension/icon-48.png and b/apple/Sources/SafariExtension/Resources/images/extension/icon-48.png differ diff --git a/apple/Sources/SafariExtension/Resources/images/extension/icon-96.png b/apple/Sources/SafariExtension/Resources/images/extension/icon-96.png index 8c1aa4123..3af64b0be 100644 Binary files a/apple/Sources/SafariExtension/Resources/images/extension/icon-96.png and b/apple/Sources/SafariExtension/Resources/images/extension/icon-96.png differ diff --git a/apple/Sources/SafariExtension/Resources/manifest.json b/apple/Sources/SafariExtension/Resources/manifest.json index de9fe4d60..63789dae2 100644 --- a/apple/Sources/SafariExtension/Resources/manifest.json +++ b/apple/Sources/SafariExtension/Resources/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "Omnivore", "short_name": "Omnivore", - "version": "2.2.0", + "version": "2.4.2", "description": "Save PDFs and Articles to your Omnivore library", "author": "Omnivore Media, Inc", "default_locale": "en", @@ -75,12 +75,12 @@ ], "browser_action": { "default_icon": { - "16": "/images/toolbar/icon-16.png", - "19": "/images/toolbar/icon-19.png", - "24": "/images/toolbar/icon-24.png", - "32": "/images/toolbar/icon-32.png", - "38": "/images/toolbar/icon-38.png", - "48": "/images/toolbar/icon-48.png" + "16": "/images/extension/icon-16.png", + "19": "/images/extension/icon-19.png", + "24": "/images/extension/icon-24.png", + "32": "/images/extension/icon-32.png", + "38": "/images/extension/icon-38.png", + "48": "/images/extension/icon-48.png" }, "default_title": "Omnivore Save Article" }, diff --git a/apple/Sources/SafariExtension/Resources/scripts/background.js b/apple/Sources/SafariExtension/Resources/scripts/background.js index 2622ae52e..0db7c28e2 100644 --- a/apple/Sources/SafariExtension/Resources/scripts/background.js +++ b/apple/Sources/SafariExtension/Resources/scripts/background.js @@ -1 +1 @@ -(()=>{"use strict";var e,t=new Uint8Array(16);function a(){if(!e&&!(e="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)||"undefined"!=typeof msCrypto&&"function"==typeof msCrypto.getRandomValues&&msCrypto.getRandomValues.bind(msCrypto)))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return e(t)}const n=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;for(var s=[],o=0;o<256;++o)s.push((o+256).toString(16).substr(1));const r=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,a=(s[e[t+0]]+s[e[t+1]]+s[e[t+2]]+s[e[t+3]]+"-"+s[e[t+4]]+s[e[t+5]]+"-"+s[e[t+6]]+s[e[t+7]]+"-"+s[e[t+8]]+s[e[t+9]]+"-"+s[e[t+10]]+s[e[t+11]]+s[e[t+12]]+s[e[t+13]]+s[e[t+14]]+s[e[t+15]]).toLowerCase();if(!function(e){return"string"==typeof e&&n.test(e)}(a))throw TypeError("Stringified UUID is invalid");return a},i=function(e,t,n){var s=(e=e||{}).random||(e.rng||a)();if(s[6]=15&s[6]|64,s[8]=63&s[8]|128,t){n=n||0;for(var o=0;o<16;++o)t[n+o]=s[o];return t}return r(s)};let d=(e=21)=>crypto.getRandomValues(new Uint8Array(e)).reduce(((e,t)=>e+((t&=63)<36?t.toString(36):t<62?(t-26).toString(36).toUpperCase():t>62?"-":"_")),"");const c="https://omnivore.app",u="https://api-prod.omnivore.app/api/";let l=[],p={};async function g(e,t){const a=JSON.stringify({query:"mutation UploadFileRequest($input: UploadFileRequestInput!) {\n uploadFileRequest(input:$input) {\n ... on UploadFileRequestError {\n errorCodes\n }\n ... on UploadFileRequestSuccess {\n id\n createdPageId\n uploadSignedUrl\n }\n }\n }",variables:{input:{url:e,contentType:t,createPageEntry:!0}}}),n="uploadFileRequest",s=await gqlRequest(u+"graphql",a);if(!s[n].errorCodes)return s.uploadFileRequest;"UNAUTHORIZED"===s[n].errorCodes[0]?(browserApi.tabs.sendMessage(currentTab.id,{action:ACTIONS.UpdateStatus,payload:{target:"logged_out",status:"logged_out",message:"You are not logged in.",ctx:toolbarCtx}}),b()):browserApi.tabs.sendMessage(currentTab.id,{action:ACTIONS.UpdateStatus,payload:{status:"failure",message:"Unable to save page.",ctx:toolbarCtx}})}function b(){getStorageItem("postInstallClickComplete").then((e=>{e&&removeStorage("postInstallClickComplete")}))}async function I(e,t){const a=i();await f(e,SAVE_URL_QUERY,"saveUrl",{source:"extension",clientRequestId:a,url:encodeURI(t)})}async function f(e,t,a,n){const s={omnivoreURL:c,originalURL:n.url,requestId:n.clientRequestId};p[s.requestId]=void 0;const o=JSON.stringify({query:t,variables:{input:n}});browserApi.tabs.sendMessage(e.id,{action:ACTIONS.ShowToolbar,payload:{type:"loading",ctx:s}});try{const t=await gqlRequest(u+"graphql",o);if(t[a].errorCodes)return void("UNAUTHORIZED"===t[a].errorCodes[0]?(browserApi.tabs.sendMessage(e.id,{action:ACTIONS.UpdateStatus,payload:{target:"logged_out",status:"logged_out",message:"You are not logged in.",ctx:s}}),b()):browserApi.tabs.sendMessage(e.id,{action:ACTIONS.UpdateStatus,payload:{status:"failure",message:"Unable to save page.",ctx:s}}));const n=t[a]?t[a].url:void 0,r=t[a]?t[a].clientRequestId:void 0;browserApi.tabs.sendMessage(e.id,{action:ACTIONS.UpdateStatus,payload:{status:"success",target:"page",ctx:{readerURL:n,responseId:r,requestId:s.requestId}}}),p[s.requestId]={readerURL:n,responseId:r,requestId:s.requestId}}catch(e){console.log("error saving: ",e)}h(e.id)}function y(e,t,a,n){browserApi.tabs.sendMessage(e,{action:ACTIONS.UpdateStatus,payload:{target:t,status:a,message:n}})}async function h(e){l.filter((t=>t.tabId===e)).forEach((async t=>{let a=!1;const n=p[t.clientRequestId];if(n)switch(t.type){case"EDIT_TITLE":a=await async function(e,t,a){return updatePageTitle(u+"graphql",a.responseId,t.title).then((()=>(y(e,"title","success","Title updated."),!0))).catch((t=>(console.log("caught error updating title: ",t),y(e,"title","failure","Error updating title."),!0)))}(e,t,n);break;case"ADD_NOTE":a=await async function(e,t,a){const n=i(),s=d(8);return addNote(u+"graphql",a.responseId,n,s,t.note).then((()=>(y(e,"note","success","Note updated."),!0))).catch((t=>(console.log("caught error updating title: ",t),y(e,"note","failure","Error adding note."),!0)))}(e,t,n);break;case"SET_LABELS":a=await async function(e,t,a){return setLabels(u+"graphql",a.responseId,t.labelIds).then((()=>(y(e,"labels","success","Labels updated."),!0))).catch((()=>(y(e,"labels","failure","Error updating labels."),!0)))}(e,t,n);break;case"ARCHIVE":a=await async function(e,t,a){return archive(u+"graphql",a.responseId).then((()=>(y(e,"extra","success","Archived"),!0))).catch((()=>(y(e,"extra","failure","Error archiving"),!0)))}(e,0,n);break;case"DELETE":a=await async function(e,t,a){return deleteItem(u+"graphql",a.responseId).then((()=>(y(e,"extra","success","Deleted"),!0))).catch((()=>(y(e,"extra","failure","Error deleting"),!0)))}(e,0,n)}if(a){const e=l.findIndex((e=>t.id===e.id));e>-1&&l.splice(e,1)}}))}async function A(e){browserApi.tabs.sendMessage(e.id,{action:ACTIONS.GetContent},(async t=>{if(!t||"object"!=typeof t)return void await I(e,e.url);const a=i();var{type:n}=t;const{pageInfo:s,doc:o,uploadContentObjUrl:r}=t;switch("html"==n&&handleBackendUrl(e.url)&&(n="url"),n){case"html":await f(e,SAVE_PAGE_QUERY,"savePage",{source:"extension",clientRequestId:a,originalContent:o,title:s.title,url:encodeURI(e.url)});break;case"url":await f(e,SAVE_URL_QUERY,"saveUrl",{source:"extension",clientRequestId:a,url:encodeURI(e.url)});break;case"pdf":{const t=await async function(e,t,a,n,s){const o={omnivoreURL:c,originalURL:t,requestId:a};p[o.requestId]=void 0,browserApi.tabs.sendMessage(e.id,{action:ACTIONS.ShowToolbar,payload:{type:"loading",ctx:o}});const r=await g(t,n);console.log("done uploading pdf",r);const i=await function({id:e,uploadSignedUrl:t},a,n){return fetch(n).then((e=>e.blob())).then((e=>fetch(t,{method:"PUT",headers:{"Content-Type":a},body:e}))).catch((e=>{console.error("error uploading file",e)}))}(r,n,s);return console.log(" uploadFileResult: ",i),URL.revokeObjectURL(s),i&&r.createdPageId&&(p[o.requestId]={requestId:o.requestId,responseId:r.createdPageId},browserApi.tabs.sendMessage(e.id,{action:ACTIONS.UpdateStatus,payload:{status:"success",target:"page",ctx:{requestId:o.requestId,responseId:r.createdPageId}}})),i}(e,encodeURI(e.url),a,s.contentType,r);if(!t||!t.id)return void await f(e,SAVE_URL_QUERY,"saveUrl",{source:"extension",clientRequestId:a,url:encodeURI(e.url)});break}}}))}async function w(e){const t=await getStorageItem(e+"_saveInProgress");if(!t)return;clearInterval(t);const a=await getStorageItem(e+"_saveInProgressTimeoutId_"+t);a&&clearTimeout(a)}function S(e){return Promise.resolve(!0)}function v(e){new Promise((e=>{browserApi.tabs.query({active:!0,currentWindow:!0},(function(t){e(t[0]||null)}))})).then((t=>{browserApi.tabs.sendMessage(t.id,{action:ACTIONS.Ping},(async function(a){if(a&&a.pong)await S(t.id)&&e(t);else{const a=browserApi.runtime.getManifest().content_scripts,n=[...a[0].js,...a[1].js];!function(a,n,s){function o(e,t,a){return function(){browserScriptingApi.executeScript(e,t,a)}}let r=async function(){await S(t.id)&&e(t)};for(let e=n.length-1;e>=0;--e)r=o(a,{file:n[e]},r);null!==r&&r()}(t.id,n)}}))}))}function R(e,t){let a="/images/toolbar/icon";if(ENV_IS_FIREFOX?a+="_firefox":ENV_IS_EDGE&&(a+="_edge"),e||(a+="_inactive"),("boolean"==typeof t?t:window.matchMedia("(prefers-color-scheme: dark)").matches)&&(a+="_dark"),ENV_IS_FIREFOX)return a+".svg";const n=["16","24","32","48"];ENV_IS_EDGE||n.push("19","38");const s={};for(let e=0;e{!function t(){browserApi.tabs.get(e,(function(e){browserApi.runtime.lastError&&setTimeout(t,150),q(e)}))}()})),browserApi.tabs.onUpdated.addListener(((e,t,a)=>{t.status&&a&&a.active&&q(a)})),browserApi.tabs.onRemoved.addListener((e=>{!function(e){getStorage().then((function(t){const a=[],n=Object.keys(t),s=e+"_saveInProgress";for(let e=0;e{if("complete"!==e.status)browserApi.tabs.sendMessage(e.id,{action:ACTIONS.ShowMessage,payload:{type:"loading",text:"Page loading..."}}),a&&"function"==typeof a&&a();else{t&&"function"==typeof t&&t(),await A(e);try{await updateLabelsCache(u+"graphql",e),browserApi.tabs.sendMessage(e.id,{action:ACTIONS.LabelCacheUpdated,payload:{}})}catch(e){return void console.error("error fetching labels",e,u)}}}))}w(e),t(null,(()=>{!function(e,t,a,n=1e3,s=10500){const o=setInterval(e,n),r=setTimeout((()=>{clearInterval(o),t()}),s);a&&"function"==typeof a&&a(o,r)}((()=>{t((()=>{w(e)}))}),(()=>{w(e),browserApi.tabs.get(e,(async e=>{await A(e)}))}),((t,a)=>{const n={};n[e+"_saveInProgress"]=t,n[e+"_saveInProgressTimeoutId_"+t]=a,setStorage(n)}))}))}(e.id)}))})),browserApi.runtime.onMessage.addListener(((e,t,a)=>{if(e.forwardToTab)return delete e.forwardToTab,void browserApi.tabs.sendRequest(t.tab.id,e);e.action===ACTIONS.RefreshDarkMode&&m(t.tab.id,e.payload.value),e.action===ACTIONS.EditTitle&&(l.push({id:i(),type:"EDIT_TITLE",tabId:t.tab.id,title:e.payload.title,clientRequestId:e.payload.ctx.requestId}),h(t.tab.id)),e.action===ACTIONS.Archive&&(l.push({id:i(),type:"ARCHIVE",tabId:t.tab.id,clientRequestId:e.payload.ctx.requestId}),h(t.tab.id)),e.action===ACTIONS.Delete&&(l.push({type:"DELETE",tabId:t.tab.id,clientRequestId:e.payload.ctx.requestId}),h(t.tab.id)),e.action===ACTIONS.AddNote&&(l.push({id:i(),type:"ADD_NOTE",tabId:t.tab.id,note:e.payload.note,clientRequestId:e.payload.ctx.requestId}),h(t.tab.id)),e.action===ACTIONS.SetLabels&&(l.push({id:i(),type:"SET_LABELS",tabId:t.tab.id,labelIds:e.payload.labelIds,clientRequestId:e.payload.ctx.requestId}),h(t.tab.id))})),browserActionApi.setIcon({path:R(!0)}),browserApi.contextMenus.create({id:"save-selection",title:"Save to Omnivore",contexts:["link"],onclick:async function(e){v((async function(t){await I(t,e.linkUrl)}))}})})(); \ No newline at end of file +(()=>{"use strict";var e,t=new Uint8Array(16);function a(){if(!e&&!(e="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)||"undefined"!=typeof msCrypto&&"function"==typeof msCrypto.getRandomValues&&msCrypto.getRandomValues.bind(msCrypto)))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return e(t)}const n=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;for(var s=[],o=0;o<256;++o)s.push((o+256).toString(16).substr(1));const r=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,a=(s[e[t+0]]+s[e[t+1]]+s[e[t+2]]+s[e[t+3]]+"-"+s[e[t+4]]+s[e[t+5]]+"-"+s[e[t+6]]+s[e[t+7]]+"-"+s[e[t+8]]+s[e[t+9]]+"-"+s[e[t+10]]+s[e[t+11]]+s[e[t+12]]+s[e[t+13]]+s[e[t+14]]+s[e[t+15]]).toLowerCase();if(!function(e){return"string"==typeof e&&n.test(e)}(a))throw TypeError("Stringified UUID is invalid");return a},i=function(e,t,n){var s=(e=e||{}).random||(e.rng||a)();if(s[6]=15&s[6]|64,s[8]=63&s[8]|128,t){n=n||0;for(var o=0;o<16;++o)t[n+o]=s[o];return t}return r(s)};let d=(e=21)=>crypto.getRandomValues(new Uint8Array(e)).reduce(((e,t)=>e+((t&=63)<36?t.toString(36):t<62?(t-26).toString(36).toUpperCase():t>62?"-":"_")),"");const c="https://omnivore.app",u="https://api-prod.omnivore.app/api/";let l=[],p={};async function g(e,t){const a=JSON.stringify({query:"mutation UploadFileRequest($input: UploadFileRequestInput!) {\n uploadFileRequest(input:$input) {\n ... on UploadFileRequestError {\n errorCodes\n }\n ... on UploadFileRequestSuccess {\n id\n createdPageId\n uploadSignedUrl\n }\n }\n }",variables:{input:{url:e,contentType:t,createPageEntry:!0}}}),n="uploadFileRequest",s=await gqlRequest(u+"graphql",a);if(!s[n].errorCodes)return s.uploadFileRequest;"UNAUTHORIZED"===s[n].errorCodes[0]?(browserApi.tabs.sendMessage(currentTab.id,{action:ACTIONS.UpdateStatus,payload:{target:"logged_out",status:"logged_out",message:"You are not logged in.",ctx:toolbarCtx}}),b()):browserApi.tabs.sendMessage(currentTab.id,{action:ACTIONS.UpdateStatus,payload:{status:"failure",message:"Unable to save page.",ctx:toolbarCtx}})}function b(){getStorageItem("postInstallClickComplete").then((e=>{e&&removeStorage("postInstallClickComplete")}))}async function I(e,t){const a=i();await f(e,SAVE_URL_QUERY,"saveUrl",{source:"extension",clientRequestId:a,url:encodeURI(t)})}async function f(e,t,a,n){const s={omnivoreURL:c,originalURL:n.url,requestId:n.clientRequestId};p[s.requestId]=void 0;const o=JSON.stringify({query:t,variables:{input:n}});browserApi.tabs.sendMessage(e.id,{action:ACTIONS.ShowToolbar,payload:{type:"loading",ctx:s}});try{const t=await gqlRequest(u+"graphql",o);if(t[a].errorCodes)return void("UNAUTHORIZED"===t[a].errorCodes[0]?(browserApi.tabs.sendMessage(e.id,{action:ACTIONS.UpdateStatus,payload:{target:"logged_out",status:"logged_out",message:"You are not logged in.",ctx:s}}),b()):browserApi.tabs.sendMessage(e.id,{action:ACTIONS.UpdateStatus,payload:{status:"failure",message:"Unable to save page.",ctx:s}}));const n=t[a]?t[a].url:void 0,r=t[a]?t[a].clientRequestId:void 0;browserApi.tabs.sendMessage(e.id,{action:ACTIONS.UpdateStatus,payload:{status:"success",target:"page",ctx:{readerURL:n,responseId:r,requestId:s.requestId}}}),p[s.requestId]={readerURL:n,responseId:r,requestId:s.requestId}}catch(e){console.log("error saving: ",e)}h(e.id)}function y(e,t,a,n){browserApi.tabs.sendMessage(e,{action:ACTIONS.UpdateStatus,payload:{target:t,status:a,message:n}})}async function h(e){l.filter((t=>t.tabId===e)).forEach((async t=>{let a=!1;const n=p[t.clientRequestId];if(n)switch(t.type){case"EDIT_TITLE":a=await async function(e,t,a){return updatePageTitle(u+"graphql",a.responseId,t.title).then((()=>(y(e,"title","success","Title updated."),!0))).catch((t=>(console.log("caught error updating title: ",t),y(e,"title","failure","Error updating title."),!0)))}(e,t,n);break;case"ADD_NOTE":a=await async function(e,t,a){const n=i(),s=d(8);return addNote(u+"graphql",a.responseId,n,s,t.note).then((()=>(y(e,"note","success","Note updated."),!0))).catch((t=>(console.log("caught error updating title: ",t),y(e,"note","failure","Error adding note."),!0)))}(e,t,n);break;case"SET_LABELS":a=await async function(e,t,a){return setLabels(u+"graphql",a.responseId,t.labelIds).then((()=>(y(e,"labels","success","Labels updated."),!0))).catch((()=>(y(e,"labels","failure","Error updating labels."),!0)))}(e,t,n);break;case"ARCHIVE":a=await async function(e,t,a){return archive(u+"graphql",a.responseId).then((()=>(y(e,"extra","success","Archived"),!0))).catch((()=>(y(e,"extra","failure","Error archiving"),!0)))}(e,0,n);break;case"DELETE":a=await async function(e,t,a){return deleteItem(u+"graphql",a.responseId).then((()=>(y(e,"extra","success","Deleted"),!0))).catch((()=>(y(e,"extra","failure","Error deleting"),!0)))}(e,0,n)}if(a){const e=l.findIndex((e=>t.id===e.id));e>-1&&l.splice(e,1)}}))}async function w(e){browserApi.tabs.sendMessage(e.id,{action:ACTIONS.GetContent},(async t=>{if(!t||"object"!=typeof t)return void await I(e,e.url);const a=i();var{type:n}=t;const{pageInfo:s,doc:o,uploadContentObjUrl:r}=t;switch("html"==n&&handleBackendUrl(e.url)&&(n="url"),n){case"html":await f(e,SAVE_PAGE_QUERY,"savePage",{source:"extension",clientRequestId:a,originalContent:o,title:s.title,url:encodeURI(e.url)});break;case"url":await f(e,SAVE_URL_QUERY,"saveUrl",{source:"extension",clientRequestId:a,url:encodeURI(e.url)});break;case"pdf":{const t=await async function(e,t,a,n,s){const o={omnivoreURL:c,originalURL:t,requestId:a};p[o.requestId]=void 0,browserApi.tabs.sendMessage(e.id,{action:ACTIONS.ShowToolbar,payload:{type:"loading",ctx:o}});const r=await g(t,n);console.log("done uploading pdf",r);const i=await function({id:e,uploadSignedUrl:t},a,n){return fetch(n).then((e=>e.blob())).then((e=>fetch(t,{method:"PUT",headers:{"Content-Type":a},body:e}))).catch((e=>{console.error("error uploading file",e)}))}(r,n,s);return console.log(" uploadFileResult: ",i),URL.revokeObjectURL(s),i&&r.createdPageId&&(p[o.requestId]={requestId:o.requestId,responseId:r.createdPageId},browserApi.tabs.sendMessage(e.id,{action:ACTIONS.UpdateStatus,payload:{status:"success",target:"page",ctx:{requestId:o.requestId,responseId:r.createdPageId}}})),i}(e,encodeURI(e.url),a,s.contentType,r);if(!t||!t.id)return void await f(e,SAVE_URL_QUERY,"saveUrl",{source:"extension",clientRequestId:a,url:encodeURI(e.url)});break}}}))}async function A(e){const t=await getStorageItem(e+"_saveInProgress");if(!t)return;clearInterval(t);const a=await getStorageItem(e+"_saveInProgressTimeoutId_"+t);a&&clearTimeout(a)}function S(e){return Promise.resolve(!0)}function q(e){new Promise((e=>{browserApi.tabs.query({active:!0,currentWindow:!0},(function(t){e(t[0]||null)}))})).then((t=>{console.log("currentTab: ",t),browserApi.tabs.sendMessage(t.id,{action:ACTIONS.Ping},(async function(a){if(a&&a.pong)await S(t.id)&&e(t);else{const a=browserApi.runtime.getManifest().content_scripts,n=[...a[0].js,...a[1].js];!function(a,n,s){function o(e,t,a){return function(){browserScriptingApi.executeScript(e,t,a)}}let r=async function(){await S(t.id)&&e(t)};for(let e=n.length-1;e>=0;--e)r=o(a,{file:n[e]},r);null!==r&&r()}(t.id,n)}}))}))}browserApi.tabs.onActivated.addListener((({tabId:e})=>{!function t(){browserApi.tabs.get(e,(function(e){browserApi.runtime.lastError&&setTimeout(t,150)}))}()})),browserApi.tabs.onRemoved.addListener((e=>{!function(e){getStorage().then((function(t){const a=[],n=Object.keys(t),s=e+"_saveInProgress";for(let e=0;e{if("complete"!==e.status)browserApi.tabs.sendMessage(e.id,{action:ACTIONS.ShowMessage,payload:{type:"loading",text:"Page loading..."}}),a&&"function"==typeof a&&a();else{t&&"function"==typeof t&&t(),await w(e);try{await updateLabelsCache(u+"graphql",e),browserApi.tabs.sendMessage(e.id,{action:ACTIONS.LabelCacheUpdated,payload:{}})}catch(e){return void console.error("error fetching labels",e,u)}}}))}A(e),t(null,(()=>{!function(e,t,a,n=1e3,s=10500){const o=setInterval(e,n),r=setTimeout((()=>{clearInterval(o),t()}),s);a&&"function"==typeof a&&a(o,r)}((()=>{t((()=>{A(e)}))}),(()=>{A(e),browserApi.tabs.get(e,(async e=>{await w(e)}))}),((t,a)=>{const n={};n[e+"_saveInProgress"]=t,n[e+"_saveInProgressTimeoutId_"+t]=a,setStorage(n)}))}))}(e.id)}))})),browserApi.runtime.onMessage.addListener(((e,t,a)=>{if(e.forwardToTab)return delete e.forwardToTab,void browserApi.tabs.sendRequest(t.tab.id,e);e.action===ACTIONS.EditTitle&&(l.push({id:i(),type:"EDIT_TITLE",tabId:t.tab.id,title:e.payload.title,clientRequestId:e.payload.ctx.requestId}),h(t.tab.id)),e.action===ACTIONS.Archive&&(l.push({id:i(),type:"ARCHIVE",tabId:t.tab.id,clientRequestId:e.payload.ctx.requestId}),h(t.tab.id)),e.action===ACTIONS.Delete&&(l.push({type:"DELETE",tabId:t.tab.id,clientRequestId:e.payload.ctx.requestId}),h(t.tab.id)),e.action===ACTIONS.AddNote&&(l.push({id:i(),type:"ADD_NOTE",tabId:t.tab.id,note:e.payload.note,clientRequestId:e.payload.ctx.requestId}),h(t.tab.id)),e.action===ACTIONS.SetLabels&&(l.push({id:i(),type:"SET_LABELS",tabId:t.tab.id,labelIds:e.payload.labelIds,clientRequestId:e.payload.ctx.requestId}),h(t.tab.id))})),browserApi.contextMenus.create({id:"save-selection",title:"Save this link to Omnivore",contexts:["link"],onclick:async function(e){q((async function(t){await I(t,e.linkUrl)}))}})})(); \ No newline at end of file diff --git a/apple/Sources/SafariExtension/Resources/scripts/content/prepare-content.js b/apple/Sources/SafariExtension/Resources/scripts/content/prepare-content.js index a2eb658ee..31fdcc15d 100644 --- a/apple/Sources/SafariExtension/Resources/scripts/content/prepare-content.js +++ b/apple/Sources/SafariExtension/Resources/scripts/content/prepare-content.js @@ -1 +1 @@ -"use strict";!function(){const e={};function t(t){const n=t.tagName.toLowerCase();if("iframe"===n){const n=e[t.src];if(!n)return;const o=document.createElement("div");o.className="omnivore-instagram-embed",o.innerHTML=n;const r=t.parentNode;if(!r)return;return void r.replaceChild(o,t)}if("img"===n||"image"===n){if(-1===window.getComputedStyle(t).getPropertyValue("filter").indexOf("blur("))return;return void t.remove()}const o=window.getComputedStyle(t),r=o.getPropertyValue("background-image");if(r&&"none"!==r)return;const i=o.getPropertyValue("filter");if(i&&-1!==i.indexOf("blur("))return void t.remove();if(t.src)return;if(t.innerHTML.length>24)return;const c=/url\("(.+?)"\)/gi,a=c.exec(r);c.lastIndex=0;const l=a&&a[1];if(!l)return;const s=document.createElement("img");s.src=l;const d=t.parentNode;d&&d.replaceChild(s,t)}function n(){const e=document.createElement("div");e.style.position="absolute",e.style.left="-2000px",e.style.zIndex="-2000",e.innerHTML=document.body.innerHTML,document.documentElement.appendChild(e),Array.from(e.getElementsByTagName("*")).forEach(t);try{if("undefined"!=typeof create_time&&create_time){const t=new Date(1e3*create_time),n=document.createElement("div");n.className="omnivore-published-date",n.innerHTML=t.toLocaleString(),e.appendChild(n)}}catch(e){console.log("Error while trying to add published date to WeChat post",e)}const n=`${document.head.innerHTML}${e.innerHTML}`;return e.remove(),n}function o(){const e=document.querySelectorAll(".webext-omnivore-backdrop");for(let t=0;t{for(let t=0;t{if(t!==ACTIONS.AddIframeContent)return;const{url:i,content:c}=n;e[i]=c,r({})})),window.prepareContent=async function(){const e=await async function(){const e=".pdf"===window.location.pathname.slice(-4).toLowerCase(),t=-1!==["application/acrobat","application/pdf","application/x-pdf","applications/vnd.pdf","text/pdf","text/x-pdf"].indexOf(document.contentType);if(!e&&!t)return Promise.resolve(null);const n=document.querySelector("embed");return n&&"application/pdf"!==n.type?Promise.resolve(null):ENV_DOES_NOT_SUPPORT_BLOB_URL_ACCESS&&n.src?Promise.resolve({type:"url",uploadContentObjUrl:n.src}):new Promise(((e,t)=>{const n=new XMLHttpRequest;n.open("GET","",!0),n.responseType="blob",n.onload=function(n){200===this.status?e({type:"pdf",uploadContentObjUrl:URL.createObjectURL(this.response)}):t(n)},n.send()}))}();if(e)return e;const t=window.location.href;try{if(handleBackendUrl(t))return{type:"url"}}catch{console.log("error checking url")}return await async function(e){const t=document.scrollingElement||document.body,n=t.scrollTop,r=t.scrollHeight;o();const i=function(){const e=document.createElement("div");return e.className="webext-omnivore-backdrop",e.style.cssText="all: initial !important;\n position: fixed !important;\n top: 0 !important;\n right: 0 !important;\n bottom: 0 !important;\n left: 0 !important;\n z-index: 99999 !important;\n background: #fff !important;\n opacity: 0.8 !important;\n transition: opacity 0.3s !important;\n -webkit-backdrop-filter: blur(4px) !important;\n backdrop-filter: blur(4px) !important;\n ",e}();for(document.body.appendChild(i);t.scrollTop<=r-500&&window.location.href===e;){const e=t.scrollTop;if(t.scrollTop+=500,await new Promise((e=>{setTimeout(e,10)})),t.scrollTop===e)break}t.scrollTop=n,await new Promise((e=>{setTimeout(e,10)}))}(t),o(),{type:"html",content:n()}}}(); \ No newline at end of file +"use strict";!function(){const e={};function t(t){const n=t.tagName.toLowerCase();if("iframe"===n){const n=e[t.src];if(!n)return;const o=document.createElement("div");o.className="omnivore-instagram-embed",o.innerHTML=n;const r=t.parentNode;if(!r)return;return void r.replaceChild(o,t)}if("img"===n||"image"===n){if(-1===window.getComputedStyle(t).getPropertyValue("filter").indexOf("blur("))return;return void t.remove()}const o=window.getComputedStyle(t),r=o.getPropertyValue("background-image");if(r&&"none"!==r)return;const i=o.getPropertyValue("filter");if(i&&-1!==i.indexOf("blur("))return void t.remove();if(t.src)return;if(t.innerHTML.length>24)return;const c=/url\("(.+?)"\)/gi,a=c.exec(r);c.lastIndex=0;const l=a&&a[1];if(!l)return;const s=document.createElement("img");s.src=l;const p=t.parentNode;p&&p.replaceChild(s,t)}function n(){const e=document.createElement("div");e.style.position="absolute",e.style.left="-2000px",e.style.zIndex="-2000",e.innerHTML=document.body.innerHTML,document.documentElement.appendChild(e),Array.from(e.getElementsByTagName("*")).forEach(t);const n=`${document.head.innerHTML}${e.innerHTML}`;return e.remove(),n}function o(){const e=document.querySelectorAll(".webext-omnivore-backdrop");for(let t=0;t{for(let t=0;t{if(t!==ACTIONS.AddIframeContent)return;const{url:i,content:c}=n;e[i]=c,r({})})),window.prepareContent=async function(){const e=await async function(){const e=".pdf"===window.location.pathname.slice(-4).toLowerCase(),t=-1!==["application/acrobat","application/pdf","application/x-pdf","applications/vnd.pdf","text/pdf","text/x-pdf"].indexOf(document.contentType);if(!e&&!t)return Promise.resolve(null);const n=document.querySelector("embed");return n&&"application/pdf"!==n.type?Promise.resolve(null):ENV_DOES_NOT_SUPPORT_BLOB_URL_ACCESS&&n.src?Promise.resolve({type:"url",uploadContentObjUrl:n.src}):new Promise(((e,t)=>{const n=new XMLHttpRequest;n.open("GET","",!0),n.responseType="blob",n.onload=function(n){200===this.status?e({type:"pdf",uploadContentObjUrl:URL.createObjectURL(this.response)}):t(n)},n.send()}))}();if(e)return e;const t=window.location.href;try{if(handleBackendUrl(t))return{type:"url"}}catch{console.log("error checking url")}return await async function(e){const t=document.scrollingElement||document.body,n=t.scrollTop,r=t.scrollHeight;o();const i=function(){const e=document.createElement("div");return e.className="webext-omnivore-backdrop",e.style.cssText="all: initial !important;\n position: fixed !important;\n top: 0 !important;\n right: 0 !important;\n bottom: 0 !important;\n left: 0 !important;\n z-index: 99999 !important;\n background: #fff !important;\n opacity: 0.8 !important;\n transition: opacity 0.3s !important;\n -webkit-backdrop-filter: blur(4px) !important;\n backdrop-filter: blur(4px) !important;\n ",e}();for(document.body.appendChild(i);t.scrollTop<=r-500&&window.location.href===e;){const e=t.scrollTop;if(t.scrollTop+=500,await new Promise((e=>{setTimeout(e,10)})),t.scrollTop===e)break}t.scrollTop=n,await new Promise((e=>{setTimeout(e,10)}))}(t),o(),{type:"html",content:n()}}}(); \ No newline at end of file diff --git a/apple/Sources/SafariExtension/Resources/scripts/content/toast.js b/apple/Sources/SafariExtension/Resources/scripts/content/toast.js index 000920040..72178c056 100644 --- a/apple/Sources/SafariExtension/Resources/scripts/content/toast.js +++ b/apple/Sources/SafariExtension/Resources/scripts/content/toast.js @@ -1 +1 @@ -!function(){let e,t,o,n=[],a=!1;const r={spinner:'\n \n \n \n \n \n \n \n \n \n ',success:'\n \n \n \n ',failure:'\n \n \n \n ',close:'',animatedLoader:'\n \n
\n '};function s(){a=!0,o&&clearTimeout(o)}function i(o){if(e)switch(o.ctx&&(t={...t,...o.ctx}),o.target){case"logged_out":s(),l("failure"),c("#omnivore-logged-out-row"),d("#omnivore-logged-out-status","empty","You are not logged in."),["#omnivore-toast-edit-title-btn","#omnivore-toast-edit-labels-btn","#omnivore-toast-read-now-btn","#omnivore-toast-add-note-btn","#omnivore-open-menu-btn"].forEach((t=>{e.shadowRoot.querySelector(t).disabled=!0}));break;case"page":l(o.status);break;case"note":d("#omnivore-add-note-status",o.status,o.message,"success"==o.status?2500:void 0),"success"==o.status&&setTimeout((()=>{c("#omnivore-add-note-status")}),3e3);break;case"title":d("#omnivore-edit-title-status",o.status,o.message,"success"==o.status?2500:void 0),"success"==o.status&&setTimeout((()=>{c("#omnivore-edit-title-status")}),3e3);break;case"labels":d("#omnivore-edit-labels-status",o.status,o.message,"success"==o.status?2500:void 0);break;case"extra":d("#omnivore-extra-status",o.status,o.message,"success"==o.status?2500:void 0),"success"==o.status&&setTimeout((()=>{e.remove()}),3e3)}else console.log("no statusBox to update")}function l(t){const n=e.shadowRoot.querySelector(".omnivore-toast-statusBox");switch(t){case"loading":n.innerHTML=r.animatedLoader;break;case"success":o=setTimeout((function(){console.log("hiding: ",e,a),a||(e.remove(),e=void 0)}),2500),n.innerHTML=r.success;break;case"failure":n.innerHTML=r.failure}}function d(t,o,n,a){const s=e.shadowRoot.querySelector(t),i=(()=>{switch(o){case"loading":return r.animatedLoader;case"success":return r.success;case"failure":return r.failure;case"none":return"";default:return}})();s.innerHTML=i?`${i}${n}`:n,a&&setTimeout((()=>{s.innerHTML=""}),a)}function c(t){console.log("currentToastEl: ",e);const o=e.shadowRoot.querySelector(t),n=o?.getAttribute("data-state");if(e.shadowRoot.querySelectorAll(".omnivore-toast-func-row").forEach((e=>{e.setAttribute("data-state","closed")})),o&&n){const e="open"===n?"closed":"open";o.setAttribute("data-state",e)}}function u(e,t){const o=document.createElement("button"),n=document.createElement("span");n.style="width:10px;height:10px;border-radius:1000px;",n.style.setProperty("background-color",e.color);const a=document.createElement("span");a.style="margin-left: 10px;pointer-events: none;",a.innerText=e.name;const r=document.createElement("span");return r.style="margin-left: auto;pointer-events: none;",r.className="checkbox",r.innerHTML='\n \n \n \n ',o.appendChild(n),o.appendChild(a),o.appendChild(r),o.onclick=m,o.onkeydown=p,o.setAttribute("data-label-id",e.id),o.setAttribute("data-label-idx",t),o.setAttribute("data-label-selected",e.selected?"on":"off"),o.setAttribute("tabIndex","-1"),o}function m(e){e.preventDefault();const t=e.target.getAttribute("data-label-id");f(e,t)}function f(e,t){const o=e.target.getAttribute("data-label-selected");if(!t||!o)return;const a="on"!=o;e.target.setAttribute("data-label-selected",a?"on":"off");const r=n.find((e=>e.id===t));r&&(r.selected=a)}function p(e){switch(e.key.toLowerCase()){case"arrowup":{if(e.target==e.target.form.querySelector("#omnivore-edit-label-text"))return;const t=e.target.getAttribute("data-label-idx");let o=t&&NaN!=Number(t)?Number(t)-1:0;if(e.target==e.target.form.querySelector("#omnivore-save-button")){const t=Math.max(...Array.from(e.target.form.querySelectorAll("button[data-label-idx]")).map((e=>Number(e.getAttribute("data-label-idx")))));NaN!=t&&(o=t)}const n=e.target.form.querySelector(`button[data-label-idx='${o}']`);n?n.focus():e.target.form.querySelector("#omnivore-edit-label-text")?.focus(),e.preventDefault();break}case"arrowdown":{const t=e.target.getAttribute("data-label-idx"),o=t&&NaN!=Number(t)?Number(t)+1:0,n=e.target.form.querySelector(`button[data-label-idx='${o}']`);n?n.focus():e.target.form.querySelector(".omnivore-save-button")?.focus(),e.preventDefault();break}case"enter":{const t=e.target.getAttribute("data-label-id");f(e,t),e.preventDefault();break}}}function b(){s(),c("#omnivore-add-note-row"),e.shadowRoot.querySelector("#omnivore-add-note-textarea")?.focus(),e.shadowRoot.querySelector("#omnivore-add-note-form").onsubmit=e=>{console.log("submitting form: ",e),d("#omnivore-add-note-status","loading","Adding note..."),browserApi.runtime.sendMessage({action:ACTIONS.AddNote,payload:{ctx:t,note:e.target.elements.title.value}}),e.preventDefault()}}function g(){console.log("editing title"),s(),c("#omnivore-edit-title-row"),e.shadowRoot.querySelector("#omnivore-edit-title-textarea")?.focus(),e.shadowRoot.querySelector("#omnivore-edit-title-form").onsubmit=e=>{d("#omnivore-edit-title-status","loading","Updating title..."),browserApi.runtime.sendMessage({action:ACTIONS.EditTitle,payload:{ctx:t,title:e.target.elements.title.value}}),e.preventDefault()}}async function v(){s(),await getStorageItem("labels").then((e=>{n=e})),c("#omnivore-edit-labels-row"),e.shadowRoot.querySelector("#omnivore-edit-label-text")?.focus();const o=e.shadowRoot.querySelector("#omnivore-edit-labels-list");e.shadowRoot.querySelector("#omnivore-edit-label-text").addEventListener("input",(function(){!async function(t){const o=e.shadowRoot.querySelector("#omnivore-edit-labels-list");o&&(o.innerHTML="",t?n.filter((e=>e.name.toLowerCase().indexOf(t.toLowerCase())>-1)).forEach((function(e,t){const n=u(e,t);o.appendChild(n)})):n.forEach((function(e,t){const n=u(e,t);o.appendChild(n)})))}(this.value)})),e.shadowRoot.querySelector("#omnivore-edit-label-text").onkeydown=p,o&&(o.innerHTML="",n.forEach((function(e,t){const n=u(e,t);o.appendChild(n)}))),e.shadowRoot.querySelector("#omnivore-edit-labels-form").onsubmit=o=>{o.preventDefault(),e.shadowRoot.querySelector("#omnivore-edit-labels-status").innerText="Updating labels...";const a=n.filter((e=>e.selected)).map((e=>e.id));browserApi.runtime.sendMessage({action:ACTIONS.SetLabels,payload:{ctx:t,labelIds:a}})}}function w(){s(),e.shadowRoot.querySelector("#omnivore-toast-container").setAttribute("data-state","open"),t&&t.readerURL?window.open(t.readerURL,"_blank"):t?window.open(new URL(`/article?url=${encodeURI(t.originalURL)}`,t.omnivoreURL),"_blank"):alert("Error no URL found."),setTimeout((()=>{L()}),1e3)}function h(e){browserApi.runtime.sendMessage({action:ACTIONS.Archive,payload:{ctx:t}}),e.preventDefault()}function C(e){browserApi.runtime.sendMessage({action:ACTIONS.Delete,payload:{ctx:t}}),e.preventDefault()}function y(){s(),c("#omnivore-extra-buttons-row")}function L(){e.remove(),e=void 0}function x(){window.open(new URL("/login",t.omnivoreURL),"_blank"),setTimeout(L,2e3)}window.showToolbar=function(o){t=o.ctx,async function(t){document.body&&(e||(e=await async function(){const e=await fetch(browserApi.runtime.getURL("views/toast.html")),t=await e.text(),o=document.createElement("div");o.attachShadow({mode:"open"}),o.shadowRoot&&(o.shadowRoot.innerHTML="");const n=document.createElement("div");return n.id="#omnivore-toast",n.innerHTML=t,o.shadowRoot.appendChild(n),document.body.appendChild(o),function(e){const t=[{id:"#omnivore-toast-add-note-btn",func:b},{id:"#omnivore-toast-edit-title-btn",func:g},{id:"#omnivore-toast-edit-labels-btn",func:v},{id:"#omnivore-toast-read-now-btn",func:w},{id:"#omnivore-open-menu-btn",func:y},{id:"#omnivore-toast-close-btn",func:L},{id:"#omnivore-toast-login-btn",func:x},{id:"#omnivore-toast-archive-btn",func:h},{id:"#omnivore-toast-delete-btn",func:C}];for(const o of t){const t=e.shadowRoot.querySelector(o.id);t&&t.addEventListener("click",o.func)}if(window.matchMedia("(max-width: 500px)").matches){e.shadowRoot.querySelectorAll(".omnivore-top-button-label").forEach((e=>{e.style.display="none"}));const t=e.shadowRoot.querySelector("#omnivore-toast-container");t.style.width="280px",t.style.top="unset",t.style.bottom="20px"}}(o),o}()),"loading"===t.type&&i({status:"loading",target:"page"}),document.querySelectorAll("#omnivore-toast").forEach((t=>{t!==e&&(console.log("removing current toast el: ",e),t.remove())})))}(o).catch((e=>console.log("error showing toast",e)))},window.updateStatus=i,window.updateLabelsFromCache=function(e){(async()=>{await getStorageItem("labels").then((e=>{n=e,console.log(" == updated labels",e)}))})()}}(); \ No newline at end of file +!function(){let e,t,o,n=[],a=!1;const r={spinner:'\n \n \n \n \n \n \n \n \n \n ',success:'\n \n \n \n ',failure:'\n \n \n \n ',close:'',animatedLoader:'\n \n
\n '};function s(){a=!0,o&&clearTimeout(o)}function i(o){if(e)switch(o.ctx&&(t={...t,...o.ctx}),o.target){case"logged_out":s(),l("failure"),c("#omnivore-logged-out-row"),d("#omnivore-logged-out-status","empty","You are not logged in."),["#omnivore-toast-edit-title-btn","#omnivore-toast-edit-labels-btn","#omnivore-toast-read-now-btn","#omnivore-toast-add-note-btn","#omnivore-open-menu-btn"].forEach((t=>{e.shadowRoot.querySelector(t).disabled=!0}));break;case"page":l(o.status);break;case"note":d("#omnivore-add-note-status",o.status,o.message,"success"==o.status?2500:void 0),"success"==o.status&&setTimeout((()=>{c("#omnivore-add-note-status")}),3e3);break;case"title":d("#omnivore-edit-title-status",o.status,o.message,"success"==o.status?2500:void 0),"success"==o.status&&setTimeout((()=>{c("#omnivore-edit-title-status")}),3e3);break;case"labels":d("#omnivore-edit-labels-status",o.status,o.message,"success"==o.status?2500:void 0);break;case"extra":d("#omnivore-extra-status",o.status,o.message,"success"==o.status?2500:void 0),"success"==o.status&&setTimeout((()=>{e.remove()}),3e3)}else console.log("no statusBox to update")}function l(t){const n=e.shadowRoot.querySelector(".omnivore-toast-statusBox");switch(t){case"loading":n.innerHTML=r.animatedLoader;break;case"success":o=setTimeout((function(){console.log("hiding: ",e,a),a||(e.remove(),e=void 0)}),2500),n.innerHTML=r.success;break;case"failure":n.innerHTML=r.failure}}function d(t,o,n,a){const s=e.shadowRoot.querySelector(t),i=(()=>{switch(o){case"loading":return r.animatedLoader;case"success":return r.success;case"failure":return r.failure;case"none":return"";default:return}})();s.innerHTML=i?`${i}${n}`:n,a&&setTimeout((()=>{s.innerHTML=""}),a)}function c(t){console.log("currentToastEl: ",e);const o=e.shadowRoot.querySelector(t),n=o?.getAttribute("data-state");if(e.shadowRoot.querySelectorAll(".omnivore-toast-func-row").forEach((e=>{e.setAttribute("data-state","closed")})),o&&n){const e="open"===n?"closed":"open";o.setAttribute("data-state",e)}}function u(e,t){const o=document.createElement("button"),n=document.createElement("span");n.style="width:10px;height:10px;border-radius:1000px;",n.style.setProperty("background-color",e.color);const a=document.createElement("span");a.style="margin-left: 10px;pointer-events: none;",a.innerText=e.name;const r=document.createElement("span");return r.style="margin-left: auto;pointer-events: none;",r.className="checkbox",r.innerHTML='\n \n \n \n ',o.appendChild(n),o.appendChild(a),o.appendChild(r),o.onclick=m,o.onkeydown=f,o.setAttribute("data-label-id",e.id),o.setAttribute("data-label-idx",t),o.setAttribute("data-label-selected",e.selected?"on":"off"),o.setAttribute("tabIndex","-1"),o}function m(e){e.preventDefault();const t=e.target.getAttribute("data-label-id");p(e,t)}function p(e,t){const o=e.target.getAttribute("data-label-selected");if(!t||!o)return;const a="on"!=o;e.target.setAttribute("data-label-selected",a?"on":"off");const r=n.find((e=>e.id===t));r&&(r.selected=a)}function f(e){switch(e.cancelBubble=!0,e.stopPropogation&&e.stopPropogation(),e.key.toLowerCase()){case"arrowup":{if(e.target==e.target.form.querySelector("#omnivore-edit-label-text"))return;const t=e.target.getAttribute("data-label-idx");let o=t&&NaN!=Number(t)?Number(t)-1:0;if(e.target==e.target.form.querySelector("#omnivore-save-button")){const t=Math.max(...Array.from(e.target.form.querySelectorAll("button[data-label-idx]")).map((e=>Number(e.getAttribute("data-label-idx")))));NaN!=t&&(o=t)}const n=e.target.form.querySelector(`button[data-label-idx='${o}']`);n?n.focus():e.target.form.querySelector("#omnivore-edit-label-text")?.focus(),e.preventDefault();break}case"arrowdown":{const t=e.target.getAttribute("data-label-idx"),o=t&&NaN!=Number(t)?Number(t)+1:0,n=e.target.form.querySelector(`button[data-label-idx='${o}']`);n?n.focus():e.target.form.querySelector(".omnivore-save-button")?.focus(),e.preventDefault();break}case"enter":{const t=e.target.getAttribute("data-label-id");p(e,t),e.preventDefault();break}}}function b(){s(),c("#omnivore-add-note-row");const o=e.shadowRoot.querySelector("#omnivore-add-note-textarea");o&&(o.focus(),o.onkeydown=e=>{e.cancelBubble=!0,e.stopPropogation&&e.stopPropogation(),13==e.keyCode&&(e.metaKey||e.ctrlKey)&&(d("#omnivore-add-note-status","loading","Adding note..."),browserApi.runtime.sendMessage({action:ACTIONS.AddNote,payload:{ctx:t,note:o.value}}))}),e.shadowRoot.querySelector("#omnivore-add-note-form").onsubmit=e=>{console.log("submitting form: ",e),d("#omnivore-add-note-status","loading","Adding note..."),browserApi.runtime.sendMessage({action:ACTIONS.AddNote,payload:{ctx:t,note:e.target.elements.title.value}}),e.preventDefault(),e.stopPropogation()}}function g(){s(),c("#omnivore-edit-title-row");const o=e.shadowRoot.querySelector("#omnivore-edit-title-textarea");o&&(o.focus(),o.onkeydown=e=>{e.cancelBubble=!0,e.stopPropogation&&e.stopPropogation()}),e.shadowRoot.querySelector("#omnivore-edit-title-form").onsubmit=e=>{d("#omnivore-edit-title-status","loading","Updating title..."),browserApi.runtime.sendMessage({action:ACTIONS.EditTitle,payload:{ctx:t,title:e.target.elements.title.value}}),e.preventDefault()}}async function v(){s(),await getStorageItem("labels").then((e=>{n=e})),c("#omnivore-edit-labels-row"),e.shadowRoot.querySelector("#omnivore-edit-label-text")?.focus();const o=e.shadowRoot.querySelector("#omnivore-edit-labels-list");e.shadowRoot.querySelector("#omnivore-edit-label-text").addEventListener("input",(function(){!async function(t){const o=e.shadowRoot.querySelector("#omnivore-edit-labels-list");o&&(o.innerHTML="",t?n.filter((e=>e.name.toLowerCase().indexOf(t.toLowerCase())>-1)).forEach((function(e,t){const n=u(e,t);o.appendChild(n)})):n.forEach((function(e,t){const n=u(e,t);o.appendChild(n)})))}(this.value)})),e.shadowRoot.querySelector("#omnivore-edit-label-text").onkeydown=f,o&&(o.innerHTML="",n.forEach((function(e,t){const n=u(e,t);o.appendChild(n)}))),e.shadowRoot.querySelector("#omnivore-edit-labels-form").onsubmit=o=>{o.preventDefault(),e.shadowRoot.querySelector("#omnivore-edit-labels-status").innerText="Updating labels...";const a=n.filter((e=>e.selected)).map((e=>e.id));browserApi.runtime.sendMessage({action:ACTIONS.SetLabels,payload:{ctx:t,labelIds:a}})}}function w(){s(),e.shadowRoot.querySelector("#omnivore-toast-container").setAttribute("data-state","open"),t&&t.readerURL?window.open(t.readerURL,"_blank"):t?window.open(new URL(`/article?url=${encodeURI(t.originalURL)}`,t.omnivoreURL),"_blank"):alert("Error no URL found."),setTimeout((()=>{x()}),1e3)}function h(e){browserApi.runtime.sendMessage({action:ACTIONS.Archive,payload:{ctx:t}}),e.preventDefault()}function y(e){browserApi.runtime.sendMessage({action:ACTIONS.Delete,payload:{ctx:t}}),e.preventDefault()}function C(){s(),c("#omnivore-extra-buttons-row")}function x(){e.remove(),e=void 0}function L(){window.open(new URL("/login",t.omnivoreURL),"_blank"),setTimeout(x,2e3)}window.showToolbar=function(o){t=o.ctx,async function(t){document.body&&(e||(e=await async function(){const e=await fetch(browserApi.runtime.getURL("views/toast.html")),t=await e.text(),o=document.createElement("div");o.tabIndex=0,o.attachShadow({mode:"open"}),o.shadowRoot&&(o.shadowRoot.innerHTML="");const n=document.createElement("div");return n.id="#omnivore-toast",n.innerHTML=t,n.tabIndex=0,o.shadowRoot.appendChild(n),document.body.appendChild(o),function(e){const t=[{id:"#omnivore-toast-add-note-btn",func:b},{id:"#omnivore-toast-edit-title-btn",func:g},{id:"#omnivore-toast-edit-labels-btn",func:v},{id:"#omnivore-toast-read-now-btn",func:w},{id:"#omnivore-open-menu-btn",func:C},{id:"#omnivore-toast-close-btn",func:x},{id:"#omnivore-toast-login-btn",func:L},{id:"#omnivore-toast-archive-btn",func:h},{id:"#omnivore-toast-delete-btn",func:y}];for(const o of t){const t=e.shadowRoot.querySelector(o.id);t&&t.addEventListener("click",o.func)}if(window.matchMedia("(max-width: 500px)").matches){e.shadowRoot.querySelectorAll(".omnivore-top-button-label").forEach((e=>{e.style.display="none"}));const t=e.shadowRoot.querySelector("#omnivore-toast-container");t.style.width="280px",t.style.top="unset",t.style.bottom="20px"}}(o),function(e){e.addEventListener("keydown",(e=>{switch(e.key){case"r":w();break;case"l":v();break;case"m":C();break;case"t":g();break;case"n":e.preventDefault(),b()}e.cancelBubble=!0,e.stopPropogation&&e.stopPropogation()}))}(o),o}()),"loading"===t.type&&i({status:"loading",target:"page"}),document.querySelectorAll("#omnivore-toast").forEach((t=>{t!==e&&(console.log("removing current toast el: ",e),t.remove())})),e.focus())}(o).catch((e=>console.log("error showing toast",e)))},window.updateStatus=i,window.updateLabelsFromCache=function(e){(async()=>{await getStorageItem("labels").then((e=>{n=e,console.log(" == updated labels",e)}))})()}}(); \ No newline at end of file