mirror of
https://github.com/logseq/logseq.git
synced 2026-04-24 22:25:01 +00:00
fix(iOS): failed to share screenshot
related to https://github.com/logseq/db-test/issues/594
This commit is contained in:
@@ -50,6 +50,21 @@ class ShareViewController: UIViewController {
|
|||||||
return copyFileUrl
|
return copyFileUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func saveData(_ data: Data, fileExtension: String) -> URL? {
|
||||||
|
let dateFormatter = DateFormatter()
|
||||||
|
dateFormatter.dateFormat = "yyyy-MM-dd-HH-mm-ss"
|
||||||
|
let filename = dateFormatter.string(from: Date()) + "." + fileExtension
|
||||||
|
|
||||||
|
let copyFileUrl = groupContainerUrl!.appendingPathComponent(filename)
|
||||||
|
do {
|
||||||
|
try data.write(to: copyFileUrl)
|
||||||
|
return copyFileUrl
|
||||||
|
} catch {
|
||||||
|
print(error.localizedDescription)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Screenshots, shared images from some system App are passed as UIImage
|
// Screenshots, shared images from some system App are passed as UIImage
|
||||||
func saveUIImage(_ image: UIImage) -> URL? {
|
func saveUIImage(_ image: UIImage) -> URL? {
|
||||||
let dateFormatter = DateFormatter()
|
let dateFormatter = DateFormatter()
|
||||||
@@ -89,6 +104,28 @@ class ShareViewController: UIViewController {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Some shares (notably system screenshots) can come through as a file URL item.
|
||||||
|
fileprivate func handleTypeFileUrl(_ attachment: NSItemProvider)
|
||||||
|
async throws -> SharedResource
|
||||||
|
{
|
||||||
|
let results = try await attachment.loadItem(forTypeIdentifier: kUTTypeFileURL as String, options: nil)
|
||||||
|
let url = results as! URL?
|
||||||
|
|
||||||
|
var res = SharedResource()
|
||||||
|
|
||||||
|
if let url, url.isFileURL {
|
||||||
|
res.name = url.lastPathComponent
|
||||||
|
res.ext = url.pathExtension.lowercased()
|
||||||
|
res.type = url.pathExtensionAsMimeType()
|
||||||
|
res.url = createSharedFileUrl(url)
|
||||||
|
} else if let url {
|
||||||
|
res.name = url.absoluteString
|
||||||
|
res.type = "text/plain"
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
fileprivate func handleTypeText(_ attachment: NSItemProvider)
|
fileprivate func handleTypeText(_ attachment: NSItemProvider)
|
||||||
async throws -> SharedResource?
|
async throws -> SharedResource?
|
||||||
{
|
{
|
||||||
@@ -127,6 +164,22 @@ class ShareViewController: UIViewController {
|
|||||||
res.ext = "png"
|
res.ext = "png"
|
||||||
res.name = res.url?.lastPathComponent
|
res.name = res.url?.lastPathComponent
|
||||||
res.type = res.url?.pathExtensionAsMimeType()
|
res.type = res.url?.pathExtensionAsMimeType()
|
||||||
|
case let data as Data:
|
||||||
|
let ext: String
|
||||||
|
if attachment.hasItemConformingToTypeIdentifier(UTType.png.identifier) {
|
||||||
|
ext = "png"
|
||||||
|
} else if attachment.hasItemConformingToTypeIdentifier(UTType.jpeg.identifier) {
|
||||||
|
ext = "jpg"
|
||||||
|
} else if attachment.hasItemConformingToTypeIdentifier(UTType.heic.identifier) {
|
||||||
|
ext = "heic"
|
||||||
|
} else {
|
||||||
|
ext = "png"
|
||||||
|
}
|
||||||
|
|
||||||
|
res.url = self.saveData(data, fileExtension: ext)
|
||||||
|
res.ext = ext
|
||||||
|
res.name = res.url?.lastPathComponent
|
||||||
|
res.type = res.url?.pathExtensionAsMimeType()
|
||||||
case let url as URL:
|
case let url as URL:
|
||||||
res.name = url.lastPathComponent
|
res.name = url.lastPathComponent
|
||||||
res.ext = url.pathExtension.lowercased()
|
res.ext = url.pathExtension.lowercased()
|
||||||
@@ -155,6 +208,10 @@ class ShareViewController: UIViewController {
|
|||||||
taskGroup.addTask {
|
taskGroup.addTask {
|
||||||
return try await self.handleTypeUrl(attachment)
|
return try await self.handleTypeUrl(attachment)
|
||||||
}
|
}
|
||||||
|
} else if attachment.hasItemConformingToTypeIdentifier(kUTTypeFileURL as String) {
|
||||||
|
taskGroup.addTask {
|
||||||
|
return try await self.handleTypeFileUrl(attachment)
|
||||||
|
}
|
||||||
} else if attachment.hasItemConformingToTypeIdentifier(kUTTypeText as String) {
|
} else if attachment.hasItemConformingToTypeIdentifier(kUTTypeText as String) {
|
||||||
taskGroup.addTask {
|
taskGroup.addTask {
|
||||||
return try await self.handleTypeText(attachment)
|
return try await self.handleTypeText(attachment)
|
||||||
@@ -167,6 +224,9 @@ class ShareViewController: UIViewController {
|
|||||||
taskGroup.addTask {
|
taskGroup.addTask {
|
||||||
return try await self.handleTypeImage(attachment)
|
return try await self.handleTypeImage(attachment)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Useful for diagnosing shares that don't match the legacy kUTType checks.
|
||||||
|
print("Unhandled attachment types:", attachment.registeredTypeIdentifiers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,35 @@
|
|||||||
[logseq.common.util :as common-util]
|
[logseq.common.util :as common-util]
|
||||||
[promesa.core :as p]))
|
[promesa.core :as p]))
|
||||||
|
|
||||||
|
(defn- normalize-native-file-path
|
||||||
|
"Normalize iOS shared file URLs to paths that Capacitor Filesystem can read.
|
||||||
|
iOS share extensions commonly provide `file://` URLs."
|
||||||
|
[url]
|
||||||
|
(let [url (some-> url common-util/safe-decode-uri-component)]
|
||||||
|
(cond
|
||||||
|
(string/blank? url)
|
||||||
|
url
|
||||||
|
|
||||||
|
(string/starts-with? url "file://")
|
||||||
|
(subs url (count "file://"))
|
||||||
|
|
||||||
|
;; Some Capacitor APIs may provide `_capacitor_file_` URLs.
|
||||||
|
(string/starts-with? url "capacitor://localhost/_capacitor_file_")
|
||||||
|
(string/replace url "capacitor://localhost/_capacitor_file_" "")
|
||||||
|
|
||||||
|
:else
|
||||||
|
url)))
|
||||||
|
|
||||||
|
(defn- <filesystem-read-file
|
||||||
|
[url]
|
||||||
|
(let [path (normalize-native-file-path url)]
|
||||||
|
(-> (.readFile Filesystem #js {:path path})
|
||||||
|
(p/catch (fn [error]
|
||||||
|
;; Fallback to the original string for older plugin versions.
|
||||||
|
(if (= path url)
|
||||||
|
(p/rejected error)
|
||||||
|
(.readFile Filesystem #js {:path url})))))))
|
||||||
|
|
||||||
(defn open-or-share-file
|
(defn open-or-share-file
|
||||||
"Share file to mobile platform"
|
"Share file to mobile platform"
|
||||||
[uri]
|
[uri]
|
||||||
@@ -85,7 +114,7 @@
|
|||||||
|
|
||||||
(defn- embed-asset-file [url _format]
|
(defn- embed-asset-file [url _format]
|
||||||
(p/let [basename (node-path/basename url)
|
(p/let [basename (node-path/basename url)
|
||||||
file (.readFile Filesystem #js {:path url})
|
file (<filesystem-read-file url)
|
||||||
file-base64-str (some-> file (.-data))
|
file-base64-str (some-> file (.-data))
|
||||||
file (some-> file-base64-str (util/base64string-to-unit8array)
|
file (some-> file-base64-str (util/base64string-to-unit8array)
|
||||||
(vector) (clj->js) (js/File. basename #js {}))
|
(vector) (clj->js) (js/File. basename #js {}))
|
||||||
@@ -107,7 +136,7 @@
|
|||||||
(config/get-pages-directory)
|
(config/get-pages-directory)
|
||||||
(str (js/encodeURI (fs-util/file-name-sanity title :markdown)) (node-path/extname url)))
|
(str (js/encodeURI (fs-util/file-name-sanity title :markdown)) (node-path/extname url)))
|
||||||
_ (p/catch
|
_ (p/catch
|
||||||
(.copy Filesystem (clj->js {:from url :to path}))
|
(.copy Filesystem (clj->js {:from (normalize-native-file-path url) :to path}))
|
||||||
(fn [error]
|
(fn [error]
|
||||||
(log/error :copy-file-error {:error error})))
|
(log/error :copy-file-error {:error error})))
|
||||||
url (ref/->page-ref title)
|
url (ref/->page-ref title)
|
||||||
@@ -124,7 +153,10 @@
|
|||||||
(p/let [{:keys [url]} result
|
(p/let [{:keys [url]} result
|
||||||
page (or (state/get-current-page) (string/lower-case (date/journal-name)))
|
page (or (state/get-current-page) (string/lower-case (date/journal-name)))
|
||||||
format (db/get-page-format page)]
|
format (db/get-page-format page)]
|
||||||
(embed-asset-file url format)))
|
(-> (embed-asset-file url format)
|
||||||
|
(p/catch (fn [error]
|
||||||
|
(log/error :share-import-media-failed {:error error :url url})
|
||||||
|
(notification/show! "Failed to import the shared media. Please try again." :error false))))))
|
||||||
|
|
||||||
(defn- handle-received-application [result]
|
(defn- handle-received-application [result]
|
||||||
(p/let [{:keys [title url type]} result
|
(p/let [{:keys [title url type]} result
|
||||||
@@ -173,14 +205,16 @@
|
|||||||
(-> (p/let [basename (node-path/basename url)
|
(-> (p/let [basename (node-path/basename url)
|
||||||
_label (-> basename util/node-path.name)
|
_label (-> basename util/node-path.name)
|
||||||
_path (assets-handler/get-asset-path basename)
|
_path (assets-handler/get-asset-path basename)
|
||||||
file (.readFile Filesystem #js {:path url})
|
file (<filesystem-read-file url)
|
||||||
file-base64-str (some-> file (.-data))
|
file-base64-str (some-> file (.-data))
|
||||||
file (some-> file-base64-str (util/base64string-to-unit8array)
|
file (some-> file-base64-str (util/base64string-to-unit8array)
|
||||||
(vector) (clj->js) (js/File. basename #js {}))
|
(vector) (clj->js) (js/File. basename #js {}))
|
||||||
result (editor-handler/db-based-save-assets!
|
result (editor-handler/db-based-save-assets!
|
||||||
(state/get-current-repo) [file] {})]
|
(state/get-current-repo) [file] {})]
|
||||||
result)
|
result)
|
||||||
(p/catch #(log/error :handle-asset-file %))))
|
(p/catch (fn [error]
|
||||||
|
(log/error :handle-asset-file {:error error :url url})
|
||||||
|
(notification/show! "Failed to import the shared file. Please try again." :error false)))))
|
||||||
|
|
||||||
(defn- handle-payload-resource
|
(defn- handle-payload-resource
|
||||||
[{:keys [type name ext url] :as resource} format]
|
[{:keys [type name ext url] :as resource} format]
|
||||||
|
|||||||
Reference in New Issue
Block a user