Explorar o código

Merge pull request #3374 from logseq/enhance/mobile

Support photo upload/undo on mobile
Tienson Qin %!s(int64=3) %!d(string=hai) anos
pai
achega
2a081a7902

+ 1 - 0
android/app/capacitor.build.gradle

@@ -12,6 +12,7 @@ dependencies {
     implementation project(':capacitor-app')
     implementation project(':capacitor-filesystem')
     implementation project(':capacitor-splash-screen')
+    implementation project(':capacitor-camera')
 
 }
 

+ 4 - 0
android/app/src/main/assets/capacitor.plugins.json

@@ -10,5 +10,9 @@
 	{
 		"pkg": "@capacitor/splash-screen",
 		"classpath": "com.capacitorjs.plugins.splashscreen.SplashScreenPlugin"
+	},
+	{
+		"pkg": "@capacitor/camera",
+		"classpath": "com.capacitorjs.plugins.camera.CameraPlugin"
 	}
 ]

+ 3 - 0
android/capacitor.settings.gradle

@@ -10,3 +10,6 @@ project(':capacitor-filesystem').projectDir = new File('../node_modules/@capacit
 
 include ':capacitor-splash-screen'
 project(':capacitor-splash-screen').projectDir = new File('../node_modules/@capacitor/splash-screen/android')
+
+include ':capacitor-camera'
+project(':capacitor-camera').projectDir = new File('../node_modules/@capacitor/camera/android')

+ 4 - 0
ios/App/App.xcodeproj/project.pbxproj

@@ -16,6 +16,7 @@
 		50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; };
 		7435D10C2704659F00AB88E0 /* FolderPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7435D10B2704659F00AB88E0 /* FolderPicker.swift */; };
 		7435D10F2704660B00AB88E0 /* FolderPicker.m in Sources */ = {isa = PBXBuildFile; fileRef = 7435D10E2704660B00AB88E0 /* FolderPicker.m */; };
+		C3718FCEFAECFFB66E93FFC4 /* Pods_Logseq.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B2E26D73EA097D0B3B22942E /* Pods_Logseq.framework */; };
 		D32752BE275496C60039291C /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D32752BD275496C60039291C /* CloudKit.framework */; };
 		D3D62A0A275C92880003FBDC /* FileContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3D62A09275C92880003FBDC /* FileContainer.swift */; };
 		D3D62A0C275C928F0003FBDC /* FileContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = D3D62A0B275C928F0003FBDC /* FileContainer.m */; };
@@ -35,6 +36,7 @@
 		7435D10D2704660A00AB88E0 /* App-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "App-Bridging-Header.h"; sourceTree = "<group>"; };
 		7435D10E2704660B00AB88E0 /* FolderPicker.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FolderPicker.m; sourceTree = "<group>"; };
 		8A489CEC51E94726DDD58810 /* Pods-Logseq.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Logseq.release.xcconfig"; path = "Target Support Files/Pods-Logseq/Pods-Logseq.release.xcconfig"; sourceTree = "<group>"; };
+		B2E26D73EA097D0B3B22942E /* Pods_Logseq.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Logseq.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		D32752BC275496A60039291C /* App.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = App.entitlements; sourceTree = "<group>"; };
 		D32752BD275496C60039291C /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; };
 		D32752BF2754C5AB0039291C /* AppDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AppDebug.entitlements; sourceTree = "<group>"; };
@@ -49,6 +51,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				D32752BE275496C60039291C /* CloudKit.framework in Frameworks */,
+				C3718FCEFAECFFB66E93FFC4 /* Pods_Logseq.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -99,6 +102,7 @@
 			isa = PBXGroup;
 			children = (
 				D32752BD275496C60039291C /* CloudKit.framework */,
+				B2E26D73EA097D0B3B22942E /* Pods_Logseq.framework */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";

+ 89 - 83
ios/App/App/Info.plist

@@ -2,88 +2,94 @@
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
-        <key>APFiles</key>
-        <dict>
-                <key>APFileDescriptionKey</key>
-                <string></string>
-                <key>APFileDestinationPath</key>
-                <string></string>
-                <key>APFileName</key>
-                <string></string>
-                <key>APFileSourcePath</key>
-                <string></string>
-        </dict>
-        <key>CFBundleDevelopmentRegion</key>
-        <string>en</string>
-        <key>CFBundleDisplayName</key>
-        <string>Logseq</string>
-        <key>CFBundleExecutable</key>
-        <string>$(EXECUTABLE_NAME)</string>
-        <key>CFBundleIdentifier</key>
-        <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
-        <key>CFBundleInfoDictionaryVersion</key>
-        <string>6.0</string>
-        <key>CFBundleName</key>
-        <string>$(PRODUCT_NAME)</string>
-        <key>CFBundlePackageType</key>
-        <string>APPL</string>
-        <key>CFBundleShortVersionString</key>
-        <string>$(MARKETING_VERSION)</string>
-        <key>CFBundleVersion</key>
-        <string>$(CURRENT_PROJECT_VERSION)</string>
-        <key>LSRequiresIPhoneOS</key>
-        <true/>
-        <key>LSSupportsOpeningDocumentsInPlace</key>
-        <true/>
-        <key>NSAppTransportSecurity</key>
-        <dict>
-                <key>NSAllowsArbitraryLoads</key>
-                <true/>
-        </dict>
-        <key>NSDocumentsFolderUsageDescription</key>
-        <string></string>
-        <key>NSDownloadsFolderUsageDescription</key>
-        <string></string>
-        <key>NSFileProviderDomainUsageDescription</key>
-        <string></string>
-        <key>NSFileProviderPresenceUsageDescription</key>
-        <string></string>
-        <key>NSUbiquitousContainers</key>
-        <dict>
-                <key>iCloud.com.logseq.logseq</key>
-                <dict>
-                        <key>NSUbiquitousContainerIsDocumentScopePublic</key>
-                        <true/>
-                        <key>NSUbiquitousContainerName</key>
-                        <string>Logseq</string>
-                        <key>NSUbiquitousContainerSupportedFolderLevels</key>
-                        <string>ANY</string>
-                </dict>
-        </dict>
-        <key>UILaunchStoryboardName</key>
-        <string>LaunchScreen</string>
-        <key>UIMainStoryboardFile</key>
-        <string>Main</string>
-        <key>UIRequiredDeviceCapabilities</key>
-        <array>
-                <string>armv7</string>
-        </array>
-        <key>UISupportedInterfaceOrientations</key>
-        <array>
-                <string>UIInterfaceOrientationPortrait</string>
-                <string>UIInterfaceOrientationLandscapeLeft</string>
-                <string>UIInterfaceOrientationLandscapeRight</string>
-        </array>
-        <key>UISupportedInterfaceOrientations~ipad</key>
-        <array>
-                <string>UIInterfaceOrientationPortrait</string>
-                <string>UIInterfaceOrientationPortraitUpsideDown</string>
-                <string>UIInterfaceOrientationLandscapeLeft</string>
-                <string>UIInterfaceOrientationLandscapeRight</string>
-        </array>
-        <key>UISupportsDocumentBrowser</key>
-        <true/>
-        <key>UIViewControllerBasedStatusBarAppearance</key>
-        <true/>
+	<key>APFiles</key>
+	<dict>
+		<key>APFileDescriptionKey</key>
+		<string></string>
+		<key>APFileDestinationPath</key>
+		<string></string>
+		<key>APFileName</key>
+		<string></string>
+		<key>APFileSourcePath</key>
+		<string></string>
+	</dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleDisplayName</key>
+	<string>Logseq</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>$(MARKETING_VERSION)</string>
+	<key>CFBundleVersion</key>
+	<string>$(CURRENT_PROJECT_VERSION)</string>
+	<key>LSRequiresIPhoneOS</key>
+	<true/>
+	<key>LSSupportsOpeningDocumentsInPlace</key>
+	<true/>
+	<key>NSAppTransportSecurity</key>
+	<dict>
+		<key>NSAllowsArbitraryLoads</key>
+		<true/>
+	</dict>
+	<key>NSDocumentsFolderUsageDescription</key>
+	<string></string>
+	<key>NSDownloadsFolderUsageDescription</key>
+	<string></string>
+	<key>NSFileProviderDomainUsageDescription</key>
+	<string></string>
+	<key>NSFileProviderPresenceUsageDescription</key>
+	<string></string>
+	<key>NSUbiquitousContainers</key>
+	<dict>
+		<key>iCloud.com.logseq.logseq</key>
+		<dict>
+			<key>NSUbiquitousContainerIsDocumentScopePublic</key>
+			<true/>
+			<key>NSUbiquitousContainerName</key>
+			<string>Logseq</string>
+			<key>NSUbiquitousContainerSupportedFolderLevels</key>
+			<string>ANY</string>
+		</dict>
+	</dict>
+	<key>UILaunchStoryboardName</key>
+	<string>LaunchScreen</string>
+	<key>UIMainStoryboardFile</key>
+	<string>Main</string>
+	<key>NSPhotoLibraryAddUsageDescription</key>
+	<string></string>
+	<key>NSCameraUsageDescription</key>
+	<string></string>
+	<key>NSPhotoLibraryUsageDescription</key>
+	<string></string>
+	<key>UIRequiredDeviceCapabilities</key>
+	<array>
+		<string>armv7</string>
+	</array>
+	<key>UISupportedInterfaceOrientations</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>UISupportedInterfaceOrientations~ipad</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationPortraitUpsideDown</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>UISupportsDocumentBrowser</key>
+	<true/>
+	<key>UIViewControllerBasedStatusBarAppearance</key>
+	<true/>
 </dict>
 </plist>

+ 1 - 0
ios/App/Podfile

@@ -12,6 +12,7 @@ def capacitor_pods
   pod 'CapacitorApp', :path => '../../node_modules/@capacitor/app'
   pod 'CapacitorFilesystem', :path => '../../node_modules/@capacitor/filesystem'
   pod 'CapacitorSplashScreen', :path => '../../node_modules/@capacitor/splash-screen'
+  pod 'CapacitorCamera', :path => '../../node_modules/@capacitor/camera'
 end
 
 target 'Logseq' do

+ 2 - 1
package.json

@@ -69,7 +69,8 @@
         "@capacitor/filesystem": "1.0.6",
         "@capacitor/ios": "3.2.2",
         "@capacitor/splash-screen": "1.1.3",
-        "@excalidraw/excalidraw": "0.4.3",
+        "@capacitor/camera": "1.2.1", 
+        "@excalidraw/excalidraw": "0.4.2",
         "@kanru/rage-wasm": "0.2.1",
         "@sentry/browser": "6.4.1",
         "@sentry/electron": "2.5.1",

+ 18 - 2
src/main/frontend/components/editor.cljs

@@ -7,6 +7,7 @@
             [frontend.components.datetime :as datetime-comp]
             [frontend.components.search :as search]
             [frontend.components.svg :as svg]
+            [frontend.mobile.camera :as mobile-camera]
             [frontend.config :as config]
             [frontend.handler.notification :as notification]
             [frontend.db :as db]
@@ -23,7 +24,8 @@
             [frontend.util.keycode :as keycode]
             [goog.dom :as gdom]
             [promesa.core :as p]
-            [rum.core :as rum]))
+            [rum.core :as rum]
+            [frontend.handler.history :as history]))
 
 (rum/defc commands < rum/reactive
   [id format]
@@ -228,7 +230,14 @@
                            (:offset-top vw-state))
                         0)}
       :class (util/classnames [{:is-vw-pending (boolean vw-pending?)}])}
-     [:div.flex.justify-around.w-full
+     [:div.flex {:style {:overflow "overlay"}}
+      [:div
+       [:button.bottom-action
+        {:on-mouse-down (fn [e]
+                        (util/stop e)
+                        (mobile-camera/embed-photo parent-id))}
+        (ui/icon "camera"
+               {:style {:fontSize ui/icon-size}})]]
       [:div
        [:button.bottom-action
         {:on-mouse-down (fn [e]
@@ -268,6 +277,13 @@
                             (.focus input)))}
         (ui/icon "arrow-back"
                  {:style {:fontSize ui/icon-size}})]]
+      [:div
+       [:button.bottom-action
+        {:on-mouse-down (fn [e]
+                          (util/stop e)
+                          (history/undo! e))}
+        (ui/icon "rotate"
+                 {:style {:fontSize ui/icon-size}})]]
       [:div
        [:button.bottom-action
         {:on-mouse-down (fn [e]

+ 5 - 1
src/main/frontend/handler/editor.cljs

@@ -1594,10 +1594,14 @@
       (util/electron?)
       (str "assets://" repo-dir path)
 
-      (mobile/is-native-platform?)
+      (mobile/native-android?)
       (mobile/convert-file-src
        (str "file://" repo-dir path))
 
+      (mobile/native-ios?)
+      (mobile/convert-file-src
+       (str repo-dir path))
+
       :else
       (let [handle-path (str "handle" repo-dir path)
             cached-url (get @*assets-url-cache (keyword handle-path))]

+ 52 - 0
src/main/frontend/mobile/camera.cljs

@@ -0,0 +1,52 @@
+(ns frontend.mobile.camera
+  (:require ["@capacitor/camera" :refer [Camera CameraResultType]]
+            ["@capacitor/filesystem" :refer [Filesystem]]
+            [lambdaisland.glogi :as log]
+            [promesa.core :as p]
+            [frontend.handler.editor :as editor-handler]
+            [frontend.state :as state]
+            [frontend.date :as date]
+            [frontend.util :as util]
+            [frontend.commands :as commands]
+            [frontend.mobile.util :as mobile-util]))
+
+(defn- save-photo []
+  (p/let [photo (p/catch
+                    (.getPhoto Camera (clj->js
+                                       {:allowEditing (get-in
+                                                       (state/get-config)
+                                                       [:mobile/photo :allow-editing?])
+                                        :saveToGallery true
+                                        :resultType (.-Base64 CameraResultType)}))
+                    (fn [error]
+                      (log/error :photo/get-failed {:error error})))
+          [repo-dir assets-dir] (editor-handler/ensure-assets-dir! (state/get-current-repo))
+          filename (str (date/get-date-time-string-2) ".jpeg")
+          path (cond
+                 (mobile-util/native-android?)
+                 (str "file://" repo-dir "/" assets-dir "/" filename)
+
+                 (mobile-util/native-ios?)
+                 (str repo-dir assets-dir "/" filename)
+
+                 :else
+                 (str repo-dir assets-dir "/" filename))
+          _file (p/catch
+                    (.writeFile Filesystem (clj->js {:data (.-base64String photo)
+                                                     :path path
+                                                     :recursive true}))
+                    (fn [error]
+                      (log/error :file/write-failed {:path path
+                                                     :error error})))]
+    (p/resolved filename)))
+
+(defn embed-photo [id]
+  (let [block (state/get-edit-block)
+        format (:block/format block)]
+    (p/let [filename (save-photo)]
+      (commands/simple-insert!
+       id
+       (case format
+         :org (util/format "[[../assets/%s]]" filename)
+         (util/format "![%s](../assets/%s)" filename filename))
+       {}))))

+ 4 - 0
templates/config.edn

@@ -182,4 +182,8 @@
  ;;  :enabled-in-all-blocks true ;display logbook in all blocks after timetracking
  ;;  :enabled-in-timestamped-blocks false ;don't display logbook at all
  ;; }
+
+ ;; Mobile photo uploading setup
+ ;; :mobile/photo
+ ;; {:allow-editing? true}
  }