From b832409dfaef5e21d27f0d63a645b8412742c988 Mon Sep 17 00:00:00 2001 From: gered Date: Wed, 12 Jan 2022 16:53:20 -0500 Subject: [PATCH] remove browserchannel stuff it's old and now that websockets is everywhere, it is unneeded --- examples/todomvc-browserchannel/.gitignore | 18 - examples/todomvc-browserchannel/README.md | 48 -- examples/todomvc-browserchannel/create_db.sql | 21 - examples/todomvc-browserchannel/project.clj | 56 -- .../resources/public/todos.css | 558 ------------------ .../resources/public/todosanim.css | 18 - .../src/todomvc/client.cljs | 180 ------ .../src/todomvc/server.clj | 188 ------ views.reagent.browserchannel/project.clj | 22 - .../views/reagent/browserchannel/client.cljs | 33 -- .../views/reagent/browserchannel/server.clj | 52 -- 11 files changed, 1194 deletions(-) delete mode 100644 examples/todomvc-browserchannel/.gitignore delete mode 100644 examples/todomvc-browserchannel/README.md delete mode 100644 examples/todomvc-browserchannel/create_db.sql delete mode 100644 examples/todomvc-browserchannel/project.clj delete mode 100644 examples/todomvc-browserchannel/resources/public/todos.css delete mode 100644 examples/todomvc-browserchannel/resources/public/todosanim.css delete mode 100644 examples/todomvc-browserchannel/src/todomvc/client.cljs delete mode 100644 examples/todomvc-browserchannel/src/todomvc/server.clj delete mode 100644 views.reagent.browserchannel/project.clj delete mode 100644 views.reagent.browserchannel/src/views/reagent/browserchannel/client.cljs delete mode 100644 views.reagent.browserchannel/src/views/reagent/browserchannel/server.clj diff --git a/examples/todomvc-browserchannel/.gitignore b/examples/todomvc-browserchannel/.gitignore deleted file mode 100644 index c7c1135..0000000 --- a/examples/todomvc-browserchannel/.gitignore +++ /dev/null @@ -1,18 +0,0 @@ -.DS_Store -/target -/classes -/checkouts -pom.xml -pom.xml.asc -*.jar -*.class -/.lein-* -/.nrepl-port -.settings/ -.project -.classpath -.idea/ -*.iml -*.ipr -*.iws -/resources/public/cljs diff --git a/examples/todomvc-browserchannel/README.md b/examples/todomvc-browserchannel/README.md deleted file mode 100644 index f3adcc9..0000000 --- a/examples/todomvc-browserchannel/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# views.reagent Example - Todo MVC (BrowserChannel) - -This is a modification of the Todo MVC app for Reagent [demonstrated here][1]. -This version of the app has been modified to use a PostgreSQL database -to store the Todos and to provide realtime synchronization of changes -to that data to any number of users currently viewing the app. - -[1]: http://reagent-project.github.io/ - -> **NOTE:** This is a copy of the [other Todo MVC example][2] and is the same -> in every respect, except that this one is using [BrowserChannel][3] instead of -> Sente as the underlying client/server messaging implementation. - -[2]: https://github.com/gered/views.reagent/tree/master/examples/todomvc -[3]: https://github.com/gered/views.reagent/tree/master/views.reagent.browserchannel - -## Running - -### Creating the Database - -This example app uses a PostgreSQL database. The SQL script to create -it is in `create_db.sql`. You can easily pipe it into `psql` at a -command line to create it quickly, for example: - - $ psql < create_db.sql - -(Of course, add any username/host parameters you might need) - -### Starting It Up - -To build everything and run in one step: - - $ lein rundemo - -Then open up a web browser or two and head to http://localhost:8080/ -to see the web app in action. - -If you want to run this application in a REPL, just be sure to build -the ClojureScript: - - $ lein cljsbuild once - -And then in the REPL you can just run: - - (-main) - -to start the web app (you should be put in the correct namespace -immediately). diff --git a/examples/todomvc-browserchannel/create_db.sql b/examples/todomvc-browserchannel/create_db.sql deleted file mode 100644 index ec23eef..0000000 --- a/examples/todomvc-browserchannel/create_db.sql +++ /dev/null @@ -1,21 +0,0 @@ --- For PostgreSQL --- run with psql. e.g. 'psql < create_db.sql' - -CREATE ROLE todomvc LOGIN PASSWORD 's3cr3t'; -CREATE DATABASE todomvc OWNER todomvc; - --- assumes you're piping this script into psql ... -\c todomvc; - -CREATE TABLE todos -( - id SERIAL PRIMARY KEY NOT NULL, - title TEXT NOT NULL, - done BOOLEAN DEFAULT FALSE NOT NULL -); -ALTER TABLE todos OWNER TO todomvc; - -INSERT INTO todos (title, done) VALUES ('Rename Cloact to Reagent', TRUE); -INSERT INTO todos (title, done) VALUES ('Add undo demo', TRUE); -INSERT INTO todos (title, done) VALUES ('Make all rendering async', TRUE); -INSERT INTO todos (title, done) VALUES ('Allow any arguments to component functions', TRUE); diff --git a/examples/todomvc-browserchannel/project.clj b/examples/todomvc-browserchannel/project.clj deleted file mode 100644 index 6865f31..0000000 --- a/examples/todomvc-browserchannel/project.clj +++ /dev/null @@ -1,56 +0,0 @@ -(defproject todomvc-browserchannel "0.1.0-SNAPSHOT" - :dependencies [[org.clojure/clojure "1.8.0"] - [org.clojure/clojurescript "1.8.51"] - [ring "1.4.0"] - [ring/ring-defaults "0.2.0" :exclusions [javax.servlet/servlet-api]] - [compojure "1.4.0"] - [org.immutant/web "2.1.4"] - - [org.clojure/java.jdbc "0.6.1"] - [org.postgresql/postgresql "9.4.1208.jre7"] - [gered/clj-browserchannel "0.3.2"] - [gered/clj-browserchannel-immutant-adapter "0.0.3"] - [gered/views "1.5"] - [gered/views.sql "0.1"] - [gered/views.reagent "0.1"] - [gered/views.reagent.browserchannel "0.1"] - - [hiccup "1.0.5"] - [reagent "0.6.0-alpha2"] - [cljs-ajax "0.5.4"] - - [environ "1.0.3"]] - - :plugins [[lein-cljsbuild "1.1.3"] - [lein-environ "1.0.3"]] - - :main todomvc.server - - :clean-targets ^{:protect false} [:target-path - [:cljsbuild :builds :main :compiler :output-dir] - [:cljsbuild :builds :main :compiler :output-to]] - :cljsbuild {:builds {:main - {:source-paths ["src"] - :compiler {:main todomvc.client - :output-to "resources/public/cljs/app.js" - :output-dir "resources/public/cljs/target" - :asset-path "cljs/target" - :source-map true - :optimizations :none - :pretty-print true}}}} - - :profiles {:dev {:env {:dev "true"}} - - :uberjar {:env {} - :aot :all - :hooks [leiningen.cljsbuild] - :cljsbuild {:jar true - :builds {:main - {:compiler ^:replace {:output-to "resources/public/cljs/app.js" - :optimizations :advanced - :pretty-print false}}}}}} - - :aliases {"rundemo" ["do" ["clean"] ["cljsbuild" "once"] ["run"]] - "uberjar" ["do" ["clean"] ["uberjar"]]} - - ) diff --git a/examples/todomvc-browserchannel/resources/public/todos.css b/examples/todomvc-browserchannel/resources/public/todos.css deleted file mode 100644 index 9095474..0000000 --- a/examples/todomvc-browserchannel/resources/public/todos.css +++ /dev/null @@ -1,558 +0,0 @@ -@charset "utf-8"; -html, -body { - margin: 0; - padding: 0; -} - -button { - margin: 0; - padding: 0; - border: 0; - background: none; - font-size: 100%; - vertical-align: baseline; - font-family: inherit; - color: inherit; - -webkit-appearance: none; - -ms-appearance: none; - -o-appearance: none; - appearance: none; -} - -body { - font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; - line-height: 1.4em; - background: #eaeaea; - /* background: #eaeaea url('bg.png'); */ - color: #4d4d4d; - width: 550px; - margin: 0 auto; - -webkit-font-smoothing: antialiased; - -moz-font-smoothing: antialiased; - -ms-font-smoothing: antialiased; - -o-font-smoothing: antialiased; - font-smoothing: antialiased; -} - -button, -input[type="checkbox"] { - outline: none; -} - -#todoapp { - background: #fff; - background: rgba(255, 255, 255, 0.9); - margin: 130px 0 40px 0; - border: 1px solid #ccc; - position: relative; - border-top-left-radius: 2px; - border-top-right-radius: 2px; - box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2), - 0 25px 50px 0 rgba(0, 0, 0, 0.15); -} - -#todoapp:before { - content: ''; - border-left: 1px solid #f5d6d6; - border-right: 1px solid #f5d6d6; - width: 2px; - position: absolute; - top: 0; - left: 40px; - height: 100%; -} - -#todoapp input::-webkit-input-placeholder { - font-style: italic; -} - -#todoapp input::-moz-placeholder { - font-style: italic; - color: #a9a9a9; -} - -#todoapp h1 { - position: absolute; - top: -120px; - width: 100%; - font-size: 70px; - font-weight: bold; - text-align: center; - color: #b3b3b3; - color: rgba(255, 255, 255, 0.3); - text-shadow: -1px -1px rgba(0, 0, 0, 0.2); - -webkit-text-rendering: optimizeLegibility; - -moz-text-rendering: optimizeLegibility; - -ms-text-rendering: optimizeLegibility; - -o-text-rendering: optimizeLegibility; - text-rendering: optimizeLegibility; -} - -#header { - padding-top: 15px; - border-radius: inherit; -} - -#header:before { - content: ''; - position: absolute; - top: 0; - right: 0; - left: 0; - height: 15px; - z-index: 2; - border-bottom: 1px solid #6c615c; - background: #8d7d77; - background: -webkit-gradient(linear, left top, left bottom, from(rgba(132, 110, 100, 0.8)),to(rgba(101, 84, 76, 0.8))); - background: -webkit-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); - background: linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); - filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670'); - border-top-left-radius: 1px; - border-top-right-radius: 1px; -} - -#new-todo, -.edit { - position: relative; - margin: 0; - width: 100%; - font-size: 24px; - font-family: inherit; - line-height: 1.4em; - border: 0; - outline: none; - color: inherit; - padding: 6px; - border: 1px solid #999; - box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); - -moz-box-sizing: border-box; - -ms-box-sizing: border-box; - -o-box-sizing: border-box; - box-sizing: border-box; - -webkit-font-smoothing: antialiased; - -moz-font-smoothing: antialiased; - -ms-font-smoothing: antialiased; - -o-font-smoothing: antialiased; - font-smoothing: antialiased; -} - -#new-todo { - padding: 16px 16px 16px 60px; - border: none; - background: rgba(0, 0, 0, 0.02); - z-index: 2; - box-shadow: none; -} - -#main { - position: relative; - z-index: 2; - border-top: 1px dotted #adadad; -} - -label[for='toggle-all'] { - display: none; -} - -#toggle-all { - position: absolute; - top: -42px; - left: -4px; - width: 40px; - text-align: center; - /* Mobile Safari */ - border: none; -} - -#toggle-all:before { - content: '»'; - font-size: 28px; - color: #d9d9d9; - padding: 0 25px 7px; -} - -#toggle-all:checked:before { - color: #737373; -} - -#todo-list { - margin: 0; - padding: 0; - list-style: none; -} - -#todo-list li { - position: relative; - font-size: 24px; - border-bottom: 1px dotted #ccc; -} - -#todo-list li:last-child { - border-bottom: none; -} - -#todo-list li.editing { - border-bottom: none; - padding: 0; -} - -#todo-list li.editing .edit { - display: block; - width: 506px; - padding: 13px 17px 12px 17px; - margin: 0 0 0 43px; -} - -#todo-list li.editing .view { - display: none; -} - -#todo-list li .toggle { - text-align: center; - width: 40px; - /* auto, since non-WebKit browsers doesn't support input styling */ - height: auto; - position: absolute; - top: 0; - bottom: 0; - margin: auto 0; - /* Mobile Safari */ - border: none; - -webkit-appearance: none; - -ms-appearance: none; - -o-appearance: none; - appearance: none; -} - -#todo-list li .toggle:after { - content: '✔'; - /* 40 + a couple of pixels visual adjustment */ - line-height: 43px; - font-size: 20px; - color: #d9d9d9; - text-shadow: 0 -1px 0 #bfbfbf; -} - -#todo-list li .toggle:checked:after { - color: #85ada7; - text-shadow: 0 1px 0 #669991; - bottom: 1px; - position: relative; -} - -#todo-list li label { - white-space: pre; - word-break: break-word; - padding: 15px 60px 15px 15px; - margin-left: 45px; - display: block; - line-height: 1.2; - -webkit-transition: color 0.4s; - transition: color 0.4s; -} - -#todo-list li.completed label { - color: #a9a9a9; - text-decoration: line-through; -} - -#todo-list li .destroy { - display: none; - position: absolute; - top: 0; - right: 10px; - bottom: 0; - width: 40px; - height: 40px; - margin: auto 0; - font-size: 22px; - color: #a88a8a; - -webkit-transition: all 0.2s; - transition: all 0.2s; -} - -#todo-list li .destroy:hover { - text-shadow: 0 0 1px #000, - 0 0 10px rgba(199, 107, 107, 0.8); - -webkit-transform: scale(1.3); - -ms-transform: scale(1.3); - transform: scale(1.3); -} - -#todo-list li .destroy:after { - content: '✖'; -} - -#todo-list li:hover .destroy { - display: block; -} - -#todo-list li .edit { - display: none; -} - -#todo-list li.editing:last-child { - margin-bottom: -1px; -} - -#footer { - color: #777; - padding: 0 15px; - position: absolute; - right: 0; - bottom: -31px; - left: 0; - height: 20px; - z-index: 1; - text-align: center; -} - -#footer:before { - content: ''; - position: absolute; - right: 0; - bottom: 31px; - left: 0; - height: 50px; - z-index: -1; - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3), - 0 6px 0 -3px rgba(255, 255, 255, 0.8), - 0 7px 1px -3px rgba(0, 0, 0, 0.3), - 0 43px 0 -6px rgba(255, 255, 255, 0.8), - 0 44px 2px -6px rgba(0, 0, 0, 0.2); -} - -#todo-count { - float: left; - text-align: left; -} - -#filters { - margin: 0; - padding: 0; - list-style: none; - position: absolute; - right: 0; - left: 0; -} - -#filters li { - display: inline; -} - -#filters li a { - color: #83756f; - margin: 2px; - text-decoration: none; -} - -#filters li a.selected { - font-weight: bold; -} - -#clear-completed { - float: right; - position: relative; - line-height: 20px; - text-decoration: none; - background: rgba(0, 0, 0, 0.1); - font-size: 11px; - padding: 0 10px; - border-radius: 3px; - box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.2); -} - -#clear-completed:hover { - background: rgba(0, 0, 0, 0.15); - box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.3); -} - -#info { - margin: 65px auto 0; - color: #a6a6a6; - font-size: 12px; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7); - text-align: center; -} - -#info a { - color: inherit; -} - -/* - Hack to remove background from Mobile Safari. - Can't use it globally since it destroys checkboxes in Firefox and Opera -*/ - -@media screen and (-webkit-min-device-pixel-ratio:0) { - #toggle-all, - #todo-list li .toggle { - background: none; - } - - #todo-list li .toggle { - height: 40px; - } - - #toggle-all { - top: -56px; - left: -15px; - width: 65px; - height: 41px; - -webkit-transform: rotate(90deg); - -ms-transform: rotate(90deg); - transform: rotate(90deg); - -webkit-appearance: none; - appearance: none; - } -} - -.hidden { - display: none; -} - -hr { - margin: 20px 0; - border: 0; - border-top: 1px dashed #C5C5C5; - border-bottom: 1px dashed #F7F7F7; -} - -.learn a { - font-weight: normal; - text-decoration: none; - color: #b83f45; -} - -.learn a:hover { - text-decoration: underline; - color: #787e7e; -} - -.learn h3, -.learn h4, -.learn h5 { - margin: 10px 0; - font-weight: 500; - line-height: 1.2; - color: #000; -} - -.learn h3 { - font-size: 24px; -} - -.learn h4 { - font-size: 18px; -} - -.learn h5 { - margin-bottom: 0; - font-size: 14px; -} - -.learn ul { - padding: 0; - margin: 0 0 30px 25px; -} - -.learn li { - line-height: 20px; -} - -.learn p { - font-size: 15px; - font-weight: 300; - line-height: 1.3; - margin-top: 0; - margin-bottom: 0; -} - -.quote { - border: none; - margin: 20px 0 60px 0; -} - -.quote p { - font-style: italic; -} - -.quote p:before { - content: '“'; - font-size: 50px; - opacity: .15; - position: absolute; - top: -20px; - left: 3px; -} - -.quote p:after { - content: '”'; - font-size: 50px; - opacity: .15; - position: absolute; - bottom: -42px; - right: 3px; -} - -.quote footer { - position: absolute; - bottom: -40px; - right: 0; -} - -.quote footer img { - border-radius: 3px; -} - -.quote footer a { - margin-left: 5px; - vertical-align: middle; -} - -.speech-bubble { - position: relative; - padding: 10px; - background: rgba(0, 0, 0, .04); - border-radius: 5px; -} - -.speech-bubble:after { - content: ''; - position: absolute; - top: 100%; - right: 30px; - border: 13px solid transparent; - border-top-color: rgba(0, 0, 0, .04); -} - -.learn-bar > .learn { - position: absolute; - width: 272px; - top: 8px; - left: -300px; - padding: 10px; - border-radius: 5px; - background-color: rgba(255, 255, 255, .6); - -webkit-transition-property: left; - transition-property: left; - -webkit-transition-duration: 500ms; - transition-duration: 500ms; -} - -@media (min-width: 899px) { - .learn-bar { - width: auto; - margin: 0 0 0 300px; - } - - .learn-bar > .learn { - left: 8px; - } - - .learn-bar #todoapp { - width: 550px; - margin: 130px auto 40px auto; - } -} \ No newline at end of file diff --git a/examples/todomvc-browserchannel/resources/public/todosanim.css b/examples/todomvc-browserchannel/resources/public/todosanim.css deleted file mode 100644 index 999d534..0000000 --- a/examples/todomvc-browserchannel/resources/public/todosanim.css +++ /dev/null @@ -1,18 +0,0 @@ - -.todoitem-enter { - opacity: 0.1; - transition: opacity .2s ease-in; -} - -.todoitem-enter.todoitem-enter-active { - opacity: 1; -} - -.todoitem-leave { - opacity: 0.8; - transition: opacity 0.2s ease-out; -} - -.todoitem-leave.todoitem-leave-active { - opacity: 0.1; -} \ No newline at end of file diff --git a/examples/todomvc-browserchannel/src/todomvc/client.cljs b/examples/todomvc-browserchannel/src/todomvc/client.cljs deleted file mode 100644 index 215ff34..0000000 --- a/examples/todomvc-browserchannel/src/todomvc/client.cljs +++ /dev/null @@ -1,180 +0,0 @@ -(ns todomvc.client - (:require - [reagent.core :as r] - [ajax.core :refer [POST default-interceptors to-interceptor]] - [net.thegeez.browserchannel.client :as browserchannel] - [views.reagent.client.component :refer [view-cursor] :refer-macros [defvc]] - [views.reagent.browserchannel.client :as vr])) - -;; Todo MVC - views.reagent example app -;; -;; This is taken from the example code shown on http://reagent-project.github.io/ -;; It has been modified so that instead of using todo data stored client-side in -;; an atom, the data is retrieved from the server. -;; -;; AJAX requests are used to add/edit/delete the todos. The list is refreshed -;; whenever a change is made (by any client currently viewing the app) by a -;; view subscription. See the 'todo-app' component near the bottom-middle of this -;; file for more details about this. - - - -;; AJAX operations - -(defn add-todo [text] (POST "/todos/add" {:format :url :params {:title text}})) -(defn toggle [id] (POST "/todos/toggle" {:format :url :params {:id id}})) -(defn save [id title] (POST "/todos/update" {:format :url :params {:id id :title title}})) -(defn delete [id] (POST "/todos/delete" {:format :url :params {:id id}})) - -(defn complete-all [v] (POST "/todos/mark-all" {:format :url :params {:done? v}})) -(defn clear-done [] (POST "/todos/delete-all-done")) - - - -;; UI Components - -(defn todo-input - [{:keys [title on-save on-stop]}] - (let [val (r/atom title) - stop #(do (reset! val "") - (if on-stop (on-stop))) - save #(let [v (-> @val str clojure.string/trim)] - (if-not (empty? v) (on-save v)) - (stop))] - (fn [props] - [:input (merge props - {:type "text" :value @val :on-blur save - :on-change #(reset! val (-> % .-target .-value)) - :on-key-down #(case (.-which %) - 13 (save) - 27 (stop) - nil)})]))) - -(def todo-edit (with-meta todo-input - {:component-did-mount #(.focus (r/dom-node %))})) - -(defn todo-stats - [{:keys [filt active done]}] - (let [props-for (fn [name] - {:class (if (= name @filt) "selected") - :on-click #(reset! filt name)})] - [:div - [:span#todo-count - [:strong active] " " (case active 1 "item" "items") " left"] - [:ul#filters - [:li [:a (props-for :all) "All"]] - [:li [:a (props-for :active) "Active"]] - [:li [:a (props-for :done) "Completed"]]] - (when (pos? done) - [:button#clear-completed {:on-click clear-done} - "Clear completed " done])])) - -(defn todo-item - [] - (let [editing (r/atom false)] - (fn [{:keys [id done title]}] - [:li {:class (str (if done "completed ") - (if @editing "editing"))} - [:div.view - [:input.toggle {:type "checkbox" :checked done - :on-change #(toggle id)}] - [:label {:on-double-click #(reset! editing true)} title] - [:button.destroy {:on-click #(delete id)}]] - (when @editing - [todo-edit {:class "edit" :title title - :on-save #(save id %) - :on-stop #(reset! editing false)}])]))) - - - -;; Main TODO app component -;; -;; Note that this component is defined using 'defvc' instead of 'defn'. This is a -;; macro provided by views.reagent which is required to be used by any Reagent -;; component that will directly subscribe/unsubscribe to views. It handles all the -;; housekeeping operations that working with views on the client entails. -;; -;; The call to 'view-cursor' is where the rest of the magic happens. This function -;; will: -;; -;; - Send a subscription request to the server for the specified view and parameters -;; if a subscription for the view (and the exact provided parameters) does not -;; already exist. -;; - Returns the most recent data for this view in a Reagent cursor. When the data -;; is changed and the server sends a view refresh, components dereferencing this -;; cursor will be rerendered, just like any other Reagent atom/cursor. -;; - If the values of the (optional) parameters passed to view-cursor change, a -;; view resubscription (with the new parameters) will be triggered automatically -;; and the server will send us new view data. -;; -;; NOTE: -;; view-cursor cannot be used in a Reagent component that was created using defn. - -(defvc todo-app - [props] - (let [filt (r/atom :all)] - (fn [] - (let [items (view-cursor :todos) - done (->> @items (filter :done) count) - active (- (count @items) done)] - [:div - [:section#todoapp - [:header#header - [:h1 "todos"] - [todo-input {:id "new-todo" - :placeholder "What needs to be done?" - :on-save add-todo}]] - (when (-> @items count pos?) - [:div - [:section#main - [:input#toggle-all {:type "checkbox" :checked (zero? active) - :on-change #(complete-all (pos? active))}] - [:label {:for "toggle-all"} "Mark all as complete"] - [:ul#todo-list - (for [todo (->> @items - (filter - (case @filt - :active (complement :done) - :done :done - :all identity)) - (sort-by :id))] - ^{:key (:id todo)} [todo-item todo])]] - [:footer#footer - [todo-stats {:active active :done done :filt filt}]]])] - [:footer#info - [:p "Double-click to edit a todo"]]])))) - - - -;; Some unfortunately necessary set up to ensure we send the CSRF token back with -;; AJAX requests - -(defn get-anti-forgery-token - [] - (if-let [hidden-field (.getElementById js/document "__anti-forgery-token")] - (.-value hidden-field))) - -(def csrf-interceptor - (to-interceptor {:name "CSRF Interceptor" - :request #(assoc-in % [:headers "X-CSRF-Token"] (get-anti-forgery-token))})) - -(swap! default-interceptors (partial cons csrf-interceptor)) - - - -;; Page load - -(defn ^:export run - [] - ; Initialize views.reagent - (vr/init!) - - ; Initialize BrowserChannel - ; NOTE: We are passing in an empty event handler map to connect! only because - ; this todo app is not using BrowserChannel for any purpose other then to - ; provide client/server messaging for views.reagent. If we wanted to use it - ; for client/server messaging in our application as well, we could pass in - ; any event handlers we want here and it would not intefere with views.reagent. - (browserchannel/connect! {} {:middleware [vr/middleware]}) - - (r/render-component [todo-app] (.getElementById js/document "app"))) diff --git a/examples/todomvc-browserchannel/src/todomvc/server.clj b/examples/todomvc-browserchannel/src/todomvc/server.clj deleted file mode 100644 index 6e3be74..0000000 --- a/examples/todomvc-browserchannel/src/todomvc/server.clj +++ /dev/null @@ -1,188 +0,0 @@ -(ns todomvc.server - (:gen-class) - (:require - [compojure.core :refer [routes GET POST]] - [compojure.route :as route] - [ring.middleware.defaults :refer [wrap-defaults site-defaults]] - [ring.util.anti-forgery :refer [anti-forgery-field]] - [ring.util.response :refer [response]] - [net.thegeez.browserchannel.server :refer [wrap-browserchannel]] - [net.thegeez.browserchannel.immutant-async-adapter :refer [wrap-immutant-async-adapter]] - [immutant.web :as immutant] - [hiccup.page :refer [html5 include-css include-js]] - [hiccup.element :refer [javascript-tag]] - [environ.core :refer [env]] - [clojure.java.jdbc :as jdbc] - [views.sql.core :refer [vexec! with-view-transaction]] - [views.sql.view :refer [view]] - [views.reagent.browserchannel.server :as vr])) - -(def dev? (boolean (env :dev))) - -(def db {:classname "org.postgresql.Driver" - :subprotocol "postgresql" - :subname "//localhost/todomvc" - :user "todomvc" - :password "s3cr3t"}) - - - -;; View system atom -;; -;; We just declare it, don't need to fill it with anything. The call below to -;; views.reagent.browserchannel.server/init! will take care of it. - -(defonce view-system (atom {})) - - - -;; View functions. -;; -;; These are functions which accept any number of parameters provided when the view -;; is subscribed to and run whenever a subscriber needs refreshed data for it. -;; -;; A view function's return value requirement depends on what views IView -;; implementation is being used. -;; -;; This example app is using views.sql, so view templates should return a SQL SELECT -;; query in a clojure.java.jdbc "sqlvec" which is a vector where the first string is -;; the actual SQL query and is followed by any number of parameters to be used in -;; the query. - -(defn todos-list - [] - ["SELECT id, title, done FROM todos ORDER BY title"]) - - - -;; Views list. -;; -;; A definition/declaration of the views in the system. Each view is given an id and -;; points to a function that returns the query that will be used to retrieve the view's -;; data. Also other properties can be provided to each view, such as the database connection. -;; -;; The view id and parameters to the view function get used later on to form a -;; "view signature" or "view-sig" when the client subscribes to a view. - -(def views - [(view :todos db #'todos-list)]) - - - -;; SQL operations triggered by AJAX requests. -;; -;; These functions are just your ordinary AJAX request handlers that do the various -;; CRUD operations on the example app's data. The only difference is that instead -;; of using clojure.java.jdbc/execute!, we instead use vexec!. -;; -;; vexec! performs the exact same operation as execute!, except that it also -;; analyzes the SQL query being run and dispatches "hints" to the view system which -;; trigger view refrehses for all subscribers of the views that the hints match. - -(defn add-todo! - [title] - (vexec! view-system db ["INSERT INTO todos (title) VALUES (?)" title]) - (response "ok")) - -(defn delete-todo! - [id] - (vexec! view-system db ["DELETE FROM todos WHERE id = ?" id]) - (response "ok")) - -(defn update-todo! - [id title] - (vexec! view-system db ["UPDATE todos SET title = ? WHERE id = ?" title id]) - (response "ok")) - -(defn toggle-todo! - [id] - ; note that a transaction is obviously not necessary here as we could have used - ; just a single UPDATE query. however, it is being done this way to demonstrate - ; using transactions with vexec!. - (with-view-transaction - view-system - [dt db] - (let [done? (:done (first (jdbc/query dt ["SELECT done FROM todos WHERE id = ?" id])))] - (vexec! view-system dt ["UPDATE todos SET done = ? WHERE id = ?" (not done?) id])) - (response "ok"))) - -(defn mark-all! - [done?] - (vexec! view-system db ["UPDATE todos SET done = ?" done?]) - (response "ok")) - -(defn delete-all-done! - [] - (vexec! view-system db ["DELETE FROM todos WHERE done = true"]) - (response "ok")) - - - -;; main page html - -(defn render-page - [] - (html5 - [:head - [:title "TodoMVC - views.reagent Example"] - (include-css "todos.css" "todosanim.css") - (include-js "cljs/app.js")] - [:body - (anti-forgery-field) - [:div#app [:h1 "This will become todomvc when the ClojureScript is compiled"]] - (javascript-tag "todomvc.client.run();")])) - - - -;; Compojure routes and Ring handler - -(def app-routes - (routes - ; db action routes - (POST "/todos/add" [title] (add-todo! title)) - (POST "/todos/delete" [id] (delete-todo! (Integer/parseInt id))) - (POST "/todos/update" [id title] (update-todo! (Integer/parseInt id) title)) - (POST "/todos/toggle" [id] (toggle-todo! (Integer/parseInt id))) - (POST "/todos/mark-all" [done?] (mark-all! (Boolean/parseBoolean done?))) - (POST "/todos/delete-all-done" [] (delete-all-done!)) - - ; main page - (GET "/" [] (render-page)) - - (route/resources "/") - (route/not-found "not found"))) - -; NOTE: We are passing in an empty event handler map to wrap-browserchannel only -; because this todo app is not using BrowserChannel for any purpose other -; then to provide client/server messaging for views.reagent. If we -; wanted to use it for client/server messaging in our application as well, -; we could pass in any event handlers we want here and it would not intefere -; with views.reagent. - -(def handler - (-> app-routes - (wrap-defaults (assoc-in site-defaults [:security :anti-forgery] (not dev?))) - (wrap-browserchannel {} {:middleware [(vr/->middleware view-system)]}) - (wrap-immutant-async-adapter))) - - - -;; Web server startup & main - -(defn run-server - [] - ; views.reagent.browserchannel.server/init! takes care of initialization of views - ; and views.reagent at the same time. As a result, we do not need to also call - ; views.core/init! anywhere. The same arguments and options you are able to pass to - ; views.core/init! can also be passed in here and they will be forwarded along, as - ; this function is intended to be a drop-in replacement for views.core/init!. - ; - ; if you need to shutdown the views system (e.g. if you're using something like - ; Component or Mount), you can just call views.core/shutdown!. - (vr/init! view-system {:views views}) - - (immutant/run handler {:port 8080})) - -(defn -main - [& args] - (run-server)) diff --git a/views.reagent.browserchannel/project.clj b/views.reagent.browserchannel/project.clj deleted file mode 100644 index 8b7e60d..0000000 --- a/views.reagent.browserchannel/project.clj +++ /dev/null @@ -1,22 +0,0 @@ -(defproject gered/views.reagent.browserchannel "0.2-SNAPSHOT" - :description "BrowserChannel client/server messaging adapter for views.reagent." - :url "https://github.com/gered/views.reagent" - :license {:name "MIT License" - :url "http://opensource.org/licenses/MIT"} - - :dependencies [[org.clojure/clojure "1.8.0"]] - - :plugins [[lein-cljsbuild "1.1.3"]] - - :profiles {:provided - {:dependencies - [[org.clojure/clojure "1.8.0"] - [org.clojure/clojurescript "1.8.51"] - [reagent "0.6.0-alpha"] - [gered/views "1.5"] - [gered/views.reagent "0.1"] - [gered/clj-browserchannel "0.3.2"]]}} - - :cljsbuild {:builds - {:main - {:source-paths ["src"]}}}) diff --git a/views.reagent.browserchannel/src/views/reagent/browserchannel/client.cljs b/views.reagent.browserchannel/src/views/reagent/browserchannel/client.cljs deleted file mode 100644 index 774b197..0000000 --- a/views.reagent.browserchannel/src/views/reagent/browserchannel/client.cljs +++ /dev/null @@ -1,33 +0,0 @@ -(ns views.reagent.browserchannel.client - (:require - [net.thegeez.browserchannel.client :as browserchannel] - [views.reagent.client.core :as client])) - -(defn init! - "performs initial configuration necessary to hook browserchannel into views.reagent - as the client/server messaging backend. should be called once on page load before - browserchannel is initialized." - [] - (reset! client/send-fn - (fn [data] - (browserchannel/send-data! data)))) - -(def middleware - "clj-browserchannel client-side event middleware. this should be included in the - middleware list provided to net.thegeez.browserchannel.client/connect!" - {:on-receive - (fn [handler] - (fn [data] - (if-not (client/on-receive! data) - ; only pass along receive events for data not intended for the views system - (handler data)))) - - :on-opening - (fn [handler] - (fn [] - ; we do this in on-opening instead of on-open since with browserchannel we - ; have the ability to queue up messages to be sent to the server in the initial - ; connection request. if this connection is actually a reconnection, then any - ; subscription requests that need to be resent get sent all in one go this way. - (client/on-open!) - (handler)))}) diff --git a/views.reagent.browserchannel/src/views/reagent/browserchannel/server.clj b/views.reagent.browserchannel/src/views/reagent/browserchannel/server.clj deleted file mode 100644 index 30d23b8..0000000 --- a/views.reagent.browserchannel/src/views/reagent/browserchannel/server.clj +++ /dev/null @@ -1,52 +0,0 @@ -(ns views.reagent.browserchannel.server - (:import - (clojure.lang Atom)) - (:require - [clojure.tools.logging :as log] - [net.thegeez.browserchannel.server :as browserchannel] - [views.core :as views] - [views.reagent.server.core :as server])) - -(defn- views-send-fn - [client-id [view-sig view-data]] - (log/trace client-id "refresh view" view-sig) - (browserchannel/send-data! client-id [:views/refresh [view-sig view-data]])) - -(defn init! - "initializes the views system and adds browserchannel-specific configuration - to it to enable the necessary hooks into views.reagent. - this function acts as a direct replacement to calling views.core/init!, so - are able to initialize both views and views.reagent by calling this - function. the arguments and return value are the same as in views.core/init! - so see that function for more information. - - one additional option :context-fn can be specified which is a function - that accepts an initial context map created by views.reagent and - allows your application to add any information necessary to the context - passed to various view system functions (such as auth-fn, namespace-fn, etc)." - ([^Atom view-system options] - (let [options (-> options - (assoc :send-fn views-send-fn))] - (views/init! view-system options) - (server/set-context-fn! view-system (:context-fn options)))) - ([options] - (init! (atom {}) options))) - -(defn ->middleware - "returns clj-browserchannel server-side event middleware for injecting - views.reagent handling into the clj-browserchannel client session - lifecycle handling. simply include the returned middleware map in your - Ring handler's wrap-browserchannel options." - [^Atom view-system] - {:on-receive - (fn [handler] - (fn [client-id request data] - (if-not (server/on-receive! view-system client-id data {:request request}) - ; only pass along receive events for data not intended for the views system - (handler client-id request data)))) - - :on-close - (fn [handler] - (fn [client-id request reason] - (server/on-close! view-system client-id {:request request}) - (handler client-id request reason)))})