Merge pull request #4902 from penpot/niwinz-notifications-improvements

 Improvements to notifications
This commit is contained in:
Alejandro
2024-07-23 16:57:51 +02:00
committed by GitHub
7 changed files with 100 additions and 76 deletions

View File

@@ -194,6 +194,12 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn notify! (defn notify!
"Send flash notifications.
This method allows send flash notifications to specified target destinations.
The message can be a free text or a preconfigured one.
The destination can be: all, profile-id, team-id, or a coll of them."
[{:keys [::mbus/msgbus ::db/pool]} & {:keys [dest code message level] [{:keys [::mbus/msgbus ::db/pool]} & {:keys [dest code message level]
:or {code :generic level :info} :or {code :generic level :info}
:as params}] :as params}]
@@ -201,10 +207,6 @@
["invalid level %" level] ["invalid level %" level]
(contains? #{:success :error :info :warning} level)) (contains? #{:success :error :info :warning} level))
(dm/verify!
["invalid code: %" code]
(contains? #{:generic :upgrade-version} code))
(letfn [(send [dest] (letfn [(send [dest]
(l/inf :hint "sending notification" :dest (str dest)) (l/inf :hint "sending notification" :dest (str dest))
(let [message {:type :notification (let [message {:type :notification
@@ -230,6 +232,9 @@
(resolve-dest [dest] (resolve-dest [dest]
(cond (cond
(= :all dest)
[uuid/zero]
(uuid? dest) (uuid? dest)
[dest] [dest]
@@ -245,14 +250,15 @@
(mapcat resolve-dest)) (mapcat resolve-dest))
dest) dest)
(and (coll? dest) (and (vector? dest)
(every? coll? dest)) (every? vector? dest))
(sequence (comp (sequence (comp
(map vec) (map vec)
(mapcat resolve-dest)) (mapcat resolve-dest))
dest) dest)
(vector? dest) (and (vector? dest)
(keyword? (first dest)))
(let [[op param] dest] (let [[op param] dest]
(cond (cond
(= op :email) (= op :email)

View File

@@ -13,6 +13,7 @@
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.features :as features] [app.main.features :as features]
[app.main.repo :as rp] [app.main.repo :as rp]
[app.main.store :as st]
[app.util.i18n :refer [tr]] [app.util.i18n :refer [tr]]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[potok.v2.core :as ptk])) [potok.v2.core :as ptk]))
@@ -58,6 +59,10 @@
[] []
(.reload js/location)) (.reload js/location))
(defn hide-notifications!
[]
(st/emit! msg/hide))
(defn handle-notification (defn handle-notification
[{:keys [message code level] :as params}] [{:keys [message code level] :as params}]
(ptk/reify ::show-notification (ptk/reify ::show-notification
@@ -75,6 +80,15 @@
:actions [{:label "Refresh" :callback force-reload!}] :actions [{:label "Refresh" :callback force-reload!}]
:tag :notification))) :tag :notification)))
:maintenance
(rx/of (msg/dialog
:content (tr "notifications.by-code.maintenance")
:controls :inline-actions
:type level
:actions [{:label (tr "labels.accept")
:callback hide-notifications!}]
:tag :notification))
(rx/of (msg/dialog (rx/of (msg/dialog
:content message :content message
:controls :close :controls :close

View File

@@ -15,42 +15,42 @@
(declare hide) (declare hide)
(declare show) (declare show)
(def default-animation-timeout 600)
(def default-timeout 7000) (def default-timeout 7000)
(def ^:private (def ^:private schema:message
schema:message [:map {:title "Message"}
(sm/define [:type [::sm/one-of #{:success :error :info :warning}]]
[:map {:title "Message"} [:status {:optional true}
[:type [::sm/one-of #{:success :error :info :warning}]] [::sm/one-of #{:visible :hide}]]
[:status {:optional true} [:position {:optional true}
[::sm/one-of #{:visible :hide}]] [::sm/one-of #{:fixed :floating :inline}]]
[:position {:optional true} [:notification-type {:optional true}
[::sm/one-of #{:fixed :floating :inline}]] [::sm/one-of #{:inline :context :toast}]]
[:notification-type {:optional true} [:controls {:optional true}
[::sm/one-of #{:inline :context :toast}]] [::sm/one-of #{:none :close :inline-actions :bottom-actions}]]
[:controls {:optional true} [:tag {:optional true}
[::sm/one-of #{:none :close :inline-actions :bottom-actions}]] [:or :string :keyword]]
[:tag {:optional true} [:timeout {:optional true}
[:or :string :keyword]] [:maybe :int]]
[:timeout {:optional true} [:actions {:optional true}
[:maybe :int]] [:vector
[:actions {:optional true} [:map
[:vector [:label :string]
[:map [:callback ::sm/fn]]]]
[:label :string] [:links {:optional true}
[:callback ::sm/fn]]]] [:vector
[:links {:optional true} [:map
[:vector [:label :string]
[:map [:callback ::sm/fn]]]]])
[:label :string]
[:callback ::sm/fn]]]]])) (def ^:private valid-message?
(sm/validator schema:message))
(defn show (defn show
[data] [data]
(dm/assert! (dm/assert!
"expected valid message map" "expected valid message map"
(sm/check! schema:message data)) (valid-message? data))
(ptk/reify ::show (ptk/reify ::show
ptk/UpdateEvent ptk/UpdateEvent
@@ -76,14 +76,7 @@
(ptk/reify ::hide (ptk/reify ::hide
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(d/update-when state :message assoc :status :hide)) (dissoc state :message))))
ptk/WatchEvent
(watch [_ _ stream]
(let [stopper (rx/filter (ptk/type? ::show) stream)]
(->> (rx/of #(dissoc % :message))
(rx/delay default-animation-timeout)
(rx/take-until stopper))))))
(defn hide-tag (defn hide-tag
[tag] [tag]

View File

@@ -17,33 +17,38 @@
(mf/defc notifications-hub (mf/defc notifications-hub
[] []
(let [message (mf/deref refs/message) (let [message (mf/deref refs/message)
on-close (mf/use-fn #(st/emit! dmsg/hide))
on-close #(st/emit! dmsg/hide) context? (and (nil? (:timeout message))
(nil? (:actions message)))
toast-message {:type (or (:type message) :info) inline? (or (= :inline (:notification-type message))
:links (:links message) (= :floating (:position message)))
:on-close on-close toast? (or (= :toast (:notification-type message))
:content (:content message)} (some? (:timeout message)))]
inline-message {:actions (:actions message)
:links (:links message)
:content (:content message)}
context-message {:type (or (:type message) :info)
:links (:links message)
:content (:content message)}
is-context-msg (and (nil? (:timeout message)) (nil? (:actions message)))
is-toast-msg (or (= :toast (:notification-type message)) (some? (:timeout message)))
is-inline-msg (or (= :inline (:notification-type message)) (and (some? (:position message)) (= :floating (:position message))))]
(when message (when message
(cond (cond
is-toast-msg toast?
[:& toast-notification toast-message] [:& toast-notification
is-inline-msg {:type (or (:type message) :info)
[:& inline-notification inline-message] :links (:links message)
is-context-msg :on-close on-close
[:& context-notification context-message] :content (:content message)}]
inline?
[:& inline-notification
{:actions (:actions message)
:links (:links message)
:content (:content message)}]
context?
[:& context-notification
{:type (or (:type message) :info)
:links (:links message)
:content (:content message)}]
:else :else
[:& toast-notification toast-message])))) [:& toast-notification
{:type (or (:type message) :info)
:links (:links message)
:on-close on-close
:content (:content message)}]))))

View File

@@ -38,12 +38,10 @@
neutral-icon)) neutral-icon))
(mf/defc toast-notification (mf/defc toast-notification
"These are ephemeral elements that disappear when "These are ephemeral elements that disappear when the close button
the close button is pressed, is pressed, the page is refreshed, the page is navigated to another
the page is refreshed, page or after 7 seconds, which is enough time to be read, except for
the page is navigated to another page or error messages that require user interaction."
after 7 seconds, which is enough time to be read,
except for error messages that require user interaction."
{::mf/props :obj} {::mf/props :obj}
[{:keys [type content on-close links] :as props}] [{:keys [type content on-close links] :as props}]

View File

@@ -2209,6 +2209,10 @@ msgstr "Update a component in a shared library"
msgid "notifications.by-code.upgrade-version" msgid "notifications.by-code.upgrade-version"
msgstr "A new version is available, please refresh the page" msgstr "A new version is available, please refresh the page"
#: src/app/main/data/common.cljs
msgid "notifications.by-code.maintenance"
msgstr "Maintenance break: we will be down for a short maintenance within 5 minutes."
#: src/app/main/ui/dashboard/team.cljs #: src/app/main/ui/dashboard/team.cljs
msgid "notifications.invitation-email-sent" msgid "notifications.invitation-email-sent"
msgstr "Invitation sent successfully" msgstr "Invitation sent successfully"

View File

@@ -2285,6 +2285,10 @@ msgstr "Actualizar un componente en biblioteca"
msgid "notifications.by-code.upgrade-version" msgid "notifications.by-code.upgrade-version"
msgstr "Una nueva versión está disponible, por favor actualiza la página" msgstr "Una nueva versión está disponible, por favor actualiza la página"
#: src/app/main/data/common.cljs
msgid "notifications.by-code.maintenance"
msgstr "Pausa de mantenimiento: en los próximos 5 minutos estaremos fuera de servicio por un breve mantenimiento."
#: src/app/main/ui/dashboard/team.cljs #: src/app/main/ui/dashboard/team.cljs
msgid "notifications.invitation-email-sent" msgid "notifications.invitation-email-sent"
msgstr "Invitación enviada con éxito" msgstr "Invitación enviada con éxito"