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/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/project.clj b/project.clj index 988511c..cf07531 100644 --- a/project.clj +++ b/project.clj @@ -4,9 +4,15 @@ :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"] + [codax "1.3.1"]] :main ^:skip-aot whisper.core :target-path "target/%s" :profiles {:uberjar {:aot :all}}) 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 diff --git a/src/whisper/core.clj b/src/whisper/core.clj index 0c56039..d362e08 100644 --- a/src/whisper/core.clj +++ b/src/whisper/core.clj @@ -1,15 +1,137 @@ (ns whisper.core "Whisper Namespace" - (:use [whisper.ui] - [whisper.signald])) + (:require [clojure.tools.logging :as log] + [clojure.data.json :as json] + [clojure.core.async :as async + :refer [ mainWindow pack! show!) - :content (reduce (fn [k v] - (apply str [k v])) - (get-next-update))))) + + ;; paint main chat window + (paint-main) + + ;; prep to read signald + ;; also open storage + (let [sigd (get-signald-sock sockpath) + db (init-db dbpath)] + + ;; 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) + (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 + (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))] + + ;; handle login basically + (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")] + + (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-req sigd-input ("type" "unsubscribe" + "username" old-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-req sigd-input {"type" "list_accounts"}) + + ;; 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))})))) + +;; TODO: modify callback into event handler +(defn paint-user-select + [users showing callback] + (aux-renderer + {:fx/type user-select-ui + :showing showing + ;; "Make New User" is a magic token. see handle-users in core + ;; TODO: dont + :users (conj users {"username" "Link New User"}) + :callback callback}))