initial framworks. linking logic is complete.
This commit is contained in:
parent
a5c8be2651
commit
3ed3b2a6d3
4 changed files with 215 additions and 36 deletions
11
project.clj
11
project.clj
|
|
@ -4,9 +4,14 @@
|
||||||
:license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
|
:license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
|
||||||
:url "https://www.eclipse.org/legal/epl-2.0/"}
|
:url "https://www.eclipse.org/legal/epl-2.0/"}
|
||||||
:dependencies [[org.clojure/clojure "1.10.0"]
|
:dependencies [[org.clojure/clojure "1.10.0"]
|
||||||
[com.kohlschutter.junixsocket/junixsocket-demo "2.2.0"]
|
[com.kohlschutter.junixsocket/junixsocket-common "2.3.2"]
|
||||||
[seesaw "1.5.0"]
|
[com.kohlschutter.junixsocket/junixsocket-native-common "2.3.2"]
|
||||||
[org.clojure/data.json "0.2.6"]]
|
[cljfx "1.7.10"]
|
||||||
|
[org.clojure/data.json "0.2.6"]
|
||||||
|
[org.clojure/tools.logging "1.1.0"]
|
||||||
|
[org.clojure/core.cache "1.0.207"]
|
||||||
|
[org.clojure/core.async "1.3.610"]
|
||||||
|
[clj.qrgen "0.4.0"]]
|
||||||
:main ^:skip-aot whisper.core
|
:main ^:skip-aot whisper.core
|
||||||
:target-path "target/%s"
|
:target-path "target/%s"
|
||||||
:profiles {:uberjar {:aot :all}})
|
:profiles {:uberjar {:aot :all}})
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,65 @@
|
||||||
(ns whisper.core
|
(ns whisper.core
|
||||||
"Whisper Namespace"
|
"Whisper Namespace"
|
||||||
|
(:require [clojure.tools.logging :as log]
|
||||||
|
[clojure.data.json :as json]
|
||||||
|
[clojure.core.async :as async
|
||||||
|
:refer [<!! thread]]
|
||||||
|
[clojure.java.io :as io])
|
||||||
|
|
||||||
|
(:import (java.io BufferedReader InputStreamReader))
|
||||||
|
|
||||||
(:use [whisper.ui]
|
(:use [whisper.ui]
|
||||||
[whisper.signald]))
|
[whisper.signald]))
|
||||||
|
|
||||||
(defn main-loop
|
(def sockpath "/var/run/signald/signald.sock")
|
||||||
"updates UI with data from Signald"
|
(def user (atom {}))
|
||||||
[content]
|
|
||||||
(update-window mainWindow content)
|
;; TODO: handle multi users (with list ui)
|
||||||
(recur (str (get-next-update))))
|
(defn handle-users
|
||||||
|
[data]
|
||||||
|
(let [users (get-in data ["data" "accounts"])]
|
||||||
|
(if (> (count users) 1)
|
||||||
|
(log/info "Alert, multi account not supported yet. picking first on list"))
|
||||||
|
(swap! user (first users))))
|
||||||
|
|
||||||
(defn -main
|
(defn -main
|
||||||
"packs and shows main window with signald version string"
|
"packs and shows main window with signald version string"
|
||||||
[& args]
|
[& args]
|
||||||
(main-loop "whisper is online"))
|
|
||||||
|
;; TODO: removeme
|
||||||
|
(add-str-flag (json/read-str "\"loading....\""))
|
||||||
|
|
||||||
|
;; paint main chat window
|
||||||
|
(paint-main)
|
||||||
|
|
||||||
|
;; register callbacks between the UI and signald
|
||||||
|
(assoc-callback-type "version" (fn [x] (add-str-flag (get-version-from-version-message x))))
|
||||||
|
(assoc-callback-type "linking_uri" (fn [x] (paint-linking x)))
|
||||||
|
(assoc-callback-type "linking_successful" (fn [_]
|
||||||
|
(paint-linking nil)))
|
||||||
|
(assoc-callback-type "linking_error" (fn [_]
|
||||||
|
(paint-linking nil)
|
||||||
|
(add-str-flag "Linking returned Error")))
|
||||||
|
|
||||||
|
;; update ui loop
|
||||||
|
(let [sigd (get-signald-sock sockpath)]
|
||||||
|
(if (nil? sigd)
|
||||||
|
;; TODO: change this to call add-json-flag
|
||||||
|
(add-str-flag "No signald connection.")
|
||||||
|
|
||||||
|
;; else, we have connection to signald
|
||||||
|
(let [sigd-input (io/writer (.getOutputStream sigd))
|
||||||
|
sigd-output (BufferedReader. (InputStreamReader. (.getInputStream sigd)))
|
||||||
|
sigd-loop (thread (callback-loop sigd-output))]
|
||||||
|
|
||||||
|
;; handle login basically
|
||||||
|
(assoc-callback-type "account_list" (fn [x] (if (< (count x) 1)
|
||||||
|
(do (make-link-data-req sigd-input)
|
||||||
|
(make-list-accounts-req sigd-input))
|
||||||
|
(handle-users x))))
|
||||||
|
|
||||||
|
;; find avail accounts
|
||||||
|
(make-list-accounts-req sigd-input)
|
||||||
|
|
||||||
|
;; finally, block on signald loop till return
|
||||||
|
(<!! sigd-loop)))))
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,54 @@
|
||||||
(ns whisper.signald
|
(ns whisper.signald
|
||||||
(:require [clojure.data.json :as json])
|
(:require [clojure.data.json :as json]
|
||||||
|
[clojure.tools.logging :as log])
|
||||||
(:import
|
(:import
|
||||||
(org.newsclub.net.unix AFUNIXSocket AFUNIXSocketAddress)
|
(org.newsclub.net.unix AFUNIXSocket AFUNIXSocketAddress)
|
||||||
(java.io File BufferedReader InputStreamReader)))
|
(java.io File)))
|
||||||
|
|
||||||
;; Signald global state vars
|
(def callback-table (atom {}))
|
||||||
(def sigdSock (AFUNIXSocket/connectTo
|
|
||||||
(AFUNIXSocketAddress. (File. "/var/run/signald/signald.sock"))))
|
|
||||||
(def sigdRead (BufferedReader. (InputStreamReader.
|
|
||||||
(. sigdSock getInputStream))))
|
|
||||||
(def sigdWrite (. sigdSock getOutputStream))
|
|
||||||
|
|
||||||
;; Signald Functions
|
(defn get-signald-sock
|
||||||
(defn get-next-update
|
[filepath]
|
||||||
"Retrieves and parses next line of incoming data from signald"
|
(try
|
||||||
[]
|
(let [target (AFUNIXSocketAddress. (File. filepath))]
|
||||||
(map :data (json/read-str (. sigdRead readLine) :key-fn keyword)))
|
(log/info "Connected to signald at: " filepath)
|
||||||
|
(AFUNIXSocket/connectTo target))
|
||||||
|
(catch Exception e
|
||||||
|
(log/error e "Caught exception connecting to socket")
|
||||||
|
nil)))
|
||||||
|
|
||||||
|
(defn assoc-callback-type
|
||||||
|
[callback-token callback]
|
||||||
|
(swap! callback-table assoc callback-token callback))
|
||||||
|
|
||||||
|
;; TODO wrap attempt to call callback func in try catch
|
||||||
|
(defn callback-loop
|
||||||
|
[BufferedReader]
|
||||||
|
(doall (for [line (line-seq BufferedReader)]
|
||||||
|
(let [obj (json/read-str line)
|
||||||
|
type-tok (get obj "type")
|
||||||
|
callback-func (get @callback-table type-tok)]
|
||||||
|
(log/info "Signald: " line)
|
||||||
|
(if (nil? callback-func)
|
||||||
|
(log/error "No callback for message type: " type-tok)
|
||||||
|
(callback-func obj))))))
|
||||||
|
|
||||||
|
(defn get-version-from-version-message
|
||||||
|
[version-message-obj]
|
||||||
|
(let [data (get version-message-obj "data")]
|
||||||
|
(str (get data "name") " "
|
||||||
|
(get data "version") " "
|
||||||
|
(get data "branch"))))
|
||||||
|
|
||||||
|
(defn -make-req
|
||||||
|
[output-stream data]
|
||||||
|
(try (.write output-stream data) (.flush output-stream)
|
||||||
|
(catch Exception e (log/error e "Error sending to signald"))))
|
||||||
|
|
||||||
|
(defn make-link-data-req
|
||||||
|
[output-stream]
|
||||||
|
(-make-req output-stream "{\"type\": \"link\"}\n"))
|
||||||
|
|
||||||
|
(defn make-list-accounts-req
|
||||||
|
[output-stream]
|
||||||
|
(-make-req output-stream "{\"type\": \"list_accounts\"}\n"))
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,107 @@
|
||||||
(ns whisper.ui
|
(ns whisper.ui
|
||||||
"Whisper UI Module"
|
"Whisper UI Module"
|
||||||
(:use [seesaw.core]))
|
(:require [cljfx.api :as fx]
|
||||||
|
[clojure.data.json :as json]
|
||||||
|
[clojure.tools.logging :as log]
|
||||||
|
[clojure.core.cache :as cache]
|
||||||
|
[clj.qrgen :as qr]
|
||||||
|
[clojure.java.io :as io])
|
||||||
|
|
||||||
;; UI Global State
|
(:import [net.glxn.qrgen.core.image ImageType]
|
||||||
(native!)
|
[java.io FileInputStream]
|
||||||
(def mainWindow (frame :title "Signald Version Reader"
|
[javafx.scene.image Image WritableImage]))
|
||||||
:content " Loading... "
|
|
||||||
:width 250
|
|
||||||
:height 40))
|
|
||||||
|
|
||||||
;; UI Functions
|
;; simple permutation of json into label
|
||||||
(defn update-window
|
(defn paint-message
|
||||||
"Updates WINDOW to display additional content: CONTENT"
|
[message]
|
||||||
[window content]
|
{:fx/type :label
|
||||||
(config! window :content
|
:text (json/write-str message)})
|
||||||
(str (:content window) content "\n"))
|
|
||||||
window)
|
(defn paint-contact
|
||||||
|
[contact]
|
||||||
|
{:fx/type :label
|
||||||
|
:text (json/write-str contact)})
|
||||||
|
|
||||||
|
(defn paint-flag
|
||||||
|
[flag]
|
||||||
|
{:fx/type :label
|
||||||
|
:v-box/margin 10
|
||||||
|
:text (json/write-str flag)})
|
||||||
|
|
||||||
|
;; atomic global state for the UI
|
||||||
|
(def main-context (atom (fx/create-context
|
||||||
|
{:contacts []
|
||||||
|
:messages []
|
||||||
|
:flags []}
|
||||||
|
cache/lru-cache-factory)))
|
||||||
|
|
||||||
|
(def qrcode-context (atom (fx/create-context
|
||||||
|
{:message "In Progress..." }
|
||||||
|
cache/lru-cache-factory)))
|
||||||
|
|
||||||
|
(defn add-json-message
|
||||||
|
[obj]
|
||||||
|
(swap! main-context fx/swap-context update :messages conj (paint-message obj)))
|
||||||
|
|
||||||
|
(defn add-json-contact
|
||||||
|
[obj]
|
||||||
|
(swap! main-context fx/swap-context update :contacts conj (paint-contact obj)))
|
||||||
|
|
||||||
|
(defn add-str-flag
|
||||||
|
[str]
|
||||||
|
(swap! main-context fx/swap-context update :flags conj
|
||||||
|
(paint-flag str)))
|
||||||
|
|
||||||
|
;; TODO: maybe an actual UI lol
|
||||||
|
(defn main-ui
|
||||||
|
[{:keys [fx/context]}]
|
||||||
|
{:fx/type :stage
|
||||||
|
:showing true
|
||||||
|
:width 500
|
||||||
|
:height 500
|
||||||
|
:scene {:fx/type :scene
|
||||||
|
:root {:fx/type :v-box
|
||||||
|
:children (fx/sub-val context :flags)}}})
|
||||||
|
|
||||||
|
(defn qrcode-ui
|
||||||
|
[{:keys [showing image]}]
|
||||||
|
{:fx/type :stage
|
||||||
|
:showing showing
|
||||||
|
:title "link device"
|
||||||
|
:width 500
|
||||||
|
:height 500
|
||||||
|
:scene {:fx/type :scene
|
||||||
|
:root {:fx/type :v-box
|
||||||
|
:children [{:fx/type :image-view
|
||||||
|
:fit-height 480
|
||||||
|
:fit-width 480
|
||||||
|
:image image}]}}})
|
||||||
|
|
||||||
|
(def main-renderer (fx/create-renderer
|
||||||
|
:middleware (comp
|
||||||
|
fx/wrap-context-desc
|
||||||
|
(fx/wrap-map-desc (fn [_] {:fx/type main-ui})))
|
||||||
|
:opts {:fx.opt/type->lifecycle #(or (fx/keyword->lifecycle %)
|
||||||
|
(fx/fn->lifecycle-with-context %))}))
|
||||||
|
|
||||||
|
(def aux-renderer (fx/create-renderer))
|
||||||
|
|
||||||
|
(defn paint-main
|
||||||
|
[]
|
||||||
|
(fx/mount-renderer main-context main-renderer))
|
||||||
|
|
||||||
|
(defn paint-linking
|
||||||
|
[obj]
|
||||||
|
(if (nil? obj)
|
||||||
|
;; if no obj, turn off window
|
||||||
|
(aux-renderer {:fx/type qrcode-ui
|
||||||
|
:showing false
|
||||||
|
:image (WritableImage. 1 1)
|
||||||
|
:message "lol"})
|
||||||
|
|
||||||
|
;; else gen qr
|
||||||
|
(let [uri (str (get-in obj ["data" "uri"]))
|
||||||
|
image (io/file (qr/from uri))]
|
||||||
|
(aux-renderer {:fx/type qrcode-ui
|
||||||
|
:showing true
|
||||||
|
:image (Image. (FileInputStream. image))}))))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue