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"
|
||||
:url "https://www.eclipse.org/legal/epl-2.0/"}
|
||||
:dependencies [[org.clojure/clojure "1.10.0"]
|
||||
[com.kohlschutter.junixsocket/junixsocket-demo "2.2.0"]
|
||||
[seesaw "1.5.0"]
|
||||
[org.clojure/data.json "0.2.6"]]
|
||||
[com.kohlschutter.junixsocket/junixsocket-common "2.3.2"]
|
||||
[com.kohlschutter.junixsocket/junixsocket-native-common "2.3.2"]
|
||||
[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
|
||||
:target-path "target/%s"
|
||||
:profiles {:uberjar {:aot :all}})
|
||||
|
|
|
|||
|
|
@ -1,15 +1,65 @@
|
|||
(ns whisper.core
|
||||
"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]
|
||||
[whisper.signald]))
|
||||
|
||||
(defn main-loop
|
||||
"updates UI with data from Signald"
|
||||
[content]
|
||||
(update-window mainWindow content)
|
||||
(recur (str (get-next-update))))
|
||||
(def sockpath "/var/run/signald/signald.sock")
|
||||
(def user (atom {}))
|
||||
|
||||
;; TODO: handle multi users (with list ui)
|
||||
(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
|
||||
"packs and shows main window with signald version string"
|
||||
[& 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
|
||||
(:require [clojure.data.json :as json])
|
||||
(:require [clojure.data.json :as json]
|
||||
[clojure.tools.logging :as log])
|
||||
(:import
|
||||
(org.newsclub.net.unix AFUNIXSocket AFUNIXSocketAddress)
|
||||
(java.io File BufferedReader InputStreamReader)))
|
||||
(java.io File)))
|
||||
|
||||
;; Signald global state vars
|
||||
(def sigdSock (AFUNIXSocket/connectTo
|
||||
(AFUNIXSocketAddress. (File. "/var/run/signald/signald.sock"))))
|
||||
(def sigdRead (BufferedReader. (InputStreamReader.
|
||||
(. sigdSock getInputStream))))
|
||||
(def sigdWrite (. sigdSock getOutputStream))
|
||||
(def callback-table (atom {}))
|
||||
|
||||
;; Signald Functions
|
||||
(defn get-next-update
|
||||
"Retrieves and parses next line of incoming data from signald"
|
||||
[]
|
||||
(map :data (json/read-str (. sigdRead readLine) :key-fn keyword)))
|
||||
(defn get-signald-sock
|
||||
[filepath]
|
||||
(try
|
||||
(let [target (AFUNIXSocketAddress. (File. filepath))]
|
||||
(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
|
||||
"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
|
||||
(native!)
|
||||
(def mainWindow (frame :title "Signald Version Reader"
|
||||
:content " Loading... "
|
||||
:width 250
|
||||
:height 40))
|
||||
(:import [net.glxn.qrgen.core.image ImageType]
|
||||
[java.io FileInputStream]
|
||||
[javafx.scene.image Image WritableImage]))
|
||||
|
||||
;; UI Functions
|
||||
(defn update-window
|
||||
"Updates WINDOW to display additional content: CONTENT"
|
||||
[window content]
|
||||
(config! window :content
|
||||
(str (:content window) content "\n"))
|
||||
window)
|
||||
;; simple permutation of json into label
|
||||
(defn paint-message
|
||||
[message]
|
||||
{:fx/type :label
|
||||
:text (json/write-str message)})
|
||||
|
||||
(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