merged develop

Former-commit-id: 7f207a3760
This commit is contained in:
Adrian Tiberius 2016-06-10 03:21:48 +03:00
commit 23a17b2632
41 changed files with 459 additions and 212 deletions

9
.gitignore vendored
View File

@ -50,3 +50,12 @@ target/
# #
figwheel_server.log figwheel_server.log
.nrepl-port .nrepl-port
# Lein
#
.lein-failures
## Doo
#
out
doo-index.html

View File

@ -1,7 +1,7 @@
{ {
"name": "StatusIm", "name": "StatusIm",
"interface": "reagent", "interface": "reagent",
"androidHost": "localhost", "androidHost": "10.0.3.2",
"modules": [ "modules": [
"react-native-contacts", "react-native-contacts",
"react-native-invertible-scroll-view", "react-native-invertible-scroll-view",

View File

@ -8,11 +8,18 @@ import com.facebook.react.shell.MainReactPackage;
import com.rt2zz.reactnativecontacts.ReactNativeContacts; import com.rt2zz.reactnativecontacts.ReactNativeContacts;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment; 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.github.ethereum.go_ethereum.cmd.Geth;
import com.bitgo.randombytes.RandomBytesPackage; import com.bitgo.randombytes.RandomBytesPackage;
import com.BV.LinearGradient.LinearGradientPackage; import com.BV.LinearGradient.LinearGradientPackage;
import com.centaurwarchief.smslistener.SmsListener; import com.centaurwarchief.smslistener.SmsListener;
import android.os.Handler;
import android.util.Log;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Properties; import java.util.Properties;
@ -25,15 +32,10 @@ import io.realm.react.RealmReactPackage;
public class MainActivity extends ReactActivity { public class MainActivity extends ReactActivity {
@Override final Handler handler = new Handler();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Required for android-16 (???) protected void startStatus() {
System.loadLibrary("gethraw"); // Required because of crazy APN settings redirecting localhost (found in GB)
System.loadLibrary("geth");
// Required because of crazy APN settings redirecting localhost
Properties properties = System.getProperties(); Properties properties = System.getProperties();
properties.setProperty("http.nonProxyHosts", "localhost|127.0.0.1"); properties.setProperty("http.nonProxyHosts", "localhost|127.0.0.1");
properties.setProperty("https.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; getApplicationInfo().dataDir;
// Launch! // 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() { new Thread(new Runnable() {
public void run() { 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(); }).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();
}
} }
/** /**

View File

@ -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();
}
}
}

View File

@ -1,3 +1,6 @@
<resources> <resources>
<string name="app_name">StatusIm</string> <string name="app_name">Status</string>
<string name="root_warning">Your phone appears to be ROOTED, by pressing CONTINUE you understand and accept the risks in using this software.</string>
<string name="root_okay">Continue</string>
<string name="root_cancel">Exit</string>
</resources> </resources>

View File

@ -8,7 +8,7 @@
[reagent "0.5.1" :exclusions [cljsjs/react]] [reagent "0.5.1" :exclusions [cljsjs/react]]
[re-frame "0.6.0"] [re-frame "0.6.0"]
[prismatic/schema "1.0.4"] [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"}} :branch "master"}}
[status-im/protocol "0.1.1-20160525_083359-g53ab2c2"] [status-im/protocol "0.1.1-20160525_083359-g53ab2c2"]
[natal-shell "0.1.6"] [natal-shell "0.1.6"]
@ -20,9 +20,12 @@
["do" "clean" ["do" "clean"
["with-profile" "prod" "cljsbuild" "once" "ios"] ["with-profile" "prod" "cljsbuild" "once" "ios"]
["with-profile" "prod" "cljsbuild" "once" "android"]]} ["with-profile" "prod" "cljsbuild" "once" "android"]]}
:test-paths ["test/clj"]
:figwheel {:nrepl-port 7888} :figwheel {:nrepl-port 7888}
:profiles {:dev {:dependencies [[figwheel-sidecar "0.5.0-2"] :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"] :source-paths ["src" "env/dev"]
:cljsbuild {:builds {:ios {:source-paths ["src" "env/dev"] :cljsbuild {:builds {:ios {:source-paths ["src" "env/dev"]
:figwheel true :figwheel true
@ -35,7 +38,13 @@
:compiler {:output-to "target/android/not-used.js" :compiler {:output-to "target/android/not-used.js"
:main "env.android.main" :main "env.android.main"
:output-dir "target/android" :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]}} :repl-options {:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}}
:prod {:cljsbuild {:builds {:ios {:source-paths ["src" "env/prod"] :prod {:cljsbuild {:builds {:ios {:source-paths ["src" "env/prod"]
:compiler {:output-to "index.ios.js" :compiler {:output-to "index.ios.js"
@ -46,5 +55,4 @@
:compiler {:output-to "index.android.js" :compiler {:output-to "index.android.js"
:main "env.android.main" :main "env.android.main"
:output-dir "target/android" :output-dir "target/android"
:optimizations :simple}}}} :optimizations :simple}}}}}})
}})

View File

@ -17,11 +17,13 @@ function tab () {
fi fi
osascript &>/dev/null <<EOF osascript &>/dev/null <<EOF
tell application "iTerm" tell application "iTerm2"
tell current terminal tell current window
launch session "Default Session" set newTab to (create tab with default profile)
tell the last session tell newTab
write text "cd \"$cdto\"$cmd" tell current session
write text "cd \"$cdto\"$cmd"
end tell
end tell end tell
end tell end tell
end tell end tell
@ -59,3 +61,10 @@ sleep 10s
adb reverse tcp:8081 tcp:8081 && adb reverse tcp:3449 tcp:3449 adb reverse tcp:8081 tcp:8081 && adb reverse tcp:3449 tcp:3449
react-native run-android react-native run-android
if [ ! -z $2 ]
then
tab "appium"
lein test
lein doo node test once
fi

View File

@ -1 +1,4 @@
(ns cljsjs.react) (ns cljsjs.react)
(when (exists? js/window)
(set! js/window.React (js/require "react-native")))

View File

@ -230,9 +230,9 @@
(sign-up-service/stop-listening-confirmation-code-sms db))) (sign-up-service/stop-listening-confirmation-code-sms db)))
(register-handler :sign-up-confirm (register-handler :sign-up-confirm
(fn [db [_ confirmation-code]] (u/side-effect!
(server/sign-up-confirm confirmation-code sign-up-service/on-send-code-response) (fn [_ [_ confirmation-code]]
db)) (server/sign-up-confirm confirmation-code sign-up-service/on-send-code-response))))
(register-handler :set-signed-up (register-handler :set-signed-up
(fn [db [_ signed-up]] (fn [db [_ signed-up]]

View File

@ -18,7 +18,7 @@
[status-im.components.toolbar :refer [toolbar]] [status-im.components.toolbar :refer [toolbar]]
[status-im.chat.views.message :refer [chat-message]] [status-im.chat.views.message :refer [chat-message]]
[status-im.chat.views.new-message :refer [chat-message-new]] [status-im.chat.views.new-message :refer [chat-message-new]]
[status-im.i18n :refer [label]])) [status-im.i18n :refer [label label-pluralize]]))
(defn contacts-by-identity [contacts] (defn contacts-by-identity [contacts]
@ -186,7 +186,7 @@
[icon :group st/group-icon] [icon :group st/group-icon]
[text {:style st/members} [text {:style st/members}
(let [cnt (inc (count @contacts))] (let [cnt (inc (count @contacts))]
(label :t/members {:count cnt}))]] (label-pluralize cnt :t/members))]]
;; TODO stub data: last activity ;; TODO stub data: last activity
[text {:style st/last-activity} (label :t/last-active)])]))) [text {:style st/last-activity} (label :t/last-active)])])))

View File

@ -38,11 +38,13 @@
:onChangeText set-input-message :onChangeText set-input-message
:onSubmitEditing (fn [] :onSubmitEditing (fn []
(when (valid? message validator) (when (valid? message validator)
(send-command)))} (send-command)))
:accessibility-label :command-input}
input-options) input-options)
message] message]
(if (valid? message validator) (if (valid? message validator)
[touchable-highlight {:on-press send-command} [touchable-highlight {:on-press send-command
:accessibility-label :stage-command}
[view st/send-container [icon :send st/send-icon]]] [view st/send-container [icon :send st/send-icon]]]
[touchable-highlight {:on-press cancel-command-input} [touchable-highlight {:on-press cancel-command-input}
[view st/cancel-container [view st/cancel-container

View File

@ -72,13 +72,18 @@
(defn set-chat-command [msg-id command] (defn set-chat-command [msg-id command]
(dispatch [:set-response-chat-command msg-id (:command command)])) (dispatch [:set-response-chat-command msg-id (:command command)]))
(defn label [{:keys [command]}]
(->> (name command)
(str "request-")))
(defn message-content-command-request (defn message-content-command-request
[{:keys [msg-id content from incoming-group]}] [{:keys [msg-id content from incoming-group]}]
(let [commands-atom (subscribe [:get-commands])] (let [commands-atom (subscribe [:get-commands])]
(fn [{:keys [msg-id content from incoming-group]}] (fn [{:keys [msg-id content from incoming-group]}]
(let [commands @commands-atom (let [commands @commands-atom
{:keys [command content]} (parse-command-request commands content)] {: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/comand-request-view
[view st/command-request-message-view [view st/command-request-message-view
(when incoming-group (when incoming-group

View File

@ -1,9 +1,9 @@
(ns status-im.chat.views.plain-input (ns status-im.chat.views.plain-input
(:require [re-frame.core :refer [subscribe dispatch]] (:require [re-frame.core :refer [subscribe dispatch]]
[status-im.components.react :refer [view [status-im.components.react :refer [view
icon icon
touchable-highlight touchable-highlight
text-input]] text-input]]
[status-im.chat.views.suggestions :refer [suggestions-view]] [status-im.chat.views.suggestions :refer [suggestions-view]]
[status-im.chat.styles.plain-input :as st])) [status-im.chat.styles.plain-input :as st]))
@ -39,15 +39,16 @@
(if @typing-command? (if @typing-command?
[icon :close-gray st/close-icon] [icon :close-gray st/close-icon]
[icon :list st/list-icon])]] [icon :list st/list-icon])]]
[text-input {:style st/message-input [text-input {:style st/message-input
:autoFocus (pos? (count @staged-commands-atom)) :autoFocus (pos? (count @staged-commands-atom))
:onChangeText set-input-message :onChangeText set-input-message
:onSubmitEditing #(try-send @chat @staged-commands-atom :onSubmitEditing #(try-send @chat @staged-commands-atom
input-message)} input-message)}
input-message] input-message]
;; TODO emoticons: not implemented ;; TODO emoticons: not implemented
[icon :smile st/smile-icon] [icon :smile st/smile-icon]
(when (message-valid? @staged-commands-atom input-message) (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 [view st/send-container
[icon :send st/send-icon]]])]])))) [icon :send st/send-icon]]])]]))))

View File

@ -36,7 +36,7 @@
[drawer-view [drawer-view
[view st/chats-container [view st/chats-container
[chats-list-toolbar] [chats-list-toolbar]
[list-view {:dataSource (to-datasource (vals @chats)) [list-view {:dataSource (to-datasource @chats)
:renderRow (fn [row _ _] :renderRow (fn [row _ _]
(list-item [chat-list-item row])) (list-item [chat-list-item row]))
:style st/list-container}] :style st/list-container}]

View File

@ -1,17 +1,18 @@
(ns status-im.chats-list.views.chat-list-item (ns status-im.chats-list.views.chat-list-item
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
[status-im.components.react :refer [view [status-im.components.react :refer [view
text text
image image
touchable-highlight]] touchable-highlight]]
[status-im.components.styles :refer [font]] [status-im.components.styles :refer [font]]
[status-im.chats-list.views.inner-item :refer [status-im.chats-list.views.inner-item :refer
[chat-list-item-inner-view]])) [chat-list-item-inner-view]]))
(defn chat-list-item [{:keys [chat-id] :as chat}] (defn chat-list-item [[chat-id chat]]
[touchable-highlight [touchable-highlight
{:on-press #(dispatch [:navigate-to :chat chat-id])} {:on-press #(dispatch [:navigate-to :chat chat-id])}
[view [chat-list-item-inner-view (merge chat [view [chat-list-item-inner-view (merge chat
;; TODO stub data ;; TODO stub data
{:new-messages-count 3 {:chat-id chat-id
:new-messages-count 3
:online true})]]]) :online true})]]])

View File

@ -1,7 +1,7 @@
(ns status-im.components.action-button (ns status-im.components.action-button
(:require [reagent.core :as r])) (: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 (r/adapt-react-class (.-default class)))
(def action-button-item (r/adapt-react-class (.. js/ActionButton -default -Item))) (def action-button-item (r/adapt-react-class (.. class -default -Item)))

View File

@ -5,11 +5,12 @@
touchable-without-feedback touchable-without-feedback
text]] text]]
[status-im.components.carousel.styles :as st] [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 [] (defn window-page-width []
(.-width (.get (.. js/React -Dimensions) "window"))) (.-width (.get (.. r/react -Dimensions) "window")))
(def defaults {:gap 10 (def defaults {:gap 10
:sneak 10 :sneak 10

View File

@ -6,7 +6,8 @@
image image
icon]] icon]]
[status-im.components.chat-icon.styles :as st] [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] (defn default-chat-icon [name styles]
[view (:default-chat-icon styles) [view (:default-chat-icon styles)
@ -26,10 +27,10 @@
(defview chat-icon-view [chat-id group-chat name online styles] (defview chat-icon-view [chat-id group-chat name online styles]
[photo-path [:chat-photo chat-id]] [photo-path [:chat-photo chat-id]]
[view (:container styles) [view (:container styles)
(if (and photo-path (not (empty? photo-path))) (if-not (s/blank? photo-path)
[chat-icon photo-path styles] [chat-icon photo-path styles]
[default-chat-icon name styles]) [default-chat-icon name styles])
(when (not group-chat) (when-not group-chat
[contact-online online styles])]) [contact-online online styles])])
(defn chat-icon-view-chat-list [chat-id group-chat name color online] (defn chat-icon-view-chat-list [chat-id group-chat name color online]
@ -80,7 +81,7 @@
[contact [:contact]] [contact [:contact]]
(let [;; TODO stub data (let [;; TODO stub data
online true online true
color color-purple] color color-purple]
[profile-icon-view (:photo-path contact) (:name contact) color online])) [profile-icon-view (:photo-path contact) (:name contact) color online]))
(defview my-profile-icon [] (defview my-profile-icon []
@ -88,5 +89,5 @@
photo-path [:get :photo-path]] photo-path [:get :photo-path]]
(let [;; TODO stub data (let [;; TODO stub data
online true online true
color color-purple] color color-purple]
[profile-icon-view photo-path name color online])) [profile-icon-view photo-path name color online]))

View File

@ -2,14 +2,14 @@
(:require [clojure.string :as s] (:require [clojure.string :as s]
[re-frame.core :refer [subscribe dispatch dispatch-sync]] [re-frame.core :refer [subscribe dispatch dispatch-sync]]
[reagent.core :as r] [reagent.core :as r]
[status-im.components.react :refer [android? [status-im.components.react :refer [react
view view
text text
image image
navigator navigator
toolbar-android toolbar-android
drawer-layout-android drawer-layout-android
touchable-opacity]] touchable-opacity]]
[status-im.resources :as res] [status-im.resources :as res]
[status-im.components.drawer.styles :as st] [status-im.components.drawer.styles :as st]
[status-im.i18n :refer [label]])) [status-im.i18n :refer [label]]))
@ -29,7 +29,7 @@
:style st/user-photo}]) :style st/user-photo}])
(defn menu-item [{:keys [name handler]}] (defn menu-item [{:keys [name handler]}]
[touchable-opacity {:style st/menu-item-touchable [touchable-opacity {:style st/menu-item-touchable
:onPress (fn [] :onPress (fn []
(close-drawer) (close-drawer)
(handler))} (handler))}
@ -40,40 +40,40 @@
(let [username (subscribe [:get :username])] (let [username (subscribe [:get :username])]
(fn [] (fn []
[view st/drawer-menu [view st/drawer-menu
[view st/user-photo-container [view st/user-photo-container
[user-photo {}]] [user-photo {}]]
[view st/name-container [view st/name-container
[text {:style st/name-text} [text {:style st/name-text}
@username]] @username]]
[view st/menu-items-container [view st/menu-items-container
[menu-item {:name (label :t/profile) [menu-item {:name (label :t/profile)
:handler #(dispatch [:navigate-to :my-profile])}] :handler #(dispatch [:navigate-to :my-profile])}]
[menu-item {:name (label :t/settings) [menu-item {:name (label :t/settings)
:handler (fn [] :handler (fn []
;; TODO not implemented ;; TODO not implemented
)}] )}]
[menu-item {:name (label :t/discovery) [menu-item {:name (label :t/discovery)
:handler #(dispatch [:navigate-to :discovery])}] :handler #(dispatch [:navigate-to :discovery])}]
[menu-item {:name (label :t/contacts) [menu-item {:name (label :t/contacts)
:handler #(dispatch [:show-contacts navigator])}] :handler #(dispatch [:show-contacts navigator])}]
[menu-item {:name (label :t/invite-friends) [menu-item {:name (label :t/invite-friends)
:handler (fn [] :handler (fn []
;; TODO not implemented ;; TODO not implemented
)}] )}]
[menu-item {:name (label :t/faq) [menu-item {:name (label :t/faq)
:handler (fn [])}]] :handler (fn [])}]]
[view st/switch-users-container [view st/switch-users-container
[touchable-opacity {:onPress (fn [] [touchable-opacity {:onPress (fn []
(close-drawer) (close-drawer)
;; TODO not implemented ;; TODO not implemented
)} )}
[text {:style st/switch-users-text} [text {:style st/switch-users-text}
(label :t/switch-users)]]]]))) (label :t/switch-users)]]]])))
(defn drawer-view [items] (defn drawer-view [items]
[drawer-layout-android {:drawerWidth 260 [drawer-layout-android {:drawerWidth 260
:drawerPosition js/React.DrawerLayoutAndroid.positions.Left :drawerPosition js/React.DrawerLayoutAndroid.positions.Left
:render-navigation-view #(r/as-element [drawer-menu]) :render-navigation-view #(r/as-element [drawer-menu])
:ref (fn [drawer] :ref (fn [drawer]
(reset! drawer-atom drawer))} (reset! drawer-atom drawer))}
items]) items])

View File

@ -1,6 +1,4 @@
(ns status-im.components.icons.ionicons (ns status-im.components.icons.ionicons
(:require [reagent.core :as r])) (:require [reagent.core :as r]))
(set! js/window.Ionicons (js/require "react-native-vector-icons/Ionicons")) (def icon (r/adapt-react-class (js/require "react-native-vector-icons/Ionicons")))
(def icon (r/adapt-react-class js/Ionicons))

View File

@ -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] (defn invertible-scroll-view [props]
(js/React.createElement js/InvertibleScrollView (r/create-element class (clj->js (merge {:inverted true} props))))
(clj->js (merge {:inverted true} props))))

View File

@ -1,7 +1,5 @@
(ns status-im.components.item-checkbox (ns status-im.components.item-checkbox
(:require [reagent.core :as r])) (:require [reagent.core :as r]))
(set! js/window.ItemCheckbox (js/require "react-native-circle-checkbox")) (def item-checkbox (r/adapt-react-class (js/require "react-native-circle-checkbox")))
(def item-checkbox (r/adapt-react-class js/ItemCheckbox))

View File

@ -1,26 +1,36 @@
(ns status-im.components.react (ns status-im.components.react
(:require [reagent.core :as r] (: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)) (defn get-react-property [name]
(def navigator (r/adapt-react-class (.-Navigator js/React))) (aget react name))
(def text (r/adapt-react-class (.-Text js/React)))
(def view (r/adapt-react-class (.-View js/React))) (defn adapt-class [class]
(def image (r/adapt-react-class (.-Image js/React))) (when class (r/adapt-react-class class)))
(def touchable-highlight-class (r/adapt-react-class (.-TouchableHighlight js/React)))
(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] (defn touchable-highlight [props content]
[touchable-highlight-class [touchable-highlight-class
(merge {:underlay-color :transparent} props) (merge {:underlay-color :transparent} props)
content]) content])
(def toolbar-android (r/adapt-react-class (.-ToolbarAndroid js/React))) (def toolbar-android (get-class "ToolbarAndroid"))
(def list-view-class (r/adapt-react-class (.-ListView js/React))) (def list-view-class (get-class "ListView"))
(defn list-view [props] (defn list-view [props]
[list-view-class (merge {:enableEmptySections true} props)]) [list-view-class (merge {:enableEmptySections true} props)])
(def scroll-view (r/adapt-react-class (.-ScrollView js/React))) (def scroll-view (get-class "ScrollView"))
(def touchable-without-feedback (r/adapt-react-class (.-TouchableWithoutFeedback js/React))) (def touchable-without-feedback (get-class "TouchableWithoutFeedback"))
(def text-input-class (r/adapt-react-class (.-TextInput js/React))) (def text-input-class (get-class "TextInput"))
(defn text-input [props text] (defn text-input [props text]
[text-input-class (merge [text-input-class (merge
{:underlineColorAndroid :transparent {:underlineColorAndroid :transparent
@ -28,11 +38,13 @@
:placeholder "Type"} :placeholder "Type"}
props) props)
text]) text])
(def drawer-layout-android (r/adapt-react-class (.-DrawerLayoutAndroid js/React))) (def drawer-layout-android (get-class "DrawerLayoutAndroid"))
(def touchable-opacity (r/adapt-react-class (.-TouchableOpacity js/React))) (def touchable-opacity (get-class "TouchableOpacity"))
(def modal (r/adapt-react-class (.-Modal js/React))) (def modal (get-class "Modal"))
(def picker (r/adapt-react-class (.-Picker js/React))) (def picker (get-class "Picker"))
(def picker-item (r/adapt-react-class (.-Item (.-Picker js/React)))) (def picker-item
(when-let [picker (get-react-property "Picker")]
(adapt-class (.-Item picker))))
(defn icon (defn icon
@ -41,20 +53,18 @@
[image {:source {:uri (keyword (str "icon_" (name n)))} [image {:source {:uri (keyword (str "icon_" (name n)))}
:style style}])) :style style}]))
;(def react-linear-gradient (.-default (js/require "react-native-linear-gradient"))) (def linear-gradient-class (u/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"))
(defn linear-gradient [props] (defn linear-gradient [props]
(js/React.createElement js/LinearGradient (r/create-element linear-gradient-class
(clj->js (merge {:inverted true} props)))) (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")) (def android? (= platform "android"))
(defn list-item [component] (defn list-item [component]
(r/as-element component)) (r/as-element component))
(def dismiss-keyboard! (js/require "dismissKeyboard")) (def dismiss-keyboard! (u/require "dismissKeyboard"))

View File

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

View File

@ -16,9 +16,7 @@
toolbar-title-container toolbar-title-container
toolbar-title-text toolbar-title-text
icon-back icon-back
toolbar-height]] toolbar-height]]))
[status-im.components.realm :refer [list-view]]
[reagent.core :as r]))
(defn toolbar [{:keys [title nav-action hide-nav? action custom-action (defn toolbar [{:keys [title nav-action hide-nav? action custom-action
background-color custom-content style]}] background-color custom-content style]}]

View File

@ -5,7 +5,8 @@
[clojure.string :as s] [clojure.string :as s]
[status-im.utils.utils :refer [http-post]] [status-im.utils.utils :refer [http-post]]
[status-im.utils.phone-number :refer [format-phone-number]] [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 (defn save-contact
[_ [_ contact]] [_ [_ contact]]
@ -26,7 +27,7 @@
(register-handler :load-contacts load-contacts!) (register-handler :load-contacts load-contacts!)
;; TODO see https://github.com/rt2zz/react-native-contacts/issues/45 ;; 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] (defn contact-name [contact]
(->> contact (->> contact
@ -91,10 +92,14 @@
(defn add-new-contacts (defn add-new-contacts
[{:keys [contacts] :as db} [_ new-contacts]] [{:keys [contacts] :as db} [_ new-contacts]]
(let [identities (set (map :whisper-identity 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 (-> db
(update :contacts concat new-contacts') (update :contacts merge new-contacts')
(assoc :new-contacts new-contacts')))) (assoc :new-contacts (vals new-contacts')))))
(register-handler :add-contacts (register-handler :add-contacts
(after save-contacts!) (after save-contacts!)

View File

@ -2,16 +2,16 @@
(:require-macros [status-im.utils.views :refer [defview]]) (:require-macros [status-im.utils.views :refer [defview]])
(:require (:require
[re-frame.core :refer [subscribe]] [re-frame.core :refer [subscribe]]
[status-im.utils.logging :as log]
[status-im.components.react :refer [android? [status-im.components.react :refer [android?
text]] text]]
[status-im.components.carousel.carousel :refer [carousel]] [status-im.components.carousel.carousel :refer [carousel]]
[status-im.discovery.styles :as st] [status-im.discovery.styles :as st]
[status-im.discovery.views.popular-list :refer [discovery-popular-list]] [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 [] (defn page-width []
(.-width (.get (.. js/React -Dimensions) "window"))) (.-width (.get (.. r/react -Dimensions) "window")))
(defview popular [] (defview popular []
[popular-tags [:get-popular-tags 3]] [popular-tags [:get-popular-tags 3]]

View File

@ -36,15 +36,19 @@
;; -- Common -------------------------------------------------------------- ;; -- Common --------------------------------------------------------------
(defn set-el [db [_ k v]]
(assoc db k v))
(register-handler :set (register-handler :set
(debug debug
(fn [db [_ k v]] set-el)
(assoc db k v))))
(defn set-in [db [_ path v]]
(assoc-in db path v))
(register-handler :set-in (register-handler :set-in
(debug debug
(fn [db [_ path v]] set-in)
(assoc-in db path v))))
(register-handler :initialize-db (register-handler :initialize-db
(fn [_ _] (fn [_ _]

View File

@ -1,25 +1,23 @@
(ns status-im.i18n (ns status-im.i18n
(:require (: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")) (def i18n (u/require "react-native-i18n"))
(set! (.-fallbacks js/I18n) true) (set! (.-fallbacks i18n) true)
(set! (.-defaultSeparator js/I18n) "/") (set! (.-defaultSeparator i18n) "/")
(set! (.-translations js.I18n) (clj->js {:en en/translations})) (set! (.-translations i18n) (clj->js {:en en/translations}))
(defn label (defn label
([path] (label path {})) ([path] (label path {}))
([path options] ([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] (defn label-pluralize [count path & options]
(let [translations (.-translations js/I18n)] (if (exists? i18n.t)
(set! (.-translations js/I18n) (clj->js (deep-merge (js->clj translations) new-translations))))) (.p i18n count (name path) (clj->js options))
) (name path)))

View File

@ -2,11 +2,10 @@
(:require [cljs.reader :refer [read-string]] (:require [cljs.reader :refer [read-string]]
[status-im.components.styles :refer [default-chat-color]] [status-im.components.styles :refer [default-chat-color]]
[status-im.utils.logging :as log] [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?])) (:refer-clojure :exclude [exists?]))
(set! js/window.Realm (js/require "realm"))
(def opts {:schema [{:name :contacts (def opts {:schema [{:name :contacts
:primaryKey :whisper-identity :primaryKey :whisper-identity
:properties {:phone-number {:type "string" :properties {:phone-number {:type "string"
@ -70,7 +69,10 @@
:objectType "tag"} :objectType "tag"}
:last-updated "date"}}]}) :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) (def schema-by-name (->> (:schema opts)
(mapv (fn [{:keys [name] :as schema}] (mapv (fn [{:keys [name] :as schema}]

View File

@ -2,14 +2,14 @@
(:require-macros [status-im.utils.views :refer [defview]]) (:require-macros [status-im.utils.views :refer [defview]])
(:require [re-frame.core :refer [subscribe dispatch]] (:require [re-frame.core :refer [subscribe dispatch]]
[status-im.components.react :refer [view [status-im.components.react :refer [view
text text
image image
icon icon
scroll-view scroll-view
touchable-highlight touchable-highlight
touchable-opacity]] touchable-opacity]]
[status-im.components.chat-icon.screen :refer [profile-icon [status-im.components.chat-icon.screen :refer [profile-icon
my-profile-icon]] my-profile-icon]]
[status-im.profile.styles :as st] [status-im.profile.styles :as st]
[status-im.components.qr-code :refer [qr-code]] [status-im.components.qr-code :refer [qr-code]]
[status-im.utils.types :refer [clj->json]] [status-im.utils.types :refer [clj->json]]
@ -62,13 +62,13 @@
[text {:style st/report-user-text} (label :t/report-user)]]]]]) [text {:style st/report-user-text} (label :t/report-user)]]]]])
(defview my-profile [] (defview my-profile []
[username [:get :username] [username [:get :username]
photo-path [:get :photo-path] photo-path [:get :photo-path]
phone-number [:get :phone-number] phone-number [:get :phone-number]
email [:get :email] email [:get :email]
status [:get :status] status [:get :status]
identity [:get-in [:user-identity :public]]] identity [:get-in [:user-identity :public]]]
[view {:style st/profile} [scroll-view {:style st/profile}
[touchable-highlight {:style st/back-btn-touchable [touchable-highlight {:style st/back-btn-touchable
:on-press #(dispatch [:navigate-back])} :on-press #(dispatch [:navigate-back])}
[view st/back-btn-container [view st/back-btn-container

View File

@ -1,9 +1,10 @@
(ns status-im.utils.crypt (ns status-im.utils.crypt
(:require [goog.crypt :refer [byteArrayToHex]] (:require [goog.crypt :refer [byteArrayToHex]]
[clojure.string :as s]) [clojure.string :as s]
[status-im.utils.utils :as u])
(:import goog.crypt.Sha256)) (: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.)) (def sha-256 (Sha256.))
@ -19,7 +20,7 @@
(byteArrayToHex (.digest sha-256))) (byteArrayToHex (.digest sha-256)))
(defn gen-random-bytes [length cb] (defn gen-random-bytes [length cb]
(.randomBytes js/window.RnRandomBytes length (fn [& [err buf]] #_(.randomBytes random-bytes length (fn [& [err buf]]
(if err (if err
(cb {:error err}) (cb {:error err})
(cb {:buffer buf}))))) (cb {:buffer buf})))))

View File

@ -1,6 +1,5 @@
(ns status-im.utils.listview (ns status-im.utils.listview
(:require-macros [natal-shell.data-source :refer [data-source]]) (:require-macros [natal-shell.data-source :refer [data-source]]))
(:require [status-im.components.realm]))
(defn clone-with-rows [ds rows] (defn clone-with-rows [ds rows]
(.cloneWithRows ds (reduce (fn [ac el] (.push ac el) ac) (.cloneWithRows ds (reduce (fn [ac el] (.push ac el) ac)

View File

@ -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 i18n (u/require "react-native-i18n"))
(def locale (.-locale i18n)) (def locale (or (.-locale i18n) "___en"))
(def country-code (subs locale 3 5)) (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 ;; todo check wrong numbers, .getNumber returns empty string
(defn format-phone-number [number] (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] (defn valid-mobile-number? [number]
(when (string? 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) (and (.isValid number-obj)
(.isMobile number-obj))))) (.isMobile number-obj)))))

View File

@ -1,7 +1,8 @@
(ns status-im.utils.sms-listener (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! ;; Only android is supported!

View File

@ -5,6 +5,11 @@
[natal-shell.toast-android :as toast]) [natal-shell.toast-android :as toast])
(:require [status-im.constants :as const])) (:require [status-im.constants :as const]))
(defn require [module]
(if (exists? js/window)
(js/require module)
#js {}))
(defn log [obj] (defn log [obj]
(.log js/console obj)) (.log js/console obj))

View File

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

View File

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

View File

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

View File

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

View File

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