0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-13 16:21:57 -05:00

🎉 Refactor notifications and ask user for updates

This commit is contained in:
Andrés Moya 2020-09-18 14:09:40 +02:00 committed by Andrey Antukh
parent 3b516aa139
commit ae61ce05c9
6 changed files with 356 additions and 180 deletions

View file

@ -2670,6 +2670,30 @@
"es" : "Texto (T)"
}
},
"workspace.updates.there-are-updates" : {
"translations" : {
"en" : "There are updates in shared libraries",
"fr" : "",
"ru" : "",
"es" : "Hay actualizaciones en librerías compartidas"
}
},
"workspace.updates.update" : {
"translations" : {
"en" : "Update",
"fr" : "",
"ru" : "",
"es" : "Actualizar"
}
},
"workspace.updates.dismiss" : {
"translations" : {
"en" : "Dismiss",
"fr" : "",
"ru" : "",
"es" : "Ignorar"
}
},
"workspace.viewport.click-to-close-path" : {
"used-in" : [ "src/app/main/ui/workspace/drawarea.cljs:59" ],
"translations" : {

View file

@ -1014,20 +1014,67 @@ input[type=range]:focus::-ms-fill-upper {
// Messages
// Banner top
.banner {
top: 0;
left: 0px;
width: 100%;
height: 40px;
z-index: 13;
position: relative;
display: flex;
justify-content: center;
align-items: center;
&.error {
background-color: $color-danger;
}
.btn-close {
&.success {
background-color: $color-success;
}
&.warning {
background-color: $color-warning;
}
&.info {
background-color: $color-info;
}
&.hide {
@include animation(0, .6s, fadeOutUp);
}
& .icon {
display: flex;
svg {
fill: $color-white;
height: 20px;
width: 20px;
}
}
& .content {
&.bottom-actions {
flex-direction: column;
& .actions {
margin-top: $medium;
display: flex;
justify-content: flex-start;
}
}
&.inline-actions {
flex-direction: row;
align-items: center;
justify-content: space-between;
& .actions {
display: flex;
justify-content: flex-start;
.btn-secondary {
margin-left: $medium;
}
}
}
}
& .btn-close {
position: absolute;
right: 0px;
top: 0px;
@ -1051,164 +1098,223 @@ input[type=range]:focus::-ms-fill-upper {
opacity: .8;
}
}
}
.content {
align-items: center;
color: $color-white;
.banner.fixed {
position: fixed;
top: 0;
left: 0px;
width: 100%;
height: 40px;
z-index: 13;
display: flex;
justify-content: center;
align-items: center;
& .wrapper {
display: flex;
justify-content: center;
align-items: center;
max-width: 60%;
.icon {
display: flex;
& .icon {
margin-right: $medium;
svg {
fill: $color-white;
height: 20px;
width: 20px;
}
}
span {
& .content {
color: $color-white;
display: flex;
align-items: center;
justify-content: center;
font-size: $fs15;
}
}
&.fixed {
position: fixed;
}
&.error {
background-color: $color-danger;
}
&.success {
background-color: $color-success;
}
&.warning {
background-color: $color-warning;
}
&.info {
background-color: $color-info;
}
&.quick {
.btn-close {
display: none;
}
}
&.hide {
@include animation(0, .6s, fadeOutUp);
}
}
.inline-banner {
display: flex;
margin-bottom: $big;
.banner.floating,
.banner.inline {
min-height: 40px;
width: 100%;
.icon {
& .wrapper {
display: flex;
flex-shrink: 0;
justify-content: center;
margin-right: $small;
padding: $small;
width: 40px;
svg {
fill: $color-white;
height: 20px;
width: 20px;
& .icon {
padding: $small;
width: 40px;
}
}
.content {
display: flex;
flex-direction: column;
}
.main {
display: flex;
}
.extra {
display: flex;
justify-content: flex-end;
padding: $small;
> div:not(:last-child) {
margin-right: $small;
& .content {
color: $color-black;
display: flex;
font-size: $fs14;
padding: $small;
width: 100%;
}
}
.text {
display: flex;
font-size: $fs14;
color: $color-black;
padding: $small;
}
&.error {
background-color: lighten($color-danger,30%);
.icon {
background-color: $color-danger;
& .content {
background-color: lighten($color-danger,30%);
}
}
&.success {
background-color: lighten($color-success,30%);
.icon {
background-color: $color-success;
& .content {
background-color: lighten($color-success,30%);
}
}
&.warning {
background-color: lighten($color-warning,30%);
.icon {
background-color: $color-warning;
& .content {
background-color: lighten($color-warning,30%);
}
}
&.info {
background-color: lighten($color-info,30%);
.icon {
background-color: $color-info;
}
}
.btn-close {
width: 40px;
height: 40px;
flex-shrink: 0;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
opacity: .35;
svg {
fill: $color-black;
height: 18px;
width: 18px;
transform: rotate(45deg);
}
&:hover {
opacity: .8;
}
}
&.quick {
.btn-close {
display: none;
& .content {
background-color: lighten($color-info,30%);
}
}
}
.banner.floating {
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.18);
position: absolute;
top: 70px;
left: 0;
right: 0;
width: 35rem;
margin-left: auto;
margin-right: auto;
z-index: 20;
&.error {
border: 1px solid $color-danger;
}
&.success {
border: 1px solid $color-success;
}
&.warning {
border: 1px solid $color-warning;
}
&.info {
border: 1px solid $color-info;
}
}
.banner.inline {
width: 100%;
margin-bottom: $big;
}
// .inline-banner {
// // display: flex;
// // margin-bottom: $big;
// // min-height: 40px;
// // width: 100%;
//
// // .icon {
// // display: flex;
// // flex-shrink: 0;
// // justify-content: center;
// // margin-right: $small;
// // padding: $small;
// // width: 40px;
// //
// // svg {
// // fill: $color-white;
// // height: 20px;
// // width: 20px;
// // }
// // }
//
// .content {
// display: flex;
// flex-direction: column;
// }
//
// .main {
// display: flex;
// }
//
// .extra {
// display: flex;
// justify-content: flex-end;
// padding: $small;
//
// > div:not(:last-child) {
// margin-right: $small;
// }
// }
//
// .text {
// display: flex;
// font-size: $fs14;
// color: $color-black;
// padding: $small;
// }
//
// &.error {
// background-color: lighten($color-danger,30%);
// .icon {
// background-color: $color-danger;
// }
// }
//
// &.success {
// background-color: lighten($color-success,30%);
// .icon {
// background-color: $color-success;
// }
// }
//
// &.warning {
// background-color: lighten($color-warning,30%);
// .icon {
// background-color: $color-warning;
// }
// }
//
// &.info {
// background-color: lighten($color-info,30%);
// .icon {
// background-color: $color-info;
// }
// }
//
// .btn-close {
// width: 40px;
// height: 40px;
// flex-shrink: 0;
// display: flex;
// justify-content: center;
// align-items: center;
// cursor: pointer;
// opacity: .35;
//
// svg {
// fill: $color-black;
// height: 18px;
// width: 18px;
// transform: rotate(45deg);
// }
//
// &:hover {
// opacity: .8;
// }
// }
//
// &.quick {
// .btn-close {
// display: none;
// }
// }
// }
.close-bezier {
fill: $color-danger;
stroke: $color-danger-dark;

View file

@ -23,6 +23,15 @@
(def +animation-timeout+ 600)
(s/def ::message-type #{:success :error :info :warning})
(s/def ::message-position #{:fixed :floating :inline})
(s/def ::message-status #{:visible :hide})
(s/def ::message-controls #{:none :close :inline-actions :bottom-actions})
(s/def ::label string?)
(s/def ::callback fn?)
(s/def ::message-action (s/keys :req-un [::label ::callback]))
(s/def ::message-actions (s/nilable (s/coll-of ::message-action :kind vector?)))
(defn show
[data]
(ptk/reify ::show
@ -79,3 +88,11 @@
(show {:content content
:type :warning
:timeout timeout})))
(defn info-dialog
[content controls actions]
(show {:content content
:type :info
:controls controls
:actions actions}))

View file

@ -13,6 +13,7 @@
[app.common.geom.point :as gpt]
[app.common.pages :as cp]
[app.common.spec :as us]
[app.main.data.messages :as dm]
[app.main.data.workspace.common :as dwc]
[app.main.data.workspace.persistence :as dwp]
[app.main.data.workspace.libraries :as dwl]
@ -23,6 +24,7 @@
[app.util.time :as dt]
[app.util.transit :as t]
[app.util.websockets :as ws]
[app.util.i18n :as i18n :refer [tr]]
[beicon.core :as rx]
[cljs.spec.alpha :as s]
[clojure.set :as set]
@ -211,6 +213,16 @@
ptk/WatchEvent
(watch [_ state stream]
(when (contains? (:workspace-libraries state) file-id)
(rx/of (dwl/ext-library-changed file-id changes)
(dwl/sync-file file-id))))))
(let [do-update #(do
(st/emit! (dwl/sync-file file-id))
(st/emit! dm/hide))
do-dismiss #(st/emit! dm/hide)]
(rx/of (dwl/ext-library-changed file-id changes)
(dm/info-dialog
(tr "workspace.updates.there-are-updates")
:inline-actions
[{:label (tr "workspace.updates.update")
:callback do-update}
{:label (tr "workspace.updates.dismiss")
:callback do-dismiss}])))))))

View file

@ -10,6 +10,9 @@
(ns app.main.ui.messages
(:require
[rumext.alpha :as mf]
[clojure.spec.alpha :as s]
[app.common.uuid :as uuid]
[app.common.spec :as us]
[app.main.ui.icons :as i]
[app.main.data.messages :as dm]
[app.main.refs :as refs]
@ -19,57 +22,69 @@
[app.util.i18n :as i18n :refer [t]]
[app.util.timers :as ts]))
(defn- type->icon
[type]
(case type
:warning i/msg-warning
:error i/msg-error
:success i/msg-success
:info i/msg-info
i/msg-error))
(mf/defc notification-item
[{:keys [type status on-close quick? content] :as props}]
(let [klass (dom/classnames
:fixed true
:success (= type :success)
:error (= type :error)
:info (= type :info)
:warning (= type :warning)
:hide (= status :hide)
:quick quick?)]
[:section.banner {:class klass}
[:div.content
[:div.icon (type->icon type)]
[:span content]]
[:div.btn-close {:on-click on-close} i/close]]))
(mf/defc banner
[{:keys [type position status controls content actions on-close] :as props}]
(us/assert ::dm/message-type type)
(us/assert ::dm/message-position position)
(us/assert ::dm/message-status status)
(us/assert ::dm/message-controls controls)
(us/assert ::dm/message-actions actions)
(us/assert (s/nilable ::us/fn) on-close)
[:div.banner {:class (dom/classnames
:warning (= type :warning)
:error (= type :error)
:success (= type :success)
:info (= type :info)
:fixed (= position :fixed)
:floating (= position :floating)
:inline (= position :inline)
:hide (= status :hide))}
[:div.wrapper
[:div.icon (case type
:warning i/msg-warning
:error i/msg-error
:success i/msg-success
:info i/msg-info
i/msg-error)]
[:div.content {:class (dom/classnames
:inline-actions (= controls :inline-actions)
:bottom-actions (= controls :bottom-actions))}
content
(when (or (= controls :bottom-actions) (= controls :inline-actions))
[:div.actions
(for [action actions]
[:div.btn-secondary.btn-small {:key (uuid/next)
:on-click (:callback action)}
(:label action)])])]
(when (= controls :close)
[:div.btn-close {:on-click on-close} i/close])]])
(mf/defc notifications
[]
(let [message (mf/deref refs/message)
on-close #(st/emit! dm/hide)]
(when message
[:& notification-item {:type (:type message)
:quick? (boolean (:timeout message))
:status (:status message)
:content (:content message)
:on-close on-close}])))
[:& banner (assoc message
:position :floating
:controls (if (some? (:controls message))
(:controls message)
(if (some? (:timeout message))
:none
:close))
:on-close on-close)])))
(mf/defc inline-banner
{::mf/wrap [mf/memo]}
[{:keys [type on-close content children] :as props}]
[:div.inline-banner {:class (dom/classnames
:warning (= type :warning)
:error (= type :error)
:success (= type :success)
:info (= type :info)
:quick (not on-close))}
[:div.icon (type->icon type)]
[:div.content
[:div.main
[:span.text content]
[:div.btn-close {:on-click on-close} i/close]]
(when children
[:div.extra
children])]])
[{:keys [type content on-close actions] :as props}]
[:& banner {:type type
:position :inline
:status :visible
:controls (if (some? on-close)
:close
(if (some? actions)
:bottom-actions
:none))
:content content
:on-close on-close
:actions actions}])

View file

@ -75,10 +75,12 @@
(not= (:pending-email prof) (:email prof))
[:& msgs/inline-banner
{:type :info
:content (t locale "settings.change-email-info3" (:pending-email prof))}
[:div.btn-secondary.btn-small
{:on-click #(st/emit! udu/cancel-email-change)}
(t locale "settings.cancel-email-change")]]
:content (t locale "settings.change-email-info3" (:pending-email prof))
:actions [{:label (t locale "settings.cancel-email-change")
:callback #(st/emit! udu/cancel-email-change)}]}]
;; [:div.btn-secondary.btn-small
;; {:on-click #(st/emit! udu/cancel-email-change)}
;; (t locale "settings.cancel-email-change")]]
:else
[:& msgs/inline-banner