From fbf758efe48b748468b891af1ccc93a023a953d5 Mon Sep 17 00:00:00 2001 From: Aidan Hahn Date: Sun, 14 Jul 2019 11:25:24 -0700 Subject: [PATCH 01/10] initial push to private repo --- src/whisper/.#ui.clj | 1 - 1 file changed, 1 deletion(-) delete mode 120000 src/whisper/.#ui.clj diff --git a/src/whisper/.#ui.clj b/src/whisper/.#ui.clj deleted file mode 120000 index bb89933..0000000 --- a/src/whisper/.#ui.clj +++ /dev/null @@ -1 +0,0 @@ -mortimer@Vespucci.502:1562735217 \ No newline at end of file From 1360406d5a53cf7819a3e8c152f1fe753c7148ef Mon Sep 17 00:00:00 2001 From: Aidan Hahn Date: Mon, 15 Jul 2019 22:37:09 -0700 Subject: [PATCH 02/10] significantly more sane design --- .#README.md | 1 + CHANGELOG.md | 18 +----------------- src/whisper/core.clj | 12 ++++++------ src/whisper/signald.clj | 2 ++ src/whisper/ui.clj | 18 ++++++++++++++++++ 5 files changed, 28 insertions(+), 23 deletions(-) create mode 120000 .#README.md create mode 100644 src/whisper/ui.clj diff --git a/.#README.md b/.#README.md new file mode 120000 index 0000000..192ca7b --- /dev/null +++ b/.#README.md @@ -0,0 +1 @@ +ink@Magellan.6427:1563239860 \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 63c0b7f..f1e02ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,20 +5,4 @@ All notable changes to this project will be documented in this file. This change ### Changed - Add a new arity to `make-widget-async` to provide a different widget shape. -## [0.1.1] - 2019-07-06 -### Changed -- Documentation on how to make the widgets. - -### Removed -- `make-widget-sync` - we're all async, all the time. - -### Fixed -- Fixed widget maker to keep working when daylight savings switches over. - -## 0.1.0 - 2019-07-06 -### Added -- Files from the new template. -- Widget maker public API - `make-widget-sync`. - -[Unreleased]: https://github.com/your-name/whisper/compare/0.1.1...HEAD -[0.1.1]: https://github.com/your-name/whisper/compare/0.1.0...0.1.1 +[Unreleased]: https://git.aidanis.online/aidan/whisper/compare/HEAD diff --git a/src/whisper/core.clj b/src/whisper/core.clj index 0c56039..bc2815b 100644 --- a/src/whisper/core.clj +++ b/src/whisper/core.clj @@ -3,13 +3,13 @@ (:use [whisper.ui] [whisper.signald])) -;; SEESAW GUI STUFF -(def mainWindow (frame :title "Signald Version Reader")) +(defn main-loop + "updates UI with data from Signald" + [content] + (update mainWindow content) + (recur (get-next-update))) (defn -main "packs and shows main window with signald version string" [& args] - (println (config! (-> mainWindow pack! show!) - :content (reduce (fn [k v] - (apply str [k v])) - (get-next-update))))) + ()) diff --git a/src/whisper/signald.clj b/src/whisper/signald.clj index b8f203b..90b91c3 100644 --- a/src/whisper/signald.clj +++ b/src/whisper/signald.clj @@ -4,12 +4,14 @@ (org.newsclub.net.unix AFUNIXSocket AFUNIXSocketAddress) (java.io File BufferedReader InputStreamReader))) +;; 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)) +;; Signald Functions (defn get-next-update "Retrieves and parses next line of incoming data from signald" [] diff --git a/src/whisper/ui.clj b/src/whisper/ui.clj new file mode 100644 index 0000000..8e90808 --- /dev/null +++ b/src/whisper/ui.clj @@ -0,0 +1,18 @@ +(ns whisper.ui + "Whisper UI Module" + (:use [seesaw.core])) + +;; UI Global State +(native!) +(def mainWindow (frame :title "Signald Version Reader" + :content " Loading... " + :width 250 + :height 40)) + +;; UI Functions +(defn update + "Updates WINDOW to display additional content: CONTENT" + [window content] + (config! window :content + (concat (:content window) content)) + window) From a5c8be2651db6d490c540910a0e145ff1ac6318b Mon Sep 17 00:00:00 2001 From: Aidan Hahn Date: Tue, 16 Jul 2019 20:10:10 -0700 Subject: [PATCH 03/10] use correct string concatenation method --- .#README.md | 1 - src/whisper/core.clj | 6 +++--- src/whisper/signald.clj | 2 +- src/whisper/ui.clj | 4 ++-- 4 files changed, 6 insertions(+), 7 deletions(-) delete mode 120000 .#README.md diff --git a/.#README.md b/.#README.md deleted file mode 120000 index 192ca7b..0000000 --- a/.#README.md +++ /dev/null @@ -1 +0,0 @@ -ink@Magellan.6427:1563239860 \ No newline at end of file diff --git a/src/whisper/core.clj b/src/whisper/core.clj index bc2815b..5fb48fb 100644 --- a/src/whisper/core.clj +++ b/src/whisper/core.clj @@ -6,10 +6,10 @@ (defn main-loop "updates UI with data from Signald" [content] - (update mainWindow content) - (recur (get-next-update))) + (update-window mainWindow content) + (recur (str (get-next-update)))) (defn -main "packs and shows main window with signald version string" [& args] - ()) + (main-loop "whisper is online")) diff --git a/src/whisper/signald.clj b/src/whisper/signald.clj index 90b91c3..48fc625 100644 --- a/src/whisper/signald.clj +++ b/src/whisper/signald.clj @@ -15,5 +15,5 @@ (defn get-next-update "Retrieves and parses next line of incoming data from signald" [] - (:data (json/read-str (.sigdRead readLine) :key-fn keyword))) + (map :data (json/read-str (. sigdRead readLine) :key-fn keyword))) diff --git a/src/whisper/ui.clj b/src/whisper/ui.clj index 8e90808..ff4bfe6 100644 --- a/src/whisper/ui.clj +++ b/src/whisper/ui.clj @@ -10,9 +10,9 @@ :height 40)) ;; UI Functions -(defn update +(defn update-window "Updates WINDOW to display additional content: CONTENT" [window content] (config! window :content - (concat (:content window) content)) + (str (:content window) content "\n")) window) From 3ed3b2a6d3c5bb006b20aa1fba2fa8f5fa4e571c Mon Sep 17 00:00:00 2001 From: Aidan Date: Mon, 23 Nov 2020 22:07:04 -0800 Subject: [PATCH 04/10] initial framworks. linking logic is complete. --- project.clj | 11 ++-- src/whisper/core.clj | 62 ++++++++++++++++++--- src/whisper/signald.clj | 61 ++++++++++++++++----- src/whisper/ui.clj | 117 +++++++++++++++++++++++++++++++++++----- 4 files changed, 215 insertions(+), 36 deletions(-) diff --git a/project.clj b/project.clj index 988511c..12ab4be 100644 --- a/project.clj +++ b/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}}) diff --git a/src/whisper/core.clj b/src/whisper/core.clj index 5fb48fb..e2aedef 100644 --- a/src/whisper/core.clj +++ b/src/whisper/core.clj @@ -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 [ (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 + (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))})))) From 3b409dd7fb8b43ed637872a532f9e9f3c9500675 Mon Sep 17 00:00:00 2001 From: Aidan Date: Wed, 25 Nov 2020 21:31:05 -0800 Subject: [PATCH 05/10] make subscribe and contact sync requests once user is chosen --- src/whisper/core.clj | 20 +++++++++++++------- src/whisper/signald.clj | 12 ++++++++++++ 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/whisper/core.clj b/src/whisper/core.clj index e2aedef..0038a83 100644 --- a/src/whisper/core.clj +++ b/src/whisper/core.clj @@ -20,7 +20,8 @@ (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)))) + (swap! user (first users)) + (get @user "username"))) (defn -main "packs and shows main window with signald version string" @@ -35,8 +36,7 @@ ;; 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_successful" (fn [_] (paint-linking nil))) (assoc-callback-type "linking_error" (fn [_] (paint-linking nil) (add-str-flag "Linking returned Error"))) @@ -53,10 +53,16 @@ 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)))) + (assoc-callback-type "account_list" + (fn [x] + (if (< (count x) 1) + (do (make-link-data-req sigd-input) + (make-list-accounts-req sigd-input) + (let [username (handle-users x)] + (make-subscription-req sigd-input username) + ;; list_contacts? + ;; TODO: sync/list groups + (make-contact-sync-req sigd-input username)))))) ;; find avail accounts (make-list-accounts-req sigd-input) diff --git a/src/whisper/signald.clj b/src/whisper/signald.clj index 34e2fa7..4aa9917 100644 --- a/src/whisper/signald.clj +++ b/src/whisper/signald.clj @@ -52,3 +52,15 @@ (defn make-list-accounts-req [output-stream] (-make-req output-stream "{\"type\": \"list_accounts\"}\n")) + +(defn make-subscribe-req + [output-stream username] + (-make-req output-stream + (json/write-str {"type" "subscribe" + "username" username}))) + +(defn make-contact-sync-req + [output-stream username] + (-make-req output-stream + (json/write-str {"type" "sync_contacts" + "username" username}))) From 60580189ae59e991926d8359bf11609653e7809b Mon Sep 17 00:00:00 2001 From: Aidan Date: Wed, 25 Nov 2020 22:06:20 -0800 Subject: [PATCH 06/10] refactor login process --- README.md | 13 +++++++++---- src/whisper/core.clj | 33 ++++++++++++++++++++++++--------- src/whisper/signald.clj | 6 ++++++ 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index de9147f..551f72c 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,18 @@ -# whisper +# Whisper -FIXME: description +Whisper is a client for signald. In combination, the two represent a complete replacement for the official Signal desktop client. Additionally, Whisper and Signald can be run on any mobile device supporting the Java Runtime Environment. + +**Whisper is in early stages of development.** ## Installation -Download from http://example.com/FIXME. +Currently: use lein run +TODO: package, wrap with a nice makefile ## Usage +Call `lein run` in a freshly cloned repository to run Whisper. Make sure Signald is already running. + FIXME: explanation $ java -jar whisper-0.1.0-standalone.jar [args] @@ -30,7 +35,7 @@ FIXME: listing of options this app accepts. ## License -Copyright © 2019 FIXME +Copyright © 2019 Aidan Hahn This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which is available at diff --git a/src/whisper/core.clj b/src/whisper/core.clj index 0038a83..1ae48f0 100644 --- a/src/whisper/core.clj +++ b/src/whisper/core.clj @@ -12,7 +12,7 @@ [whisper.signald])) (def sockpath "/var/run/signald/signald.sock") -(def user (atom {})) +(def user (atom {"username" "" "filename" ""})) ;; TODO: handle multi users (with list ui) (defn handle-users @@ -20,8 +20,7 @@ (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)) - (get @user "username"))) + (swap! user (first users)))) (defn -main "packs and shows main window with signald version string" @@ -55,14 +54,30 @@ ;; handle login basically (assoc-callback-type "account_list" (fn [x] - (if (< (count x) 1) + (if (< (count (get-in x ["data" "accounts"])) 1) (do (make-link-data-req sigd-input) (make-list-accounts-req sigd-input) - (let [username (handle-users x)] - (make-subscription-req sigd-input username) - ;; list_contacts? - ;; TODO: sync/list groups - (make-contact-sync-req sigd-input username)))))) + (handle-users x))))) + + ;; log in every time username changes + (add-watch user :login-manager + (fn [key atom old-state new-state] + (let [old-user (get old-state "username") + new-user (get new-state "username")] + + (log/info key " changed state from " + old-user " to " new-user) + + ;; unsub from old acc + (if (some? old-user) + ;; TODO: make this a do statement + ;; add a call to reset the UI + (make-unsubscribe-req sigd-input old-user)) + + ;; sub to new account + (if (some? new-user) + (make-subscribe-req sigd-input new-user) + (make-contact-sync-req sigd-input new-user))))) ;; find avail accounts (make-list-accounts-req sigd-input) diff --git a/src/whisper/signald.clj b/src/whisper/signald.clj index 4aa9917..dccb59f 100644 --- a/src/whisper/signald.clj +++ b/src/whisper/signald.clj @@ -59,6 +59,12 @@ (json/write-str {"type" "subscribe" "username" username}))) +(defn make-unsubscribe-req + [output-stream username] + (-make-req output-stream + (json/write-str {"type" "unsubscribe" + "username" username}))) + (defn make-contact-sync-req [output-stream username] (-make-req output-stream From 85b3346f5da6029087021cd5a6df5408768e5197 Mon Sep 17 00:00:00 2001 From: Aidan Date: Wed, 25 Nov 2020 23:19:42 -0800 Subject: [PATCH 07/10] fix defects found in testing --- src/whisper/core.clj | 39 +++++++++++++++++++++++++-------------- src/whisper/signald.clj | 17 ++++++++++++++--- 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/whisper/core.clj b/src/whisper/core.clj index 1ae48f0..0b3942a 100644 --- a/src/whisper/core.clj +++ b/src/whisper/core.clj @@ -12,23 +12,21 @@ [whisper.signald])) (def sockpath "/var/run/signald/signald.sock") -(def user (atom {"username" "" "filename" ""})) +(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)))) + (if (> (count users) 1) + (log/info "Alert, multi account not supported yet. picking first on list")) + (reset! user (first users))) + (disassoc-callback-type "account_list")) (defn -main "packs and shows main window with signald version string" [& args] - ;; TODO: removeme - (add-str-flag (json/read-str "\"loading....\"")) - ;; paint main chat window (paint-main) @@ -39,11 +37,17 @@ (assoc-callback-type "linking_error" (fn [_] (paint-linking nil) (add-str-flag "Linking returned Error"))) + (assoc-callback-type "unexpected_error" (fn [x] (add-str-flag (str "Unexpected error: " + (get-in x ["data" "message"]))))) + (assoc-callback-type "contacts_list" (fn [x] (let [contacts (get x "data")] + ;; Should we iter in a background thread? + (doseq [contact contacts] + ;; TODO: sync with storage + (add-json-contact contact))))) - ;; update ui loop + ;; prep to read signald (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 @@ -56,10 +60,15 @@ (fn [x] (if (< (count (get-in x ["data" "accounts"])) 1) (do (make-link-data-req sigd-input) - (make-list-accounts-req sigd-input) - (handle-users x))))) + ;; recur via callback + (make-list-accounts-req sigd-input)) + (handle-users x)))) - ;; log in every time username changes + ;; log in every time user state changes + ;; another way we could do this is to have a callback triggered on a + ;; signald message type of "subscribed" + ;; but I thought it would be more efficient to just watch for results + ;; from the call to handle-users (add-watch user :login-manager (fn [key atom old-state new-state] (let [old-user (get old-state "username") @@ -76,8 +85,10 @@ ;; sub to new account (if (some? new-user) - (make-subscribe-req sigd-input new-user) - (make-contact-sync-req sigd-input new-user))))) + (do + (make-subscribe-req sigd-input new-user) + (make-contact-sync-req sigd-input new-user) + (make-contact-list-req sigd-input new-user)))))) ;; find avail accounts (make-list-accounts-req sigd-input) diff --git a/src/whisper/signald.clj b/src/whisper/signald.clj index dccb59f..c62614e 100644 --- a/src/whisper/signald.clj +++ b/src/whisper/signald.clj @@ -21,6 +21,10 @@ [callback-token callback] (swap! callback-table assoc callback-token callback)) +(defn disassoc-callback-type + [callback-token] + (swap! callback-table dissoc callback-token)) + ;; TODO wrap attempt to call callback func in try catch (defn callback-loop [BufferedReader] @@ -40,18 +44,20 @@ (get data "version") " " (get data "branch")))) +;; sends data to signal socket +;; adds a newline (defn -make-req [output-stream data] - (try (.write output-stream data) (.flush output-stream) + (try (.write output-stream (str data "\n")) (.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")) + (-make-req output-stream "{\"type\": \"link\"}")) (defn make-list-accounts-req [output-stream] - (-make-req output-stream "{\"type\": \"list_accounts\"}\n")) + (-make-req output-stream "{\"type\": \"list_accounts\"}")) (defn make-subscribe-req [output-stream username] @@ -70,3 +76,8 @@ (-make-req output-stream (json/write-str {"type" "sync_contacts" "username" username}))) +(defn make-contact-list-req + [output-stream username] + (-make-req output-stream + (json/write-str {"type" "list_contacts" + "username" username}))) From 170c4cb559b2b71724f2d0fb0ed076ef23fe23f2 Mon Sep 17 00:00:00 2001 From: Aidan Date: Mon, 30 Nov 2020 22:04:43 -0800 Subject: [PATCH 08/10] add storage system, also parse groups --- project.clj | 3 +- src/whisper/core.clj | 61 ++++++++++++++++++++++++++--------------- src/whisper/signald.clj | 17 +++++++++++- src/whisper/storage.clj | 59 +++++++++++++++++++++++++++++++++++++++ src/whisper/ui.clj | 7 +++-- 5 files changed, 121 insertions(+), 26 deletions(-) create mode 100644 src/whisper/storage.clj diff --git a/project.clj b/project.clj index 12ab4be..cf07531 100644 --- a/project.clj +++ b/project.clj @@ -11,7 +11,8 @@ [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"]] + [clj.qrgen "0.4.0"] + [codax "1.3.1"]] :main ^:skip-aot whisper.core :target-path "target/%s" :profiles {:uberjar {:aot :all}}) diff --git a/src/whisper/core.clj b/src/whisper/core.clj index 0b3942a..5f1d152 100644 --- a/src/whisper/core.clj +++ b/src/whisper/core.clj @@ -9,8 +9,12 @@ (:import (java.io BufferedReader InputStreamReader)) (:use [whisper.ui] - [whisper.signald])) + [whisper.signald] + [whisper.storage])) +;; TODO: config management +;; TODO: make config dir +(def dbpath "/tmp/temp_db") (def sockpath "/var/run/signald/signald.sock") (def user (atom {})) @@ -23,6 +27,15 @@ (reset! user (first users))) (disassoc-callback-type "account_list")) +(defn digest-group-or-contact-list + [list db] + ;; Should we iter in a background thread? + (doseq [contact list] + (let [id (get-id-from-contact-or-group contact) + name (get contact "name")] + (store-contact db id contact) + (add-json-contact {:id id :name name})))) + (defn -main "packs and shows main window with signald version string" [& args] @@ -30,27 +43,30 @@ ;; 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"))) - (assoc-callback-type "unexpected_error" (fn [x] (add-str-flag (str "Unexpected error: " - (get-in x ["data" "message"]))))) - (assoc-callback-type "contacts_list" (fn [x] (let [contacts (get x "data")] - ;; Should we iter in a background thread? - (doseq [contact contacts] - ;; TODO: sync with storage - (add-json-contact contact))))) - ;; prep to read signald - (let [sigd (get-signald-sock sockpath)] - (if (nil? sigd) - (add-str-flag "No signald connection.") + ;; also open storage + (let [sigd (get-signald-sock sockpath) + db (init-db dbpath)] - ;; else, we have connection to signald + ;; check if signald conn failed + (if (nil? sigd) + (add-str-flag "No signald connection.")) + + ;; 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"))) + (assoc-callback-type "unexpected_error" (fn [x] (add-str-flag (str "Unexpected error: " + (get-in x ["data" "message"]))))) + (assoc-callback-type "contacts_list" (fn [x] (digest-group-or-contact-list + (get-in x ["data"]) db))) + (assoc-callback-type "group_list" (fn [x] (digest-group-or-contact-list + (get-in x ["data" "groups"]) db))) + + ;; get io channels for signald (let [sigd-input (io/writer (.getOutputStream sigd)) sigd-output (BufferedReader. (InputStreamReader. (.getInputStream sigd))) sigd-loop (thread (callback-loop sigd-output))] @@ -88,10 +104,11 @@ (do (make-subscribe-req sigd-input new-user) (make-contact-sync-req sigd-input new-user) - (make-contact-list-req sigd-input new-user)))))) + (make-contact-list-req sigd-input new-user) + (make-group-list-req sigd-input new-user)))))) ;; find avail accounts (make-list-accounts-req sigd-input) ;; finally, block on signald loop till return - ( Date: Thu, 3 Dec 2020 21:19:26 -0800 Subject: [PATCH 09/10] startup user selection --- src/whisper/core.clj | 87 +++++++++++++++++++++++++--------------- src/whisper/signald.clj | 41 ++----------------- src/whisper/storage.clj~ | 3 ++ src/whisper/ui.clj | 52 ++++++++++++++++++++++-- 4 files changed, 110 insertions(+), 73 deletions(-) create mode 100644 src/whisper/storage.clj~ diff --git a/src/whisper/core.clj b/src/whisper/core.clj index 5f1d152..54c8978 100644 --- a/src/whisper/core.clj +++ b/src/whisper/core.clj @@ -17,15 +17,19 @@ (def dbpath "/tmp/temp_db") (def sockpath "/var/run/signald/signald.sock") (def user (atom {})) +(def link-status (atom false)) ;; 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")) - (reset! user (first users))) - (disassoc-callback-type "account_list")) + [sigd data] + (paint-user-select + (get-in data ["data" "accounts"]) + true + (fn [x _] + ;; TODO: dont magic string this + (if (= (get x "username") "Link New User") + (make-req sigd {"type" "link"}) + (reset! user x))))) (defn digest-group-or-contact-list [list db] @@ -55,10 +59,12 @@ ;; 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"))) + (assoc-callback-type "linking_successful" (fn [_] (paint-linking nil) + (reset! link-status true))) + (assoc-callback-type "linking_error" (fn [_] (paint-linking nil) + (log/error "Linking failed") + (add-str-flag "Linking failed") + (reset! link-status true))) (assoc-callback-type "unexpected_error" (fn [x] (add-str-flag (str "Unexpected error: " (get-in x ["data" "message"]))))) (assoc-callback-type "contacts_list" (fn [x] (digest-group-or-contact-list @@ -72,43 +78,60 @@ sigd-loop (thread (callback-loop sigd-output))] ;; handle login basically - (assoc-callback-type "account_list" - (fn [x] - (if (< (count (get-in x ["data" "accounts"])) 1) - (do (make-link-data-req sigd-input) - ;; recur via callback - (make-list-accounts-req sigd-input)) - (handle-users x)))) + (assoc-callback-type "account_list" (fn [x] (handle-users sigd-input x))) ;; log in every time user state changes ;; another way we could do this is to have a callback triggered on a ;; signald message type of "subscribed" ;; but I thought it would be more efficient to just watch for results ;; from the call to handle-users + ;; ALSO: close account picker (add-watch user :login-manager (fn [key atom old-state new-state] (let [old-user (get old-state "username") new-user (get new-state "username")] - (log/info key " changed state from " - old-user " to " new-user) + (if (not= old-user new-user) + (do (log/info key " changed state from " + old-user " to " new-user) - ;; unsub from old acc - (if (some? old-user) - ;; TODO: make this a do statement - ;; add a call to reset the UI - (make-unsubscribe-req sigd-input old-user)) + ;; unsub from old acc + (if (some? old-user) + ;; TODO: make this a, do statement + ;; add a call to reset the UI + (make-req sigd-input ("type" "unsubscribe" + "username" old-user))) - ;; sub to new account - (if (some? new-user) - (do - (make-subscribe-req sigd-input new-user) - (make-contact-sync-req sigd-input new-user) - (make-contact-list-req sigd-input new-user) - (make-group-list-req sigd-input new-user)))))) + ;; sub to new account + (if (some? new-user) + (do + (make-req sigd-input {"type" "subscribe" + "username" new-user}) + (make-req sigd-input {"type" "sync_contacts" + "username" new-user}) + (make-req sigd-input {"type" "list_contacts" + "username" new-user}) + (make-req sigd-input {"type" "list_groups" + "username" new-user}) + ;; TODO: load messages or something + ;; maybe wait till here to paint main UI + (add-str-flag (str "Now logged in as " new-user)) + )) + + ;; close account picker + (paint-user-select [] false (fn [_ _] (log/error "this cannot be called")))))))) + + ;; whenever a link account operation finishes, retrigger handle-users + (add-watch link-status :linking-manager + (fn [key atom old-state new-state] + (if (and (not= old-state new-state) new-state) + (do + (log/info key " has detected a link account operation finished.") + (reset! link-status false) + (make-req sigd-input {"type" "list_accounts"}))))) ;; find avail accounts - (make-list-accounts-req sigd-input) + (make-req sigd-input {"type" "list_accounts"}) ;; finally, block on signald loop till return ( Date: Thu, 3 Dec 2020 21:59:07 -0800 Subject: [PATCH 10/10] multi user updates for storage module --- src/whisper/core.clj | 8 +++--- src/whisper/storage.clj | 58 ++++++++++++++++++++++++----------------- src/whisper/ui.clj | 2 +- 3 files changed, 39 insertions(+), 29 deletions(-) diff --git a/src/whisper/core.clj b/src/whisper/core.clj index 54c8978..d362e08 100644 --- a/src/whisper/core.clj +++ b/src/whisper/core.clj @@ -29,7 +29,8 @@ ;; TODO: dont magic string this (if (= (get x "username") "Link New User") (make-req sigd {"type" "link"}) - (reset! user x))))) + (do (reset! user x) + (init-user-schema (get x "username"))))))) (defn digest-group-or-contact-list [list db] @@ -37,7 +38,7 @@ (doseq [contact list] (let [id (get-id-from-contact-or-group contact) name (get contact "name")] - (store-contact db id contact) + (store-contact db (get @user "username") id contact) (add-json-contact {:id id :name name})))) (defn -main @@ -115,8 +116,7 @@ "username" new-user}) ;; TODO: load messages or something ;; maybe wait till here to paint main UI - (add-str-flag (str "Now logged in as " new-user)) - )) + (add-str-flag (str "Now logged in as " new-user)))) ;; close account picker (paint-user-select [] false (fn [_ _] (log/error "this cannot be called")))))))) diff --git a/src/whisper/storage.clj b/src/whisper/storage.clj index c15d372..6aca1e1 100644 --- a/src/whisper/storage.clj +++ b/src/whisper/storage.clj @@ -7,53 +7,63 @@ "opens db at filepath and makes sure schema is in place" [filepath] (let [db (c/open-database! filepath)] - (if (nil? (c/get-at! db [:contacts])) - (c/assoc-at! db [:contacts] {})) - (if (nil? (c/get-at! db [:messages])) - (c/assoc-at! db [:messages] {})) + ;; TODO: schema preperations here db)) +(defn init-user-schema + [db username] + (let [user-doc (c/get-at! db [username]) + user-contact-doc (get user-doc :contacts) + user-messages-doc (get user-doc :messages)] + + (if (nil? user-doc) + (c/assoc-at! db [username] {:contacts {} + :messages {}})) + (if (nil? user-contact-doc) + (c/assoc-at! db [username :contacts] {})) + + (if (nil? user-messages-doc) + (c/assoc-at! db [username :messages] {})))) + (defn store-contact "adds a new contact to db" - [db contact-id contact-doc] - (let [curr-doc (c/get-at! db [:contacts contact-id])] - (if (nil? curr-doc) - (c/assoc-at! db [:contacts contact-id] contact-doc) - (if (not (= curr-doc contact-doc)) - (c/assoc-at! db [:contacts contact-id] contact-doc)))) - (if (nil? (c/get-at! db [:messages contact-id])) - (c/assoc-at! db [:messages contact-id] []))) + [db username contact-id contact-doc] + (let [curr-doc (c/get-at! db [username :contacts contact-id])] + (if (or (nil? curr-doc) (not= curr-doc contact-doc)) + (c/assoc-at! db [username :contacts contact-id] contact-doc))) + (if (nil? (c/get-at! db [username :messages contact-id])) + (c/assoc-at! db [username :messages contact-id] []))) (defn store-message "adds a message to the db" - [db contact-id message-doc] - (let [curr-thread (c/get-at! db [:messages contact-id])] + [db username contact-id message-doc] + (let [curr-thread (c/get-at! db [username :messages contact-id])] ;; no contact? dont bother (if (nil? curr-thread) (log/info "Received message for undiscovered contact (wont store).") - (c/merge-at! db [:messages contact-id] [message-doc])))) + (c/merge-at! db [username :messages contact-id] [message-doc])))) (defn get-contacts "retrieve all contacts" - [db] - (c/get-at! db [:contacts])) + [db username] + (c/get-at! db [username :contacts])) (defn get-contact "retrieve a specific contact" - [db contact-id] - (c/get-at! db [:contacts contact-id])) + [db username contact-id] + (c/get-at! db [username :contacts contact-id])) (defn get-thread "get all messages to and from a specific contact/group" - [db contact-id] - (c/get-at! db [:messages contact-id])) + [db username contact-id] + (c/get-at! db [username :messages contact-id])) (defn get-thread-and-contact-if-contact "get all messages to and from a contact, as well as their contact info IF they are a stored contact" - [db contact-id] - (let [contact-doc (c/get-at! db [:contacts contact-id])] + [db username contact-id] + (let [contact-doc (c/get-at! db [username :contacts contact-id])] (if (not (nil? contact-doc)) ;; contact exists - {:contact contact-doc :thread (c/get-at! db [:messages contact-id])} + {:contact contact-doc :thread (c/get-at! db [username :messages contact-id])} ;; else return nil nil))) diff --git a/src/whisper/ui.clj b/src/whisper/ui.clj index 2d4432e..2336125 100644 --- a/src/whisper/ui.clj +++ b/src/whisper/ui.clj @@ -150,7 +150,7 @@ (aux-renderer {:fx/type user-select-ui :showing showing - ;; "Make New User" is a magic token + ;; "Make New User" is a magic token. see handle-users in core ;; TODO: dont :users (conj users {"username" "Link New User"}) :callback callback}))