diff --git a/src/uxbox/data/auth.cljs b/src/uxbox/data/auth.cljs index db85c33523..47079e3cc3 100644 --- a/src/uxbox/data/auth.cljs +++ b/src/uxbox/data/auth.cljs @@ -15,9 +15,9 @@ [uxbox.state :as st] [uxbox.schema :as sc] [uxbox.locales :refer (tr)] - [uxbox.data.projects :as dp] + [uxbox.data.projects :as udp] [uxbox.data.users :as udu] - [uxbox.ui.messages :as uum])) + [uxbox.data.messages :as udm])) ;; --- Logged In diff --git a/src/uxbox/data/forms.cljs b/src/uxbox/data/forms.cljs index c4826999d9..3b643e61c1 100644 --- a/src/uxbox/data/forms.cljs +++ b/src/uxbox/data/forms.cljs @@ -12,8 +12,7 @@ [uxbox.rstore :as rs] [uxbox.state :as st] [uxbox.schema :as sc] - [uxbox.locales :refer (tr)] - [uxbox.ui.messages :as uum])) + [uxbox.locales :refer (tr)])) ;; --- Assign Errors diff --git a/src/uxbox/data/history.cljs b/src/uxbox/data/history.cljs index 37df2a60b3..405b35d74f 100644 --- a/src/uxbox/data/history.cljs +++ b/src/uxbox/data/history.cljs @@ -17,7 +17,6 @@ [uxbox.data.pages :as udp] [uxbox.state :as st] [uxbox.state.project :as stpr] - [uxbox.ui.messages :as uum] [uxbox.util.datetime :as dt] [uxbox.util.data :refer (without-keys replace-by-id diff --git a/src/uxbox/data/messages.cljs b/src/uxbox/data/messages.cljs new file mode 100644 index 0000000000..e8c9201a23 --- /dev/null +++ b/src/uxbox/data/messages.cljs @@ -0,0 +1,112 @@ +;; 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) 2016 Andrey Antukh + +(ns uxbox.data.messages + (:require [cuerdas.core :as str] + [promesa.core :as p] + [beicon.core :as rx] + [lentes.core :as l] + [uxbox.rstore :as rs])) + +;; --- Constants + +(def ^:const +animation-timeout+ 600) + +;; --- Message Event + +(declare hide-message) +(declare show-message?) + +(defrecord ShowMessage [data] + rs/UpdateEvent + (-apply-update [_ state] + (let [message (assoc data :state :visible)] + (assoc state :message message))) + + rs/WatchEvent + (-apply-watch [_ state s] + (let [stoper (->> (rx/filter show-message? s) + (rx/take 1))] + (->> (rx/of (hide-message)) + (rx/delay (:timeout data)) + (rx/take-until stoper))))) + +(defn show-message + [message] + (ShowMessage. message)) + +(defn show-message? + [v] + (instance? ShowMessage v)) + +(defn show-error + [message & {:keys [timeout] :or {timeout 3000}}] + (show-message {:content message + :type :error + :timeout timeout})) + +(defn show-info + [message & {:keys [timeout] :or {timeout 3000}}] + (show-message {:content message + :type :info + :timeout timeout})) + +(defn show-dialog + [message & {:keys [on-accept on-cancel]}] + (show-message {:content message + :on-accept on-accept + :on-cancel on-cancel + :timeout js/Number.MAX_SAFE_INTEGER + :type :dialog})) + +;; --- Hide Message + +(defrecord HideMessage [^:mutable canceled?] + rs/UpdateEvent + (-apply-update [_ state] + (update state :message + (fn [v] + (if (nil? v) + (do (set! canceled? true) nil) + (assoc v :state :hide))))) + + rs/WatchEvent + (-apply-watch [_ state s] + (println "HideMessage.-apply-watch" canceled?) + (if canceled? + (rx/empty) + (->> (rx/of #(dissoc state :message)) + (rx/delay +animation-timeout+))))) + +(defn hide-message + [] + (HideMessage. false)) + +;; --- Direct Call Api + +(defn error! + [& args] + (rs/emit! (apply show-error args))) + +(defn info! + [& args] + (rs/emit! (apply show-info args))) + +(defn dialog! + [& args] + (rs/emit! (apply show-dialog args))) + +(defn close! + [] + (rs/emit! (hide-message))) + +(defn error + [& args] + (rx/of (apply show-error args))) + +(defn info + [& args] + (rx/of (apply show-info args))) diff --git a/src/uxbox/data/pages.cljs b/src/uxbox/data/pages.cljs index 0c566db603..a534121a01 100644 --- a/src/uxbox/data/pages.cljs +++ b/src/uxbox/data/pages.cljs @@ -17,7 +17,6 @@ [uxbox.schema :as sc] [uxbox.state :as st] [uxbox.state.project :as stpr] - [uxbox.ui.messages :as uum] [uxbox.util.datetime :as dt] [uxbox.util.data :refer (without-keys replace-by-id)])) @@ -76,7 +75,7 @@ (-> (sc/validate! data create-page-schema) (map->CreatePage))) -;; --- Sync Page +;; --- Page Synced (defrecord PageSynced [page] rs/UpdateEvent @@ -89,6 +88,8 @@ [event] (instance? PageSynced event)) +;; --- Sync Page + (defrecord SyncPage [id] rs/WatchEvent (-apply-watch [this state s] diff --git a/src/uxbox/data/projects.cljs b/src/uxbox/data/projects.cljs index 243b7b439c..94bde79db5 100644 --- a/src/uxbox/data/projects.cljs +++ b/src/uxbox/data/projects.cljs @@ -14,8 +14,7 @@ [uxbox.locales :refer (tr)] [uxbox.schema :as sc] [uxbox.state.project :as stpr] - [uxbox.data.pages :as udp] - [uxbox.ui.messages :as uum])) + [uxbox.data.pages :as udp])) ;; --- Projects Fetched diff --git a/src/uxbox/data/users.cljs b/src/uxbox/data/users.cljs index 67f2a94369..e323a2ab26 100644 --- a/src/uxbox/data/users.cljs +++ b/src/uxbox/data/users.cljs @@ -12,8 +12,8 @@ [uxbox.state :as st] [uxbox.schema :as sc] [uxbox.locales :refer (tr)] - [uxbox.data.forms :as forms] - [uxbox.ui.messages :as uum])) + [uxbox.data.forms :as udf] + [uxbox.data.messages :as udm])) ;; --- Profile Fetched diff --git a/src/uxbox/rstore.cljs b/src/uxbox/rstore.cljs index 77ccf3baee..076b13c629 100644 --- a/src/uxbox/rstore.cljs +++ b/src/uxbox/rstore.cljs @@ -8,8 +8,7 @@ (ns uxbox.rstore "Reactive storage management architecture helpers." (:require [beicon.core :as rx] - [uxbox.locales :refer (tr)] - [uxbox.ui.messages :as uum])) + [uxbox.locales :refer (tr)])) ;; An abstraction for implement a simple state ;; transition. The `-apply-update` function receives @@ -99,7 +98,7 @@ :else (do - (uum/error (tr "errors.generic")) + (uxbox.data.messages/error! (tr "errors.generic")) (println "Unexpected error: " error) (js/console.log (.-stack error)) (rx/throw error)))) diff --git a/src/uxbox/ui/auth.cljs b/src/uxbox/ui/auth.cljs index e0f102f556..8ca73950de 100644 --- a/src/uxbox/ui/auth.cljs +++ b/src/uxbox/ui/auth.cljs @@ -7,15 +7,14 @@ [uxbox.state :as s] [uxbox.rstore :as rs] [uxbox.data.auth :as da] + [uxbox.data.messages :as udm] [uxbox.util.dom :as dom] [uxbox.ui.icons :as i] [uxbox.ui.messages :as uum] [uxbox.ui.navigation :as nav] [uxbox.ui.mixins :as mx])) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Login -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; --- Login (defn- login-submit [event local] diff --git a/src/uxbox/ui/messages.cljs b/src/uxbox/ui/messages.cljs index c47fe0b20f..b498fa4a5a 100644 --- a/src/uxbox/ui/messages.cljs +++ b/src/uxbox/ui/messages.cljs @@ -1,106 +1,35 @@ (ns uxbox.ui.messages (:require [sablono.core :as html :refer-macros [html]] [rum.core :as rum] - [cuerdas.core :as str] + [promesa.core :as p] + [lentes.core :as l] + [uxbox.state :as st] + [uxbox.data.messages :as udm] [uxbox.ui.icons :as i] [uxbox.ui.mixins :as mx] [uxbox.util.data :refer (classnames)] [uxbox.util.dom :as dom])) -;; --- Constants +;; --- Lenses -(defonce +message+ (atom nil)) - -(def ^:const +animation-timeout+ 600) - -;; --- Helpers - -(defn set-timeout! - [ms callback] - (js/setTimeout callback ms)) - -(defn abort-timeout! - [v] - (when v - (js/clearTimeout v))) - -;; --- Public Api - -(defn- clean-prev-msgstate! - [message] - (let [type (namespace (:type message))] - (case type - "notification" - (do - (abort-timeout! (:tsem-main message)) - (abort-timeout! (:tsem message))) - - "dialog" - (abort-timeout! (:tsem message))))) - -(defn error - ([message] (error message nil)) - ([message {:keys [timeout] :or {timeout 6000}}] - (when-let [prev @+message+] - (clean-prev-msgstate! prev)) - (let [timeout' (+ timeout +animation-timeout+) - tsem-main (set-timeout! timeout' #(reset! +message+ nil)) - tsem (set-timeout! timeout #(swap! +message+ assoc :state :hide))] - (reset! +message+ {:type :notification/error - :state :normal - :tsem-main tsem-main - :tsem tsem - :content message})))) - -(defn info - ([message] (info message nil)) - ([message {:keys [timeout] :or {timeout 6000}}] - (when-let [prev @+message+] - (clean-prev-msgstate! prev)) - (let [timeout' (+ timeout +animation-timeout+) - tsem-main (set-timeout! timeout' #(reset! +message+ nil)) - tsem (set-timeout! timeout #(swap! +message+ assoc :state :hide))] - (reset! +message+ {:type :notification/info - :state :normal - :tsem-main tsem-main - :tsem tsem - :content message})))) - -(defn dialog - [& {:keys [message on-accept on-cancel] - :or {on-cancel (constantly nil)} - :as opts}] - {:pre [(ifn? on-accept) - (string? message)]} - (when-let [prev @+message+] - (clean-prev-msgstate! prev)) - (reset! +message+ {:type :dialog/simple - :state :normal - :content message - :on-accept on-accept - :on-cancel on-cancel})) -(defn close - [] - (when @+message+ - (let [timeout +animation-timeout+ - tsem (set-timeout! timeout #(reset! +message+ nil))] - (swap! +message+ assoc - :state :hide - :tsem tsem)))) +(def ^:const ^:private message-l + (-> (l/key :message) + (l/focus-atom st/state))) ;; --- Notification Component (defn notification-render - [own message] - (let [msgtype (name (:type message)) - classes (classnames :error (= msgtype "error") - :info (= msgtype "info") + [own {:keys [type] :as message}] + (let [classes (classnames :error (= type :error) + :info (= type :info) :hide-message (= (:state message) :hide) - :quick true)] + :quick true) + close #(udm/close!)] (html [:div.message {:class classes} [:div.message-body - [:span.close i/close] + [:span.close {:on-clock close} + i/close] [:span (:content message)]]]))) (def ^:private notification-box @@ -114,21 +43,21 @@ (defn dialog-render [own {:keys [on-accept on-cancel] :as message}] (let [classes (classnames :info true - :hide-message (= (:state message) :hide)) - local (:rum/local own)] + :hide-message (= (:state message) :hide))] (letfn [(accept [event] (dom/prevent-default event) - (close) - (on-accept)) + (on-accept) + (p/schedule 0 udm/close!)) + (cancel [event] (dom/prevent-default event) - (close) (when on-cancel - (on-cancel)))] + (on-cancel)) + (p/schedule 0 udm/close!))] (html [:div.message {:class classes} [:div.message-body - [:span.close i/close] + [:span.close {:on-click cancel} i/close] [:span (:content message)] [:div.message-action [:a.btn-transparent.btn-small @@ -148,11 +77,11 @@ (defn messages-render [own] - (when-let [message (rum/react +message+)] - (case (namespace (:type message)) - "notification" (notification-box message) - "dialog" (dialog-box message) - (throw (ex-info "Invalid message type" message))))) + (let [message (rum/react message-l)] + (case (:type message) + :error (notification-box message) + :dialog (dialog-box message) + nil))) (def ^:const messages (mx/component diff --git a/src/uxbox/ui/workspace/sidebar/history.cljs b/src/uxbox/ui/workspace/sidebar/history.cljs index acb57ae84b..181e96b30d 100644 --- a/src/uxbox/ui/workspace/sidebar/history.cljs +++ b/src/uxbox/ui/workspace/sidebar/history.cljs @@ -18,8 +18,8 @@ [uxbox.data.workspace :as dw] [uxbox.data.pages :as udp] [uxbox.data.history :as udh] + [uxbox.data.messages :as udm] [uxbox.ui.workspace.base :as wb] - [uxbox.ui.messages :as msg] [uxbox.ui.icons :as i] [uxbox.ui.mixins :as mx] [uxbox.util.datetime :as dt] @@ -94,11 +94,11 @@ (let [[page history] (:rum/props own)] (if-let [version (:selected history)] (let [selected (get-in history [:by-version version])] - (msg/dialog - :message (tr "history.alert-message" version) + (udm/dialog! + (tr "history.alert-message" version) :on-accept #(rs/emit! (udh/apply-selected-history (:id page))) :on-cancel #(rs/emit! (udh/discard-selected-history (:id page))))) - (msg/close)) + (udm/close!)) own)) (def history-list