diff --git a/.github/workflows/build-desktop-release.yml b/.github/workflows/build-desktop-release.yml index a9e1222a4..a4bdf5f37 100644 --- a/.github/workflows/build-desktop-release.yml +++ b/.github/workflows/build-desktop-release.yml @@ -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 diff --git a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs index cae03b2f6..c324153ce 100644 --- a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs +++ b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs @@ -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) diff --git a/deps/outliner/src/logseq/outliner/core.cljs b/deps/outliner/src/logseq/outliner/core.cljs index 168e842da..8824a4e81 100644 --- a/deps/outliner/src/logseq/outliner/core.cljs +++ b/deps/outliner/src/logseq/outliner/core.cljs @@ -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))])))) diff --git a/resources/package.json b/resources/package.json index c23d7881e..06d7b717d 100644 --- a/resources/package.json +++ b/resources/package.json @@ -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", diff --git a/src/main/frontend/db_worker.cljs b/src/main/frontend/db_worker.cljs index c2c28c473..ce27c181a 100644 --- a/src/main/frontend/db_worker.cljs +++ b/src/main/frontend/db_worker.cljs @@ -10,6 +10,7 @@ [datascript.core :as d] [datascript.storage :refer [IStorage]] [frontend.worker.async-util :include-macros true :refer [vec [iter] (when iter diff --git a/src/main/frontend/util.cljc b/src/main/frontend/util.cljc index d579119a6..51d91ee97 100644 --- a/src/main/frontend/util.cljc +++ b/src/main/frontend/util.cljc @@ -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! diff --git a/src/main/frontend/worker/db_listener.cljs b/src/main/frontend/worker/db_listener.cljs new file mode 100644 index 000000000..3ca0250db --- /dev/null +++ b/src/main/frontend/worker/db_listener.cljs @@ -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)))))))) diff --git a/src/main/frontend/worker/rtc/db_listener.cljs b/src/main/frontend/worker/rtc/db_listener.cljs index da6c22f0a..ff3a655b3 100644 --- a/src/main/frontend/worker/rtc/db_listener.cljs +++ b/src/main/frontend/worker/rtc/db_listener.cljs @@ -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)) diff --git a/src/main/frontend/worker/state.cljs b/src/main/frontend/worker/state.cljs index a21e9990f..c2322574e 100644 --- a/src/main/frontend/worker/state.cljs +++ b/src/main/frontend/worker/state.cljs @@ -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)) diff --git a/src/main/frontend/worker/undo_redo.cljs b/src/main/frontend/worker/undo_redo.cljs index e08bb8ed4..e83f774b5 100644 --- a/src/main/frontend/worker/undo_redo.cljs +++ b/src/main/frontend/worker/undo_redo.cljs @@ -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) diff --git a/src/resources/dicts/it.edn b/src/resources/dicts/it.edn index 63b5e7d42..1f13b4569 100644 --- a/src/resources/dicts/it.edn +++ b/src/resources/dicts/it.edn @@ -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" diff --git a/src/test/frontend/util_test.cljs b/src/test/frontend/util_test.cljs index ff83020cb..92ba31520 100644 --- a/src/test/frontend/util_test.cljs +++ b/src/test/frontend/util_test.cljs @@ -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" diff --git a/src/test/frontend/worker/undo_redo_test.cljs b/src/test/frontend/worker/undo_redo_test.cljs index 3da3f0798..988fcf3d7 100644 --- a/src/test/frontend/worker/undo_redo_test.cljs +++ b/src/test/frontend/worker/undo_redo_test.cljs @@ -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 ) diff --git a/static/yarn.lock b/static/yarn.lock index 85611935d..a51b1c313 100644 --- a/static/yarn.lock +++ b/static/yarn.lock @@ -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"