Refactor messages (split ui and business logic).

This commit is contained in:
Andrey Antukh
2016-04-15 23:22:07 +03:00
parent 6571c29c1d
commit 95c4f2fbc1
11 changed files with 156 additions and 119 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 <niwi@niwi.nz>
(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)))

View File

@@ -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]

View File

@@ -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

View File

@@ -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

View File

@@ -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))))

View File

@@ -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]

View File

@@ -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

View File

@@ -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