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