mirror of
https://github.com/penpot/penpot.git
synced 2025-05-15 15:06:39 +02:00
🎉 Add push notifications support
This commit is contained in:
parent
15a9035ed1
commit
e5dedb1e3d
8 changed files with 189 additions and 3 deletions
|
@ -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]} _]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
Add a link
Reference in a new issue