Merge branch 'feature/36' into develop
26
README.md
|
@ -10,16 +10,12 @@ generated using Luminus version "2.9.11.05"
|
|||
|
||||
## Status
|
||||
|
||||
**Nothing works yet**. This is very early development code, a long way pre-alpha.
|
||||
Very early pre-alpha; user interface mostly works (enough to demonstrate), back end is hardly started.
|
||||
|
||||
## What is it supposed to do?
|
||||
|
||||
To understand what I'm aiming for, read [this essay](http://blog.journeyman.cc/2016/10/preparing-for-next-independence.html). Design documentation, such as there is of it yet, is in the *dummy* sub-directory. Also look at [src/clj/youyesyet/db/schema.clj](https://github.com/simon-brooke/youyesyet/blob/master/src/clj/youyesyet/db/schema.clj).
|
||||
|
||||
### Dummy website
|
||||
|
||||
I've put a [dummy site](http://www.journeyman.cc/~simon/tmp/yyy-dummy/index.html) up for people to play with. Obviously it's intended to work with mobile phones.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You will need [Leiningen][1] 2.0 or above installed. The database required must be [Postgres][2] 9.3 or above.
|
||||
|
@ -49,6 +45,16 @@ You should also read the [User-Oriented Specification](doc/specification/userspe
|
|||
|
||||
## Getting the database up
|
||||
|
||||
You'll need a file *profiles.clj*, with content similar to the following; it's not in the repository because it contains passwords.
|
||||
|
||||
;; WARNING
|
||||
;; The profiles.clj file is used for local environment variables, such as database credentials.
|
||||
;; This file is listed in .gitignore and will be excluded from version control by Git.
|
||||
|
||||
{:profiles/dev {:env {:database-url "jdbc:postgresql://127.0.0.1/youyesyet_dev?user=youyesyet&password=yourverysecurepassword"}}
|
||||
:profiles/test {:env {:database-url "jdbc:postgresql://127.0.0.1/youyesyet_test?user=youyesyet&password=yourverysecurepassword"}}}
|
||||
|
||||
|
||||
Do get the database initialised, run
|
||||
|
||||
createdb youyesyet_dev
|
||||
|
@ -59,9 +65,11 @@ followed by
|
|||
|
||||
**NOTE THAT** in the namespace *youyesyet.db.schema*, there are a series of functions *create-xxx-table!*. These are a snare and a delusion; they are how I originally bootstrapped the database, but are no longer used (and should probably be deleted). The database is now constructed using [migratus](https://github.com/yogthos/migratus).
|
||||
|
||||
## Running
|
||||
## Running in a dev environment
|
||||
|
||||
To start a web server for the application, run:
|
||||
To run in a dev environment, checkout the *develop* branch
|
||||
|
||||
To start a development web server for the application, run:
|
||||
|
||||
lein run
|
||||
|
||||
|
@ -73,6 +81,10 @@ as above; in the other, run
|
|||
|
||||
lein figwheel
|
||||
|
||||
## Running in a production environment
|
||||
|
||||
Doesn't really work yet; if you want to try it, see [Bug #36](https://github.com/simon-brooke/youyesyet/issues/36) and check out the associated feature branch.
|
||||
|
||||
## Working on this project
|
||||
|
||||
You should get the [ZenHub](https://github.com/integrations/zenhub) plug-in for your favourite browser; I use ZenHub for project management, and you (and I) will find collaborating much easier if you do. To join in
|
||||
|
|
12
externs.js
|
@ -1,12 +1,14 @@
|
|||
/* I'm not certain that this file is still needed at all; however, it doesn't seem to be doing any harm. */
|
||||
/* Things which should not get renamed when compiling ClojureScript */
|
||||
|
||||
/* this block relates to the use of Leaflet */
|
||||
var L = {
|
||||
"map": {
|
||||
"setView": function(){}
|
||||
},
|
||||
"tileLayer": {
|
||||
"map": {
|
||||
"setView": function(){},
|
||||
"eg": function(){}
|
||||
},
|
||||
"tileLayer": {
|
||||
"addTo": function(){}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
BIN
favicon.ico
Before Width: | Height: | Size: 13 KiB |
26
project.clj
|
@ -8,6 +8,7 @@
|
|||
[ring/ring-servlet "1.5.1"]
|
||||
[lib-noir "0.9.9" :exclusions [org.clojure/tools.reader]]
|
||||
[clj-oauth "1.5.5"]
|
||||
[cljsjs/react-leaflet "0.12.3-4"]
|
||||
[ch.qos.logback/logback-classic "1.2.2"]
|
||||
[re-frame "0.9.2"]
|
||||
[cljs-ajax "0.5.8"]
|
||||
|
@ -65,12 +66,25 @@
|
|||
|
||||
:hooks [leiningen.less]
|
||||
|
||||
:uberwar
|
||||
{:prep-tasks ["compile" "bower" ["cljsbuild" "once" "min"]]
|
||||
:handler youyesyet.handler/app
|
||||
:init youyesyet.handler/init
|
||||
:destroy youyesyet.handler/destroy
|
||||
:name "youyesyet.war"}
|
||||
:uberwar {:handler youyesyet.handler/app
|
||||
:init youyesyet.handler/init
|
||||
:destroy youyesyet.handler/destroy
|
||||
:name "youyesyet.war"
|
||||
:uberjar {:omit-source true
|
||||
:prep-tasks ["compile" ["bower" "install"] ["cljsbuild" "once" "min"]]
|
||||
:cljsbuild
|
||||
{:builds
|
||||
{:min
|
||||
{:source-paths ["src/cljc" "src/cljs" "env/prod/cljs"]
|
||||
:compiler
|
||||
{:main "youyesyet.core"
|
||||
:optimizations :advanced
|
||||
:pretty-print false
|
||||
:verbose true}}}}
|
||||
:aot :all
|
||||
:uberjar-name "youyesyet.jar"
|
||||
:source-paths ["env/prod/clj"]
|
||||
:resource-paths ["env/prod/resources"]}}
|
||||
|
||||
:clean-targets ^{:protect false}
|
||||
[:target-path [:cljsbuild :builds :app :compiler :output-dir] [:cljsbuild :builds :app :compiler :output-to]]
|
||||
|
|
156
project.new.clj
Normal file
|
@ -0,0 +1,156 @@
|
|||
(defproject youyesyet "0.1.0-SNAPSHOT"
|
||||
|
||||
:description "Canvassing tool for referenda"
|
||||
:url "https://github.com/simon-brooke/youyesyet"
|
||||
|
||||
:dependencies [[bouncer "1.0.1"]
|
||||
[clj-oauth "1.5.4"]
|
||||
[cljs-ajax "0.5.8"]
|
||||
[compojure "1.5.2"]
|
||||
[conman "0.6.3"]
|
||||
[cprop "0.1.10"]
|
||||
[funcool/struct "1.0.0"]
|
||||
[korma "0.4.3"]
|
||||
;; TODO: Latest Luminus no longer includes noir, and I only
|
||||
;; use it in home.clj for routing. Worth looking at how Luminus
|
||||
;; currently does roouting, and perhaps removing this dependency.
|
||||
[lib-noir "0.9.9" :exclusions [org.clojure/tools.reader]]
|
||||
[luminus-migrations "0.3.0"]
|
||||
[luminus-nrepl "0.1.4"]
|
||||
[luminus/ring-ttl-session "0.3.1"]
|
||||
[markdown-clj "0.9.98"]
|
||||
[metosin/muuntaja "0.1.0"]
|
||||
[metosin/ring-http-response "0.8.2"]
|
||||
[mount "0.1.11"]
|
||||
[org.clojure/clojure "1.8.0"]
|
||||
[org.clojure/clojurescript "1.9.495" :scope "provided"]
|
||||
[org.clojure/tools.cli "0.3.5"]
|
||||
[org.clojure/tools.logging "0.3.1"]
|
||||
[org.postgresql/postgresql "42.0.0"]
|
||||
[org.webjars.bower/tether "1.4.0"]
|
||||
[org.webjars/bootstrap "4.0.0-alpha.5"]
|
||||
[org.webjars/font-awesome "4.7.0"]
|
||||
[re-frame "0.9.2"]
|
||||
[reagent "0.6.1"]
|
||||
[reagent-utils "0.2.1"]
|
||||
[ring-webjars "0.1.1"]
|
||||
[ring/ring-core "1.6.0-RC1"]
|
||||
[ring/ring-defaults "0.2.3"]
|
||||
[ring/ring-servlet "1.4.0"]
|
||||
[secretary "1.2.3"]
|
||||
[selmer "1.10.7"]]
|
||||
|
||||
:min-lein-version "2.0.0"
|
||||
|
||||
:license {:name "GNU General Public License v2"
|
||||
:url "http://www.gnu.org/licenses/gpl-2.0.html"}
|
||||
|
||||
:jvm-opts ["-server" "-Dconf=.lein-env"]
|
||||
:source-paths ["src/clj" "src/cljc"]
|
||||
:test-paths ["test/clj"]
|
||||
:resource-paths ["resources" "target/cljsbuild"]
|
||||
:target-path "target/%s/"
|
||||
:main ^:skip-aot youyesyet.core
|
||||
:migratus {:store :database :db ~(get (System/getenv) "DATABASE_URL")}
|
||||
|
||||
:plugins [[lein-cprop "1.0.1"]
|
||||
[migratus-lein "0.4.4"]
|
||||
[lein-cljsbuild "1.1.5"]
|
||||
[lein-immutant "2.1.0"]
|
||||
[lein-kibit "0.1.2"]
|
||||
[lein-uberwar "0.2.0"]
|
||||
[lein-bower "0.5.1"]]
|
||||
|
||||
:bower-dependencies [[leaflet "0.7.3"]]
|
||||
|
||||
:uberwar
|
||||
{:handler youyesyet.handler/app
|
||||
:init youyesyet.handler/init
|
||||
:destroy youyesyet.handler/destroy
|
||||
:name "youyesyet.war"}
|
||||
|
||||
:clean-targets ^{:protect false}
|
||||
[:target-path [:cljsbuild :builds :app :compiler :output-dir] [:cljsbuild :builds :app :compiler :output-to]]
|
||||
|
||||
:figwheel
|
||||
{:http-server-root "public"
|
||||
:nrepl-port 7002
|
||||
:css-dirs ["resources/public/css"]
|
||||
:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}
|
||||
|
||||
:externs ["externs.js"]
|
||||
|
||||
:profiles
|
||||
{:uberjar {:omit-source true
|
||||
:prep-tasks ["compile" ["bower" "install"] ["cljsbuild" "once" "min"]]
|
||||
:cljsbuild
|
||||
{:builds
|
||||
{:min
|
||||
{:source-paths ["src/cljc" "src/cljs" "env/prod/cljs"]
|
||||
:compiler
|
||||
{:output-to "target/cljsbuild/public/js/app.js"
|
||||
:optimizations :advanced
|
||||
:pretty-print false
|
||||
:closure-warnings
|
||||
{:externs-validation :off :non-standard-jsdoc :off}
|
||||
:externs ["react/externs/react.js"]}}}}
|
||||
|
||||
|
||||
:aot :all
|
||||
:uberjar-name "youyesyet.jar"
|
||||
:source-paths ["env/prod/clj"]
|
||||
:resource-paths ["env/prod/resources"]}
|
||||
|
||||
:dev [:project/dev :profiles/dev]
|
||||
:test [:project/dev :project/test :profiles/test]
|
||||
|
||||
:project/dev {:dependencies [[prone "1.1.4"]
|
||||
[ring/ring-mock "0.3.0"]
|
||||
[ring/ring-devel "1.5.1"]
|
||||
[org.webjars/webjars-locator-jboss-vfs "0.1.0"]
|
||||
[luminus-immutant "0.2.3"]
|
||||
[pjstadig/humane-test-output "0.8.1"]
|
||||
[binaryage/devtools "0.9.2"]
|
||||
[com.cemerick/piggieback "0.2.2-SNAPSHOT"]
|
||||
[directory-naming/naming-java "0.8"]
|
||||
[doo "0.1.7"]
|
||||
[figwheel-sidecar "0.5.9"]]
|
||||
:plugins [[com.jakemccrary/lein-test-refresh "0.18.1"]
|
||||
[lein-doo "0.1.7"]
|
||||
[lein-figwheel "0.5.9"]
|
||||
[org.clojure/clojurescript "1.9.495"]]
|
||||
:cljsbuild
|
||||
{:builds
|
||||
{:app
|
||||
{:source-paths ["src/cljs" "src/cljc" "env/dev/cljs"]
|
||||
:compiler
|
||||
{:main "youyesyet.app"
|
||||
:asset-path "/js/out"
|
||||
:output-to "target/cljsbuild/public/js/app.js"
|
||||
:output-dir "target/cljsbuild/public/js/out"
|
||||
:source-map true
|
||||
:optimizations :none
|
||||
:pretty-print true}}}}
|
||||
|
||||
|
||||
|
||||
:doo {:build "test"}
|
||||
:source-paths ["env/dev/clj"]
|
||||
:resource-paths ["env/dev/resources"]
|
||||
:repl-options {:init-ns user}
|
||||
:injections [(require 'pjstadig.humane-test-output)
|
||||
(pjstadig.humane-test-output/activate!)]}
|
||||
:project/test {:resource-paths ["env/test/resources"]
|
||||
:cljsbuild
|
||||
{:builds
|
||||
{:test
|
||||
{:source-paths ["src/cljc" "src/cljs" "test/cljs"]
|
||||
:compiler
|
||||
{:output-to "target/test.js"
|
||||
:main "youyesyet.doo-runner"
|
||||
:optimizations :whitespace
|
||||
:pretty-print true}}}}
|
||||
|
||||
}
|
||||
:profiles/dev {}
|
||||
:profiles/test {}})
|
163
project.old.clj
Normal file
|
@ -0,0 +1,163 @@
|
|||
(defproject youyesyet "0.1.0-SNAPSHOT"
|
||||
|
||||
:description "Canvassing tool for referenda"
|
||||
:url "https://github.com/simon-brooke/youyesyet"
|
||||
|
||||
:dependencies [[org.clojure/clojure "1.8.0"]
|
||||
[org.clojure/clojurescript "1.9.229" :scope "provided"]
|
||||
[ring/ring-servlet "1.5.1"]
|
||||
[lib-noir "0.9.9" :exclusions [org.clojure/tools.reader]]
|
||||
[clj-oauth "1.5.5"]
|
||||
[ch.qos.logback/logback-classic "1.2.2"]
|
||||
[re-frame "0.9.2"]
|
||||
[cljs-ajax "0.5.8"]
|
||||
[secretary "1.2.3"]
|
||||
[reagent-utils "0.2.1"]
|
||||
[reagent "0.6.1"]
|
||||
[korma "0.4.3"]
|
||||
[selmer "1.10.6"]
|
||||
[markdown-clj "0.9.98"]
|
||||
[ring-middleware-format "0.7.2"]
|
||||
[metosin/ring-http-response "0.8.2"]
|
||||
[bouncer "1.0.1"]
|
||||
[org.webjars/bootstrap "4.0.0-alpha.6-1"]
|
||||
[org.webjars/font-awesome "4.7.0"]
|
||||
[org.webjars.bower/tether "1.4.0"]
|
||||
[org.clojure/tools.logging "0.3.1"]
|
||||
[compojure "1.5.2"]
|
||||
[metosin/compojure-api "1.1.10"]
|
||||
[ring-webjars "0.1.1"]
|
||||
[ring/ring-defaults "0.2.3"]
|
||||
[luminus/ring-ttl-session "0.3.1"]
|
||||
[mount "0.1.11"]
|
||||
[cprop "0.1.10"]
|
||||
[org.clojure/tools.cli "0.3.5"]
|
||||
[migratus "0.8.33"]
|
||||
[luminus-nrepl "0.1.4"]
|
||||
[luminus-migrations "0.3.0"]
|
||||
[conman "0.6.3"]
|
||||
[org.postgresql/postgresql "9.4.1212"]
|
||||
]
|
||||
|
||||
:min-lein-version "2.0.0"
|
||||
|
||||
:license {:name "GNU General Public License v2"
|
||||
:url "http://www.gnu.org/licenses/gpl-2.0.html"}
|
||||
|
||||
:jvm-opts ["-server" "-Dconf=.lein-env"]
|
||||
:source-paths ["src/clj" "src/cljc"]
|
||||
:resource-paths ["resources" "target/cljsbuild"]
|
||||
:target-path "target/%s/"
|
||||
:main youyesyet.core
|
||||
:migratus {:store :database :db ~(get (System/getenv) "DATABASE_URL")}
|
||||
|
||||
:plugins [[lein-cprop "1.0.1"]
|
||||
[migratus-lein "0.4.2"]
|
||||
[org.clojars.punkisdead/lein-cucumber "1.0.5"]
|
||||
[lein-cljsbuild "1.1.4"]
|
||||
[lein-uberwar "0.2.0"]
|
||||
[lein-bower "0.5.1"]
|
||||
[lein-less "1.7.5"]]
|
||||
|
||||
:bower-dependencies [[leaflet "0.7.3"]]
|
||||
|
||||
:cucumber-feature-paths ["test/clj/features"]
|
||||
|
||||
:hooks [leiningen.less]
|
||||
|
||||
:uberwar
|
||||
{:handler youyesyet.handler/app
|
||||
:init youyesyet.handler/init
|
||||
:destroy youyesyet.handler/destroy
|
||||
:name "youyesyet.war"}
|
||||
|
||||
:clean-targets ^{:protect false}
|
||||
[:target-path [:cljsbuild :builds :app :compiler :output-dir] [:cljsbuild :builds :app :compiler :output-to]]
|
||||
|
||||
:figwheel
|
||||
{:http-server-root "public"
|
||||
:nrepl-port 7002
|
||||
:css-dirs ["resources/public/css"]
|
||||
:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}
|
||||
|
||||
:externs ["externs.js"]
|
||||
|
||||
:profiles
|
||||
{:uberjar {:omit-source true
|
||||
:prep-tasks ["compile" ["bower" "install"] ["cljsbuild" "once" "min"]]
|
||||
:cljsbuild
|
||||
{:builds
|
||||
{:min
|
||||
{:source-paths ["src/cljc" "src/cljs" "env/prod/cljs"]
|
||||
:compiler
|
||||
{:asset-path "/youyesyet/js/out"
|
||||
:externs "externs.js"
|
||||
:main "youyesyet.core"
|
||||
:optimizations :advanced
|
||||
:output-dir "resources/public/js"
|
||||
:output-to "resources/public/js/app.js"
|
||||
:pretty-print false
|
||||
:verbose true}}}}
|
||||
:aot :all
|
||||
:uberjar-name "youyesyet.jar"
|
||||
:source-paths ["env/prod/clj"]
|
||||
:resource-paths ["env/prod/resources"]}
|
||||
|
||||
:dev [:project/dev :profiles/dev]
|
||||
:test [:project/dev :project/test :profiles/test]
|
||||
|
||||
:project/dev {:dependencies [[prone "1.1.4"]
|
||||
[ring/ring-mock "0.3.0"]
|
||||
[ring/ring-devel "1.5.1"]
|
||||
[luminus-jetty "0.1.4"]
|
||||
[pjstadig/humane-test-output "0.8.1"]
|
||||
[org.clojure/core.cache "0.6.5"]
|
||||
[org.apache.httpcomponents/httpcore "4.4.6"]
|
||||
[clj-webdriver/clj-webdriver "0.7.2"]
|
||||
[org.seleniumhq.selenium/selenium-server "3.3.1" :exclusions [org.seleniumhq.selenium/selenium-support]]
|
||||
[doo "0.1.7"]
|
||||
[binaryage/devtools "0.9.2"]
|
||||
[figwheel-sidecar "0.5.9"]
|
||||
[com.cemerick/piggieback "0.2.2-SNAPSHOT"]
|
||||
[directory-naming/naming-java "0.8"]]
|
||||
:plugins [[com.jakemccrary/lein-test-refresh "0.14.0"]
|
||||
[lein-doo "0.1.7"]
|
||||
[lein-figwheel "0.5.9"]
|
||||
[org.clojure/clojurescript "1.9.229"]]
|
||||
:cljsbuild
|
||||
{:builds
|
||||
{:app
|
||||
{:source-paths ["src/cljs" "src/cljc" "env/dev/cljs"]
|
||||
:compiler
|
||||
{:asset-path "/youyesyet/js/out"
|
||||
:main "youyesyet.app"
|
||||
:externs ["react/externs/react.js" "externs.js"]
|
||||
:output-to "target/cljsbuild/public/js/app.js"
|
||||
:output-dir "target/cljsbuild/public/js/out"
|
||||
:source-map true
|
||||
:optimizations :none
|
||||
:pretty-print true}}}}
|
||||
|
||||
|
||||
|
||||
:doo {:build "test"}
|
||||
:source-paths ["env/dev/clj" "test/clj"]
|
||||
:resource-paths ["env/dev/resources"]
|
||||
:repl-options {:init-ns user}
|
||||
:injections [(require 'pjstadig.humane-test-output)
|
||||
(pjstadig.humane-test-output/activate!)]}
|
||||
:project/test {:resource-paths ["env/dev/resources" "env/test/resources"]
|
||||
:cljsbuild
|
||||
{:builds
|
||||
{:test
|
||||
{:source-paths ["src/cljc" "src/cljs" "test/cljs"]
|
||||
:compiler
|
||||
{:output-to "target/test.js"
|
||||
:externs ["react/externs/react.js" "externs.js"]
|
||||
:main "youyesyet.doo-runner"
|
||||
:optimizations :whitespace
|
||||
:pretty-print true}}}}
|
||||
|
||||
}
|
||||
:profiles/dev {}
|
||||
:profiles/test {}})
|
|
@ -60,13 +60,9 @@ footer {
|
|||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0.25em 0;
|
||||
bottom:0;
|
||||
position:fixed;
|
||||
vertical-align: top;
|
||||
bottom: 0;
|
||||
position: fixed;
|
||||
z-index:150;
|
||||
_position:absolute;
|
||||
_top:expression(eval(document.documentElement.scrollTop+
|
||||
(document.documentElement.clientHeight-this.offsetHeight)));
|
||||
}
|
||||
|
||||
footer div {
|
||||
|
@ -365,8 +361,12 @@ th {
|
|||
/* phones, and, indeed, smaller phones. Adapted to touch; display radically
|
||||
* decluttered */
|
||||
@media all and (max-device-width: 768px) {
|
||||
footer {
|
||||
display: none;
|
||||
button, input, select {
|
||||
background-color: rgb( 50, 109, 177);
|
||||
color: white;
|
||||
font-size: 1.1em;
|
||||
padding: 0.25em 1em;
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
|
||||
h1 {
|
||||
|
@ -375,14 +375,6 @@ th {
|
|||
padding-left: 75px;
|
||||
}
|
||||
|
||||
input, select {
|
||||
background-color: rgb( 50, 109, 177);
|
||||
color: white;
|
||||
font-size: 1.1em;
|
||||
padding: 0.25em 1em;
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 0 B After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.7 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 665 KiB |
Before Width: | Height: | Size: 2.8 KiB |
|
@ -37,16 +37,16 @@
|
|||
<footer>
|
||||
<div id="credits">
|
||||
<div>
|
||||
<img src="img/ric-logo.png" width="24" height="24"/>
|
||||
<img src="img/credits/ric-logo.png" width="24" height="24"/>
|
||||
A project of the
|
||||
<a href="https://radical.scot/">Radical Independence Campaign</a> ||
|
||||
Version {{version}}
|
||||
</div>
|
||||
<div>
|
||||
<img height="16" width="16" alt="Clojure" src="img/luminus-logo.png"/>Built with <a href="http://www.luminusweb.net/">LuminusWeb</a> ||
|
||||
<img height="16" width="16" alt="Clojure" src="img/clojure-icon.gif"/> Powered by <a href="http://clojure.org">Clojure</a> ||
|
||||
<img height="16" width="16" alt="GitHub" src="img/github-logo-transparent.png"/>Find me/fork me on <a href="https://github.com/simon-brooke/smeagol">Github</a> ||
|
||||
<img height="16" width="16" alt="Free Software Foundation" src="img/gnu.small.png"/>Licensed under the <a href="http://www.gnu.org/licenses/gpl-2.0.html">GNU General Public License version 2.0</a>
|
||||
<img height="16" width="16" alt="Clojure" src="img/credits/luminus-logo.png"/>Built with <a href="http://www.luminusweb.net/">LuminusWeb</a> ||
|
||||
<img height="16" width="16" alt="Clojure" src="img/credits/clojure-icon.gif"/> Powered by <a href="http://clojure.org">Clojure</a> ||
|
||||
<img height="16" width="16" alt="GitHub" src="img/credits/github-logo-transparent.png"/>Find me/fork me on <a href="https://github.com/simon-brooke/smeagol">Github</a> ||
|
||||
<img height="16" width="16" alt="Free Software Foundation" src="img/credits/gnu.small.png"/>Licensed under the <a href="http://www.gnu.org/licenses/gpl-2.0.html">GNU General Public License version 2.0</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
|
115
src/cljc/youyesyet/outqueue.cljc
Normal file
|
@ -0,0 +1,115 @@
|
|||
(ns youyesyet.outqueue
|
||||
(:require
|
||||
#?(:clj [clojure.core]
|
||||
:cljs [reagent.core :refer [atom]])))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;
|
||||
;;;; youyesyet.outqueue: queue of messages waiting to be sent to the server.
|
||||
;;;;
|
||||
;;;; This program is free software; you can redistribute it and/or
|
||||
;;;; modify it under the terms of the GNU General Public License
|
||||
;;;; as published by the Free Software Foundation; either version 2
|
||||
;;;; of the License, or (at your option) any later version.
|
||||
;;;;
|
||||
;;;; This program is distributed in the hope that it will be useful,
|
||||
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;;; GNU General Public License for more details.
|
||||
;;;;
|
||||
;;;; You should have received a copy of the GNU General Public License
|
||||
;;;; along with this program; if not, write to the Free Software
|
||||
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
;;;; USA.
|
||||
;;;;
|
||||
;;;; Copyright (C) 2016 Simon Brooke for Radical Independence Campaign
|
||||
;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;;; The items are (obviously) the actual items in the queue;
|
||||
;;; the queue is locked if an attempt is currently being made to transmit
|
||||
;;; an item.
|
||||
|
||||
(defn new-queue
|
||||
"Create a new queue"
|
||||
([]
|
||||
(new-queue '()))
|
||||
([items]
|
||||
(atom {:locked false
|
||||
:items (if
|
||||
(seq? items)
|
||||
(reverse items)
|
||||
(list items))})))
|
||||
|
||||
(defn add!
|
||||
"Add this item to the queue."
|
||||
[q item]
|
||||
(swap! q
|
||||
(fn [a]
|
||||
(assoc a :items
|
||||
(cons item (:items a))))))
|
||||
|
||||
(defn q?
|
||||
[x]
|
||||
(try
|
||||
(let [q (deref x)
|
||||
locked (:locked q)]
|
||||
(and
|
||||
(seq? (:items q))
|
||||
(or (true? locked) (false? locked))))
|
||||
(catch #?(:clj Exception :cljs js/Object) any
|
||||
#?(:clj (print (.getMessage any))
|
||||
:cljs (js/console.log (str any))))))
|
||||
|
||||
(defn peek
|
||||
"Look at the next item which could be removed from the queue."
|
||||
[q]
|
||||
(last (:items (deref q))))
|
||||
|
||||
(defn locked?
|
||||
[q]
|
||||
(:locked (deref q)))
|
||||
|
||||
(defn unlock!
|
||||
([q ]
|
||||
(unlock! q true))
|
||||
([q value]
|
||||
(swap! q (fn [a] (assoc a :locked (not (true? value)))))))
|
||||
|
||||
(defn lock!
|
||||
[q]
|
||||
(unlock! q false))
|
||||
|
||||
|
||||
(defn count
|
||||
"Return the count of items currently in the queue."
|
||||
[q]
|
||||
(count (deref q)))
|
||||
|
||||
(defn take!
|
||||
"Return the first item from the queue, rebind the queue to the remaining
|
||||
items. If the queue is empty return nil."
|
||||
[q]
|
||||
(swap! q (fn [a]
|
||||
(let [items (reverse (:items a))
|
||||
item (first items)
|
||||
new-queue (reverse (rest items))]
|
||||
(assoc (assoc a :items new-queue) :v item))))
|
||||
(:v (deref q)))
|
||||
|
||||
(defn maybe-process-next
|
||||
"Apply this process, assumed to be a function of one argument, to the next
|
||||
item in the queue, if the queue is not currently locked; return the value
|
||||
returned by process."
|
||||
[q process]
|
||||
(if (and (q? q)(not (locked? q)))
|
||||
(try
|
||||
(lock! q)
|
||||
(let [v (apply process (list (peek q)))]
|
||||
(take! q)
|
||||
v)
|
||||
(catch #?(:clj Exception :cljs js/Object) any
|
||||
#?(:clj (print (.getMessage any))
|
||||
:cljs (js/console.log (str any))))
|
||||
(finally (unlock! q)))
|
||||
))
|
|
@ -1,11 +1,12 @@
|
|||
(ns youyesyet.core
|
||||
(:require [reagent.core :as r]
|
||||
[re-frame.core :as rf]
|
||||
[secretary.core :as secretary]
|
||||
(:require cljsjs.react-leaflet
|
||||
[ajax.core :refer [GET POST]]
|
||||
[goog.events :as events]
|
||||
[goog.history.EventType :as HistoryEventType]
|
||||
[markdown.core :refer [md->html]]
|
||||
[ajax.core :refer [GET POST]]
|
||||
[reagent.core :as r]
|
||||
[re-frame.core :as rf]
|
||||
[secretary.core :as secretary]
|
||||
[youyesyet.ajax :refer [load-interceptors!]]
|
||||
[youyesyet.handlers]
|
||||
[youyesyet.subscriptions]
|
||||
|
@ -71,16 +72,26 @@
|
|||
:map #'map-page
|
||||
})
|
||||
|
||||
|
||||
(defn page
|
||||
"Render the single page of the app, taking the current panel from
|
||||
the :page key in the state map."
|
||||
[]
|
||||
[:div
|
||||
[:header
|
||||
[ui/navbar]]
|
||||
(let [content (pages @(rf/subscribe [:page]))]
|
||||
(let [content (pages @(rf/subscribe [:page]))
|
||||
error @(rf/subscribe [:error])
|
||||
feedback @(rf/subscribe [:feedback])
|
||||
outqueue @(rf/subscribe [:outqueue])]
|
||||
[:div
|
||||
[:header
|
||||
[ui/navbar]]
|
||||
(if content [content]
|
||||
[:div.error (str "No content in page " :page)]))])
|
||||
[:div.error (str "No content in page " :page)])
|
||||
[:footer
|
||||
[:div.error {:style [:display (if error "block" "none")]} (str error)]
|
||||
[:div.feedback {:style [:display (if feedback :block :none)]} (str feedback)]
|
||||
[:div.queue (if
|
||||
(nil? outqueue) ""
|
||||
(str (count outqueue) " items queued to send"))]]]))
|
||||
|
||||
;; -------------------------
|
||||
;; Routes
|
||||
|
@ -108,7 +119,7 @@
|
|||
(rf/dispatch [:set-elector-and-page {:elector-id elector-id :page :issues}]))
|
||||
|
||||
(secretary/defroute "/issue/:issue" {issue :issue}
|
||||
(rf/dispatch [:set-issue issue]))
|
||||
(rf/dispatch [:set-and-go-to-issue issue]))
|
||||
|
||||
(secretary/defroute "/map" []
|
||||
(rf/dispatch [:set-active-page :map]))
|
||||
|
|
|
@ -31,42 +31,48 @@
|
|||
|
||||
(def default-db
|
||||
{;;; the currently selected address, if any.
|
||||
:address {:id 1 :address "13 Imaginary Terrace, IM1 3TE" :latitude 55.8253043 :longitude -4.2569057
|
||||
:electors [{:id 1 :name "Alan Anderson" :gender :male :intention :no}
|
||||
{:id 2 :name "Ann Anderson" :gender :female}
|
||||
{:id 3 :name "Alex Anderson" :gender :fluid :intention :yes}
|
||||
{:id 4 :name "Andy Anderson" :intention :yes}]}
|
||||
;;; a list of the addresses in the current location at which there
|
||||
;;; are electors registered.
|
||||
:addresses [{:id 1 :address "13 Imaginary Terrace, IM1 3TE" :latitude 55.8253043 :longitude -4.2570944
|
||||
:electors [{:id 1 :name "Alan Anderson" :gender :male :intention :no}
|
||||
{:id 2 :name "Ann Anderson" :gender :female}
|
||||
{:id 3 :name "Alex Anderson" :gender :fluid :intention :yes}
|
||||
{:id 4 :name "Andy Anderson" :intention :yes}]}
|
||||
{:id 2 :address "15 Imaginary Terrace, IM1 3TE" :latitude 55.8252354 :longitude -4.2572778
|
||||
:electors [{:id 1 :name "Beryl Brown" :gender :female}
|
||||
{:id 2 :name "Betty Black" :gender :female}]}
|
||||
{:id 3 :address "17 Imaginary Terrace, IM1 3TE" :latitude 55.825166 :longitude -4.257026
|
||||
:electors [{:id 1 :name "Catriona Crathie" :gender :female :intention :yes}
|
||||
{:id 2 :name "Colin Caruthers" :gender :male :intention :yes}
|
||||
{:id 3 :name "Calum Crathie" :intention :yes}]}
|
||||
{:id 4 :address "19 Imaginary Terrace, IM1 3TE" :latitude 55.82506950000001 :longitude -4.2570239
|
||||
:electors [{:id 1 :name "David Dewar" :gender :male :intention :no}]}]
|
||||
;;; electors at the currently selected address
|
||||
:electors [{:id 1 :name "Alan Anderson" :gender :male :intention :no}
|
||||
{:id 2 :name "Ann Anderson" :gender :female}
|
||||
{:id 3 :name "Alex Anderson" :gender :fluid :intention :yes}
|
||||
{:id 4 :name "Andy Anderson" :intention :yes}]
|
||||
;;; the issue from among the issues which is currently selected.
|
||||
:issue "Currency"
|
||||
;;; the issues selected for the issues page on this day.
|
||||
:issues {"Currency" "Scotland could keep the Pound, or use the Euro. But we could also set up a new currency of our own. Yada yada yada"
|
||||
"Monarchy" "Scotland could keep the Queen. This is an issue to be decided after independence. Yada yada yada"
|
||||
"Defence" "Scotland will not have nuclear weapons, and will probably not choose to engage in far-off wars. But we could remain members of NATO"}
|
||||
;;; message of the day
|
||||
:motd "This is a test version only. There is no real data."
|
||||
;;; the options from among which electors can select.
|
||||
:options [{:id :yes :description "Yes"} {:id :no :description "No"}]
|
||||
;;; the currently displayed 'page' within the app.
|
||||
:page :home
|
||||
})
|
||||
:address {:id 1 :address "13 Imaginary Terrace, IM1 3TE" :latitude 55.8253043 :longitude -4.2569057
|
||||
:electors [{:id 1 :name "Alan Anderson" :gender :male :intention :no}
|
||||
{:id 2 :name "Ann Anderson" :gender :female}
|
||||
{:id 3 :name "Alex Anderson" :gender :fluid :intention :yes}
|
||||
{:id 4 :name "Andy Anderson" :intention :yes}]}
|
||||
;;; a list of the addresses in the current location at which there
|
||||
;;; are electors registered.
|
||||
:addresses [{:id 1 :address "13 Imaginary Terrace, IM1 3TE" :latitude 55.8253043 :longitude -4.2570944
|
||||
:electors [{:id 1 :name "Alan Anderson" :gender :male :intention :no}
|
||||
{:id 2 :name "Ann Anderson" :gender :female}
|
||||
{:id 3 :name "Alex Anderson" :gender :fluid :intention :yes}
|
||||
{:id 4 :name "Andy Anderson" :intention :yes}]}
|
||||
{:id 2 :address "15 Imaginary Terrace, IM1 3TE" :latitude 55.8252354 :longitude -4.2572778
|
||||
:electors [{:id 1 :name "Beryl Brown" :gender :female}
|
||||
{:id 2 :name "Betty Black" :gender :female}]}
|
||||
{:id 3 :address "17 Imaginary Terrace, IM1 3TE" :latitude 55.825166 :longitude -4.257026
|
||||
:electors [{:id 1 :name "Catriona Crathie" :gender :female :intention :yes}
|
||||
{:id 2 :name "Colin Caruthers" :gender :male :intention :yes}
|
||||
{:id 3 :name "Calum Crathie" :intention :yes}]}
|
||||
{:id 4 :address "19 Imaginary Terrace, IM1 3TE" :latitude 55.82506950000001 :longitude -4.2570239
|
||||
:electors [{:id 1 :name "David Dewar" :gender :male :intention :no}]}]
|
||||
;;; electors at the currently selected address
|
||||
:electors [{:id 1 :name "Alan Anderson" :gender :male :intention :no}
|
||||
{:id 2 :name "Ann Anderson" :gender :female}
|
||||
{:id 3 :name "Alex Anderson" :gender :fluid :intention :yes}
|
||||
{:id 4 :name "Andy Anderson" :intention :yes}]
|
||||
;;; any error to display
|
||||
:error nil
|
||||
;;; the issue from among the issues which is currently selected.
|
||||
;;; any confirmation message to display
|
||||
:feedback nil
|
||||
;;; the currently selected issue
|
||||
:issue "Currency"
|
||||
;;; the issues selected for the issues page on this day.
|
||||
:issues {"Currency" "Scotland could keep the Pound, or use the Euro. But we could also set up a new currency of our own. Yada yada yada"
|
||||
"Monarchy" "Scotland could keep the Queen. This is an issue to be decided after independence. Yada yada yada"
|
||||
"Defence" "Scotland will not have nuclear weapons, and will probably not choose to engage in far-off wars. But we could remain members of NATO"}
|
||||
;;; message of the day
|
||||
:motd "This is a test version only. There is no real data."
|
||||
;;; the options from among which electors can select.
|
||||
:options [{:id :yes :description "Yes"} {:id :no :description "No"}]
|
||||
;;; the currently displayed 'page' within the app.
|
||||
:outqueue ()
|
||||
:page :home
|
||||
})
|
||||
|
|
|
@ -27,17 +27,81 @@
|
|||
;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn clear-messages
|
||||
"Return a state like this state except with the error and feedback messages
|
||||
set nil"
|
||||
[state]
|
||||
(merge state {:error nil :feedback nil}))
|
||||
|
||||
|
||||
(defn get-elector
|
||||
"Return the elector at this address (or the current address if not specified)
|
||||
with this id."
|
||||
([elector-id state]
|
||||
(get-elector elector-id state (:address state)))
|
||||
([elector-id state address]
|
||||
(first
|
||||
(remove
|
||||
nil?
|
||||
(map
|
||||
#(if (= elector-id (:id %)) %)
|
||||
(:electors address))))))
|
||||
|
||||
|
||||
(reg-event-db
|
||||
:initialize-db
|
||||
(fn [_ _]
|
||||
db/default-db))
|
||||
|
||||
|
||||
(reg-event-db
|
||||
:send-intention
|
||||
(fn [db [_ args]]
|
||||
(let [intention (:intention args)
|
||||
elector-id (:elector-id args)
|
||||
elector
|
||||
(first
|
||||
(remove nil?
|
||||
(map
|
||||
#(if (= elector-id (:id %)) %)
|
||||
(:electors (:address db)))))
|
||||
old-address (:address db)
|
||||
new-address (assoc old-address :electors (cons (assoc elector :intention intention) (remove #(= % elector) (:electors old-address))))]
|
||||
(cond
|
||||
(nil? elector)
|
||||
(assoc db :error "No elector found; not setting intention")
|
||||
(= intention (:intention elector)) (do (js/console.log "Elector's intention hasn't changed; not setting intention") db)
|
||||
true
|
||||
(do
|
||||
(js/console.log (str "Setting intention of elector " elector " to " intention))
|
||||
(merge
|
||||
(clear-messages db)
|
||||
{:addresses
|
||||
(cons new-address (remove old-address (:addresses db)))
|
||||
:address new-address
|
||||
:outqueue (cons (assoc args :action :set-intention) (:outqueue db))}))))))
|
||||
|
||||
|
||||
(reg-event-db
|
||||
:send-request
|
||||
(fn [db [_ _]]
|
||||
(if (and (:elector db) (:issue db) (:telephone db))
|
||||
(do
|
||||
(js/console.log "Sending request")
|
||||
(assoc db
|
||||
:feedback "Request has been queued"
|
||||
:outqueue (cons
|
||||
{:elector-id (:id (:elector db))
|
||||
:issue (:issue db)
|
||||
:action :add-request} (:outqueue db))))
|
||||
(assoc db :error "Please supply a telephone number to call"))))
|
||||
|
||||
|
||||
(reg-event-db
|
||||
:set-active-page
|
||||
(fn [db [_ page]]
|
||||
(if page
|
||||
(assoc db :page page))))
|
||||
(assoc (clear-messages db) :page page))))
|
||||
|
||||
|
||||
(reg-event-db
|
||||
|
@ -45,48 +109,43 @@
|
|||
(fn [db [_ address-id]]
|
||||
(let [id (read-string address-id)
|
||||
address (first (remove nil? (map #(if (= id (:id %)) %) (:addresses db))))]
|
||||
(assoc (assoc db :address address) :page :electors))))
|
||||
(assoc (clear-messages db) :address address :page :electors))))
|
||||
|
||||
|
||||
(reg-event-db
|
||||
:set-elector-and-page
|
||||
(fn [db [_ args]]
|
||||
(let [page (:page args)
|
||||
elector-id (read-string (:elector-id args))
|
||||
elector
|
||||
(first
|
||||
(remove nil?
|
||||
(map
|
||||
#(if (= elector-id (:id %)) %)
|
||||
(:electors (:address db)))))]
|
||||
(js/console.log (str "Setting page to " page ", elector to " elector))
|
||||
(assoc (assoc db :elector elector) :page page))))
|
||||
:set-and-go-to-issue
|
||||
(fn [db [_ issue]]
|
||||
(js/console.log (str "Setting page to :issue, issue to " issue))
|
||||
(assoc (assoc (clear-messages db) :issue issue) :page :issue)))
|
||||
|
||||
|
||||
(reg-event-db
|
||||
:set-intention
|
||||
(reg-event-db
|
||||
:set-elector-and-page
|
||||
(fn [db [_ args]]
|
||||
(let [intention (:intention args)
|
||||
(let [page (:page args)
|
||||
elector-id (read-string (:elector-id args))
|
||||
elector
|
||||
(first
|
||||
(remove nil?
|
||||
(map
|
||||
#(if (= elector-id (:id %)) %)
|
||||
(:electors (:address db)))))
|
||||
old-address (:address db)
|
||||
new-address (assoc old-address :electors (cons (assoc elector :intention intention) (remove #(= % elector) (:electors old-address))))]
|
||||
(cond
|
||||
(nil? elector)(do (js/console.log "No elector found; not setting intention") db)
|
||||
(= intention (:intention elector)) (do (js/console.log "Elector's intention hasn't changed; not setting intention") db)
|
||||
true
|
||||
(do
|
||||
(js/console.log (str "Setting intention of elector " elector " to " intention))
|
||||
(assoc db :addresses (cons new-address (remove old-address (:addresses db)))))))))
|
||||
elector (get-elector elector-id db)]
|
||||
(js/console.log (str "Setting page to " page ", elector to " elector))
|
||||
(assoc (clear-messages db) :elector elector :page page))))
|
||||
|
||||
|
||||
(reg-event-db
|
||||
:set-elector
|
||||
(fn [db [_ elector-id]]
|
||||
(let [elector (get-elector (read-string elector-id) db)]
|
||||
(js/console.log (str "Setting elector to " elector))
|
||||
(assoc (clear-messages db) :elector elector))))
|
||||
|
||||
|
||||
(reg-event-db
|
||||
:set-issue
|
||||
(fn [db [_ issue]]
|
||||
(js/console.log (str "Setting page to :issue, issue to " issue))
|
||||
(assoc (assoc db :issue issue) :page :issue)))
|
||||
(js/console.log (str "Setting issue to " issue))
|
||||
(assoc (clear-messages db) :issue issue)))
|
||||
|
||||
|
||||
(reg-event-db
|
||||
:set-telephone
|
||||
(fn [db [_ telephone]]
|
||||
(js/console.log (str "Setting telephone to " telephone))
|
||||
(assoc (clear-messages db) :telephone telephone)))
|
||||
|
|
|
@ -39,11 +39,26 @@
|
|||
(fn [db _]
|
||||
(:addresses db)))
|
||||
|
||||
(reg-sub
|
||||
:changes
|
||||
(fn [db _]
|
||||
(:changes db)))
|
||||
|
||||
(reg-sub
|
||||
:elector
|
||||
(fn [db _]
|
||||
(:elector db)))
|
||||
|
||||
(reg-sub
|
||||
:error
|
||||
(fn [db _]
|
||||
(:error db)))
|
||||
|
||||
(reg-sub
|
||||
:feedback
|
||||
(fn [db _]
|
||||
(:feedback db)))
|
||||
|
||||
(reg-sub
|
||||
:issue
|
||||
(fn [db _]
|
||||
|
@ -63,3 +78,9 @@
|
|||
:options
|
||||
(fn [db _]
|
||||
(:options db)))
|
||||
|
||||
(reg-sub
|
||||
:outqueue
|
||||
(fn [db _]
|
||||
(:outqueue db)))
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;
|
||||
;;;; youyesyet.views.electors: about/credits view for youyesyet.
|
||||
;;;; youyesyet.views.about: about/credits view for youyesyet.
|
||||
;;;;
|
||||
;;;; This program is free software; you can redistribute it and/or
|
||||
;;;; modify it under the terms of the GNU General Public License
|
||||
|
@ -41,23 +41,23 @@
|
|||
[:p.motd {:dangerouslySetInnerHTML
|
||||
{:__html (md->html motd)}}]
|
||||
[:p
|
||||
[:img {:src "img/ric-logo.png" :width "24" :height "24"}]
|
||||
[:img {:src "img/credits/ric-logo.png" :width "24" :height "24"}]
|
||||
" A project of the "
|
||||
[:a {:href "https://radical.scot/"} "Radical Independence Campaign"]]
|
||||
[:p
|
||||
[:img {:src "img/luminus-logo.png" :alt "Luminus" :height "24" :width "24"}]
|
||||
[:img {:src "img/credits/luminus-logo.png" :alt "Luminus" :height "24" :width "24"}]
|
||||
" Built with "
|
||||
[:a {:href "http://www.luminusweb.net/"} "Luminus Web"]]
|
||||
[:p
|
||||
[:img {:src "img/clojure-icon.gif" :alt "Clojure" :height "24" :width "24"}]
|
||||
[:img {:src "img/credits/clojure-icon.gif" :alt "Clojure" :height "24" :width "24"}]
|
||||
" Powered by "
|
||||
[:a {:href "http://clojure.org"} "Clojure"]]
|
||||
[:p
|
||||
[:img {:src "img/github-logo-transparent.png" :alt "GitHub" :height "24" :width "24"}]
|
||||
[:img {:src "img/credits/github-logo-transparent.png" :alt "GitHub" :height "24" :width "24"}]
|
||||
" Find me/fork me on "
|
||||
[:a {:href "https://github.com/simon-brooke/youyesyet"} "GitHub"]]
|
||||
[:p
|
||||
[:img {:src "img/gnu.small.png" :alt "Free Software Foundation" :height "24" :width "24"}]
|
||||
[:img {:src "img/credits/gnu.small.png" :alt "Free Software Foundation" :height "24" :width "24"}]
|
||||
" Licensed under the "
|
||||
[:a {:href "http://www.gnu.org/licenses/gpl-2.0.html"}
|
||||
"GNU General Public License v2.0"]]
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
(ns youyesyet.views.electors
|
||||
(:require [re-frame.core :refer [reg-sub subscribe]]
|
||||
(:require [reagent.core :refer [atom]]
|
||||
[re-frame.core :refer [reg-sub subscribe dispatch]]
|
||||
[youyesyet.ui-utils :as ui]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -47,12 +48,14 @@
|
|||
image (if gender (name gender) "unknown")]
|
||||
[:td {:key (:id elector)} [:img {:src (str "img/gender/" image ".png") :alt image}]]))
|
||||
|
||||
|
||||
(defn genders-row
|
||||
[electors]
|
||||
[:tr
|
||||
(map
|
||||
#(gender-cell %) electors)])
|
||||
|
||||
|
||||
(defn name-cell
|
||||
[elector]
|
||||
[:td {:key (str "name-" (:id elector))} (:name elector)])
|
||||
|
@ -63,26 +66,33 @@
|
|||
(map
|
||||
#(name-cell %) electors)])
|
||||
|
||||
|
||||
(defn options-row
|
||||
[electors option]
|
||||
(let [optid (:id option)
|
||||
optname (name optid)]
|
||||
[:tr {:key (str "options-" optid)}
|
||||
[:tr {:key (str "options-" optname)}
|
||||
(map
|
||||
#(let [selected (= optid (:intention %))
|
||||
image (if selected (str "img/option/" optname "-selected.png")
|
||||
(str "img/option/" optname "-unselected.png"))]
|
||||
[:td {:key (str "option-" optid "-" (:id %))}
|
||||
[:a {:href (str "#/set-intention/" (:id %) "/" optid)}
|
||||
[:img {:src image :alt optname}]]])
|
||||
electors)]))
|
||||
(fn [elector] (let [selected (= optid (:intention elector))
|
||||
image (if selected (str "img/option/" optname "-selected.png")
|
||||
(str "img/option/" optname "-unselected.png"))]
|
||||
[:td {:key (str "option-" optid "-" (:id elector))}
|
||||
[:img
|
||||
{:src image
|
||||
:alt optname
|
||||
:on-click #(dispatch
|
||||
[:send-intention {:elector-id (:id elector)
|
||||
:intention optid}])}]]))
|
||||
;; TODO: impose an ordering on electors - by name or by id
|
||||
electors)]))
|
||||
|
||||
|
||||
(defn issue-cell
|
||||
"Create an issue cell for a particular elector"
|
||||
[elector]
|
||||
[:td {:key (:id elector)}
|
||||
[:a {:href (str "#/issues/" (:id elector))}
|
||||
[:img {:src "/img/issues.png" :alt "Issues"}]]])
|
||||
[:img {:src "img/issues.png" :alt "Issues"}]]])
|
||||
|
||||
|
||||
(defn issues-row
|
||||
|
@ -97,8 +107,9 @@
|
|||
[]
|
||||
(let [address @(subscribe [:address])
|
||||
addresses @(subscribe [:addresses])
|
||||
electors (:electors address)
|
||||
options @(subscribe [:options])]
|
||||
electors (sort-by :id (:electors address))
|
||||
options @(subscribe [:options])
|
||||
changes @(subscribe [:changes])]
|
||||
(if address
|
||||
[:div
|
||||
[:h1 (:address address)]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
(ns youyesyet.views.followup
|
||||
(:require [re-frame.core :refer [reg-sub subscribe]]
|
||||
(:require [re-frame.core :refer [reg-sub subscribe dispatch]]
|
||||
[youyesyet.ui-utils :as ui]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -50,25 +50,28 @@
|
|||
[:div
|
||||
[:h1 "Followup Request"]
|
||||
[:div.container {:id "main-container"}
|
||||
[:form {}
|
||||
[:div {}
|
||||
[:p.widget
|
||||
[:label {:for "elector"} "Elector"]
|
||||
[:select {:id "elector" :name "elector" :value (:id elector)}
|
||||
[:select {:id "elector" :name "elector" :defaultValue (:id elector)
|
||||
:on-change #(dispatch [:set-elector (.-value (.-target %))])}
|
||||
(map
|
||||
#(let []
|
||||
[:option {:value (:id %) :key (:id %)} (:name %)]) (:electors address))]]
|
||||
[:p.widget
|
||||
[:label {:for "issue"} "Issue"]
|
||||
[:select {:id "issue" :name "issue" :value issue}
|
||||
;; #(reset! val (-> % .-target .-value))
|
||||
[:select {:id "issue" :name "issue" :defaultValue issue
|
||||
:on-change #(dispatch [:set-issue (.-value (.-target %))])}
|
||||
(map
|
||||
#(let []
|
||||
[:option {:key % :value %} %]) (keys issues))]]
|
||||
[:p.widget
|
||||
[:label {:for "telephone"} "Telephone number"]
|
||||
[:input {:type "text" :id "telephone" :name "telephone"}]]
|
||||
[:input {:type "text" :id "telephone" :name "telephone"
|
||||
:on-change #(dispatch [:set-telephone (.-value (.-target %))])}]]
|
||||
[:p.widget
|
||||
[:label {:for "submit"} "To request a call"]
|
||||
[:input {:id "submit" :name "submit" :type "submit" :value "Send this!"}]]
|
||||
]
|
||||
[:label {:for "send"} "To request a call"]
|
||||
[:button {:id "send" :on-click #(dispatch [:send-request])} "Send this!"]]]
|
||||
(ui/back-link)]])))
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@
|
|||
[id]
|
||||
(js/console.log (str "Click handler for address #" id))
|
||||
(set! window.location.href (str "#electors/" id)))
|
||||
;; This way is probably more idiomatic React, but back links don't work:
|
||||
;; This way is probably more idiomatic React, but history doesn't work:
|
||||
;; (defn map-pin-click-handler
|
||||
;; [id]
|
||||
;; (dispatch [:set-address id]))
|
||||
|
|