mirror of
https://github.com/penpot/penpot.git
synced 2025-03-15 17:21:17 -05:00
Merge remote-tracking branch 'origin/staging' into develop
This commit is contained in:
commit
9afe499075
26 changed files with 440 additions and 171 deletions
14
CHANGES.md
14
CHANGES.md
|
@ -15,16 +15,28 @@
|
|||
|
||||
### :sparkles: New features
|
||||
|
||||
- Add many performance related improvements to indexes handling on workspace.
|
||||
- Add option to interactively scale text [Taiga #1527](https://tree.taiga.io/project/penpot/us/1527)
|
||||
- Add the ability to upload/use custom fonts (and automatically generate all needed webfonts).
|
||||
- Refactor dashboard state management (improves considerably the performance when you have a dashboard with a big collection of projects and files).
|
||||
- Translate automatic names of new files and projects.
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
- Remove interactions when the destination artboard is deleted [Taiga #1656](https://tree.taiga.io/project/penpot/issue/1656)
|
||||
- Fix snap index problem [Taiga #1661](https://tree.taiga.io/project/penpot/issue/1661)
|
||||
|
||||
### :arrow_up: Deps updates
|
||||
|
||||
- Update exporter dependencies (puppetteer), that fixes some unexpected exceptions.
|
||||
- Update string manipulation library.
|
||||
|
||||
|
||||
### :boom: Breaking changes
|
||||
|
||||
- The OIDC setting `PENPOT_OIDC_SCOPES` has changed the default semantics. Before this
|
||||
configuration added scopes to the default set. Now it replaces it, so use with care, because
|
||||
penpot requires at least `name` and `email` props found on the user info object.
|
||||
|
||||
### :heart: Community contributions by (Thank you!)
|
||||
|
||||
|
||||
|
|
|
@ -232,8 +232,7 @@
|
|||
:token-uri (cf/get :oidc-token-uri)
|
||||
:auth-uri (cf/get :oidc-auth-uri)
|
||||
:user-uri (cf/get :oidc-user-uri)
|
||||
:scopes (into #{"openid" "profile" "email" "name"}
|
||||
(cf/get :oidc-scopes #{}))
|
||||
:scopes (cf/get :oidc-scopes #{"openid" "profile"})
|
||||
:roles-attr (cf/get :oidc-roles-attr)
|
||||
:roles (cf/get :oidc-roles)
|
||||
:name "oidc"}]
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
[app.common.spec :as us]
|
||||
[app.rlimits :as rlm]
|
||||
[app.rpc.queries.svg :as svg]
|
||||
[buddy.core.bytes :as bb]
|
||||
[buddy.core.codecs :as bc]
|
||||
[clojure.java.io :as io]
|
||||
[clojure.java.shell :as sh]
|
||||
[clojure.spec.alpha :as s]
|
||||
|
@ -64,7 +66,8 @@
|
|||
|
||||
(defmethod process-error :default
|
||||
[error]
|
||||
(ex/raise :type :internal :cause error))
|
||||
(ex/raise :type :internal
|
||||
:cause error))
|
||||
|
||||
(defn run
|
||||
[{:keys [rlimits] :as cfg} {:keys [rlimit] :or {rlimit :image} :as params}]
|
||||
|
@ -232,6 +235,19 @@
|
|||
(fs/slurp-bytes output-file))))
|
||||
|
||||
|
||||
(otf->ttf [data]
|
||||
(let [input-file (fs/create-tempfile :prefix "penpot")
|
||||
output-file (fs/path (str input-file ".ttf"))
|
||||
_ (with-open [out (io/output-stream input-file)]
|
||||
(IOUtils/writeChunked ^bytes data ^OutputStream out)
|
||||
(.flush ^OutputStream out))
|
||||
res (sh/sh "fontforge" "-lang=ff" "-c"
|
||||
(str/fmt "Open('%s'); Generate('%s')"
|
||||
(str input-file)
|
||||
(str output-file)))]
|
||||
(when (zero? (:exit res))
|
||||
(fs/slurp-bytes output-file))))
|
||||
|
||||
(ttf-or-otf->woff [data]
|
||||
(let [input-file (fs/create-tempfile :prefix "penpot" :suffix "")
|
||||
output-file (fs/path (str input-file ".woff"))
|
||||
|
@ -250,17 +266,68 @@
|
|||
(.flush ^OutputStream out))
|
||||
res (sh/sh "woff2_compress" (str input-file))]
|
||||
(when (zero? (:exit res))
|
||||
(fs/slurp-bytes output-file))))]
|
||||
(fs/slurp-bytes output-file))))
|
||||
|
||||
(woff->sfnt [data]
|
||||
(let [input-file (fs/create-tempfile :prefix "penpot" :suffix "")
|
||||
_ (with-open [out (io/output-stream input-file)]
|
||||
(IOUtils/writeChunked ^bytes data ^OutputStream out)
|
||||
(.flush ^OutputStream out))
|
||||
res (sh/sh "woff2sfnt" (str input-file)
|
||||
:out-enc :bytes)]
|
||||
(when (zero? (:exit res))
|
||||
(:out res))))
|
||||
|
||||
;; Documented here:
|
||||
;; https://docs.microsoft.com/en-us/typography/opentype/spec/otff#table-directory
|
||||
(get-sfnt-type [data]
|
||||
(let [buff (bb/slice data 0 4)
|
||||
type (bc/bytes->hex buff)]
|
||||
(case type
|
||||
"4f54544f" :otf
|
||||
"00010000" :ttf
|
||||
(ex/raise :type :internal
|
||||
:code :unexpected-data
|
||||
:hint "unexpected font data"))))
|
||||
|
||||
(gen-if-nil [val factory]
|
||||
(if (nil? val)
|
||||
(factory)
|
||||
val))]
|
||||
|
||||
(let [current (into #{} (keys input))]
|
||||
(if (contains? current "font/ttf")
|
||||
(-> input
|
||||
(assoc "font/otf" (ttf->otf (get input "font/ttf")))
|
||||
(assoc "font/woff" (ttf-or-otf->woff (get input "font/ttf")))
|
||||
(assoc "font/woff2" (ttf-or-otf->woff2 (get input "font/ttf"))))
|
||||
(cond
|
||||
(contains? current "font/ttf")
|
||||
(let [data (get input "font/ttf")]
|
||||
(-> input
|
||||
(update "font/otf" gen-if-nil #(ttf->otf data))
|
||||
(update "font/woff" gen-if-nil #(ttf-or-otf->woff data))
|
||||
(assoc "font/woff2" (ttf-or-otf->woff2 data))))
|
||||
|
||||
(-> input
|
||||
;; TODO: pending to implement
|
||||
;; (assoc "font/ttf" (otf->ttf (get input "font/ttf")))
|
||||
(assoc "font/woff" (ttf-or-otf->woff (get input "font/otf")))
|
||||
(assoc "font/woff2" (ttf-or-otf->woff2 (get input "font/otf"))))))))
|
||||
(contains? current "font/otf")
|
||||
(let [data (get input "font/otf")]
|
||||
(-> input
|
||||
(update "font/woff" gen-if-nil #(ttf-or-otf->woff data))
|
||||
(assoc "font/ttf" (otf->ttf data))
|
||||
(assoc "font/woff2" (ttf-or-otf->woff2 data))))
|
||||
|
||||
(contains? current "font/woff")
|
||||
(let [data (get input "font/woff")
|
||||
sfnt (woff->sfnt data)]
|
||||
(when-not sfnt
|
||||
(ex/raise :type :validation
|
||||
:code :invalid-woff-file
|
||||
:hint "invalid woff file"))
|
||||
(let [stype (get-sfnt-type sfnt)]
|
||||
(cond-> input
|
||||
true
|
||||
(-> (assoc "font/woff" data)
|
||||
(assoc "font/woff2" (ttf-or-otf->woff2 sfnt)))
|
||||
|
||||
(= stype :otf)
|
||||
(-> (assoc "font/otf" sfnt)
|
||||
(assoc "font/ttf" (otf->ttf sfnt)))
|
||||
|
||||
(= stype :ttf)
|
||||
(-> (assoc "font/otf" (ttf->otf sfnt))
|
||||
(assoc "font/ttf" sfnt)))))))))
|
||||
|
|
BIN
backend/tests/app/tests/_files/font-1.otf
Normal file
BIN
backend/tests/app/tests/_files/font-1.otf
Normal file
Binary file not shown.
BIN
backend/tests/app/tests/_files/font-1.ttf
Normal file
BIN
backend/tests/app/tests/_files/font-1.ttf
Normal file
Binary file not shown.
BIN
backend/tests/app/tests/_files/font-1.woff
Normal file
BIN
backend/tests/app/tests/_files/font-1.woff
Normal file
Binary file not shown.
BIN
backend/tests/app/tests/_files/font-2.otf
Normal file
BIN
backend/tests/app/tests/_files/font-2.otf
Normal file
Binary file not shown.
BIN
backend/tests/app/tests/_files/font-2.woff
Normal file
BIN
backend/tests/app/tests/_files/font-2.woff
Normal file
Binary file not shown.
92
backend/tests/app/tests/test_services_fonts.clj
Normal file
92
backend/tests/app/tests/test_services_fonts.clj
Normal file
|
@ -0,0 +1,92 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) UXBOX Labs SL
|
||||
|
||||
(ns app.tests.test-services-fonts
|
||||
(:require
|
||||
[app.common.uuid :as uuid]
|
||||
[app.db :as db]
|
||||
[app.http :as http]
|
||||
[app.storage :as sto]
|
||||
[app.tests.helpers :as th]
|
||||
[clojure.java.io :as io]
|
||||
[clojure.test :as t]
|
||||
[datoteka.core :as fs]))
|
||||
|
||||
(t/use-fixtures :once th/state-init)
|
||||
(t/use-fixtures :each th/database-reset)
|
||||
|
||||
(t/deftest ttf-font-upload-1
|
||||
(let [prof (th/create-profile* 1 {:is-active true})
|
||||
team-id (:default-team-id prof)
|
||||
proj-id (:default-project-id prof)
|
||||
|
||||
ttfdata (-> (io/resource "app/tests/_files/font-1.ttf")
|
||||
(fs/slurp-bytes))
|
||||
|
||||
params {::th/type :create-font-variant
|
||||
:profile-id (:id prof)
|
||||
:team-id team-id
|
||||
:font-id "custom-somefont"
|
||||
:font-family "somefont"
|
||||
:font-weight 400
|
||||
:font-style "normal"
|
||||
:data {"font/ttf" ttfdata}}
|
||||
out (th/mutation! params)]
|
||||
|
||||
;; (th/print-result! out)
|
||||
(t/is (nil? (:error out)))
|
||||
(let [result (:result out)]
|
||||
(t/is (uuid? (:id result)))
|
||||
(t/is (uuid? (:ttf-file-id result)))
|
||||
(t/is (uuid? (:otf-file-id result)))
|
||||
(t/is (uuid? (:woff1-file-id result)))
|
||||
(t/is (uuid? (:woff2-file-id result)))
|
||||
(t/are [k] (= (get params k)
|
||||
(get result k))
|
||||
:team-id
|
||||
:font-id
|
||||
:font-family
|
||||
:font-weight
|
||||
:font-style))))
|
||||
|
||||
(t/deftest ttf-font-upload-2
|
||||
(let [prof (th/create-profile* 1 {:is-active true})
|
||||
team-id (:default-team-id prof)
|
||||
proj-id (:default-project-id prof)
|
||||
|
||||
data (-> (io/resource "app/tests/_files/font-1.woff")
|
||||
(fs/slurp-bytes))
|
||||
|
||||
params {::th/type :create-font-variant
|
||||
:profile-id (:id prof)
|
||||
:team-id team-id
|
||||
:font-id "custom-somefont"
|
||||
:font-family "somefont"
|
||||
:font-weight 400
|
||||
:font-style "normal"
|
||||
:data {"font/woff" data}}
|
||||
out (th/mutation! params)]
|
||||
|
||||
;; (th/print-result! out)
|
||||
(t/is (nil? (:error out)))
|
||||
(let [result (:result out)]
|
||||
(t/is (uuid? (:id result)))
|
||||
(t/is (uuid? (:ttf-file-id result)))
|
||||
(t/is (uuid? (:otf-file-id result)))
|
||||
(t/is (uuid? (:woff1-file-id result)))
|
||||
(t/is (uuid? (:woff2-file-id result)))
|
||||
(t/are [k] (= (get params k)
|
||||
(get result k))
|
||||
:team-id
|
||||
:font-id
|
||||
:font-family
|
||||
:font-weight
|
||||
:font-style))))
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -29,14 +29,14 @@
|
|||
"map-stream": "0.0.7",
|
||||
"marked": "^2.0.3",
|
||||
"mkdirp": "^1.0.4",
|
||||
"postcss": "^8.2.7",
|
||||
"postcss": "^8.2.15",
|
||||
"postcss-clean": "^1.2.2",
|
||||
"rimraf": "^3.0.0",
|
||||
"sass": "^1.32.8",
|
||||
"shadow-cljs": "2.12.5"
|
||||
"shadow-cljs": "2.12.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"date-fns": "^2.21.1",
|
||||
"date-fns": "^2.21.3",
|
||||
"draft-js": "^0.11.7",
|
||||
"highlight.js": "^10.6.0",
|
||||
"js-beautify": "^1.13.5",
|
||||
|
@ -46,7 +46,7 @@
|
|||
"randomcolor": "^0.6.2",
|
||||
"react": "~17.0.1",
|
||||
"react-dom": "~17.0.1",
|
||||
"rxjs": "~7.0.0-beta.12",
|
||||
"rxjs": "~7.0.1",
|
||||
"source-map-support": "^0.5.16",
|
||||
"tdigest": "^0.1.1",
|
||||
"ua-parser-js": "^0.7.28",
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
:jvm-opts ["-Xmx600m" "-Xms50m" "-XX:+UseSerialGC"]
|
||||
:dev-http {8888 "classpath:public"}
|
||||
|
||||
:source-paths ["src", "vendor", "resources", "../common", "tests"]
|
||||
:source-paths ["src", "vendor", "resources", "../common", "tests", "dev"]
|
||||
:dependencies
|
||||
[[binaryage/devtools "RELEASE"]
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
|||
[funcool/okulary "2020.04.14-0"]
|
||||
[funcool/potok "4.0.0"]
|
||||
[funcool/promesa "6.0.0"]
|
||||
[funcool/rumext "2021.01.26-0"]
|
||||
[funcool/rumext "2021.05.12-1"]
|
||||
|
||||
[lambdaisland/uri "1.4.54"
|
||||
:exclusions [org.clojure/data.json]]
|
||||
|
|
|
@ -5,14 +5,22 @@
|
|||
;; Copyright (c) UXBOX Labs SL
|
||||
|
||||
(ns app.main.data.shortcuts
|
||||
(:refer-clojure :exclude [meta reset!])
|
||||
(:require
|
||||
["mousetrap" :as mousetrap]
|
||||
[app.common.data :as d]
|
||||
[app.common.spec :as us]
|
||||
[app.config :as cfg]
|
||||
[app.util.logging :as log])
|
||||
(:refer-clojure :exclude [meta]))
|
||||
[app.util.logging :as log]
|
||||
[cljs.spec.alpha :as s]
|
||||
[potok.core :as ptk]))
|
||||
|
||||
(log/set-level! :warn)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Helpers
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(def mac-command "\u2318")
|
||||
(def mac-option "\u2325")
|
||||
(def mac-delete "\u232B")
|
||||
|
@ -44,30 +52,8 @@
|
|||
[shortcut]
|
||||
(c-mod (a-mod shortcut)))
|
||||
|
||||
(defn bind-shortcuts
|
||||
([shortcuts-config]
|
||||
(bind-shortcuts
|
||||
shortcuts-config
|
||||
mousetrap/bind
|
||||
(fn [key cb]
|
||||
(fn [event]
|
||||
(log/debug :msg (str "Shortcut" key))
|
||||
(.preventDefault event)
|
||||
(cb event)))))
|
||||
|
||||
([shortcuts-config bind-fn cb-fn]
|
||||
(doseq [[key {:keys [command disabled fn type]}] shortcuts-config]
|
||||
(when-not disabled
|
||||
(if (vector? command)
|
||||
(doseq [cmd (seq command)]
|
||||
(bind-fn cmd (cb-fn key fn) type))
|
||||
(bind-fn command (cb-fn key fn) type))))))
|
||||
|
||||
(defn remove-shortcuts
|
||||
[]
|
||||
(mousetrap/reset))
|
||||
|
||||
(defn meta [key]
|
||||
(defn meta
|
||||
[key]
|
||||
;; If the key is "+" we need to surround with quotes
|
||||
;; otherwise will not be very readable
|
||||
(let [key (if (and (not (cfg/check-platform? :macos))
|
||||
|
@ -80,37 +66,120 @@
|
|||
"Ctrl+")
|
||||
key)))
|
||||
|
||||
(defn shift [key]
|
||||
(defn shift
|
||||
[key]
|
||||
(str
|
||||
(if (cfg/check-platform? :macos)
|
||||
mac-shift
|
||||
"Shift+")
|
||||
key))
|
||||
|
||||
(defn alt [key]
|
||||
(defn alt
|
||||
[key]
|
||||
(str
|
||||
(if (cfg/check-platform? :macos)
|
||||
mac-option
|
||||
"Alt+")
|
||||
key))
|
||||
|
||||
(defn meta-shift [key]
|
||||
(defn meta-shift
|
||||
[key]
|
||||
(-> key meta shift))
|
||||
|
||||
(defn meta-alt [key]
|
||||
(defn meta-alt
|
||||
[key]
|
||||
(-> key meta alt))
|
||||
|
||||
(defn supr []
|
||||
(defn supr
|
||||
[]
|
||||
(if (cfg/check-platform? :macos)
|
||||
mac-delete
|
||||
"Supr"))
|
||||
|
||||
(defn esc []
|
||||
(defn esc
|
||||
[]
|
||||
(if (cfg/check-platform? :macos)
|
||||
mac-esc
|
||||
"Escape"))
|
||||
|
||||
(defn enter []
|
||||
(defn enter
|
||||
[]
|
||||
(if (cfg/check-platform? :macos)
|
||||
mac-enter
|
||||
"Enter"))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Events
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; --- EVENT: push
|
||||
|
||||
(s/def ::tooltip ::us/string)
|
||||
(s/def ::fn fn?)
|
||||
|
||||
(s/def ::command
|
||||
(s/or :str ::us/string
|
||||
:vec vector?))
|
||||
|
||||
(s/def ::shortcut
|
||||
(s/keys :req-un [::command]
|
||||
:opt-un [::fn
|
||||
::tooltip]))
|
||||
|
||||
(s/def ::shortcuts
|
||||
(s/map-of ::us/keyword
|
||||
::shortcut))
|
||||
|
||||
(defn- wrap-cb
|
||||
[key cb]
|
||||
(fn [event]
|
||||
(log/debug :msg (str "Shortcut" key))
|
||||
(.preventDefault event)
|
||||
(cb event)))
|
||||
|
||||
(defn- bind!
|
||||
[shortcuts]
|
||||
(->> shortcuts
|
||||
(remove #(:disabled (second %)))
|
||||
(run! (fn [[key {:keys [command fn type]}]]
|
||||
(if (vector? command)
|
||||
(run! #(mousetrap/bind % (wrap-cb key fn) type) command)
|
||||
(mousetrap/bind command (wrap-cb key fn) type))))))
|
||||
|
||||
(defn- reset!
|
||||
([]
|
||||
(mousetrap/reset))
|
||||
([shortcuts]
|
||||
(mousetrap/reset)
|
||||
(bind! shortcuts)))
|
||||
|
||||
(defn push-shortcuts
|
||||
[key shortcuts]
|
||||
(us/assert ::us/keyword key)
|
||||
(us/assert ::shortcuts shortcuts)
|
||||
(ptk/reify ::push-shortcuts
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(update :shortcuts (fnil conj '()) [key shortcuts])))
|
||||
|
||||
ptk/EffectEvent
|
||||
(effect [_ state stream]
|
||||
(let [[key shortcuts] (peek (:shortcuts state))]
|
||||
(reset! shortcuts)))))
|
||||
|
||||
(defn pop-shortcuts
|
||||
[key]
|
||||
(ptk/reify ::pop-shortcuts
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :shortcuts (fn [shortcuts]
|
||||
(let [current-key (first (peek shortcuts))]
|
||||
(if (= key current-key)
|
||||
(pop shortcuts)
|
||||
shortcuts)))))
|
||||
ptk/EffectEvent
|
||||
(effect [_ state stream]
|
||||
(let [[key* shortcuts] (peek (:shortcuts state))]
|
||||
(when (not= key key*)
|
||||
(reset! shortcuts))))))
|
||||
|
|
|
@ -161,40 +161,40 @@
|
|||
(->> stream
|
||||
(rx/filter (ptk/type? ::dwp/bundle-fetched))
|
||||
(rx/take 1)
|
||||
|
||||
(rx/map deref)
|
||||
(rx/mapcat (fn [bundle]
|
||||
(rx/of (dwn/initialize file-id)
|
||||
(dwp/initialize-file-persistence file-id)
|
||||
(dwc/initialize-indices bundle)))))
|
||||
(rx/mapcat (fn [{:keys [project] :as bundle}]
|
||||
(rx/merge
|
||||
(rx/of (dwn/initialize file-id)
|
||||
(dwp/initialize-file-persistence file-id)
|
||||
(dwc/initialize-indices bundle))
|
||||
|
||||
;; Mark file initialized when indexes are ready
|
||||
(->> stream
|
||||
(rx/filter #(= ::dwc/index-initialized %))
|
||||
(rx/first)
|
||||
(rx/map (fn []
|
||||
(file-initialized project-id file-id))))
|
||||
|
||||
))))
|
||||
(->> stream
|
||||
(rx/filter #(= ::dwc/index-initialized %))
|
||||
(rx/first)
|
||||
(rx/map #(file-initialized bundle)))))))))))
|
||||
|
||||
(defn- file-initialized
|
||||
[project-id file-id]
|
||||
[{:keys [file users project libraries] :as bundle}]
|
||||
(ptk/reify ::file-initialized
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :workspace-file
|
||||
(fn [file]
|
||||
(if (= (:id file) file-id)
|
||||
(assoc file :initialized true)
|
||||
file))))
|
||||
(assoc state
|
||||
:current-team-id (:team-id project)
|
||||
:users (d/index-by :id users)
|
||||
:workspace-undo {}
|
||||
:workspace-project project
|
||||
:workspace-file (assoc file :initialized true)
|
||||
:workspace-data (:data file)
|
||||
:workspace-libraries (d/index-by :id libraries)))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [it state stream]
|
||||
(let [ignore-until (get-in state [:workspace-file :ignore-sync-until])
|
||||
(let [file-id (:id file)
|
||||
ignore-until (:ignore-sync-until file)
|
||||
needs-update? (some #(and (> (:modified-at %) (:synced-at %))
|
||||
(or (not ignore-until)
|
||||
(> (:modified-at %) ignore-until)))
|
||||
(vals (get state :workspace-libraries)))]
|
||||
libraries)]
|
||||
(when needs-update?
|
||||
(rx/of (dwl/notify-sync-file file-id)))))))
|
||||
|
||||
|
|
|
@ -263,29 +263,14 @@
|
|||
(rp/query :team-users {:file-id file-id})
|
||||
(rp/query :project {:id project-id})
|
||||
(rp/query :file-libraries {:file-id file-id}))
|
||||
(rx/first)
|
||||
(rx/map (fn [bundle] (apply bundle-fetched bundle)))))))
|
||||
|
||||
(defn- bundle-fetched
|
||||
[file users project libraries]
|
||||
(ptk/reify ::bundle-fetched
|
||||
IDeref
|
||||
(-deref [_]
|
||||
{:file file
|
||||
:users users
|
||||
:project project
|
||||
:libraries libraries})
|
||||
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc state
|
||||
:users (d/index-by :id users)
|
||||
:workspace-undo {}
|
||||
:workspace-project project
|
||||
:workspace-file file
|
||||
:workspace-data (:data file)
|
||||
:workspace-libraries (d/index-by :id libraries)))))
|
||||
|
||||
(rx/take 1)
|
||||
(rx/map (fn [[file users project libraries]]
|
||||
{:file file
|
||||
:users users
|
||||
:project project
|
||||
:libraries libraries}))
|
||||
(rx/mapcat (fn [{:keys [project] :as bundle}]
|
||||
(rx/of (ptk/data-event ::bundle-fetched bundle))))))))
|
||||
|
||||
;; --- Set File shared
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
:toggle-assets {:tooltip (ds/alt "I")
|
||||
:command (ds/a-mod "i")
|
||||
:fn #(st/emit! (dw/go-to-layout :assets))}
|
||||
|
||||
|
||||
:toggle-history {:tooltip (ds/alt "H")
|
||||
:command (ds/a-mod "h")
|
||||
:fn #(st/emit! (dw/go-to-layout :document-history))}
|
||||
|
@ -45,7 +45,7 @@
|
|||
:toggle-rules {:tooltip (ds/meta-shift "R")
|
||||
:command (ds/c-mod "shift+r")
|
||||
:fn #(st/emit! (dw/toggle-layout-flags :rules))}
|
||||
|
||||
|
||||
:select-all {:tooltip (ds/meta "A")
|
||||
:command (ds/c-mod "a")
|
||||
:fn #(st/emit! (dw/select-all))}
|
||||
|
@ -73,7 +73,7 @@
|
|||
:decrease-zoom {:tooltip "-"
|
||||
:command "-"
|
||||
:fn #(st/emit! (dw/decrease-zoom nil))}
|
||||
|
||||
|
||||
:group {:tooltip (ds/meta "G")
|
||||
:command (ds/c-mod "g")
|
||||
:fn #(st/emit! dw/group-selected)}
|
||||
|
@ -173,7 +173,8 @@
|
|||
|
||||
:paste {:tooltip (ds/meta "V")
|
||||
:disabled true
|
||||
:command (ds/c-mod "v")}
|
||||
:command (ds/c-mod "v")
|
||||
:fn (constantly nil)}
|
||||
|
||||
:delete {:tooltip (ds/supr)
|
||||
:command ["del" "backspace"]
|
||||
|
|
|
@ -44,17 +44,18 @@
|
|||
(fn [node]
|
||||
;; There is a problem when changing the state in this callback that
|
||||
;; produces the dropdown to close in the same event
|
||||
(timers/schedule
|
||||
#(when-let [bounds (when node (dom/get-bounding-rect node))]
|
||||
(let [{window-height :height} (dom/get-window-size)
|
||||
{:keys [left top height]} bounds
|
||||
bottom (when (< (- window-height top) 300) (- window-height top))
|
||||
top (when (>= (- window-height top) 300) (+ top height))]
|
||||
(swap! state
|
||||
assoc
|
||||
:left left
|
||||
:top top
|
||||
:bottom bottom)))))]
|
||||
(when node
|
||||
(timers/schedule
|
||||
#(when-let [bounds (when node (dom/get-bounding-rect node))]
|
||||
(let [{window-height :height} (dom/get-window-size)
|
||||
{:keys [left top height]} bounds
|
||||
bottom (when (< (- window-height top) 300) (- window-height top))
|
||||
top (when (>= (- window-height top) 300) (+ top height))]
|
||||
(swap! state
|
||||
assoc
|
||||
:left left
|
||||
:top top
|
||||
:bottom bottom))))))]
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps value)
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
[app.util.router :as rt]
|
||||
[app.util.time :as dt]
|
||||
[app.util.timers :as ts]
|
||||
[app.util.webapi :as wapi]
|
||||
[beicon.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
[rumext.alpha :as mf]))
|
||||
|
@ -255,17 +256,19 @@
|
|||
|
||||
(mf/use-effect
|
||||
(fn []
|
||||
(let [node (mf/ref-val rowref)
|
||||
obs (new js/ResizeObserver
|
||||
(fn [entries x]
|
||||
(ts/raf #(let [row (first entries)
|
||||
row-rect (.-contentRect ^js row)
|
||||
row-width (.-width ^js row-rect)]
|
||||
(reset! width row-width)))))]
|
||||
|
||||
(.observe ^js obs node)
|
||||
(let [node (mf/ref-val rowref)
|
||||
mnt? (volatile! true)
|
||||
sub (->> (wapi/observe-resize node)
|
||||
(rx/observe-on :af)
|
||||
(rx/subs (fn [entries]
|
||||
(let [row (first entries)
|
||||
row-rect (.-contentRect ^js row)
|
||||
row-width (.-width ^js row-rect)]
|
||||
(when @mnt?
|
||||
(reset! width row-width))))))]
|
||||
(fn []
|
||||
(.disconnect ^js obs)))))
|
||||
(vreset! mnt? false)
|
||||
(rx/dispose! sub)))))
|
||||
|
||||
[:div.grid-row.no-wrap {:ref rowref}
|
||||
(when dragging?
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
(events/unlistenByKey key1))))]
|
||||
|
||||
(mf/use-effect on-mount)
|
||||
(hooks/use-shortcuts sc/shortcuts)
|
||||
(hooks/use-shortcuts ::handoff sc/shortcuts)
|
||||
|
||||
[:div.handoff-layout {:class (dom/classnames :force-visible
|
||||
(:show-thumbnails state))}
|
||||
|
|
|
@ -9,10 +9,11 @@
|
|||
(:require
|
||||
[app.common.spec :as us]
|
||||
[app.main.data.shortcuts :as dsc]
|
||||
[app.main.store :as st]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.object :as obj]
|
||||
[app.util.dom.dnd :as dnd]
|
||||
[app.util.logging :as log]
|
||||
[app.util.object :as obj]
|
||||
[app.util.timers :as ts]
|
||||
[app.util.transit :as t]
|
||||
[app.util.webapi :as wapi]
|
||||
|
@ -35,11 +36,13 @@
|
|||
state))
|
||||
|
||||
(defn use-shortcuts
|
||||
[shortcuts]
|
||||
[key shortcuts]
|
||||
(mf/use-effect
|
||||
#js [(str key) shortcuts]
|
||||
(fn []
|
||||
(dsc/bind-shortcuts shortcuts)
|
||||
(fn [] (dsc/remove-shortcuts)))))
|
||||
(st/emit! (dsc/push-shortcuts key shortcuts))
|
||||
(fn []
|
||||
(st/emit! (dsc/pop-shortcuts key))))))
|
||||
|
||||
(defn invisible-image
|
||||
[]
|
||||
|
|
|
@ -237,7 +237,7 @@
|
|||
(events/unlistenByKey key3))))]
|
||||
|
||||
(mf/use-effect on-mount)
|
||||
(hooks/use-shortcuts sc/shortcuts)
|
||||
(hooks/use-shortcuts ::viewer sc/shortcuts)
|
||||
|
||||
[:div.viewer-layout {:class (dom/classnames :force-visible
|
||||
(:show-thumbnails state))}
|
||||
|
|
|
@ -138,7 +138,7 @@
|
|||
(mf/use-effect
|
||||
(fn []
|
||||
;; Close any non-modal dialog that may be still open
|
||||
(st/emitf dm/hide)))
|
||||
(st/emit! dm/hide)))
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps file)
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
[app.util.logging :as log]
|
||||
[app.util.object :as obj]
|
||||
[app.util.timers :as timers]
|
||||
[app.util.webapi :as wapi]
|
||||
[app.util.text-editor :as ted]
|
||||
[okulary.core :as l]
|
||||
[beicon.core :as rx]
|
||||
|
@ -62,6 +63,7 @@
|
|||
(true? (obj/get props "edition?"))
|
||||
(update-with-current-editor-state))
|
||||
|
||||
mnt (mf/use-ref true)
|
||||
paragraph-ref (mf/use-state nil)
|
||||
|
||||
handle-resize-text
|
||||
|
@ -83,20 +85,24 @@
|
|||
(mf/deps handle-resize-text)
|
||||
(fn [node]
|
||||
(when node
|
||||
(let [obs-ref (atom nil)]
|
||||
(timers/schedule
|
||||
(fn []
|
||||
(when-let [ps-node (dom/query node ".paragraph-set")]
|
||||
(reset! paragraph-ref ps-node))))))))]
|
||||
(timers/schedule
|
||||
#(when (mf/ref-val mnt)
|
||||
(when-let [ps-node (dom/query node ".paragraph-set")]
|
||||
(reset! paragraph-ref ps-node)))))))]
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps @paragraph-ref handle-resize-text grow-type)
|
||||
(fn []
|
||||
(when-let [paragraph-node @paragraph-ref]
|
||||
(let [observer (js/ResizeObserver. handle-resize-text)]
|
||||
(let [sub (->> (wapi/observe-resize paragraph-node)
|
||||
(rx/observe-on :af)
|
||||
(rx/subs handle-resize-text))]
|
||||
(log/debug :msg "Attach resize observer" :shape-id id :shape-name name)
|
||||
(.observe observer paragraph-node)
|
||||
#(.disconnect observer)))))
|
||||
(fn []
|
||||
(rx/dispose! sub))))))
|
||||
|
||||
(mf/use-effect
|
||||
(fn [] #(mf/set-ref-val! mnt false)))
|
||||
|
||||
[:& text/text-shape {:ref text-ref-cb :shape shape :grow-type (:grow-type shape)}]))
|
||||
|
||||
|
|
|
@ -153,18 +153,6 @@
|
|||
(utils/update-transform render-node roots modifiers)
|
||||
(utils/remove-transform render-node roots))))))
|
||||
|
||||
(defn setup-shortcuts [path-editing? drawing-path?]
|
||||
(mf/use-effect
|
||||
(mf/deps path-editing? drawing-path?)
|
||||
(fn []
|
||||
(cond
|
||||
(or drawing-path? path-editing?)
|
||||
(dsc/bind-shortcuts psc/shortcuts)
|
||||
|
||||
:else
|
||||
(dsc/bind-shortcuts wsc/shortcuts))
|
||||
dsc/remove-shortcuts)))
|
||||
|
||||
(defn inside-vbox [vbox objects frame-id]
|
||||
(let [frame (get objects frame-id)]
|
||||
|
||||
|
@ -195,3 +183,17 @@
|
|||
(:frame-id @hover))]
|
||||
(when (not (contains? @active-frames frame-id))
|
||||
(swap! active-frames assoc frame-id true))))))
|
||||
|
||||
;; NOTE: this is executed on each page change, maybe we need to move
|
||||
;; this shortcuts outside the viewport?
|
||||
|
||||
(defn setup-shortcuts
|
||||
[path-editing? drawing-path?]
|
||||
(hooks/use-shortcuts ::workspace wsc/shortcuts)
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps path-editing? drawing-path?)
|
||||
(fn []
|
||||
(when (or drawing-path? path-editing?)
|
||||
(st/emit! (dsc/push-shortcuts ::path psc/shortcuts))
|
||||
(st/emitf (dsc/pop-shortcuts ::path))))))
|
||||
|
|
|
@ -293,3 +293,21 @@
|
|||
|
||||
(defn remove-attribute [^js node ^string attr]
|
||||
(.removeAttribute node attr))
|
||||
|
||||
(defn scroll-into-view!
|
||||
([element]
|
||||
(.scrollIntoView ^js element false))
|
||||
([element scroll-top]
|
||||
(.scrollIntoView ^js element scroll-top)))
|
||||
|
||||
(defn is-in-viewport?
|
||||
[element]
|
||||
(let [rect (.getBoundingClientRect element)
|
||||
height (or (.-innerHeight js/window)
|
||||
(.. js/document -documentElement -clientHeight))
|
||||
width (or (.-innerWidth js/window)
|
||||
(.. js/document -documentElement -clientWidth))]
|
||||
(and (>= (.-top rect) 0)
|
||||
(>= (.-left rect) 0)
|
||||
(<= (.-bottom rect) height)
|
||||
(<= (.-right rect) width))))
|
||||
|
|
|
@ -131,3 +131,14 @@
|
|||
:else
|
||||
(ex/raise :type :not-supported
|
||||
:hint "seems like the current browset does not support fullscreen api.")))
|
||||
|
||||
(defn observe-resize
|
||||
[node]
|
||||
(rx/create
|
||||
(fn [subs]
|
||||
(let [obs (js/ResizeObserver.
|
||||
(fn [entries x]
|
||||
(rx/push! subs entries)))]
|
||||
(.observe ^js obs node)
|
||||
(fn []
|
||||
(.disconnect ^js obs))))))
|
||||
|
|
|
@ -1169,10 +1169,10 @@ dashdash@^1.12.0:
|
|||
dependencies:
|
||||
assert-plus "^1.0.0"
|
||||
|
||||
date-fns@^2.21.1:
|
||||
version "2.21.1"
|
||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.21.1.tgz#679a4ccaa584c0706ea70b3fa92262ac3009d2b0"
|
||||
integrity sha512-m1WR0xGiC6j6jNFAyW4Nvh4WxAi4JF4w9jRJwSI8nBmNcyZXPcP9VUQG+6gHQXAmqaGEKDKhOqAtENDC941UkA==
|
||||
date-fns@^2.21.3:
|
||||
version "2.21.3"
|
||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.21.3.tgz#8f5f6889d7a96bbcc1f0ea50239b397a83357f9b"
|
||||
integrity sha512-HeYdzCaFflc1i4tGbj7JKMjM4cKGYoyxwcIIkHzNgCkX8xXDNJDZXgDDVchIWpN4eQc3lH37WarduXFZJOtxfw==
|
||||
|
||||
dateformat@^3.0.3:
|
||||
version "3.0.3"
|
||||
|
@ -3343,10 +3343,10 @@ nan@^2.12.1, nan@^2.13.2:
|
|||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"
|
||||
integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==
|
||||
|
||||
nanoid@^3.1.22:
|
||||
version "3.1.22"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.22.tgz#b35f8fb7d151990a8aebd5aa5015c03cf726f844"
|
||||
integrity sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ==
|
||||
nanoid@^3.1.23:
|
||||
version "3.1.23"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81"
|
||||
integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==
|
||||
|
||||
nanomatch@^1.2.9:
|
||||
version "1.2.13"
|
||||
|
@ -3962,13 +3962,13 @@ postcss@^7.0.16:
|
|||
source-map "^0.6.1"
|
||||
supports-color "^6.1.0"
|
||||
|
||||
postcss@^8.2.7:
|
||||
version "8.2.13"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.13.tgz#dbe043e26e3c068e45113b1ed6375d2d37e2129f"
|
||||
integrity sha512-FCE5xLH+hjbzRdpbRb1IMCvPv9yZx2QnDarBEYSN0N0HYk+TcXsEhwdFcFb+SRWOKzKGErhIEbBK2ogyLdTtfQ==
|
||||
postcss@^8.2.15:
|
||||
version "8.2.15"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.15.tgz#9e66ccf07292817d226fc315cbbf9bc148fbca65"
|
||||
integrity sha512-2zO3b26eJD/8rb106Qu2o7Qgg52ND5HPjcyQiK2B98O388h43A448LCslC0dI2P97wCAQRJsFvwTRcXxTKds+Q==
|
||||
dependencies:
|
||||
colorette "^1.2.2"
|
||||
nanoid "^3.1.22"
|
||||
nanoid "^3.1.23"
|
||||
source-map "^0.6.1"
|
||||
|
||||
pretty-hrtime@^1.0.0:
|
||||
|
@ -4376,10 +4376,10 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
|
|||
hash-base "^3.0.0"
|
||||
inherits "^2.0.1"
|
||||
|
||||
rxjs@~7.0.0-beta.12:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.0.0.tgz#c55d67c52aee8804d32ab60965e335bd41e2dc2d"
|
||||
integrity sha512-I1V/ArAtGJg4kmCfms8fULm0SwYgEsAf2d5WPCBGzTYm2qTjO3Tx4EDFaGjbOox8CeEsC69jQK22mnmfyA26sw==
|
||||
rxjs@~7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.0.1.tgz#5f41c4f991cea550471fc5d215727390103702c7"
|
||||
integrity sha512-wViQ4Vgps1xJwqWIBooMNN44usCSthL7wCUl4qWqrVjhGfWyVyXcxlYzfDKkJKACQvZMTOft/jJ3RkbwK1j9QQ==
|
||||
dependencies:
|
||||
tslib "~2.1.0"
|
||||
|
||||
|
@ -4493,10 +4493,10 @@ shadow-cljs-jar@1.3.2:
|
|||
resolved "https://registry.yarnpkg.com/shadow-cljs-jar/-/shadow-cljs-jar-1.3.2.tgz#97273afe1747b6a2311917c1c88d9e243c81957b"
|
||||
integrity sha512-XmeffAZHv8z7451kzeq9oKh8fh278Ak+UIOGGrapyqrFBB773xN8vMQ3O7J7TYLnb9BUwcqadKkmgaq7q6fhZg==
|
||||
|
||||
shadow-cljs@2.12.5:
|
||||
version "2.12.5"
|
||||
resolved "https://registry.yarnpkg.com/shadow-cljs/-/shadow-cljs-2.12.5.tgz#d3cf29fc1f1e02dd875939549419979e0feadbf4"
|
||||
integrity sha512-o3xo3coRgnlkI/iI55ccHjj6AU3F1+ovk3hhK86e3P2JGGOpNTAwsGNxUpMC5JAwS9Nz0v6sSk73hWjEOnm6fQ==
|
||||
shadow-cljs@2.12.6:
|
||||
version "2.12.6"
|
||||
resolved "https://registry.yarnpkg.com/shadow-cljs/-/shadow-cljs-2.12.6.tgz#039539fdc35a19c2f2cd15792ae17e7928f97428"
|
||||
integrity sha512-dNw989EFQki/59kD8Cd8b6HIpBTqPj9ksWIvSg6hI1bgezZHT0oHfJH5UIbXPD+dnVLvbOnDnfOMWYH6ozalcA==
|
||||
dependencies:
|
||||
node-libs-browser "^2.2.1"
|
||||
readline-sync "^1.4.7"
|
||||
|
|
Loading…
Add table
Reference in a new issue