diff --git a/.re-natal b/.re-natal
index f1c7bbd66b..657ba14566 100644
--- a/.re-natal
+++ b/.re-natal
@@ -1,7 +1,7 @@
{
"name": "StatusIm",
"interface": "reagent",
- "androidHost": "10.0.3.2",
+ "androidHost": "localhost",
"modules": [
"react-native-contacts",
"react-native-invertible-scroll-view",
@@ -17,7 +17,9 @@
"dismissKeyboard",
"react-native-linear-gradient",
"react-native-android-sms-listener",
- "react-native-status"
+ "react-native-status",
+ "react-native-camera",
+ "react-native-qrcode"
],
"imageDirs": [
"images"
diff --git a/android/app/build.gradle b/android/app/build.gradle
index edc54bc340..4182c69686 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -130,6 +130,7 @@ dependencies {
compile project(':react-native-i18n')
compile project(':react-native-linear-gradient')
compile project(':ReactNativeAndroidSmsListener')
+ compile project(':react-native-camera')
compile project(':react-native-status')
// todo replace this when jail will be integrated into geth
compile (name: "geth-android-16", ext:"aar")
diff --git a/android/app/src/main/java/com/statusim/MainActivity.java b/android/app/src/main/java/com/statusim/MainActivity.java
index dc1a4cd324..a074f3f653 100644
--- a/android/app/src/main/java/com/statusim/MainActivity.java
+++ b/android/app/src/main/java/com/statusim/MainActivity.java
@@ -8,31 +8,35 @@ 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;
import java.io.File;
import com.statusim.Jail.JailPackage;
+import com.lwansbrough.RCTCamera.*;
import com.i18n.reactnativei18n.ReactNativeI18n;
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");
@@ -44,11 +48,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();
+ }
+
}
/**
@@ -84,6 +133,7 @@ public class MainActivity extends ReactActivity {
new ReactNativeI18n(),
new RandomBytesPackage(),
new LinearGradientPackage(),
+ new RCTCameraPackage(),
new SmsListener(this)
);
}
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/drawable-hdpi/corner_left_bottom.png b/android/app/src/main/res/drawable-hdpi/corner_left_bottom.png
new file mode 100644
index 0000000000..1c9c9b446d
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/corner_left_bottom.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/corner_left_top.png b/android/app/src/main/res/drawable-hdpi/corner_left_top.png
new file mode 100644
index 0000000000..1a0a4e57f8
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/corner_left_top.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/corner_right_bottom.png b/android/app/src/main/res/drawable-hdpi/corner_right_bottom.png
new file mode 100644
index 0000000000..e6071d1ae1
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/corner_right_bottom.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/corner_right_top.png b/android/app/src/main/res/drawable-hdpi/corner_right_top.png
new file mode 100644
index 0000000000..6eaca3cd9a
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/corner_right_top.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/icon_back_white.png b/android/app/src/main/res/drawable-hdpi/icon_back_white.png
new file mode 100644
index 0000000000..6bd5aec7c4
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_back_white.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/icon_close_white.png b/android/app/src/main/res/drawable-hdpi/icon_close_white.png
new file mode 100644
index 0000000000..79f55e700a
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_close_white.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/icon_dollar_green.png b/android/app/src/main/res/drawable-hdpi/icon_dollar_green.png
new file mode 100644
index 0000000000..32cf51e421
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_dollar_green.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/icon_drag_white.png b/android/app/src/main/res/drawable-hdpi/icon_drag_white.png
new file mode 100644
index 0000000000..6a079b6f6f
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_drag_white.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/icon_qr.png b/android/app/src/main/res/drawable-hdpi/icon_qr.png
new file mode 100644
index 0000000000..6b2f825374
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_qr.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/corner_left_bottom.png b/android/app/src/main/res/drawable-mdpi/corner_left_bottom.png
new file mode 100644
index 0000000000..da09ea7a24
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/corner_left_bottom.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/corner_left_top.png b/android/app/src/main/res/drawable-mdpi/corner_left_top.png
new file mode 100644
index 0000000000..988d93651c
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/corner_left_top.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/corner_right_bottom.png b/android/app/src/main/res/drawable-mdpi/corner_right_bottom.png
new file mode 100644
index 0000000000..e74544e83b
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/corner_right_bottom.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/corner_right_top.png b/android/app/src/main/res/drawable-mdpi/corner_right_top.png
new file mode 100644
index 0000000000..33726dfa01
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/corner_right_top.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/icon_back_white.png b/android/app/src/main/res/drawable-mdpi/icon_back_white.png
new file mode 100644
index 0000000000..ac448ee920
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_back_white.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/icon_close_white.png b/android/app/src/main/res/drawable-mdpi/icon_close_white.png
new file mode 100644
index 0000000000..956a2c04fb
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_close_white.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/icon_dollar_green.png b/android/app/src/main/res/drawable-mdpi/icon_dollar_green.png
new file mode 100644
index 0000000000..fc6f77f756
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_dollar_green.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/icon_drag_white.png b/android/app/src/main/res/drawable-mdpi/icon_drag_white.png
new file mode 100644
index 0000000000..7845600020
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_drag_white.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/icon_qr.png b/android/app/src/main/res/drawable-mdpi/icon_qr.png
new file mode 100644
index 0000000000..c8b6ef10fd
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_qr.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/corner_left_bottom.png b/android/app/src/main/res/drawable-xhdpi/corner_left_bottom.png
new file mode 100644
index 0000000000..b43f25a58d
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/corner_left_bottom.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/corner_left_top.png b/android/app/src/main/res/drawable-xhdpi/corner_left_top.png
new file mode 100644
index 0000000000..f8180a9c1c
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/corner_left_top.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/corner_right_bottom.png b/android/app/src/main/res/drawable-xhdpi/corner_right_bottom.png
new file mode 100644
index 0000000000..ce0a7ab9c0
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/corner_right_bottom.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/corner_right_top.png b/android/app/src/main/res/drawable-xhdpi/corner_right_top.png
new file mode 100644
index 0000000000..2ae2e61860
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/corner_right_top.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/icon_back_white.png b/android/app/src/main/res/drawable-xhdpi/icon_back_white.png
new file mode 100644
index 0000000000..bcc735db8e
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_back_white.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/icon_close_white.png b/android/app/src/main/res/drawable-xhdpi/icon_close_white.png
new file mode 100644
index 0000000000..5c8005f904
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_close_white.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/icon_dollar_green.png b/android/app/src/main/res/drawable-xhdpi/icon_dollar_green.png
new file mode 100644
index 0000000000..cdea0d4af4
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_dollar_green.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/icon_drag_white.png b/android/app/src/main/res/drawable-xhdpi/icon_drag_white.png
new file mode 100644
index 0000000000..2b200673bb
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_drag_white.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/icon_qr.png b/android/app/src/main/res/drawable-xhdpi/icon_qr.png
new file mode 100644
index 0000000000..c55551f687
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_qr.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/corner_left_bottom.png b/android/app/src/main/res/drawable-xxhdpi/corner_left_bottom.png
new file mode 100644
index 0000000000..0fff341220
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/corner_left_bottom.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/corner_left_top.png b/android/app/src/main/res/drawable-xxhdpi/corner_left_top.png
new file mode 100644
index 0000000000..e25037ece3
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/corner_left_top.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/corner_right_bottom.png b/android/app/src/main/res/drawable-xxhdpi/corner_right_bottom.png
new file mode 100644
index 0000000000..e542ec0d64
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/corner_right_bottom.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/corner_right_top.png b/android/app/src/main/res/drawable-xxhdpi/corner_right_top.png
new file mode 100644
index 0000000000..a6a65aedc9
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/corner_right_top.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_back_white.png b/android/app/src/main/res/drawable-xxhdpi/icon_back_white.png
new file mode 100644
index 0000000000..166bc865a4
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_back_white.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_close_white.png b/android/app/src/main/res/drawable-xxhdpi/icon_close_white.png
new file mode 100644
index 0000000000..32e84becb6
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_close_white.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_dollar_green.png b/android/app/src/main/res/drawable-xxhdpi/icon_dollar_green.png
new file mode 100644
index 0000000000..dac30955fe
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_dollar_green.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_drag_white.png b/android/app/src/main/res/drawable-xxhdpi/icon_drag_white.png
new file mode 100644
index 0000000000..23fa971c5e
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_drag_white.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_qr.png b/android/app/src/main/res/drawable-xxhdpi/icon_qr.png
new file mode 100644
index 0000000000..f7b3c5ad15
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_qr.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/corner_left_bottom.png b/android/app/src/main/res/drawable-xxxhdpi/corner_left_bottom.png
new file mode 100644
index 0000000000..79d8ff98d3
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/corner_left_bottom.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/corner_left_top.png b/android/app/src/main/res/drawable-xxxhdpi/corner_left_top.png
new file mode 100644
index 0000000000..8ccdd18d77
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/corner_left_top.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/corner_right_bottom.png b/android/app/src/main/res/drawable-xxxhdpi/corner_right_bottom.png
new file mode 100644
index 0000000000..1fa347dffe
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/corner_right_bottom.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/corner_right_top.png b/android/app/src/main/res/drawable-xxxhdpi/corner_right_top.png
new file mode 100644
index 0000000000..a26ebe0e61
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/corner_right_top.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_back_white.png b/android/app/src/main/res/drawable-xxxhdpi/icon_back_white.png
new file mode 100644
index 0000000000..8992428891
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_back_white.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_close_white.png b/android/app/src/main/res/drawable-xxxhdpi/icon_close_white.png
new file mode 100644
index 0000000000..68dbc7fb36
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_close_white.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_dollar_green.png b/android/app/src/main/res/drawable-xxxhdpi/icon_dollar_green.png
new file mode 100644
index 0000000000..1f3c1c57d1
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_dollar_green.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_drag_white.png b/android/app/src/main/res/drawable-xxxhdpi/icon_drag_white.png
new file mode 100644
index 0000000000..fd3f08e9ae
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_drag_white.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_qr.png b/android/app/src/main/res/drawable-xxxhdpi/icon_qr.png
new file mode 100644
index 0000000000..863a0a5eb1
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_qr.png differ
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/android/settings.gradle b/android/settings.gradle
index a42e8ee4c2..fa0064d1f4 100644
--- a/android/settings.gradle
+++ b/android/settings.gradle
@@ -21,3 +21,6 @@ project(':ReactNativeAndroidSmsListener').projectDir = new File(rootProject.proj
include ':react-native-status'
project(':react-native-status').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-status/android')
+include ':react-native-camera'
+project(':react-native-camera').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-camera/android')
+
diff --git a/env/dev/env/android/main.cljs b/env/dev/env/android/main.cljs
index 1bf474e505..f0c3d96fca 100644
--- a/env/dev/env/android/main.cljs
+++ b/env/dev/env/android/main.cljs
@@ -10,7 +10,7 @@
(def root-el (r/as-element [reloader]))
(figwheel/watch-and-reload
- :websocket-url "ws://localhost:3449/figwheel-ws"
+ :websocket-url "ws://10.0.3.2:3449/figwheel-ws"
:heads-up-display false
:jsload-callback #(swap! cnt inc))
diff --git a/package.json b/package.json
index e6ea438243..59e41e8753 100644
--- a/package.json
+++ b/package.json
@@ -11,12 +11,14 @@
"react-native": "^0.24.1",
"react-native-action-button": "^1.1.4",
"react-native-android-sms-listener": "^0.1.3",
+ "react-native-camera": "github:codyhazelwood/react-native-camera",
"react-native-circle-checkbox": "^0.1.3",
"react-native-contacts": "^0.2.4",
"react-native-i18n": "0.0.8",
"react-native-invertible-scroll-view": "^1.0.0",
"react-native-linear-gradient": "^1.5.7",
"react-native-loading-spinner-overlay": "0.0.8",
+ "react-native-qrcode": "^0.2.2",
"react-native-randombytes": "^2.0.0",
"react-native-vector-icons": "^1.3.4",
"realm": "^0.11.1",
diff --git a/resources/commands.js b/resources/commands.js
index b4820adf51..47bd9909d4 100644
--- a/resources/commands.js
+++ b/resources/commands.js
@@ -62,6 +62,7 @@ var descriptionStyle = {
};
function startsWith(str1, str2) {
+ // String.startsWith(...) doesn't work in otto
return str1.lastIndexOf(str2, 0) == 0 && str1 != str2;
}
@@ -134,7 +135,7 @@ status.response({
name: "confirmation-code",
color: "#7099e6",
description: "Confirmation code",
- parameters: [{
+ params: [{
name: "code",
type: status.types.NUMBER
}],
@@ -151,7 +152,7 @@ status.response({
color: "#7099e6",
description: "Keypair password",
icon: "icon_lock_white",
- parameters: [{
+ params: [{
name: "password",
type: status.types.PASSWORD
}],
diff --git a/src/status_im/android/core.cljs b/src/status_im/android/core.cljs
index c864b06ec6..951c7d1b32 100644
--- a/src/status_im/android/core.cljs
+++ b/src/status_im/android/core.cljs
@@ -8,6 +8,8 @@
[status-im.components.react :refer [navigator app-registry]]
[status-im.components.main-tabs :refer [main-tabs]]
[status-im.contacts.screen :refer [contact-list]]
+ [status-im.contacts.views.new-contact :refer [new-contact]]
+ [status-im.qr-scanner.screen :refer [qr-scanner]]
[status-im.discovery.screen :refer [discovery]]
[status-im.discovery.tag :refer [discovery-tag]]
[status-im.chat.screen :refer [chat]]
@@ -44,6 +46,8 @@
:new-group [new-group]
:group-settings [group-settings]
:contact-list [main-tabs]
+ :new-contact [new-contact]
+ :qr-scanner [qr-scanner]
:chat [chat]
:profile [profile]
:my-profile [my-profile]))))
diff --git a/src/status_im/chat/handlers.cljs b/src/status_im/chat/handlers.cljs
index d7d6b4cc60..d66ab6440b 100644
--- a/src/status_im/chat/handlers.cljs
+++ b/src/status_im/chat/handlers.cljs
@@ -3,6 +3,7 @@
[status-im.models.commands :as commands]
[clojure.string :as str]
[status-im.components.styles :refer [default-chat-color]]
+ [status-im.chat.styles.response :refer [request-info-height response-height-normal]]
[status-im.chat.suggestions :as suggestions]
[status-im.protocol.api :as api]
[status-im.models.messages :as messages]
@@ -15,10 +16,13 @@
[status-im.utils.handlers :as u]
[status-im.persistence.realm :as r]
[status-im.handlers.server :as server]
+ [status-im.handlers.content-suggestions :refer [get-content-suggestions]]
[status-im.utils.phone-number :refer [format-phone-number]]
[status-im.utils.datetime :as time]
[status-im.components.jail :as j]
- [status-im.commands.utils :refer [generate-hiccup]]))
+ [status-im.commands.utils :refer [generate-hiccup]]
+ [status-im.chat.handlers.animation :refer [update-response-height
+ get-response-height]]))
(register-handler :set-show-actions
(fn [db [_ show-actions]]
@@ -59,10 +63,27 @@
#(dispatch [:suggestions-handler {:command command
:content content} %]))))
+(register-handler :start-cancel-command
+ (u/side-effect!
+ (fn [db _]
+ (if (commands/get-chat-command-to-msg-id db)
+ (dispatch [:animate-cancel-command])
+ (dispatch [:cancel-command])))))
+
+(defn animate-set-chat-command-content [db _]
+ (when (commands/get-chat-command-to-msg-id db)
+ (dispatch [:animate-response-resize])))
+
(register-handler :set-chat-command-content
- (after invoke-suggestions-handler!)
- (fn [db [_ content]]
- (commands/set-chat-command-content db content)))
+ [(after invoke-suggestions-handler!)
+ (after animate-set-chat-command-content)]
+ (fn [{:keys [current-chat-id] :as db} [_ content]]
+ (as-> db db
+ (commands/set-chat-command-content db content)
+ (assoc-in db [:chats current-chat-id :input-text] nil)
+ (if (commands/get-chat-command-to-msg-id db)
+ (update-response-height db)
+ db))))
(defn update-input-text
[{:keys [current-chat-id] :as db} text]
@@ -85,7 +106,7 @@
(register-handler :stage-command
(after invoke-command-preview!)
(fn [{:keys [current-chat-id] :as db} _]
- (let [db (update-input-text db nil)
+ (let [db (update-input-text db nil)
{:keys [command content]}
(get-in db [:chats current-chat-id :command-input])
command-info {:command command
@@ -94,8 +115,19 @@
(commands/stage-command command-info)
(assoc :staged-command command-info)))))
+(register-handler :set-message-input []
+ (fn [db [_ input]]
+ (assoc db :message-input input)))
+
+(register-handler :blur-message-input
+ (u/side-effect!
+ (fn [db _]
+ (when-let [message-input (:message-input db)]
+ (.blur message-input)))))
+
(register-handler :set-response-chat-command
- (after invoke-suggestions-handler!)
+ [(after invoke-suggestions-handler!)
+ (after #(dispatch [:animate-show-response]))]
(fn [db [_ to-msg-id command-key]]
(commands/set-response-chat-command db to-msg-id command-key)))
@@ -104,8 +136,12 @@
(update-input-text db text))
(defn update-command [db [_ text]]
- (let [[command] (suggestions/check-suggestion db text)]
- (commands/set-chat-command db command)))
+ (if-not (commands/get-chat-command db)
+ (let [{:keys [command]} (suggestions/check-suggestion db text)]
+ (if command
+ (commands/set-chat-command db command)
+ db))
+ db))
(register-handler :set-chat-input-text
((enrich update-command) update-text))
@@ -130,7 +166,21 @@
(defn add-message-to-db
[db chat-id message]
(let [messages [:chats chat-id :messages]]
- (update-in db messages conj message)))
+ (update-in db messages conj (assoc message :chat-id chat-id
+ :new? true))))
+
+(defn set-message-shown
+ [db chat-id msg-id]
+ (update-in db [:chats chat-id :messages] (fn [messages]
+ (map (fn [msg]
+ (if (= msg-id (:msg-id msg))
+ (assoc msg :new? false)
+ msg))
+ messages))))
+
+(register-handler :set-message-shown
+ (fn [db [_ {:keys [chat-id msg-id]}]]
+ (set-message-shown db chat-id msg-id)))
(defn prepare-message
[{:keys [identity current-chat-id] :as db} _]
diff --git a/src/status_im/chat/handlers/animation.cljs b/src/status_im/chat/handlers/animation.cljs
new file mode 100644
index 0000000000..2859261ab3
--- /dev/null
+++ b/src/status_im/chat/handlers/animation.cljs
@@ -0,0 +1,111 @@
+(ns status-im.chat.handlers.animation
+ (:require [re-frame.core :refer [register-handler after dispatch]]
+ [re-frame.middleware :refer [path]]
+ [status-im.models.commands :as commands]
+ [status-im.handlers.content-suggestions :refer [get-content-suggestions]]
+ [status-im.chat.styles.message-input :refer [input-height]]
+ [status-im.chat.styles.response :refer [request-info-height response-height-normal]]
+ [status-im.chat.styles.response-suggestions :as response-suggestions-styles]
+ [status-im.constants :refer [response-input-hiding-duration]]))
+
+(def zero-height input-height)
+
+(defn animation-handler
+ ([name handler] (animation-handler name nil handler))
+ ([name middleware handler]
+ (register-handler name [(path :animations) middleware] handler)))
+
+(animation-handler :finish-animate-cancel-command
+ (fn [db _]
+ (assoc db :commands-input-is-switching? false)))
+
+(animation-handler :animate-cancel-command
+ (fn [db _]
+ (if-not (:commands-input-is-switching? db)
+ (assoc db
+ :commands-input-is-switching? true
+ :message-input-buttons-scale 1
+ :message-input-offset 0
+ :to-response-height zero-height
+ :messages-offset 0)
+ db)))
+
+(animation-handler :finish-animate-response-resize
+ (fn [db _]
+ (let [fixed (:to-response-height db)]
+ (assoc db :response-height-current fixed
+ :response-resize? false))))
+
+(animation-handler :set-response-height
+ (fn [db [_ value]]
+ (assoc db :response-height-current value)))
+
+(animation-handler :animate-response-resize
+ (fn [db _]
+ (assoc db :response-resize? true)))
+
+(defn get-response-height [db]
+ (let [command (commands/get-chat-command db)
+ text (commands/get-chat-command-content db)
+ suggestions (get-content-suggestions command text)
+ suggestions-height (reduce + 0 (map #(if (:header %)
+ response-suggestions-styles/header-height
+ response-suggestions-styles/suggestion-height)
+ suggestions))]
+ (+ zero-height
+ (min response-height-normal (+ suggestions-height request-info-height)))))
+
+(defn update-response-height [db]
+ (assoc-in db [:animations :to-response-height] (get-response-height db)))
+
+(animation-handler :finish-show-response
+ (fn [db _]
+ (assoc db :commands-input-is-switching? false)))
+
+(register-handler :animate-show-response
+ (after #(dispatch [:animate-response-resize]))
+ (fn [db _]
+ (-> db
+ (assoc-in [:animations :commands-input-is-switching?] true)
+ (assoc-in [:animations :response-height-current] zero-height)
+ (assoc-in [:animations :message-input-buttons-scale] 0.1)
+ (assoc-in [:animations :message-input-offset] -40)
+ (assoc-in [:animations :messages-offset] request-info-height)
+ (update-response-height))))
+
+(animation-handler :set-response-max-height
+ (fn [db [_ height]]
+ (let [prev-height (:response-height-max db)]
+ (if (not= height prev-height)
+ (let [db (assoc db :response-height-max height)]
+ (if (= prev-height (:to-response-height db))
+ (assoc db :to-response-height height
+ :response-height-current height)
+ db))
+ db))))
+
+(animation-handler :on-drag-response
+ (fn [db [_ dy]]
+ (let [fixed (:to-response-height db)]
+ (assoc db :response-height-current (- fixed dy)
+ :response-resize? false))))
+
+(register-handler :fix-response-height
+ (fn [db _]
+ (if (and (commands/get-chat-command-to-msg-id db)
+ (not (get-in db [:animations :commands-input-is-switching?])))
+ (let [current (get-in db [:animations :response-height-current])
+ normal-height response-height-normal
+ command (commands/get-chat-command db)
+ text (commands/get-chat-command-content db)
+ suggestions (get-content-suggestions command text)
+ max-height (get-in db [:animations :response-height-max])
+ delta (/ normal-height 2)
+ new-fixed (cond
+ (or (<= current (+ zero-height delta))
+ (empty? suggestions)) (+ zero-height request-info-height)
+ (<= current (+ zero-height normal-height delta)) (get-response-height db)
+ :else max-height)]
+ (dispatch [:animate-response-resize])
+ (assoc-in db [:animations :to-response-height] new-fixed))
+ db)))
diff --git a/src/status_im/chat/screen.cljs b/src/status_im/chat/screen.cljs
index 3ff250e238..feb88de74e 100644
--- a/src/status_im/chat/screen.cljs
+++ b/src/status_im/chat/screen.cljs
@@ -3,6 +3,7 @@
(:require [re-frame.core :refer [subscribe dispatch]]
[clojure.string :as s]
[status-im.components.react :refer [view
+ animated-view
text
image
icon
@@ -12,13 +13,18 @@
[status-im.components.chat-icon.screen :refer [chat-icon-view-action
chat-icon-view-menu-item]]
[status-im.chat.styles.screen :as st]
- [status-im.utils.listview :refer [to-datasource]]
+ [status-im.utils.listview :refer [to-datasource-inverted]]
[status-im.utils.utils :refer [truncate-str]]
[status-im.components.invertible-scroll-view :refer [invertible-scroll-view]]
[status-im.components.toolbar :refer [toolbar]]
[status-im.chat.views.message :refer [chat-message]]
+ [status-im.chat.views.content-suggestions :refer [content-suggestions-view]]
+ [status-im.chat.views.suggestions :refer [suggestions-view]]
+ [status-im.chat.views.response :refer [response-view]]
[status-im.chat.views.new-message :refer [chat-message-new]]
- [status-im.i18n :refer [label label-pluralize]]))
+ [status-im.i18n :refer [label label-pluralize]]
+ [status-im.components.animation :as anim]
+ [reagent.core :as r]))
(defn contacts-by-identity [contacts]
@@ -55,12 +61,12 @@
(for [member ["Geoff" "Justas"]]
^{:key member} [typing member])])
-(defn message-row [contact-by-identity group-chat]
+(defn message-row [contact-by-identity group-chat messages-count]
(fn [row _ idx]
(let [msg (-> row
(add-msg-color contact-by-identity)
(assoc :group-chat group-chat)
- (assoc :last-msg (zero? (js/parseInt idx))))]
+ (assoc :last-msg (= (js/parseInt idx) (dec messages-count))))]
(list-item [chat-message msg]))))
(defn on-action-selected [position]
@@ -213,21 +219,58 @@
:custom-action [toolbar-action]}])))
(defview messages-view [group-chat]
- [messages [:chat :messages]
- contacts [:chat :contacts]]
- (let [contacts' (contacts-by-identity contacts)]
- [list-view {:renderRow (message-row contacts' group-chat)
- :renderScrollComponent #(invertible-scroll-view (js->clj %))
- :onEndReached #(dispatch [:load-more-messages])
- :enableEmptySections true
- :dataSource (to-datasource messages)}]))
+ [messages [:chat :messages]
+ contacts [:chat :contacts]]
+ (let [contacts' (contacts-by-identity contacts)]
+ [list-view {:renderRow (message-row contacts' group-chat (count messages))
+ :renderScrollComponent #(invertible-scroll-view (js->clj %))
+ :onEndReached #(dispatch [:load-more-messages])
+ :enableEmptySections true
+ :keyboardShouldPersistTaps true
+ :dataSource (to-datasource-inverted messages)}]))
+
+(defn messages-container-animation-logic [{:keys [to-value val]}]
+ (fn [_]
+ (let [to-value @to-value]
+ (anim/start (anim/spring val {:toValue to-value})
+ (fn [arg]
+ (when (.-finished arg)
+ (dispatch [:set-animation ::messages-offset-current to-value])))))))
+
+(defn messages-container [messages]
+ (let [to-messages-offset (subscribe [:animations :messages-offset])
+ cur-messages-offset (subscribe [:animations ::messages-offset-current])
+ messages-offset (anim/create-value (or @cur-messages-offset 0))
+ context {:to-value to-messages-offset
+ :val messages-offset}
+ on-update (messages-container-animation-logic context)]
+ (r/create-class
+ {:component-did-mount
+ on-update
+ :component-did-update
+ on-update
+ :reagent-render
+ (fn [messages]
+ @to-messages-offset
+ [animated-view {:style (st/messages-container messages-offset)}
+ messages])})))
(defview chat []
[group-chat [:chat :group-chat]
- show-actions-atom [:show-actions]]
- [view st/chat-view
+ show-actions-atom [:show-actions]
+ command [:get-chat-command]
+ to-msg-id [:get-chat-command-to-msg-id]]
+ [view {:style st/chat-view
+ :onLayout (fn [event]
+ (let [height (.. event -nativeEvent -layout -height)]
+ (dispatch [:set-response-max-height height])))}
[chat-toolbar]
- [messages-view group-chat]
+ [messages-container
+ [messages-view group-chat]]
(when group-chat [typing-all])
+ (cond
+ (and command to-msg-id) [response-view]
+ command [content-suggestions-view]
+ :else [suggestions-view])
[chat-message-new]
(when show-actions-atom [actions-view])])
diff --git a/src/status_im/chat/styles/content_suggestions.cljs b/src/status_im/chat/styles/content_suggestions.cljs
index 01b328fc78..99c64afc20 100644
--- a/src/status_im/chat/styles/content_suggestions.cljs
+++ b/src/status_im/chat/styles/content_suggestions.cljs
@@ -43,6 +43,9 @@
:backgroundColor color-white
:borderRadius 5})
+(def container
+ {:backgroundColor color-white})
+
(def drag-down-touchable
{:height 22
:alignItems :center
diff --git a/src/status_im/chat/styles/input.cljs b/src/status_im/chat/styles/input.cljs
index 474cd2a115..53e02cb16e 100644
--- a/src/status_im/chat/styles/input.cljs
+++ b/src/status_im/chat/styles/input.cljs
@@ -37,7 +37,8 @@
(def command-input
{:flex 1
:marginLeft 8
- :marginTop 7
+ :marginTop -2
+ :padding 0
:fontSize 14
:fontFamily font
:color text1-color})
diff --git a/src/status_im/chat/styles/message.cljs b/src/status_im/chat/styles/message.cljs
index 2357e7e0f2..5bb60573d4 100644
--- a/src/status_im/chat/styles/message.cljs
+++ b/src/status_im/chat/styles/message.cljs
@@ -131,20 +131,27 @@
(def command-request-message-view
{:borderRadius 14
:padding 12
+ :paddingRight 28
:backgroundColor color-white})
(def command-request-from-text
(merge style-sub-text {:marginBottom 2}))
-(defn command-request-image-view
- [command]
- {:position :absolute
- :top 12
- :right 0
- :width 32
+(def command-request-image-touchable
+ {:position :absolute
+ :top 4
+ :right -8
+ :alignItems :center
+ :justifyContent :center
+ :width 48
+ :height 48})
+
+(defn command-request-image-view [command scale]
+ {:width 32
:height 32
:borderRadius 50
- :backgroundColor (:color command)})
+ :backgroundColor (:color command)
+ :transform [{:scale scale}]})
(defn command-image-view
[command]
@@ -317,6 +324,9 @@
(def message-date-text
(assoc style-sub-text :textAlign :center))
+(defn message-container [height]
+ {:height height})
+
(def new-message-container
{:backgroundColor color-white
:elevation 4})
diff --git a/src/status_im/chat/styles/message_input.cljs b/src/status_im/chat/styles/message_input.cljs
new file mode 100644
index 0000000000..9cbcc4e27f
--- /dev/null
+++ b/src/status_im/chat/styles/message_input.cljs
@@ -0,0 +1,32 @@
+(ns status-im.chat.styles.message-input
+ (:require [status-im.components.styles :refer [color-white
+ color-blue]]))
+
+(def input-height 56)
+
+(defn message-input-container [offset]
+ {:flex 1
+ :transform [{:translateX offset}]
+ :marginRight offset})
+
+(def input-container
+ {:flexDirection :column})
+
+(def input-view
+ {:flexDirection :row
+ :height input-height
+ :backgroundColor color-white})
+
+(def send-icon
+ {:marginTop 10.5
+ :marginLeft 12
+ :width 15
+ :height 15})
+
+(def send-container
+ {:marginTop 10
+ :marginRight 10
+ :width 36
+ :height 36
+ :borderRadius 50
+ :backgroundColor color-blue})
diff --git a/src/status_im/chat/styles/plain_input.cljs b/src/status_im/chat/styles/plain_input.cljs
deleted file mode 100644
index b8806518dd..0000000000
--- a/src/status_im/chat/styles/plain_input.cljs
+++ /dev/null
@@ -1,55 +0,0 @@
-(ns status-im.chat.styles.plain-input
- (:require [status-im.components.styles :refer [font
- text2-color
- color-white
- color-blue]]))
-
-(def input-container
- {:flexDirection :column})
-
-(def input-view
- {:flexDirection :row
- :height 56
- :backgroundColor color-white})
-
-(def switch-commands-touchable
- {:width 56
- :height 56
- :alignItems :center
- :justifyContent :center})
-
-(def list-icon
- {:width 13
- :height 12})
-
-(def close-icon
- {:width 12
- :height 12})
-
-(def message-input
- {:flex 1
- :marginTop -2
- :padding 0
- :fontSize 14
- :fontFamily font
- :color text2-color})
-
-(def smile-icon
- {:marginTop 18
- :marginRight 18
- :width 20
- :height 20})
-
-(def send-icon
- {:marginTop 10.5
- :marginLeft 12
- :width 15
- :height 15})
-
-(def send-container
- {:marginTop 10
- :marginRight 10
- :width 36
- :height 36
- :borderRadius 50
- :backgroundColor color-blue})
diff --git a/src/status_im/chat/styles/plain_message.cljs b/src/status_im/chat/styles/plain_message.cljs
new file mode 100644
index 0000000000..d52ae4d257
--- /dev/null
+++ b/src/status_im/chat/styles/plain_message.cljs
@@ -0,0 +1,32 @@
+(ns status-im.chat.styles.plain-message
+ (:require [status-im.components.styles :refer [font
+ text2-color]]))
+
+(def message-input-button-touchable
+ {:width 56
+ :height 56
+ :alignItems :center
+ :justifyContent :center})
+
+(defn message-input-button [scale]
+ {:transform [{:scale scale}]})
+
+(def list-icon
+ {:width 13
+ :height 12})
+
+(def close-icon
+ {:width 12
+ :height 12})
+
+(def message-input
+ {:flex 1
+ :marginTop -2
+ :padding 0
+ :fontSize 14
+ :fontFamily font
+ :color text2-color})
+
+(def smile-icon
+ {:width 20
+ :height 20})
diff --git a/src/status_im/chat/styles/response.cljs b/src/status_im/chat/styles/response.cljs
new file mode 100644
index 0000000000..029ca2ed86
--- /dev/null
+++ b/src/status_im/chat/styles/response.cljs
@@ -0,0 +1,96 @@
+(ns status-im.chat.styles.response
+ (:require [status-im.components.styles :refer [font
+ color-white
+ color-blue
+ text1-color
+ text2-color
+ chat-background
+ color-black]]
+ [status-im.chat.styles.message-input :refer [input-height]]))
+
+(def response-height-normal 211)
+(def request-info-height 61)
+
+(def drag-container
+ {:height 16
+ :alignItems :center
+ :justifyContent :center})
+
+(def drag-icon
+ {:width 14
+ :height 3})
+
+(def command-icon-container
+ {:marginTop 1
+ :marginLeft 12
+ :width 32
+ :height 32
+ :alignItems :center
+ :justifyContent :center
+ :borderRadius 50
+ :backgroundColor color-white})
+
+(def command-icon
+ {:width 9
+ :height 15})
+
+(def info-container
+ {:flex 1
+ :marginLeft 12})
+
+(def command-name
+ {:marginTop 0
+ :fontSize 12
+ :fontFamily font
+ :color color-white})
+
+(def message-info
+ {:marginTop 1
+ :fontSize 12
+ :fontFamily font
+ :opacity 0.69
+ :color color-white})
+
+(defn response-view [height]
+ {:flexDirection :column
+ :position :absolute
+ :left 0
+ :right 0
+ :bottom 0
+ :height height
+ :backgroundColor color-white
+ :elevation 2})
+
+(def input-placeholder
+ {:height input-height})
+
+(defn request-info [color]
+ {:flexDirection :column
+ :height request-info-height
+ :backgroundColor color})
+
+(def inner-container
+ {:flexDirection :row
+ :height 45})
+
+(def cancel-container
+ {:marginTop 2.5
+ :marginRight 16
+ :width 24
+ :height 24})
+
+(def cancel-icon
+ {:marginTop 6
+ :marginLeft 6
+ :width 12
+ :height 12})
+
+(def command-input
+ {:flex 1
+ :marginLeft 56
+ :marginRight 16
+ :marginTop -2
+ :padding 0
+ :fontSize 14
+ :fontFamily font
+ :color text1-color})
diff --git a/src/status_im/chat/styles/response_suggestions.cljs b/src/status_im/chat/styles/response_suggestions.cljs
new file mode 100644
index 0000000000..2561f2ae53
--- /dev/null
+++ b/src/status_im/chat/styles/response_suggestions.cljs
@@ -0,0 +1,59 @@
+(ns status-im.chat.styles.response-suggestions
+ (:require [status-im.components.styles :refer [font
+ font-medium
+ color-light-blue-transparent
+ color-white
+ color-black
+ color-blue
+ color-blue-transparent
+ selected-message-color
+ online-color
+ separator-color
+ text1-color
+ text2-color
+ text3-color]]))
+
+(def header-height 50)
+(def suggestion-height 56)
+
+(def header-container
+ {:paddingLeft 16
+ :height header-height
+ :backgroundColor color-white})
+
+(def header-text
+ {:marginTop 18
+ :fontSize 13
+ :fontFamily font-medium
+ :color text2-color})
+
+(def suggestion-container
+ {:flexDirection :column
+ :paddingLeft 16
+ :height suggestion-height
+ :backgroundColor color-white})
+
+(def suggestion-sub-container
+ {:height suggestion-height
+ :borderBottomWidth 1
+ :borderBottomColor separator-color})
+
+(def value-text
+ {:marginTop 10
+ :fontSize 12
+ :fontFamily font
+ :color text1-color})
+
+(def description-text
+ {:marginTop 2
+ :fontSize 12
+ :fontFamily font
+ :color text2-color})
+
+(def suggestions-container
+ {:flexDirection :row
+ :flex 1
+ :marginVertical 1
+ :marginHorizontal 0
+ :backgroundColor color-white
+ :borderRadius 5})
diff --git a/src/status_im/chat/styles/screen.cljs b/src/status_im/chat/styles/screen.cljs
index 35e4d0b3d7..49133f8d32 100644
--- a/src/status_im/chat/styles/screen.cljs
+++ b/src/status_im/chat/styles/screen.cljs
@@ -14,6 +14,10 @@
{:flex 1
:backgroundColor chat-background})
+(defn messages-container [bottom]
+ {:flex 1
+ :bottom bottom})
+
(def toolbar-view
{:flexDirection :row
:height 56
diff --git a/src/status_im/chat/styles/suggestions.cljs b/src/status_im/chat/styles/suggestions.cljs
index 2d5eaaab8e..0c35347f79 100644
--- a/src/status_im/chat/styles/suggestions.cljs
+++ b/src/status_im/chat/styles/suggestions.cljs
@@ -59,6 +59,9 @@
:backgroundColor color-white
:borderRadius 5})
+(def container
+ {:backgroundColor color-white})
+
(def drag-down-touchable
{:height 22
:alignItems :center
diff --git a/src/status_im/chat/subs.cljs b/src/status_im/chat/subs.cljs
index c9de7660e5..fc805b3183 100644
--- a/src/status_im/chat/subs.cljs
+++ b/src/status_im/chat/subs.cljs
@@ -1,11 +1,12 @@
(ns status-im.chat.subs
(:require-macros [reagent.ratom :refer [reaction]])
- (:require [re-frame.core :refer [register-sub]]
+ (:require [re-frame.core :refer [register-sub dispatch]]
[status-im.db :as db]
;todo handlers in subs?...
[status-im.chat.suggestions :refer
- [get-suggestions typing-command? get-content-suggestions]]
+ [get-suggestions typing-command?]]
[status-im.models.commands :as commands]
+ [status-im.constants :refer [response-suggesstion-resize-duration]]
[status-im.handlers.content-suggestions :refer [get-content-suggestions]]))
(register-sub :chat-properties
@@ -82,6 +83,10 @@
(fn [db _]
(reaction (commands/get-chat-command-content @db))))
+(register-sub :get-chat-command-to-msg-id
+ (fn [db _]
+ (reaction (commands/get-chat-command-to-msg-id @db))))
+
(register-sub :chat-command-request
(fn [db _]
(reaction (commands/get-chat-command-request @db))))
@@ -102,5 +107,5 @@
(register-sub :get-content-suggestions
(fn [db _]
(let [command (reaction (commands/get-chat-command @db))
- text (reaction (commands/get-chat-command-content @db))]
- (reaction (get-content-suggestions @db @command @text)))))
+ text (reaction (commands/get-chat-command-content @db))]
+ (reaction (get-content-suggestions @command @text)))))
diff --git a/src/status_im/chat/views/command.cljs b/src/status_im/chat/views/command.cljs
index 039f949c9b..4f5d889c9e 100644
--- a/src/status_im/chat/views/command.cljs
+++ b/src/status_im/chat/views/command.cljs
@@ -1,16 +1,15 @@
(ns status-im.chat.views.command
+ (:require-macros [status-im.utils.views :refer [defview]])
(:require [re-frame.core :refer [subscribe dispatch]]
[status-im.components.react :refer [view
- icon
- text
- text-input
- touchable-highlight]]
- [status-im.chat.views.content-suggestions :refer
- [content-suggestions-view]]
+ icon
+ text
+ text-input
+ touchable-highlight]]
[status-im.chat.styles.input :as st]))
(defn cancel-command-input []
- (dispatch [:cancel-command]))
+ (dispatch [:start-cancel-command]))
(defn set-input-message [message]
(dispatch [:set-chat-command-content message]))
@@ -24,7 +23,8 @@
(validator message)
(pos? (count message))))
-(defn simple-command-input-view [command input-options & {:keys [validator]}]
+
+#_(defn simple-command-input-view [command input-options & {:keys [validator]}]
(let [message-atom (subscribe [:get-chat-command-content])]
(fn [command input-options & {:keys [validator]}]
(let [message @message-atom]
@@ -49,3 +49,19 @@
[touchable-highlight {:on-press cancel-command-input}
[view st/cancel-container
[icon :close-gray st/cancel-icon]]])]]))))
+
+(defn try-send [message validator]
+ (when (valid? message validator)
+ (send-command)))
+
+(defn command-icon [command]
+ [view (st/command-text-container command)
+ [text {:style st/command-text} (:text command)]])
+
+(defview cancel-button []
+ [commands-input-is-switching? [:animations :commands-input-is-switching?]]
+ [touchable-highlight {:disabled commands-input-is-switching?
+ :on-press cancel-command-input}
+ [view st/cancel-container
+ [icon :close-gray st/cancel-icon]]])
+
diff --git a/src/status_im/chat/views/confirmation_code.cljs b/src/status_im/chat/views/confirmation_code.cljs
deleted file mode 100644
index b1d5f4c94e..0000000000
--- a/src/status_im/chat/views/confirmation_code.cljs
+++ /dev/null
@@ -1,6 +0,0 @@
-(ns status-im.chat.views.confirmation-code
- (:require
- [status-im.chat.views.command :refer [simple-command-input-view]]))
-
-(defn confirmation-code-input-view [command]
- [simple-command-input-view command {:keyboardType :numeric}])
diff --git a/src/status_im/chat/views/content_suggestions.cljs b/src/status_im/chat/views/content_suggestions.cljs
index 1578f9484e..bcf5103606 100644
--- a/src/status_im/chat/views/content_suggestions.cljs
+++ b/src/status_im/chat/views/content_suggestions.cljs
@@ -2,11 +2,11 @@
(:require-macros [status-im.utils.views :refer [defview]])
(:require [re-frame.core :refer [subscribe dispatch]]
[status-im.components.react :refer [view
- icon
- text
- touchable-highlight
- list-view
- list-item]]
+ icon
+ text
+ touchable-highlight
+ list-view
+ list-item]]
[status-im.chat.styles.content-suggestions :as st]
[status-im.utils.listview :refer [to-datasource]]))
@@ -26,7 +26,7 @@
(defview content-suggestions-view []
[suggestions [:get :current-suggestion]]
(when (seq suggestions)
- [view
+ [view st/container
[touchable-highlight {:style st/drag-down-touchable
;; TODO hide suggestions?
:onPress (fn [])}
diff --git a/src/status_im/chat/views/message.cljs b/src/status_im/chat/views/message.cljs
index 7bf6ee480e..656cb72631 100644
--- a/src/status_im/chat/views/message.cljs
+++ b/src/status_im/chat/views/message.cljs
@@ -2,23 +2,28 @@
(:require-macros [status-im.utils.views :refer [defview]])
(:require [clojure.string :as s]
[re-frame.core :refer [subscribe dispatch]]
+ [reagent.core :as r]
[status-im.components.react :refer [view
text
image
+ animated-view
touchable-highlight]]
+ [status-im.components.animation :as anim]
+ [status-im.chat.views.request-message :refer [message-content-command-request]]
[status-im.chat.styles.message :as st]
[status-im.models.commands :refer [parse-command-msg-content
parse-command-request]]
[status-im.resources :as res]
+ [status-im.utils.datetime :as time]
[status-im.constants :refer [text-content-type
content-type-status
content-type-command
content-type-command-request]]))
-(defn message-date [{:keys [date]}]
+(defn message-date [timestamp]
[view {}
[view st/message-date-container
- [text {:style st/message-date-text} date]]])
+ [text {:style st/message-date-text} (time/to-short-str timestamp)]]])
(defn contact-photo [{:keys [photo-path]}]
[view st/contact-photo-container
@@ -79,7 +84,8 @@
(->> (when command (name command))
(str "request-")))
-(defview message-content-command-request
+;; todo remove (merging leftover)
+#_(defview message-content-command-request
[{:keys [msg-id content from incoming-group]}]
[commands [:get-responses]]
(let [{:keys [command content]} (parse-command-request commands content)]
@@ -183,12 +189,50 @@
(when (and outgoing delivery-status)
[message-delivery-status {:delivery-status delivery-status}])]))
+(defn message-container-animation-logic [{:keys [to-value val callback]}]
+ (fn [_]
+ (let [to-value @to-value]
+ (when (< 0 to-value)
+ (anim/start
+ (anim/spring val {:toValue to-value
+ :friction 4
+ :tension 10})
+ (fn [arg]
+ (when (.-finished arg)
+ (callback))))))))
+
+(defn message-container [message & children]
+ (if (:new? message)
+ (let [layout-height (r/atom 0)
+ anim-value (anim/create-value 1)
+ anim-callback #(dispatch [:set-message-shown message])
+ context {:to-value layout-height
+ :val anim-value
+ :callback anim-callback}
+ on-update (message-container-animation-logic context)]
+ (r/create-class
+ {:component-did-mount
+ on-update
+ :component-did-update
+ on-update
+ :reagent-render
+ (fn [message & children]
+ @layout-height
+ [animated-view {:style (st/message-container anim-value)}
+ (into [view {:onLayout (fn [event]
+ (let [height (.. event -nativeEvent -layout -height)]
+ (reset! layout-height height)))}]
+ children)])}))
+ (into [view] children)))
+
(defn chat-message
- [{:keys [outgoing delivery-status date new-day group-chat]
+ [{:keys [outgoing delivery-status timestamp new-day group-chat]
:as message}]
- [view {}
- (when new-day [message-date {:date date}])
- [view {}
+ [message-container message
+ ;; TODO there is no new-day info in message
+ (when new-day
+ [message-date timestamp])
+ [view
(let [incoming-group (and group-chat (not outgoing))]
[message-content
(if incoming-group
diff --git a/src/status_im/chat/views/message_input.cljs b/src/status_im/chat/views/message_input.cljs
new file mode 100644
index 0000000000..4c26e15eb8
--- /dev/null
+++ b/src/status_im/chat/views/message_input.cljs
@@ -0,0 +1,127 @@
+(ns status-im.chat.views.message-input
+ (:require-macros [status-im.utils.views :refer [defview]])
+ (:require [re-frame.core :refer [subscribe dispatch]]
+ [reagent.core :as r]
+ [status-im.components.react :refer [view
+ animated-view
+ icon
+ touchable-highlight
+ text-input
+ dismiss-keyboard!]]
+ [status-im.components.animation :as anim]
+ [status-im.chat.views.plain-message :as plain-message]
+ [status-im.chat.views.command :as command]
+ [status-im.chat.views.response :as response]
+ [status-im.chat.styles.message-input :as st]
+ [status-im.chat.styles.plain-message :as st-message]
+ [status-im.chat.styles.input :as st-command]
+ [status-im.chat.styles.response :as st-response]
+ [status-im.constants :refer [response-input-hiding-duration]]))
+
+(defview send-button [{:keys [on-press accessibility-label]}]
+ [commands-input-is-switching? [:animations :commands-input-is-switching?]]
+ [touchable-highlight {:disabled commands-input-is-switching?
+ :on-press on-press
+ :accessibility-label accessibility-label}
+ [view st/send-container
+ [icon :send st/send-icon]]])
+
+(defn animation-logic [{:keys [to-value val]}]
+ (fn [_]
+ (let [to-value @to-value]
+ (anim/start (anim/timing val {:toValue to-value
+ :duration response-input-hiding-duration})
+ (fn [arg]
+ (when (.-finished arg)
+ (dispatch [:set-animation ::message-input-offset-current to-value])))))))
+
+(defn message-input-container [input]
+ (let [to-message-input-offset (subscribe [:animations :message-input-offset])
+ cur-message-input-offset (subscribe [:animations ::message-input-offset-current])
+ message-input-offset (anim/create-value (or @cur-message-input-offset 0))
+ context {:to-value to-message-input-offset
+ :val message-input-offset}
+ on-update (animation-logic context)]
+ (r/create-class
+ {:component-did-mount
+ on-update
+ :component-did-update
+ on-update
+ :reagent-render
+ (fn [input]
+ @to-message-input-offset
+ [animated-view {:style (st/message-input-container message-input-offset)}
+ input])})))
+
+(defview message-input [input-options validator]
+ [input-message [:get-chat-input-text]
+ command [:get-chat-command]
+ to-msg-id [:get-chat-command-to-msg-id]
+ input-command [:get-chat-command-content]
+ staged-commands [:get-chat-staged-commands]
+ typing-command? [:typing-command?]
+ commands-input-is-switching? [:animations :commands-input-is-switching?]]
+ (let [dismiss-keyboard (not (or command typing-command?))
+ response? (and command to-msg-id)
+ message-input? (or (not command) commands-input-is-switching?)
+ animation? commands-input-is-switching?]
+ [text-input (merge {:style (cond
+ message-input? st-message/message-input
+ response? st-response/command-input
+ command st-command/command-input)
+ :ref (fn [input]
+ (dispatch [:set-message-input input]))
+ :autoFocus false
+ :blurOnSubmit dismiss-keyboard
+ :onChangeText (fn [text]
+ (when-not animation?
+ ((if message-input?
+ plain-message/set-input-message
+ command/set-input-message)
+ text)))
+ :onSubmitEditing #(when-not animation?
+ (if message-input?
+ (plain-message/try-send staged-commands
+ input-message
+ dismiss-keyboard)
+ (command/try-send input-command validator)))}
+ (when command
+ {:accessibility-label :command-input})
+ input-options)
+ (if message-input?
+ input-message
+ input-command)]))
+
+(defview plain-message-input-view [{:keys [input-options validator]}]
+ [input-message [:get-chat-input-text]
+ command [:get-chat-command]
+ to-msg-id [:get-chat-command-to-msg-id]
+ input-command [:get-chat-command-content]
+ staged-commands [:get-chat-staged-commands]
+ typing-command? [:typing-command?]
+ commands-input-is-switching? [:animations :commands-input-is-switching?]]
+ (let [dismiss-keyboard (not (or command typing-command?))
+ response? (and command to-msg-id)
+ message-input? (or (not command) commands-input-is-switching?)]
+ [view st/input-container
+ [view st/input-view
+ (if message-input?
+ [plain-message/commands-button]
+ (when (and command (not response?))
+ [command/command-icon command response?]))
+ [message-input-container
+ [message-input input-options validator]]
+ ;; TODO emoticons: not implemented
+ (when message-input?
+ [plain-message/smile-button])
+ (if message-input?
+ (when (plain-message/message-valid? staged-commands input-message)
+ [send-button {:on-press #(plain-message/try-send staged-commands
+ input-message
+ dismiss-keyboard)
+ :accessibility-label :send-message}])
+ (if (command/valid? input-command validator)
+ [send-button {:on-press command/send-command
+ :accessibility-label :stage-command}]
+ (when-not response?
+ [command/cancel-button])))]]))
diff --git a/src/status_im/chat/views/money.cljs b/src/status_im/chat/views/money.cljs
deleted file mode 100644
index 0051b597f6..0000000000
--- a/src/status_im/chat/views/money.cljs
+++ /dev/null
@@ -1,7 +0,0 @@
-(ns status-im.chat.views.money
- (:require
- [status-im.chat.views.command :refer [simple-command-input-view]]))
-
-(defn money-input-view [command]
- [simple-command-input-view command
- {:keyboardType :numeric}])
diff --git a/src/status_im/chat/views/new_message.cljs b/src/status_im/chat/views/new_message.cljs
index 29b203a067..d6cdf010dd 100644
--- a/src/status_im/chat/views/new_message.cljs
+++ b/src/status_im/chat/views/new_message.cljs
@@ -1,14 +1,11 @@
(ns status-im.chat.views.new-message
+ (:require-macros [status-im.utils.views :refer [defview]])
(:require
[re-frame.core :refer [subscribe]]
[status-im.components.react :refer [view]]
- [status-im.chat.views.plain-input :refer [plain-message-input-view]]
- [status-im.chat.views.command :refer [simple-command-input-view]]
- [status-im.chat.views.phone :refer [phone-input-view]]
- [status-im.chat.views.password :refer [password-input-view]]
- [status-im.chat.views.confirmation-code :refer [confirmation-code-input-view]]
- [status-im.chat.views.money :refer [money-input-view]]
+ [status-im.chat.views.message-input :refer [plain-message-input-view]]
[status-im.chat.views.staged-command :refer [simple-command-staged-view]]
+ [status-im.utils.phone-number :refer [valid-mobile-number?]]
[status-im.chat.styles.message :as st]))
(defn staged-command-view [stage-command]
@@ -19,27 +16,23 @@
(for [command staged-commands]
^{:key command} [staged-command-view command])])
-(defn default-command-input-view [command]
- [simple-command-input-view command {}])
+(defn show-input [command]
+ [plain-message-input-view
+ (when command
+ (case (:command command)
+ :phone {:input-options {:keyboardType :phone-pad}
+ :validator valid-mobile-number?}
+ :keypair {:input-options {:secureTextEntry true}}
+ :confirmation-code {:input-options {:keyboardType :numeric}}
+ :money {:input-options {:keyboardType :numeric}}
+ :request {:input-options {:keyboardType :numeric}}
+ ;; todo maybe nil is finr for now :)
+ nil #_(throw (js/Error. "Uknown command type"))))])
-(defn special-input-view [command]
- (case (:command command)
- :phone [phone-input-view command]
- :keypair-password [password-input-view command]
- :confirmation-code [confirmation-code-input-view command]
- :money [money-input-view command]
- :request [money-input-view command]
- [default-command-input-view command]))
-
-(defn chat-message-new []
- (let [command-atom (subscribe [:get-chat-command])
- staged-commands-atom (subscribe [:get-chat-staged-commands])]
- (fn []
- (let [command @command-atom
- staged-commands @staged-commands-atom]
- [view st/new-message-container
- (when (and staged-commands (pos? (count staged-commands)))
- [staged-commands-view staged-commands])
- (if command
- [special-input-view command]
- [plain-message-input-view])]))))
+(defview chat-message-new []
+ [command [:get-chat-command]
+ staged-commands [:get-chat-staged-commands]]
+ [view st/new-message-container
+ (when (and staged-commands (pos? (count staged-commands)))
+ [staged-commands-view staged-commands])
+ [show-input command]])
diff --git a/src/status_im/chat/views/password.cljs b/src/status_im/chat/views/password.cljs
deleted file mode 100644
index cc87e05fff..0000000000
--- a/src/status_im/chat/views/password.cljs
+++ /dev/null
@@ -1,7 +0,0 @@
-(ns status-im.chat.views.password
- (:require
- [status-im.chat.views.command
- :refer [simple-command-input-view]]))
-
-(defn password-input-view [command]
- [simple-command-input-view command {:secureTextEntry true}])
diff --git a/src/status_im/chat/views/phone.cljs b/src/status_im/chat/views/phone.cljs
deleted file mode 100644
index f74ad65568..0000000000
--- a/src/status_im/chat/views/phone.cljs
+++ /dev/null
@@ -1,9 +0,0 @@
-(ns status-im.chat.views.phone
- (:require
- [status-im.chat.views.command
- :refer [simple-command-input-view]]
- [status-im.utils.phone-number :refer [valid-mobile-number?]]))
-
-(defn phone-input-view [command]
- [simple-command-input-view command {:keyboardType :phone-pad}
- :validator valid-mobile-number?])
diff --git a/src/status_im/chat/views/plain_input.cljs b/src/status_im/chat/views/plain_input.cljs
deleted file mode 100644
index b932d53032..0000000000
--- a/src/status_im/chat/views/plain_input.cljs
+++ /dev/null
@@ -1,50 +0,0 @@
-(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]]
- [status-im.chat.views.suggestions :refer [suggestions-view]]
- [status-im.chat.styles.plain-input :as st]))
-
-(defn set-input-message [message]
- (dispatch [:set-chat-input-text message]))
-
-(defn send [] (dispatch [:send-chat-msg]))
-
-(defn message-valid? [staged-commands message]
- (or (and (pos? (count message))
- (not= "!" message))
- (pos? (count staged-commands))))
-
-(defn try-send [staged-commands message]
- (when (message-valid? staged-commands message) (send)))
-
-(defn plain-message-input-view []
- (let [input-message-atom (subscribe [:get-chat-input-text])
- staged-commands-atom (subscribe [:get-chat-staged-commands])
- typing-command? (subscribe [:typing-command?])]
- (fn []
- (let [input-message @input-message-atom]
- [view st/input-container
- [suggestions-view]
- [view st/input-view
- [touchable-highlight {:on-press #(dispatch [:switch-command-suggestions])
- :style st/switch-commands-touchable}
- [view nil
- (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 @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)
- :accessibility-label :send-message}
- [view st/send-container
- [icon :send st/send-icon]]])]]))))
diff --git a/src/status_im/chat/views/plain_message.cljs b/src/status_im/chat/views/plain_message.cljs
new file mode 100644
index 0000000000..7c637ebbd0
--- /dev/null
+++ b/src/status_im/chat/views/plain_message.cljs
@@ -0,0 +1,108 @@
+(ns status-im.chat.views.plain-message
+ (:require-macros [status-im.utils.views :refer [defview]])
+ (:require [re-frame.core :refer [subscribe dispatch]]
+ [reagent.core :as r]
+ [status-im.components.react :refer [view
+ animated-view
+ icon
+ touchable-highlight
+ dismiss-keyboard!]]
+ [status-im.components.animation :as anim]
+ [status-im.chat.styles.plain-message :as st]
+ [status-im.constants :refer [response-input-hiding-duration]]))
+
+(defn set-input-message [message]
+ (dispatch [:set-chat-input-text message]))
+
+(defn send [dismiss-keyboard]
+ (when dismiss-keyboard
+ (dismiss-keyboard!))
+ (dispatch [:send-chat-msg]))
+
+(defn message-valid? [staged-commands message]
+ (or (and (pos? (count message))
+ (not= "!" message))
+ (pos? (count staged-commands))))
+
+(defn try-send [staged-commands message dismiss-keyboard]
+ (when (message-valid? staged-commands message)
+ (send dismiss-keyboard)))
+
+(defn prepare-message-input [message-input]
+ (when message-input
+ (.clear message-input)
+ (.focus message-input)))
+
+(defn commands-button-animation-callback [message-input]
+ (fn [arg to-value]
+ (when (.-finished arg)
+ (dispatch [:set-animation ::message-input-buttons-scale-current to-value])
+ (when (<= to-value 0.1)
+ (dispatch [:finish-show-response])
+ (prepare-message-input @message-input)))))
+
+(defn button-animation-logic [{:keys [to-value val callback]}]
+ (fn [_]
+ (let [to-scale @to-value
+ minimum 0.1
+ scale (cond (< 1 to-scale) 1
+ (< to-scale minimum) minimum
+ :else to-scale)]
+ (anim/start (anim/timing val {:toValue scale
+ :duration response-input-hiding-duration})
+ (when callback
+ (fn [arg]
+ (callback arg to-scale)))))))
+
+(defn commands-button []
+ (let [typing-command? (subscribe [:typing-command?])
+ message-input (subscribe [:get :message-input])
+ animation? (subscribe [:animations :commands-input-is-switching?])
+ to-scale (subscribe [:animations :message-input-buttons-scale])
+ cur-scale (subscribe [:animations ::message-input-buttons-scale-current])
+ buttons-scale (anim/create-value (or @cur-scale 1))
+ anim-callback (commands-button-animation-callback message-input)
+ context {:to-value to-scale
+ :val buttons-scale
+ :callback anim-callback}
+ on-update (button-animation-logic context)]
+ (r/create-class
+ {:component-did-mount
+ on-update
+ :component-did-update
+ on-update
+ :reagent-render
+ (fn []
+ (let [typing-command? @typing-command?]
+ @to-scale
+ [touchable-highlight {:disabled @animation?
+ :on-press #(dispatch [:switch-command-suggestions])
+ :style st/message-input-button-touchable}
+ [animated-view {:style (st/message-input-button buttons-scale)}
+ (if typing-command?
+ [icon :close-gray st/close-icon]
+ [icon :list st/list-icon])]]))})))
+
+(defn smile-button []
+ (let [animation? (subscribe [:animations :commands-input-is-switching?])
+ to-scale (subscribe [:animations :message-input-buttons-scale])
+ cur-scale (subscribe [:animations ::message-input-buttons-scale-current])
+ buttons-scale (anim/create-value (or @cur-scale 1))
+ context {:to-value to-scale
+ :val buttons-scale}
+ on-update (button-animation-logic context)]
+ (r/create-class
+ {:component-did-mount
+ on-update
+ :component-did-update
+ on-update
+ :reagent-render
+ (fn []
+ @to-scale
+ [touchable-highlight {:disabled @animation?
+ :on-press (fn []
+ ;; TODO emoticons: not implemented
+ )
+ :style st/message-input-button-touchable}
+ [animated-view {:style (st/message-input-button buttons-scale)}
+ [icon :smile st/smile-icon]]])})))
diff --git a/src/status_im/chat/views/request_message.cljs b/src/status_im/chat/views/request_message.cljs
new file mode 100644
index 0000000000..907fc52311
--- /dev/null
+++ b/src/status_im/chat/views/request_message.cljs
@@ -0,0 +1,86 @@
+(ns status-im.chat.views.request-message
+ (:require [re-frame.core :refer [subscribe dispatch]]
+ [reagent.core :as r]
+ [status-im.components.react :refer [view
+ animated-view
+ text
+ image
+ touchable-highlight]]
+ [status-im.chat.styles.message :as st]
+ [status-im.models.commands :refer [parse-command-request]]
+ [status-im.components.animation :as anim]))
+
+(def request-message-icon-scale-delay 600)
+
+(defn set-chat-command [msg-id command]
+ (dispatch [:set-response-chat-command msg-id (keyword (:name command))]))
+
+(defn label [{:keys [command]}]
+ (->> (name command)
+ (str "request-")))
+
+(defn request-button-animation-logic [{:keys [to-value val loop?]}]
+ (fn [_]
+ (let [loop? @loop?
+ minimum 1
+ maximum 1.3
+ to-scale (if loop?
+ (or @to-value maximum)
+ minimum)]
+ (anim/start
+ (anim/anim-sequence
+ [(anim/anim-delay (if loop? request-message-icon-scale-delay 0))
+ (anim/spring val {:toValue to-scale})])
+ (fn [arg]
+ (when (.-finished arg)
+ (dispatch [:set-animation ::request-button-scale-current to-scale])
+ (when loop?
+ (dispatch [:set-animation ::request-button-scale (if (= to-scale minimum)
+ maximum
+ minimum)]))))))))
+
+(defn request-button [msg-id command]
+ (let [to-scale (subscribe [:animations ::request-button-scale])
+ cur-scale (subscribe [:animations ::request-button-scale-current])
+ scale-anim-val (anim/create-value (or @cur-scale 1))
+ loop? (r/atom true)
+ context {:to-value to-scale
+ :val scale-anim-val
+ :loop? loop?}
+ on-update (request-button-animation-logic context)]
+ (r/create-class
+ {:component-did-mount
+ on-update
+ :component-did-update
+ on-update
+ :reagent-render
+ (fn [msg-id command]
+ @to-scale
+ [touchable-highlight {:on-press (fn []
+ (reset! loop? false)
+ (set-chat-command msg-id command))
+ :style st/command-request-image-touchable
+ ;:accessibility-label (label command)
+ }
+ [animated-view {:style (st/command-request-image-view command scale-anim-val)}
+ [image {:source {:uri (:icon command)}
+ :style st/command-request-image}]]])})))
+
+(defn message-content-command-request
+ [{:keys [msg-id content from incoming-group]}]
+ (let [commands-atom (subscribe [:get-responses])]
+ (fn [{:keys [msg-id content from incoming-group]}]
+ (let [commands @commands-atom
+ {:keys [command content]} (parse-command-request commands content)]
+ [view st/comand-request-view
+ [view st/command-request-message-view
+ (when incoming-group
+ [text {:style st/command-request-from-text}
+ from])
+ [text {:style st/style-message-text}
+ content]]
+ [request-button msg-id command]
+ (when (:request-text command)
+ [view st/command-request-text-view
+ [text {:style st/style-sub-text}
+ (:request-text command)]])]))))
diff --git a/src/status_im/chat/views/response.cljs b/src/status_im/chat/views/response.cljs
new file mode 100644
index 0000000000..6f745b5844
--- /dev/null
+++ b/src/status_im/chat/views/response.cljs
@@ -0,0 +1,99 @@
+(ns status-im.chat.views.response
+ (:require-macros [reagent.ratom :refer [reaction]]
+ [status-im.utils.views :refer [defview]])
+ (:require [re-frame.core :refer [subscribe dispatch]]
+ [reagent.core :as r]
+ [status-im.components.react :refer [view
+ animated-view
+ icon
+ image
+ text
+ text-input
+ touchable-highlight]]
+ [status-im.components.drag-drop :as drag]
+ [status-im.chat.views.response-suggestions :refer [response-suggestions-view]]
+ [status-im.chat.styles.response :as st]
+ [status-im.chat.styles.message-input :refer [input-height]]
+ [status-im.components.animation :as anim]))
+
+(defn drag-icon []
+ [view st/drag-container
+ [icon :drag-white st/drag-icon]])
+
+(defn command-icon []
+ [view st/command-icon-container
+ ;; TODO stub data: command icon
+ [icon :dollar-green st/command-icon]])
+
+(defn info-container [command]
+ [view st/info-container
+ [text {:style st/command-name}
+ (:description command)]
+ [text {:style st/message-info}
+ ;; TODO stub data: request message info
+ "By ???, MMM 1st at HH:mm"]])
+
+(defn create-response-pan-responder []
+ (drag/create-pan-responder
+ {:on-move (fn [e gesture]
+ (dispatch [:on-drag-response (.-dy gesture)]))
+ :on-release (fn [e gesture]
+ (dispatch [:fix-response-height]))}))
+
+(defn request-info []
+ (let [pan-responder (create-response-pan-responder)
+ command (subscribe [:get-chat-command])]
+ (fn []
+ [view (merge (drag/pan-handlers pan-responder)
+ {:style (st/request-info (:color @command))})
+ [drag-icon]
+ [view st/inner-container
+ [command-icon nil]
+ [info-container @command]
+ [touchable-highlight {:on-press #(dispatch [:start-cancel-command])}
+ [view st/cancel-container
+ [icon :close-white st/cancel-icon]]]]])))
+
+(defn container-animation-logic [{:keys [animation? to-value current-value val]}]
+ (fn [_]
+ (if @animation?
+ (let [to-value @to-value]
+ (anim/start (anim/spring val {:toValue to-value})
+ (fn [arg]
+ (when (.-finished arg)
+ (dispatch [:set-animation :response-height-current to-value])
+ (dispatch [:finish-animate-response-resize])
+ (when (= to-value input-height)
+ (dispatch [:finish-animate-cancel-command])
+ (dispatch [:cancel-command]))))))
+ (anim/set-value val @current-value))))
+
+(defn container [& children]
+ (let [commands-input-is-switching? (subscribe [:animations :commands-input-is-switching?])
+ response-resize? (subscribe [:animations :response-resize?])
+ to-response-height (subscribe [:animations :to-response-height])
+ cur-response-height (subscribe [:animations :response-height-current])
+ response-height (anim/create-value (or @cur-response-height 0))
+ context {:animation? (reaction (or @commands-input-is-switching? @response-resize?))
+ :to-value to-response-height
+ :current-value cur-response-height
+ :val response-height}
+ on-update (container-animation-logic context)]
+ (r/create-class
+ {:component-did-mount
+ on-update
+ :component-did-update
+ on-update
+ :reagent-render
+ (fn [& children]
+ @to-response-height
+ (into [animated-view {:style (st/response-view (if (or @commands-input-is-switching? @response-resize?)
+ response-height
+ (or @cur-response-height 0)))}]
+ children))})))
+
+(defn response-view []
+ [container
+ [request-info]
+ [response-suggestions-view]
+ [view st/input-placeholder]])
diff --git a/src/status_im/chat/views/response_suggestions.cljs b/src/status_im/chat/views/response_suggestions.cljs
new file mode 100644
index 0000000000..54eed3bb7d
--- /dev/null
+++ b/src/status_im/chat/views/response_suggestions.cljs
@@ -0,0 +1,38 @@
+(ns status-im.chat.views.response-suggestions
+ (:require-macros [status-im.utils.views :refer [defview]])
+ (:require [re-frame.core :refer [subscribe dispatch]]
+ [status-im.components.react :refer [view
+ icon
+ text
+ touchable-highlight
+ list-view
+ list-item]]
+ [status-im.chat.styles.response-suggestions :as st]
+ [status-im.utils.listview :refer [to-datasource]]))
+
+(defn set-command-content [content]
+ (dispatch [:set-chat-command-content content]))
+
+(defn header-list-item [{:keys [header]}]
+ [view st/header-container
+ [text {:style st/header-text} header]])
+
+(defn suggestion-list-item [{:keys [value description]}]
+ [touchable-highlight {:onPress #(set-command-content value)}
+ [view st/suggestion-container
+ [view st/suggestion-sub-container
+ [text {:style st/value-text} value]
+ [text {:style st/description-text} description]]]])
+
+(defn render-row [row _ _]
+ (list-item (if (:header row)
+ [header-list-item row]
+ [suggestion-list-item row])))
+
+(defview response-suggestions-view []
+ [suggestions [:get-content-suggestions]]
+ (when (seq suggestions)
+ [view st/suggestions-container
+ [list-view {:dataSource (to-datasource suggestions)
+ :keyboardShouldPersistTaps true
+ :renderRow render-row}]]))
diff --git a/src/status_im/chat/views/suggestions.cljs b/src/status_im/chat/views/suggestions.cljs
index 67c55ff148..202150423f 100644
--- a/src/status_im/chat/views/suggestions.cljs
+++ b/src/status_im/chat/views/suggestions.cljs
@@ -15,8 +15,8 @@
(defn suggestion-list-item
[[command {:keys [description]
- name :name
- :as suggestion}]]
+ name :name
+ :as suggestion}]]
(let [label (str "!" name)]
[touchable-highlight
{:onPress #(set-command-input command)}
@@ -30,10 +30,11 @@
(defn render-row [row _ _]
(list-item [suggestion-list-item row]))
+
(defview suggestions-view []
[suggestions [:get-suggestions]]
(when (seq suggestions)
- [view
+ [view st/container
[touchable-highlight {:style st/drag-down-touchable
:onPress (fn []
;; TODO hide suggestions?
@@ -41,6 +42,7 @@
[view
[icon :drag_down st/drag-down-icon]]]
[view (st/suggestions-container (count suggestions))
- [list-view {:dataSource (to-datasource suggestions)
- :enableEmptySections true
- :renderRow render-row}]]]))
+ [list-view {:dataSource (to-datasource suggestions)
+ :enableEmptySections true
+ :keyboardShouldPersistTaps true
+ :renderRow render-row}]]]))
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/chats_list/views/inner_item.cljs b/src/status_im/chats_list/views/inner_item.cljs
index 783578c3a1..2803b5d6e4 100644
--- a/src/status_im/chats_list/views/inner_item.cljs
+++ b/src/status_im/chats_list/views/inner_item.cljs
@@ -7,7 +7,7 @@
[status-im.utils.datetime :as time]))
(defn chat-list-item-inner-view
- [{:keys [chat-id name color photo-path new-messages-count
+ [{:keys [chat-id name color new-messages-count
online group-chat contacts] :as chat}]
(let [last-message (first (:messages chat))]
[view st/chat-container
diff --git a/src/status_im/components/animation.cljs b/src/status_im/components/animation.cljs
new file mode 100644
index 0000000000..a945d44f4c
--- /dev/null
+++ b/src/status_im/components/animation.cljs
@@ -0,0 +1,51 @@
+(ns status-im.components.animation
+ (:require [status-im.components.react :refer [animated]]))
+
+(defn start
+ ([anim] (.start anim))
+ ([anim callback] (.start anim callback)))
+
+(defn timing [anim-value config]
+ (.timing animated anim-value (clj->js config)))
+
+(defn spring [anim-value config]
+ (.spring animated anim-value (clj->js config)))
+
+(defn anim-sequence [animations]
+ (.sequence animated (clj->js animations)))
+
+(defn anim-delay [duration]
+ (.delay animated duration))
+
+(defn event [config]
+ (.event animated (clj->js [nil, config])))
+
+(defn add-listener [anim-value listener]
+ (.addListener anim-value listener))
+
+(defn remove-all-listeners [anim-value]
+ (.removeAllListeners anim-value))
+
+(defn stop-animation [anim-value]
+ (.stopAnimation anim-value))
+
+(defn value [anim-value]
+ (.-value anim-value))
+
+(defn set-value [anim-value value]
+ (.setValue anim-value value))
+
+(defn create-value [value]
+ (js/React.Animated.Value. value))
+
+(defn x [value-xy]
+ (.-x value-xy))
+
+(defn y [value-xy]
+ (.-y value-xy))
+
+(defn get-layout [value-xy]
+ (js->clj (.getLayout value-xy)))
+
+(defn create-value-xy [x y]
+ (js/React.Animated.ValueXY. (clj->js {:x x, :y y})))
diff --git a/src/status_im/components/camera.cljs b/src/status_im/components/camera.cljs
new file mode 100644
index 0000000000..f53b70ffae
--- /dev/null
+++ b/src/status_im/components/camera.cljs
@@ -0,0 +1,7 @@
+(ns status-im.components.camera
+ (:require [reagent.core :as r]))
+
+(def class (.-default (js/require "react-native-camera")))
+
+(defn camera [props]
+ (r/create-element class (clj->js (merge {:inverted true} props))))
\ No newline at end of file
diff --git a/src/status_im/components/drag_drop.cljs b/src/status_im/components/drag_drop.cljs
new file mode 100644
index 0000000000..708efdfb12
--- /dev/null
+++ b/src/status_im/components/drag_drop.cljs
@@ -0,0 +1,12 @@
+(ns status-im.components.drag-drop
+ (:require [status-im.components.react :refer [animated pan-responder]]
+ [status-im.components.animation :as anim]))
+
+(defn pan-handlers [pan-responder]
+ (js->clj (.-panHandlers pan-responder)))
+
+(defn create-pan-responder [{:keys [on-move on-release]}]
+ (.create pan-responder
+ (clj->js {:onStartShouldSetPanResponder (fn [] true)
+ :onPanResponderMove on-move
+ :onPanResponderRelease on-release})))
diff --git a/src/status_im/components/main_tabs.cljs b/src/status_im/components/main_tabs.cljs
index 16fb5b074d..01551e75da 100644
--- a/src/status_im/components/main_tabs.cljs
+++ b/src/status_im/components/main_tabs.cljs
@@ -1,11 +1,16 @@
(ns status-im.components.main-tabs
- (:require-macros [status-im.utils.views :refer [defview]])
+ (:require-macros [reagent.ratom :refer [reaction]]
+ [status-im.utils.views :refer [defview]])
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
+ [reagent.core :as r]
[status-im.components.react :refer [view
+ animated-view
text-input
text
image
- touchable-highlight]]
+ touchable-highlight
+ get-dimensions]]
+ [status-im.components.animation :as anim]
[status-im.chats-list.screen :refer [chats-list]]
[status-im.discovery.screen :refer [discovery]]
[status-im.contacts.screen :refer [contact-list]]
@@ -14,33 +19,91 @@
[status-im.components.styles :as common-st]
[status-im.i18n :refer [label]]))
+(def window-width (:width (get-dimensions "window")))
+
(def tab-list
[{:view-id :chat-list
:title (label :t/chats)
:screen chats-list
- :icon :icon_tab_chats}
+ :icon :icon_tab_chats
+ :index 0}
{:view-id :discovery
:title (label :t/discovery)
:screen discovery
- :icon :icon_tab_discovery}
+ :icon :icon_tab_discovery
+ :index 1}
{:view-id :contact-list
:title (label :t/contacts)
:screen contact-list
- :icon :icon_tab_contacts}])
+ :icon :icon_tab_contacts
+ :index 2}])
-(defn show-view? [current-view view-id]
- (let [key-map {:key view-id}]
- (if (= current-view view-id)
- (merge st/show-tab key-map)
- (merge st/hide-tab key-map))))
+(defn animation-logic [{:keys [offsets val tab-id to-tab-id]}]
+ (fn [_]
+ (when-let [offsets @offsets]
+ (let [from-value (:from offsets)
+ to-value (:to offsets)
+ to-tab-id @to-tab-id]
+ (anim/set-value val from-value)
+ (when to-value
+ (anim/start
+ (anim/timing val {:toValue to-value
+ :duration 300})
+ (when (= tab-id to-tab-id)
+ (fn [arg]
+ (when (.-finished arg)
+ (dispatch [:on-navigated-to-tab]))))))))))
-(defn tab-view [current-view {:keys [view-id screen]}]
- [view (show-view? current-view view-id)
+(defn get-tab-index-by-id [id]
+ (:index (first (filter #(= id (:view-id %)) tab-list))))
+
+(defn get-offsets [tab-id from-id to-id]
+ (let [tab (get-tab-index-by-id tab-id)
+ from (get-tab-index-by-id from-id)
+ to (get-tab-index-by-id to-id)]
+ (if (or (= tab from) (= tab to))
+ (cond
+ (or (nil? from) (= from to)) {:from 0}
+ (< from to) (if (= tab to)
+ {:from window-width, :to 0}
+ {:from 0, :to (- window-width)})
+ (< to from) (if (= tab to)
+ {:from (- window-width), :to 0}
+ {:from 0, :to window-width}))
+ {:from (- window-width)})))
+
+(defn tab-view-container [tab-id content]
+ (let [cur-tab-id (subscribe [:get :view-id])
+ prev-tab-id (subscribe [:get :prev-tab-view-id])
+ offsets (reaction (get-offsets tab-id @prev-tab-id @cur-tab-id))
+ anim-value (anim/create-value (- window-width))
+ context {:offsets offsets
+ :val anim-value
+ :tab-id tab-id
+ :to-tab-id cur-tab-id}
+ on-update (animation-logic context)]
+ (r/create-class
+ {:component-did-mount
+ on-update
+ :component-did-update
+ on-update
+ :reagent-render
+ (fn [tab-id content]
+ @offsets
+ [animated-view {:style (st/tab-view-container anim-value)}
+ content])})))
+
+(defn tab-view [{:keys [view-id screen]}]
+ ^{:key view-id}
+ [tab-view-container view-id
[screen]])
(defview main-tabs []
- [view-id [:get :view-id]]
- [view common-st/flex
- (doall (map #(tab-view view-id %1) tab-list))
- [tabs {:selected-view-id view-id
- :tab-list tab-list}]])
+ [view-id [:get :view-id]
+ tab-animation? [:get :prev-tab-view-id]]
+ [view {:style common-st/flex}
+ [view {:style common-st/flex
+ :pointerEvents (if tab-animation? :none :auto)}
+ (doall (map #(tab-view %) tab-list))]
+ [tabs {:selected-view-id view-id
+ :tab-list tab-list}]])
diff --git a/src/status_im/components/qr-code.cljs b/src/status_im/components/qr-code.cljs
new file mode 100644
index 0000000000..6c1d7d066d
--- /dev/null
+++ b/src/status_im/components/qr-code.cljs
@@ -0,0 +1,7 @@
+(ns status-im.components.qr-code
+ (:require [reagent.core :as r]))
+
+(def class (js/require "react-native-qrcode"))
+
+(defn qr-code [props]
+ (r/create-element class (clj->js (merge {:inverted true} props))))
\ No newline at end of file
diff --git a/src/status_im/components/react.cljs b/src/status_im/components/react.cljs
index 0ab2d2b755..1347f22ea2 100644
--- a/src/status_im/components/react.cljs
+++ b/src/status_im/components/react.cljs
@@ -46,6 +46,14 @@
(when-let [picker (get-react-property "Picker")]
(adapt-class (.-Item picker))))
+(def pan-responder (.-PanResponder js/React))
+(def animated (.-Animated js/React))
+(def animated-view (r/adapt-react-class (.-View animated)))
+(def animated-text (r/adapt-react-class (.-Text animated)))
+
+(def dimensions (.-Dimensions js/React))
+(defn get-dimensions [name]
+ (js->clj (.get dimensions name) :keywordize-keys true))
(defn icon
([n] (icon n {}))
diff --git a/src/status_im/components/styles.cljs b/src/status_im/components/styles.cljs
index 5aa0bf675d..a1176d46bf 100644
--- a/src/status_im/components/styles.cljs
+++ b/src/status_im/components/styles.cljs
@@ -19,6 +19,7 @@
(def text1-color color-black)
(def text2-color color-gray)
(def text3-color color-blue)
+(def text4-color color-white)
(def online-color color-blue)
(def new-messages-count-color color-blue-transparent)
(def chat-background color-light-gray)
@@ -28,5 +29,73 @@
(def toolbar-background2 color-light-gray)
(def default-chat-color color-purple)
+(def toolbar-height 56)
+
(def flex
- {:style {:flex 1}})
+ {:flex 1})
+
+(def hamburger-icon
+ {:width 16
+ :height 12})
+
+(def icon-search
+ {:width 17
+ :height 17})
+
+(def create-icon
+ {:fontSize 20
+ :height 22
+ :color :white})
+
+(def icon-back
+ {:width 8
+ :height 14})
+
+(def icon-add
+ {:width 14
+ :height 14})
+
+(def icon-ok
+ {:width 18
+ :height 14})
+
+(def icon-qr
+ {:width 23
+ :height 22})
+
+(def icon-plus
+ {:width 18
+ :height 18})
+
+(def form-text-input
+ {:marginLeft -4
+ :fontSize 14
+ :fontFamily font
+ :color text1-color})
+
+(def white-form-text-input
+ {:marginLeft -4
+ :fontSize 14
+ :fontFamily font
+ :color color-white})
+
+(def toolbar-title-container
+ {:flex 1
+ :alignItems :center
+ :justifyContent :center})
+
+(def toolbar-title-text
+ {:marginTop -2.5
+ :color text1-color
+ :fontSize 16
+ :fontFamily font})
+
+(def button-input-container
+ {:flex 1
+ :flexDirection :row
+ :height 50})
+
+(def button-input
+ {:flex 1
+ :flexDirection :column
+ :height 50})
\ No newline at end of file
diff --git a/src/status_im/components/tabs/styles.cljs b/src/status_im/components/tabs/styles.cljs
index f3571cf3c8..45af60e439 100644
--- a/src/status_im/components/tabs/styles.cljs
+++ b/src/status_im/components/tabs/styles.cljs
@@ -54,17 +54,11 @@
:justifyContent :center
:alignItems :center})
-(def show-tab
- {:flex 1
- :pointerEvents :auto
- :position :absolute
- :top 0
- :left 0
- :right 0
- :bottom tab-height})
-
-(def hide-tab
- {:opacity 0
- :pointerEvents :none
- :overflow :hidden})
-
+(defn tab-view-container [offset-x]
+ {:flex 1
+ :position :absolute
+ :top 0
+ :left 0
+ :right 0
+ :bottom tab-height
+ :transform [{:translateX offset-x}]})
diff --git a/src/status_im/components/tabs/tab.cljs b/src/status_im/components/tabs/tab.cljs
index e2f56af6b5..9ee44dcc44 100644
--- a/src/status_im/components/tabs/tab.cljs
+++ b/src/status_im/components/tabs/tab.cljs
@@ -10,9 +10,9 @@
[status-im.components.tabs.styles :as st]))
(defview tab [{:keys [view-id title icon selected-view-id]}]
- [touchable-highlight {:style st/tab
- :onPress #(dispatch [:navigate-to
- view-id])}
+ [touchable-highlight {:style st/tab
+ :disabled (= view-id selected-view-id)
+ :onPress #(dispatch [:navigate-to-tab view-id])}
[view {:style st/tab-container}
[image {:source {:uri icon}
:style st/tab-icon}]
diff --git a/src/status_im/components/toolbar.cljs b/src/status_im/components/toolbar.cljs
index 0591c1aba1..820b338b9c 100644
--- a/src/status_im/components/toolbar.cljs
+++ b/src/status_im/components/toolbar.cljs
@@ -1,24 +1,28 @@
(ns status-im.components.toolbar
(:require [re-frame.core :refer [subscribe dispatch]]
[status-im.components.react :refer [view
- text-input
- icon
- text
- image
- touchable-highlight]]
+ text-input
+ icon
+ text
+ image
+ touchable-highlight]]
[status-im.components.styles :refer [font
- title-font
- color-white
- color-purple
- text1-color
- text2-color
- toolbar-background1]]))
+ title-font
+ color-white
+ color-purple
+ text1-color
+ text2-color
+ toolbar-background1
+ toolbar-title-container
+ toolbar-title-text
+ icon-back
+ toolbar-height]]))
(defn toolbar [{:keys [title nav-action hide-nav? action custom-action
background-color custom-content style]}]
(let [style (merge {:flexDirection :row
:backgroundColor (or background-color toolbar-background1)
- :height 56
+ :height toolbar-height
:elevation 2} style)]
[view {:style style}
(when (not hide-nav?)
@@ -31,20 +35,14 @@
[image (:image nav-action)]]]
[touchable-highlight {:on-press #(dispatch [:navigate-back])}
[view {:width 56
- :height 56}
+ :height 56
+ :alignItems :center
+ :justifyContent :center}
[image {:source {:uri :icon_back}
- :style {:marginTop 21
- :marginLeft 23
- :width 8
- :height 14}}]]]))
+ :style icon-back}]]]))
(or custom-content
- [view {:style {:flex 1
- :alignItems :center
- :justifyContent :center}}
- [text {:style {:marginTop -2.5
- :color text1-color
- :fontSize 16
- :fontFamily font}}
+ [view {:style toolbar-title-container}
+ [text {:style toolbar-title-text}
title]])
custom-action
(when action
diff --git a/src/status_im/constants.cljs b/src/status_im/constants.cljs
index a684182899..556f9b7898 100644
--- a/src/status_im/constants.cljs
+++ b/src/status_im/constants.cljs
@@ -10,3 +10,8 @@
(def content-type-command "command")
(def content-type-command-request "command-request")
(def content-type-status "status")
+
+(def max-chat-name-length 20)
+
+(def response-input-hiding-duration 300)
+(def response-suggesstion-resize-duration 100)
diff --git a/src/status_im/contacts/handlers.cljs b/src/status_im/contacts/handlers.cljs
index 2fd7e586c4..2e0cdc73a6 100644
--- a/src/status_im/contacts/handlers.cljs
+++ b/src/status_im/contacts/handlers.cljs
@@ -103,3 +103,21 @@
(register-handler :add-contacts
(after save-contacts!)
add-new-contacts)
+
+(defn add-new-contact [db [_ {:keys [whisper-identity] :as contact}]]
+ (-> db
+ (update :contacts assoc whisper-identity contact)
+ (assoc :new-contact {:name ""
+ :address ""
+ :whisper-identity ""
+ :phone-number ""})))
+
+(register-handler :add-new-contact
+ (after save-contact)
+ add-new-contact)
+
+(defn set-new-contact-from-qr
+ [{:keys [new-contact] :as db} [_ _ qr-contact]]
+ (assoc db :new-contact (merge new-contact qr-contact)))
+
+(register-handler :set-new-contact-from-qr set-new-contact-from-qr)
diff --git a/src/status_im/contacts/screen.cljs b/src/status_im/contacts/screen.cljs
index b8672807da..a763c2ea50 100644
--- a/src/status_im/contacts/screen.cljs
+++ b/src/status_im/contacts/screen.cljs
@@ -6,9 +6,18 @@
touchable-highlight
list-view
list-item]]
+ [status-im.components.action-button :refer [action-button
+ action-button-item]]
[status-im.contacts.views.contact :refer [contact-view]]
[status-im.components.styles :refer [toolbar-background2]]
[status-im.components.toolbar :refer [toolbar]]
+ [status-im.components.drawer.view :refer [drawer-view open-drawer]]
+ [status-im.components.icons.ionicons :refer [icon]]
+ [status-im.components.styles :refer [color-blue
+ hamburger-icon
+ icon-search
+ create-icon
+ toolbar-background2]]
[status-im.contacts.styles :as st]
[status-im.utils.listview :as lw]
[status-im.i18n :refer [label]]))
@@ -17,20 +26,34 @@
(list-item [contact-view row]))
(defn contact-list-toolbar []
- [toolbar {:title (label :t/contacts)
+ [toolbar {:nav-action {:image {:source {:uri :icon_hamburger}
+ :style hamburger-icon}
+ :handler open-drawer}
+ :title (label :t/contacts)
:background-color toolbar-background2
:action {:image {:source {:uri :icon_search}
- :style st/search-icon}
+ :style icon-search}
:handler (fn [])}}])
(defview contact-list []
[contacts [:get-contacts]]
- [view st/contacts-list-container
- [contact-list-toolbar]
- ;; todo what if there is no contacts, should we show some information
- ;; about this?
- (when contacts
- [list-view {:dataSource (lw/to-datasource contacts)
- :enableEmptySections true
- :renderRow render-row
- :style st/contacts-list}])])
+ [drawer-view
+ [view st/contacts-list-container
+ [contact-list-toolbar]
+ ;; todo what if there is no contacts, should we show some information
+ ;; about this?
+ (when contacts
+ [list-view {:dataSource (lw/to-datasource contacts)
+ :enableEmptySections true
+ :renderRow render-row
+ :style st/contacts-list}])
+ [action-button {:buttonColor color-blue
+ :offsetY 16
+ :offsetX 16}
+ [action-button-item
+ {:title (label :t/new-contact)
+ :buttonColor :#9b59b6
+ :onPress #(dispatch [:navigate-to :new-contact])}
+ [icon {:name :android-create
+ :style create-icon}]]
+ ]]])
diff --git a/src/status_im/contacts/styles.cljs b/src/status_im/contacts/styles.cljs
index 4cbec2cdff..4b64148fc9 100644
--- a/src/status_im/contacts/styles.cljs
+++ b/src/status_im/contacts/styles.cljs
@@ -1,13 +1,12 @@
(ns status-im.contacts.styles
(:require [status-im.components.styles :refer [font
- title-font
- text1-color
- color-white
- online-color]]))
+ title-font
+ text1-color
+ color-white
+ toolbar-background2
+ online-color]]))
+
-(def search-icon
- {:width 17
- :height 17})
(def contacts-list-container
{:flex 1
@@ -67,3 +66,20 @@
:fontSize 16
:fontFamily font
:color text1-color})
+
+; new contact
+
+(def contact-form-container
+ {:flex 1
+ :color :white})
+
+(def gradient-background
+ {:position :absolute
+ :top 0
+ :right 0
+ :bottom 0
+ :left 0})
+
+(def form-container
+ {:marginLeft 16
+ :margin-top 50})
\ No newline at end of file
diff --git a/src/status_im/contacts/views/new_contact.cljs b/src/status_im/contacts/views/new_contact.cljs
new file mode 100644
index 0000000000..469ed6b914
--- /dev/null
+++ b/src/status_im/contacts/views/new_contact.cljs
@@ -0,0 +1,78 @@
+(ns status-im.contacts.views.new-contact
+ (:require-macros [status-im.utils.views :refer [defview]])
+ (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
+ [status-im.components.react :refer [view
+ text
+ text-input
+ image
+ linear-gradient
+ touchable-highlight]]
+ [status-im.components.toolbar :refer [toolbar]]
+ [status-im.components.drawer.view :refer [drawer-view open-drawer]]
+ [status-im.components.styles :refer [color-purple
+ color-white
+ icon-search
+ icon-back
+ icon-qr
+ toolbar-background1
+ toolbar-title-container
+ toolbar-title-text
+ button-input-container
+ button-input
+ white-form-text-input]]
+ [status-im.qr-scanner.views.import-button :refer [import-button]]
+ [status-im.i18n :refer [label]]
+ [status-im.contacts.styles :as st]))
+
+
+
+(def toolbar-title
+ [view toolbar-title-container
+ [text {:style (merge toolbar-title-text {:color color-white})}
+ (label :t/new-contact)]])
+
+(defview contact-name-input [name]
+ []
+ [text-input
+ {:underlineColorAndroid color-white
+ :placeholderTextColor color-white
+ :style white-form-text-input
+ :autoFocus true
+ :placeholder (label :t/contact-name)
+ :onChangeText #(dispatch [:set-in [:new-contact :name] %])}
+ name])
+
+(defview contact-whisper-id-input [whisper-identity]
+ [view button-input-container
+ [text-input
+ {:underlineColorAndroid color-white
+ :placeholderTextColor color-white
+ :style (merge white-form-text-input button-input)
+ :autoFocus true
+ :placeholder (label :t/whisper-identity)
+ :onChangeText #(dispatch [:set-in [:new-contact :whisper-identity] %])}
+ whisper-identity]
+ [import-button #(dispatch [:scan-qr-code {:toolbar-title (label :t/new-contact)} :set-new-contact-from-qr])]])
+
+(defview new-contact []
+ [{:keys [name whisper-identity phone-number] :as new-contact} [:get :new-contact]]
+ [drawer-view
+ [view st/contact-form-container
+ [linear-gradient {:colors ["rgba(182, 116, 241, 1)" "rgba(107, 147, 231, 1)" "rgba(43, 171, 238, 1)"]
+ :start [0, 0]
+ :end [0.5, 1]
+ :locations [0, 0.8 ,1]
+ :style st/gradient-background}]
+
+ [toolbar {:background-color :transparent
+ :nav-action {:image {:source {:uri :icon_back_white}
+ :style icon-back}
+ :handler #(dispatch [:navigate-back])}
+ :custom-content toolbar-title
+ :action {:image {:source {:uri :icon_add}
+ :style icon-search}
+ :handler #(dispatch [:add-new-contact new-contact])}}]
+ [view st/form-container
+ [contact-whisper-id-input whisper-identity]
+ [contact-name-input name]
+ ]]])
diff --git a/src/status_im/db.cljs b/src/status_im/db.cljs
index a46c61f087..2b3ad6f88f 100644
--- a/src/status_im/db.cljs
+++ b/src/status_im/db.cljs
@@ -1,5 +1,7 @@
(ns status-im.db
- (:require [schema.core :as s :include-macros true]))
+ (:require [schema.core :as s :include-macros true]
+ [status-im.components.react :refer [animated]]
+ [status-im.components.animation :as anim]))
;; schema of app-db
(def schema {:greeting s/Str})
@@ -29,7 +31,20 @@
:email "myemail@gmail.com"
:status "Hi, this is my status"
:current-tag nil
- :disable-group-creation false})
+ :qr-codes {}
+ :new-contact {:name ""
+ :address ""
+ :whisper-identity ""
+ :phone-number ""}
+ :disable-group-creation false
+ :animations {;; mutable data
+ :to-response-height nil
+ :response-height-current nil
+ :message-input-offset 0
+ :message-input-buttons-scale 1
+ :messages-offset 0
+ :commands-input-is-switching? false
+ :response-resize? false}})
(def protocol-initialized-path [:protocol-initialized])
(defn chat-input-text-path [chat-id]
diff --git a/src/status_im/group_settings/screen.cljs b/src/status_im/group_settings/screen.cljs
index 346b783912..a35bdbcf53 100644
--- a/src/status_im/group_settings/screen.cljs
+++ b/src/status_im/group_settings/screen.cljs
@@ -146,7 +146,9 @@
(defview chat-name []
[name [:chat :name]
new-name [:get :new-chat-name]
- focused? [:get ::name-input-focused]]
+ validation-messages [:new-chat-name-validation-messages]
+ focused? [:get ::name-input-focused]
+ valid? [:new-chat-name-valid?]]
[view
[text {:style st/chat-name-text} (label :t/chat-name)]
[view (st/chat-name-value-container focused?)
@@ -157,12 +159,14 @@
:on-blur blur}
name]
(if (or focused? (not= name new-name))
- [touchable-highlight {:style st/chat-name-btn-edit-container
+ [touchable-highlight {:style (st/chat-name-btn-edit-container valid?)
:on-press save}
[view [icon :ok-purple st/add-members-icon]]]
- [touchable-highlight {:style st/chat-name-btn-edit-container
+ [touchable-highlight {:style (st/chat-name-btn-edit-container true)
:on-press focus}
- [text {:style st/chat-name-btn-edit-text} (label :t/edit)]])]])
+ [text {:style st/chat-name-btn-edit-text} (label :t/edit)]])]
+ (when (pos? (count validation-messages))
+ [text {:style st/chat-name-validation-message} (first validation-messages)])])
(defview group-settings []
[show-color-picker [:group-settings :show-color-picker]]
diff --git a/src/status_im/group_settings/styles/group_settings.cljs b/src/status_im/group_settings/styles/group_settings.cljs
index 91d407c604..9950bb2f9c 100644
--- a/src/status_im/group_settings/styles/group_settings.cljs
+++ b/src/status_im/group_settings/styles/group_settings.cljs
@@ -91,9 +91,15 @@
:fontFamily font
:color text1-color})
-(def chat-name-btn-edit-container
+(def chat-name-validation-message
+ {:marginTop 8
+ :marginLeft 16
+ :color :red})
+
+(defn chat-name-btn-edit-container [enabled?]
{:padding 16
- :justifyContent :center})
+ :justifyContent :center
+ :opacity (if enabled? 1 0.3)})
(def chat-name-btn-edit-text
{:marginTop -1
diff --git a/src/status_im/group_settings/subs.cljs b/src/status_im/group_settings/subs.cljs
index f2b6ac1db3..c1bb17da4d 100644
--- a/src/status_im/group_settings/subs.cljs
+++ b/src/status_im/group_settings/subs.cljs
@@ -1,6 +1,7 @@
(ns status-im.group-settings.subs
(:require-macros [reagent.ratom :refer [reaction]])
- (:require [re-frame.core :refer [register-sub]]))
+ (:require [re-frame.core :refer [register-sub]]
+ [status-im.constants :refer [max-chat-name-length]]))
(register-sub :selected-participant
(fn [db _]
@@ -11,3 +12,20 @@
(register-sub :group-settings
(fn [db [_ k]]
(reaction (get-in @db [:group-settings k]))))
+
+(defn get-chat-name-validation-messages [chat-name]
+ (filter some?
+ (list (when (zero? (count chat-name))
+ "Chat name can't be empty")
+ (when (< max-chat-name-length (count chat-name))
+ "Chat name is too long"))))
+
+(register-sub :new-chat-name-validation-messages
+ (fn [db [_]]
+ (let [chat-name (reaction (:new-chat-name @db))]
+ (reaction (get-chat-name-validation-messages @chat-name)))))
+
+(register-sub :new-chat-name-valid?
+ (fn [db [_]]
+ (let [chat-name (reaction (:new-chat-name @db))]
+ (reaction (zero? (count (get-chat-name-validation-messages @chat-name)))))))
diff --git a/src/status_im/handlers.cljs b/src/status_im/handlers.cljs
index bb48cec2d5..6540a30948 100644
--- a/src/status_im/handlers.cljs
+++ b/src/status_im/handlers.cljs
@@ -15,9 +15,10 @@
status-im.discovery.handlers
status-im.new-group.handlers
status-im.participants.handlers
- status-im.protocol.handlers
status-im.commands.handlers.loading
- status-im.commands.handlers.jail))
+ status-im.commands.handlers.jail
+ status-im.qr-scanner.handlers
+ status-im.protocol.handlers))
;; -- Middleware ------------------------------------------------------------
;;
@@ -49,6 +50,10 @@
debug
set-in)
+(register-handler :set-animation
+ (fn [db [_ k v]]
+ (assoc-in db [:animations k] v)))
+
(register-handler :initialize-db
(fn [_ _]
(assoc app-db
diff --git a/src/status_im/handlers/content_suggestions.cljs b/src/status_im/handlers/content_suggestions.cljs
index b8c193a8eb..c3928b3303 100644
--- a/src/status_im/handlers/content_suggestions.cljs
+++ b/src/status_im/handlers/content_suggestions.cljs
@@ -4,19 +4,22 @@
[status-im.utils.logging :as log]
[clojure.string :as s]))
+;; TODO stub data?
(def suggestions
- {:phone [{:value "89171111111"
+ {:phone [{:header "Phone number formats"}
+ {:value "89171111111"
:description "Number format 1"}
- {:value "+79171111111"
+ {:value "+79171111111"
:description "Number format 2"}
- {:value "9171111111"
+ {:value "9171111111"
:description "Number format 3"}]})
-(defn get-content-suggestions [db command text]
+(defn get-content-suggestions [command text]
(or (when command
(when-let [command-suggestions ((keyword (:name command)) suggestions)]
(filterv (fn [s]
- (and (.startsWith (:value s) (or text ""))
- (not= (:value s) text)))
+ (or (:header s)
+ (and (.startsWith (:value s) (or text ""))
+ (not= (:value s) text))))
command-suggestions)))
[]))
diff --git a/src/status_im/i18n.cljs b/src/status_im/i18n.cljs
index bdc113852c..f07f1d6d24 100644
--- a/src/status_im/i18n.cljs
+++ b/src/status_im/i18n.cljs
@@ -9,13 +9,15 @@
(set! (.-translations i18n) (clj->js {:en en/translations}))
-(defn label [path & options]
- (if (exists? i18n.t)
- (.t i18n (name path) (clj->js options))
- (name path)))
+(defn label
+ ([path] (label path {}))
+ ([path options]
+ (if (exists? i18n.t)
+ (.t i18n (name path) (clj->js options))
+ (name path))))
+
(defn label-pluralize [count path & options]
(if (exists? i18n.t)
(.p i18n count (name path) (clj->js options))
- (name path)))
-
+ (name path)))
\ No newline at end of file
diff --git a/src/status_im/models/commands.cljs b/src/status_im/models/commands.cljs
index 7da49a9b4b..aa426640eb 100644
--- a/src/status_im/models/commands.cljs
+++ b/src/status_im/models/commands.cljs
@@ -3,6 +3,7 @@
[clojure.walk :refer [stringify-keys keywordize-keys]]
[re-frame.core :refer [subscribe dispatch]]
[status-im.db :as db]
+ [status-im.components.animation :as anim]
[status-im.components.styles :refer [color-blue color-dark-mint]]
[status-im.i18n :refer [label]]))
@@ -34,7 +35,7 @@
[{:keys [current-chat-id] :as db} msg-id command-key]
(update-in db [:chats current-chat-id :command-input] merge
{:content nil
- :command (merge (get-command db command-key))
+ :command (get-command db command-key)
:to-msg-id msg-id}))
(defn set-chat-command [db command-key]
diff --git a/src/status_im/models/messages.cljs b/src/status_im/models/messages.cljs
index 3d6611a887..22ec4a564c 100644
--- a/src/status_im/models/messages.cljs
+++ b/src/status_im/models/messages.cljs
@@ -49,9 +49,11 @@
(defn get-messages [chat-id]
(->> (-> (r/get-by-field :msgs :chat-id chat-id)
- (r/sorted :timestamp :asc)
+ (r/sorted :timestamp :desc)
(r/collection->map))
(into '())
+ ;; todo why reverse?
+ reverse
(map (fn [{:keys [content-type preview] :as message}]
(if (command-type? content-type)
(-> message
diff --git a/src/status_im/navigation/handlers.cljs b/src/status_im/navigation/handlers.cljs
index 9e249c6186..e687ed45c6 100644
--- a/src/status_im/navigation/handlers.cljs
+++ b/src/status_im/navigation/handlers.cljs
@@ -42,12 +42,25 @@
(assoc :view-id view-id)
(assoc :navigation-stack navigation-stack'))))))
+(register-handler :navigate-to-tab
+ (enrich preload-data!)
+ (fn [db [_ view-id]]
+ (-> db
+ (assoc :prev-tab-view-id (:view-id db))
+ (replace-view view-id))))
+
+(register-handler :on-navigated-to-tab
+ (enrich preload-data!)
+ (fn [db [_]]
+ (assoc db :prev-tab-view-id nil)))
+
(register-handler :show-group-new
(debug
(fn [db _]
(-> db
(push-view :new-group)
- (assoc :new-group #{})))))
+ (assoc :new-group #{})
+ (assoc :new-chat-name nil)))))
(register-handler :show-contacts
(fn [db _]
diff --git a/src/status_im/new_group/screen.cljs b/src/status_im/new_group/screen.cljs
index 4b266486c6..9e81f77b89 100644
--- a/src/status_im/new_group/screen.cljs
+++ b/src/status_im/new_group/screen.cljs
@@ -3,13 +3,13 @@
(:require [re-frame.core :refer [subscribe dispatch]]
[status-im.resources :as res]
[status-im.components.react :refer [view
- text-input
- text
- image
- icon
- touchable-highlight
- list-view
- list-item]]
+ text-input
+ text
+ image
+ icon
+ touchable-highlight
+ list-view
+ list-item]]
[status-im.components.styles :refer [color-purple]]
[status-im.components.toolbar :refer [toolbar]]
[status-im.utils.listview :refer [to-datasource]]
@@ -19,24 +19,30 @@
(defview new-group-toolbar []
- [group-name [:get ::group-name]
- creation-disabled? [:get :disable-group-creation]]
- [toolbar
- {:title (label :t/new-group-chat)
- :action {:image {:source res/v ;; {:uri "icon_search"}
- :style st/toolbar-icon}
- :handler (when-not creation-disabled?
- #(dispatch [:init-group-creation group-name]))}}])
+ [group-name [:get :new-chat-name]
+ creation-disabled? [:get :disable-group-creation]
+ valid? [:new-chat-name-valid?]]
+ (let [create-btn-enabled? (and valid? (not creation-disabled?))]
+ [toolbar
+ {:title (label :t/new-group-chat)
+ :action {:image {:source res/v ;; {:uri "icon_search"}
+ :style (st/toolbar-icon create-btn-enabled?)}
+ :handler (when create-btn-enabled?
+ #(dispatch [:init-group-creation group-name]))}}]))
(defview group-name-input []
- [group-name [:get ::group-name]]
- [text-input
- {:underlineColorAndroid color-purple
- :style st/group-name-input
- :autoFocus true
- :placeholder (label :t/group-name)
- :onChangeText #(dispatch [:set ::group-name %])}
- group-name])
+ [group-name [:get :new-chat-name]
+ validation-messages [:new-chat-name-validation-messages]]
+ [view
+ [text-input
+ {:underlineColorAndroid color-purple
+ :style st/group-name-input
+ :autoFocus true
+ :placeholder (label :t/group-name)
+ :onChangeText #(dispatch [:set :new-chat-name %])}
+ group-name]
+ (when (pos? (count validation-messages))
+ [text {:style st/group-name-validation-message} (first validation-messages)])])
(defview new-group []
[contacts [:all-contacts]]
diff --git a/src/status_im/new_group/styles.cljs b/src/status_im/new_group/styles.cljs
index cd16b8dcc8..3d15673453 100644
--- a/src/status_im/new_group/styles.cljs
+++ b/src/status_im/new_group/styles.cljs
@@ -7,9 +7,10 @@
text2-color
toolbar-background1]]))
-(def toolbar-icon
- {:width 20
- :height 18})
+(defn toolbar-icon [enabled?]
+ {:width 20
+ :height 18
+ :opacity (if enabled? 1 0.3)})
(def new-group-container
{:flex 1
@@ -33,6 +34,9 @@
:fontFamily font
:color text1-color})
+(def group-name-validation-message
+ {:color :red})
+
(def members-text
{:marginTop 24
:marginBottom 16
diff --git a/src/status_im/profile/screen.cljs b/src/status_im/profile/screen.cljs
index bb0c52a1d8..ffff2d2e47 100644
--- a/src/status_im/profile/screen.cljs
+++ b/src/status_im/profile/screen.cljs
@@ -11,6 +11,8 @@
[status-im.components.chat-icon.screen :refer [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]]
[status-im.i18n :refer [label]]))
(defn profile-property-view [{:keys [name value]}]
@@ -64,7 +66,8 @@
photo-path [:get :photo-path]
phone-number [:get :phone-number]
email [:get :email]
- status [:get :status]]
+ status [:get :status]
+ identity [:get-in [:user-identity :public]]]
[scroll-view {:style st/profile}
[touchable-highlight {:style st/back-btn-touchable
:on-press #(dispatch [:navigate-back])}
@@ -81,10 +84,14 @@
[my-profile-icon]]
[text {:style st/user-name} username]
[text {:style st/status} status]]
- [view st/profile-properties-container
+ [scroll-view st/profile-properties-container
[profile-property-view {:name (label :t/username)
:value username}]
[profile-property-view {:name (label :t/phone-number)
:value phone-number}]
[profile-property-view {:name (label :t/email)
- :value email}]]])
+ :value email}]
+ [view st/qr-code-container
+ [qr-code {:value (clj->json {:name username
+ :whisper-identity identity})
+ :size 200}]]]])
diff --git a/src/status_im/profile/styles.cljs b/src/status_im/profile/styles.cljs
index 95d230920e..fcaaad9b54 100644
--- a/src/status_im/profile/styles.cljs
+++ b/src/status_im/profile/styles.cljs
@@ -135,3 +135,8 @@
:color text2-color
;; IOS:
:letterSpacing 0.5})
+
+(def qr-code-container
+ {:flex 1
+ :alignItems :center
+ :margin 15})
diff --git a/src/status_im/qr_scanner/handlers.cljs b/src/status_im/qr_scanner/handlers.cljs
new file mode 100644
index 0000000000..be05d7fb65
--- /dev/null
+++ b/src/status_im/qr_scanner/handlers.cljs
@@ -0,0 +1,38 @@
+(ns status-im.qr-scanner.handlers
+ (:require [re-frame.core :refer [register-handler after dispatch debug enrich]]
+ [status-im.navigation.handlers :as nav]
+ [status-im.utils.handlers :as u]))
+
+(defmethod nav/preload-data! :qr-scanner
+ [db [_ _ identifier]]
+ (assoc db :current-qr-context identifier))
+
+(defn set-current-identifier [db [_ identifier handler]]
+ (assoc-in db [:qr-codes identifier] handler))
+
+(defn navigate-to-scanner
+ [_ [_ identifier]]
+ (dispatch [:navigate-to :qr-scanner identifier]))
+
+(register-handler :scan-qr-code
+ (after navigate-to-scanner)
+ set-current-identifier)
+
+(register-handler :clear-qr-code
+ (fn [db [_ identifier]]
+ (update db :qr-codes dissoc identifier)))
+
+(defn handle-qr-request
+ [db [_ context data]]
+ (let [handler (get-in db [:qr-codes context])]
+ (dispatch [handler context data])))
+
+(defn clear-qr-request [db [_ context]]
+ (-> db
+ (update :qr-codes dissoc context)
+ (dissoc :current-qr-context)))
+
+(register-handler :set-qr-code
+ (-> (u/side-effect! handle-qr-request)
+ ((enrich clear-qr-request))
+ ((after #(dispatch [:navigate-back])))))
diff --git a/src/status_im/qr_scanner/screen.cljs b/src/status_im/qr_scanner/screen.cljs
new file mode 100644
index 0000000000..2f27d30b9f
--- /dev/null
+++ b/src/status_im/qr_scanner/screen.cljs
@@ -0,0 +1,37 @@
+(ns status-im.qr-scanner.screen
+ (:require-macros [status-im.utils.views :refer [defview]])
+ (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
+ [status-im.components.react :refer [view
+ image]]
+ [status-im.components.camera :refer [camera]]
+ [status-im.components.styles :refer [toolbar-background1
+ icon-search]]
+ [status-im.components.toolbar :refer [toolbar]]
+ [status-im.qr-scanner.styles :as st]
+ [status-im.utils.types :refer [json->clj]]))
+
+(defn qr-scanner-toolbar [title]
+ [toolbar {:title title
+ :background-color toolbar-background1
+ :action {:image {:source {:uri :icon_lock_white}
+ :style icon-search}
+ :handler #()}}])
+
+(defview qr-scanner []
+ [identifier [:get :current-qr-context]]
+ [view st/barcode-scanner-container
+ [qr-scanner-toolbar (:toolbar-title identifier)]
+ [camera {;:on-bar-code-read #(js/alert "ok")
+ :onBarCodeRead #(let [data (json->clj (.-data %))]
+ (dispatch [:set-qr-code identifier data]))
+ :style st/barcode-scanner}]
+ [view st/rectangle-container
+ [view st/rectangle
+ [image {:source {:uri :corner_left_top}
+ :style st/corner-left-top}]
+ [image {:source {:uri :corner_right_top}
+ :style st/corner-right-top}]
+ [image {:source {:uri :corner_right_bottom}
+ :style st/corner-right-bottom}]
+ [image {:source {:uri :corner_left_bottom}
+ :style st/corner-left-bottom}]]]])
diff --git a/src/status_im/qr_scanner/styles.cljs b/src/status_im/qr_scanner/styles.cljs
new file mode 100644
index 0000000000..05198031b1
--- /dev/null
+++ b/src/status_im/qr_scanner/styles.cljs
@@ -0,0 +1,76 @@
+(ns status-im.qr-scanner.styles
+ (:require [status-im.components.styles :refer [toolbar-height
+ color-white]]))
+
+(def barcode-scanner-container
+ {:flex 1
+ :backgroundColor :white})
+
+(def barcode-scanner
+ {:flex 1
+ :justifyContent :flex-end
+ :alignItems :center})
+
+(def rectangle-container
+ {:position :absolute
+ :left 0
+ :top toolbar-height
+ :bottom 0
+ :right 0
+ :flex 1
+ :alignItems :center
+ :justifyContent :center
+ :backgroundColor :transparent})
+
+(def rectangle
+ {:height 250
+ :width 250
+ :backgroundColor :transparent})
+
+(def corner-left-top
+ {:position :absolute
+ :left 0
+ :top 0
+ :width 56
+ :height 56})
+
+(def corner-right-top
+ {:position :absolute
+ :right 0
+ :top 0
+ :width 56
+ :height 56})
+
+(def corner-right-bottom
+ {:position :absolute
+ :right 0
+ :bottom 0
+ :width 56
+ :height 56})
+
+(def corner-left-bottom
+ {:position :absolute
+ :left 0
+ :bottom 0
+ :width 56
+ :height 56})
+
+(def import-button
+ {:position :absolute
+ :right 16
+ :flex 1
+ :height 50
+ :alignItems :center})
+
+(def import-button-content
+ {:flex 1
+ :flexDirection :row
+ :height 50
+ :alignItems :center
+ :alignSelf :center})
+
+(def import-text
+ {:flex 1
+ :flexDirection :column
+ :color color-white
+ :margin-left 8})
diff --git a/src/status_im/qr_scanner/views/import-qr-button.cljs b/src/status_im/qr_scanner/views/import-qr-button.cljs
new file mode 100644
index 0000000000..99b7dbc7b7
--- /dev/null
+++ b/src/status_im/qr_scanner/views/import-qr-button.cljs
@@ -0,0 +1,23 @@
+(ns status-im.qr-scanner.views.import-button
+ (:require-macros [status-im.utils.views :refer [defview]])
+ (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
+ [status-im.components.react :refer [view
+ text
+ image
+ touchable-highlight]]
+ [status-im.components.toolbar :refer [toolbar]]
+ [status-im.components.drawer.view :refer [drawer-view open-drawer]]
+ [status-im.components.styles :refer [icon-qr]]
+ [status-im.i18n :refer [label]]
+ [status-im.qr-scanner.styles :as st]))
+
+
+(defview import-button [handler]
+ []
+ [view st/import-button
+ [touchable-highlight
+ {:on-press handler}
+ [view st/import-button-content
+ [image {:source {:uri :icon_qr}
+ :style icon-qr}]
+ [text {:style st/import-text} (label :t/import-qr)]]]])
\ No newline at end of file
diff --git a/src/status_im/subs.cljs b/src/status_im/subs.cljs
index 972c57887c..f31dc4ebe2 100644
--- a/src/status_im/subs.cljs
+++ b/src/status_im/subs.cljs
@@ -15,3 +15,7 @@
(register-sub :get-in
(fn [db [_ path]]
(reaction (get-in @db path))))
+
+(register-sub :animations
+ (fn [db [_ k]]
+ (reaction (get-in @db [:animations k]))))
diff --git a/src/status_im/translations/en.cljs b/src/status_im/translations/en.cljs
index cf3424d440..8ec3ba3fb7 100644
--- a/src/status_im/translations/en.cljs
+++ b/src/status_im/translations/en.cljs
@@ -92,7 +92,7 @@
:location-command-description "Send location"
:phone-command-description "Send phone number"
:phone-request-text "Phone number request"
- :confirmation-code-coomand-description "Send confirmation code"
+ :confirmation-code-command-description "Send confirmation code"
:confirmation-code-request-text "Confirmation code request"
:send-command-description "Send location"
:request-command-description "Send request"
@@ -115,8 +115,18 @@
:You "You"
;new-contact
- :import-qr "Import from QR"
+ :import-qr "Import"
:contact-name "Contact Name"
- :contact-address "Contact Address"
+ :whisper-identity "Whisper Identity"
+
+ ;login
+ :recover-from-passphrase "Recover from passphrase"
+ :connect "Connect"
+ :address "Address"
+ :password "Password"
+ :login "Login"
+
+ ;users
+ :add-account "Add account"
})
\ No newline at end of file
diff --git a/src/status_im/utils/listview.cljs b/src/status_im/utils/listview.cljs
index dcd63da8bd..d9ab791710 100644
--- a/src/status_im/utils/listview.cljs
+++ b/src/status_im/utils/listview.cljs
@@ -7,3 +7,12 @@
(defn to-datasource [items]
(clone-with-rows (data-source {:rowHasChanged not=}) items))
+
+(defn clone-with-rows-inverted [ds rows]
+ (let [rows (reduce (fn [ac el] (.push ac el) ac)
+ (clj->js []) (reverse rows))
+ row-ids (.reverse (.map rows (fn [_ index] index)))]
+ (.cloneWithRows ds rows row-ids)))
+
+(defn to-datasource-inverted [items]
+ (clone-with-rows-inverted (data-source {:rowHasChanged not=}) items))
diff --git a/src/status_im/utils/types.cljs b/src/status_im/utils/types.cljs
index aa256f0056..93c942df77 100644
--- a/src/status_im/utils/types.cljs
+++ b/src/status_im/utils/types.cljs
@@ -6,4 +6,10 @@
s))
(defn to-edn-string [value]
- (with-out-str (pr value)))
\ No newline at end of file
+ (with-out-str (pr value)))
+
+(defn clj->json [data]
+ (.stringify js/JSON (clj->js data)))
+
+(defn json->clj [data]
+ (js->clj (.parse js/JSON data) :keywordize-keys true))