mirror of
https://github.com/penpot/penpot.git
synced 2025-05-06 05:46:08 +02:00
Merge pull request #3517 from penpot/niwinz-enhancements-push-notifications
🎉 Add the ability to send push notifications
This commit is contained in:
commit
a1ac839b2a
17 changed files with 388 additions and 74 deletions
|
@ -6,7 +6,6 @@
|
||||||
rumext.v2/defc clojure.core/defn
|
rumext.v2/defc clojure.core/defn
|
||||||
rumext.v2/fnc clojure.core/fn
|
rumext.v2/fnc clojure.core/fn
|
||||||
app.common.data/export clojure.core/def
|
app.common.data/export clojure.core/def
|
||||||
app.db/with-atomic clojure.core/with-open
|
|
||||||
app.common.data.macros/get-in clojure.core/get-in
|
app.common.data.macros/get-in clojure.core/get-in
|
||||||
app.common.data.macros/with-open clojure.core/with-open
|
app.common.data.macros/with-open clojure.core/with-open
|
||||||
app.common.data.macros/select-keys clojure.core/select-keys
|
app.common.data.macros/select-keys clojure.core/select-keys
|
||||||
|
@ -17,6 +16,7 @@
|
||||||
{app.common.data.macros/export hooks.export/export
|
{app.common.data.macros/export hooks.export/export
|
||||||
potok.core/reify hooks.export/potok-reify
|
potok.core/reify hooks.export/potok-reify
|
||||||
app.util.services/defmethod hooks.export/service-defmethod
|
app.util.services/defmethod hooks.export/service-defmethod
|
||||||
|
app.db/with-atomic hooks.export/penpot-with-atomic
|
||||||
}}
|
}}
|
||||||
|
|
||||||
:output
|
:output
|
||||||
|
|
|
@ -39,6 +39,43 @@
|
||||||
other))]
|
other))]
|
||||||
{:node result})))
|
{:node result})))
|
||||||
|
|
||||||
|
(defn penpot-with-atomic
|
||||||
|
[{:keys [node]}]
|
||||||
|
(let [[_ params & other] (:children node)
|
||||||
|
|
||||||
|
result (if (api/vector-node? params)
|
||||||
|
(api/list-node
|
||||||
|
(into [(api/token-node (symbol "clojure.core" "with-open")) params] other))
|
||||||
|
(api/list-node
|
||||||
|
(into [(api/token-node (symbol "clojure.core" "with-open"))
|
||||||
|
(api/vector-node [params params])]
|
||||||
|
other)))
|
||||||
|
|
||||||
|
]
|
||||||
|
{:node result}))
|
||||||
|
|
||||||
|
(defn penpot-defrecord
|
||||||
|
[{:keys [:node]}]
|
||||||
|
(let [[rnode rtype rparams & other] (:children node)
|
||||||
|
|
||||||
|
nodes [(api/token-node (symbol "do"))
|
||||||
|
(api/list-node
|
||||||
|
(into [(api/token-node (symbol (name (:value rnode)))) rtype rparams] other))
|
||||||
|
(api/list-node
|
||||||
|
[(api/token-node (symbol "defn"))
|
||||||
|
(api/token-node (symbol (str "pos->" (:string-value rtype))))
|
||||||
|
(api/vector-node
|
||||||
|
(->> (:children rparams)
|
||||||
|
(mapv (fn [t]
|
||||||
|
(api/token-node (symbol (str "_" (:string-value t))))))))
|
||||||
|
(api/token-node nil)])]
|
||||||
|
|
||||||
|
result (api/list-node nodes)]
|
||||||
|
|
||||||
|
;; (prn "=====>" (into {} rparams))
|
||||||
|
;; (prn (api/sexpr result))
|
||||||
|
{:node result}))
|
||||||
|
|
||||||
(defn clojure-specify
|
(defn clojure-specify
|
||||||
[{:keys [:node]}]
|
[{:keys [:node]}]
|
||||||
(let [[rnode rtype & other] (:children node)
|
(let [[rnode rtype & other] (:children node)
|
||||||
|
@ -48,7 +85,6 @@
|
||||||
other))]
|
other))]
|
||||||
{:node result}))
|
{:node result}))
|
||||||
|
|
||||||
|
|
||||||
(defn service-defmethod
|
(defn service-defmethod
|
||||||
[{:keys [:node]}]
|
[{:keys [:node]}]
|
||||||
(let [[rnode rtype ?meta & other] (:children node)
|
(let [[rnode rtype ?meta & other] (:children node)
|
||||||
|
|
|
@ -7,7 +7,7 @@ Report: {{hint|abbreviate:150}} - {{id}} - Penpot Error Report (v3)
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<nav>
|
<nav>
|
||||||
<div>[<a href="/dbg/error">⮜</a>]</div>
|
<div>[<a href="/dbg/error">⮜</a>]</div>
|
||||||
<div>[<a href="#message">message</a>]</div>
|
<div>[<a href="#head">head</a>]</div>
|
||||||
<div>[<a href="#props">props</a>]</div>
|
<div>[<a href="#props">props</a>]</div>
|
||||||
<div>[<a href="#context">context</a>]</div>
|
<div>[<a href="#context">context</a>]</div>
|
||||||
{% if params %}
|
{% if params %}
|
||||||
|
@ -29,10 +29,11 @@ Report: {{hint|abbreviate:150}} - {{id}} - Penpot Error Report (v3)
|
||||||
<main>
|
<main>
|
||||||
<div class="table">
|
<div class="table">
|
||||||
<div class="table-row multiline">
|
<div class="table-row multiline">
|
||||||
<div id="message" class="table-key">MESSAGE: </div>
|
<div id="head" class="table-key">HEAD</div>
|
||||||
|
|
||||||
<div class="table-val">
|
<div class="table-val">
|
||||||
<h1>{{hint}}</h1>
|
<h1><span class="not-important">Hint:</span> <br/> {{hint}}</h1>
|
||||||
|
<h2><span class="not-important">Reported at:</span> <br/> {{created-at}}</h2>
|
||||||
|
<h2><span class="not-important">Report ID:</span> <br/> {{id}}</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,11 @@ small {
|
||||||
color: #888;
|
color: #888;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.not-important {
|
||||||
|
color: #888;
|
||||||
|
font-weight: 200;
|
||||||
|
}
|
||||||
|
|
||||||
small > strong {
|
small > strong {
|
||||||
font-size: 9px;
|
font-size: 9px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -567,7 +567,7 @@
|
||||||
profile (get-profile cfg info)]
|
profile (get-profile cfg info)]
|
||||||
(generate-redirect cfg request info profile))
|
(generate-redirect cfg request info profile))
|
||||||
(catch Throwable cause
|
(catch Throwable cause
|
||||||
(l/error :hint "error on oauth process" :cause cause)
|
(l/warn :hint "error on oauth process" :cause cause)
|
||||||
(generate-error-redirect cfg cause))))
|
(generate-error-redirect cfg cause))))
|
||||||
|
|
||||||
(def provider-lookup
|
(def provider-lookup
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
;; Copyright (c) KALEIDOS INC
|
;; Copyright (c) KALEIDOS INC
|
||||||
|
|
||||||
(ns app.db
|
(ns app.db
|
||||||
(:refer-clojure :exclude [get])
|
(:refer-clojure :exclude [get run!])
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.exceptions :as ex]
|
[app.common.exceptions :as ex]
|
||||||
|
@ -218,7 +218,13 @@
|
||||||
|
|
||||||
(defmacro with-atomic
|
(defmacro with-atomic
|
||||||
[& args]
|
[& args]
|
||||||
`(jdbc/with-transaction ~@args))
|
(if (symbol? (first args))
|
||||||
|
(let [cfgs (first args)
|
||||||
|
body (rest args)]
|
||||||
|
`(jdbc/with-transaction [conn# (::pool ~cfgs)]
|
||||||
|
(let [~cfgs (assoc ~cfgs ::conn conn#)]
|
||||||
|
~@body)))
|
||||||
|
`(jdbc/with-transaction ~@args)))
|
||||||
|
|
||||||
(defn open
|
(defn open
|
||||||
[pool]
|
[pool]
|
||||||
|
@ -293,6 +299,10 @@
|
||||||
:hint "database object not found"))
|
:hint "database object not found"))
|
||||||
row))
|
row))
|
||||||
|
|
||||||
|
(defn plan
|
||||||
|
[ds sql]
|
||||||
|
(jdbc/plan ds sql sql/default-opts))
|
||||||
|
|
||||||
(defn get-by-id
|
(defn get-by-id
|
||||||
[ds table id & {:as opts}]
|
[ds table id & {:as opts}]
|
||||||
(get ds table {:id id} opts))
|
(get ds table {:id id} opts))
|
||||||
|
@ -381,6 +391,52 @@
|
||||||
([^Connection conn ^Savepoint sp]
|
([^Connection conn ^Savepoint sp]
|
||||||
(.rollback conn sp)))
|
(.rollback conn sp)))
|
||||||
|
|
||||||
|
(defn tx-run!
|
||||||
|
[cfg f]
|
||||||
|
(cond
|
||||||
|
(connection? cfg)
|
||||||
|
(tx-run! {::conn cfg} f)
|
||||||
|
|
||||||
|
(pool? cfg)
|
||||||
|
(tx-run! {::pool cfg} f)
|
||||||
|
|
||||||
|
(::conn cfg)
|
||||||
|
(let [conn (::conn cfg)
|
||||||
|
sp (savepoint conn)]
|
||||||
|
(try
|
||||||
|
(let [result (f cfg)]
|
||||||
|
(release! conn sp)
|
||||||
|
result)
|
||||||
|
(catch Throwable cause
|
||||||
|
(rollback! sp)
|
||||||
|
(throw cause))))
|
||||||
|
|
||||||
|
(::pool cfg)
|
||||||
|
(with-atomic [conn (::pool cfg)]
|
||||||
|
(f (assoc cfg ::conn conn)))
|
||||||
|
|
||||||
|
:else
|
||||||
|
(throw (IllegalArgumentException. "invalid arguments"))))
|
||||||
|
|
||||||
|
(defn run!
|
||||||
|
[cfg f]
|
||||||
|
(cond
|
||||||
|
(connection? cfg)
|
||||||
|
(run! {::conn cfg} f)
|
||||||
|
|
||||||
|
(pool? cfg)
|
||||||
|
(run! {::pool cfg} f)
|
||||||
|
|
||||||
|
(::conn cfg)
|
||||||
|
(f cfg)
|
||||||
|
|
||||||
|
(::pool cfg)
|
||||||
|
(with-open [^Connection conn (open (::pool cfg))]
|
||||||
|
(f (assoc cfg ::conn conn)))
|
||||||
|
|
||||||
|
:else
|
||||||
|
(throw (IllegalArgumentException. "invalid arguments"))))
|
||||||
|
|
||||||
(defn interval
|
(defn interval
|
||||||
[o]
|
[o]
|
||||||
(cond
|
(cond
|
||||||
|
|
|
@ -238,9 +238,11 @@
|
||||||
(-> (io/resource "app/templates/error-report.v2.tmpl")
|
(-> (io/resource "app/templates/error-report.v2.tmpl")
|
||||||
(tmpl/render report)))
|
(tmpl/render report)))
|
||||||
|
|
||||||
(render-template-v3 [{report :content id :id}]
|
(render-template-v3 [{:keys [content id created-at]}]
|
||||||
(-> (io/resource "app/templates/error-report.v3.tmpl")
|
(-> (io/resource "app/templates/error-report.v3.tmpl")
|
||||||
(tmpl/render (assoc report :id id))))
|
(tmpl/render (-> content
|
||||||
|
(assoc :id id)
|
||||||
|
(assoc :created-at (dt/format-instant created-at :rfc1123))))))
|
||||||
]
|
]
|
||||||
|
|
||||||
(when-not (authorized? pool request)
|
(when-not (authorized? pool request)
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
[app.common.logging :as l]
|
[app.common.logging :as l]
|
||||||
[app.common.pprint :as pp]
|
[app.common.pprint :as pp]
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
[app.db :as db]
|
[app.db :as db]
|
||||||
[app.http.session :as session]
|
[app.http.session :as session]
|
||||||
[app.metrics :as mtx]
|
[app.metrics :as mtx]
|
||||||
|
@ -99,7 +100,10 @@
|
||||||
(sp/pipe ch output-ch false)
|
(sp/pipe ch output-ch false)
|
||||||
|
|
||||||
;; Subscribe to the profile topic on msgbus/redis
|
;; Subscribe to the profile topic on msgbus/redis
|
||||||
(mbus/sub! msgbus :topic profile-id :chan ch)))
|
(mbus/sub! msgbus :topic profile-id :chan ch)
|
||||||
|
|
||||||
|
;; Subscribe to the system topic on msgbus/redis
|
||||||
|
(mbus/sub! msgbus :topic (str uuid/zero) :chan ch)))
|
||||||
|
|
||||||
(defmethod handle-message :close
|
(defmethod handle-message :close
|
||||||
[{:keys [::mbus/msgbus]} {:keys [::ws/id ::ws/state ::profile-id ::session-id]} _]
|
[{:keys [::mbus/msgbus]} {:keys [::ws/id ::ws/state ::profile-id ::session-id]} _]
|
||||||
|
|
|
@ -294,28 +294,40 @@
|
||||||
[output & {:keys [level] :or {level 0}}]
|
[output & {:keys [level] :or {level 0}}]
|
||||||
(ZstdOutputStream. ^OutputStream output (int level)))
|
(ZstdOutputStream. ^OutputStream output (int level)))
|
||||||
|
|
||||||
(defn- retrieve-file
|
(defn- get-files
|
||||||
[pool file-id]
|
[cfg ids]
|
||||||
(dm/with-open [conn (db/open pool)]
|
(letfn [(get-files* [{:keys [::db/conn]}]
|
||||||
(binding [pmap/*load-fn* (partial files/load-pointer conn file-id)]
|
(let [sql (str "SELECT id FROM file "
|
||||||
(some-> (db/get* conn :file {:id file-id})
|
" WHERE id = ANY(?) ")
|
||||||
(files/decode-row)
|
ids (db/create-array conn "uuid" ids)]
|
||||||
(files/process-pointers deref)))))
|
(->> (db/exec! conn [sql ids])
|
||||||
|
(into [] (map :id))
|
||||||
|
(not-empty))))]
|
||||||
|
|
||||||
(def ^:private sql:file-media-objects
|
(db/run! cfg get-files*)))
|
||||||
"SELECT * FROM file_media_object WHERE id = ANY(?)")
|
|
||||||
|
|
||||||
(defn- retrieve-file-media
|
(defn- get-file
|
||||||
[pool {:keys [data id] :as file}]
|
[cfg file-id]
|
||||||
|
(letfn [(get-file* [{:keys [::db/conn]}]
|
||||||
|
(binding [pmap/*load-fn* (partial files/load-pointer conn file-id)]
|
||||||
|
(some-> (db/get* conn :file {:id file-id} {::db/remove-deleted? false})
|
||||||
|
(files/decode-row)
|
||||||
|
(files/process-pointers deref))))]
|
||||||
|
|
||||||
|
(db/run! cfg get-file*)))
|
||||||
|
|
||||||
|
(defn- get-file-media
|
||||||
|
[{:keys [::db/pool]} {:keys [data id] :as file}]
|
||||||
(dm/with-open [conn (db/open pool)]
|
(dm/with-open [conn (db/open pool)]
|
||||||
(let [ids (app.tasks.file-gc/collect-used-media data)
|
(let [ids (app.tasks.file-gc/collect-used-media data)
|
||||||
ids (db/create-array conn "uuid" ids)]
|
ids (db/create-array conn "uuid" ids)
|
||||||
|
sql (str "SELECT * FROM file_media_object WHERE id = ANY(?)")]
|
||||||
|
|
||||||
;; We assoc the file-id again to the file-media-object row
|
;; We assoc the file-id again to the file-media-object row
|
||||||
;; because there are cases that used objects refer to other
|
;; because there are cases that used objects refer to other
|
||||||
;; files and we need to ensure in the exportation process that
|
;; files and we need to ensure in the exportation process that
|
||||||
;; all ids matches
|
;; all ids matches
|
||||||
(->> (db/exec! conn [sql:file-media-objects ids])
|
(->> (db/exec! conn [sql ids])
|
||||||
(mapv #(assoc % :file-id id))))))
|
(mapv #(assoc % :file-id id))))))
|
||||||
|
|
||||||
(def ^:private storage-object-id-xf
|
(def ^:private storage-object-id-xf
|
||||||
|
@ -325,34 +337,32 @@
|
||||||
|
|
||||||
(def ^:private sql:file-libraries
|
(def ^:private sql:file-libraries
|
||||||
"WITH RECURSIVE libs AS (
|
"WITH RECURSIVE libs AS (
|
||||||
SELECT fl.id, fl.deleted_at
|
SELECT fl.id
|
||||||
FROM file AS fl
|
FROM file AS fl
|
||||||
JOIN file_library_rel AS flr ON (flr.library_file_id = fl.id)
|
JOIN file_library_rel AS flr ON (flr.library_file_id = fl.id)
|
||||||
WHERE flr.file_id = ANY(?)
|
WHERE flr.file_id = ANY(?)
|
||||||
UNION
|
UNION
|
||||||
SELECT fl.id, fl.deleted_at
|
SELECT fl.id
|
||||||
FROM file AS fl
|
FROM file AS fl
|
||||||
JOIN file_library_rel AS flr ON (flr.library_file_id = fl.id)
|
JOIN file_library_rel AS flr ON (flr.library_file_id = fl.id)
|
||||||
JOIN libs AS l ON (flr.file_id = l.id)
|
JOIN libs AS l ON (flr.file_id = l.id)
|
||||||
)
|
)
|
||||||
SELECT DISTINCT l.id
|
SELECT DISTINCT l.id
|
||||||
FROM libs AS l
|
FROM libs AS l")
|
||||||
WHERE l.deleted_at IS NULL OR l.deleted_at > now();")
|
|
||||||
|
|
||||||
(defn- retrieve-libraries
|
(defn- get-libraries
|
||||||
[pool ids]
|
[{:keys [::db/pool]} ids]
|
||||||
(dm/with-open [conn (db/open pool)]
|
(dm/with-open [conn (db/open pool)]
|
||||||
(let [ids (db/create-array conn "uuid" ids)]
|
(let [ids (db/create-array conn "uuid" ids)]
|
||||||
(map :id (db/exec! pool [sql:file-libraries ids])))))
|
(map :id (db/exec! pool [sql:file-libraries ids])))))
|
||||||
|
|
||||||
(def ^:private sql:file-library-rels
|
(defn- get-library-relations
|
||||||
"SELECT * FROM file_library_rel
|
[cfg ids]
|
||||||
WHERE file_id = ANY(?)")
|
(db/run! cfg (fn [{:keys [::db/conn]}]
|
||||||
|
(let [ids (db/create-array conn "uuid" ids)
|
||||||
(defn- retrieve-library-relations
|
sql (str "SELECT flr.* FROM file_library_rel AS flr "
|
||||||
[pool ids]
|
" WHERE flr.file_id = ANY(?)")]
|
||||||
(dm/with-open [conn (db/open pool)]
|
(db/exec! conn [sql ids])))))
|
||||||
(db/exec! conn [sql:file-library-rels (db/create-array conn "uuid" ids)])))
|
|
||||||
|
|
||||||
(defn- create-or-update-file
|
(defn- create-or-update-file
|
||||||
[conn params]
|
[conn params]
|
||||||
|
@ -378,7 +388,7 @@
|
||||||
;; --- EXPORT WRITER
|
;; --- EXPORT WRITER
|
||||||
|
|
||||||
(defn- embed-file-assets
|
(defn- embed-file-assets
|
||||||
[data conn file-id]
|
[data cfg file-id]
|
||||||
(letfn [(walk-map-form [form state]
|
(letfn [(walk-map-form [form state]
|
||||||
(cond
|
(cond
|
||||||
(uuid? (:fill-color-ref-file form))
|
(uuid? (:fill-color-ref-file form))
|
||||||
|
@ -408,7 +418,7 @@
|
||||||
;; NOTE: there is a possibility that shape refers to an
|
;; NOTE: there is a possibility that shape refers to an
|
||||||
;; non-existant file because the file was removed. In this
|
;; non-existant file because the file was removed. In this
|
||||||
;; case we just ignore the asset.
|
;; case we just ignore the asset.
|
||||||
(if-let [lib (retrieve-file conn lib-id)]
|
(if-let [lib (get-file cfg lib-id)]
|
||||||
(reduce (partial process-asset lib) data items)
|
(reduce (partial process-asset lib) data items)
|
||||||
data))
|
data))
|
||||||
|
|
||||||
|
@ -476,28 +486,33 @@
|
||||||
[:v1/metadata :v1/files :v1/rels :v1/sobjects])))))
|
[:v1/metadata :v1/files :v1/rels :v1/sobjects])))))
|
||||||
|
|
||||||
(defmethod write-section :v1/metadata
|
(defmethod write-section :v1/metadata
|
||||||
[{:keys [::db/pool ::output ::file-ids ::include-libraries?]}]
|
[{:keys [::output ::file-ids ::include-libraries?] :as cfg}]
|
||||||
(let [libs (when include-libraries?
|
(if-let [fids (get-files cfg file-ids)]
|
||||||
(retrieve-libraries pool file-ids))
|
(let [lids (when include-libraries?
|
||||||
files (into file-ids libs)]
|
(get-libraries cfg file-ids))
|
||||||
(write-obj! output {:version cf/version :files files})
|
ids (into fids lids)]
|
||||||
(vswap! *state* assoc :files files)))
|
(write-obj! output {:version cf/version :files ids})
|
||||||
|
(vswap! *state* assoc :files ids))
|
||||||
|
(ex/raise :type :not-found
|
||||||
|
:code :files-not-found
|
||||||
|
:hint "unable to retrieve files for export")))
|
||||||
|
|
||||||
(defmethod write-section :v1/files
|
(defmethod write-section :v1/files
|
||||||
[{:keys [::db/pool ::output ::embed-assets?]}]
|
[{:keys [::output ::embed-assets?] :as cfg}]
|
||||||
|
|
||||||
;; Initialize SIDS with empty vector
|
;; Initialize SIDS with empty vector
|
||||||
(vswap! *state* assoc :sids [])
|
(vswap! *state* assoc :sids [])
|
||||||
|
|
||||||
(doseq [file-id (-> *state* deref :files)]
|
(doseq [file-id (-> *state* deref :files)]
|
||||||
(let [file (cond-> (retrieve-file pool file-id)
|
(let [file (cond-> (get-file cfg file-id)
|
||||||
embed-assets?
|
embed-assets?
|
||||||
(update :data embed-file-assets pool file-id))
|
(update :data embed-file-assets cfg file-id))
|
||||||
|
|
||||||
media (retrieve-file-media pool file)]
|
media (get-file-media cfg file)]
|
||||||
|
|
||||||
(l/debug :hint "write penpot file"
|
(l/debug :hint "write penpot file"
|
||||||
:id file-id
|
:id file-id
|
||||||
|
:name (:name file)
|
||||||
:media (count media)
|
:media (count media)
|
||||||
::l/sync? true)
|
::l/sync? true)
|
||||||
|
|
||||||
|
@ -508,9 +523,10 @@
|
||||||
(vswap! *state* update :sids into storage-object-id-xf media))))
|
(vswap! *state* update :sids into storage-object-id-xf media))))
|
||||||
|
|
||||||
(defmethod write-section :v1/rels
|
(defmethod write-section :v1/rels
|
||||||
[{:keys [::db/pool ::output ::include-libraries?]}]
|
[{:keys [::output ::include-libraries?] :as cfg}]
|
||||||
(let [rels (when include-libraries?
|
(let [ids (-> *state* deref :files)
|
||||||
(retrieve-library-relations pool (-> *state* deref :files)))]
|
rels (when include-libraries?
|
||||||
|
(get-library-relations cfg ids))]
|
||||||
(l/debug :hint "found rels" :total (count rels) ::l/sync? true)
|
(l/debug :hint "found rels" :total (count rels) ::l/sync? true)
|
||||||
(write-obj! output rels)))
|
(write-obj! output rels)))
|
||||||
|
|
||||||
|
@ -518,6 +534,7 @@
|
||||||
[{:keys [::sto/storage ::output]}]
|
[{:keys [::sto/storage ::output]}]
|
||||||
(let [sids (-> *state* deref :sids)
|
(let [sids (-> *state* deref :sids)
|
||||||
storage (media/configure-assets-storage storage)]
|
storage (media/configure-assets-storage storage)]
|
||||||
|
|
||||||
(l/debug :hint "found sobjects"
|
(l/debug :hint "found sobjects"
|
||||||
:items (count sids)
|
:items (count sids)
|
||||||
::l/sync? true)
|
::l/sync? true)
|
||||||
|
@ -630,6 +647,8 @@
|
||||||
(when (not= file-id expected-file-id)
|
(when (not= file-id expected-file-id)
|
||||||
(ex/raise :type :validation
|
(ex/raise :type :validation
|
||||||
:code :inconsistent-penpot-file
|
:code :inconsistent-penpot-file
|
||||||
|
:found-id file-id
|
||||||
|
:expected-id expected-file-id
|
||||||
:hint "the penpot file seems corrupt, found unexpected uuid (file-id)"))
|
:hint "the penpot file seems corrupt, found unexpected uuid (file-id)"))
|
||||||
|
|
||||||
;; Update index using with media
|
;; Update index using with media
|
||||||
|
@ -679,18 +698,27 @@
|
||||||
|
|
||||||
(defmethod read-section :v1/rels
|
(defmethod read-section :v1/rels
|
||||||
[{:keys [::db/conn ::input ::timestamp]}]
|
[{:keys [::db/conn ::input ::timestamp]}]
|
||||||
(let [rels (read-obj! input)]
|
(let [rels (read-obj! input)
|
||||||
|
ids (into #{} (-> *state* deref :files))]
|
||||||
;; Insert all file relations
|
;; Insert all file relations
|
||||||
(doseq [rel rels]
|
(doseq [{:keys [library-file-id] :as rel} rels]
|
||||||
(let [rel (-> rel
|
(let [rel (-> rel
|
||||||
(assoc :synced-at timestamp)
|
(assoc :synced-at timestamp)
|
||||||
(update :file-id lookup-index)
|
(update :file-id lookup-index)
|
||||||
(update :library-file-id lookup-index))]
|
(update :library-file-id lookup-index))]
|
||||||
(l/debug :hint "create file library link"
|
|
||||||
:file-id (:file-id rel)
|
(if (contains? ids library-file-id)
|
||||||
:lib-id (:library-file-id rel)
|
(do
|
||||||
::l/sync? true)
|
(l/debug :hint "create file library link"
|
||||||
(db/insert! conn :file-library-rel rel)))))
|
:file-id (:file-id rel)
|
||||||
|
:lib-id (:library-file-id rel)
|
||||||
|
::l/sync? true)
|
||||||
|
(db/insert! conn :file-library-rel rel))
|
||||||
|
|
||||||
|
(l/warn :hint "ignoring file library link"
|
||||||
|
:file-id (:file-id rel)
|
||||||
|
:lib-id (:library-file-id rel)
|
||||||
|
::l/sync? true))))))
|
||||||
|
|
||||||
(defmethod read-section :v1/sobjects
|
(defmethod read-section :v1/sobjects
|
||||||
[{:keys [::sto/storage ::db/conn ::input ::overwrite?]}]
|
[{:keys [::sto/storage ::db/conn ::input ::overwrite?]}]
|
||||||
|
|
|
@ -8,10 +8,15 @@
|
||||||
"A collection of adhoc fixes scripts."
|
"A collection of adhoc fixes scripts."
|
||||||
#_:clj-kondo/ignore
|
#_:clj-kondo/ignore
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
[app.common.logging :as l]
|
[app.common.logging :as l]
|
||||||
[app.common.pprint :as p]
|
[app.common.pprint :as p]
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
|
[app.config :as cf]
|
||||||
[app.db :as db]
|
[app.db :as db]
|
||||||
|
[app.msgbus :as mbus]
|
||||||
[app.rpc.commands.auth :as auth]
|
[app.rpc.commands.auth :as auth]
|
||||||
[app.rpc.commands.profile :as profile]
|
[app.rpc.commands.profile :as profile]
|
||||||
[app.srepl.fixes :as f]
|
[app.srepl.fixes :as f]
|
||||||
|
@ -164,3 +169,106 @@
|
||||||
(alter-var-root var (fn [f]
|
(alter-var-root var (fn [f]
|
||||||
(or (::original (meta f)) f))))
|
(or (::original (meta f)) f))))
|
||||||
|
|
||||||
|
(defn notify!
|
||||||
|
[{:keys [::mbus/msgbus ::db/pool]} & {:keys [dest code message level]
|
||||||
|
:or {code :generic level :info}
|
||||||
|
:as params}]
|
||||||
|
(dm/verify!
|
||||||
|
["invalid level %" level]
|
||||||
|
(contains? #{:success :error :info :warning} level))
|
||||||
|
|
||||||
|
(dm/verify!
|
||||||
|
["invalid code: %" code]
|
||||||
|
(contains? #{:generic :upgrade-version} code))
|
||||||
|
|
||||||
|
(letfn [(send [dest]
|
||||||
|
(l/inf :hint "sending notification" :dest (str dest))
|
||||||
|
(let [message {:type :notification
|
||||||
|
:code code
|
||||||
|
:level level
|
||||||
|
:version (:full cf/version)
|
||||||
|
:subs-id dest
|
||||||
|
:message message}
|
||||||
|
message (->> (dissoc params :dest :code :message :level)
|
||||||
|
(merge message))]
|
||||||
|
(mbus/pub! msgbus
|
||||||
|
:topic (str dest)
|
||||||
|
:message message)))
|
||||||
|
|
||||||
|
(resolve-profile [email]
|
||||||
|
(some-> (db/get* pool :profile {:email (str/lower email)} {:columns [:id]}) :id vector))
|
||||||
|
|
||||||
|
(resolve-team [team-id]
|
||||||
|
(->> (db/query pool :team-profile-rel
|
||||||
|
{:team-id team-id}
|
||||||
|
{:columns [:profile-id]})
|
||||||
|
(map :profile-id)))
|
||||||
|
|
||||||
|
(parse-uuid [v]
|
||||||
|
(if (uuid? v)
|
||||||
|
v
|
||||||
|
(d/parse-uuid v)))
|
||||||
|
|
||||||
|
(resolve-dest [dest]
|
||||||
|
(cond
|
||||||
|
(uuid? dest)
|
||||||
|
[dest]
|
||||||
|
|
||||||
|
(string? dest)
|
||||||
|
(some-> dest parse-uuid resolve-dest)
|
||||||
|
|
||||||
|
(nil? dest)
|
||||||
|
(resolve-dest uuid/zero)
|
||||||
|
|
||||||
|
(map? dest)
|
||||||
|
(sequence (comp
|
||||||
|
(map vec)
|
||||||
|
(mapcat resolve-dest))
|
||||||
|
dest)
|
||||||
|
|
||||||
|
(and (coll? dest)
|
||||||
|
(every? coll? dest))
|
||||||
|
(sequence (comp
|
||||||
|
(map vec)
|
||||||
|
(mapcat resolve-dest))
|
||||||
|
dest)
|
||||||
|
|
||||||
|
(vector? dest)
|
||||||
|
(let [[op param] dest]
|
||||||
|
(cond
|
||||||
|
(= op :email)
|
||||||
|
(cond
|
||||||
|
(and (coll? param)
|
||||||
|
(every? string? param))
|
||||||
|
(sequence (comp
|
||||||
|
(keep resolve-profile)
|
||||||
|
(mapcat identity))
|
||||||
|
param)
|
||||||
|
|
||||||
|
(string? param)
|
||||||
|
(resolve-profile param))
|
||||||
|
|
||||||
|
(= op :team-id)
|
||||||
|
(cond
|
||||||
|
(coll? param)
|
||||||
|
(sequence (comp
|
||||||
|
(mapcat resolve-team)
|
||||||
|
(keep parse-uuid))
|
||||||
|
param)
|
||||||
|
|
||||||
|
(uuid? param)
|
||||||
|
(resolve-team param)
|
||||||
|
|
||||||
|
(string? param)
|
||||||
|
(some-> param parse-uuid resolve-team))
|
||||||
|
|
||||||
|
(= op :profile-id)
|
||||||
|
(if (coll? param)
|
||||||
|
(sequence (keep parse-uuid) param)
|
||||||
|
(resolve-dest param))))))
|
||||||
|
]
|
||||||
|
|
||||||
|
(->> (resolve-dest dest)
|
||||||
|
(filter some?)
|
||||||
|
(into #{})
|
||||||
|
(run! send))))
|
||||||
|
|
|
@ -1157,6 +1157,7 @@ input[type="range"]:focus::-ms-fill-upper {
|
||||||
|
|
||||||
.wrapper {
|
.wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
padding: $size-2;
|
padding: $size-2;
|
||||||
|
@ -1173,6 +1174,9 @@ input[type="range"]:focus::-ms-fill-upper {
|
||||||
padding: $size-2;
|
padding: $size-2;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
padding: 10px 15px;
|
||||||
|
min-height: 48px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,10 @@
|
||||||
(ns app.main.data.common
|
(ns app.main.data.common
|
||||||
"A general purpose events."
|
"A general purpose events."
|
||||||
(:require
|
(:require
|
||||||
|
[app.config :as cf]
|
||||||
|
[app.main.data.messages :as msg]
|
||||||
[app.main.repo :as rp]
|
[app.main.repo :as rp]
|
||||||
|
[app.util.i18n :refer [tr]]
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[potok.core :as ptk]))
|
[potok.core :as ptk]))
|
||||||
|
|
||||||
|
@ -43,3 +46,33 @@
|
||||||
(watch [_ _ _]
|
(watch [_ _ _]
|
||||||
(->> (rp/cmd! :delete-share-link {:id id})
|
(->> (rp/cmd! :delete-share-link {:id id})
|
||||||
(rx/ignore)))))
|
(rx/ignore)))))
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; NOTIFICATIONS
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(defn force-reload!
|
||||||
|
[]
|
||||||
|
(.reload js/location))
|
||||||
|
|
||||||
|
(defn handle-notification
|
||||||
|
[{:keys [message code level] :as params}]
|
||||||
|
(ptk/reify ::show-notification
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ _ _]
|
||||||
|
(case code
|
||||||
|
:upgrade-version
|
||||||
|
(when (or (not= (:version params) (:full cf/version))
|
||||||
|
(true? (:force params)))
|
||||||
|
(rx/of (msg/dialog
|
||||||
|
:content (tr "notifications.by-code.upgrade-version")
|
||||||
|
:controls :inline-actions
|
||||||
|
:type level
|
||||||
|
:actions [{:label "Refresh" :callback force-reload!}]
|
||||||
|
:tag :notification)))
|
||||||
|
|
||||||
|
(rx/of (msg/dialog
|
||||||
|
:content message
|
||||||
|
:controls :close
|
||||||
|
:type level
|
||||||
|
:tag :notification))))))
|
||||||
|
|
|
@ -13,10 +13,12 @@
|
||||||
[app.common.uri :as u]
|
[app.common.uri :as u]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.config :as cf]
|
[app.config :as cf]
|
||||||
|
[app.main.data.common :refer [handle-notification]]
|
||||||
[app.main.data.events :as ev]
|
[app.main.data.events :as ev]
|
||||||
[app.main.data.fonts :as df]
|
[app.main.data.fonts :as df]
|
||||||
[app.main.data.media :as di]
|
[app.main.data.media :as di]
|
||||||
[app.main.data.users :as du]
|
[app.main.data.users :as du]
|
||||||
|
[app.main.data.websocket :as dws]
|
||||||
[app.main.features :as features]
|
[app.main.features :as features]
|
||||||
[app.main.repo :as rp]
|
[app.main.repo :as rp]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
|
@ -61,7 +63,23 @@
|
||||||
(ptk/watch (fetch-projects) state stream)
|
(ptk/watch (fetch-projects) state stream)
|
||||||
(ptk/watch (fetch-team-members) state stream)
|
(ptk/watch (fetch-team-members) state stream)
|
||||||
(ptk/watch (du/fetch-teams) state stream)
|
(ptk/watch (du/fetch-teams) state stream)
|
||||||
(ptk/watch (du/fetch-users {:team-id id}) state stream)))))
|
(ptk/watch (du/fetch-users {:team-id id}) state stream)
|
||||||
|
|
||||||
|
(let [stoper (rx/filter (ptk/type? ::finalize) stream)
|
||||||
|
profile-id (:profile-id state)]
|
||||||
|
(->> stream
|
||||||
|
(rx/filter (ptk/type? ::dws/message))
|
||||||
|
(rx/map deref)
|
||||||
|
(rx/filter (fn [{:keys [subs-id type] :as msg}]
|
||||||
|
(and (or (= subs-id uuid/zero)
|
||||||
|
(= subs-id profile-id))
|
||||||
|
(= :notification type))))
|
||||||
|
(rx/map handle-notification)
|
||||||
|
(rx/take-until stoper)))))))
|
||||||
|
|
||||||
|
(defn finalize
|
||||||
|
[params]
|
||||||
|
(ptk/data-event ::finalize params))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; Data Fetching (context aware: current team)
|
;; Data Fetching (context aware: current team)
|
||||||
|
|
|
@ -120,6 +120,17 @@
|
||||||
:position :fixed
|
:position :fixed
|
||||||
:timeout timeout})))
|
:timeout timeout})))
|
||||||
|
|
||||||
|
(defn dialog
|
||||||
|
[& {:keys [content controls actions position tag type]
|
||||||
|
:or {controls :none position :floating type :info}}]
|
||||||
|
(show (d/without-nils
|
||||||
|
{:content content
|
||||||
|
:type type
|
||||||
|
:position position
|
||||||
|
:controls controls
|
||||||
|
:actions actions
|
||||||
|
:tag tag})))
|
||||||
|
|
||||||
(defn info-dialog
|
(defn info-dialog
|
||||||
([content controls actions]
|
([content controls actions]
|
||||||
(info-dialog content controls actions nil))
|
(info-dialog content controls actions nil))
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.pages.changes :as cpc]
|
[app.common.pages.changes :as cpc]
|
||||||
[app.common.schema :as sm]
|
[app.common.schema :as sm]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
|
[app.main.data.common :refer [handle-notification]]
|
||||||
[app.main.data.websocket :as dws]
|
[app.main.data.websocket :as dws]
|
||||||
[app.main.data.workspace.changes :as dch]
|
[app.main.data.workspace.changes :as dch]
|
||||||
[app.main.data.workspace.libraries :as dwl]
|
[app.main.data.workspace.libraries :as dwl]
|
||||||
|
@ -57,8 +59,9 @@
|
||||||
(rx/filter (ptk/type? ::dws/message))
|
(rx/filter (ptk/type? ::dws/message))
|
||||||
(rx/map deref)
|
(rx/map deref)
|
||||||
(rx/filter (fn [{:keys [subs-id] :as msg}]
|
(rx/filter (fn [{:keys [subs-id] :as msg}]
|
||||||
(or (= subs-id team-id)
|
(or (= subs-id uuid/zero)
|
||||||
(= subs-id profile-id)
|
(= subs-id profile-id)
|
||||||
|
(= subs-id team-id)
|
||||||
(= subs-id file-id))))
|
(= subs-id file-id))))
|
||||||
(rx/map process-message))
|
(rx/map process-message))
|
||||||
|
|
||||||
|
@ -96,6 +99,7 @@
|
||||||
:pointer-update (handle-pointer-update msg)
|
:pointer-update (handle-pointer-update msg)
|
||||||
:file-change (handle-file-change msg)
|
:file-change (handle-file-change msg)
|
||||||
:library-change (handle-library-change msg)
|
:library-change (handle-library-change msg)
|
||||||
|
:notification (handle-notification msg)
|
||||||
nil))
|
nil))
|
||||||
|
|
||||||
(defn- handle-pointer-send
|
(defn- handle-pointer-send
|
||||||
|
|
|
@ -154,18 +154,18 @@
|
||||||
(hooks/use-shortcuts ::dashboard sc/shortcuts)
|
(hooks/use-shortcuts ::dashboard sc/shortcuts)
|
||||||
|
|
||||||
(mf/with-effect [team-id]
|
(mf/with-effect [team-id]
|
||||||
(st/emit! (dd/initialize {:id team-id})))
|
(st/emit! (dd/initialize {:id team-id}))
|
||||||
|
(fn []
|
||||||
|
(dd/finalize {:id team-id})))
|
||||||
|
|
||||||
(mf/use-effect
|
(mf/with-effect []
|
||||||
(fn []
|
(let [key (events/listen goog/global "keydown"
|
||||||
(let [events [(events/listen goog/global "keydown"
|
(fn [event]
|
||||||
(fn [event]
|
(when (kbd/enter? event)
|
||||||
(when (kbd/enter? event)
|
(dom/stop-propagation event)
|
||||||
(dom/stop-propagation event)
|
(st/emit! (dd/open-selected-file)))))]
|
||||||
(st/emit! (dd/open-selected-file)))))]]
|
(fn []
|
||||||
(fn []
|
(events/unlistenByKey key))))
|
||||||
(doseq [key events]
|
|
||||||
(events/unlistenByKey key))))))
|
|
||||||
|
|
||||||
[:& (mf/provider ctx/current-team-id) {:value team-id}
|
[:& (mf/provider ctx/current-team-id) {:value team-id}
|
||||||
[:& (mf/provider ctx/current-project-id) {:value project-id}
|
[:& (mf/provider ctx/current-project-id) {:value project-id}
|
||||||
|
|
|
@ -4960,3 +4960,7 @@ msgstr "Marketing"
|
||||||
#: src/app/main/ui/onboarding/questions.cljs
|
#: src/app/main/ui/onboarding/questions.cljs
|
||||||
msgid "questions.student-teacher"
|
msgid "questions.student-teacher"
|
||||||
msgstr "Student or teacher"
|
msgstr "Student or teacher"
|
||||||
|
|
||||||
|
#: src/app/main/data/common.cljs
|
||||||
|
msgid "notifications.by-code.upgrade-version"
|
||||||
|
msgstr "A new version is available, please refresh the page"
|
||||||
|
|
Loading…
Add table
Reference in a new issue