Просмотр исходного кода

Merge branch 'master' into enhance/mobile-ux-2

charlie 3 лет назад
Родитель
Сommit
363336e123

+ 11 - 5
.github/workflows/build-ios-release.yml

@@ -20,23 +20,23 @@ jobs:
     runs-on: macos-latest
     steps:
       - name: Check out Git repository
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
         with:
           ref: ${{ github.event.inputs.git-ref }}
 
       - name: Install Node.js, NPM and Yarn
-        uses: actions/setup-node@v2
+        uses: actions/setup-node@v3
         with:
           node-version: ${{ env.NODE_VERSION }}
 
       - name: Setup Java JDK
-        uses: actions/setup-java@v2
+        uses: actions/setup-java@v3
         with:
           distribution: 'zulu'
           java-version: ${{ env.JAVA_VERSION }}
 
       - name: Cache clojure deps
-        uses: actions/cache@v2
+        uses: actions/cache@v3
         with:
           path: |
             ~/.m2/repository
@@ -44,7 +44,7 @@ jobs:
           key: ${{ runner.os }}-clojure-lib-${{ hashFiles('**/deps.edn') }}
 
       - name: Setup clojure
-        uses: DeLaGuardo/setup-clojure@3.5
+        uses: DeLaGuardo/setup-clojure@10.1
         with:
           cli: ${{ env.CLOJURE_VERSION }}
 
@@ -75,3 +75,9 @@ jobs:
           SLACK_URL: ${{ secrets.SLACK_URL }}
           MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
           MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }}
+
+      - name: Save Static File
+        uses: actions/upload-artifact@v3
+        with:
+          name: static
+          path: static

+ 15 - 19
.github/workflows/build.yml

@@ -30,10 +30,10 @@ jobs:
 
     steps:
       - name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
 
       - name: Set up Node
-        uses: actions/setup-node@v2
+        uses: actions/setup-node@v3
         with:
           node-version: ${{ env.NODE_VERSION }}
           cache: 'yarn'
@@ -42,18 +42,18 @@ jobs:
             static/yarn.lock
 
       - name: Set up Java
-        uses: actions/setup-java@v2
+        uses: actions/setup-java@v3
         with:
           distribution: 'zulu'
           java-version: ${{ env.JAVA_VERSION }}
 
       - name: Set up Clojure
-        uses: DeLaGuardo/setup-clojure@master
+        uses: DeLaGuardo/setup-clojure@10.1
         with:
           cli: ${{ env.CLOJURE_VERSION }}
 
       - name: Clojure cache
-        uses: actions/cache@v2
+        uses: actions/cache@v3
         id: clojure-deps
         with:
           path: |
@@ -79,23 +79,19 @@ jobs:
 
     steps:
       - name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
 
       - name: Set up Java
-        uses: actions/setup-java@v2
+        uses: actions/setup-java@v3
         with:
           distribution: 'zulu'
           java-version: ${{ env.JAVA_VERSION }}
 
       - name: Set up Clojure
-        uses: DeLaGuardo/setup-clojure@master
+        uses: DeLaGuardo/setup-clojure@10.1
         with:
           cli: ${{ env.CLOJURE_VERSION }}
-
-      - name: Setup Babashka
-        uses: turtlequeue/[email protected]
-        with:
-          babashka-version: ${{ env.BABASHKA_VERSION }}
+          bb: ${{ env.BABASHKA_VERSION }}
 
       - name: Run clj-kondo lint
         run: clojure -M:clj-kondo --parallel --lint src
@@ -117,10 +113,10 @@ jobs:
 
     steps:
       - name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
 
       - name: Set up Node
-        uses: actions/setup-node@v2
+        uses: actions/setup-node@v3
         with:
           node-version: ${{ env.NODE_VERSION }}
           cache: 'yarn'
@@ -129,13 +125,13 @@ jobs:
             static/yarn.lock
 
       - name: Set up Java
-        uses: actions/setup-java@v2
+        uses: actions/setup-java@v3
         with:
           distribution: 'zulu'
           java-version: ${{ env.JAVA_VERSION }}
 
       - name: Set up Clojure
-        uses: DeLaGuardo/setup-clojure@master
+        uses: DeLaGuardo/setup-clojure@10.1
         with:
           cli: ${{ env.CLOJURE_VERSION }}
 
@@ -154,7 +150,7 @@ jobs:
         run: clojure -A:cljs -P
 
       - name: Shadow-cljs cache
-        uses: actions/cache@v2
+        uses: actions/cache@v3
         with:
           path: .shadow-cljs
           # ensure update cache every time
@@ -194,7 +190,7 @@ jobs:
 
       - name: Save test artifacts
         if: ${{ failure() }}
-        uses: actions/upload-artifact@v2
+        uses: actions/upload-artifact@v3
         with:
           name: e2e-test-report
           path: e2e-dump/*

+ 4 - 12
.github/workflows/graph-parser.yml

@@ -53,14 +53,10 @@ jobs:
           java-version: ${{ env.JAVA_VERSION }}
 
       - name: Set up Clojure
-        uses: DeLaGuardo/setup-clojure@master
+        uses: DeLaGuardo/setup-clojure@10.1
         with:
           cli: ${{ env.CLOJURE_VERSION }}
-
-      - name: Setup Babashka
-        uses: turtlequeue/[email protected]
-        with:
-          babashka-version: ${{ env.BABASHKA_VERSION }}
+          bb: ${{ env.BABASHKA_VERSION }}
 
       - name: Clojure cache
         uses: actions/cache@v3
@@ -103,14 +99,10 @@ jobs:
           java-version: ${{ env.JAVA_VERSION }}
 
       - name: Set up Clojure
-        uses: DeLaGuardo/setup-clojure@master
+        uses: DeLaGuardo/setup-clojure@10.1
         with:
           cli: ${{ env.CLOJURE_VERSION }}
-
-      - name: Setup Babashka
-        uses: turtlequeue/[email protected]
-        with:
-          babashka-version: ${{ env.BABASHKA_VERSION }}
+          bb: ${{ env.BABASHKA_VERSION }}
 
       - name: Run clj-kondo lint
         run: clojure -M:clj-kondo --parallel --lint src test

BIN
resources/img/whiteboard-welcome-dark.png


BIN
resources/img/whiteboard-welcome-light.png


+ 56 - 0
src/main/frontend/components/onboarding/quick_tour.cljs

@@ -2,6 +2,7 @@
   (:require [promesa.core :as p]
             [cljs-bean.core :as bean]
             [frontend.state :as state]
+            [frontend.context.i18n :refer [t]]
             [frontend.date :as date]
             [frontend.util :as util]
             [frontend.handler.route :as router-handler]
@@ -151,6 +152,38 @@
                                     {:name    "offset"
                                      :options {:offset [0, 15]}}]}}])
 
+(defn- create-steps-whiteboard! [^js jsTour]
+  [;; step 1
+   {:id                "whiteboard-home"
+    :text              (h/render-html [:section [:h2  (t :on-boarding/tour-whiteboard-home "🖼")]
+                                       [:p (t :on-boarding/tour-whiteboard-home-description)]])
+    :attachTo          {:element ".nav-header .whiteboard" :on "right"}
+    :beforeShowPromise (fn []
+                         (when-not (state/sub :ui/left-sidebar-open?)
+                           (state/toggle-left-sidebar!))
+                         (wait-target ".nav-header .whiteboard" 500))
+    :canClickTarget    true
+    :buttons           [{:text "Next" :action (.-next jsTour)}]
+    :popperOptions     {:modifiers [{:name    "preventOverflow"
+                                     :options {:padding 20}}
+                                    {:name    "offset"
+                                     :options {:offset [0, 10]}}]}}
+
+   ;; step 2
+   {:id                "whiteboard-new"
+    :text              (h/render-html [:section [:h2 (t :on-boarding/tour-whiteboard-new "🆕️")]
+                                       [:p (t :on-boarding/tour-whiteboard-new-description)]])
+    :beforeShowPromise (fn []
+                         (router-handler/redirect-to-whiteboard-dashboard!)
+                         (wait-target ".dashboard-create-card" 500))
+    :attachTo          {:element ".dashboard-create-card" :on "bottom"}
+    :buttons           [{:text "Back" :classes "back" :action (.-back jsTour)}
+                        {:text "Finish" :action (.-complete jsTour)}]
+    :popperOptions     {:modifiers [{:name    "preventOverflow"
+                                     :options {:padding 20}}
+                                    {:name    "offset"
+                                     :options {:offset [0, 10]}}]}}])
+
 (defn start
   []
   (let [^js jsTour (js/Shepherd.Tour.
@@ -209,6 +242,29 @@
     ;(.start jsTour)
     ))
 
+(defn start-whiteboard
+  []
+  (let [^js jsTour (js/Shepherd.Tour.
+                    (bean/->js
+                     {:useModalOverlay    true
+                      :defaultStepOptions {:classes  "cp__onboarding-quick-tour"
+                                           :scrollTo false}}))
+        steps      (create-steps-whiteboard! jsTour)
+        steps      (map-indexed #(assoc %2 :text (str (:text %2) (inject-steps-indicator (inc %1) (count steps)))) steps)
+        [show-skip! hide-skip!] (make-skip-fns jsTour)]
+
+    ;; events
+    (doto jsTour
+      (.on "show" show-skip!)
+      (.on "hide" hide-skip!)
+      (.on "complete" hide-skip!)
+      (.on "cancel" hide-skip!))
+
+    (doseq [step steps]
+      (.addStep jsTour (bean/->js step)))
+
+    (.start jsTour)))
+
 (defn ready
   [callback]
   (p/then

+ 3 - 3
src/main/frontend/components/sidebar.cljs

@@ -256,7 +256,7 @@
    {}))
 
 (rum/defc ^:large-vars/cleanup-todo sidebar-nav
-  [route-match close-modal-fn left-sidebar-open? srs-open?
+  [route-match close-modal-fn left-sidebar-open? enable-whiteboards? srs-open?
    *closing? close-signal touching-x-offset]
   (let [[local-closing? set-local-closing?] (rum/use-state false)
         [el-rect set-el-rect!] (rum/use-state nil)
@@ -264,7 +264,6 @@
         ref-open?           (rum/use-ref left-sidebar-open?)
         default-home        (get-default-home-if-valid)
         route-name          (get-in route-match [:data :name])
-        enable-whiteboards? (state/enable-whiteboards?)
         on-contents-scroll  #(when-let [^js el (.-target %)]
                                (let [top  (.-scrollTop el)
                                      cls  (.-classList el)
@@ -423,6 +422,7 @@
         *closing?            (::closing? s)
         *touch-state         (::touch-state s)
         *close-signal        (::close-signal s)
+        enable-whiteboards?  (state/enable-whiteboards?)
         touch-point-fn       (fn [^js e] (some-> (gobj/get e "touches") (aget 0) (#(hash-map :x (.-clientX %) :y (.-clientY %)))))
         srs-open?            (= :srs (state/sub :modal/id))
         touching-x-offset    (and (some-> @*touch-state :after)
@@ -454,7 +454,7 @@
         (reset! *touch-state nil))}
 
      ;; sidebar contents
-     (sidebar-nav route-match close-fn left-sidebar-open? srs-open? *closing?
+     (sidebar-nav route-match close-fn left-sidebar-open? enable-whiteboards? srs-open? *closing?
                   @*close-signal (and touch-pending? touching-x-offset))]))
 
 (rum/defc recording-bar

+ 35 - 0
src/main/frontend/components/whiteboard.cljs

@@ -2,6 +2,7 @@
   "Whiteboard related components"
   (:require [cljs.math :as math]
             [frontend.components.content :as content]
+            [frontend.components.onboarding.quick-tour :as quick-tour]
             [frontend.components.page :as page]
             [frontend.components.reference :as reference]
             [frontend.context.i18n :refer [t]]
@@ -10,10 +11,12 @@
             [frontend.handler.common :as common-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.user :as user-handler]
+            [frontend.handler.config :as config-handler]
             [frontend.handler.whiteboard :as whiteboard-handler]
             [frontend.rum :refer [use-bounding-client-rect use-breakpoint
                                   use-click-outside]]
             [frontend.state :as state]
+            [frontend.storage :as storage]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [promesa.core :as p]
@@ -292,3 +295,35 @@
     (let [name (get-in route-match [:parameters :path :name])
           {:keys [block-id]} (get-in route-match [:parameters :query])]
       (whiteboard-page name block-id))))
+
+(defn onboarding-show
+  []
+  (when (and (user-handler/feature-available? :whiteboard)
+             (not (or (state/sub :whiteboard/onboarding-tour?)
+                      (state/enable-whiteboards?)
+                      (util/mobile?))))
+    (state/pub-event! [:whiteboard/onboarding])
+    (state/set-state! [:whiteboard/onboarding-tour?] true)
+    (storage/set :whiteboard-onboarding-tour? true)))
+
+(rum/defc onboarding-welcome
+  [close-fn]
+  [:div.cp__whiteboard-welcome
+   [:span.head-bg
+
+    [:strong (t :on-boarding/closed-feature (name (:whiteboard user-handler/feature-matrix)))]]
+
+   [:h1.text-2xl.font-bold.flex-col.sm:flex-row
+    (t :on-boarding/welcome-whiteboard-modal-title)]
+
+   [:p (t :on-boarding/welcome-whiteboard-modal-description)]
+
+   [:div.pt-6.flex.justify-center.space-x-2.sm:justify-end
+    (ui/button (t :on-boarding/welcome-whiteboard-modal-later) :on-click close-fn :background "gray" :class "opacity-60")
+    (ui/button (t :on-boarding/welcome-whiteboard-modal-start)
+               :on-click (fn []
+                           (config-handler/set-config! :feature/enable-whiteboards? true)
+                           (quick-tour/ready
+                            (fn []
+                              (quick-tour/start-whiteboard)
+                              (close-fn)))))]])

+ 44 - 0
src/main/frontend/components/whiteboard.css

@@ -222,3 +222,47 @@ html:is(.is-ios, is-native-ios, is-native-ipad) [data-page="whiteboard"] * {
   -ms-user-select: none;
   user-select: none;
 }
+
+.cp__whiteboard-welcome {
+  > .head-bg {
+    @apply flex m-auto mb-10 w-auto sm:w-[500px];
+
+    background-image: url("../img/whiteboard-welcome-dark.png");
+    background-size: contain;
+    background-repeat: no-repeat;
+    background-position: center;
+    padding-top: 86px;
+    max-width: 90vw;
+
+    > strong {
+      @apply block rounded text-gray-700 text-[10px] font-semibold px-2 py-0.5
+      m-auto translate-y-8 uppercase;
+
+      background-color: var(--ls-link-text-color);
+      color: var(--ls-primary-background-color);
+    }
+  }
+
+  > h1 {
+    color: var(--ls-title-text-color);
+  }
+
+  > h1, p {
+    @apply flex justify-center text-center;
+  }
+
+  > p {
+    @apply text-sm mx-10;
+
+    max-width: 540px;
+  }
+}
+
+
+html[data-theme='light'] {
+  .cp__whiteboard-welcome {
+    > .head-bg {
+      background-image: url("../img/whiteboard-welcome-light.png");
+    }
+  }
+}

+ 9 - 0
src/main/frontend/dicts.cljc

@@ -8,6 +8,7 @@
                           :default "tutorial-en.md")
         :tutorial/dummy-notes #?(:cljs (rc/inline "dummy-notes-en.md")
                                  :default "dummy-notes-en.md")
+        :on-boarding/closed-feature "Closed {1}"
         :on-boarding/demo-graph "This is a demo graph, changes will not be saved until you open a local folder."
         :on-boarding/add-graph "Add a graph"
         :on-boarding/open-local-dir "Open a local directory"
@@ -16,6 +17,14 @@
         :on-boarding/new-graph-desc-3 "/journals - store your journal pages"
         :on-boarding/new-graph-desc-4 "/pages - store the other pages"
         :on-boarding/new-graph-desc-5 "/logseq - store configuration, custom.css, and some metadata."
+        :on-boarding/welcome-whiteboard-modal-title "A new canvas for your thoughts."
+        :on-boarding/welcome-whiteboard-modal-description "Whiteboards are a great tool for brainstorming and organization. Now you can place any of your thoughts from the knowledge base or new ones next to each other on a spatial canvas to connect, associate and understand in new ways."
+        :on-boarding/welcome-whiteboard-modal-later "Later"
+        :on-boarding/welcome-whiteboard-modal-start "Start whiteboarding"
+        :on-boarding/tour-whiteboard-home "{1} Home for your whiteboards"
+        :on-boarding/tour-whiteboard-home-description "Whiteboards have their own section in the app where you can see them at a glance, create new ones or delete them easily."
+        :on-boarding/tour-whiteboard-new "{1} Create new whiteboard"
+        :on-boarding/tour-whiteboard-new-description "There are multiple ways of creating a new whiteboard. One of them is always right here in the dashboard."
         :help/start "Getting started"
         :help/about "About Logseq"
         :help/roadmap "Roadmap"

+ 3 - 3
src/main/frontend/format/block.cljs

@@ -1,7 +1,6 @@
 (ns frontend.format.block
   "Block code needed by app but not graph-parser"
-  (:require ["@sentry/react" :as Sentry]
-            [cljs-time.format :as tf]
+  (:require [cljs-time.format :as tf]
             [clojure.string :as string]
             [frontend.config :as config]
             [frontend.date :as date]
@@ -29,7 +28,8 @@ and handles unexpected failure."
                               :date-formatter (state/get-date-formatter)})
     (catch :default e
       (log/error :exception e)
-      (Sentry/captureException e)
+      (state/pub-event! [:capture-error {:error e
+                                         :payload {:type "Extract-blocks"}}])
       (notification/show! "An unexpected error occurred during block extraction." :error)
       [])))
 

+ 1 - 5
src/main/frontend/fs/capacitor_fs.cljs

@@ -228,11 +228,7 @@
         (str prefix "///private/" others))
 
       :else
-      (do
-        (state/pub-event! [:capture-error {:error (js/Error. "ios path missing slashes")
-                                           :payload {:type :error/ios-path-missing-slashes
-                                                     :path (gp-util/safe-subs (str path) 12)}}])
-        path))
+      path)
     path))
 
 (defn normalize-file-protocol-path [dir path]

+ 11 - 11
src/main/frontend/fs/sync.cljs

@@ -778,17 +778,17 @@
 
 (declare <rsapi-cancel-all-requests)
 
-(defn- build-local-file-metadatas
-  [this graph-uuid result read-chan!]
-  (loop [[[path metadata] & others] (js->clj result)
-         result #{}]
+(defn- <build-local-file-metadatas
+  [this graph-uuid r]
+  (go-loop [[[path metadata] & others] (js->clj r)
+            result #{}]
     (if-not (and path metadata)
       ;; finish
       result
       (let [normalized-path (path-normalize path)
-            encryptedFname (if (not= path normalized-path)
-                             (first (read-chan! (<encrypt-fnames this graph-uuid [normalized-path])))
-                             (get metadata "encryptedFname"))]
+            encryptedFname  (if (not= path normalized-path)
+                              (first (<! (<encrypt-fnames this graph-uuid [normalized-path])))
+                              (get metadata "encryptedFname"))]
         (recur others
                (conj result
                      (->FileMetadata (get metadata "size") (get metadata "md5") normalized-path
@@ -816,12 +816,12 @@
       (let [r (<! (<retry-rsapi #(p->c (ipc/ipc "get-local-all-files-meta" graph-uuid base-path))))]
         (if (instance? ExceptionInfo r)
           r
-          (build-local-file-metadatas this graph-uuid r <!)))))
+          (<! (<build-local-file-metadatas this graph-uuid r))))))
   (<get-local-files-meta [this graph-uuid base-path filepaths]
     (go
       (let [r (<! (<retry-rsapi #(p->c (ipc/ipc "get-local-files-meta" graph-uuid base-path filepaths))))]
         (assert (not (instance? ExceptionInfo r)) "get-local-files-meta shouldn't return exception")
-        (build-local-file-metadatas this graph-uuid r <!))))
+        (<! (<build-local-file-metadatas this graph-uuid r)))))
   (<rename-local-file [_ graph-uuid base-path from to]
     (<retry-rsapi #(p->c (ipc/ipc "rename-local-file" graph-uuid base-path
                                   (path-normalize from)
@@ -900,7 +900,7 @@
                                                                                :basePath base-path}))))]
         (if (instance? ExceptionInfo r)
           r
-          (build-local-file-metadatas this graph-uuid (.-result r) <!)))))
+          (<! (<build-local-file-metadatas this graph-uuid (.-result r)))))))
 
   (<get-local-files-meta [this graph-uuid base-path filepaths]
     (go
@@ -909,7 +909,7 @@
                                                       :basePath base-path
                                                       :filePaths filepaths}))))]
         (assert (not (instance? ExceptionInfo r)) "get-local-files-meta shouldn't return exception")
-        (build-local-file-metadatas this graph-uuid (.-result r) <!))))
+        (<! (<build-local-file-metadatas this graph-uuid (.-result r))))))
 
   (<rename-local-file [_ graph-uuid base-path from to]
     (p->c (.renameLocalFile mobile-util/file-sync

+ 16 - 3
src/main/frontend/handler/events.cljs

@@ -55,6 +55,7 @@
             [frontend.components.file-sync :as file-sync]
             [frontend.components.encryption :as encryption]
             [frontend.components.conversion :as conversion-component]
+            [frontend.components.whiteboard :as whiteboard]
             [goog.dom :as gdom]
             [logseq.db.schema :as db-schema]
             [promesa.core :as p]
@@ -99,7 +100,8 @@
                                     (util/uuid-string? (second (:sync-meta %)))) repos)
                     (sync/<sync-start)))))
             (ui-handler/re-render-root!)
-            (file-sync/maybe-onboarding-show status)))))))
+            (file-sync/maybe-onboarding-show status)
+            (whiteboard/onboarding-show)))))))
 
 (defmethod handle :user/logout [[_]]
   (file-sync-handler/reset-session-graphs)
@@ -419,8 +421,13 @@
   (posthog/capture type payload))
 
 (defmethod handle :capture-error [[_ {:keys [error payload]}]]
-  (Sentry/captureException error
-                           (bean/->js {:extra payload})))
+  (let [[user-uuid graph-uuid tx-id] @sync/graphs-txid
+        payload (assoc payload
+                       :user-id user-uuid
+                       :graph-id graph-uuid
+                       :tx-id tx-id)]
+    (Sentry/captureException error
+                            (bean/->js {:extra payload}))))
 
 (defmethod handle :exec-plugin-cmd [[_ {:keys [pid cmd action]}]]
   (commands/exec-plugin-simple-command! pid cmd action))
@@ -709,6 +716,12 @@
           (route-handler/redirect! {:to :page
                                     :path-params {:name (:block/name page-entity)}}))))))
 
+(defmethod handle :whiteboard/onboarding [[_ opts]]
+  (state/set-modal!
+   (fn [close-fn] (whiteboard/onboarding-welcome close-fn))
+   (merge {:close-btn?      false
+           :center?         true
+           :close-backdrop? false} opts)))
 
 (defmethod handle :file-sync/onboarding-tip [[_ type opts]]
   (let [type (keyword type)]

+ 11 - 0
src/main/frontend/handler/user.cljs

@@ -191,3 +191,14 @@
 (defn alpha-or-beta-user?
   []
   (or (alpha-user?) (beta-user?)))
+
+(defonce feature-matrix {:file-sync :beta
+                         :whiteboard :alpha})
+
+(defn feature-available?
+  [feature]
+  (when (logged-in?)
+    (case (feature feature-matrix)
+      :beta (alpha-or-beta-user?)
+      :alpha (alpha-user?)
+      false)))

+ 1 - 1
src/main/frontend/state.cljs

@@ -275,7 +275,7 @@
      :graph/importing-state                 {}
 
      :whiteboard/onboarding-whiteboard?     (or (storage/get :ls-onboarding-whiteboard?) false)
-     })))
+     :whiteboard/onboarding-tour?           (or (storage/get :whiteboard-onboarding-tour?) false)})))
 
 ;; Block ast state
 ;; ===============