Commit 371b5a01 authored by Alex Sarmanov's avatar Alex Sarmanov

sass, client secretary router, local html5 storage state, config

parent 11499771
......@@ -6,3 +6,4 @@ figwheel_server.log
.nrepl-port
pom.xml
pom.xml.asc
.sass-cache
......@@ -3,7 +3,7 @@
Has:
- http-kit server
- compojure router
- compojure server router
- clojurescript app
- rum/react
- websocket endpoint
......@@ -11,6 +11,9 @@ Has:
- figwheel development server
- piggieback cljs repl
- beta reduxDevToolsConnection
- optional html5 local state storage
- secretary client routing
- sass
## Dev setup
......@@ -28,7 +31,7 @@ It is enought for me at the moment, clojure server version on sockets work in pr
#
![some info](http://gitlab.xet.ru:9999/publicpr/clojurescript-redux/raw/master/resources/public/gif/sample.gif)
![some info](https://gitlab.xet.ru/publicpr/clojurescript-redux/raw/master/resources/public/gif/sample.gif)
## Prod setup
......
(defproject cljs-bmain "0.1.0-SNAPSHOT"
:dependencies [
[org.clojure/clojure "1.8.0"]
[org.clojure/clojurescript "1.9.225"]
[rum "0.10.6"]
[http-kit "2.2.0"]
[compojure "1.5.1" :exclusions [commons-codec]]
[com.cognitect/transit-clj "0.8.288"]
[com.cognitect/transit-cljs "0.8.239"]
]
:plugins [
[lein-cljsbuild "1.1.3"]
[lein-figwheel "0.5.4-7"]
]
:aliases { "package" ["do"
(defproject cljs-bmain "0.1.1-SNAPSHOT"
:dependencies [
[org.clojure/clojure "1.8.0"]
[org.clojure/clojurescript "1.9.229"]
[rum "0.10.6"]
[http-kit "2.2.0"]
[compojure "1.5.1" :exclusions [commons-codec]]
[com.cognitect/transit-clj "0.8.288"]
[com.cognitect/transit-cljs "0.8.239"]
[binaryage/devtools "0.8.1"]
[secretary "1.2.3"]
[kibu/pushy "0.3.6"]
[alandipert/storage-atom "2.0.1"] ;; persistant atom storage HTML 5
]
:eval-in-leiningen true
:plugins [[lein-cljsbuild "1.1.4"]
[lein-figwheel "0.5.7"]]
:aliases { "package" ["do"
"cljsbuild" "once" "advanced,"
"uberjar"] }
:aot [ bmain.server ]
:uberjar-name "bmain.jar"
:uberjar-exclusions [#"public/js/out"]
:main bmain.server
:figwheel { :ring-handler "bmain.server/app"
:css-dirs ["resources/public"]
......@@ -34,7 +35,7 @@
:cljsbuild {
:builds [
{ :id "none"
{ :id "none"
:source-paths ["src"]
:figwheel { :on-jsload "bmain.app/refresh" }
:compiler { :optimizations :none
......@@ -44,8 +45,8 @@
:output-dir "resources/public/js/out"
:source-map true
:compiler-stats true } }
{ :id "advanced"
{ :id "advanced"
:source-paths ["src"]
:compiler { :optimizations :advanced
:main bmain.app
......
body{text-align:center}#app{font:14px Helvetica,sans-serif}.cursor{cursor:pointer;cursor:hand}.hover:hover{background-color:gray;color:#fff}button{font-size:18px;padding:15px;margin:5px}.state{color:green}.history-component{border:2px dotted gray}.test{color:#000}
/*# sourceMappingURL=main.css.map */
\ No newline at end of file
{
"version": 3,
"file": "main.css",
"sources": [
"../../sass/main.scss",
"../../sass/variables.scss",
"../../sass/styles.scss",
"../../sass/components/test.scss"
],
"sourcesContent": [
"@import \"variables.scss\";\n@import \"styles.scss\";\n\n@import \"components/test\";\n\n",
"$font: Helvetica, sans-serif;\n$font-size: 14px;\n$color: #000;\n",
"body {\n text-align: center;\n}\n\n#app {\n font: $font-size $font;\n}\n\n.cursor {\n cursor: pointer;\n cursor: hand;\n}\n\n.hover:hover {\n background-color:gray;\n color: #fff;\n}\n\nbutton {\n font-size:18px;\n padding:15px;\n margin:5px;\n}\n\n.state {\n color: green;\n}\n\n.history-component {\n border: 2px dotted gray;\n}\n",
".test {\n color: $color;\n}"
],
"mappings": "AEAA,AAAA,IAAI,AAAC,CACD,UAAU,CAAE,MAAO,CACtB,AAED,AAAA,IAAI,AAAC,CACD,IAAI,CDJI,IAAI,CADT,SAAS,CAAE,UAAU,CCM3B,AAED,AAAA,OAAO,AAAC,CACJ,MAAM,CAAE,OAAQ,CAChB,MAAM,CAAE,IAAK,CAChB,AAED,AAAM,MAAA,AAAA,MAAM,AAAC,CACT,gBAAgB,CAAC,IAAK,CACtB,KAAK,CAAE,IAAK,CACf,AAED,AAAA,MAAM,AAAC,CACH,SAAS,CAAC,IAAK,CACf,OAAO,CAAC,IAAK,CACb,MAAM,CAAC,GAAI,CACd,AAED,AAAA,MAAM,AAAC,CACH,KAAK,CAAE,KAAM,CAChB,AAED,AAAA,kBAAkB,AAAC,CACf,MAAM,CAAE,eAAgB,CAC3B,AC9BD,AAAA,KAAK,AAAC,CACL,KAAK,CFCE,IAAI,CEAX",
"names": []
}
\ No newline at end of file
/*# sourceMappingURL=variables.css.map */
\ No newline at end of file
{
"version": 3,
"file": "variables.css",
"sources": [
"../../sass/variables.scss"
],
"sourcesContent": [
"$font: Helvetica, sans-serif;\n$font-size: 14px;\n$color: #000;\n"
],
"mappings": "",
"names": []
}
\ No newline at end of file
......@@ -4,7 +4,7 @@
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
<title>Skeleton app</title>
<link href="/style.css" rel="stylesheet" type="text/css" />
<link href="/css/main.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="app"></div>
......
.test {
color: $color;
}
\ No newline at end of file
@import "variables.scss";
@import "styles.scss";
@import "components/test";
......@@ -3,7 +3,7 @@ body {
}
#app {
font: 14px Georgia, serif;
font: $font-size $font;
}
.cursor {
......@@ -27,5 +27,5 @@ button {
}
.history-component {
border: 1px dotted gray;
}
\ No newline at end of file
border: 2px dotted gray;
}
$font: Helvetica, sans-serif;
$font-size: 14px;
$color: #000;
#!/bin/sh
node-sass resources/sass/main.scss --recursive --source-map true --source-map-contents --output-style compressed --output resources/public/css
\ No newline at end of file
#!/bin/sh
node-sass --watch resources/sass/main.scss --recursive --source-map true --source-map-contents --output-style compressed --output resources/public/css
\ No newline at end of file
(ns bmain.app
(:require
[bmain.config :as config]
[devtools.core :as devtools]
[rum.core :as rum]
[bmain.routes :as routes]
[redux.core :as r]
[utils.sys :as sys]
[cognitect.transit :as t]
[clojure.data :as cldata]))
(enable-console-print!)
[clojure.data :as cldata]
[components.state-history :as state-history]))
(defn dev-setup []
(when config/debug?
(enable-console-print!)
(println "dev mode")
(devtools/install!)))
(defn read-transit-str [s]
(t/read (t/reader :json) s))
......@@ -34,35 +43,9 @@
(when (== 1 (.-readyState socket)) ;; WS_OPEN
(.send socket (write-transit-str message))))
(rum/defc state-history < rum/reactive []
[:div.history-component
"state-history component"
(for [x (sys/indexed-vector @r/!actions-history)]
(let [index (nth x 0)]
[:div.cursor.hover {:key (str index)}
(str index " -> action -> ")
(str (nth x 1) " -> diff -> ")
(str (sys/diff-state @r/!state-history index))
]))])
(rum/defc app < rum/reactive []
"Root rum/react component"
(let [state (rum/react r/!state)]
[:div "[" (:count state) "] " (pr-str (:message state))]
[:div
[:div (state-history)]
[:pre.state "current state: " (str @r/!state)]
;; [:pre "actions: " (str @r/!actions-history)]
;; [:pre "state-history: " (str @r/!state-history)]
[:button {:on-click #(r/dispatch! {:type :some-greeting :name "YourName"})} "Hello"]
[:button {:on-click #(r/dispatch! {:type :math-increment :num 1})} "+1"]
[:button {:on-click #(r/dispatch! {:type :math-increment :num 10})} "+10"]
[:button {:on-click #(r/dispatch! {:type :math-decrement :num 1})} "-1"]
[:button {:on-click #(r/dispatch! {:type :math-decrement :num 10})} "-10"]
[:button {:on-click #(r/dispatch! {:type :HelloWorld})} "Error"]]
))
(defn ^:export refresh []
"Remount rum/react application to root node"
(send! "refreshed")
(rum/mount (app) (js/document.querySelector "#app")))
(send! "refreshed"))
(dev-setup)
(routes/app-routes)
;; secretary dispatch an url action based on url params, see routes.cljs
(ns bmain.config)
(def debug?
^boolean js/goog.DEBUG)
(def html5-storage? true)
(ns bmain.routes
(:import goog.History)
(:require [secretary.core :as secretary :include-macros true :refer-macros [defroute]]
[pushy.core :as pushy]
[goog.events :as events]
[goog.history.EventType :as EventType]
[redux.core :as r]
[rum.core :as rum]
[containers.index-page :as index-page]
[containers.about-page :as about-page]))
(defmulti page-container identity)
(defmethod page-container :index [] index-page/index-page)
(defmethod page-container :about [] about-page/about-page)
(defmethod page-container :default [] [:div "route not found"])
(defn mount [container & [node]]
(let [point-node (or node "#app")]
(rum/mount (container) (js/document.querySelector point-node))))
(defn hook-browser-navigation! []
(doto (History.)
(events/listen
EventType/NAVIGATE
(fn [event]
(secretary/dispatch! (.-token event))))
(.setEnabled true)))
(def history (pushy/pushy secretary/dispatch!
(fn [x] (when (secretary/locate-route x) x))))
(add-watch r/!state
:url
(fn [k r old-state new-state]
(def new-uri (:uri (:url new-state)))
(pushy/set-token! history (str "/#" new-uri))
(mount (page-container (:page (:url new-state))))))
;; Start event listeners
(pushy/start! history)
;; Maybe i can make a universal macro route later
(defn app-routes []
(secretary/set-config! :prefix "#")
(defroute about-page-route "/about*" []
(mount about-page/about-page))
;; define routes here
(defroute index-page-route "/*" []
(mount index-page/index-page))
;; Catch all
;; (defroute "*" []
;; (set-html! application "<h1>LOL! YOU LOST!</h1>"))
(hook-browser-navigation!))
......@@ -14,7 +14,6 @@
(t/reader :json)
(t/read)))
(defn write-transit-str [o]
(let [os (java.io.ByteArrayOutputStream.)]
(t/write (t/writer os :json) o)
......@@ -34,10 +33,9 @@
(let [message (read-transit-str payload)]
(println "Recieved:" message)
(httpkit/send! chan (write-transit-str ["pong" message])))))))
(compojure/GET "/" [] (response/resource-response "public/index.html"))
(compojure/GET "/*" [] (response/resource-response "public/index.html"))
(route/resources "/" {:root "public"}))
(defn -main [& args]
(println "Starting server at port 8080")
(httpkit/run-server #'app {:port 8080}))
(ns components.state-history
(:require
[rum.core :as rum]
[redux.core :as r]
[utils.sys :as sys]))
(rum/defc state-history < rum/reactive []
[:div.history-component
"state-history component"
(for [x (sys/indexed-vector @r/!actions-history)]
(let [index (nth x 0)]
[:div.cursor.hover {:key (str index)}
(str index " -> action -> ")
(str (nth x 1) " -> diff -> ")
(str (sys/diff-state @r/!state-history index))
]))])
(ns containers.about-page
(:require
[rum.core :as rum]
[redux.core :as r]
[components.state-history :as state-history]))
(rum/defc about-page < rum/reactive []
"Root rum/react component"
(let [state (rum/react r/!state)]
[:div
[:button {:on-click #(r/dispatch! {:type :mount-page :page :index :uri "/?zzz=aaa"})} "To home page"]
"About bmain"
[:div (state-history/state-history)]
[:pre.state "current state: " (str @r/!state)]]))
(ns containers.index-page
(:require
[rum.core :as rum]
[redux.core :as r]
[utils.sys :as sys]
[clojure.data :as cldata]
[components.state-history :as state-history]
[secretary.core :as secretary]))
(rum/defc index-page < rum/reactive []
"Root rum/react component"
(let [state (rum/react r/!state)]
;; (println sys/get-query-params)
[:div "[" (:count state) "] " (pr-str (:message state))]
[:div
[:button {:on-click #(r/dispatch! {:type :mount-page :page :about :uri "/about"})} "To About page"]
[:div (state-history/state-history)]
[:pre.state "current state: " (str @r/!state)]
;; [:pre "actions: " (str @r/!actions-history)]
;; [:pre "state-history: " (str @r/!state-history)]
[:button {:on-click #(r/dispatch! {:type :some-greeting :name "YourName"})} "Hello"]
[:button {:on-click #(r/dispatch! {:type :math-increment :num 1})} "+1"]
[:button {:on-click #(r/dispatch! {:type :math-increment :num 10})} "+10"]
[:button {:on-click #(r/dispatch! {:type :math-decrement :num 1})} "-1"]
[:button {:on-click #(r/dispatch! {:type :math-decrement :num 10})} "-10"]
[:button {:on-click #(r/dispatch! {:type :HelloWorld})} "Error"]]))
(ns reducers.socket
(:require [redux.reducer :refer [Action]]))
(defmethod Action :socket-on-message [answer]
(assoc-in state [:socket :message] (:message answer)))
;; (defmethod Action :socket-on-message [answer]
;; (assoc-in state [:socket :message] (:message answer)))
(ns reducers.url
(:require [redux.reducer :refer [Action]]
[rum.core :as rum]
))
(defmethod Action :mount-page [{:keys [page uri]} state]
(let [url-state {:page page :uri uri}]
(assoc-in state [:url] url-state)))
(ns redux.core
(:require [rum.core :as r]
[redux.reducer :refer [Action]]
[bmain.config :as config]
[alandipert.storage-atom :refer [local-storage]] ;; HTML 5 atom storage
[reducers.socket]
[reducers.math]
[reducers.text]
))
[reducers.url]))
(def dev-tools js/devToolsExtension)
(enable-console-print!)
(def initial-state {:math {:counter 0}})
(def initial-state {:math {:counter 0} :url {:page :index :uri "/"}})
(defonce !actions-history (atom []))
(defonce !state-history (atom []))
(defonce !state (atom initial-state))
(defn dispatch! [action]
"Save actions to actions history"
(swap! !actions-history conj action))
;; restore state from html5 storage
(if config/html5-storage?
(do
(if (.getItem js/localStorage ":!state")
(defonce !state (atom initial-state))
(def !state (local-storage (atom {}) :!state))))
(if (not= "" (.-hash (.-location js/window)))
(set! (.-location js/window) "/")
(defonce !state (atom initial-state))))
(add-watch !actions-history :reduce
(fn [_ _ _ new-state]
"Call main reducer thru defmulti"
......
(ns utils.sys
(:require [clojure.data :as cldata]))
(:require [redux.core :as r]
[clojure.data :as cldata]))
(defn indexed-vector [vec]
"Return [a b] vector with indexes [[0 a] [1 b] ...]"
......@@ -14,3 +15,14 @@
(first (cldata/diff
(last (nth indexed-state index1))
(last (nth indexed-state index2)))))))
(defn query-to-map [req]
"Convert ?xxx=zzz&aaa=bbb to map {:xxx zzz :aaa bbb}"
(into {} (for [[_ k v] (re-seq #"([^&=]+)=([^&]+)" req)]
[(keyword k) v])))
(defn get-query-params []
"Get query params from state"
(let [query-params (:uri (:url r/!state))]
(js/console.warn query-params)
(query-to-map query-params)))
lein ancient upgrade :check-clojure :interactive
\ No newline at end of file
lein ancient upgrade :all :check-clojure :interactive
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment