mirror of https://github.com/logseq/logseq
Merge branch 'feat/db' into refactor/db-properties-schema
commit
7084b53dad
|
@ -222,7 +222,7 @@ jobs:
|
|||
DEBUG: "pw:api"
|
||||
RELEASE: true # skip dev only test
|
||||
|
||||
build-linux:
|
||||
build-linux-x64:
|
||||
runs-on: ubuntu-20.04
|
||||
needs: [ compile-cljs ]
|
||||
steps:
|
||||
|
@ -265,7 +265,61 @@ jobs:
|
|||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: logseq-linux-builds
|
||||
name: logseq-linux-x64-builds
|
||||
path: builds
|
||||
|
||||
build-linux-arm64:
|
||||
runs-on: ubuntu-20.04
|
||||
needs: [ compile-cljs ]
|
||||
steps:
|
||||
- name: Download The Static Asset
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: static
|
||||
path: static
|
||||
|
||||
- name: Retrieve tag version
|
||||
id: ref
|
||||
run: |
|
||||
pkgver=$(cat ./static/VERSION)
|
||||
echo "version=$pkgver" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Install Node.js, NPM and Yarn
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Fetch deps
|
||||
env:
|
||||
npm_config_arch: arm64
|
||||
run: |
|
||||
yarn install --target_arch=arm64 --target_platform=linux
|
||||
rsapi_version=`node -e 'console.log(require("@logseq/rsapi/package.json").optionalDependencies["@logseq/rsapi-linux-arm64-gnu"])'`
|
||||
temp_dir=`mktemp -d`
|
||||
cd "$temp_dir"
|
||||
echo '{"dependencies": {"@logseq/rsapi-linux-arm64-gnu": "'"$rsapi_version"'"}}' > package.json
|
||||
yarn install --ignore-platform
|
||||
cd -
|
||||
mv "$temp_dir/node_modules/@logseq/rsapi-linux-arm64-gnu" node_modules/@logseq/rsapi-linux-arm64-gnu
|
||||
rm -rf "$temp_dir" "node_modules/@logseq/rsapi-linux-x64-gnu"
|
||||
working-directory: ./static
|
||||
|
||||
- name: Build/Release Electron App
|
||||
run: yarn electron:make-linux-arm64
|
||||
working-directory: ./static
|
||||
|
||||
- name: Save artifacts
|
||||
run: |
|
||||
mkdir -p builds
|
||||
# NOTE: save VERSION file to builds directory
|
||||
cp static/VERSION ./builds/VERSION
|
||||
# mv static/out/make/*-*.AppImage ./builds/Logseq-linux-arm64-${{ steps.ref.outputs.version }}.AppImage
|
||||
mv static/out/make/zip/linux/arm64/*-linux-arm64-*.zip ./builds/Logseq-linux-arm64-${{ steps.ref.outputs.version }}.zip
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: logseq-linux-arm64-builds
|
||||
path: builds
|
||||
|
||||
build-windows:
|
||||
|
@ -498,7 +552,7 @@ jobs:
|
|||
|
||||
nightly-release:
|
||||
if: ${{ github.event_name == 'schedule' || github.event.inputs.build-target == 'nightly' }}
|
||||
needs: [ build-macos-x64, build-macos-arm64, build-linux, build-windows, build-android, e2e-test ]
|
||||
needs: [ build-macos-x64, build-macos-arm64, build-linux-x64, build-linux-arm64, build-windows, build-android, e2e-test ]
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Download MacOS x64 Artifacts
|
||||
|
@ -513,10 +567,16 @@ jobs:
|
|||
name: logseq-darwin-arm64-builds
|
||||
path: ./
|
||||
|
||||
- name: Download The Linux Artifacts
|
||||
- name: Download The Linux x64 Artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: logseq-linux-builds
|
||||
name: logseq-linux-x64-builds
|
||||
path: ./
|
||||
|
||||
- name: Download The Linux arm64 Artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: logseq-linux-arm64-builds
|
||||
path: ./
|
||||
|
||||
- name: Download The Windows Artifact
|
||||
|
@ -565,7 +625,7 @@ jobs:
|
|||
release:
|
||||
# NOTE: For now, we only have beta channel to be released on Github
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.build-target == 'beta' }}
|
||||
needs: [ build-macos-x64, build-macos-arm64, build-linux, build-windows, e2e-test ]
|
||||
needs: [ build-macos-x64, build-macos-arm64, build-linux-x64, build-linux-arm64, build-windows, e2e-test ]
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Download MacOS x64 Artifacts
|
||||
|
@ -580,10 +640,16 @@ jobs:
|
|||
name: logseq-darwin-arm64-builds
|
||||
path: ./
|
||||
|
||||
- name: Download The Linux Artifacts
|
||||
- name: Download The Linux x64 Artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: logseq-linux-builds
|
||||
name: logseq-linux-x64-builds
|
||||
path: ./
|
||||
|
||||
- name: Download The Linux arm64 Artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: logseq-linux-arm64-builds
|
||||
path: ./
|
||||
|
||||
- name: Download The Windows Artifact
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
[logseq.db.frontend.property :as db-property]
|
||||
[logseq.db.frontend.property.type :as db-property-type]
|
||||
[logseq.common.util.macro :as macro-util]
|
||||
[logseq.common.util.date-time :as date-time-util]
|
||||
[logseq.db.sqlite.util :as sqlite-util]
|
||||
[logseq.db :as ldb]
|
||||
[logseq.db.frontend.rules :as rules]
|
||||
|
@ -110,6 +111,108 @@
|
|||
(keep #(convert-tag-to-class % tag-classes) tags)))))
|
||||
block))
|
||||
|
||||
(defn- update-block-marker
|
||||
"If a block has a marker, convert it to a task object"
|
||||
[block db {:keys [log-fn]}]
|
||||
(if-let [marker (:block/marker block)]
|
||||
(let [old-to-new {"TODO" :logseq.task/status.todo
|
||||
"LATER" :logseq.task/status.todo
|
||||
"IN-PROGRESS" :logseq.task/status.doing
|
||||
"NOW" :logseq.task/status.doing
|
||||
"DOING" :logseq.task/status.doing
|
||||
"DONE" :logseq.task/status.done
|
||||
"WAIT" :logseq.task/status.backlog
|
||||
"WAITING" :logseq.task/status.backlog
|
||||
"CANCELED" :logseq.task/status.canceled
|
||||
"CANCELLED" :logseq.task/status.canceled}
|
||||
status-prop (:block/uuid (d/entity db :logseq.task/status))
|
||||
status-ident (or (old-to-new marker)
|
||||
(do
|
||||
(log-fn :invalid-todo (str (pr-str marker) " is not a valid marker so setting it to TODO"))
|
||||
:logseq.task/status.todo))
|
||||
status-value (:block/uuid (d/entity db status-ident))]
|
||||
(-> block
|
||||
(update :block/properties assoc status-prop status-value)
|
||||
(update :block/content string/replace-first (re-pattern (str marker "\\s*")) "")
|
||||
(update :block/tags (fnil conj []) :logseq.class/task)
|
||||
(update :block/refs (fn [refs]
|
||||
(into (remove #(= marker (:block/original-name %)) refs)
|
||||
[:logseq.class/task :logseq.task/status status-ident])))
|
||||
(update :block/path-refs (fn [refs]
|
||||
(into (remove #(= marker (:block/original-name %)) refs)
|
||||
[:logseq.class/task :logseq.task/status status-ident])))
|
||||
(dissoc :block/marker)))
|
||||
block))
|
||||
|
||||
(defn- update-block-priority
|
||||
[block db {:keys [log-fn]}]
|
||||
(if-let [priority (:block/priority block)]
|
||||
(let [old-to-new {"A" :logseq.task/priority.high
|
||||
"B" :logseq.task/priority.medium
|
||||
"C" :logseq.task/priority.low}
|
||||
priority-prop (:block/uuid (d/entity db :logseq.task/priority))
|
||||
priority-ident (or (old-to-new priority)
|
||||
(do
|
||||
(log-fn :invalid-priority (str (pr-str priority) " is not a valid priority so setting it to low"))
|
||||
:logseq.task/priority.low))
|
||||
priority-value (:block/uuid (d/entity db priority-ident))]
|
||||
(-> block
|
||||
(update :block/properties assoc priority-prop priority-value)
|
||||
(update :block/content string/replace-first (re-pattern (str "\\[#" priority "\\]" "\\s*")) "")
|
||||
(update :block/refs (fn [refs]
|
||||
(into (remove #(= priority (:block/original-name %)) refs)
|
||||
[:logseq.task/priority priority-ident])))
|
||||
(update :block/path-refs (fn [refs]
|
||||
(into (remove #(= priority (:block/original-name %)) refs)
|
||||
[:logseq.task/priority priority-ident])))
|
||||
(dissoc :block/priority)))
|
||||
block))
|
||||
|
||||
(defn- update-block-deadline
|
||||
":block/content doesn't contain DEADLINE.* text so unable to detect timestamp
|
||||
or repeater usage and notify user that they aren't supported"
|
||||
[block db {:keys [user-config]}]
|
||||
(if-let [deadline (:block/deadline block)]
|
||||
(let [deadline-prop (:block/uuid (d/entity db :logseq.task/deadline))
|
||||
deadline-page (or (ffirst (d/q '[:find (pull ?b [:block/uuid])
|
||||
:in $ ?journal-day
|
||||
:where [?b :block/journal-day ?journal-day]]
|
||||
db deadline))
|
||||
;; FIXME: Register new pages so that two different refs to same new page
|
||||
;; don't create different uuids and thus an invalid page
|
||||
(assoc (sqlite-util/build-new-page
|
||||
(date-time-util/int->journal-title deadline (common-config/get-date-formatter user-config)))
|
||||
:block/journal? true
|
||||
:block/journal-day deadline
|
||||
:block/format :markdown))]
|
||||
(-> block
|
||||
(update :block/properties assoc deadline-prop (:block/uuid deadline-page))
|
||||
(update :block/refs (fnil into []) [:logseq.task/deadline deadline-page])
|
||||
(update :block/path-refs (fnil into []) [:logseq.task/deadline deadline-page])
|
||||
(dissoc :block/deadline)))
|
||||
block))
|
||||
|
||||
(defn- update-block-scheduled
|
||||
"Should have same implementation as update-block-deadline"
|
||||
[block db {:keys [user-config]}]
|
||||
(if-let [scheduled (:block/scheduled block)]
|
||||
(let [scheduled-prop (:block/uuid (d/entity db :logseq.task/scheduled))
|
||||
scheduled-page (or (ffirst (d/q '[:find (pull ?b [:block/uuid])
|
||||
:in $ ?journal-day
|
||||
:where [?b :block/journal-day ?journal-day]]
|
||||
db scheduled))
|
||||
(assoc (sqlite-util/build-new-page
|
||||
(date-time-util/int->journal-title scheduled (common-config/get-date-formatter user-config)))
|
||||
:block/journal? true
|
||||
:block/journal-day scheduled
|
||||
:block/format :markdown))]
|
||||
(-> block
|
||||
(update :block/properties assoc scheduled-prop (:block/uuid scheduled-page))
|
||||
(update :block/refs (fnil into []) [:logseq.task/scheduled scheduled-page])
|
||||
(update :block/path-refs (fnil into []) [:logseq.task/scheduled scheduled-page])
|
||||
(dissoc :block/scheduled)))
|
||||
block))
|
||||
|
||||
(defn- text-with-refs?
|
||||
"Detects if a property value has text with refs e.g. `#Logseq is #awesome`
|
||||
instead of `#Logseq #awesome`. If so the property type is :default instead of :page"
|
||||
|
@ -448,6 +551,10 @@
|
|||
(handle-block-properties db page-names-to-uuids (:block/refs block) options)
|
||||
(update-block-refs page-names-to-uuids old-property-schemas options)
|
||||
(update-block-tags tag-classes page-names-to-uuids)
|
||||
(update-block-marker db options)
|
||||
(update-block-priority db options)
|
||||
(update-block-deadline db options)
|
||||
(update-block-scheduled db options)
|
||||
add-missing-timestamps
|
||||
;; ((fn [x] (prn :block-out x) x))
|
||||
;; TODO: org-mode content needs to be handled
|
||||
|
@ -831,6 +938,7 @@
|
|||
:user-config config
|
||||
:filename-format (or (:file/name-format config) :legacy)
|
||||
:verbose (:verbose options)}
|
||||
:user-config config
|
||||
:user-options (select-keys options [:tag-classes :property-classes :property-parent-classes])
|
||||
:page-tags-uuid (:block/uuid (d/entity @conn :logseq.property/page-tags))
|
||||
:import-state (new-import-state)
|
||||
|
|
|
@ -931,9 +931,10 @@
|
|||
(remove nil?)))))
|
||||
|
||||
(defn ^:api delete-block
|
||||
"Delete block from the tree."
|
||||
"FIXME: why expose this fn? there's already a public fn `delete-blocks!`
|
||||
Delete block from the tree."
|
||||
[repo conn txs-state node {:keys [children? children-check? date-formatter]
|
||||
:or {children-check? true}}]
|
||||
:or {children-check? true}}]
|
||||
(if (and children-check?
|
||||
(not children?)
|
||||
(first (:block/_parent (d/entity @conn [:block/uuid (:block/uuid (get-data node))]))))
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
"electron:dev": "electron-forge start",
|
||||
"electron:debug": "electron-forge start --inspect-electron",
|
||||
"electron:make": "electron-forge make",
|
||||
"electron:make-linux-arm64": "electron-forge make --platform=linux --arch=arm64",
|
||||
"electron:make-macos-arm64": "electron-forge make --platform=darwin --arch=arm64",
|
||||
"electron:publish:github": "electron-forge publish",
|
||||
"rebuild:all": "electron-rebuild -v 27.1.3 -f",
|
||||
|
@ -28,7 +29,7 @@
|
|||
"chokidar": "^3.5.1",
|
||||
"command-exists": "1.2.9",
|
||||
"diff-match-patch": "1.0.5",
|
||||
"dugite": "2.5.0",
|
||||
"dugite": "2.5.1",
|
||||
"electron-deeplink": "1.0.10",
|
||||
"electron-dl": "3.3.0",
|
||||
"electron-log": "4.3.1",
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
[datascript.core :as d]
|
||||
[datascript.storage :refer [IStorage]]
|
||||
[frontend.worker.async-util :include-macros true :refer [<?] :as async-util]
|
||||
[frontend.worker.db-listener :as db-listener]
|
||||
[frontend.worker.db-metadata :as worker-db-metadata]
|
||||
[frontend.worker.export :as worker-export]
|
||||
[frontend.worker.file :as file]
|
||||
|
@ -22,6 +23,7 @@
|
|||
[frontend.worker.rtc.snapshot :as rtc-snapshot]
|
||||
[frontend.worker.search :as search]
|
||||
[frontend.worker.state :as worker-state]
|
||||
[frontend.worker.undo-redo]
|
||||
[frontend.worker.util :as worker-util]
|
||||
[logseq.db :as ldb]
|
||||
[logseq.db.sqlite.common-db :as sqlite-common-db]
|
||||
|
@ -174,7 +176,9 @@
|
|||
conn (sqlite-common-db/get-storage-conn storage schema)]
|
||||
(swap! *datascript-conns assoc repo conn)
|
||||
(p/let [_ (op-mem-layer/<init-load-from-indexeddb! repo)]
|
||||
(rtc-db-listener/listen-to-db-changes! repo conn))))))
|
||||
(rtc-db-listener/listen-to-db-changes! repo conn)
|
||||
(db-listener/listen-db-changes! repo conn))
|
||||
))))
|
||||
|
||||
(defn- iter->vec [iter]
|
||||
(when iter
|
||||
|
|
|
@ -718,7 +718,7 @@
|
|||
(js/console.error e)
|
||||
(dec current-pos)))
|
||||
(dec current-pos))
|
||||
(dec current-pos))))
|
||||
current-pos)))
|
||||
|
||||
#?(:cljs
|
||||
;; for widen char
|
||||
|
@ -735,7 +735,7 @@
|
|||
(js/console.error e)
|
||||
(inc current-pos)))
|
||||
(inc current-pos))
|
||||
(inc current-pos))))
|
||||
current-pos)))
|
||||
|
||||
#?(:cljs
|
||||
(defn kill-line-before!
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
(ns frontend.worker.db-listener
|
||||
"Db listeners for worker-db."
|
||||
(:require [datascript.core :as d]))
|
||||
|
||||
|
||||
(defn- entity-datoms=>attr->datom
|
||||
[entity-datoms]
|
||||
(reduce
|
||||
(fn [m datom]
|
||||
(let [[_e a _v t add?] datom]
|
||||
(if-let [[_e _a _v old-t old-add?] (get m a)]
|
||||
(cond
|
||||
(and (= old-t t)
|
||||
(true? add?)
|
||||
(false? old-add?))
|
||||
(assoc m a datom)
|
||||
|
||||
(< old-t t)
|
||||
(assoc m a datom)
|
||||
|
||||
:else
|
||||
m)
|
||||
(assoc m a datom))))
|
||||
{} entity-datoms))
|
||||
|
||||
|
||||
(defmulti listen-db-changes
|
||||
(fn [listen-key & _] listen-key))
|
||||
|
||||
(defn listen-db-changes!
|
||||
[repo conn]
|
||||
(let [handlers (methods listen-db-changes)]
|
||||
(prn :listen-db-changes! (keys handlers))
|
||||
(d/unlisten! conn ::listen-db-changes!)
|
||||
(d/listen! conn ::listen-db-changes!
|
||||
(fn [{:keys [tx-data] :as args}]
|
||||
(let [datom-vec-coll (map vec tx-data)
|
||||
id->same-entity-datoms (group-by first datom-vec-coll)
|
||||
id-order (distinct (map first datom-vec-coll))
|
||||
same-entity-datoms-coll (map id->same-entity-datoms id-order)
|
||||
id->attr->datom (update-vals id->same-entity-datoms entity-datoms=>attr->datom)]
|
||||
(doseq [[k handler-fn] handlers]
|
||||
(handler-fn k (assoc args
|
||||
:repo repo
|
||||
:id->attr->datom id->attr->datom
|
||||
:same-entity-datoms-coll same-entity-datoms-coll))))))))
|
|
@ -18,10 +18,18 @@
|
|||
[entity-datoms]
|
||||
(reduce
|
||||
(fn [m datom]
|
||||
(let [[_e a _v t _add?] datom]
|
||||
(if-let [[_e _a _v old-t _old-add?] (get m a)]
|
||||
(if (<= old-t t)
|
||||
(let [[_e a _v t add?] datom]
|
||||
(if-let [[_e _a _v old-t old-add?] (get m a)]
|
||||
(cond
|
||||
(and (= old-t t)
|
||||
(true? add?)
|
||||
(false? old-add?))
|
||||
(assoc m a datom)
|
||||
|
||||
(< old-t t)
|
||||
(assoc m a datom)
|
||||
|
||||
:else
|
||||
m)
|
||||
(assoc m a datom))))
|
||||
{} entity-datoms))
|
||||
|
|
|
@ -8,11 +8,15 @@
|
|||
:db/latest-transact-time {}
|
||||
:worker/context {}
|
||||
|
||||
;; FIXME: this name :config is too general
|
||||
:config {}
|
||||
:git/current-repo nil
|
||||
:rtc/batch-processing? false
|
||||
:rtc/remote-batch-txs nil
|
||||
:rtc/downloading-graph? false}))
|
||||
:rtc/downloading-graph? false
|
||||
|
||||
:undo/repo->undo-stack (atom {})
|
||||
:undo/repo->redo-stack (atom {})}))
|
||||
|
||||
(defonce *rtc-ws-url (atom nil))
|
||||
|
||||
|
|
|
@ -1,35 +1,52 @@
|
|||
(ns frontend.worker.undo-redo
|
||||
"undo/redo related fns and op-schema"
|
||||
(:require [datascript.core :as d]))
|
||||
|
||||
(:require [datascript.core :as d]
|
||||
[frontend.worker.db-listener :as db-listener]
|
||||
[frontend.worker.state :as worker-state]
|
||||
[logseq.common.config :as common-config]
|
||||
[logseq.outliner.core :as outliner-core]
|
||||
[logseq.outliner.transaction :as outliner-tx]
|
||||
[malli.core :as m]
|
||||
[malli.util :as mu]))
|
||||
|
||||
(def undo-op-schema
|
||||
[:multi {:dispatch first}
|
||||
[:boundary
|
||||
[:cat :keyword]]
|
||||
[:insert-block
|
||||
[:cat :keyword
|
||||
[:map
|
||||
[:block-uuid :uuid]]]]
|
||||
[:move-block
|
||||
[:cat :keyword
|
||||
[:map
|
||||
[:block-uuid :uuid]
|
||||
[:block-origin-left :uuid]
|
||||
[:block-origin-parent :uuid]]]]
|
||||
[:remove-block
|
||||
[:cat :keyword
|
||||
[:map
|
||||
[:block-uuid :uuid]
|
||||
[:block-entity-map :map]]]]
|
||||
[:update-block
|
||||
[:cat :keyword
|
||||
[:map
|
||||
[:block-uuid :uuid]
|
||||
[:block-origin-content {:optional true} :string]
|
||||
;; TODO: add more attrs
|
||||
]]]])
|
||||
(mu/closed-schema
|
||||
[:multi {:dispatch first}
|
||||
[:boundary
|
||||
[:cat :keyword]]
|
||||
[:insert-block
|
||||
[:cat :keyword
|
||||
[:map
|
||||
[:block-uuid :uuid]]]]
|
||||
[:move-block
|
||||
[:cat :keyword
|
||||
[:map
|
||||
[:block-uuid :uuid]
|
||||
[:block-origin-left :uuid]
|
||||
[:block-origin-parent :uuid]]]]
|
||||
[:remove-block
|
||||
[:cat :keyword
|
||||
[:map
|
||||
[:block-uuid :uuid]
|
||||
[:block-entity-map
|
||||
[:map
|
||||
[:block/uuid :uuid]
|
||||
[:block/left :uuid]
|
||||
[:block/parent :uuid]
|
||||
[:block/content :string]
|
||||
[:block/created-at :int]
|
||||
[:block/updated-at :int]
|
||||
[:block/format :any]
|
||||
[:block/tags {:optional true} [:sequential :uuid]]]]]]]
|
||||
[:update-block
|
||||
[:cat :keyword
|
||||
[:map
|
||||
[:block-uuid :uuid]
|
||||
[:block-origin-content {:optional true} :string]
|
||||
;; TODO: add more attrs
|
||||
]]]]))
|
||||
|
||||
(def undo-ops-validator (m/validator [:sequential undo-op-schema]))
|
||||
|
||||
(defn reverse-op
|
||||
[db op]
|
||||
|
@ -68,3 +85,198 @@
|
|||
[:update-block
|
||||
(cond-> {:block-uuid block-uuid}
|
||||
block-origin-content (assoc :block-origin-content block-origin-content))]))))
|
||||
|
||||
|
||||
(def ^:private apply-conj-vec (partial apply (fnil conj [])))
|
||||
|
||||
(defn- push-undo-ops
|
||||
[repo ops]
|
||||
(swap! (:undo/repo->undo-stack @worker-state/*state) update repo apply-conj-vec ops))
|
||||
|
||||
(defn- pop-undo-op
|
||||
[repo]
|
||||
(let [repo->undo-stack (:undo/repo->undo-stack @worker-state/*state)]
|
||||
(when-let [peek-op (peek (@repo->undo-stack repo))]
|
||||
(swap! repo->undo-stack update repo pop)
|
||||
peek-op)))
|
||||
|
||||
(defn- push-redo-ops
|
||||
[repo ops]
|
||||
(swap! (:undo/repo->redo-stack @worker-state/*state) update repo apply-conj-vec ops))
|
||||
|
||||
(defn- pop-redo-op
|
||||
[repo]
|
||||
(let [repo->redo-stack (:undo/repo->redo-stack @worker-state/*state)]
|
||||
(when-let [peek-op (peek (@repo->redo-stack repo))]
|
||||
(swap! repo->redo-stack update repo pop)
|
||||
peek-op)))
|
||||
|
||||
|
||||
(defmulti reverse-apply-op (fn [op _conn _repo] (first op)))
|
||||
(defmethod reverse-apply-op :remove-block
|
||||
[op conn repo]
|
||||
(let [[_ {:keys [block-uuid block-entity-map]}] op]
|
||||
(when-let [left-entity (d/entity @conn [:block/uuid (:block/left block-entity-map)])]
|
||||
(let [sibling? (not= (:block/left block-entity-map) (:block/parent block-entity-map))]
|
||||
(outliner-tx/transact!
|
||||
{:gen-undo-op? false
|
||||
:outliner-op :insert-blocks
|
||||
:transact-opts {:repo repo
|
||||
:conn conn}}
|
||||
(outliner-core/insert-blocks! repo conn
|
||||
[(cond-> {:block/uuid block-uuid
|
||||
:block/content (:block/content block-entity-map)
|
||||
:block/created-at (:block/created-at block-entity-map)
|
||||
:block/updated-at (:block/updated-at block-entity-map)
|
||||
:block/format :markdown}
|
||||
(seq (:block/tags block-entity-map))
|
||||
(assoc :block/tags (mapv (partial vector :block/uuid)
|
||||
(:block/tags block-entity-map))))]
|
||||
left-entity {:sibling? sibling? :keep-uuid? true}))
|
||||
:push-undo-redo
|
||||
))))
|
||||
|
||||
(defmethod reverse-apply-op :insert-block
|
||||
[op conn repo]
|
||||
(let [[_ {:keys [block-uuid]}] op]
|
||||
(when-let [block-entity (d/entity @conn [:block/uuid block-uuid])]
|
||||
(when (empty? (seq (:block/_parent block-entity))) ;if have children, skip
|
||||
(outliner-tx/transact!
|
||||
{:gen-undo-op? false
|
||||
:outliner-op :delete-blocks
|
||||
:transact-opts {:repo repo
|
||||
:conn conn}}
|
||||
(outliner-core/delete-blocks! repo conn
|
||||
(common-config/get-date-formatter (worker-state/get-config repo))
|
||||
[block-entity]
|
||||
{:children? false}))
|
||||
:push-undo-redo))))
|
||||
|
||||
(defmethod reverse-apply-op :move-block
|
||||
[op conn repo]
|
||||
(let [[_ {:keys [block-uuid block-origin-left block-origin-parent]}] op]
|
||||
(when-let [block-entity (d/entity @conn [:block/uuid block-uuid])]
|
||||
(when-let [left-entity (d/entity @conn [:block/uuid block-origin-left])]
|
||||
(let [sibling? (not= block-origin-left block-origin-parent)]
|
||||
(outliner-tx/transact!
|
||||
{:gen-undo-op? false
|
||||
:outliner-op :move-blocks
|
||||
:transact-opts {:repo repo
|
||||
:conn conn}}
|
||||
(outliner-core/move-blocks! repo conn [block-entity] left-entity sibling?))
|
||||
:push-undo-redo)))))
|
||||
|
||||
(defmethod reverse-apply-op :update-block
|
||||
[op conn repo]
|
||||
(let [[_ {:keys [block-uuid block-origin-content]}] op]
|
||||
(when-let [block-entity (d/entity @conn [:block/uuid block-uuid])]
|
||||
(let [new-block (assoc block-entity :block/content block-origin-content)]
|
||||
(outliner-tx/transact!
|
||||
{:gen-undo-op? false
|
||||
:outliner-op :save-block
|
||||
:transact-opts {:repo repo
|
||||
:conn conn}}
|
||||
(outliner-core/save-block! repo conn
|
||||
(common-config/get-date-formatter (worker-state/get-config repo))
|
||||
new-block))
|
||||
:push-undo-redo))))
|
||||
|
||||
|
||||
(defn undo
|
||||
[repo]
|
||||
(when-let [op (pop-undo-op repo)]
|
||||
(let [conn (worker-state/get-datascript-conn repo)
|
||||
rev-op (reverse-op @conn op)]
|
||||
(when (= :push-undo-redo (reverse-apply-op op conn repo))
|
||||
(push-redo-ops repo [rev-op])))))
|
||||
|
||||
(defn redo
|
||||
[repo]
|
||||
(when-let [op (pop-redo-op repo)]
|
||||
(let [conn (worker-state/get-datascript-conn repo)
|
||||
rev-op (reverse-op @conn op)]
|
||||
(when (= :push-undo-redo (reverse-apply-op op conn repo))
|
||||
(push-undo-ops repo [rev-op])))))
|
||||
|
||||
|
||||
;;; listen db changes and push undo-ops
|
||||
|
||||
(def ^:private entity-map-pull-pattern
|
||||
[:block/uuid
|
||||
{:block/left [:block/uuid]}
|
||||
{:block/parent [:block/uuid]}
|
||||
:block/content
|
||||
:block/created-at
|
||||
:block/updated-at
|
||||
:block/format
|
||||
{:block/tags [:block/uuid]}])
|
||||
|
||||
(defn- ->block-entity-map
|
||||
[db eid]
|
||||
(let [m (-> (d/pull db entity-map-pull-pattern eid)
|
||||
(update :block/left :block/uuid)
|
||||
(update :block/parent :block/uuid))]
|
||||
(if (seq (:block/tags m))
|
||||
(update m :block/tags (partial mapv :block/uuid))
|
||||
m)))
|
||||
|
||||
(defn- normal-block?
|
||||
[entity]
|
||||
(and (:block/parent entity)
|
||||
(:block/left entity)))
|
||||
|
||||
|
||||
(defn- entity-datoms=>ops
|
||||
[db-before db-after id->attr->datom entity-datoms]
|
||||
(when-let [e (ffirst entity-datoms)]
|
||||
(let [attr->datom (id->attr->datom e)]
|
||||
(when (seq attr->datom)
|
||||
(let [{[_ _ block-uuid _ add1?] :block/uuid
|
||||
[_ _ block-content _ add2?] :block/content
|
||||
[_ _ _ _ add3?] :block/left
|
||||
[_ _ _ _ add4?] :block/parent} attr->datom
|
||||
entity-before (d/entity db-before e)
|
||||
entity-after (d/entity db-after e)]
|
||||
(cond
|
||||
(and (not add1?) block-uuid
|
||||
(normal-block? entity-before))
|
||||
[[:remove-block
|
||||
{:block-uuid (:block/uuid entity-before)
|
||||
:block-entity-map (->block-entity-map db-before e)}]]
|
||||
|
||||
(and add1? block-uuid
|
||||
(normal-block? entity-after))
|
||||
[[:insert-block {:block-uuid (:block/uuid entity-after)}]]
|
||||
|
||||
(and (or add3? add4?)
|
||||
(normal-block? entity-after))
|
||||
(cond-> [[:move-block
|
||||
{:block-uuid (:block/uuid entity-after)
|
||||
:block-origin-left (:block/uuid (:block/left entity-before))
|
||||
:block-origin-parent (:block/uuid (:block/parent entity-before))}]]
|
||||
(and add2? block-content)
|
||||
(conj [:update-block
|
||||
{:block-uuid (:block/uuid entity-after)
|
||||
:block-origin-content (:block/content entity-before)}]))
|
||||
|
||||
(and add2? block-content
|
||||
(normal-block? entity-after))
|
||||
[[:update-block
|
||||
{:block-uuid (:block/uuid entity-after)
|
||||
:block-origin-content (:block/content entity-before)}]]))))))
|
||||
|
||||
(defn- generate-undo-ops
|
||||
[repo db-before db-after same-entity-datoms-coll id->attr->datom]
|
||||
(let [ops (mapcat (partial entity-datoms=>ops db-before db-after id->attr->datom) same-entity-datoms-coll)]
|
||||
(assert (undo-ops-validator ops) ops)
|
||||
(when (seq ops)
|
||||
(push-undo-ops repo ops))))
|
||||
|
||||
|
||||
(defmethod db-listener/listen-db-changes :gen-undo-ops
|
||||
[_ {:keys [_tx-data tx-meta db-before db-after
|
||||
repo id->attr->datom same-entity-datoms-coll]}]
|
||||
(when (:gen-undo-op? tx-meta true)
|
||||
(generate-undo-ops repo db-before db-after same-entity-datoms-coll id->attr->datom)))
|
||||
|
||||
;;; listen db changes and push undo-ops (ends)
|
||||
|
|
|
@ -585,11 +585,11 @@
|
|||
:plugin.install-from-file/title "Installa plugin da plugins.edn"
|
||||
:query/config-property-settings "Impostazioni per le proprietà di questa richiesta:"
|
||||
:right-side-bar/flashcards "Flashcard"
|
||||
:right-side-bar/history "(Dev) cronologia disfai/rifai"
|
||||
:right-side-bar/history "(Dev) cronologia disfare/rifare"
|
||||
:right-side-bar/history-global "globale"
|
||||
:right-side-bar/history-pageonly "solo pagina corrente"
|
||||
:right-side-bar/history-redos "Rifai"
|
||||
:right-side-bar/history-undos "Disfai"
|
||||
:right-side-bar/history-undos "Rifare"
|
||||
:right-side-bar/pane-close "Chiudi"
|
||||
:right-side-bar/pane-close-all "Chiudi tutti"
|
||||
:right-side-bar/pane-close-others "Chiudi altri"
|
||||
|
@ -649,9 +649,9 @@
|
|||
:settings-page/tab-editor "Editor"
|
||||
:settings-page/tab-features "Funzionalità"
|
||||
:settings-page/tab-keymap "Scorciatoie"
|
||||
:settings-page/theme-dark "scuro"
|
||||
:settings-page/theme-light "chiaro"
|
||||
:settings-page/theme-system "sistema"
|
||||
:settings-page/theme-dark "Scuro"
|
||||
:settings-page/theme-light "Chiaro"
|
||||
:settings-page/theme-system "Sistema"
|
||||
:settings-page/update-available "Trovato una nuova versione "
|
||||
:settings-page/update-error-1 "⚠️ Ops, qualcosa è andato storto!"
|
||||
:settings-page/update-error-2 " Per favore, controlla il "
|
||||
|
@ -703,8 +703,8 @@
|
|||
:whiteboard/link-to-any-page-or-block "Collega a qualsiasi pagina o blocco"
|
||||
:whiteboard/lock "Blocca"
|
||||
:whiteboard/medium "Medio"
|
||||
:whiteboard/move-to-back "Sposta in retro"
|
||||
:whiteboard/move-to-front "Sposta in fronte"
|
||||
:whiteboard/move-to-back "Sposta sul retro"
|
||||
:whiteboard/move-to-front "Sposta sul fronte"
|
||||
:whiteboard/new-block "Nuovo blocco:"
|
||||
:whiteboard/new-block-no-colon "Nuovo blocco"
|
||||
:whiteboard/new-page "Nuova pagina:"
|
||||
|
@ -713,8 +713,8 @@
|
|||
:whiteboard/open-page "Apri pagina"
|
||||
:whiteboard/open-page-in-sidebar "Apri pagina nel pannello laterale"
|
||||
:whiteboard/open-twitter-url "Apri link Twitter"
|
||||
:whiteboard/open-website-url "Open sito internet"
|
||||
:whiteboard/open-youtube-url "Open link YouTube"
|
||||
:whiteboard/open-website-url "Apri sito internet"
|
||||
:whiteboard/open-youtube-url "Apri link YouTube"
|
||||
:whiteboard/pack-into-rectangle "Forma rettangolo compresso"
|
||||
:whiteboard/pan "Muovi"
|
||||
:whiteboard/paste "Incolla"
|
||||
|
@ -741,7 +741,7 @@
|
|||
:whiteboard/toggle-pen-mode "Attiva/disattiva modalità penna"
|
||||
:whiteboard/triangle "Triangolo"
|
||||
:whiteboard/twitter-url "Indirizzo Twitter"
|
||||
:whiteboard/undo "Disfai"
|
||||
:whiteboard/undo "Rifare"
|
||||
:whiteboard/ungroup "Annulla raggruppamento"
|
||||
:whiteboard/unlock "Blocca"
|
||||
:whiteboard/website-url "Link sito internet"
|
||||
|
|
|
@ -15,14 +15,17 @@
|
|||
(is (= 0 (util/safe-dec-current-pos-from-end "😀" 2)))
|
||||
(is (= 0 (util/safe-dec-current-pos-from-end "a" 1)))
|
||||
(is (= 4 (util/safe-dec-current-pos-from-end "abcde" 5)))
|
||||
(is (= 1 (util/safe-dec-current-pos-from-end "中文" 2))))
|
||||
(is (= 1 (util/safe-dec-current-pos-from-end "中文" 2)))
|
||||
(is (= 0 (util/safe-dec-current-pos-from-end "中" 1)))
|
||||
(is (= 0 (util/safe-dec-current-pos-from-end "a" 1))))
|
||||
|
||||
(testing "safe current position from start for emoji"
|
||||
(is (= 5 (util/safe-inc-current-pos-from-start "abc😀d" 3)))
|
||||
(is (= 2 (util/safe-inc-current-pos-from-start "😀" 0)))
|
||||
(is (= 2 (util/safe-inc-current-pos-from-start "abcde" 1)))
|
||||
(is (= 1 (util/safe-inc-current-pos-from-start "a" 0)))
|
||||
(is (= 1 (util/safe-inc-current-pos-from-start "中文" 0)))))
|
||||
(is (= 1 (util/safe-inc-current-pos-from-start "中文" 0)))
|
||||
(is (= 2 (util/safe-inc-current-pos-from-start "😀" 0)))
|
||||
(is (= 1 (util/safe-inc-current-pos-from-start "中" 0)))
|
||||
(is (= 1 (util/safe-inc-current-pos-from-start "a" 0)))))
|
||||
|
||||
(deftest test-get-line-pos
|
||||
(testing "get-line-pos"
|
||||
|
|
|
@ -8,4 +8,6 @@
|
|||
;; TODO: add tests for undo-redo
|
||||
undo-redo/undo-op-schema
|
||||
undo-redo/reverse-op
|
||||
undo-redo/undo
|
||||
undo-redo/redo
|
||||
)
|
||||
|
|
|
@ -1856,10 +1856,10 @@ ds-store@^0.1.5:
|
|||
macos-alias "~0.2.5"
|
||||
tn1150 "^0.1.0"
|
||||
|
||||
dugite@2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/dugite/-/dugite-2.5.0.tgz#8b235564fdf8692688283c714149a59d9da79865"
|
||||
integrity sha512-sYsSOqV7NidthDtMUPgKCvqMGqswKkcyAxOMhwEswlcGZ+kHadT2SEDFUJOy0AVR/yTJL6wBF7q1OiySfU0gGA==
|
||||
dugite@2.5.1:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/dugite/-/dugite-2.5.1.tgz#6ab808ebf321809edf42d974e62eea9c9e256722"
|
||||
integrity sha512-9OjUguynzq8v3GSmp01kbVcMmErc65ZZ0OssO/0PM2RyhD8Dzb8cCuy3z72+IxLwPwNi754jZ0FtMLAFA3D0qA==
|
||||
dependencies:
|
||||
progress "^2.0.3"
|
||||
tar "^6.1.11"
|
||||
|
|
Loading…
Reference in New Issue