Now actually pulling data from server, but some regressions.
The map isn't rendering, and there's something wrong with options. But a lot is working.
This commit is contained in:
parent
d4f0f4aa5c
commit
3a49e50a51
59 changed files with 1263 additions and 3166 deletions
|
|
@ -72,6 +72,8 @@
|
|||
(-> #'auto-selmer-routes
|
||||
(wrap-routes middleware/wrap-csrf)
|
||||
(wrap-routes middleware/wrap-formats))
|
||||
(-> #'auto-rest-routes
|
||||
(wrap-routes middleware/wrap-formats))
|
||||
(-> #'rest-routes
|
||||
(wrap-routes middleware/wrap-formats))
|
||||
'oauth-routes
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,4 +1,4 @@
|
|||
(ns ^{:doc "Routes which handle data transfer to/from the canvasser app."
|
||||
(ns ^{:doc "Manually maintained routes which handle data transfer to/from the canvasser app."
|
||||
:author "Simon Brooke"} youyesyet.routes.rest
|
||||
(:require [clojure.core.memoize :as memo]
|
||||
[clojure.java.io :as io]
|
||||
|
|
@ -36,6 +36,7 @@
|
|||
;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;;;; See also src/clj/youyesyet/routes/auto-json.clj
|
||||
|
||||
(def in-get-local-data
|
||||
"Local data is volatile, because hopefully canvassers are updating it as they
|
||||
|
|
@ -82,13 +83,8 @@
|
|||
(in-get-local-data here)))
|
||||
|
||||
|
||||
|
||||
(defn get-issues
|
||||
"Get current issues. No arguments expected."
|
||||
[request])
|
||||
|
||||
(defroutes rest-routes
|
||||
(GET "/rest/get-local-data" request (get-local-data request))
|
||||
(GET "/rest/get-local-data" request (route/restricted (get-local-data request)))
|
||||
;; (GET "/rest/get-issues" request (route/restricted (get-issues request)))
|
||||
;; (GET "/rest/set-intention" request (route/restricted (set-intention request)))
|
||||
;; (GET "/rest/request-followup" request (route/restricted (request-followup request))))
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
(ns youyesyet.utils
|
||||
#?(:clj (require [clojure.tools.logging :as log]))
|
||||
)
|
||||
#?(:clj (:require [clojure.tools.logging :as log])
|
||||
:cljs (:require [cljs.reader :refer [read-string]])))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;
|
||||
;;;; youyesyet.locality: small utility functions.
|
||||
;;;; youyesyet.utils: small utility functions.
|
||||
;;;;
|
||||
;;;; This program is free software; you can redistribute it and/or
|
||||
;;;; modify it under the terms of the GNU General Public License
|
||||
|
|
@ -27,9 +27,11 @@
|
|||
|
||||
(defn coerce-to-number [v]
|
||||
"If it is possible to do so, coerce `v` to a number"
|
||||
;; TODO: this doesn't work in cljs.
|
||||
(if (number? v) v
|
||||
(try
|
||||
(read-string (str v))
|
||||
#?(:clj (catch Exception any
|
||||
(log/error (str "Could not coerce '" v "' to number: " any)))
|
||||
:cljs (js/console.log (str "Could not coerce '" v "' to number: " any))))))
|
||||
:cljs (catch js/Object any
|
||||
(js/console.log (str "Could not coerce '" v "' to number: " any)))))))
|
||||
|
|
|
|||
|
|
@ -104,8 +104,10 @@
|
|||
(if content [content]
|
||||
[:div.error (str "No content in page " @(rf/subscribe [:page]))])
|
||||
[:footer
|
||||
[:div.error {:style [:display (if error "block" "none")]} (str error)]
|
||||
[:div.feedback {:style [:display (if feedback :block :none)]} (str feedback)]
|
||||
[:div.error {:style [:display (if (empty? error) :none :block)]} (apply str error)]
|
||||
[:div.feedback
|
||||
{:style [:display (if (empty? feedback) :none :block)]}
|
||||
(apply str (map #(h/feedback-messages %) (distinct feedback)))]
|
||||
[:div.queue (if
|
||||
(nil? outqueue) ""
|
||||
(str (count outqueue) " items queued to send"))]]]))
|
||||
|
|
@ -190,6 +192,9 @@
|
|||
(defn init! []
|
||||
(rf/dispatch-sync [:initialize-db])
|
||||
(h/get-current-location)
|
||||
(rf/dispatch [:fetch-locality])
|
||||
(rf/dispatch [:fetch-options])
|
||||
(rf/dispatch [:fetch-issues])
|
||||
(load-interceptors!)
|
||||
(hook-browser-navigation!)
|
||||
(mount-components))
|
||||
|
|
|
|||
|
|
@ -2,9 +2,12 @@
|
|||
:author "Simon Brooke"}
|
||||
youyesyet.canvasser-app.handlers
|
||||
(:require [cljs.reader :refer [read-string]]
|
||||
[re-frame.core :refer [dispatch reg-event-db]]
|
||||
[cemerick.url :refer (url url-encode)]
|
||||
[day8.re-frame.http-fx]
|
||||
[re-frame.core :refer [dispatch reg-event-db reg-event-fx subscribe]]
|
||||
[ajax.core :refer [GET]]
|
||||
[ajax.json :refer [json-request-format json-response-format]]
|
||||
[youyesyet.canvasser-app.state :as db]
|
||||
[youyesyet.utils :refer :all]
|
||||
))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
@ -34,9 +37,47 @@
|
|||
"Return a state like this state except with the error and feedback messages
|
||||
set nil"
|
||||
[state]
|
||||
(merge state {:error nil :feedback nil}))
|
||||
(merge state {:error '() :feedback '()}))
|
||||
|
||||
|
||||
(def source-host (assoc
|
||||
(url js/window.location)
|
||||
:path "/"
|
||||
:query nil
|
||||
:anchor nil))
|
||||
|
||||
|
||||
(def feedback-messages
|
||||
{:fetch-locality "Fetching local data."
|
||||
:send-request "Request has been queued."
|
||||
})
|
||||
|
||||
|
||||
(defn add-to-feedback
|
||||
"Add the value of `k` in `feedback-messages` to the feedback in this `db`."
|
||||
[db k]
|
||||
(assoc db :feedback (cons k (:feedback db))))
|
||||
|
||||
|
||||
(defn remove-from-feedback
|
||||
"Remove the value of `k` in `feedback-messages` to the feedback in this `db`."
|
||||
[db k]
|
||||
(assoc db
|
||||
:feedback
|
||||
(remove
|
||||
#(= % k)
|
||||
(:feedback db))))
|
||||
|
||||
|
||||
(defn coerce-to-number [v]
|
||||
"If it is possible to do so, coerce `v` to a number.
|
||||
NOTE: I tried to do this in *cljc*, but it did not work. Leave it alone."
|
||||
(if (number? v) v
|
||||
(try
|
||||
(read-string (str v))
|
||||
(catch js/Object any
|
||||
(js/console.log (str "Could not coerce '" v "' to number: " any))
|
||||
v))))
|
||||
|
||||
|
||||
(defn get-elector
|
||||
|
|
@ -60,17 +101,250 @@
|
|||
(:electors state) "'")))))
|
||||
|
||||
|
||||
;; map stuff. If we do this in canvasser-app.views.map we get circular
|
||||
;; references, so do it here.
|
||||
(defn pin-image
|
||||
"select the name of a suitable pin image for this address"
|
||||
[address]
|
||||
(let [intentions
|
||||
(set
|
||||
(remove
|
||||
nil?
|
||||
(map
|
||||
:intention
|
||||
(mapcat :electors
|
||||
(:dwellings 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))
|
||||
(let [view @(subscribe [:view])
|
||||
centre (.getCenter view)]
|
||||
(dispatch [:set-zoom (.getZoom view)])
|
||||
(dispatch [:set-latitude (.-lat centre)])
|
||||
(dispatch [:set-longitude (.-lng centre)]))
|
||||
(set! window.location.href (str "#building/" 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
|
||||
{:iconAnchor [16 41]
|
||||
:iconSize [32 42]
|
||||
:iconUrl (str "img/map-pins/" (pin-image address) ".png")
|
||||
:riseOnHover true
|
||||
:shadowAnchor [16 23]
|
||||
:shadowSize [57 24]
|
||||
:shadowUrl "img/map-pins/shadow_pin.png"}))
|
||||
marker (.marker js/L
|
||||
(.latLng js/L lat lng)
|
||||
(clj->js {:icon pin
|
||||
:title (:address address)}))]
|
||||
(.on (.addTo marker view) "click" (fn [_] (map-pin-click-handler (str (:id address)))))
|
||||
marker))
|
||||
|
||||
|
||||
(defn map-remove-pins
|
||||
"Remove all pins from this map `view`. Side-effecty; liable to be problematic."
|
||||
[view]
|
||||
(try
|
||||
(if
|
||||
view
|
||||
(.eachLayer
|
||||
view
|
||||
(fn [layer]
|
||||
(if
|
||||
(instance? layer js/L.Marker)
|
||||
(.removeLayer view layer)))))
|
||||
(catch js/Object any (js/console.log (str "Failed to remove pins from map: " any))))
|
||||
view)
|
||||
|
||||
|
||||
(defn refresh-map-pins
|
||||
"Refresh the map pins on this map. Side-effecty; liable to be problematic."
|
||||
[]
|
||||
(let [view (map-remove-pins @(subscribe [:view]))
|
||||
addresses @(subscribe [:addresses])]
|
||||
(if
|
||||
view
|
||||
(do
|
||||
(js/console.log (str "Adding " (count addresses) " pins"))
|
||||
(doall (map #(add-map-pin % view) addresses)))
|
||||
(js/console.log "View is not yet ready"))
|
||||
view))
|
||||
|
||||
|
||||
(reg-event-db
|
||||
:initialize-db
|
||||
(fn [_ _]
|
||||
db/default-db))
|
||||
|
||||
|
||||
(defn get-current-location []
|
||||
"Get the current location from the device."
|
||||
(try
|
||||
(if (.-geolocation js/navigator)
|
||||
(.getCurrentPosition
|
||||
(.-geolocation js/navigator)
|
||||
(fn [position]
|
||||
(js/console.log "Current location is: " + position)
|
||||
(dispatch [:set-latitude (.-latitude (.-coords position))])
|
||||
(dispatch [:set-longitude (.-longitude (.-coords position))])))
|
||||
(js/console.log "Geolocation not available"))
|
||||
(catch js/Object any
|
||||
(js/console.log "Exception while trying to access location: " + any))))
|
||||
|
||||
|
||||
;; (reg-event-fx
|
||||
;; :feedback
|
||||
;; (fn [x y]
|
||||
;; (js/console.log (str "Feedback event called with x = " x "; y = " y))
|
||||
;; (:db x)))
|
||||
|
||||
|
||||
;; (reg-event-fx
|
||||
;; :issues
|
||||
;; (fn [x y]
|
||||
;; (js/console.log (str "Issues event called with x = " x "; y = " y))
|
||||
;; (:db x)))
|
||||
|
||||
|
||||
;; (reg-event-fx
|
||||
;; :options
|
||||
;; (fn [x y]
|
||||
;; (js/console.log (str "Options event called with x = " x "; y = " y))
|
||||
;; (:db x)))
|
||||
|
||||
|
||||
;; (reg-event-fx
|
||||
;; :event
|
||||
;; (fn [x y]
|
||||
;; (js/console.log (str "Event event called with x = " x "; y = " y))
|
||||
;; (:db x)))
|
||||
|
||||
|
||||
(reg-event-fx
|
||||
:fetch-locality
|
||||
(fn [{db :db} _]
|
||||
(js/console.log "Fetching locality data")
|
||||
;; we return a map of (side) effects
|
||||
{:http-xhrio {:method :get
|
||||
:uri (str source-host
|
||||
"rest/get-local-data?latitude="
|
||||
(:latitude db)
|
||||
"&longitude="
|
||||
(:longitude db))
|
||||
:format (json-request-format)
|
||||
:response-format (json-response-format {:keywords? true})
|
||||
:on-success [:process-locality]
|
||||
:on-failure [:bad-locality]}
|
||||
:db (add-to-feedback db :fetch-locality)}))
|
||||
|
||||
|
||||
(reg-event-db
|
||||
:process-locality
|
||||
(fn
|
||||
[db [_ response]]
|
||||
(js/console.log "Updating locality data")
|
||||
(assoc
|
||||
(remove-from-feedback db :fetch-locality)
|
||||
(refresh-map-pins)
|
||||
:addresses (js->clj response))))
|
||||
|
||||
|
||||
(reg-event-db
|
||||
:bad-locality
|
||||
(fn [db _]
|
||||
;; TODO: signal something has failed? It doesn't matter very much, unless it keeps failing.
|
||||
(js/console.log "Failed to fetch locality data")
|
||||
(assoc
|
||||
(remove-from-feedback db :fetch-locality)
|
||||
:error (cons :fetch-locality (:error db)))))
|
||||
|
||||
|
||||
(reg-event-fx
|
||||
:fetch-options
|
||||
(fn [{db :db} _]
|
||||
(js/console.log "Fetching options")
|
||||
;; we return a map of (side) effects
|
||||
{:http-xhrio {:method :get
|
||||
:uri (str source-host "json/auto/list-options")
|
||||
:format (json-request-format)
|
||||
:response-format (json-response-format {:keywords? true})
|
||||
:on-success [:process-options]
|
||||
:on-failure [:bad-options]}
|
||||
:db (add-to-feedback db :fetch-options)}))
|
||||
|
||||
|
||||
(reg-event-db
|
||||
:process-options
|
||||
(fn
|
||||
[db [_ response]]
|
||||
(js/console.log "Updating options")
|
||||
(assoc
|
||||
(remove-from-feedback db :fetch-options)
|
||||
:options (js->clj response))))
|
||||
|
||||
|
||||
(reg-event-db
|
||||
:bad-options
|
||||
(fn [db _]
|
||||
(js/console.log "Failed to fetch options")
|
||||
(assoc
|
||||
(remove-from-feedback db :fetch-options)
|
||||
:error (cons :fetch-options (:error db)))))
|
||||
|
||||
|
||||
(reg-event-fx
|
||||
:fetch-issues
|
||||
(fn [{db :db} _]
|
||||
(js/console.log "Fetching issues")
|
||||
;; we return a map of (side) effects
|
||||
{:http-xhrio {:method :get
|
||||
:uri (str source-host "json/auto/list-issues")
|
||||
:format (json-request-format)
|
||||
:response-format (json-response-format {:keywords? true})
|
||||
:on-success [:process-issues]
|
||||
:on-failure [:bad-issues]}
|
||||
:db (add-to-feedback db :fetch-issues)}))
|
||||
|
||||
|
||||
(reg-event-db
|
||||
:process-issues
|
||||
(fn
|
||||
[db [_ response]]
|
||||
(js/console.log "Updating issues")
|
||||
(assoc
|
||||
(remove-from-feedback db :fetch-issues)
|
||||
:issues (js->clj response))))
|
||||
|
||||
|
||||
(reg-event-db
|
||||
:bad-issues
|
||||
(fn [db _]
|
||||
(js/console.log "Failed to fetch issues")
|
||||
(assoc
|
||||
(remove-from-feedback db :fetch-issues)
|
||||
:error (cons :fetch-issues (:error db)))))
|
||||
|
||||
|
||||
(reg-event-db
|
||||
:send-intention
|
||||
(fn [db [_ args]]
|
||||
(let [intention (:intention args)
|
||||
elector-id (coerce-to-number (:elector-id args))
|
||||
elector-id (:elector-id args)
|
||||
old-elector (first
|
||||
(remove nil?
|
||||
(map
|
||||
|
|
@ -97,11 +371,11 @@
|
|||
(:dwellings old-address))))]
|
||||
(cond
|
||||
(nil? old-elector)
|
||||
(assoc db :error "No elector found; not setting intention")
|
||||
(= intention (:intention old-elector))
|
||||
(do
|
||||
(js/console.log "Elector's intention hasn't changed; not setting intention")
|
||||
db)
|
||||
(assoc db :error (cons "No elector found; not setting intention" (:error db))
|
||||
(= intention (:intention old-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 " old-elector " to " intention))
|
||||
|
|
@ -125,8 +399,7 @@
|
|||
(if (and (:elector db) (:issue db) (:telephone db))
|
||||
(do
|
||||
(js/console.log "Sending request")
|
||||
(assoc db
|
||||
:feedback "Request has been queued"
|
||||
(assoc (add-to-feedback db :send-request)
|
||||
:outqueue (cons
|
||||
{:elector-id (:id (:elector db))
|
||||
:issue (:issue db)
|
||||
|
|
@ -136,15 +409,16 @@
|
|||
|
||||
(reg-event-db
|
||||
:set-active-page
|
||||
(fn [db [_ page]]
|
||||
(if page
|
||||
(assoc (clear-messages db) :page page))))
|
||||
(fn [db [_ k]]
|
||||
(if k
|
||||
(assoc (clear-messages db) :page k)
|
||||
db)))
|
||||
|
||||
|
||||
(reg-event-db
|
||||
:set-address
|
||||
(fn [db [_ address-id]]
|
||||
(let [id (coerce-to-number address-id)
|
||||
(let [id (coerce-to-number address-id)
|
||||
address (first (remove nil? (map #(if (= id (:id %)) %) (:addresses db))))]
|
||||
(if
|
||||
(= (count (:dwellings address)) 1)
|
||||
|
|
@ -167,7 +441,7 @@
|
|||
consent (:consent args)
|
||||
elector-id (coerce-to-number (:elector-id args))
|
||||
elector (get-elector elector-id db)]
|
||||
(js/console.log (str "Setting page to " page ", consent to " consent " for " elector))
|
||||
(js/console.log (str "Setting page to " page ", consent to " consent " for " (:name elector)))
|
||||
(assoc (clear-messages db) :elector (assoc elector :consent true) :page page))))
|
||||
|
||||
|
||||
|
|
@ -200,7 +474,7 @@
|
|||
:set-elector-and-page
|
||||
(fn [db [_ args]]
|
||||
(let [page (:page args)
|
||||
elector-id (coerce-to-number (:elector-id args))
|
||||
elector-id (: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))))
|
||||
|
|
@ -252,18 +526,3 @@
|
|||
(if (integer? zoom)
|
||||
(assoc db :zoom zoom)
|
||||
db)))
|
||||
|
||||
|
||||
(defn get-current-location []
|
||||
"Get the current location from the device."
|
||||
(try
|
||||
(if (.-geolocation js/navigator)
|
||||
(.getCurrentPosition
|
||||
(.-geolocation js/navigator)
|
||||
(fn [position]
|
||||
(js/console.log "Current location is: " + position)
|
||||
(dispatch [:set-latitude (.-latitude (.-coords position))])
|
||||
(dispatch [:set-longitude (.-longitude (.-coords position))])))
|
||||
(js/console.log "Geolocation not available"))
|
||||
(catch js/Object any
|
||||
(js/console.log "Exception while trying to access location: " + any))))
|
||||
|
|
|
|||
|
|
@ -29,59 +29,128 @@
|
|||
;;; The atom gets updated by 'events' registered in handler.cljs, q.v.
|
||||
|
||||
(def default-db
|
||||
{;;; the currently selected address, if any.
|
||||
:address {:id 1 :address "13 Imaginary Terrace, IM1 3TE" :latitude 55.8253043 :longitude -4.2569057
|
||||
:dwellings [{:id 1
|
||||
: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
|
||||
:dwellings [{:id 1
|
||||
: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
|
||||
:dwellings [{:id 2
|
||||
:electors [{:id 5 :name "Beryl Brown" :gender :female}
|
||||
{:id 6 :name "Betty Black" :gender :female}]}]}
|
||||
{
|
||||
:addresses
|
||||
[{:locality 548223905,
|
||||
:address
|
||||
"HAZELFIELD HOUSE, CASTLE DOUGLAS, DG7 1RF",
|
||||
:phone nil,
|
||||
:postcode "DG7 1RF",
|
||||
:longitude -3.905045374625994,
|
||||
:district_id 1,
|
||||
:dwellings
|
||||
[{:address_id_expanded
|
||||
"HAZELFIELD HOUSE, CASTLE DOUGLAS, DG7 1RF, DG7 1RF",
|
||||
:address_id 18,
|
||||
:sub_address "",
|
||||
:id 17,
|
||||
:id_2 17,
|
||||
:address_id_2 18,
|
||||
:sub_address_2 "",
|
||||
:electors
|
||||
[{:email nil,
|
||||
:dwelling_id_2 17,
|
||||
:dwelling_id_expanded
|
||||
"HAZELFIELD HOUSE, CASTLE DOUGLAS, DG7 1RF, DG7 1RF, ",
|
||||
:intentions
|
||||
[{:locality 548223905,
|
||||
:visit_id_expanded
|
||||
"HAZELFIELD HOUSE, CASTLE DOUGLAS, DG7 1RF, DG7 1RF, 2018-06-14 20:29:34.721522",
|
||||
:option_id_expanded "Yes",
|
||||
:option_id "Yes",
|
||||
:option_id_2 "Yes",
|
||||
:visit_id_2 1,
|
||||
:elector_id_2 61,
|
||||
:visit_id 1,
|
||||
:elector_id 61,
|
||||
:id 1,
|
||||
:elector_id_expanded nil,
|
||||
:id_2 1}],
|
||||
:phone nil,
|
||||
:phone_2 nil,
|
||||
:gender_expanded "Female",
|
||||
:name "Alice Sutherland",
|
||||
:dwelling_id 17,
|
||||
:id 61,
|
||||
:gender "Female",
|
||||
:gender_2 "Female",
|
||||
:name_2 "Alice Sutherland",
|
||||
:email_2 nil,
|
||||
:id_2 61}
|
||||
{:email nil,
|
||||
:dwelling_id_2 17,
|
||||
:dwelling_id_expanded
|
||||
"HAZELFIELD HOUSE, CASTLE DOUGLAS, DG7 1RF, DG7 1RF, ",
|
||||
:intentions [],
|
||||
:phone nil,
|
||||
:phone_2 nil,
|
||||
:gender_expanded "Female",
|
||||
:name "Charlie Sutherland",
|
||||
:dwelling_id 17,
|
||||
:id 62,
|
||||
:gender "Female",
|
||||
:gender_2 "Female",
|
||||
:name_2 "Charlie Sutherland",
|
||||
:email_2 nil,
|
||||
:id_2 62}
|
||||
{:email nil,
|
||||
:dwelling_id_2 17,
|
||||
:dwelling_id_expanded
|
||||
"HAZELFIELD HOUSE, CASTLE DOUGLAS, DG7 1RF, DG7 1RF, ",
|
||||
:intentions [],
|
||||
:phone nil,
|
||||
:phone_2 nil,
|
||||
:gender_expanded "Male",
|
||||
:name "Keith Sutherland",
|
||||
:dwelling_id 17,
|
||||
:id 64,
|
||||
:gender "Male",
|
||||
:gender_2 "Male",
|
||||
:name_2 "Keith Sutherland",
|
||||
:email_2 nil,
|
||||
:id_2 64}
|
||||
{:email nil,
|
||||
:dwelling_id_2 17,
|
||||
:dwelling_id_expanded
|
||||
"HAZELFIELD HOUSE, CASTLE DOUGLAS, DG7 1RF, DG7 1RF, ",
|
||||
:intentions [],
|
||||
:phone nil,
|
||||
:phone_2 nil,
|
||||
:gender_expanded "Female",
|
||||
:name "Lucy Sutherland",
|
||||
:dwelling_id 17,
|
||||
:id 63,
|
||||
:gender "Female",
|
||||
:gender_2 "Female",
|
||||
:name_2 "Lucy Sutherland",
|
||||
:email_2 nil,
|
||||
:id_2 63}]}],
|
||||
:id 18,
|
||||
:latitude 54.8222716877376}]
|
||||
|
||||
{:id 3 :address "17 Imaginary Terrace, IM1 3TE" :latitude 55.825166 :longitude -4.257026
|
||||
:dwellings [{:id 3 :sub-address "Flat 1"
|
||||
:electors [{:id 7 :name "Catriona Crathie" :gender :female :intention :yes}
|
||||
{:id 8 :name "Colin Caruthers" :gender :male :intention :yes}
|
||||
{:id 9 :name "Calum Crathie" :intention :yes}]}
|
||||
{:id 4 :sub-address "Flat 2"
|
||||
:electors [{:id 1 :name "David Dewar" :gender :male :intention :no}]}]}]
|
||||
;;; the currently selected address, if any.
|
||||
:address nil
|
||||
;;; electors at the currently selected dwelling
|
||||
: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}]
|
||||
:electors nil
|
||||
;;; any error to display
|
||||
:error nil
|
||||
;;; the issue from among the issues which is currently selected.
|
||||
;;; any confirmation message to display
|
||||
:feedback nil
|
||||
:feedback '()
|
||||
;;; the currently selected issue
|
||||
:issue "Currency"
|
||||
:issue nil
|
||||
;;; 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"}
|
||||
:issues nil
|
||||
;;; 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"}]
|
||||
:options nil
|
||||
;;; the queue of items waiting to be transmitted.
|
||||
:outqueue ()
|
||||
;;; the currently displayed page within the app.
|
||||
:page :home
|
||||
:view nil
|
||||
:latitude 55.82
|
||||
:longitude -4.25
|
||||
:latitude 54.82
|
||||
:longitude -3.90
|
||||
:zoom 12})
|
||||
|
||||
|
|
|
|||
|
|
@ -37,15 +37,16 @@
|
|||
[:div.back-link-container {:key (gensym "back-link")}
|
||||
[:a.back-link {:href target} "Back"]]))
|
||||
|
||||
|
||||
(defn big-link
|
||||
[text & {:keys [target handler]}]
|
||||
(js/console.log (str "Constructing big link; target is '" target "'; handler is '" handler "'"))
|
||||
[:div.big-link-container {:key (gensym "big-link")}
|
||||
[:a.big-link (merge
|
||||
(if target {:href target}{})
|
||||
(if handler {:on-click handler}{}))
|
||||
text]])
|
||||
|
||||
|
||||
(defn nav-link [uri title page collapsed?]
|
||||
(let [selected-page @(rf/subscribe [:page])]
|
||||
[:li.nav-item
|
||||
|
|
|
|||
|
|
@ -50,12 +50,10 @@
|
|||
(defn gender-cell
|
||||
[elector]
|
||||
(let [gender (:gender elector)
|
||||
image (if gender (name gender) "unknown")]
|
||||
image (if gender (name gender) "Unknown")]
|
||||
[:td {:key (str "gender-" (:id elector))}
|
||||
[:a {:href (str "#gdpr/" (:id elector))}
|
||||
[:img {:src (str "img/gender/" image ".png") :alt image
|
||||
;; :on-click #(go-to-gdpr-for-elector elector)
|
||||
}]]]))
|
||||
[:img {:src (str "img/gender/" image ".png") :alt image}]]]))
|
||||
|
||||
|
||||
(defn genders-row
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@
|
|||
[elector]
|
||||
[:tr
|
||||
[:td {:key (:id elector)}
|
||||
[:a {:href (str "#/issues/" (:id elector))}
|
||||
[:a {:href (str "#issues/" (:id elector))}
|
||||
[:img {:src "img/issues.png" :alt "Issues"}]]]])
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -38,14 +38,13 @@
|
|||
(defn panel
|
||||
"Generate the issue panel."
|
||||
[]
|
||||
(let [issue @(subscribe [:issue])
|
||||
issues @(subscribe [:issues])]
|
||||
(let [issue @(subscribe [:issue])]
|
||||
[:div
|
||||
[:h1 issue]
|
||||
[:h1 (:id issue)]
|
||||
[:div.container {:id "main-container"}
|
||||
[:div {:id "issue"}
|
||||
[:div {:id "issue-text"
|
||||
:dangerouslySetInnerHTML
|
||||
{:__html (md->html (issues issue))}}]]
|
||||
{:__html (md->html (:brief issue))}}]]
|
||||
(ui/big-link "Request call" :target "#/followup")
|
||||
(ui/back-link)]]))
|
||||
|
|
|
|||
|
|
@ -46,5 +46,5 @@
|
|||
[:div.container {:id "main-container"}
|
||||
(ui/back-link)
|
||||
[:div {:id "issue-list"}
|
||||
(map (fn [k] (ui/big-link k :target (str "#/issue/" k))) (keys issues))]]]
|
||||
(map (fn [i] (ui/big-link (:id i) :target (str "#issue/" (:id i)))) issues)]]]
|
||||
(ui/error-panel "No issues loaded"))))
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
(ns ^{:doc "Canvasser app map view panel."
|
||||
:author "Simon Brooke"}
|
||||
youyesyet.canvasser-app.views.map
|
||||
(:require [re-frame.core :refer [reg-sub subscribe dispatch]]
|
||||
(:require [re-frame.core :refer [reg-sub subscribe dispatch dispatch-sync]]
|
||||
[reagent.core :as reagent]
|
||||
[youyesyet.canvasser-app.handlers :refer [get-current-location]]))
|
||||
[youyesyet.canvasser-app.handlers :refer [get-current-location refresh-map-pins]]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;
|
||||
|
|
@ -54,66 +54,6 @@
|
|||
(def osm-url "http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png")
|
||||
(def osm-attrib "Map data © <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
|
||||
(mapcat :electors
|
||||
(:dwellings 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))
|
||||
(let [view @(subscribe [:view])
|
||||
centre (.getCenter view)]
|
||||
(dispatch [:set-zoom (.getZoom view)])
|
||||
(dispatch [:set-latitude (.-lat centre)])
|
||||
(dispatch [:set-longitude (.-lng centre)]))
|
||||
(set! window.location.href (str "#building/" 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
|
||||
{:iconAnchor [16 41]
|
||||
:iconSize [32 42]
|
||||
:iconUrl (str "img/map-pins/" (pin-image address) ".png")
|
||||
:riseOnHover true
|
||||
:shadowAnchor [16 23]
|
||||
:shadowSize [57 24]
|
||||
:shadowUrl "img/map-pins/shadow_pin.png"}))
|
||||
marker (.marker js/L
|
||||
(.latLng js/L lat lng)
|
||||
(clj->js {:icon pin
|
||||
:title (:address address)}))
|
||||
]
|
||||
|
||||
(.on (.addTo marker view) "click" (fn [_] (map-pin-click-handler (str (:id address)))))
|
||||
))
|
||||
|
||||
|
||||
;; My gods mapbox is user-hostile!
|
||||
(defn map-did-mount-mapbox
|
||||
"Did-mount function loading map tile data from MapBox (proprietary)."
|
||||
|
|
@ -123,8 +63,8 @@
|
|||
;; 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 © [...]"
|
||||
:maxZoom 18}))
|
||||
view)))
|
||||
:maxZoom 18})))
|
||||
view))
|
||||
|
||||
|
||||
(defn map-did-mount-osm
|
||||
|
|
@ -132,28 +72,24 @@
|
|||
[]
|
||||
(get-current-location)
|
||||
(let [view (.setView
|
||||
(.map js/L "map" (clj->js {:zoomControl false}))
|
||||
(.map js/L
|
||||
"map"
|
||||
(clj->js {:zoomControl false}))
|
||||
#js [@(subscribe [:latitude]) @(subscribe [:longitude])]
|
||||
@(subscribe [:zoom]))
|
||||
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}))
|
||||
view)
|
||||
(dispatch [:set-view view])
|
||||
@(subscribe [:zoom]))]
|
||||
(dispatch-sync [:set-view view])
|
||||
(refresh-map-pins)
|
||||
view))
|
||||
|
||||
|
||||
(defn map-did-mount
|
||||
"Select the actual map provider to use."
|
||||
[]
|
||||
(case *map-provider*
|
||||
:mapbox (map-did-mount-mapbox)
|
||||
:osm (map-did-mount-osm))
|
||||
;; potentially others
|
||||
)
|
||||
(dispatch-sync [:set-view (case *map-provider*
|
||||
:mapbox (map-did-mount-mapbox)
|
||||
:osm (map-did-mount-osm)
|
||||
;; potentially others
|
||||
)]))
|
||||
|
||||
|
||||
(defn map-render
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue