Merge pull request #842 from omnivore-app/fix/mac-app-updates
Mac app updates
This commit is contained in:
@ -159,7 +159,7 @@
|
||||
"location" : "https://github.com/PSPDFKit/PSPDFKit-SP",
|
||||
"state" : {
|
||||
"branch" : "master",
|
||||
"revision" : "0e18629c443e3f39ecfee0f600d9ef5551ecf488"
|
||||
"revision" : "b7e5465ab62f5b48735145756e371502fa2a24f0"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@ -1,10 +1,3 @@
|
||||
//
|
||||
// File.swift
|
||||
//
|
||||
//
|
||||
// Created by Jackson Harper on 6/1/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Models
|
||||
import Services
|
||||
@ -18,23 +11,25 @@ class ExtensionSaveService {
|
||||
self.queue = OperationQueue()
|
||||
}
|
||||
|
||||
private func queueSaveOperation(
|
||||
_ pageScrape: PageScrapePayload,
|
||||
shareExtensionViewModel: ShareExtensionChildViewModel
|
||||
) {
|
||||
ProcessInfo().performExpiringActivity(withReason: "app.omnivore.SaveActivity") { [self] expiring in
|
||||
guard !expiring else {
|
||||
self.queue.cancelAllOperations()
|
||||
#if os(iOS)
|
||||
private func queueSaveOperation(
|
||||
_ pageScrape: PageScrapePayload,
|
||||
shareExtensionViewModel: ShareExtensionChildViewModel
|
||||
) {
|
||||
ProcessInfo().performExpiringActivity(withReason: "app.omnivore.SaveActivity") { [self] expiring in
|
||||
guard !expiring else {
|
||||
self.queue.cancelAllOperations()
|
||||
self.queue.waitUntilAllOperationsAreFinished()
|
||||
return
|
||||
}
|
||||
|
||||
let operation = SaveOperation(pageScrapePayload: pageScrape, shareExtensionViewModel: shareExtensionViewModel)
|
||||
|
||||
self.queue.addOperation(operation)
|
||||
self.queue.waitUntilAllOperationsAreFinished()
|
||||
return
|
||||
}
|
||||
|
||||
let operation = SaveOperation(pageScrapePayload: pageScrape, shareExtensionViewModel: shareExtensionViewModel)
|
||||
|
||||
self.queue.addOperation(operation)
|
||||
self.queue.waitUntilAllOperationsAreFinished()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public func save(_ extensionContext: NSExtensionContext, shareExtensionViewModel: ShareExtensionChildViewModel) {
|
||||
PageScraper.scrape(extensionContext: extensionContext) { [weak self] result in
|
||||
@ -71,7 +66,10 @@ class ExtensionSaveService {
|
||||
}
|
||||
}
|
||||
}
|
||||
self.queueSaveOperation(payload, shareExtensionViewModel: shareExtensionViewModel)
|
||||
#if os(iOS)
|
||||
// TODO: need alternative call for macos
|
||||
self.queueSaveOperation(payload, shareExtensionViewModel: shareExtensionViewModel)
|
||||
#endif
|
||||
case .failure:
|
||||
DispatchQueue.main.async {
|
||||
shareExtensionViewModel.status = .failed(error: .unknown(description: "Could not retrieve content"))
|
||||
|
||||
@ -66,24 +66,26 @@ struct LinkedItemTitleEditView: View {
|
||||
NavigationView {
|
||||
editForm
|
||||
.navigationTitle("Edit Title and Description")
|
||||
#if os(iOS)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .barTrailing) {
|
||||
Button(
|
||||
action: {
|
||||
viewModel.submit(dataService: dataService, item: item)
|
||||
presentationMode.wrappedValue.dismiss()
|
||||
},
|
||||
label: { Text("Save").foregroundColor(.appGrayTextContrast) }
|
||||
)
|
||||
}
|
||||
ToolbarItem(placement: .barLeading) {
|
||||
Button(
|
||||
action: { presentationMode.wrappedValue.dismiss() },
|
||||
label: { Text("Cancel").foregroundColor(.appGrayTextContrast) }
|
||||
)
|
||||
}
|
||||
#endif
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .barTrailing) {
|
||||
Button(
|
||||
action: {
|
||||
viewModel.submit(dataService: dataService, item: item)
|
||||
presentationMode.wrappedValue.dismiss()
|
||||
},
|
||||
label: { Text("Save").foregroundColor(.appGrayTextContrast) }
|
||||
)
|
||||
}
|
||||
ToolbarItem(placement: .barLeading) {
|
||||
Button(
|
||||
action: { presentationMode.wrappedValue.dismiss() },
|
||||
label: { Text("Cancel").foregroundColor(.appGrayTextContrast) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
.task { viewModel.load(item: item) }
|
||||
}
|
||||
|
||||
@ -59,8 +59,7 @@ import Views
|
||||
}
|
||||
|
||||
func handleGoogleAuth(authenticator: Authenticator) async {
|
||||
guard let presentingViewController = presentingViewController() else { return }
|
||||
let googleAuthResponse = await authenticator.handleGoogleAuth(presenting: presentingViewController)
|
||||
let googleAuthResponse = await authenticator.handleGoogleAuth()
|
||||
|
||||
switch googleAuthResponse {
|
||||
case let .loginError(error):
|
||||
@ -72,15 +71,3 @@ import Views
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func presentingViewController() -> PlatformViewController? {
|
||||
#if os(iOS)
|
||||
let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene
|
||||
return scene?.windows
|
||||
.filter(\.isKeyWindow)
|
||||
.first?
|
||||
.rootViewController
|
||||
#elseif os(macOS)
|
||||
return nil
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -14,9 +14,14 @@ public class PersistentContainer: NSPersistentContainer {
|
||||
|
||||
// Store the sqlite file in the app group container.
|
||||
// This allows shared access for app and app extensions.
|
||||
let appGroupID = "group.app.omnivoreapp"
|
||||
#if os(iOS)
|
||||
let appGroupID = "group.app.omnivoreapp"
|
||||
#else
|
||||
let appGroupID = "QJF2XZ86HB.app.omnivore.app"
|
||||
#endif
|
||||
let appGroupContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupID)
|
||||
let appGroupContainerURL = appGroupContainer?.appendingPathComponent("store.sqlite")
|
||||
|
||||
container.persistentStoreDescriptions.first!.url = appGroupContainerURL
|
||||
|
||||
container.viewContext.automaticallyMergesChangesFromParent = true
|
||||
|
||||
@ -10,8 +10,11 @@ public enum GoogleAuthResponse {
|
||||
}
|
||||
|
||||
extension Authenticator {
|
||||
public func handleGoogleAuth(presenting: PlatformViewController) async -> GoogleAuthResponse {
|
||||
let idToken = try? await googleSignIn(presenting: presenting)
|
||||
public func handleGoogleAuth() async -> GoogleAuthResponse {
|
||||
let idToken = await withCheckedContinuation { continuation in
|
||||
googleSignIn { continuation.resume(returning: $0) }
|
||||
}
|
||||
|
||||
guard let idToken = idToken else { return .loginError(error: .unauthorized) }
|
||||
|
||||
do {
|
||||
@ -47,27 +50,47 @@ extension Authenticator {
|
||||
}
|
||||
}
|
||||
|
||||
func googleSignIn(presenting: PlatformViewController) async throws -> String {
|
||||
try await withCheckedThrowingContinuation { continuation in
|
||||
let clientID = "\(AppKeys.sharedInstance?.iosClientGoogleId ?? "").apps.googleusercontent.com"
|
||||
GIDSignIn.sharedInstance.signIn(
|
||||
with: GIDConfiguration(clientID: clientID),
|
||||
presenting: presenting
|
||||
) { user, error in
|
||||
guard let user = user, error == nil else {
|
||||
continuation.resume(throwing: LoginError.unauthorized)
|
||||
func googleSignIn(completion: @escaping (String?) -> Void) {
|
||||
#if os(iOS)
|
||||
let presenting = presentingViewController()
|
||||
#else
|
||||
let presenting = NSApplication.shared.windows.first
|
||||
#endif
|
||||
|
||||
guard let presenting = presenting else {
|
||||
completion(nil)
|
||||
return
|
||||
}
|
||||
let clientID = "\(AppKeys.sharedInstance?.iosClientGoogleId ?? "").apps.googleusercontent.com"
|
||||
|
||||
GIDSignIn.sharedInstance.signIn(
|
||||
with: GIDConfiguration(clientID: clientID),
|
||||
presenting: presenting
|
||||
) { user, error in
|
||||
guard let user = user, error == nil else {
|
||||
completion(nil)
|
||||
return
|
||||
}
|
||||
|
||||
user.authentication.do { authentication, error in
|
||||
guard let idToken = authentication?.idToken, error == nil else {
|
||||
completion(nil)
|
||||
return
|
||||
}
|
||||
|
||||
user.authentication.do { authentication, error in
|
||||
guard let idToken = authentication?.idToken, error == nil else {
|
||||
continuation.resume(throwing: LoginError.unauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
continuation.resume(returning: idToken)
|
||||
}
|
||||
completion(idToken)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func presentingViewController() -> PlatformViewController? {
|
||||
#if os(iOS)
|
||||
let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene
|
||||
return scene?.windows
|
||||
.filter(\.isKeyWindow)
|
||||
.first?
|
||||
.rootViewController
|
||||
#elseif os(macOS)
|
||||
return nil
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -4,9 +4,14 @@ import Foundation
|
||||
import Models
|
||||
import OSLog
|
||||
import QuickLookThumbnailing
|
||||
import UIKit
|
||||
import Utils
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
#else
|
||||
import AppKit
|
||||
#endif
|
||||
|
||||
let logger = Logger(subsystem: "app.omnivore", category: "data-service")
|
||||
|
||||
public final class DataService: ObservableObject {
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
import CoreImage
|
||||
import Foundation
|
||||
import QuickLookThumbnailing
|
||||
import UIKit
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
#else
|
||||
import AppKit
|
||||
#endif
|
||||
|
||||
public enum PDFUtils {
|
||||
public static func copyToLocal(url: URL) throws -> String {
|
||||
@ -64,7 +68,11 @@ public enum PDFUtils {
|
||||
|
||||
public static func createThumbnailFor(inputUrl: URL) async throws -> URL? {
|
||||
let size = CGSize(width: 80, height: 80)
|
||||
let scale = await UIScreen.main.scale
|
||||
#if os(iOS)
|
||||
let scale = await UIScreen.main.scale
|
||||
#else
|
||||
let scale = NSScreen.main?.backingScaleFactor ?? 1
|
||||
#endif
|
||||
let outputUrl = thumbnailUrl(localUrl: inputUrl)
|
||||
|
||||
// Create the thumbnail request.
|
||||
|
||||
@ -168,14 +168,19 @@ public enum WebViewManager {
|
||||
context.coordinator.needsReload = false
|
||||
}
|
||||
|
||||
if annotationSaveTransactionID != context.coordinator.lastSavedAnnotationID {
|
||||
context.coordinator.lastSavedAnnotationID = annotationSaveTransactionID
|
||||
(webView as? WebView)?.dispatchEvent(.saveAnnotation(annotation: annotation))
|
||||
}
|
||||
|
||||
if sendIncreaseFontSignal {
|
||||
sendIncreaseFontSignal = false
|
||||
(webView as? WebView)?.increaseFontSize()
|
||||
(webView as? WebView)?.updateFontSize()
|
||||
}
|
||||
|
||||
if sendDecreaseFontSignal {
|
||||
sendDecreaseFontSignal = false
|
||||
(webView as? WebView)?.decreaseFontSize()
|
||||
(webView as? WebView)?.updateFontSize()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,9 +86,9 @@ public final class WebView: WKWebView {
|
||||
super.viewDidChangeEffectiveAppearance()
|
||||
switch effectiveAppearance.bestMatch(from: [.aqua, .darkAqua]) {
|
||||
case .some(.darkAqua):
|
||||
dispatchEvent("switchToDarkMode")
|
||||
dispatchEvent(.updateColorMode(isDark: true))
|
||||
default:
|
||||
dispatchEvent("switchToLightMode")
|
||||
dispatchEvent(.updateColorMode(isDark: false))
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -81,7 +81,9 @@ public struct WebPreferencesPopoverView: View {
|
||||
}
|
||||
}
|
||||
.listStyle(.plain)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
#if os(iOS)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
#endif
|
||||
.navigationTitle("Reader Font")
|
||||
}
|
||||
|
||||
@ -148,9 +150,11 @@ public struct WebPreferencesPopoverView: View {
|
||||
}
|
||||
.padding()
|
||||
.navigationTitle("Reader Preferences")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
#if os(iOS)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
#endif
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
ToolbarItem(placement: .barTrailing) {
|
||||
Button(
|
||||
action: dismissAction,
|
||||
label: { Text("Done").foregroundColor(.appGrayTextContrast).padding() }
|
||||
@ -158,7 +162,9 @@ public struct WebPreferencesPopoverView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationViewStyle(.stack)
|
||||
#if os(iOS)
|
||||
.navigationViewStyle(.stack)
|
||||
#endif
|
||||
.accentColor(.appGrayTextContrast)
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,36 +37,6 @@ public enum ShareExtensionStatus {
|
||||
}
|
||||
}
|
||||
|
||||
struct CornerRadiusStyle: ViewModifier {
|
||||
var radius: CGFloat
|
||||
var corners: UIRectCorner
|
||||
|
||||
struct CornerRadiusShape: Shape {
|
||||
var radius = CGFloat.infinity
|
||||
var corners = UIRectCorner.allCorners
|
||||
|
||||
func path(in rect: CGRect) -> Path {
|
||||
let path = UIBezierPath(
|
||||
roundedRect: rect,
|
||||
byRoundingCorners: corners,
|
||||
cornerRadii: CGSize(width: radius, height: radius)
|
||||
)
|
||||
return Path(path.cgPath)
|
||||
}
|
||||
}
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.clipShape(CornerRadiusShape(radius: radius, corners: corners))
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View {
|
||||
ModifiedContent(content: self, modifier: CornerRadiusStyle(radius: radius, corners: corners))
|
||||
}
|
||||
}
|
||||
|
||||
private extension SaveArticleError {
|
||||
var displayMessage: String {
|
||||
switch self {
|
||||
@ -197,9 +167,15 @@ public struct ShareExtensionChildView: View {
|
||||
}
|
||||
|
||||
private func localImage(from url: URL) -> Image? {
|
||||
if let data = try? Data(contentsOf: url), let img = UIImage(data: data) {
|
||||
return Image(uiImage: img)
|
||||
}
|
||||
#if os(iOS)
|
||||
if let data = try? Data(contentsOf: url), let img = UIImage(data: data) {
|
||||
return Image(uiImage: img)
|
||||
}
|
||||
#else
|
||||
if let data = try? Data(contentsOf: url), let img = NSImage(data: data) {
|
||||
return Image(nsImage: img)
|
||||
}
|
||||
#endif
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user