mirror of https://github.com/logseq/logseq
Merge branch 'master' into feat/integrated-title-bar
commit
13d29a072f
|
@ -6,8 +6,8 @@ android {
|
|||
applicationId "com.logseq.app"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 59
|
||||
versionName "0.9.6"
|
||||
versionCode 60
|
||||
versionName "0.9.7"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
aaptOptions {
|
||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||
|
|
|
@ -92,10 +92,23 @@ public class FsWatcher extends Plugin {
|
|||
shouldRead = true;
|
||||
}
|
||||
|
||||
URI dir = (new File(mPath)).toURI();
|
||||
URI fpath = f.toURI();
|
||||
Uri dir = Uri.fromFile(new File(mPath));
|
||||
Uri fpath = Uri.fromFile(f);
|
||||
String relpath = null;
|
||||
|
||||
obj.put("path", Normalizer.normalize(dir.relativize(fpath).toString(), Normalizer.Form.NFC));
|
||||
if (fpath.getPath().startsWith(dir.getPath())) {
|
||||
relpath = fpath.getPath().substring(dir.getPath().length());
|
||||
if (relpath.startsWith("/")) {
|
||||
relpath = relpath.substring(1);
|
||||
}
|
||||
relpath = Uri.decode(relpath);
|
||||
} else {
|
||||
Log.e("FsWatcher", "file path not under watch path");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
obj.put("path", Normalizer.normalize(relpath, Normalizer.Form.NFC));
|
||||
obj.put("dir", Uri.fromFile(new File(mPath))); // Uri is for Android. URI is for RFC compatible
|
||||
JSObject stat;
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
D3D62A0C275C928F0003FBDC /* FileContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = D3D62A0B275C928F0003FBDC /* FileContainer.m */; };
|
||||
FE647FF427BDFEDE00F3206B /* FsWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE647FF327BDFEDE00F3206B /* FsWatcher.swift */; };
|
||||
FE647FF627BDFEF500F3206B /* FsWatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = FE647FF527BDFEF500F3206B /* FsWatcher.m */; };
|
||||
FE96D6102A1B811A001ECE32 /* SharedData.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE96D60F2A1B811A001ECE32 /* SharedData.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
|
@ -83,6 +84,7 @@
|
|||
DE5650F4AD4E2242AB9C012D /* Pods-Logseq.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Logseq.debug.xcconfig"; path = "Target Support Files/Pods-Logseq/Pods-Logseq.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
FE647FF327BDFEDE00F3206B /* FsWatcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FsWatcher.swift; sourceTree = "<group>"; };
|
||||
FE647FF527BDFEF500F3206B /* FsWatcher.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FsWatcher.m; sourceTree = "<group>"; };
|
||||
FE96D60F2A1B811A001ECE32 /* SharedData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedData.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -156,6 +158,7 @@
|
|||
children = (
|
||||
5FFF7D7927E4E70700B00DA8 /* ShareViewController.entitlements */,
|
||||
5FFF7D6C27E343FA00B00DA8 /* ShareViewController.swift */,
|
||||
FE96D60F2A1B811A001ECE32 /* SharedData.swift */,
|
||||
5FFF7D6E27E343FA00B00DA8 /* MainInterface.storyboard */,
|
||||
5FFF7D7127E343FA00B00DA8 /* Info.plist */,
|
||||
);
|
||||
|
@ -345,6 +348,7 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
FE96D6102A1B811A001ECE32 /* SharedData.swift in Sources */,
|
||||
5FFF7D6D27E343FA00B00DA8 /* ShareViewController.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -438,7 +442,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
|
@ -492,7 +496,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
|
@ -515,7 +519,7 @@
|
|||
INFOPLIST_FILE = App/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MARKETING_VERSION = 0.9.6;
|
||||
MARKETING_VERSION = 0.9.7;
|
||||
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -542,7 +546,7 @@
|
|||
INFOPLIST_FILE = App/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MARKETING_VERSION = 0.9.6;
|
||||
MARKETING_VERSION = 0.9.7;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
|
||||
|
@ -565,9 +569,9 @@
|
|||
INFOPLIST_FILE = ShareViewController/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = ShareViewController;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||
MARKETING_VERSION = 0.9.6;
|
||||
MARKETING_VERSION = 0.9.7;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq.ShareViewController;
|
||||
|
@ -592,9 +596,9 @@
|
|||
INFOPLIST_FILE = ShareViewController/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = ShareViewController;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||
MARKETING_VERSION = 0.9.6;
|
||||
MARKETING_VERSION = 0.9.7;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq.ShareViewController;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
<dict>
|
||||
<key>NSExtensionActivationRule</key>
|
||||
<dict>
|
||||
<key>NSExtensionActivationDictionaryVersion</key>
|
||||
<integer>2</integer>
|
||||
<key>NSExtensionActivationSupportsFileWithMaxCount</key>
|
||||
<integer>5</integer>
|
||||
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
|
||||
|
@ -17,9 +19,9 @@
|
|||
<key>NSExtensionActivationSupportsText</key>
|
||||
<true/>
|
||||
<key>NSExtensionActivationSupportsWebPageWithMaxCount</key>
|
||||
<integer>1</integer>
|
||||
<integer>3</integer>
|
||||
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
|
||||
<integer>1</integer>
|
||||
<integer>3</integer>
|
||||
<key>NSExtensionActivationUsesStrictMatching</key>
|
||||
<false/>
|
||||
</dict>
|
||||
|
|
|
@ -9,182 +9,180 @@
|
|||
import MobileCoreServices
|
||||
import Social
|
||||
import UIKit
|
||||
|
||||
class ShareItem {
|
||||
public var title: String?
|
||||
public var type: String?
|
||||
public var url: String?
|
||||
}
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
class ShareViewController: UIViewController {
|
||||
|
||||
private var shareItems: [ShareItem] = []
|
||||
|
||||
|
||||
private var sharedData: SharedData = SharedData.init(resources: [])
|
||||
|
||||
var groupContainerUrl: URL? {
|
||||
return FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.logseq.logseq")
|
||||
}
|
||||
|
||||
|
||||
override public func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
|
||||
super.viewDidAppear(animated)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
|
||||
self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func sendData() {
|
||||
let queryItems = shareItems.map {
|
||||
let encoder: JSONEncoder = JSONEncoder()
|
||||
let data = try? encoder.encode(self.sharedData)
|
||||
let queryPayload = String(decoding: data!, as: UTF8.self)
|
||||
|
||||
let queryItems =
|
||||
[
|
||||
URLQueryItem(
|
||||
name: "title",
|
||||
value: $0.title?.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? ""),
|
||||
URLQueryItem(name: "description", value: ""),
|
||||
URLQueryItem(
|
||||
name: "type",
|
||||
value: $0.type?.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? ""),
|
||||
URLQueryItem(
|
||||
name: "url",
|
||||
value: $0.url?.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? ""),
|
||||
name: "payload",
|
||||
value: queryPayload.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? ""),
|
||||
]
|
||||
}.flatMap({ $0 })
|
||||
var urlComps = URLComponents(string: "logseq://shared?")!
|
||||
urlComps.queryItems = queryItems
|
||||
openURL(urlComps.url!)
|
||||
}
|
||||
|
||||
fileprivate func createSharedFileUrl(_ url: URL?) -> String {
|
||||
|
||||
let copyFileUrl = groupContainerUrl!.absoluteString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)! + "/" + url!
|
||||
|
||||
fileprivate func createSharedFileUrl(_ url: URL?) -> URL? {
|
||||
let tempFilename = url!
|
||||
.lastPathComponent.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
|
||||
try? Data(contentsOf: url!).write(to: URL(string: copyFileUrl)!)
|
||||
|
||||
let copyFileUrl = groupContainerUrl!.appendingPathComponent(tempFilename)
|
||||
try? Data(contentsOf: url!).write(to: copyFileUrl)
|
||||
return copyFileUrl
|
||||
}
|
||||
|
||||
func saveScreenshot(_ image: UIImage) -> String {
|
||||
|
||||
|
||||
// Screenshots, shared images from some system App are passed as UIImage
|
||||
func saveUIImage(_ image: UIImage) -> URL? {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateFormat = "yyyy-MM-dd-HH-mm-ss"
|
||||
|
||||
let copyFileUrl = groupContainerUrl!.absoluteString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
|
||||
+ dateFormatter.string(from: Date()) + ".png"
|
||||
|
||||
let filename = dateFormatter.string(from: Date()) + ".png"
|
||||
|
||||
let copyFileUrl = groupContainerUrl!.appendingPathComponent(filename)
|
||||
|
||||
do {
|
||||
try image.pngData()?.write(to: URL(string: copyFileUrl)!)
|
||||
try image.pngData()?.write(to: copyFileUrl)
|
||||
return copyFileUrl
|
||||
} catch {
|
||||
print(error.localizedDescription)
|
||||
return ""
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Can be a path or a web URL
|
||||
fileprivate func handleTypeUrl(_ attachment: NSItemProvider)
|
||||
async throws -> ShareItem
|
||||
async throws -> SharedResource
|
||||
{
|
||||
let results = try await attachment.loadItem(forTypeIdentifier: kUTTypeURL as String, options: nil)
|
||||
let url = results as! URL?
|
||||
let shareItem: ShareItem = ShareItem()
|
||||
|
||||
|
||||
var res = SharedResource()
|
||||
|
||||
if url!.isFileURL {
|
||||
shareItem.title = url!.lastPathComponent
|
||||
shareItem.type = "application/" + url!.pathExtension.lowercased()
|
||||
shareItem.url = createSharedFileUrl(url)
|
||||
res.name = url!.lastPathComponent
|
||||
res.ext = url!.pathExtension
|
||||
res.type = url!.pathExtensionAsMimeType()
|
||||
res.url = createSharedFileUrl(url)
|
||||
} else {
|
||||
shareItem.title = url!.absoluteString
|
||||
shareItem.url = url!.absoluteString
|
||||
shareItem.type = "text/plain"
|
||||
res.name = url!.absoluteString
|
||||
res.type = "text/plain"
|
||||
}
|
||||
|
||||
return shareItem
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
|
||||
fileprivate func handleTypeText(_ attachment: NSItemProvider)
|
||||
async throws -> ShareItem
|
||||
async throws -> SharedResource?
|
||||
{
|
||||
let results = try await attachment.loadItem(forTypeIdentifier: kUTTypeText as String, options: nil)
|
||||
let shareItem: ShareItem = ShareItem()
|
||||
let text = results as! String
|
||||
shareItem.title = text
|
||||
shareItem.type = "text/plain"
|
||||
|
||||
return shareItem
|
||||
let item = try await attachment.loadItem(forTypeIdentifier: kUTTypeText as String, options: nil)
|
||||
self.sharedData.text = item as? String
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
fileprivate func handleTypeMovie(_ attachment: NSItemProvider)
|
||||
async throws -> ShareItem
|
||||
async throws -> SharedResource
|
||||
{
|
||||
let results = try await attachment.loadItem(forTypeIdentifier: kUTTypeMovie as String, options: nil)
|
||||
let shareItem: ShareItem = ShareItem()
|
||||
|
||||
|
||||
let url = results as! URL?
|
||||
shareItem.title = url!.lastPathComponent
|
||||
shareItem.type = "video/" + url!.pathExtension.lowercased()
|
||||
shareItem.url = createSharedFileUrl(url)
|
||||
|
||||
return shareItem
|
||||
|
||||
let name = url!.lastPathComponent
|
||||
let ext = url!.pathExtension.lowercased()
|
||||
let type = url!.pathExtensionAsMimeType()
|
||||
let sharedUrl = createSharedFileUrl(url)
|
||||
|
||||
let res = SharedResource(name: name, ext: ext, type: type, url: sharedUrl)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
|
||||
fileprivate func handleTypeImage(_ attachment: NSItemProvider)
|
||||
async throws -> ShareItem
|
||||
async throws -> SharedResource
|
||||
{
|
||||
let data = try await attachment.loadItem(forTypeIdentifier: kUTTypeImage as String, options: nil)
|
||||
|
||||
let shareItem: ShareItem = ShareItem()
|
||||
|
||||
var res = SharedResource()
|
||||
|
||||
switch data {
|
||||
case let image as UIImage:
|
||||
shareItem.title = "screenshot"
|
||||
shareItem.type = "image/png"
|
||||
shareItem.url = self.saveScreenshot(image)
|
||||
res.url = self.saveUIImage(image)
|
||||
res.ext = "png"
|
||||
res.name = res.url?.lastPathComponent
|
||||
res.type = res.url?.pathExtensionAsMimeType()
|
||||
case let url as URL:
|
||||
shareItem.title = url.lastPathComponent
|
||||
shareItem.type = "image/" + url.pathExtension.lowercased()
|
||||
shareItem.url = self.createSharedFileUrl(url)
|
||||
res.name = url.lastPathComponent
|
||||
res.ext = url.pathExtension.lowercased()
|
||||
res.type = url.pathExtensionAsMimeType()
|
||||
res.url = self.createSharedFileUrl(url)
|
||||
default:
|
||||
print("Unexpected image data:", type(of: data))
|
||||
}
|
||||
|
||||
return shareItem
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
override public func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
shareItems.removeAll()
|
||||
|
||||
let extensionItem = extensionContext?.inputItems.first as! NSExtensionItem
|
||||
|
||||
sharedData.empty()
|
||||
let inputItems = extensionContext?.inputItems as! [NSExtensionItem]
|
||||
Task {
|
||||
try await withThrowingTaskGroup(
|
||||
of: ShareItem.self,
|
||||
of: SharedResource?.self,
|
||||
body: { taskGroup in
|
||||
|
||||
for attachment in extensionItem.attachments! {
|
||||
if attachment.hasItemConformingToTypeIdentifier(kUTTypeURL as String) {
|
||||
taskGroup.addTask {
|
||||
return try await self.handleTypeUrl(attachment)
|
||||
}
|
||||
} else if attachment.hasItemConformingToTypeIdentifier(kUTTypeText as String) {
|
||||
taskGroup.addTask {
|
||||
return try await self.handleTypeText(attachment)
|
||||
}
|
||||
} else if attachment.hasItemConformingToTypeIdentifier(kUTTypeMovie as String) {
|
||||
taskGroup.addTask {
|
||||
return try await self.handleTypeMovie(attachment)
|
||||
}
|
||||
} else if attachment.hasItemConformingToTypeIdentifier(kUTTypeImage as String) {
|
||||
taskGroup.addTask {
|
||||
return try await self.handleTypeImage(attachment)
|
||||
for extensionItem in inputItems {
|
||||
for attachment in extensionItem.attachments! {
|
||||
if attachment.hasItemConformingToTypeIdentifier(kUTTypeURL as String) {
|
||||
taskGroup.addTask {
|
||||
return try await self.handleTypeUrl(attachment)
|
||||
}
|
||||
} else if attachment.hasItemConformingToTypeIdentifier(kUTTypeText as String) {
|
||||
taskGroup.addTask {
|
||||
return try await self.handleTypeText(attachment)
|
||||
}
|
||||
} else if attachment.hasItemConformingToTypeIdentifier(kUTTypeMovie as String) {
|
||||
taskGroup.addTask {
|
||||
return try await self.handleTypeMovie(attachment)
|
||||
}
|
||||
} else if attachment.hasItemConformingToTypeIdentifier(kUTTypeImage as String) {
|
||||
taskGroup.addTask {
|
||||
return try await self.handleTypeImage(attachment)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for try await item in taskGroup {
|
||||
self.shareItems.append(item)
|
||||
if let item = item {
|
||||
self.sharedData.resources.append(item)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
self.sendData()
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@discardableResult
|
||||
@objc func openURL(_ url: URL) -> Bool {
|
||||
var responder: UIResponder? = self
|
||||
|
@ -196,6 +194,14 @@ class ShareViewController: UIViewController {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
extension URL {
|
||||
func pathExtensionAsMimeType() -> String? {
|
||||
let type = UTType(filenameExtension: self.pathExtension)
|
||||
return type?.preferredMIMEType
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// SharedData.swift
|
||||
// ShareViewController
|
||||
//
|
||||
// Created by Mono Wang on 5/22/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct SharedResource: Decodable, Encodable {
|
||||
var name: String?
|
||||
var ext: String?
|
||||
var type: String?
|
||||
var url: URL?
|
||||
}
|
||||
|
||||
public struct SharedData: Decodable, Encodable {
|
||||
var text: String?
|
||||
var resources: [SharedResource]
|
||||
|
||||
mutating func empty() {
|
||||
text = nil
|
||||
resources = []
|
||||
}
|
||||
}
|
|
@ -122,7 +122,7 @@
|
|||
"path-complete-extname": "1.0.0",
|
||||
"pixi-graph-fork": "0.2.0",
|
||||
"pixi.js": "6.2.0",
|
||||
"posthog-js": "1.10.2",
|
||||
"posthog-js": "1.57.2",
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
"react-grid-layout": "0.16.6",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "Logseq",
|
||||
"productName": "Logseq",
|
||||
"version": "0.9.6",
|
||||
"version": "0.9.7",
|
||||
"main": "electron.js",
|
||||
"author": "Logseq",
|
||||
"license": "AGPL-3.0",
|
||||
|
|
|
@ -1777,7 +1777,7 @@
|
|||
(when (sync-state--valid-to-accept-filewatcher-event? sync-state)
|
||||
(when (or (:mtime stat) (= type "unlink"))
|
||||
(go
|
||||
(let [path (path-normalize (remove-dir-prefix dir path))
|
||||
(let [path (path-normalize path)
|
||||
files-meta (and (not= "unlink" type)
|
||||
(<! (<get-local-files-meta
|
||||
rsapi (:current-syncing-graph-uuid sync-state) dir [path])))
|
||||
|
|
|
@ -1193,10 +1193,6 @@
|
|||
(common-handler/copy-to-clipboard-without-id-property! (:block/format block) md-content html sorted-blocks)
|
||||
(delete-block-aux! block true))))
|
||||
|
||||
(defn clear-last-selected-block!
|
||||
[]
|
||||
(state/drop-last-selection-block!))
|
||||
|
||||
(defn highlight-selection-area!
|
||||
[end-block]
|
||||
(when-let [start-block (state/get-selection-start-block-or-first)]
|
||||
|
@ -1212,13 +1208,17 @@
|
|||
(cond
|
||||
;; when editing, quit editing and select current block
|
||||
(state/editing?)
|
||||
(state/exit-editing-and-set-selected-blocks! [(gdom/getElement (state/get-editing-block-dom-id))])
|
||||
(let [element (gdom/getElement (state/get-editing-block-dom-id))]
|
||||
(when element
|
||||
(util/scroll-to-block element)
|
||||
(state/exit-editing-and-set-selected-blocks! [element])))
|
||||
|
||||
;; when selection and one block selected, select next block
|
||||
(and (state/selection?) (== 1 (count (state/get-selection-blocks))))
|
||||
(let [f (if (= :up direction) util/get-prev-block-non-collapsed util/get-next-block-non-collapsed-skip)
|
||||
element (f (first (state/get-selection-blocks)))]
|
||||
(when element
|
||||
(util/scroll-to-block element)
|
||||
(state/conj-selection-block! element direction)))
|
||||
|
||||
;; if same direction, keep conj on same direction
|
||||
|
@ -1227,11 +1227,17 @@
|
|||
first-last (if (= :up direction) first last)
|
||||
element (f (first-last (state/get-selection-blocks)))]
|
||||
(when element
|
||||
(util/scroll-to-block element)
|
||||
(state/conj-selection-block! element direction)))
|
||||
|
||||
;; if different direction, keep clear until one left
|
||||
(state/selection?)
|
||||
(clear-last-selected-block!))
|
||||
(let [f (if (= :up direction) util/get-prev-block-non-collapsed util/get-next-block-non-collapsed)
|
||||
last-first (if (= :up direction) last first)
|
||||
element (f (last-first (state/get-selection-blocks)))]
|
||||
(when element
|
||||
(util/scroll-to-block element)
|
||||
(state/drop-last-selection-block!))))
|
||||
nil)
|
||||
|
||||
(defn on-select-block
|
||||
|
@ -2482,13 +2488,6 @@
|
|||
(.preventDefault e)
|
||||
(keydown-new-line))))
|
||||
|
||||
(defn- scroll-to-block
|
||||
[block]
|
||||
(when block
|
||||
(when-not (util/element-visible? block)
|
||||
(.scrollIntoView block #js {:behavior "smooth"
|
||||
:block "center"}))))
|
||||
|
||||
(defn- select-first-last
|
||||
"Select first or last block in viewpoint"
|
||||
[direction]
|
||||
|
@ -2496,7 +2495,7 @@
|
|||
block (->> (util/get-blocks-noncollapse)
|
||||
(f))]
|
||||
(when block
|
||||
(scroll-to-block block)
|
||||
(util/scroll-to-block block)
|
||||
(state/exit-editing-and-set-selected-blocks! [block]))))
|
||||
|
||||
(defn- select-up-down [direction]
|
||||
|
@ -2509,7 +2508,7 @@
|
|||
:down util/get-next-block-non-collapsed)
|
||||
sibling-block (f selected)]
|
||||
(when (and sibling-block (dom/attr sibling-block "blockid"))
|
||||
(scroll-to-block sibling-block)
|
||||
(util/scroll-to-block sibling-block)
|
||||
(state/exit-editing-and-set-selected-blocks! [sibling-block]))))
|
||||
|
||||
(defn- move-cross-boundary-up-down
|
||||
|
|
|
@ -233,12 +233,20 @@
|
|||
(state/set-state! :editor/on-paste? true)
|
||||
(let [clipboard-data (gobj/get e "clipboardData")
|
||||
html (.getData clipboard-data "text/html")
|
||||
text (.getData clipboard-data "text")]
|
||||
text (.getData clipboard-data "text")
|
||||
has-files? (seq (.-files clipboard-data))]
|
||||
(cond
|
||||
(and (string/blank? text) (string/blank? html))
|
||||
;; When both text and html are blank, paste file if exists.
|
||||
;; NOTE: util/stop is not called here if no file is provided,
|
||||
;; so the default paste behavior of the native platform will be used.
|
||||
(when has-files?
|
||||
(paste-file-if-exists id e))
|
||||
|
||||
;; both file attachment and text/html exist
|
||||
(and has-files? (state/preferred-pasting-file?))
|
||||
(paste-file-if-exists id e)
|
||||
(and (seq (.-files clipboard-data)) (state/preferred-pasting-file?))
|
||||
(paste-file-if-exists id e)
|
||||
|
||||
:else
|
||||
(let [text' (or (when (gp-util/url? text)
|
||||
(wrap-macro-url text))
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
[frontend.handler.route :as route-handler]
|
||||
[frontend.mobile.intent :as intent]
|
||||
[frontend.state :as state]
|
||||
[frontend.util.text :as text-util]))
|
||||
[frontend.util.text :as text-util]
|
||||
[logseq.graph-parser.util :as gp-util]))
|
||||
|
||||
(def *link-to-another-graph (atom false))
|
||||
|
||||
|
@ -70,8 +71,14 @@
|
|||
(= hostname "shared")
|
||||
(let [result (into {} (map (fn [key]
|
||||
[(keyword key) (.get search-params key)])
|
||||
["title" "url" "type"]))]
|
||||
(intent/handle-result result))
|
||||
["title" "url" "type" "payload"]))]
|
||||
(if (:payload result)
|
||||
(let [raw (gp-util/safe-decode-uri-component (:payload result))
|
||||
payload (-> raw
|
||||
js/JSON.parse
|
||||
(js->clj :keywordize-keys true))]
|
||||
(intent/handle-payload payload))
|
||||
(intent/handle-result result)))
|
||||
|
||||
:else
|
||||
nil)))
|
||||
|
|
|
@ -152,7 +152,92 @@
|
|||
(gp-util/safe-decode-uri-component v)
|
||||
v))])))
|
||||
|
||||
(defn handle-result [result]
|
||||
(defn- handle-asset-file [url format]
|
||||
(p/let [basename (node-path/basename url)
|
||||
label (-> basename util/node-path.name)
|
||||
path (editor-handler/get-asset-path basename)
|
||||
_file (p/catch
|
||||
(.copy Filesystem (clj->js {:from url :to path}))
|
||||
(fn [error]
|
||||
(log/error :copy-file-error {:error error})))
|
||||
url (util/format "../assets/%s" basename)
|
||||
url-link (editor-handler/get-asset-file-link format url label true)]
|
||||
url-link))
|
||||
|
||||
(defn- handle-payload-resource
|
||||
[{:keys [type name ext url] :as resource} format]
|
||||
(if url
|
||||
(cond
|
||||
(contains? (set/union config/doc-formats config/media-formats)
|
||||
(keyword ext))
|
||||
(handle-asset-file url format)
|
||||
|
||||
:else
|
||||
(notification/show!
|
||||
[:div
|
||||
"Parsing current shared content are not supported. Please report the following codes on "
|
||||
[:a {:href "https://github.com/logseq/logseq/issues/new?labels=from:in-app&template=bug_report.yaml"
|
||||
:target "_blank"} "Github"]
|
||||
". We will look into it soon."
|
||||
[:pre.code (with-out-str (pprint/pprint resource))]] :warning false))
|
||||
|
||||
(cond
|
||||
(= type "text/plain")
|
||||
name
|
||||
|
||||
:else
|
||||
(notification/show!
|
||||
[:div
|
||||
"Parsing current shared content are not supported. Please report the following codes on "
|
||||
[:a {:href "https://github.com/logseq/logseq/issues/new?labels=from:in-app&template=bug_report.yaml"
|
||||
:target "_blank"} "Github"]
|
||||
". We will look into it soon."
|
||||
[:pre.code (with-out-str (pprint/pprint resource))]] :warning false))))
|
||||
|
||||
(defn handle-payload
|
||||
"Mobile share intent handler v2, use complex payload to support more types of content."
|
||||
[payload]
|
||||
;; use :text template, use {url} as rich text placeholder
|
||||
(p/let [page (or (state/get-current-page) (string/lower-case (date/journal-name)))
|
||||
format (db/get-page-format page)
|
||||
|
||||
template (get-in (state/get-config)
|
||||
[:quick-capture-templates :text]
|
||||
"**{time}** [[quick capture]]: {text} {url}")
|
||||
{:keys [text resources]} payload
|
||||
text (or text "")
|
||||
rich-content (-> (p/all (map (fn [resource]
|
||||
(handle-payload-resource resource format))
|
||||
resources))
|
||||
(p/then (partial string/join "\n")))]
|
||||
(when (or (not-empty text) (not-empty rich-content))
|
||||
(let [time (date/get-current-time)
|
||||
date-ref-name (date/today)
|
||||
content (-> template
|
||||
(string/replace "{time}" time)
|
||||
(string/replace "{date}" date-ref-name)
|
||||
(string/replace "{text}" text)
|
||||
(string/replace "{url}" rich-content))
|
||||
edit-content (state/get-edit-content)
|
||||
edit-content-blank? (string/blank? edit-content)
|
||||
edit-content-include-capture? (and (not-empty edit-content)
|
||||
(string/includes? edit-content "[[quick capture]]"))]
|
||||
(if (and (state/editing?) (not edit-content-include-capture?))
|
||||
(if edit-content-blank?
|
||||
(editor-handler/insert content)
|
||||
(editor-handler/insert (str "\n" content)))
|
||||
|
||||
(do
|
||||
(editor-handler/escape-editing)
|
||||
(js/setTimeout #(editor-handler/api-insert-new-block! content {:page page
|
||||
:edit-block? true
|
||||
:replace-empty-target? true})
|
||||
100)))))))
|
||||
|
||||
|
||||
(defn handle-result
|
||||
"Mobile share intent handler v1, legacy. Only for Android"
|
||||
[result]
|
||||
(let [result (decode-received-result result)]
|
||||
(when-let [type (:type result)]
|
||||
(cond
|
||||
|
|
|
@ -405,6 +405,16 @@
|
|||
%))
|
||||
(take-while (complement nil?) (iterate #(.-parentElement %) element)))))
|
||||
|
||||
#?(:cljs
|
||||
(defn element-visible?
|
||||
[element]
|
||||
(when element
|
||||
(when-let [r (.getBoundingClientRect element)]
|
||||
(and (>= (.-top r) 0)
|
||||
(<= (+ (.-bottom r) 64)
|
||||
(or (.-innerHeight js/window)
|
||||
(js/document.documentElement.clientHeight))))))))
|
||||
|
||||
#?(:cljs
|
||||
(defn element-top [elem top]
|
||||
(when elem
|
||||
|
@ -455,6 +465,19 @@
|
|||
([animate?]
|
||||
(scroll-to (app-scroll-container-node) 0 animate?))))
|
||||
|
||||
#?(:cljs
|
||||
(defn scroll-to-block
|
||||
"Scroll into the view to vertically align a non-visible block to the centre
|
||||
of the visible area"
|
||||
([block]
|
||||
(scroll-to-block block true))
|
||||
([block animate?]
|
||||
(when block
|
||||
(when-not (element-visible? block)
|
||||
(.scrollIntoView block
|
||||
#js {:behavior (if animate? "smooth" "auto")
|
||||
:block "center"}))))))
|
||||
|
||||
#?(:cljs
|
||||
(defn link?
|
||||
[node]
|
||||
|
@ -1404,16 +1427,6 @@
|
|||
(fn [resolve]
|
||||
(load url resolve)))))
|
||||
|
||||
#?(:cljs
|
||||
(defn element-visible?
|
||||
[element]
|
||||
(when element
|
||||
(when-let [r (.getBoundingClientRect element)]
|
||||
(and (>= (.-top r) 0)
|
||||
(<= (+ (.-bottom r) 64)
|
||||
(or (.-innerHeight js/window)
|
||||
(js/document.documentElement.clientHeight))))))))
|
||||
|
||||
#?(:cljs
|
||||
(defn copy-image-to-clipboard
|
||||
[src]
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
(ns ^:no-doc frontend.version)
|
||||
|
||||
(defonce version "0.9.6")
|
||||
(defonce version "0.9.7")
|
||||
|
|
14
yarn.lock
14
yarn.lock
|
@ -5807,12 +5807,13 @@ postcss@^8.2.1:
|
|||
picocolors "^1.0.0"
|
||||
source-map-js "^1.0.2"
|
||||
|
||||
posthog-js@1.10.2:
|
||||
version "1.10.2"
|
||||
resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.10.2.tgz#74d6c84f9675b65dfd4ff6f4051ed8d3cb974076"
|
||||
integrity sha512-JNjWstHEexhj5CEKldSeYNyPJbtOvZQ3ZPL55fxU7+f+gTBL8RlOb8eFohCPYIk0VhMf2UM1rXxwVBOeMQQQFw==
|
||||
posthog-js@1.57.2:
|
||||
version "1.57.2"
|
||||
resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.57.2.tgz#131fb93e2ad099baff4317f3d91a4d6c96a08e7f"
|
||||
integrity sha512-ER4gkYZasrd2Zwmt/yLeZ5G/nZJ6tpaYBCpx3CvocDx+3F16WdawJlYMT0IyLKHXDniC5+AsjzFd6fi8uyYlJA==
|
||||
dependencies:
|
||||
fflate "^0.4.1"
|
||||
rrweb-snapshot "^1.1.14"
|
||||
|
||||
prepend-http@^2.0.0:
|
||||
version "2.0.0"
|
||||
|
@ -6384,6 +6385,11 @@ roarr@^2.15.3:
|
|||
semver-compare "^1.0.0"
|
||||
sprintf-js "^1.1.2"
|
||||
|
||||
rrweb-snapshot@^1.1.14:
|
||||
version "1.1.14"
|
||||
resolved "https://registry.yarnpkg.com/rrweb-snapshot/-/rrweb-snapshot-1.1.14.tgz#9d4d9be54a28a893373428ee4393ec7e5bd83fcc"
|
||||
integrity sha512-eP5pirNjP5+GewQfcOQY4uBiDnpqxNRc65yKPW0eSoU1XamDfc4M8oqpXGMyUyvLyxFDB0q0+DChuxxiU2FXBQ==
|
||||
|
||||
run-parallel@^1.1.9:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
|
||||
|
|
Loading…
Reference in New Issue