diff --git a/.gitignore b/.gitignore index 14773fe52f..3e79181fd8 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,12 @@ target/ # figwheel_server.log .nrepl-port + +# Lein +# +.lein-failures + +## Doo +# +out +doo-index.html diff --git a/.re-natal b/.re-natal index f5a732806b..4fecaf5ca5 100644 --- a/.re-natal +++ b/.re-natal @@ -1,7 +1,7 @@ { "name": "StatusIm", "interface": "reagent", - "androidHost": "localhost", + "androidHost": "10.0.3.2", "modules": [ "react-native-contacts", "react-native-invertible-scroll-view", diff --git a/android/app/src/main/java/com/statusim/MainActivity.java b/android/app/src/main/java/com/statusim/MainActivity.java index e60faddecc..cf17707c29 100644 --- a/android/app/src/main/java/com/statusim/MainActivity.java +++ b/android/app/src/main/java/com/statusim/MainActivity.java @@ -8,11 +8,18 @@ import com.facebook.react.shell.MainReactPackage; import com.rt2zz.reactnativecontacts.ReactNativeContacts; import android.os.Bundle; import android.os.Environment; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.content.DialogInterface.OnCancelListener; import com.github.ethereum.go_ethereum.cmd.Geth; import com.bitgo.randombytes.RandomBytesPackage; import com.BV.LinearGradient.LinearGradientPackage; import com.centaurwarchief.smslistener.SmsListener; +import android.os.Handler; +import android.util.Log; + import java.util.Arrays; import java.util.List; import java.util.Properties; @@ -25,15 +32,10 @@ import io.realm.react.RealmReactPackage; public class MainActivity extends ReactActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + final Handler handler = new Handler(); - // Required for android-16 (???) - System.loadLibrary("gethraw"); - System.loadLibrary("geth"); - - // Required because of crazy APN settings redirecting localhost + protected void startStatus() { + // Required because of crazy APN settings redirecting localhost (found in GB) Properties properties = System.getProperties(); properties.setProperty("http.nonProxyHosts", "localhost|127.0.0.1"); properties.setProperty("https.nonProxyHosts", "localhost|127.0.0.1"); @@ -45,11 +47,56 @@ public class MainActivity extends ReactActivity { getApplicationInfo().dataDir; // Launch! + final Runnable addPeer = new Runnable() { + public void run() { + Log.w("Geth", "adding peer"); + Geth.run("--exec admin.addPeer(\"enode://e2f28126720452aa82f7d3083e49e6b3945502cb94d9750a15e27ee310eed6991618199f878e5fbc7dfa0e20f0af9554b41f491dc8f1dbae8f0f2d37a3a613aa@139.162.13.89:55555\") attach http://localhost:8545"); + } + }; new Thread(new Runnable() { public void run() { - Geth.run("--bootnodes enode://e2f28126720452aa82f7d3083e49e6b3945502cb94d9750a15e27ee310eed6991618199f878e5fbc7dfa0e20f0af9554b41f491dc8f1dbae8f0f2d37a3a613aa@139.162.13.89:30303 --shh --ipcdisable --nodiscover --rpc --rpcapi db,eth,net,web3,shh,admin --fast --datadir=" + dataFolder); + Geth.run("--shh --ipcdisable --nodiscover --rpc --rpcapi db,eth,net,web3,shh,admin --fast --datadir=" + dataFolder); + } }).start(); + handler.postDelayed(addPeer, 5000); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Required for android-16 (???) + // Crash if put in startStatus() ? + System.loadLibrary("gethraw"); + System.loadLibrary("geth"); + + if(!RootUtil.isDeviceRooted()) { + startStatus(); + } else { + AlertDialog dialog = new AlertDialog.Builder(MainActivity.this).setMessage(getResources().getString(R.string.root_warning)) + .setPositiveButton(getResources().getString(R.string.root_okay), new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + startStatus(); + } + }).setNegativeButton(getResources().getString(R.string.root_cancel), new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + MainActivity.this.finishAffinity(); + } + }).setOnCancelListener(new OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + dialog.dismiss(); + MainActivity.this.finishAffinity(); + } + }).create(); + dialog.show(); + } + } /** diff --git a/android/app/src/main/java/com/statusim/RootUtil.java b/android/app/src/main/java/com/statusim/RootUtil.java new file mode 100644 index 0000000000..c5b9e88df0 --- /dev/null +++ b/android/app/src/main/java/com/statusim/RootUtil.java @@ -0,0 +1,41 @@ +package com.statusim; + +import java.io.File; +import java.io.BufferedReader; +import java.io.InputStreamReader; + +/** @author Kevin Kowalewski */ +public class RootUtil { + + public static boolean isDeviceRooted() { + return checkRootMethod1() || checkRootMethod2() || checkRootMethod3(); + } + + private static boolean checkRootMethod1() { + String buildTags = android.os.Build.TAGS; + return buildTags != null && buildTags.contains("test-keys"); + } + + private static boolean checkRootMethod2() { + String[] paths = { "/system/app/Superuser.apk", "/sbin/su", "/system/bin/su", "/system/xbin/su", "/data/local/xbin/su", "/data/local/bin/su", "/system/sd/xbin/su", + "/system/bin/failsafe/su", "/data/local/su" }; + for (String path : paths) { + if (new File(path).exists()) return true; + } + return false; + } + + private static boolean checkRootMethod3() { + Process process = null; + try { + process = Runtime.getRuntime().exec(new String[] { "/system/xbin/which", "su" }); + BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream())); + if (in.readLine() != null) return true; + return false; + } catch (Throwable t) { + return false; + } finally { + if (process != null) process.destroy(); + } + } +} diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index fb87cafc71..428623ba43 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -1,3 +1,6 @@ - StatusIm - + Status + Your phone appears to be ROOTED, by pressing CONTINUE you understand and accept the risks in using this software. + Continue + Exit + \ No newline at end of file diff --git a/project.clj b/project.clj index 647d7ad498..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 "https://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,9 +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"]] + [com.cemerick/piggieback "0.2.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 @@ -35,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" @@ -46,5 +55,4 @@ :compiler {:output-to "index.android.js" :main "env.android.main" :output-dir "target/android" - :optimizations :simple}}}} - }}) + :optimizations :simple}}}}}}) diff --git a/run-osx.sh b/run-osx.sh index 9f89a05857..64a86b653f 100755 --- a/run-osx.sh +++ b/run-osx.sh @@ -17,11 +17,13 @@ function tab () { fi osascript &>/dev/null <> (name command) + (str "request-"))) + (defn message-content-command-request [{:keys [msg-id content from incoming-group]}] (let [commands-atom (subscribe [:get-commands])] (fn [{:keys [msg-id content from incoming-group]}] (let [commands @commands-atom {:keys [command content]} (parse-command-request commands content)] - [touchable-highlight {:onPress #(set-chat-command msg-id command)} + [touchable-highlight {:onPress #(set-chat-command msg-id command) + :accessibility-label (label command)} [view st/comand-request-view [view st/command-request-message-view (when incoming-group diff --git a/src/status_im/chat/views/plain_input.cljs b/src/status_im/chat/views/plain_input.cljs index eaece17dfb..dcbc75780a 100644 --- a/src/status_im/chat/views/plain_input.cljs +++ b/src/status_im/chat/views/plain_input.cljs @@ -1,9 +1,9 @@ (ns status-im.chat.views.plain-input (:require [re-frame.core :refer [subscribe dispatch]] [status-im.components.react :refer [view - icon - touchable-highlight - text-input]] + icon + touchable-highlight + text-input]] [status-im.chat.views.suggestions :refer [suggestions-view]] [status-im.chat.styles.plain-input :as st])) @@ -39,15 +39,16 @@ (if @typing-command? [icon :close-gray st/close-icon] [icon :list st/list-icon])]] - [text-input {:style st/message-input - :autoFocus (pos? (count @staged-commands-atom)) - :onChangeText set-input-message - :onSubmitEditing #(try-send @chat @staged-commands-atom - input-message)} + [text-input {:style st/message-input + :autoFocus (pos? (count @staged-commands-atom)) + :onChangeText set-input-message + :onSubmitEditing #(try-send @chat @staged-commands-atom + input-message)} input-message] ;; TODO emoticons: not implemented [icon :smile st/smile-icon] (when (message-valid? @staged-commands-atom input-message) - [touchable-highlight {:on-press #(send @chat input-message)} + [touchable-highlight {:on-press #(send @chat input-message) + :accessibility-label :send-message} [view st/send-container [icon :send st/send-icon]]])]])))) diff --git a/src/status_im/chats_list/screen.cljs b/src/status_im/chats_list/screen.cljs index a82849d7e6..572f0ac5a9 100644 --- a/src/status_im/chats_list/screen.cljs +++ b/src/status_im/chats_list/screen.cljs @@ -36,7 +36,7 @@ [drawer-view [view st/chats-container [chats-list-toolbar] - [list-view {:dataSource (to-datasource (vals @chats)) + [list-view {:dataSource (to-datasource @chats) :renderRow (fn [row _ _] (list-item [chat-list-item row])) :style st/list-container}] diff --git a/src/status_im/chats_list/views/chat_list_item.cljs b/src/status_im/chats_list/views/chat_list_item.cljs index 0c343f9b59..a7877e5e82 100644 --- a/src/status_im/chats_list/views/chat_list_item.cljs +++ b/src/status_im/chats_list/views/chat_list_item.cljs @@ -1,17 +1,18 @@ (ns status-im.chats-list.views.chat-list-item (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] [status-im.components.react :refer [view - text - image - touchable-highlight]] + text + image + touchable-highlight]] [status-im.components.styles :refer [font]] [status-im.chats-list.views.inner-item :refer [chat-list-item-inner-view]])) -(defn chat-list-item [{:keys [chat-id] :as chat}] +(defn chat-list-item [[chat-id chat]] [touchable-highlight {:on-press #(dispatch [:navigate-to :chat chat-id])} [view [chat-list-item-inner-view (merge chat ;; TODO stub data - {:new-messages-count 3 + {:chat-id chat-id + :new-messages-count 3 :online true})]]]) diff --git a/src/status_im/components/action_button.cljs b/src/status_im/components/action_button.cljs index db303f5f6d..6ee896d1ed 100644 --- a/src/status_im/components/action_button.cljs +++ b/src/status_im/components/action_button.cljs @@ -1,7 +1,7 @@ (ns status-im.components.action-button (:require [reagent.core :as r])) -(set! js/window.ActionButton (js/require "react-native-action-button")) +(def class (js/require "react-native-action-button")) -(def action-button (r/adapt-react-class (.-default js/ActionButton))) -(def action-button-item (r/adapt-react-class (.. js/ActionButton -default -Item))) \ No newline at end of file +(def action-button (r/adapt-react-class (.-default class))) +(def action-button-item (r/adapt-react-class (.. class -default -Item))) diff --git a/src/status_im/components/carousel/carousel.cljs b/src/status_im/components/carousel/carousel.cljs index c3181df6ce..8c61cb114e 100644 --- a/src/status_im/components/carousel/carousel.cljs +++ b/src/status_im/components/carousel/carousel.cljs @@ -5,11 +5,12 @@ touchable-without-feedback text]] [status-im.components.carousel.styles :as st] - [status-im.utils.logging :as log])) + [status-im.utils.logging :as log] + [status-im.components.react :as r])) (defn window-page-width [] - (.-width (.get (.. js/React -Dimensions) "window"))) + (.-width (.get (.. r/react -Dimensions) "window"))) (def defaults {:gap 10 :sneak 10 diff --git a/src/status_im/components/chat_icon/screen.cljs b/src/status_im/components/chat_icon/screen.cljs index e44d334e33..0d93e38cbb 100644 --- a/src/status_im/components/chat_icon/screen.cljs +++ b/src/status_im/components/chat_icon/screen.cljs @@ -6,7 +6,8 @@ image icon]] [status-im.components.chat-icon.styles :as st] - [status-im.components.styles :refer [color-purple]])) + [status-im.components.styles :refer [color-purple]] + [clojure.string :as s])) (defn default-chat-icon [name styles] [view (:default-chat-icon styles) @@ -26,10 +27,10 @@ (defview chat-icon-view [chat-id group-chat name online styles] [photo-path [:chat-photo chat-id]] [view (:container styles) - (if (and photo-path (not (empty? photo-path))) + (if-not (s/blank? photo-path) [chat-icon photo-path styles] [default-chat-icon name styles]) - (when (not group-chat) + (when-not group-chat [contact-online online styles])]) (defn chat-icon-view-chat-list [chat-id group-chat name color online] @@ -80,7 +81,7 @@ [contact [:contact]] (let [;; TODO stub data online true - color color-purple] + color color-purple] [profile-icon-view (:photo-path contact) (:name contact) color online])) (defview my-profile-icon [] @@ -88,5 +89,5 @@ photo-path [:get :photo-path]] (let [;; TODO stub data online true - color color-purple] + color color-purple] [profile-icon-view photo-path name color online])) diff --git a/src/status_im/components/drawer/view.cljs b/src/status_im/components/drawer/view.cljs index 1cd4ae43e8..a98fb9d542 100644 --- a/src/status_im/components/drawer/view.cljs +++ b/src/status_im/components/drawer/view.cljs @@ -2,14 +2,14 @@ (: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 :refer [react + 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]])) @@ -29,7 +29,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))} @@ -40,40 +40,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 js/React.DrawerLayoutAndroid.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/icons/ionicons.cljs b/src/status_im/components/icons/ionicons.cljs index 885af057d5..5bc4a06ddc 100644 --- a/src/status_im/components/icons/ionicons.cljs +++ b/src/status_im/components/icons/ionicons.cljs @@ -1,6 +1,4 @@ (ns status-im.components.icons.ionicons (:require [reagent.core :as r])) -(set! js/window.Ionicons (js/require "react-native-vector-icons/Ionicons")) - -(def icon (r/adapt-react-class js/Ionicons)) +(def icon (r/adapt-react-class (js/require "react-native-vector-icons/Ionicons"))) diff --git a/src/status_im/components/invertible_scroll_view.cljs b/src/status_im/components/invertible_scroll_view.cljs index daf873a773..429d66d6ed 100644 --- a/src/status_im/components/invertible_scroll_view.cljs +++ b/src/status_im/components/invertible_scroll_view.cljs @@ -1,8 +1,8 @@ -(ns status-im.components.invertible-scroll-view) +(ns status-im.components.invertible-scroll-view + (:require [reagent.core :as r])) -(set! js/window.InvertibleScrollView (js/require "react-native-invertible-scroll-view")) +(def class (js/require "react-native-invertible-scroll-view")) (defn invertible-scroll-view [props] - (js/React.createElement js/InvertibleScrollView - (clj->js (merge {:inverted true} props)))) + (r/create-element class (clj->js (merge {:inverted true} props)))) diff --git a/src/status_im/components/item_checkbox.cljs b/src/status_im/components/item_checkbox.cljs index 97d43ecd37..45c667141d 100644 --- a/src/status_im/components/item_checkbox.cljs +++ b/src/status_im/components/item_checkbox.cljs @@ -1,7 +1,5 @@ (ns status-im.components.item-checkbox (:require [reagent.core :as r])) -(set! js/window.ItemCheckbox (js/require "react-native-circle-checkbox")) - -(def item-checkbox (r/adapt-react-class js/ItemCheckbox)) +(def item-checkbox (r/adapt-react-class (js/require "react-native-circle-checkbox"))) diff --git a/src/status_im/components/react.cljs b/src/status_im/components/react.cljs index 930f6deedf..0ab2d2b755 100644 --- a/src/status_im/components/react.cljs +++ b/src/status_im/components/react.cljs @@ -1,26 +1,36 @@ (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])) -(set! js/window.React (js/require "react-native")) +(def react (u/require "react-native")) -(def app-registry (.-AppRegistry js/React)) -(def navigator (r/adapt-react-class (.-Navigator js/React))) -(def text (r/adapt-react-class (.-Text js/React))) -(def view (r/adapt-react-class (.-View js/React))) -(def image (r/adapt-react-class (.-Image js/React))) -(def touchable-highlight-class (r/adapt-react-class (.-TouchableHighlight js/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 js/React))) -(def list-view-class (r/adapt-react-class (.-ListView js/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 js/React))) -(def touchable-without-feedback (r/adapt-react-class (.-TouchableWithoutFeedback js/React))) -(def text-input-class (r/adapt-react-class (.-TextInput js/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 @@ -28,11 +38,13 @@ :placeholder "Type"} props) text]) -(def drawer-layout-android (r/adapt-react-class (.-DrawerLayoutAndroid js/React))) -(def touchable-opacity (r/adapt-react-class (.-TouchableOpacity js/React))) -(def modal (r/adapt-react-class (.-Modal js/React))) -(def picker (r/adapt-react-class (.-Picker js/React))) -(def picker-item (r/adapt-react-class (.-Item (.-Picker js/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 @@ -41,20 +53,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)) - -(set! js/window.LinearGradient (js/require "react-native-linear-gradient")) +(def linear-gradient-class (u/require "react-native-linear-gradient")) (defn linear-gradient [props] - (js/React.createElement js/LinearGradient - (clj->js (merge {:inverted true} props)))) + (r/create-element linear-gradient-class + (clj->js (merge {:inverted true} props)))) -(def platform (.. js/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/components/realm.cljs b/src/status_im/components/realm.cljs deleted file mode 100644 index eff8c1fee5..0000000000 --- a/src/status_im/components/realm.cljs +++ /dev/null @@ -1,9 +0,0 @@ -(ns status-im.components.realm - (:require [reagent.core :as r])) - -(set! js/window.RealmReactNative (js/require "realm/react-native")) - -(def list-view-class (r/adapt-react-class (.-ListView js/RealmReactNative))) - -(defn list-view [props] - [list-view-class (merge {:enableEmptySections true} props)]) diff --git a/src/status_im/components/toolbar.cljs b/src/status_im/components/toolbar.cljs index a757e56dbd..820b338b9c 100644 --- a/src/status_im/components/toolbar.cljs +++ b/src/status_im/components/toolbar.cljs @@ -16,9 +16,7 @@ toolbar-title-container toolbar-title-text icon-back - toolbar-height]] - [status-im.components.realm :refer [list-view]] - [reagent.core :as r])) + toolbar-height]])) (defn toolbar [{:keys [title nav-action hide-nav? action custom-action background-color custom-content style]}] diff --git a/src/status_im/contacts/handlers.cljs b/src/status_im/contacts/handlers.cljs index b374edd438..ac11cf33a2 100644 --- a/src/status_im/contacts/handlers.cljs +++ b/src/status_im/contacts/handlers.cljs @@ -5,7 +5,8 @@ [clojure.string :as s] [status-im.utils.utils :refer [http-post]] [status-im.utils.phone-number :refer [format-phone-number]] - [status-im.utils.handlers :as u])) + [status-im.utils.handlers :as u] + [status-im.utils.utils :refer [require]])) (defn save-contact [_ [_ contact]] @@ -26,7 +27,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 (require "react-native-contacts")) (defn contact-name [contact] (->> contact @@ -91,10 +92,14 @@ (defn add-new-contacts [{:keys [contacts] :as db} [_ new-contacts]] (let [identities (set (map :whisper-identity contacts)) - new-contacts' (remove #(identities (:whisper-identity %)) new-contacts)] + new-contacts' (->> new-contacts + (remove #(identities (:whisper-identity %))) + (map #(vector (:whisper-identity %) %)) + (into {}))] + (println new-contacts') (-> db - (update :contacts concat new-contacts') - (assoc :new-contacts new-contacts')))) + (update :contacts merge new-contacts') + (assoc :new-contacts (vals new-contacts'))))) (register-handler :add-contacts (after save-contacts!) diff --git a/src/status_im/discovery/views/popular.cljs b/src/status_im/discovery/views/popular.cljs index 9fbeb4f9fb..b91b233603 100644 --- a/src/status_im/discovery/views/popular.cljs +++ b/src/status_im/discovery/views/popular.cljs @@ -2,16 +2,16 @@ (:require-macros [status-im.utils.views :refer [defview]]) (:require [re-frame.core :refer [subscribe]] - [status-im.utils.logging :as log] [status-im.components.react :refer [android? text]] [status-im.components.carousel.carousel :refer [carousel]] [status-im.discovery.styles :as st] [status-im.discovery.views.popular-list :refer [discovery-popular-list]] - [status-im.i18n :refer [label]])) + [status-im.i18n :refer [label]] + [status-im.components.react :as r])) (defn page-width [] - (.-width (.get (.. js/React -Dimensions) "window"))) + (.-width (.get (.. r/react -Dimensions) "window"))) (defview popular [] [popular-tags [:get-popular-tags 3]] diff --git a/src/status_im/handlers.cljs b/src/status_im/handlers.cljs index a5c839bf61..5d43f80031 100644 --- a/src/status_im/handlers.cljs +++ b/src/status_im/handlers.cljs @@ -36,15 +36,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 6d3815715a..f07f1d6d24 100644 --- a/src/status_im/i18n.cljs +++ b/src/status_im/i18n.cljs @@ -1,25 +1,23 @@ (ns status-im.i18n (:require - [status-im.translations.en :as en])) + [status-im.translations.en :as en] + [status-im.utils.utils :as u])) -(set! js/window.I18n (js/require "react-native-i18n")) -(set! (.-fallbacks js/I18n) true) -(set! (.-defaultSeparator js/I18n) "/") +(def i18n (u/require "react-native-i18n")) +(set! (.-fallbacks i18n) true) +(set! (.-defaultSeparator i18n) "/") -(set! (.-translations js.I18n) (clj->js {:en en/translations})) +(set! (.-translations i18n) (clj->js {:en en/translations})) (defn label ([path] (label path {})) ([path options] - (.t js/I18n (name path) (clj->js options)))) + (if (exists? i18n.t) + (.t i18n (name path) (clj->js options)) + (name path)))) -(comment - (defn deep-merge [& maps] - (if (every? map? maps) - (apply merge-with deep-merge maps) - (last maps))) - (defn add-translations [new-translations] - (let [translations (.-translations js/I18n)] - (set! (.-translations js/I18n) (clj->js (deep-merge (js->clj translations) new-translations))))) - ) +(defn label-pluralize [count path & options] + (if (exists? i18n.t) + (.p i18n count (name path) (clj->js options)) + (name path))) \ No newline at end of file diff --git a/src/status_im/persistence/realm.cljs b/src/status_im/persistence/realm.cljs index a7d564f143..5d304828fe 100644 --- a/src/status_im/persistence/realm.cljs +++ b/src/status_im/persistence/realm.cljs @@ -2,11 +2,10 @@ (: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?])) -(set! js/window.Realm (js/require "realm")) - (def opts {:schema [{:name :contacts :primaryKey :whisper-identity :properties {:phone-number {:type "string" @@ -70,7 +69,10 @@ :objectType "tag"} :last-updated "date"}}]}) -(def realm (js/Realm. (clj->js opts))) +(def realm-class (u/require "realm")) + +(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/profile/screen.cljs b/src/status_im/profile/screen.cljs index 4451febd36..ffff2d2e47 100644 --- a/src/status_im/profile/screen.cljs +++ b/src/status_im/profile/screen.cljs @@ -2,14 +2,14 @@ (:require-macros [status-im.utils.views :refer [defview]]) (:require [re-frame.core :refer [subscribe dispatch]] [status-im.components.react :refer [view - text - image - icon - scroll-view - touchable-highlight - touchable-opacity]] + text + image + icon + scroll-view + touchable-highlight + touchable-opacity]] [status-im.components.chat-icon.screen :refer [profile-icon - my-profile-icon]] + my-profile-icon]] [status-im.profile.styles :as st] [status-im.components.qr-code :refer [qr-code]] [status-im.utils.types :refer [clj->json]] @@ -62,13 +62,13 @@ [text {:style st/report-user-text} (label :t/report-user)]]]]]) (defview my-profile [] - [username [:get :username] - photo-path [:get :photo-path] + [username [:get :username] + photo-path [:get :photo-path] phone-number [:get :phone-number] - email [:get :email] - status [:get :status] + email [:get :email] + status [:get :status] identity [:get-in [:user-identity :public]]] - [view {:style st/profile} + [scroll-view {:style st/profile} [touchable-highlight {:style st/back-btn-touchable :on-press #(dispatch [:navigate-back])} [view st/back-btn-container diff --git a/src/status_im/utils/crypt.cljs b/src/status_im/utils/crypt.cljs index 722581d10c..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)) -(set! js/window.RnRandomBytes (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 js/window.RnRandomBytes length (fn [& [err buf]] - (if err - (cb {:error err}) - (cb {:buffer buf}))))) + #_(.randomBytes random-bytes length (fn [& [err buf]] + (if err + (cb {:error err}) + (cb {:buffer buf}))))) diff --git a/src/status_im/utils/listview.cljs b/src/status_im/utils/listview.cljs index bd9dfe5dc5..dcd63da8bd 100644 --- a/src/status_im/utils/listview.cljs +++ b/src/status_im/utils/listview.cljs @@ -1,6 +1,5 @@ (ns status-im.utils.listview - (:require-macros [natal-shell.data-source :refer [data-source]]) - (:require [status-im.components.realm])) + (:require-macros [natal-shell.data-source :refer [data-source]])) (defn clone-with-rows [ds rows] (.cloneWithRows ds (reduce (fn [ac el] (.push ac el) ac) 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/clj/status_im/appium.clj b/test/clj/status_im/appium.clj new file mode 100644 index 0000000000..d3a77bc435 --- /dev/null +++ b/test/clj/status_im/appium.clj @@ -0,0 +1,86 @@ +(ns status-im.appium + (:require [clojure.java.io :as io] + [clojure.test :refer :all]) + (:import (org.openqa.selenium.remote DesiredCapabilities) + (org.openqa.selenium By) + (io.appium.java_client.android AndroidDriver) + (java.net URL) + (java.util.concurrent TimeUnit))) + + +(defn init [] + (let [dir (io/file (str (System/getProperty "user.dir") + "/android/app/build/outputs/apk")) + app (io/file dir "app-debug.apk") + capabilities (doto (DesiredCapabilities.) + (.setCapability "deviceName" "device") + (.setCapability "platformVersion" "6.0.0") + (.setCapability "app" (.getAbsolutePath app)) + (.setCapability "appPackage" "com.statusim") + (.setCapability "appActivity" ".MainActivity")) + driver (AndroidDriver. (URL. "http://127.0.0.1:4723/wd/hub") capabilities)] + (-> driver + .manage + .timeouts + (.implicitlyWait 25 TimeUnit/SECONDS)) + driver)) + +(defn by-xpath [driver xpath] + (.findElement driver (By/xpath xpath))) + +(defn elements-by-xpath [driver xpath] + (.findElements driver (By/xpath xpath))) + +(defn by-id [driver id] + (.findElementByAccessibilityId driver (name id))) + +(defn get-element [driver id] + (if (keyword? id) + (by-id driver id) + (by-xpath driver id))) + +(defn click [driver id] + (.click (get-element driver id))) + +(defn write [driver input-xpath text] + (.sendKeys (get-element driver input-xpath) (into-array [text]))) + +(defn get-text [driver xpath] + (.getText (by-xpath driver xpath))) + +(defn xpath-by-text [text] + (str ".//*[@text='" text "']")) + +(defn contains-text [driver text] + (is (= 1 (->> (xpath-by-text text) + (elements-by-xpath driver) + (.size))))) + +(defn quit [driver] + (.quit driver)) + +(defmacro appium-test + "Defines test which will create new appium session and will pass that + session as first argument to each command inside it's body. After execution + of all commands session will be closed. + + For instance, + + (appium-test my-test + (click :button) + (write :input \"oops\")) + + will be expanded to + + (deftest my-test + (let [session (init)] + (click session :button) + (write session :input \"oops\") + (quit session)))" + [name & body] + (let [sym (gensym)] + `(deftest ~name + (let [~sym (init)] + ~@(for [[f & rest] body] + `(~f ~sym ~@rest)) + (quit ~sym))))) diff --git a/test/clj/status_im/console.clj b/test/clj/status_im/console.clj new file mode 100644 index 0000000000..5cdf17ae45 --- /dev/null +++ b/test/clj/status_im/console.clj @@ -0,0 +1,15 @@ +(ns status-im.console + (:require [clojure.test :refer :all] + [status-im.appium :refer :all])) + +(def message-text + (str "Your phone number is also required to use the app. Type" + " the exclamation mark or hit the icon to open the command " + "list and choose the !phone command")) + +(appium-test console-test + (click :request-keypair-password) + (write :command-input "123") + (click :stage-command) + (click :send-message) + (contains-text message-text)) 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) diff --git a/test/status_im/core_test.clj b/test/status_im/core_test.clj deleted file mode 100644 index 1224033d1c..0000000000 --- a/test/status_im/core_test.clj +++ /dev/null @@ -1,7 +0,0 @@ -(ns status-im.core-test - (:require [clojure.test :refer :all] - [status-im.core :refer :all])) - -(deftest a-test - (testing "FIXME, I fail." - (is (= 0 1))))