diff --git a/.gitignore b/.gitignore index ea7f8f836b..3e79181fd8 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,8 @@ figwheel_server.log # Lein # .lein-failures + +## Doo +# +out +doo-index.html diff --git a/project.clj b/project.clj index d68172cc05..4007f3100d 100644 --- a/project.clj +++ b/project.clj @@ -8,7 +8,7 @@ [reagent "0.5.1" :exclusions [cljsjs/react]] [re-frame "0.6.0"] [prismatic/schema "1.0.4"] - ^{:voom {:repo "git@github.com:status-im/status-lib.git" + ^{:voom {:repo "git@github.com:status-im/status-lib.git" :branch "master"}} [status-im/protocol "0.1.1-20160525_083359-g53ab2c2"] [natal-shell "0.1.6"] @@ -20,11 +20,12 @@ ["do" "clean" ["with-profile" "prod" "cljsbuild" "once" "ios"] ["with-profile" "prod" "cljsbuild" "once" "android"]]} + :test-paths ["test/clj"] :figwheel {:nrepl-port 7888} :profiles {:dev {:dependencies [[figwheel-sidecar "0.5.0-2"] [com.cemerick/piggieback "0.2.1"] - [io.appium/java-client "3.4.1"] - ] + [io.appium/java-client "3.4.1"]] + :plugins [[lein-doo "0.1.6"]] :source-paths ["src" "env/dev"] :cljsbuild {:builds {:ios {:source-paths ["src" "env/dev"] :figwheel true @@ -37,7 +38,13 @@ :compiler {:output-to "target/android/not-used.js" :main "env.android.main" :output-dir "target/android" - :optimizations :none}}}} + :optimizations :none}} + :test {:source-paths ["src" "test/cljs"] + :compiler + {:main status-im.test.runner + :output-to "target/test/test.js" + :optimizations :none + :target :nodejs}}}} :repl-options {:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}} :prod {:cljsbuild {:builds {:ios {:source-paths ["src" "env/prod"] :compiler {:output-to "index.ios.js" diff --git a/src/status_im/android/core.cljs b/src/status_im/android/core.cljs index cf3c7d48e1..080a91fa54 100644 --- a/src/status_im/android/core.cljs +++ b/src/status_im/android/core.cljs @@ -60,5 +60,4 @@ (dispatch [:init-console-chat]) (dispatch [:init-chat]) (init-back-button-handler!) - ;(.registerComponent app-registry "StatusIm" #(r/reactify-component app-root)) - ) + (.registerComponent app-registry "StatusIm" #(r/reactify-component app-root))) diff --git a/src/status_im/components/drawer/view.cljs b/src/status_im/components/drawer/view.cljs index 0ac23a7dbb..61b7d698b6 100644 --- a/src/status_im/components/drawer/view.cljs +++ b/src/status_im/components/drawer/view.cljs @@ -2,18 +2,16 @@ (:require [clojure.string :as s] [re-frame.core :refer [subscribe dispatch dispatch-sync]] [reagent.core :as r] - [status-im.components.react :refer [android? - view - text - image - navigator - toolbar-android - drawer-layout-android - touchable-opacity]] + [status-im.components.react :as re :refer [view + text + image + navigator + toolbar-android + drawer-layout-android + touchable-opacity]] [status-im.resources :as res] [status-im.components.drawer.styles :as st] - [status-im.i18n :refer [label]] - [status-im.components.react :refer [react]])) + [status-im.i18n :refer [label]])) (defonce drawer-atom (atom)) @@ -30,7 +28,7 @@ :style st/user-photo}]) (defn menu-item [{:keys [name handler]}] - [touchable-opacity {:style st/menu-item-touchable + [touchable-opacity {:style st/menu-item-touchable :onPress (fn [] (close-drawer) (handler))} @@ -41,40 +39,40 @@ (let [username (subscribe [:get :username])] (fn [] [view st/drawer-menu - [view st/user-photo-container - [user-photo {}]] - [view st/name-container - [text {:style st/name-text} - @username]] - [view st/menu-items-container - [menu-item {:name (label :t/profile) - :handler #(dispatch [:navigate-to :my-profile])}] - [menu-item {:name (label :t/settings) - :handler (fn [] - ;; TODO not implemented - )}] - [menu-item {:name (label :t/discovery) - :handler #(dispatch [:navigate-to :discovery])}] - [menu-item {:name (label :t/contacts) - :handler #(dispatch [:show-contacts navigator])}] - [menu-item {:name (label :t/invite-friends) - :handler (fn [] - ;; TODO not implemented - )}] - [menu-item {:name (label :t/faq) - :handler (fn [])}]] - [view st/switch-users-container - [touchable-opacity {:onPress (fn [] - (close-drawer) - ;; TODO not implemented - )} - [text {:style st/switch-users-text} - (label :t/switch-users)]]]]))) + [view st/user-photo-container + [user-photo {}]] + [view st/name-container + [text {:style st/name-text} + @username]] + [view st/menu-items-container + [menu-item {:name (label :t/profile) + :handler #(dispatch [:navigate-to :my-profile])}] + [menu-item {:name (label :t/settings) + :handler (fn [] + ;; TODO not implemented + )}] + [menu-item {:name (label :t/discovery) + :handler #(dispatch [:navigate-to :discovery])}] + [menu-item {:name (label :t/contacts) + :handler #(dispatch [:show-contacts navigator])}] + [menu-item {:name (label :t/invite-friends) + :handler (fn [] + ;; TODO not implemented + )}] + [menu-item {:name (label :t/faq) + :handler (fn [])}]] + [view st/switch-users-container + [touchable-opacity {:onPress (fn [] + (close-drawer) + ;; TODO not implemented + )} + [text {:style st/switch-users-text} + (label :t/switch-users)]]]]))) (defn drawer-view [items] [drawer-layout-android {:drawerWidth 260 - :drawerPosition react.DrawerLayoutAndroid.positions.Left + :drawerPosition re/react.DrawerLayoutAndroild.positions.Left :render-navigation-view #(r/as-element [drawer-menu]) - :ref (fn [drawer] - (reset! drawer-atom drawer))} + :ref (fn [drawer] + (reset! drawer-atom drawer))} items]) diff --git a/src/status_im/components/react.cljs b/src/status_im/components/react.cljs index d79d7d4fa9..5daccaee88 100644 --- a/src/status_im/components/react.cljs +++ b/src/status_im/components/react.cljs @@ -1,29 +1,39 @@ (ns status-im.components.react (:require [reagent.core :as r] - [status-im.components.styles :as st])) + [status-im.components.styles :as st] + [status-im.utils.utils :as u])) (when (exists? js/window) (set! js/window.React (js/require "react-native"))) -(def react (js/require "react-native")) +(def react (u/require "react-native")) -(def app-registry (.-AppRegistry react)) -(def navigator (r/adapt-react-class (.-Navigator react))) -(def text (r/adapt-react-class (.-Text react))) -(def view (r/adapt-react-class (.-View react))) -(def image (r/adapt-react-class (.-Image react))) -(def touchable-highlight-class (r/adapt-react-class (.-TouchableHighlight react))) +(defn get-react-property [name] + (aget react name)) + +(defn adapt-class [class] + (when class (r/adapt-react-class class))) + +(defn get-class [name] + (adapt-class (get-react-property name))) + +(def app-registry (get-react-property "AppRegistry")) +(def navigator (get-class "Navigator")) +(def text (get-class "Text")) +(def view (get-class "View")) +(def image (get-class "Image")) +(def touchable-highlight-class (get-class "TouchableHighlight")) (defn touchable-highlight [props content] [touchable-highlight-class (merge {:underlay-color :transparent} props) content]) -(def toolbar-android (r/adapt-react-class (.-ToolbarAndroid react))) -(def list-view-class (r/adapt-react-class (.-ListView react))) +(def toolbar-android (get-class "ToolbarAndroid")) +(def list-view-class (get-class "ListView")) (defn list-view [props] [list-view-class (merge {:enableEmptySections true} props)]) -(def scroll-view (r/adapt-react-class (.-ScrollView react))) -(def touchable-without-feedback (r/adapt-react-class (.-TouchableWithoutFeedback react))) -(def text-input-class (r/adapt-react-class (.-TextInput react))) +(def scroll-view (get-class "ScrollView")) +(def touchable-without-feedback (get-class "TouchableWithoutFeedback")) +(def text-input-class (get-class "TextInput")) (defn text-input [props text] [text-input-class (merge {:underlineColorAndroid :transparent @@ -31,11 +41,13 @@ :placeholder "Type"} props) text]) -(def drawer-layout-android (r/adapt-react-class (.-DrawerLayoutAndroid react))) -(def touchable-opacity (r/adapt-react-class (.-TouchableOpacity react))) -(def modal (r/adapt-react-class (.-Modal react))) -(def picker (r/adapt-react-class (.-Picker react))) -(def picker-item (r/adapt-react-class (.-Item (.-Picker react)))) +(def drawer-layout-android (get-class "DrawerLayoutAndroid")) +(def touchable-opacity (get-class "TouchableOpacity")) +(def modal (get-class "Modal")) +(def picker (get-class "Picker")) +(def picker-item + (when-let [picker (get-react-property "Picker")] + (adapt-class (.-Item picker)))) (defn icon @@ -44,20 +56,18 @@ [image {:source {:uri (keyword (str "icon_" (name n)))} :style style}])) -;(def react-linear-gradient (.-default (js/require "react-native-linear-gradient"))) -;(def linear-gradient (r/adapt-react-class react-linear-gradient)) - -(def linear-gradient-class (js/require "react-native-linear-gradient")) +(def linear-gradient-class (u/require "react-native-linear-gradient")) (defn linear-gradient [props] - (r/creacteElement linear-gradient-class + (r/create-element linear-gradient-class (clj->js (merge {:inverted true} props)))) -(def platform (.. react -Platform -OS)) +(def platform + (when-let [pl (.-Platform react)] (.-OS pl))) (def android? (= platform "android")) (defn list-item [component] (r/as-element component)) -(def dismiss-keyboard! (js/require "dismissKeyboard")) +(def dismiss-keyboard! (u/require "dismissKeyboard")) diff --git a/src/status_im/contacts/handlers.cljs b/src/status_im/contacts/handlers.cljs index 268b701815..bbb7fd8b7c 100644 --- a/src/status_im/contacts/handlers.cljs +++ b/src/status_im/contacts/handlers.cljs @@ -26,7 +26,7 @@ (register-handler :load-contacts load-contacts!) ;; TODO see https://github.com/rt2zz/react-native-contacts/issues/45 -(def react-native-contacts (js/require "react-native-contacts")) +(def react-native-contacts #_(js/require "react-native-contacts")) (defn contact-name [contact] (->> contact diff --git a/src/status_im/handlers.cljs b/src/status_im/handlers.cljs index 4010049665..6aa74b5c5f 100644 --- a/src/status_im/handlers.cljs +++ b/src/status_im/handlers.cljs @@ -35,15 +35,19 @@ ;; -- Common -------------------------------------------------------------- +(defn set-el [db [_ k v]] + (assoc db k v)) + (register-handler :set - (debug - (fn [db [_ k v]] - (assoc db k v)))) + debug + set-el) + +(defn set-in [db [_ path v]] + (assoc-in db path v)) (register-handler :set-in - (debug - (fn [db [_ path v]] - (assoc-in db path v)))) + debug + set-in) (register-handler :initialize-db (fn [_ _] diff --git a/src/status_im/i18n.cljs b/src/status_im/i18n.cljs index 4a6157cc23..aff04c97b2 100644 --- a/src/status_im/i18n.cljs +++ b/src/status_im/i18n.cljs @@ -1,12 +1,15 @@ (ns status-im.i18n (:require - [status-im.translations.en :as en])) + [status-im.translations.en :as en] + [status-im.utils.utils :as u])) -(def i18n (js/require "react-native-i18n")) +(def i18n (u/require "react-native-i18n")) (set! (.-fallbacks i18n) true) (set! (.-defaultSeparator i18n) "/") (set! (.-translations i18n) (clj->js {:en en/translations})) (defn label [path & options] - (.t i18n (name path) (clj->js options))) + (if (exists? i18n.t) + (.t i18n (name path) (clj->js options)) + (name path))) diff --git a/src/status_im/persistence/realm.cljs b/src/status_im/persistence/realm.cljs index 7056c11e9d..5d304828fe 100644 --- a/src/status_im/persistence/realm.cljs +++ b/src/status_im/persistence/realm.cljs @@ -2,7 +2,8 @@ (:require [cljs.reader :refer [read-string]] [status-im.components.styles :refer [default-chat-color]] [status-im.utils.logging :as log] - [status-im.utils.types :refer [to-string]]) + [status-im.utils.types :refer [to-string]] + [status-im.utils.utils :as u]) (:refer-clojure :exclude [exists?])) (def opts {:schema [{:name :contacts @@ -68,9 +69,10 @@ :objectType "tag"} :last-updated "date"}}]}) -(def realm-class (js/require "realm")) +(def realm-class (u/require "realm")) -(def realm (realm-class. (clj->js opts))) +(def realm (when (cljs.core/exists? js/window) + (realm-class. (clj->js opts)))) (def schema-by-name (->> (:schema opts) (mapv (fn [{:keys [name] :as schema}] diff --git a/src/status_im/utils/crypt.cljs b/src/status_im/utils/crypt.cljs index 87730efe8c..0b3f2dd7d0 100644 --- a/src/status_im/utils/crypt.cljs +++ b/src/status_im/utils/crypt.cljs @@ -1,9 +1,10 @@ (ns status-im.utils.crypt (:require [goog.crypt :refer [byteArrayToHex]] - [clojure.string :as s]) + [clojure.string :as s] + [status-im.utils.utils :as u]) (:import goog.crypt.Sha256)) -(def random-bytes (js/require "react-native-randombytes")) +(def random-bytes (u/require "react-native-randombytes")) (def sha-256 (Sha256.)) @@ -19,7 +20,7 @@ (byteArrayToHex (.digest sha-256))) (defn gen-random-bytes [length cb] - (.randomBytes random-bytes length (fn [& [err buf]] + #_(.randomBytes random-bytes length (fn [& [err buf]] (if err (cb {:error err}) (cb {:buffer buf}))))) diff --git a/src/status_im/utils/phone_number.cljs b/src/status_im/utils/phone_number.cljs index 137a1c37b5..da0ada6733 100644 --- a/src/status_im/utils/phone_number.cljs +++ b/src/status_im/utils/phone_number.cljs @@ -1,16 +1,17 @@ -(ns status-im.utils.phone-number) +(ns status-im.utils.phone-number + (:require [status-im.utils.utils :as u])) -(def i18n (js/require "react-native-i18n")) -(def locale (.-locale i18n)) +(def i18n (u/require "react-native-i18n")) +(def locale (or (.-locale i18n) "___en")) (def country-code (subs locale 3 5)) -(set! js/PhoneNumber (js/require "awesome-phonenumber")) +(def awesome-phonenumber (u/require "awesome-phonenumber")) ;; todo check wrong numbers, .getNumber returns empty string (defn format-phone-number [number] - (str (.getNumber (js/PhoneNumber. number country-code "international")))) + (str (.getNumber (awesome-phonenumber. number country-code "international")))) (defn valid-mobile-number? [number] (when (string? number) - (let [number-obj (js/PhoneNumber. number country-code "international")] + (let [number-obj (awesome-phonenumber. number country-code "international")] (and (.isValid number-obj) (.isMobile number-obj))))) diff --git a/src/status_im/utils/sms_listener.cljs b/src/status_im/utils/sms_listener.cljs index f00d54b7e9..04204116a2 100644 --- a/src/status_im/utils/sms_listener.cljs +++ b/src/status_im/utils/sms_listener.cljs @@ -1,7 +1,8 @@ (ns status-im.utils.sms-listener - (:require [status-im.components.react :refer [android?]])) + (:require [status-im.components.react :refer [android?]] + [status-im.utils.utils :as u])) -(def sms-listener (.-default (js/require "react-native-android-sms-listener"))) +(def sms-listener (.-default (u/require "react-native-android-sms-listener"))) ;; Only android is supported! diff --git a/src/status_im/utils/utils.cljs b/src/status_im/utils/utils.cljs index 02d54d3f88..c20a6c1e2a 100644 --- a/src/status_im/utils/utils.cljs +++ b/src/status_im/utils/utils.cljs @@ -5,6 +5,11 @@ [natal-shell.toast-android :as toast]) (:require [status-im.constants :as const])) +(defn require [module] + (if (exists? js/window) + (js/require module) + #js {})) + (defn log [obj] (.log js/console obj)) diff --git a/test/status_im/appium.clj b/test/clj/status_im/appium.clj similarity index 100% rename from test/status_im/appium.clj rename to test/clj/status_im/appium.clj diff --git a/test/status_im/console.clj b/test/clj/status_im/console.clj similarity index 100% rename from test/status_im/console.clj rename to test/clj/status_im/console.clj diff --git a/test/cljs/status_im/test/handlers.cljs b/test/cljs/status_im/test/handlers.cljs new file mode 100644 index 0000000000..70f4ae44c6 --- /dev/null +++ b/test/cljs/status_im/test/handlers.cljs @@ -0,0 +1,6 @@ +(ns status-im.test.handlers + (:require [cljs.test :refer-macros [deftest is]] + [status-im.handlers :as h])) + +(deftest test-set-val + (is (= {:key :val} (h/set-el {} [nil :key :val])))) diff --git a/test/cljs/status_im/test/runner.cljs b/test/cljs/status_im/test/runner.cljs new file mode 100644 index 0000000000..14bda7de00 --- /dev/null +++ b/test/cljs/status_im/test/runner.cljs @@ -0,0 +1,5 @@ +(ns status-im.test.runner + (:require [doo.runner :refer-macros [doo-tests]] + [status-im.test.handlers])) + +(doo-tests 'status-im.test.handlers)