#5: Sorting out merge horror

This commit is contained in:
simon 2017-04-01 15:24:30 +01:00
commit 3795f4e0fe
109 changed files with 3648 additions and 1436 deletions

View file

@ -15,9 +15,6 @@
Timestamp
PreparedStatement]))
;; TODO: I am CERTANLY misunderstanding something here. We ought to be getting
;; the database connection string and credentials fomr profiles.clj
;; (def ^:dynamic *db* {:name "java:comp/env/jdbc/EmployeeDB"})
(defstate ^:dynamic *db*
:start (conman/connect! {:jdbc-url (env :database-url)
:driver-class-name "org.postgresql.Driver"})

View file

@ -22,7 +22,8 @@
(assoc params
:page template
:csrf-token *anti-forgery-token*
:servlet-context *app-context*)))
:servlet-context *app-context*
:version (System/getProperty "youyesyet.version"))))
"text/html; charset=utf-8"))
(defn error-page

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

View file

@ -1,57 +1,132 @@
(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]
[youyesyet.ui-utils :as ui]
[youyesyet.views.about :as about]
[youyesyet.views.home :as home]
[youyesyet.views.electors :as electors]
[youyesyet.views.followup :as followup]
[youyesyet.views.issue :as issue]
[youyesyet.views.issues :as issues]
[youyesyet.views.map :as maps])
(:import goog.History))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
;;;; youyesyet.core: core of the app.
;;;;
;;;; 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
;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; So that we can do debug logging!
(enable-console-print!)
(defn about-page []
(about/panel))
(defn electors-page []
(electors/panel))
(defn home-page []
(home/panel))
(defn followup-page []
(followup/panel))
(defn issues-page []
(issues/panel))
(defn issue-page []
(issue/panel))
(defn map-page []
(maps/panel))
(def pages
{:home #'home-page
{:about #'about-page
:electors #'electors-page
:followup #'followup-page
:issues #'issues-page
:issue #'issue-page
:map #'map-page
:about #'about-page})
})
(defn page []
[:div
[:header
[ui/navbar]
[:h1 "You yes yet?"]]
[(pages @(rf/subscribe [:page]))]])
(defn page
"Render the single page of the app, taking the current panel from
the :page key in the state map."
[]
(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)])
[: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
(secretary/set-config! :prefix "#")
(secretary/defroute "/" []
(rf/dispatch [:set-active-page :home]))
(rf/dispatch [:set-active-page :map]))
(secretary/defroute "/about" []
(rf/dispatch [:set-active-page :about]))
(secretary/defroute "/electors" []
(rf/dispatch [:set-active-page :electors]))
(secretary/defroute "/electors/:address" {address-id :address}
(rf/dispatch [:set-address address-id]))
(secretary/defroute "/followup" []
(rf/dispatch [:set-active-page :followup]))
(secretary/defroute "/issues" []
(rf/dispatch [:set-active-page :issues]))
(secretary/defroute "/issues/:elector" {elector-id :elector}
(rf/dispatch [:set-elector-and-page {:elector-id elector-id :page :issues}]))
(secretary/defroute "/issue/:issue" {issue :issue}
(rf/dispatch [:set-and-go-to-issue issue]))
(secretary/defroute "/map" []
(rf/dispatch [:set-active-page :map]))
(secretary/defroute "/set-intention/:elector/:intention" {elector-id :elector intention :intention}
(rf/dispatch [:set-intention {:elector-id elector-id :intention intention}]))
;; -------------------------
;; History
;; must be called after routes have been defined
@ -65,9 +140,6 @@
;; -------------------------
;; Initialize app
(defn fetch-docs! []
(GET (str js/context "/docs")
{:handler #(rf/dispatch [:set-docs %])}))
(defn mount-components []
(r/render [#'page] (.getElementById js/document "app")))
@ -75,6 +147,5 @@
(defn init! []
(rf/dispatch-sync [:initialize-db])
(load-interceptors!)
(fetch-docs!)
(hook-browser-navigation!)
(mount-components))

View file

@ -1,4 +1,79 @@
(ns youyesyet.db)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
;;;; youyesyet.db: the state of the app.
;;;;
;;;; 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
;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; This is the constructor for the atom in which the state of the user interface is held.
;;; The atom gets updated by 'events' registered in handler.cljs, q.v.
;;;
;;; not wonderfully happy with 'db' as a name for this namespace; will probably change to
;;; 'client-state'.
(def default-db
{:page :home})
{;;; 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}]
;;; 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 queue of items waiting to be transmitted.
:outqueue ()
;;; the currently displayed page within the app.
:page :home
})

View file

@ -1,18 +1,152 @@
(ns youyesyet.handlers
(:require [youyesyet.db :as db]
[re-frame.core :refer [dispatch reg-event-db]]))
(:require [cljs.reader :refer [read-string]]
[re-frame.core :refer [dispatch reg-event-db]]
[youyesyet.db :as db]
))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
;;;; youyesyet.handlers: handlers for events.
;;;;
;;;; 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
;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(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
:set-active-page
(fn [db [_ page]]
(assoc db :page page)))
(reg-event-db
:set-docs
(fn [db [_ docs]]
(assoc db :docs docs)))
: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
:elector elector
: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 (clear-messages db) :page page))))
(reg-event-db
:set-address
(fn [db [_ address-id]]
(let [id (read-string address-id)
address (first (remove nil? (map #(if (= id (:id %)) %) (:addresses db))))]
(assoc (clear-messages db) :address address :page :electors))))
(reg-event-db
: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-elector-and-page
(fn [db [_ args]]
(let [page (:page args)
elector-id (read-string (:elector-id args))
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 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)))

View file

@ -1,12 +1,86 @@
(ns youyesyet.subscriptions
(:require [re-frame.core :refer [reg-sub]]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
;;;; youyesyet.views.electors: subscriptions for everything in the app state.
;;;;
;;;; 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
;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(reg-sub
:motd
(fn [db _]
(:motd db)))
(reg-sub
:address
(fn [db _]
(:address db)))
(reg-sub
:addresses
(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 _]
(:issue db)))
(reg-sub
:issues
(fn [db _]
(:issues db)))
(reg-sub
:page
(fn [db _]
(:page db)))
(reg-sub
:docs
:options
(fn [db _]
(:docs db)))
(:options db)))
(reg-sub
:outqueue
(fn [db _]
(:outqueue db)))

View file

@ -32,7 +32,7 @@
(defn big-link [text target]
[:div.big-link-container
[:div.big-link-container {:key target}
[:a.big-link {:href target} text]])
@ -45,6 +45,14 @@
:on-click #(reset! collapsed? true)} title]]))
(defn error-panel
[message]
[:div
[:h1.error message]
[:div.container {:id "main-container"}
(back-link)]])
(defn navbar []
(r/with-let [collapsed? (r/atom true)]
[:div {:id "nav"}
@ -52,8 +60,7 @@
:src "img/threelines.png"
:on-click #(swap! collapsed? not)}]
[:menu.nav {:id "nav-menu" :class (if @collapsed? "hidden" "shown")}
(nav-link "#/" "Home" :home collapsed?)
(nav-link "#/library" "Library" :library collapsed?)
(nav-link "#/register" "Register" :register collapsed?)
(nav-link "#/login" "Login" :login collapsed?)
(nav-link "#/map" "Map" :map collapsed?)
(nav-link "#/electors" "Electors" :electors collapsed?)
(nav-link "#/issues" "Issues" :issues collapsed?)
(nav-link "#/about" "About" :about collapsed?)]]))

View file

@ -1,10 +1,11 @@
(ns youyesyet.views.about
(:require [re-frame.core :refer [reg-sub]]
(:require [re-frame.core :refer [reg-sub subscribe]]
[markdown.core :refer [md->html]]
[youyesyet.ui-utils :as ui]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
;;;; 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
@ -25,7 +26,6 @@
;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; The pattern from the re-com demo (https://github.com/Day8/re-com) is to have
;;; one source file/namespace per view. Each namespace contains a function 'panel'
;;; whose output is an enlive-style specification of the view to be redered.
@ -33,22 +33,32 @@
(defn panel
"Generate the about panel."
[]
[:div.container {:id "main-container"}
[:h2 "Pre-alpha/proof of concept"]
[:p {:class "centre"}
[:img {:src "img/ric-logo.png" :width "24" :height "24"}]
" A project of the "
[:a {:href "https://radical.scot/"} "Radical Independence Campaign"]]
[:p {:class "centre"}
[:img {:src "img/clojure-icon.gif" :alt "Clojure" :height "24" :width "24"}]
" Powered by "
[:a {:href "http://clojure.org"} "Clojure"]]
[:p {:class "centre"}
[:img {:src "img/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 {:class "centre"}
[:img {:src "img/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"]]])
(let [motd @(subscribe [:motd])]
[:div
[:h1 "You Yes Yet?"]
[:div.container {:id "main-container"}
[:h2 "Pre-alpha/proof of concept"]
[:p.motd {:dangerouslySetInnerHTML
{:__html (md->html motd)}}]
[:p
[: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/credits/luminus-logo.png" :alt "Luminus" :height "24" :width "24"}]
" Built with "
[:a {:href "http://www.luminusweb.net/"} "Luminus Web"]]
[:p
[: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/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/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"]]
(ui/back-link)]]))

View file

@ -1,5 +1,7 @@
(ns youyesyet.views.electors
(:require [re-frame.core :refer [reg-sub]]))
(:require [reagent.core :refer [atom]]
[re-frame.core :refer [reg-sub subscribe dispatch]]
[youyesyet.ui-utils :as ui]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
@ -32,7 +34,99 @@
;;; See https://github.com/simon-brooke/youyesyet/blob/master/doc/specification/userspec.md#electors-view
;;; The design for this panel is one column per elector within the address.
;;; Each column contains
;;; 1. a stick figure identifying gender (for recognition);
;;; 2. the elector's name;
;;; 3. one icon for each option on the ballot;
;;; 4. an 'issues' icon.
;;; The mechanics of how this panel is laid out don't matter.
(defn gender-cell
[elector]
(let [gender (:gender elector)
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)])
(defn names-row
[electors]
[:tr
(map
#(name-cell %) electors)])
(defn options-row
[electors option]
(let [optid (:id option)
optname (name optid)]
[:tr {:key (str "options-" optname)}
(map
(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"}]]])
(defn issues-row
[electors]
[:tr
(map
#(issue-cell %)
electors)])
(defn panel
"Generate the electors panel."
[]
[])
(let [address @(subscribe [:address])
addresses @(subscribe [:addresses])
electors (sort-by :id (:electors address))
options @(subscribe [:options])
changes @(subscribe [:changes])]
(if address
[:div
[:h1 (:address address)]
[:div.container {:id "main-container"}
[:table
[:tbody
;; genders row
(genders-row electors)
;; names row
(names-row electors)
;; options rows
(map
#(options-row electors %)
options)
;; issues row
(issues-row electors)]]
(ui/back-link)]]
(ui/error-panel "No address selected"))))

View file

@ -0,0 +1,77 @@
(ns youyesyet.views.followup
(:require [re-frame.core :refer [reg-sub subscribe dispatch]]
[youyesyet.ui-utils :as ui]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
;;;; youyesyet.views.followup-request: followup-request 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
;;;; 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 pattern from the re-com demo (https://github.com/Day8/re-com) is to have
;;; one source file/namespace per view. Each namespace contains a function 'panel'
;;; whose output is an enlive-style specification of the view to be redered.
;;; I propose to follow this pattern. This file will (eventually) provide the followup-request view.
;;; See https://github.com/simon-brooke/youyesyet/blob/master/doc/specification/userspec.md#followup-request-form
(defn panel
"Generate the followup-request panel."
[]
(let [issue @(subscribe [:issue])
issues @(subscribe [:issues])
elector @(subscribe [:elector])
address @(subscribe [:address])]
(js/console.log (str "Issue is " issue "; elector is " elector))
(cond
(nil? address)
(ui/error-panel "No address selected")
(nil? issues)
(ui/error-panel "No issues loaded")
true
[:div
[:h1 "Followup Request"]
[:div.container {:id "main-container"}
[:div {}
[:p.widget
[:label {:for "elector"} "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"]
;; #(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"
:on-change #(dispatch [:set-telephone (.-value (.-target %))])}]]
[:p.widget
[:label {:for "send"} "To request a call"]
[:button {:id "send" :on-click #(dispatch [:send-request])} "Send this!"]]]
(ui/back-link)]])))

View file

@ -1,38 +0,0 @@
(ns youyesyet.views.followup-request
(:require [re-frame.core :refer [reg-sub]]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
;;;; youyesyet.views.followup-request: followup-request 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
;;;; 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 pattern from the re-com demo (https://github.com/Day8/re-com) is to have
;;; one source file/namespace per view. Each namespace contains a function 'panel'
;;; whose output is an enlive-style specification of the view to be redered.
;;; I propose to follow this pattern. This file will (eventually) provide the followup-request view.
;;; See https://github.com/simon-brooke/youyesyet/blob/master/doc/specification/userspec.md#followup-request-form
(defn panel
"Generate the followup-request panel."
[]
[])

View file

@ -1,38 +0,0 @@
(ns youyesyet.views.login
(:require [re-frame.core :refer [reg-sub]]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
;;;; youyesyet.views.login: login 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
;;;; 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 pattern from the re-com demo (https://github.com/Day8/re-com) is to have
;;; one source file/namespace per view. Each namespace contains a function 'panel'
;;; whose output is an enlive-style specification of the view to be redered.
;;; I propose to follow this pattern. This file will (eventually) provide the login view.
;;; See https://github.com/simon-brooke/youyesyet/blob/master/doc/specification/userspec.md#followup-requests-view
(defn panel
"Generate the login panel."
[]
[])

View file

@ -1,49 +0,0 @@
(ns youyesyet.views.home
(:require [re-frame.core :as rf]
[markdown.core :refer [md->html]]
[youyesyet.ui-utils :as ui]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
;;;; youyesyet.views.electors: home 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
;;;; 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 pattern from the re-com demo (https://github.com/Day8/re-com) is to have
;;; one source file/namespace per view. Each namespace contains a function 'panel'
;;; whose output is an enlive-style specification of the view to be redered.
;;; I propose to follow this pattern. This file will provide the home view.
(defn panel
"Generate the home panel."
[]
[:div.container {:id "main-container"}
(ui/big-link "About" "#/about")
(ui/big-link "Map" "#/map")
[:div.jumbotron
[:h1 "You Yes Yet?"]
[:p "Time to start building your site!"]
[:p [:a.btn.btn-primary.btn-lg {:href "http://luminusweb.net"} "Learn more »"]]]])
(when-let [docs @(rf/subscribe [:docs])]
[:div.row
[:div.col-md-12
[:div {:dangerouslySetInnerHTML
{:__html (md->html docs)}}]]])

View file

@ -1,9 +1,12 @@
(ns youyesyet.views.followup-action
(:require [re-frame.core :refer [reg-sub]]))
(ns youyesyet.views.issue
(:require [re-frame.core :refer [reg-sub subscribe]]
[markdown.core :refer [md->html]]
[youyesyet.ui-utils :as ui]
[youyesyet.views.issues :as issues]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
;;;; youyesyet.views.followup-action: followup-action view for youyesyet.
;;;; youyesyet.views.issues: issues 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
@ -28,11 +31,19 @@
;;; The pattern from the re-com demo (https://github.com/Day8/re-com) is to have
;;; one source file/namespace per view. Each namespace contains a function 'panel'
;;; whose output is an enlive-style specification of the view to be redered.
;;; I propose to follow this pattern. This file will (eventually) provide the followup-action view.
;;; See https://github.com/simon-brooke/youyesyet/blob/master/doc/specification/userspec.md#followup-action-view
;;; I propose to follow this pattern. This file will (eventually) provide the single issue view.
(defn panel
"Generate the followup-action panel."
"Generate the issue panel."
[]
[])
(let [issue @(subscribe [:issue])
issues @(subscribe [:issues])]
[:div
[:h1 issue]
[:div.container {:id "main-container"}
[:div {:id "issue"}
[:div {:id "issue-text"
:dangerouslySetInnerHTML
{:__html (md->html (issues issue))}}]]
(ui/big-link "Request call" "#/followup")
(ui/back-link)]]))

View file

@ -1,5 +1,6 @@
(ns youyesyet.views.issues
(:require [re-frame.core :refer [reg-sub]]))
(:require [re-frame.core :refer [reg-sub subscribe]]
[youyesyet.ui-utils :as ui]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
@ -32,7 +33,16 @@
;;; See https://github.com/simon-brooke/youyesyet/blob/master/doc/specification/userspec.md#issues-view
;;; Simple list of the issues of the day.
(defn panel
"Generate the issues panel."
[]
[])
(let [issues @(subscribe [:issues])]
(if issues
[:div
[:h1 "Issues"]
[:div.container {:id "main-container"}
(ui/back-link)
[:div {:id "issue-list"}
(map (fn [k] (ui/big-link k (str "#/issue/" k))) (keys issues))]]]
(ui/error-panel "No issues loaded"))))

View file

@ -1,38 +0,0 @@
(ns youyesyet.views.login
(:require [re-frame.core :refer [reg-sub]]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
;;;; youyesyet.views.login: login 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
;;;; 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 pattern from the re-com demo (https://github.com/Day8/re-com) is to have
;;; one source file/namespace per view. Each namespace contains a function 'panel'
;;; whose output is an enlive-style specification of the view to be redered.
;;; I propose to follow this pattern. This file will (eventually) provide the login view.
;;; See https://github.com/simon-brooke/youyesyet/blob/master/doc/specification/userspec.md#logging-in
(defn panel
"Generate the login panel."
[]
[])

View file

@ -1,5 +1,5 @@
(ns youyesyet.views.map
(:require [re-frame.core :refer [reg-sub]]
(:require [re-frame.core :refer [reg-sub subscribe dispatch]]
[reagent.core :as reagent]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -33,7 +33,6 @@
;;; See https://github.com/simon-brooke/youyesyet/blob/master/doc/specification/userspec.md#map-view
;;; Cribbed heavily from
;;; https://github.com/reagent-project/reagent-cookbook/tree/master/recipes/leaflet
;;; but using OSM data because we can't afford commercial, so also cribbed from
@ -41,6 +40,11 @@
;;; Note that this is raw reagent stylee; it should be refactoed into re-frame stylee
;;; when I understand it better.
;;; There should be one flag on the map for each address record currently in frame.
;;; Clicking the flag sets that address as the current address in the app state,
;;; and redirects to the electors view. How we handle blocks of flats needs further
;;; thought.
;; which provider to use
(def *map-provider* :osm)
@ -48,26 +52,75 @@
(def osm-attrib "Map data &copy; <a href='http://openstreetmap.org'>OpenStreetMap</a> contributors")
(defn pin-image
"select the name of a suitable pin image for this address"
[address]
(let [intentions (set (remove nil? (map #(:intention %) (:electors address))))]
(case (count intentions)
0 "unknown-pin"
1 (str (name (first intentions)) "-pin")
"mixed-pin")))
(defn map-pin-click-handler
"On clicking on the pin, navigate to the electors at the address.
This way of doing it adds an antry in the browser location history,
so back links work."
[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 history doesn't work:
;; (defn map-pin-click-handler
;; [id]
;; (dispatch [:set-address id]))
(defn add-map-pin
"Add a map-pin at this address in this map view"
[address view]
(let [lat (:latitude address)
lng (:longitude address)
pin (.icon js/L
(clj->js
{:iconUrl (str "img/map-pins/" (pin-image address) ".png")
:shadowUrl "img/map-pins/shadow_pin.png"
:iconSize [32 42]
:shadowSize [57 24]
:iconAnchor [16 41]
:shadowAnchor [16 23]}))
marker (.marker js/L
(.latLng js/L lat lng)
(clj->js {:icon pin
:title (:address address)}))
]
(.on marker "click" (fn [_] (map-pin-click-handler (str (:id address)))))
(.addTo marker view)))
;; My gods mapbox is user-hostile!
(defn map-did-mount-mapbox
"Did-mount function loading map tile data from MapBox (proprietary)."
"Did-mount function loading map tile data from MapBox (proprietary)."
[]
(let [map (.setView (.map js/L "map") #js [55.86 -4.25] 13)]
(let [view (.setView (.map js/L "map" (clj->js {:zoomControl "false"})) #js [55.82 -4.25] 40)]
;; NEED TO REPLACE FIXME with your mapID!
(.addTo (.tileLayer js/L "http://{s}.tiles.mapbox.com/v3/FIXME/{z}/{x}/{y}.png"
(clj->js {:attribution "Map data &copy; [...]"
:maxZoom 18}))
map)))
view)))
(defn map-did-mount-osm
"Did-mount function loading map tile data from Open Street Map (open)."
"Did-mount function loading map tile data from Open Street Map."
[]
(let [map (.setView (.map js/L "map") #js [55.86 -4.25] 13)]
(let [view (.setView (.map js/L "map" (clj->js {:zoomControl false})) #js [55.82 -4.25] 13)
addresses @(subscribe [:addresses])]
(js/console.log (str "Adding " (count addresses) " pins"))
(doall (map #(add-map-pin % view) addresses))
(.addTo (.tileLayer js/L osm-url
(clj->js {:attribution osm-attrib
:maxZoom 18}))
map)))
view)
))
(defn map-did-mount
@ -91,4 +144,3 @@
[]
(reagent/create-class {:reagent-render map-render
:component-did-mount map-did-mount}))

View file

@ -1,39 +0,0 @@
(ns youyesyet.views.role-menu
(:require [re-frame.core :refer [reg-sub]]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
;;;; youyesyet.views.role-menu: role-menu 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
;;;; 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 pattern from the re-com demo (https://github.com/Day8/re-com) is to have
;;; one source file/namespace per view. Each namespace contains a function 'panel'
;;; whose output is an enlive-style specification of the view to be redered.
;;; I propose to follow this pattern. This file will (eventually) provide the role-menu view.
;;; See https://github.com/simon-brooke/youyesyet/blob/master/doc/specification/userspec.md#after-login
(defn panel
"Generate the role-menu panel."
[]
[])

View file

@ -1,38 +0,0 @@
(ns youyesyet.views.signup
(:require [re-frame.core :refer [reg-sub]]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
;;;; youyesyet.views.signup: signup 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
;;;; 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 pattern from the re-com demo (https://github.com/Day8/re-com) is to have
;;; one source file/namespace per view. Each namespace contains a function 'panel'
;;; whose output is an enlive-style specification of the view to be redered.
;;; I propose to follow this pattern. This file will (eventually) provide the signup view.
;;; See https://github.com/simon-brooke/youyesyet/blob/master/doc/specification/userspec.md#creating-an-account
(defn panel
"Generate the signup panel."
[]
[])