From fdcbc4563f5e96fa4a8bd1d95edb47396b57b45c Mon Sep 17 00:00:00 2001 From: Chen-I Lim <46905241+chenilim@users.noreply.github.com> Date: Mon, 14 Feb 2022 19:57:44 -0800 Subject: [PATCH] Fix #2333. Handle export in Mac App (#2334) --- mac/Focalboard.xcodeproj/project.pbxproj | 8 +- mac/Focalboard/DownloadHandler.swift | 40 ++++++++++ mac/Focalboard/ViewController.swift | 93 ++++++------------------ 3 files changed, 70 insertions(+), 71 deletions(-) create mode 100644 mac/Focalboard/DownloadHandler.swift diff --git a/mac/Focalboard.xcodeproj/project.pbxproj b/mac/Focalboard.xcodeproj/project.pbxproj index 4adf28c4ea7..8ad05f0a9d6 100644 --- a/mac/Focalboard.xcodeproj/project.pbxproj +++ b/mac/Focalboard.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 8014951C261598D600A51700 /* PortUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8014951B261598D600A51700 /* PortUtils.swift */; }; 804E57FC27441B6B008526F0 /* whatsnew.txt in Resources */ = {isa = PBXBuildFile; fileRef = 804E57FB27441B6B008526F0 /* whatsnew.txt */; }; + 80672A8B27BAFEBA00257B8C /* DownloadHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80672A8A27BAFEBA00257B8C /* DownloadHandler.swift */; }; 80D6DEBB252E13CB00AEED9E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80D6DEBA252E13CB00AEED9E /* AppDelegate.swift */; }; 80D6DEBD252E13CB00AEED9E /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80D6DEBC252E13CB00AEED9E /* ViewController.swift */; }; 80D6DEBF252E13CD00AEED9E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 80D6DEBE252E13CD00AEED9E /* Assets.xcassets */; }; @@ -42,6 +43,7 @@ /* Begin PBXFileReference section */ 8014951B261598D600A51700 /* PortUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PortUtils.swift; sourceTree = ""; }; 804E57FB27441B6B008526F0 /* whatsnew.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = whatsnew.txt; sourceTree = ""; }; + 80672A8A27BAFEBA00257B8C /* DownloadHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadHandler.swift; sourceTree = ""; }; 80D6DEB7252E13CB00AEED9E /* Focalboard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Focalboard.app; sourceTree = BUILT_PRODUCTS_DIR; }; 80D6DEBA252E13CB00AEED9E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 80D6DEBC252E13CB00AEED9E /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; @@ -116,6 +118,7 @@ 80F8BF572624EB0C00FF3943 /* Globals.swift */, 8014951B261598D600A51700 /* PortUtils.swift */, 80D6DEBC252E13CB00AEED9E /* ViewController.swift */, + 80672A8A27BAFEBA00257B8C /* DownloadHandler.swift */, 80F174B62788C1A2000A9EEA /* CustomWKWebView.swift */, 80F8BF4F2624E1BB00FF3943 /* WhatsNewViewController.swift */, 804E57FB27441B6B008526F0 /* whatsnew.txt */, @@ -303,6 +306,7 @@ 80F8BF502624E1BB00FF3943 /* WhatsNewViewController.swift in Sources */, 80F174B72788C1A2000A9EEA /* CustomWKWebView.swift in Sources */, 80F8BF582624EB0C00FF3943 /* Globals.swift in Sources */, + 80672A8B27BAFEBA00257B8C /* DownloadHandler.swift in Sources */, 80D6DF18252F9BDE00AEED9E /* AutoSaveWindowController.swift in Sources */, 80D6DEBD252E13CB00AEED9E /* ViewController.swift in Sources */, 80D6DEBB252E13CB00AEED9E /* AppDelegate.swift in Sources */, @@ -403,7 +407,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 11.3; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -458,7 +462,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 11.3; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = macosx; diff --git a/mac/Focalboard/DownloadHandler.swift b/mac/Focalboard/DownloadHandler.swift new file mode 100644 index 00000000000..7b5afe03341 --- /dev/null +++ b/mac/Focalboard/DownloadHandler.swift @@ -0,0 +1,40 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import Foundation +import WebKit + +class DownloadHandler: NSObject, WKDownloadDelegate { + func download(_ download: WKDownload, decideDestinationUsing response: URLResponse, suggestedFilename: String, completionHandler: @escaping (URL?) -> Void) { + DispatchQueue.main.async { + // Let user select location of file + let savePanel = NSSavePanel() + savePanel.canCreateDirectories = true + savePanel.nameFieldStringValue = suggestedFilename + // BUGBUG: Specifying the allowedFileTypes causes Catalina to hang / error out + //savePanel.allowedFileTypes = [".boardsarchive"] + savePanel.begin { (result) in + if result.rawValue == NSApplication.ModalResponse.OK.rawValue, + let fileUrl = savePanel.url { + if (FileManager.default.fileExists(atPath: fileUrl.path)) { + // HACKHACK: WKWebView doesn't appear to overwrite files, so delete exsiting files first + do { + try FileManager.default.removeItem(at: fileUrl) + } catch { + let alert = NSAlert() + alert.messageText = "Unable to replace \(fileUrl.path)" + alert.addButton(withTitle: "OK") + alert.alertStyle = .warning + alert.runModal() + } + } + completionHandler(fileUrl) + } + } + } + } + + func downloadDidFinish(_ download: WKDownload) { + NSLog("downloadDidFinish") + } +} diff --git a/mac/Focalboard/ViewController.swift b/mac/Focalboard/ViewController.swift index a35685c3ed9..0c6f98c4c99 100644 --- a/mac/Focalboard/ViewController.swift +++ b/mac/Focalboard/ViewController.swift @@ -12,6 +12,7 @@ class ViewController: @IBOutlet var webView: CustomWKWebView! private var didLoad = false private var refreshWebViewOnLoad = true + private let downloadHandler = DownloadHandler() override func viewDidLoad() { super.viewDidLoad() @@ -100,60 +101,6 @@ class ViewController: webView.load(request) } - private func downloadJsonUrl(_ url: URL) { - NSLog("downloadJsonUrl") - let prefix = "data:text/json," - let urlString = url.absoluteString - let encodedJson = String(urlString[urlString.index(urlString.startIndex, offsetBy: prefix.lengthOfBytes(using: .utf8))...]) - guard let json = encodedJson.removingPercentEncoding else { - return - } - - // Form file name - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "yyyy-M-d" - let dateString = dateFormatter.string(from: Date()) - let filename = "archive-\(dateString).focalboard" - - // Save file - let savePanel = NSSavePanel() - savePanel.canCreateDirectories = true - savePanel.nameFieldStringValue = filename - // BUGBUG: Specifying the allowedFileTypes causes Catalina to hang / error out - //savePanel.allowedFileTypes = [".focalboard"] - savePanel.begin { (result) in - if result.rawValue == NSApplication.ModalResponse.OK.rawValue, - let fileUrl = savePanel.url { - try? json.write(to: fileUrl, atomically: true, encoding: .utf8) - } - } - } - - private func downloadCsvUrl(_ url: URL) { - NSLog("downloadCsvUrl") - let prefix = "data:text/csv;charset=utf-8," - let urlString = url.absoluteString - let encodedContents = String(urlString[urlString.index(urlString.startIndex, offsetBy: prefix.lengthOfBytes(using: .utf8))...]) - guard let contents = encodedContents.removingPercentEncoding else { - return - } - - let filename = "data.csv" - - // Save file - let savePanel = NSSavePanel() - savePanel.canCreateDirectories = true - savePanel.nameFieldStringValue = filename - // BUGBUG: Specifying the allowedFileTypes causes Catalina to hang / error out - //savePanel.allowedFileTypes = [".focalboard"] - savePanel.begin { (result) in - if result.rawValue == NSApplication.ModalResponse.OK.rawValue, - let fileUrl = savePanel.url { - try? contents.write(to: fileUrl, atomically: true, encoding: .utf8) - } - } - } - func webView(_ webView: WKWebView, runOpenPanelWith parameters: WKOpenPanelParameters, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping ([URL]?) -> Void) { NSLog("webView runOpenPanel") let openPanel = NSOpenPanel() @@ -169,22 +116,30 @@ class ViewController: } } - func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { - if let url = navigationAction.request.url { - // Intercept archive downloads, and present native UI - if (url.absoluteString.hasPrefix("data:text/json,")) { - decisionHandler(.cancel) - downloadJsonUrl(url) - return - } - if (url.absoluteString.hasPrefix("data:text/csv;charset=utf-8,")) { - decisionHandler(.cancel) - downloadCsvUrl(url) - return - } - NSLog("decidePolicyFor navigationAction: \(url.absoluteString)") + // Handle downloads + + func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, preferences: WKWebpagePreferences, decisionHandler: @escaping (WKNavigationActionPolicy, WKWebpagePreferences) -> Void) { + if navigationAction.shouldPerformDownload { + decisionHandler(.download, preferences) + } else { + decisionHandler(.allow, preferences) } - decisionHandler(.allow) + } + + func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) { + if navigationResponse.canShowMIMEType { + decisionHandler(.allow) + } else { + decisionHandler(.download) + } + } + + func webView(_ webView: WKWebView, navigationAction: WKNavigationAction, didBecome download: WKDownload) { + download.delegate = downloadHandler + } + + func webView(_ webView: WKWebView, navigationResponse: WKNavigationResponse, didBecome download: WKDownload) { + download.delegate = downloadHandler } func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {