initial framworks. linking logic is complete.

This commit is contained in:
Aidan 2020-11-23 22:07:04 -08:00
parent a5c8be2651
commit 3ed3b2a6d3
No known key found for this signature in database
GPG key ID: 327711E983899316
4 changed files with 215 additions and 36 deletions

View file

@ -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}})

View file

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

View file

@ -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"))

View file

@ -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))}))))