Pārlūkot izejas kodu

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

charlie 2 gadi atpakaļ
vecāks
revīzija
ee7029c5fc

+ 8 - 17
.github/ISSUE_TEMPLATE/bug_report.yaml

@@ -31,33 +31,24 @@ body:
     validations:
       required: false
   - type: textarea
-    id: Screenshots
+    id: screenshots
     attributes:
       label: Screenshots
       description: |
-        If applicable, add screenshots to help explain your problem.
+        If applicable, add screenshots or screen recordings to help explain your problem.
     validations:
       required: false
   - type: textarea
-    id: desktop
+    id: platform
     attributes:
-      label: Desktop Platform Information
+      label: Desktop or mobile Platform Information
       description: |
-        Would you mind to tell us the system information about your desktop platform?
+        Would you mind to tell us the system information about your desktop or mobile platform?
       placeholder: |
         OS version, Browser or App, Logseq App version
-        example: MacOS 12.2, App, v0.5.9
-    validations:
-      required: false
-  - type: textarea
-    id: mobile
-    attributes:
-      label: Mobile Platform Information
-      description: |
-        Would you mind to tell us the system information about your mobile platform?
-      placeholder: |
-        Device, OS version, Browser or App, Logseq App version
-        example: iPhone6, iOS8.1, App, v0.5.9
+        example: MacOS 12.2, Desktop App v0.5.9
+        example: iPhone 12, iOS8.1, v0.5.9
+        example: Pixel XL, Android 12, v0.5.9
     validations:
       required: false
   - type: textarea

+ 1 - 0
.gitignore

@@ -1,3 +1,4 @@
+/e2e-dump
 /target
 /classes
 /checkouts

+ 7 - 0
docs/dev-practices.md

@@ -94,6 +94,13 @@ If e2e failed after first running:
 - `rm -rdf <repo dir>/tmp/`  
 - `rm -rdf <appData dir>/Electron`  (Reference: https://www.electronjs.org/de/docs/latest/api/app#appgetpathname)
 
+If e2e tests fail, they can be debugged by examining a trace dump with [the
+playwright trace
+viewer](https://playwright.dev/docs/trace-viewer#recording-a-trace). Locally
+this will get dumped into e2e-dump/. On CI the trace file will be under
+Artifacts at the bottom of a run page e.g.
+https://github.com/logseq/logseq/actions/runs/3574600322.
+
 ### Unit Testing
 
 Our unit tests use the [shadow-cljs test-runner](https://shadow-cljs.github.io/docs/UsersGuide.html#_testing). To run them:

+ 45 - 24
e2e-tests/page-search.spec.ts

@@ -1,5 +1,6 @@
 import { expect, Page } from '@playwright/test'
 import { test } from './fixtures'
+import { Block } from './types'
 import { IsMac, createRandomPage, newBlock, newInnerBlock, randomString, lastBlock, enterNextBlock } from './utils'
 
 /***
@@ -8,14 +9,14 @@ import { IsMac, createRandomPage, newBlock, newInnerBlock, randomString, lastBlo
  * Consider diacritics
  ***/
 
-test('Search page and blocks (diacritics)', async ({ page, block }) => {
-  let hotkeyOpenLink = 'Control+o'
-  let hotkeyBack = 'Control+['
-  if (IsMac) {
-    hotkeyOpenLink = 'Meta+o'
-    hotkeyBack = 'Meta+['
-  }
+ let hotkeyOpenLink = 'Control+o'
+ let hotkeyBack = 'Control+['
+ if (IsMac) {
+   hotkeyOpenLink = 'Meta+o'
+   hotkeyBack = 'Meta+['
+ }
 
+test('Search page and blocks (diacritics)', async ({ page, block }) => {
   const rand = randomString(20)
 
   // diacritic opening test
@@ -45,21 +46,43 @@ test('Search page and blocks (diacritics)', async ({ page, block }) => {
   await page.waitForTimeout(2000) // wait longer for search contents to render
   // 2 blocks + 1 page + 1 page content
   const searchResults = page.locator('#ui__ac-inner>div')
-  await expect(searchResults).toHaveCount(4)
+  await expect(searchResults).toHaveCount(5) // 1 page + 2 block + 2 page content
 
   await page.keyboard.press("Escape") // escape search box typing
   await page.waitForTimeout(500)
   await page.keyboard.press("Escape") // escape modal
 })
 
-async function alias_test(page: Page, page_name: string, search_kws: string[]) {
-  let hotkeyOpenLink = 'Control+o'
-  let hotkeyBack = 'Control+['
-  if (IsMac) {
-    hotkeyOpenLink = 'Meta+o'
-    hotkeyBack = 'Meta+['
-  }
+test('Search CJK', async ({ page, block }) => {
+  const rand = randomString(20)
+
+  // diacritic opening test
+  await createRandomPage(page)
 
+  await block.mustType('[[今日daytime进度条' + rand + ']] diacritic-block-1', { delay: 10 })
+  await page.keyboard.press(hotkeyOpenLink)
+
+  const pageTitle = page.locator('.page-title').first()
+  expect(await pageTitle.innerText()).toEqual('今日daytime进度条' + rand)
+
+  await page.waitForTimeout(500)
+
+  // check if diacritics are indexed
+  await page.click('#search-button')
+  await page.waitForSelector('[placeholder="Search or create page"]')
+  await page.type('[placeholder="Search or create page"]', '进度', { delay: 10 })
+
+  await page.waitForTimeout(2000) // wait longer for search contents to render
+  // 2 blocks + 1 page + 1 page content
+  const searchResults = page.locator('#ui__ac-inner>div')
+  await expect(searchResults).toHaveCount(4) // 1 new page + 1 page + 1 block + 1 page content
+
+  await page.keyboard.press("Escape") // escape search box typing
+  await page.waitForTimeout(500)
+  await page.keyboard.press("Escape") // escape modal
+})
+
+async function alias_test( block: Block, page: Page, page_name: string, search_kws: string[] ) {
   const rand = randomString(10)
   let target_name = page_name + ' target ' + rand
   let alias_name = page_name + ' alias ' + rand
@@ -107,7 +130,7 @@ async function alias_test(page: Page, page_name: string, search_kws: string[]) {
   await page.type('textarea >> nth=0', alias_test_content_2)
   await page.keyboard.press(hotkeyBack)
 
-  // pressing enter opening test
+  // pressing enter on alias opening test
   await lastBlock(page)
   await page.press('textarea >> nth=0', 'ArrowLeft')
   await page.press('textarea >> nth=0', 'ArrowLeft')
@@ -119,8 +142,8 @@ async function alias_test(page: Page, page_name: string, search_kws: string[]) {
   await page.type('textarea >> nth=0', alias_test_content_3)
   await page.keyboard.press(hotkeyBack)
 
-  // clicking opening test
-  await newBlock(page)
+  // clicking alias ref opening test
+  await block.enterNext()
   await page.waitForSelector('.page-blocks-inner .ls-block .page-ref >> nth=-1')
   await page.click('.page-blocks-inner .ls-block .page-ref >> nth=-1')
   await lastBlock(page)
@@ -138,15 +161,13 @@ async function alias_test(page: Page, page_name: string, search_kws: string[]) {
     await page.waitForTimeout(500)
 
     const results = await page.$$('#ui__ac-inner>div')
-    expect(results.length).toEqual(3) // page + block + alias property
+    expect(results.length).toEqual(5) // page + block + alias property + page content
 
     // test search results
     expect(await results[0].innerText()).toContain("Alias -> " + target_name)
     expect(await results[0].innerText()).toContain(alias_name)
-    expect(await results[1].innerText()).toContain(parent_title)
     expect(await results[1].innerText()).toContain("[[" + alias_name + "]]")
-    expect(await results[2].innerText()).toContain(target_name)
-    expect(await results[2].innerText()).toContain("alias:: [[" + alias_name + "]]")
+    expect(await results[2].innerText()).toContain("[[" + alias_name + "]]")
 
     // test search entering (page)
     page.keyboard.press("Enter")
@@ -171,6 +192,6 @@ async function alias_test(page: Page, page_name: string, search_kws: string[]) {
   // TODO: search clicking (alias property)
 }
 
-test.skip('page diacritic alias', async ({ page }) => {
-  await alias_test(page, "ü", ["ü", "ü", "Ü"])
+test.skip('page diacritic alias', async ({ block, page }) => {
+  await alias_test(block, page, "ü", ["ü", "ü", "Ü"])
 })

+ 1 - 0
e2e-tests/utils.ts

@@ -115,6 +115,7 @@ export async function newInnerBlock(page: Page): Promise<Locator> {
   return page.locator('textarea >> nth=0')
 }
 
+// Deprecated by block.enterNext
 export async function newBlock(page: Page): Promise<Locator> {
   let blockNumber = await page.locator('.page-blocks-inner .ls-block').count()
   await lastBlock(page)

+ 4 - 2
shadow-cljs.edn

@@ -33,10 +33,12 @@
                                                 "externs.js"]
                            :warnings           {:fn-deprecated false
                                                 :redef false}}
+        :build-hooks      [(shadow.hooks/git-revision-hook "--long --always --dirty")]
         :closure-defines  {goog.debug.LOGGING_ENABLED       true
-                           frontend.config/ENABLE-PLUGINS   #shadow/env ["ENABLE_PLUGINS"   :as :bool :default true]
+                           frontend.config/ENABLE-PLUGINS #shadow/env ["ENABLE_PLUGINS"   :as :bool :default true]
                            frontend.config/ENABLE-FILE-SYNC-PRODUCTION #shadow/env ["ENABLE_FILE_SYNC_PRODUCTION" :as :bool :default true]
-                           frontend.config/TEST #shadow/env ["LOGSEQ_CI" :as :bool :default false]}
+                           frontend.config/TEST #shadow/env ["LOGSEQ_CI" :as :bool :default false]
+                           frontend.config/REVISION #shadow/env ["LOGSEQ_REVISION" :default "dev"]} ;; set by git-revision-hook
 
         ;; NOTE: electron, browser/mobile-app use different asset-paths.
         ;;   For browser/mobile-app devs, assets are located in /static/js(via HTTP root).

+ 19 - 6
src/dev-cljs/shadow/hooks.clj

@@ -1,15 +1,15 @@
 (ns shadow.hooks
   (:require [clojure.java.shell :refer [sh]]
-            [clojure.string :as str]))
+            [clojure.string :as string]))
 
 ;; copied from https://gist.github.com/mhuebert/ba885b5e4f07923e21d1dc4642e2f182
 (defn exec [& cmd]
-  (let [cmd (str/split (str/join " " (flatten cmd)) #"\s+")
-        _ (println (str/join " " cmd))
+  (let [cmd (string/split (string/join " " (flatten cmd)) #"\s+")
+        _ (println (string/join " " cmd))
         {:keys [exit out err]} (apply sh cmd)]
     (if (zero? exit)
-      (when-not (str/blank? out)
-        (println out))
+      (when-not (string/blank? out)
+        (string/trim out))
       (println err))))
 
 (defn purge-css
@@ -27,5 +27,18 @@
     :dev
     (do
       (exec "mkdir -p" public-dir)
-      (exec "cp" css-source (str public-dir "/" (last (str/split css-source #"/"))))))
+      (exec "cp" css-source (str public-dir "/" (last (string/split css-source #"/"))))))
   state)
+
+(defn git-revision-hook
+  {:shadow.build/stage :configure}
+  [build-state & args]
+  (let [defines-in-config (get-in build-state [:shadow.build/config :closure-defines])
+        defines-in-options (get-in build-state [:compiler-options :closure-defines])
+        revision (exec "git" "describe" args)]
+    (prn ::git-revision-hook revision)
+    (-> build-state
+        (assoc-in [:shadow.build/config :closure-defines]
+                  (assoc defines-in-config 'frontend.config/REVISION revision))
+        (assoc-in [:compiler-options :closure-defines]
+                  (assoc defines-in-options 'frontend.config/REVISION revision)))))

+ 23 - 3
src/electron/electron/search.cljs

@@ -273,17 +273,33 @@
         (->>
          (concat matched-result
                  (search-blocks-aux database non-match-sql non-match-input page limit))
-         (distinct-by :id)
+         (distinct-by :rowid)
          (take limit)
          (vec))))))
 
+(defn- snippet-by
+  [content length]
+  (str (subs content 0 length) (when (> (count content) 250) "...")))
+
 (defn- search-pages-res-unpack
   [arr]
   (let [[rowid uuid content snippet] arr]
     {:id      rowid
      :uuid    uuid
      :content content
-     :snippet snippet}))
+     ;; post processing
+     :snippet (let [;; Remove title from snippet
+                    flag-title " $<pfts_f6ld$ "
+                    flag-title-pos (string/index-of snippet flag-title)
+                    snippet (if flag-title-pos
+                              (subs snippet (+ flag-title-pos (count flag-title)))
+                              snippet)
+                    ;; Cut snippet to 250 chars for non-matched results
+                    flag-highlight "$pfts_2lqh>$ "
+                    snippet (if (string/includes? snippet flag-highlight)
+                              snippet
+                              (snippet-by snippet 250))]
+                snippet)}))
 
 (defn- search-pages-aux
   [database sql input limit]
@@ -301,6 +317,7 @@
   (when-let [database (get-db repo)]
     (when-not (string/blank? q)
       (let [match-inputs (get-match-inputs q)
+            non-match-input (str "%" (string/replace q #"\s+" "%") "%")
             limit  (or limit 20)
             ;; https://www.sqlite.org/fts5.html#the_highlight_function
             ;; the 2nd column in pages_fts (content)
@@ -310,6 +327,8 @@
             select (str "select rowid, uuid, content, " snippet-aux " from pages_fts where ")
             match-sql (str select
                            " content match ? order by rank limit ?")
+            non-match-sql (str select
+                               " content like ? limit ?")
             matched-result (->>
                             (map
                               (fn [match-input]
@@ -317,7 +336,8 @@
                               match-inputs)
                             (apply concat))]
         (->>
-         matched-result
+         (concat matched-result
+                 (search-pages-aux database non-match-sql non-match-input limit))
          (distinct-by :id)
          (take limit)
          (vec))))))

+ 1 - 1
src/main/frontend/components/encryption.cljs

@@ -14,7 +14,7 @@
   [*show-password?]
   [:div.flex.flex-row.items-center
    [:label.px-1 {:for "show-password"}
-    (ui/checkbox {:checked?  @*show-password?
+    (ui/checkbox {:checked   @*show-password?
                   :on-change (fn [e]
                                (reset! *show-password? (util/echecked? e)))
                   :id        "show-password"})

+ 18 - 0
src/main/frontend/components/header.cljs

@@ -18,6 +18,7 @@
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.util :as util]
+            [frontend.version :refer [version]]
             [reitit.frontend.easy :as rfe]
             [rum.core :as rum]))
 
@@ -57,6 +58,16 @@
       :on-click on-click}
      (ui/icon "menu-2" {:size ui/icon-size})]))
 
+(def bug-report-url
+  (let [platform (str "App Version: " version "\n"
+                      "Platform: " (.-userAgent js/navigator) "\n"
+                      "Language: " (.-language js/navigator))]
+    (str "https://github.com/logseq/logseq/issues/new?"
+         "title=&"
+         "template=bug_report.yaml&"
+         "platform="
+         (js/encodeURIComponent platform))))
+
 (rum/defc dropdown-menu < rum/reactive
   < {:key-fn #(identity "repos-dropdown-menu")}
   [{:keys [current-repo t]}]
@@ -101,6 +112,13 @@
                   :title (t :discourse-title)
                   :target "_blank"}
         :icon (ui/icon "brand-discord")}
+       
+       {:title [:div.flex-row.flex.justify-between.items-center
+                [:span "Bug report"]]
+        :options {:href bug-report-url
+                  :title "Fire a bug report on Github"
+                  :target "_blank"}
+        :icon (ui/icon "bug")} 
 
        (when (and (state/sub :auth/id-token) (user-handler/logged-in?))
          {:title (str (t :logout) " (" (user-handler/email) ")")

+ 4 - 3
src/main/frontend/components/page.cljs

@@ -287,7 +287,8 @@
                   (if fmt-journal? (date/journal-title->custom-format title) title))
           old-name (or title page-name)]
       [:h1.page-title.flex.cursor-pointer.gap-1.w-full
-       {:on-mouse-down (fn [e]
+       {:class (when-not whiteboard-page? "title")
+        :on-mouse-down (fn [e]
                          (when (util/right-click? e)
                            (state/set-state! :page-title/context {:page page-name})))
         :on-click (fn [e]
@@ -415,8 +416,8 @@
                                    (page-mouse-leave e *control-show?))}
                 (page-blocks-collapse-control title *control-show? *all-collapsed?)])
              (when-not whiteboard?
-               [:div.flex-1.flex-row.w-full
-                [:h1.title.ls-page-title (page-title page-name icon title format fmt-journal?)]])
+               [:div.ls-page-title.flex-1.flex-row.w-full
+                (page-title page-name icon title format fmt-journal?)])
              (when (not config/publishing?)
                (when config/lsp-enabled?
                  [:div.flex.flex-row

+ 11 - 2
src/main/frontend/components/settings.cljs

@@ -77,7 +77,16 @@
                :else
                nil)]
 
-       [:div.text-sm version]
+       [:div.text-sm.cursor
+        {:title (str "Revision: " config/revison)
+         :on-click (fn []
+                     (notification/show! [:div "Current Revision: "
+                                          [:a {:target "_blank"
+                                               :href (str "https://github.com/logseq/logseq/commit/" config/revison)}
+                                           config/revison]]
+                                         :info
+                                         false))}
+        version]
 
        [:a.text-sm.fade-link.underline.inline
         {:target "_blank"
@@ -306,7 +315,7 @@
    [:label.block.text-sm.font-medium.leading-5.opacity-70
     {:for "custom_date_format"}
     (t :settings-page/custom-date-format)
-    (ui/tippy {:html        (t :settings-page/custom-date-format-warning)     
+    (ui/tippy {:html        (t :settings-page/custom-date-format-warning)
                :class       "tippy-hover ml-2"
                :interactive true
                :disabled    false}

+ 3 - 0
src/main/frontend/config.cljs

@@ -16,6 +16,9 @@
 (goog-define PUBLISHING false)
 (defonce publishing? PUBLISHING)
 
+(goog-define REVISION "unknown")
+(defonce revison REVISION)
+
 (reset! state/publishing? publishing?)
 
 (goog-define TEST false)

+ 4 - 2
src/main/frontend/fs/sync.cljs

@@ -2794,7 +2794,7 @@
 
   (idle [this]
     (go
-      (let [{:keys [stop remote->local local->remote local->remote-full-sync remote->local-full-sync pause] :as result}
+      (let [{:keys [stop remote->local local->remote local->remote-full-sync remote->local-full-sync pause resume] :as result}
             (<! ops-chan)]
         (cond
           (or stop (nil? result))
@@ -2809,6 +2809,8 @@
           (<! (.schedule this ::remote->local-full-sync nil nil))
           pause
           (<! (.schedule this ::pause nil nil))
+          resume
+          (<! (.schedule this ::idle nil nil))
           :else
           (do
             (state/pub-event! [:capture-error {:error (js/Error. "sync/wrong-ops-chan-when-idle")
@@ -2816,7 +2818,7 @@
                                                          :ops-chan-result result
                                                          :user-id user-uuid
                                                          :graph-id graph-uuid}}])
-            nil)))))
+            (<! (.schedule this ::idle nil nil)))))))
 
   (full-sync [this]
     (go

+ 9 - 6
src/main/frontend/handler/command_palette.cljs

@@ -79,12 +79,15 @@
   To add i18n support, prefix `id` with command and put that item in dict.
   Example: {:zh-CN {:command.document/open-logseq-doc \"打开文档\"}}"
   [{:keys [id] :as command}]
-  (spec/validate :command/command command)
-  (let [cmds (get-commands)]
-    (if (some (fn [existing-cmd] (= (:id existing-cmd) id)) cmds)
-      (log/error :command/register {:msg "Failed to register command. Command with same id already exist"
-                                    :id  id})
-      (state/set-state! :command-palette/commands (conj cmds command)))))
+  (if (:command/shortcut command)
+    (log/error :shortcut/missing (str "Shortcut is missing for " (:id command)))
+    (do
+      (spec/validate :command/command command)
+      (let [cmds (get-commands)]
+        (if (some (fn [existing-cmd] (= (:id existing-cmd) id)) cmds)
+          (log/error :command/register {:msg "Failed to register command. Command with same id already exist"
+                                        :id  id})
+          (state/set-state! :command-palette/commands (conj cmds command)))))))
 
 (defn unregister
   [id]

+ 12 - 15
src/main/frontend/handler/editor.cljs

@@ -1,6 +1,5 @@
 (ns ^:no-doc frontend.handler.editor
-  (:require ["path" :as path]
-            [clojure.set :as set]
+  (:require [clojure.set :as set]
             [clojure.string :as string]
             [clojure.walk :as w]
             [dommy.core :as dom]
@@ -1329,20 +1328,18 @@
              (util/format "[[%s][%s]]" url file-name))
       nil)))
 
-(defn ensure-assets-dir!
+(defn- ensure-assets-dir!
   [repo]
-  (let [repo-dir (config/get-repo-dir repo)
-        assets-dir "assets"]
-    (p/then
-     (fs/mkdir-if-not-exists (str repo-dir "/" assets-dir))
-     (fn [] [repo-dir assets-dir]))))
-
-(defn get-asset-path [filename]
-  (p/let [[repo-dir assets-dir] (ensure-assets-dir! (state/get-current-repo))
-          path (path/join repo-dir assets-dir filename)]
-    (if (mobile-util/native-android?)
-      path
-      (js/encodeURI (js/decodeURI path)))))
+  (p/let [repo-dir (config/get-repo-dir repo)
+          assets-dir "assets"
+          _ (fs/mkdir-if-not-exists (str repo-dir "/" assets-dir))]
+    [repo-dir assets-dir]))
+
+(defn get-asset-path
+  "Get asset path from filename, ensure assets dir exists"
+  [filename]
+  (p/let [[repo-dir assets-dir] (ensure-assets-dir! (state/get-current-repo))]
+    (util/safe-path-join repo-dir assets-dir filename)))
 
 (defn save-assets!
   ([_ repo files]

+ 1 - 1
src/main/frontend/handler/events.cljs

@@ -86,7 +86,7 @@
         nil
         (map? result)
         (do
-          (state/set-state! :user/info result)
+          (state/set-user-info! result)
           (let [status (if (user-handler/alpha-or-beta-user?) :welcome :unavailable)]
             (when (and (= status :welcome) (user-handler/logged-in?))
               (when-not (false? (state/enable-sync?)) ; user turns it off

+ 33 - 30
src/main/frontend/mobile/camera.cljs

@@ -6,32 +6,35 @@
             [frontend.handler.editor :as editor-handler]
             [frontend.state :as state]
             [frontend.date :as date]
-            [frontend.util :as util]
             [frontend.commands :as commands]
             [goog.object :as gobj]
             [frontend.util.cursor :as cursor]))
 
-(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})))
-          filename (str (date/get-date-time-string-2) ".jpeg")
-          path (editor-handler/get-asset-path filename)
-          _file (when photo
-                  (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- take-or-choose-photo []
+  (-> (.getPhoto Camera (clj->js
+                         {:allowEditing (get-in
+                                         (state/get-config)
+                                         [:mobile/photo :allow-editing?])
+                          :quality (get-in (state/get-config)
+                                           [:mobile/photo :quality] 80)
+                          :saveToGallery true
+                          :resultType (.-Base64 CameraResultType)}))
+      (p/catch (fn [error]
+                 (log/error :photo/get-failed {:error error})))
+      (p/then (fn [photo]
+                (prn ::debug-photo photo)
+                (if (nil? photo)
+                  (p/resolved nil)
+                  ;; NOTE: For iOS and Android, only jpeg format will be returned as base64 string.
+                  ;; See-also: https://capacitorjs.com/docs/apis/camera#galleryphoto
+                  (p/let [filename (str (date/get-date-time-string-2) ".jpeg")
+                          image-path (editor-handler/get-asset-path filename)
+                          _ret (.writeFile Filesystem (clj->js {:data (.-base64String photo)
+                                                                :path image-path
+                                                                :recursive true}))]
+                    filename))))
+      (p/catch (fn [error]
+                 (log/error :file/write-failed {:error error})))))
 
 (defn embed-photo [id]
   (let [block (state/get-edit-block)
@@ -48,11 +51,11 @@
 
                        :else " ")
         format (:block/format block)]
-    (p/let [filename (save-photo)
-            url (util/format "../assets/%s" filename)]
-      (commands/simple-insert!
-       id
-       (str left-padding
-            (editor-handler/get-asset-file-link format url filename true)
-        " ")
-       {}))))
+    (p/let [filename (take-or-choose-photo)]
+      (when (not-empty filename)
+        (commands/simple-insert!
+         id
+         (str left-padding
+              (editor-handler/get-asset-file-link format (str "../assets/" filename) filename true)
+              " ")
+         {})))))

+ 16 - 16
src/main/frontend/mobile/mobile_bar.cljs

@@ -48,23 +48,23 @@
      [:button.bottom-action
       {:on-mouse-down (fn [event]
                         (util/stop event)
-                        (let [target (gdom/getNextElementSibling (.-target event))]
+                        (let [target (gdom/getNextElementSibling (gdom/getParentElement (.-target event)))]
                           (dom/add-class! target "show-submenu")))}
-      (ui/icon "calendar" {:size ui/icon-size})
-      [:div.submenu.fixed.hidden.flex.flex-col.w-full.justify-evenly
-       {:style {:bottom @util/keyboard-height}}
-       (command-cp #(let [today (page-handler/get-page-ref-text (date/today))]
-                      (commands/simple-insert! parent-id today {}))
-                   "Today")
-       (command-cp #(let [tomorrow (page-handler/get-page-ref-text (date/tomorrow))]
-                      (commands/simple-insert! parent-id tomorrow {}))
-                   "Tomorrow")
-       (command-cp #(let [yesterday (page-handler/get-page-ref-text (date/yesterday))]
-                      (commands/simple-insert! parent-id yesterday {}))
-                   "Yesterday")
-       (command-cp #(let [timestamp (date/get-current-time)]
-                      (commands/simple-insert! parent-id timestamp {}))
-                   "Time")]]]))
+      (ui/icon "calendar" {:size ui/icon-size})]
+     [:div.submenu.fixed.left-0.hidden.w-full.flex-row.justify-evenly.items-center
+      {:style {:bottom @util/keyboard-height}}
+      (command-cp #(let [today (page-handler/get-page-ref-text (date/today))]
+                     (commands/simple-insert! parent-id today {}))
+                  "Today")
+      (command-cp #(let [tomorrow (page-handler/get-page-ref-text (date/tomorrow))]
+                     (commands/simple-insert! parent-id tomorrow {}))
+                  "Tomorrow")
+      (command-cp #(let [yesterday (page-handler/get-page-ref-text (date/yesterday))]
+                     (commands/simple-insert! parent-id yesterday {}))
+                  "Yesterday")
+      (command-cp #(let [timestamp (date/get-current-time)]
+                     (commands/simple-insert! parent-id timestamp {}))
+                  "Time")]]))
 
 (defn commands
   [parent-id]

+ 8 - 5
src/main/frontend/modules/instrumentation/sentry.cljs

@@ -14,11 +14,14 @@
                          version)
    :environment (if config/dev? "development" "production")
    :initialScope {:tags
-                  {:platform (cond
-                               (util/electron?) "electron"
-                               (mobile-util/native-platform?) "mobile"
-                               :else "web")
-                   :publishing config/publishing?}}
+                  (merge
+                   (when (not-empty config/revison)
+                     {:revision config/revison})
+                   {:platform (cond
+                                (util/electron?) "electron"
+                                (mobile-util/native-platform?) "mobile"
+                                :else "web")
+                    :publishing config/publishing?})}
    ;; :integrations [(new posthog/SentryIntegration posthog "logseq" 5311485)
    ;;                (new BrowserTracing)]
    :debug config/dev?

+ 3 - 2
src/main/frontend/search/db.cljs

@@ -30,13 +30,14 @@
 (defn page->index
   "Convert a page name to the index for searching (page content level)
    Generate index based on the DB content AT THE POINT OF TIME"
-  [{:block/keys [uuid _original-name] :as page}]
+  [{:block/keys [uuid original-name] :as page}]
   (when-let [content (some-> (:block/file page)
                              (:file/content))]
     (when-not (> (count content) (* (max-len) 10))
       {:id   (:db/id page)
        :uuid (str uuid)
-       :content (sanitize content)})))
+       ;; Add page name to the index
+       :content (sanitize (str "$pfts_f6ld>$ " original-name " $<pfts_f6ld$ " content))})))
 
 (defn build-blocks-indice
   ;; TODO: Remove repo effects fns further up the call stack. db fns need standardization on taking connection

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

@@ -263,7 +263,7 @@
      :file-sync/graph-state                 {:current-graph-uuid nil
                                              ;; graph-uuid -> ...
                                              }
-
+     :user/info                             {:UserGroups (storage/get :user-groups)}
      :encryption/graph-parsing?             false
 
      :ui/loading?                           {}
@@ -2069,3 +2069,11 @@ Similar to re-frame subscriptions"
      (when (and shape-id (parse-uuid shape-id))
        (. api selectShapes shape-id)
        (. api zoomToSelection)))))
+
+(defn set-user-info!
+  [info]
+  (when info
+    (set-state! :user/info info)
+    (let [groups (:UserGroups info)]
+      (when (seq groups)
+        (storage/set :user-groups groups)))))