Merge branch 'develop' into feature/#123
First attempt. Conflicts: .re-natal android/app/build.gradle android/settings.gradle src/status_im/chat/handlers.cljs src/status_im/chat/views/command.cljs src/status_im/chat/views/content_suggestions.cljs src/status_im/chat/views/message.cljs src/status_im/chat/views/plain_input.cljs
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "StatusIm",
|
"name": "StatusIm",
|
||||||
"interface": "reagent",
|
"interface": "reagent",
|
||||||
"androidHost": "10.0.3.2",
|
"androidHost": "localhost",
|
||||||
"modules": [
|
"modules": [
|
||||||
"react-native-contacts",
|
"react-native-contacts",
|
||||||
"react-native-invertible-scroll-view",
|
"react-native-invertible-scroll-view",
|
||||||
|
@ -17,7 +17,9 @@
|
||||||
"dismissKeyboard",
|
"dismissKeyboard",
|
||||||
"react-native-linear-gradient",
|
"react-native-linear-gradient",
|
||||||
"react-native-android-sms-listener",
|
"react-native-android-sms-listener",
|
||||||
"react-native-status"
|
"react-native-status",
|
||||||
|
"react-native-camera",
|
||||||
|
"react-native-qrcode"
|
||||||
],
|
],
|
||||||
"imageDirs": [
|
"imageDirs": [
|
||||||
"images"
|
"images"
|
||||||
|
|
|
@ -130,6 +130,7 @@ dependencies {
|
||||||
compile project(':react-native-i18n')
|
compile project(':react-native-i18n')
|
||||||
compile project(':react-native-linear-gradient')
|
compile project(':react-native-linear-gradient')
|
||||||
compile project(':ReactNativeAndroidSmsListener')
|
compile project(':ReactNativeAndroidSmsListener')
|
||||||
|
compile project(':react-native-camera')
|
||||||
compile project(':react-native-status')
|
compile project(':react-native-status')
|
||||||
// todo replace this when jail will be integrated into geth
|
// todo replace this when jail will be integrated into geth
|
||||||
compile (name: "geth-android-16", ext:"aar")
|
compile (name: "geth-android-16", ext:"aar")
|
||||||
|
|
|
@ -8,31 +8,35 @@ import com.facebook.react.shell.MainReactPackage;
|
||||||
import com.rt2zz.reactnativecontacts.ReactNativeContacts;
|
import com.rt2zz.reactnativecontacts.ReactNativeContacts;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.DialogInterface.OnClickListener;
|
||||||
|
import android.content.DialogInterface.OnCancelListener;
|
||||||
import com.github.ethereum.go_ethereum.cmd.Geth;
|
import com.github.ethereum.go_ethereum.cmd.Geth;
|
||||||
import com.bitgo.randombytes.RandomBytesPackage;
|
import com.bitgo.randombytes.RandomBytesPackage;
|
||||||
import com.BV.LinearGradient.LinearGradientPackage;
|
import com.BV.LinearGradient.LinearGradientPackage;
|
||||||
import com.centaurwarchief.smslistener.SmsListener;
|
import com.centaurwarchief.smslistener.SmsListener;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import com.statusim.Jail.JailPackage;
|
import com.statusim.Jail.JailPackage;
|
||||||
|
|
||||||
|
import com.lwansbrough.RCTCamera.*;
|
||||||
import com.i18n.reactnativei18n.ReactNativeI18n;
|
import com.i18n.reactnativei18n.ReactNativeI18n;
|
||||||
import io.realm.react.RealmReactPackage;
|
import io.realm.react.RealmReactPackage;
|
||||||
|
|
||||||
|
|
||||||
public class MainActivity extends ReactActivity {
|
public class MainActivity extends ReactActivity {
|
||||||
|
|
||||||
@Override
|
final Handler handler = new Handler();
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
// Required for android-16 (???)
|
protected void startStatus() {
|
||||||
System.loadLibrary("gethraw");
|
// Required because of crazy APN settings redirecting localhost (found in GB)
|
||||||
System.loadLibrary("geth");
|
|
||||||
|
|
||||||
// Required because of crazy APN settings redirecting localhost
|
|
||||||
Properties properties = System.getProperties();
|
Properties properties = System.getProperties();
|
||||||
properties.setProperty("http.nonProxyHosts", "localhost|127.0.0.1");
|
properties.setProperty("http.nonProxyHosts", "localhost|127.0.0.1");
|
||||||
properties.setProperty("https.nonProxyHosts", "localhost|127.0.0.1");
|
properties.setProperty("https.nonProxyHosts", "localhost|127.0.0.1");
|
||||||
|
@ -44,11 +48,56 @@ public class MainActivity extends ReactActivity {
|
||||||
getApplicationInfo().dataDir;
|
getApplicationInfo().dataDir;
|
||||||
|
|
||||||
// Launch!
|
// Launch!
|
||||||
|
final Runnable addPeer = new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
Log.w("Geth", "adding peer");
|
||||||
|
Geth.run("--exec admin.addPeer(\"enode://e2f28126720452aa82f7d3083e49e6b3945502cb94d9750a15e27ee310eed6991618199f878e5fbc7dfa0e20f0af9554b41f491dc8f1dbae8f0f2d37a3a613aa@139.162.13.89:55555\") attach http://localhost:8545");
|
||||||
|
}
|
||||||
|
};
|
||||||
new Thread(new Runnable() {
|
new Thread(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
Geth.run("--bootnodes enode://e2f28126720452aa82f7d3083e49e6b3945502cb94d9750a15e27ee310eed6991618199f878e5fbc7dfa0e20f0af9554b41f491dc8f1dbae8f0f2d37a3a613aa@139.162.13.89:30303 --shh --ipcdisable --nodiscover --rpc --rpcapi db,eth,net,web3,shh,admin --fast --datadir=" + dataFolder);
|
Geth.run("--shh --ipcdisable --nodiscover --rpc --rpcapi db,eth,net,web3,shh,admin --fast --datadir=" + dataFolder);
|
||||||
|
|
||||||
}
|
}
|
||||||
}).start();
|
}).start();
|
||||||
|
handler.postDelayed(addPeer, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
// Required for android-16 (???)
|
||||||
|
// Crash if put in startStatus() ?
|
||||||
|
System.loadLibrary("gethraw");
|
||||||
|
System.loadLibrary("geth");
|
||||||
|
|
||||||
|
if(!RootUtil.isDeviceRooted()) {
|
||||||
|
startStatus();
|
||||||
|
} else {
|
||||||
|
AlertDialog dialog = new AlertDialog.Builder(MainActivity.this).setMessage(getResources().getString(R.string.root_warning))
|
||||||
|
.setPositiveButton(getResources().getString(R.string.root_okay), new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
dialog.dismiss();
|
||||||
|
startStatus();
|
||||||
|
}
|
||||||
|
}).setNegativeButton(getResources().getString(R.string.root_cancel), new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
dialog.dismiss();
|
||||||
|
MainActivity.this.finishAffinity();
|
||||||
|
}
|
||||||
|
}).setOnCancelListener(new OnCancelListener() {
|
||||||
|
@Override
|
||||||
|
public void onCancel(DialogInterface dialog) {
|
||||||
|
dialog.dismiss();
|
||||||
|
MainActivity.this.finishAffinity();
|
||||||
|
}
|
||||||
|
}).create();
|
||||||
|
dialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -84,6 +133,7 @@ public class MainActivity extends ReactActivity {
|
||||||
new ReactNativeI18n(),
|
new ReactNativeI18n(),
|
||||||
new RandomBytesPackage(),
|
new RandomBytesPackage(),
|
||||||
new LinearGradientPackage(),
|
new LinearGradientPackage(),
|
||||||
|
new RCTCameraPackage(),
|
||||||
new SmsListener(this)
|
new SmsListener(this)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 526 B |
After Width: | Height: | Size: 614 B |
After Width: | Height: | Size: 613 B |
After Width: | Height: | Size: 537 B |
After Width: | Height: | Size: 202 B |
After Width: | Height: | Size: 253 B |
After Width: | Height: | Size: 776 B |
After Width: | Height: | Size: 151 B |
After Width: | Height: | Size: 800 B |
After Width: | Height: | Size: 330 B |
After Width: | Height: | Size: 333 B |
After Width: | Height: | Size: 340 B |
After Width: | Height: | Size: 334 B |
After Width: | Height: | Size: 174 B |
After Width: | Height: | Size: 201 B |
After Width: | Height: | Size: 481 B |
After Width: | Height: | Size: 120 B |
After Width: | Height: | Size: 422 B |
After Width: | Height: | Size: 826 B |
After Width: | Height: | Size: 686 B |
After Width: | Height: | Size: 823 B |
After Width: | Height: | Size: 825 B |
After Width: | Height: | Size: 233 B |
After Width: | Height: | Size: 329 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 145 B |
After Width: | Height: | Size: 767 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 370 B |
After Width: | Height: | Size: 401 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 187 B |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 467 B |
After Width: | Height: | Size: 546 B |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 223 B |
After Width: | Height: | Size: 1.8 KiB |
|
@ -1,3 +1,6 @@
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">StatusIm</string>
|
<string name="app_name">Status</string>
|
||||||
|
<string name="root_warning">Your phone appears to be ROOTED, by pressing CONTINUE you understand and accept the risks in using this software.</string>
|
||||||
|
<string name="root_okay">Continue</string>
|
||||||
|
<string name="root_cancel">Exit</string>
|
||||||
</resources>
|
</resources>
|
|
@ -21,3 +21,6 @@ project(':ReactNativeAndroidSmsListener').projectDir = new File(rootProject.proj
|
||||||
|
|
||||||
include ':react-native-status'
|
include ':react-native-status'
|
||||||
project(':react-native-status').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-status/android')
|
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')
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
(def root-el (r/as-element [reloader]))
|
(def root-el (r/as-element [reloader]))
|
||||||
|
|
||||||
(figwheel/watch-and-reload
|
(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
|
:heads-up-display false
|
||||||
:jsload-callback #(swap! cnt inc))
|
:jsload-callback #(swap! cnt inc))
|
||||||
|
|
||||||
|
|
|
@ -11,12 +11,14 @@
|
||||||
"react-native": "^0.24.1",
|
"react-native": "^0.24.1",
|
||||||
"react-native-action-button": "^1.1.4",
|
"react-native-action-button": "^1.1.4",
|
||||||
"react-native-android-sms-listener": "^0.1.3",
|
"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-circle-checkbox": "^0.1.3",
|
||||||
"react-native-contacts": "^0.2.4",
|
"react-native-contacts": "^0.2.4",
|
||||||
"react-native-i18n": "0.0.8",
|
"react-native-i18n": "0.0.8",
|
||||||
"react-native-invertible-scroll-view": "^1.0.0",
|
"react-native-invertible-scroll-view": "^1.0.0",
|
||||||
"react-native-linear-gradient": "^1.5.7",
|
"react-native-linear-gradient": "^1.5.7",
|
||||||
"react-native-loading-spinner-overlay": "0.0.8",
|
"react-native-loading-spinner-overlay": "0.0.8",
|
||||||
|
"react-native-qrcode": "^0.2.2",
|
||||||
"react-native-randombytes": "^2.0.0",
|
"react-native-randombytes": "^2.0.0",
|
||||||
"react-native-vector-icons": "^1.3.4",
|
"react-native-vector-icons": "^1.3.4",
|
||||||
"realm": "^0.11.1",
|
"realm": "^0.11.1",
|
||||||
|
|
|
@ -62,6 +62,7 @@ var descriptionStyle = {
|
||||||
};
|
};
|
||||||
|
|
||||||
function startsWith(str1, str2) {
|
function startsWith(str1, str2) {
|
||||||
|
// String.startsWith(...) doesn't work in otto
|
||||||
return str1.lastIndexOf(str2, 0) == 0 && str1 != str2;
|
return str1.lastIndexOf(str2, 0) == 0 && str1 != str2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +135,7 @@ status.response({
|
||||||
name: "confirmation-code",
|
name: "confirmation-code",
|
||||||
color: "#7099e6",
|
color: "#7099e6",
|
||||||
description: "Confirmation code",
|
description: "Confirmation code",
|
||||||
parameters: [{
|
params: [{
|
||||||
name: "code",
|
name: "code",
|
||||||
type: status.types.NUMBER
|
type: status.types.NUMBER
|
||||||
}],
|
}],
|
||||||
|
@ -151,7 +152,7 @@ status.response({
|
||||||
color: "#7099e6",
|
color: "#7099e6",
|
||||||
description: "Keypair password",
|
description: "Keypair password",
|
||||||
icon: "icon_lock_white",
|
icon: "icon_lock_white",
|
||||||
parameters: [{
|
params: [{
|
||||||
name: "password",
|
name: "password",
|
||||||
type: status.types.PASSWORD
|
type: status.types.PASSWORD
|
||||||
}],
|
}],
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
[status-im.components.react :refer [navigator app-registry]]
|
[status-im.components.react :refer [navigator app-registry]]
|
||||||
[status-im.components.main-tabs :refer [main-tabs]]
|
[status-im.components.main-tabs :refer [main-tabs]]
|
||||||
[status-im.contacts.screen :refer [contact-list]]
|
[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.screen :refer [discovery]]
|
||||||
[status-im.discovery.tag :refer [discovery-tag]]
|
[status-im.discovery.tag :refer [discovery-tag]]
|
||||||
[status-im.chat.screen :refer [chat]]
|
[status-im.chat.screen :refer [chat]]
|
||||||
|
@ -44,6 +46,8 @@
|
||||||
:new-group [new-group]
|
:new-group [new-group]
|
||||||
:group-settings [group-settings]
|
:group-settings [group-settings]
|
||||||
:contact-list [main-tabs]
|
:contact-list [main-tabs]
|
||||||
|
:new-contact [new-contact]
|
||||||
|
:qr-scanner [qr-scanner]
|
||||||
:chat [chat]
|
:chat [chat]
|
||||||
:profile [profile]
|
:profile [profile]
|
||||||
:my-profile [my-profile]))))
|
:my-profile [my-profile]))))
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
[status-im.models.commands :as commands]
|
[status-im.models.commands :as commands]
|
||||||
[clojure.string :as str]
|
[clojure.string :as str]
|
||||||
[status-im.components.styles :refer [default-chat-color]]
|
[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.chat.suggestions :as suggestions]
|
||||||
[status-im.protocol.api :as api]
|
[status-im.protocol.api :as api]
|
||||||
[status-im.models.messages :as messages]
|
[status-im.models.messages :as messages]
|
||||||
|
@ -15,10 +16,13 @@
|
||||||
[status-im.utils.handlers :as u]
|
[status-im.utils.handlers :as u]
|
||||||
[status-im.persistence.realm :as r]
|
[status-im.persistence.realm :as r]
|
||||||
[status-im.handlers.server :as server]
|
[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.phone-number :refer [format-phone-number]]
|
||||||
[status-im.utils.datetime :as time]
|
[status-im.utils.datetime :as time]
|
||||||
[status-im.components.jail :as j]
|
[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
|
(register-handler :set-show-actions
|
||||||
(fn [db [_ show-actions]]
|
(fn [db [_ show-actions]]
|
||||||
|
@ -59,10 +63,27 @@
|
||||||
#(dispatch [:suggestions-handler {:command command
|
#(dispatch [:suggestions-handler {:command command
|
||||||
:content content} %]))))
|
: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
|
(register-handler :set-chat-command-content
|
||||||
(after invoke-suggestions-handler!)
|
[(after invoke-suggestions-handler!)
|
||||||
(fn [db [_ content]]
|
(after animate-set-chat-command-content)]
|
||||||
(commands/set-chat-command-content db 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
|
(defn update-input-text
|
||||||
[{:keys [current-chat-id] :as db} text]
|
[{:keys [current-chat-id] :as db} text]
|
||||||
|
@ -94,8 +115,19 @@
|
||||||
(commands/stage-command command-info)
|
(commands/stage-command command-info)
|
||||||
(assoc :staged-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
|
(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]]
|
(fn [db [_ to-msg-id command-key]]
|
||||||
(commands/set-response-chat-command 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))
|
(update-input-text db text))
|
||||||
|
|
||||||
(defn update-command [db [_ text]]
|
(defn update-command [db [_ text]]
|
||||||
(let [[command] (suggestions/check-suggestion db text)]
|
(if-not (commands/get-chat-command db)
|
||||||
(commands/set-chat-command db command)))
|
(let [{:keys [command]} (suggestions/check-suggestion db text)]
|
||||||
|
(if command
|
||||||
|
(commands/set-chat-command db command)
|
||||||
|
db))
|
||||||
|
db))
|
||||||
|
|
||||||
(register-handler :set-chat-input-text
|
(register-handler :set-chat-input-text
|
||||||
((enrich update-command) update-text))
|
((enrich update-command) update-text))
|
||||||
|
@ -130,7 +166,21 @@
|
||||||
(defn add-message-to-db
|
(defn add-message-to-db
|
||||||
[db chat-id message]
|
[db chat-id message]
|
||||||
(let [messages [:chats chat-id :messages]]
|
(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
|
(defn prepare-message
|
||||||
[{:keys [identity current-chat-id] :as db} _]
|
[{:keys [identity current-chat-id] :as db} _]
|
||||||
|
|
|
@ -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)))
|
|
@ -3,6 +3,7 @@
|
||||||
(:require [re-frame.core :refer [subscribe dispatch]]
|
(:require [re-frame.core :refer [subscribe dispatch]]
|
||||||
[clojure.string :as s]
|
[clojure.string :as s]
|
||||||
[status-im.components.react :refer [view
|
[status-im.components.react :refer [view
|
||||||
|
animated-view
|
||||||
text
|
text
|
||||||
image
|
image
|
||||||
icon
|
icon
|
||||||
|
@ -12,13 +13,18 @@
|
||||||
[status-im.components.chat-icon.screen :refer [chat-icon-view-action
|
[status-im.components.chat-icon.screen :refer [chat-icon-view-action
|
||||||
chat-icon-view-menu-item]]
|
chat-icon-view-menu-item]]
|
||||||
[status-im.chat.styles.screen :as st]
|
[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.utils.utils :refer [truncate-str]]
|
||||||
[status-im.components.invertible-scroll-view :refer [invertible-scroll-view]]
|
[status-im.components.invertible-scroll-view :refer [invertible-scroll-view]]
|
||||||
[status-im.components.toolbar :refer [toolbar]]
|
[status-im.components.toolbar :refer [toolbar]]
|
||||||
[status-im.chat.views.message :refer [chat-message]]
|
[status-im.chat.views.message :refer [chat-message]]
|
||||||
|
[status-im.chat.views.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.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]
|
(defn contacts-by-identity [contacts]
|
||||||
|
@ -55,12 +61,12 @@
|
||||||
(for [member ["Geoff" "Justas"]]
|
(for [member ["Geoff" "Justas"]]
|
||||||
^{:key member} [typing member])])
|
^{: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]
|
(fn [row _ idx]
|
||||||
(let [msg (-> row
|
(let [msg (-> row
|
||||||
(add-msg-color contact-by-identity)
|
(add-msg-color contact-by-identity)
|
||||||
(assoc :group-chat group-chat)
|
(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]))))
|
(list-item [chat-message msg]))))
|
||||||
|
|
||||||
(defn on-action-selected [position]
|
(defn on-action-selected [position]
|
||||||
|
@ -216,18 +222,55 @@
|
||||||
[messages [:chat :messages]
|
[messages [:chat :messages]
|
||||||
contacts [:chat :contacts]]
|
contacts [:chat :contacts]]
|
||||||
(let [contacts' (contacts-by-identity contacts)]
|
(let [contacts' (contacts-by-identity contacts)]
|
||||||
[list-view {:renderRow (message-row contacts' group-chat)
|
[list-view {:renderRow (message-row contacts' group-chat (count messages))
|
||||||
:renderScrollComponent #(invertible-scroll-view (js->clj %))
|
:renderScrollComponent #(invertible-scroll-view (js->clj %))
|
||||||
:onEndReached #(dispatch [:load-more-messages])
|
:onEndReached #(dispatch [:load-more-messages])
|
||||||
:enableEmptySections true
|
:enableEmptySections true
|
||||||
:dataSource (to-datasource messages)}]))
|
: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 []
|
(defview chat []
|
||||||
[group-chat [:chat :group-chat]
|
[group-chat [:chat :group-chat]
|
||||||
show-actions-atom [:show-actions]]
|
show-actions-atom [:show-actions]
|
||||||
[view st/chat-view
|
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]
|
[chat-toolbar]
|
||||||
[messages-view group-chat]
|
[messages-container
|
||||||
|
[messages-view group-chat]]
|
||||||
(when group-chat [typing-all])
|
(when group-chat [typing-all])
|
||||||
|
(cond
|
||||||
|
(and command to-msg-id) [response-view]
|
||||||
|
command [content-suggestions-view]
|
||||||
|
:else [suggestions-view])
|
||||||
[chat-message-new]
|
[chat-message-new]
|
||||||
(when show-actions-atom [actions-view])])
|
(when show-actions-atom [actions-view])])
|
||||||
|
|
|
@ -43,6 +43,9 @@
|
||||||
:backgroundColor color-white
|
:backgroundColor color-white
|
||||||
:borderRadius 5})
|
:borderRadius 5})
|
||||||
|
|
||||||
|
(def container
|
||||||
|
{:backgroundColor color-white})
|
||||||
|
|
||||||
(def drag-down-touchable
|
(def drag-down-touchable
|
||||||
{:height 22
|
{:height 22
|
||||||
:alignItems :center
|
:alignItems :center
|
||||||
|
|
|
@ -37,7 +37,8 @@
|
||||||
(def command-input
|
(def command-input
|
||||||
{:flex 1
|
{:flex 1
|
||||||
:marginLeft 8
|
:marginLeft 8
|
||||||
:marginTop 7
|
:marginTop -2
|
||||||
|
:padding 0
|
||||||
:fontSize 14
|
:fontSize 14
|
||||||
:fontFamily font
|
:fontFamily font
|
||||||
:color text1-color})
|
:color text1-color})
|
||||||
|
|
|
@ -131,20 +131,27 @@
|
||||||
(def command-request-message-view
|
(def command-request-message-view
|
||||||
{:borderRadius 14
|
{:borderRadius 14
|
||||||
:padding 12
|
:padding 12
|
||||||
|
:paddingRight 28
|
||||||
:backgroundColor color-white})
|
:backgroundColor color-white})
|
||||||
|
|
||||||
(def command-request-from-text
|
(def command-request-from-text
|
||||||
(merge style-sub-text {:marginBottom 2}))
|
(merge style-sub-text {:marginBottom 2}))
|
||||||
|
|
||||||
(defn command-request-image-view
|
(def command-request-image-touchable
|
||||||
[command]
|
|
||||||
{:position :absolute
|
{:position :absolute
|
||||||
:top 12
|
:top 4
|
||||||
:right 0
|
:right -8
|
||||||
:width 32
|
:alignItems :center
|
||||||
|
:justifyContent :center
|
||||||
|
:width 48
|
||||||
|
:height 48})
|
||||||
|
|
||||||
|
(defn command-request-image-view [command scale]
|
||||||
|
{:width 32
|
||||||
:height 32
|
:height 32
|
||||||
:borderRadius 50
|
:borderRadius 50
|
||||||
:backgroundColor (:color command)})
|
:backgroundColor (:color command)
|
||||||
|
:transform [{:scale scale}]})
|
||||||
|
|
||||||
(defn command-image-view
|
(defn command-image-view
|
||||||
[command]
|
[command]
|
||||||
|
@ -317,6 +324,9 @@
|
||||||
(def message-date-text
|
(def message-date-text
|
||||||
(assoc style-sub-text :textAlign :center))
|
(assoc style-sub-text :textAlign :center))
|
||||||
|
|
||||||
|
(defn message-container [height]
|
||||||
|
{:height height})
|
||||||
|
|
||||||
(def new-message-container
|
(def new-message-container
|
||||||
{:backgroundColor color-white
|
{:backgroundColor color-white
|
||||||
:elevation 4})
|
:elevation 4})
|
||||||
|
|
|
@ -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})
|
|
@ -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})
|
|
|
@ -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})
|
|
@ -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})
|
|
@ -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})
|
|
@ -14,6 +14,10 @@
|
||||||
{:flex 1
|
{:flex 1
|
||||||
:backgroundColor chat-background})
|
:backgroundColor chat-background})
|
||||||
|
|
||||||
|
(defn messages-container [bottom]
|
||||||
|
{:flex 1
|
||||||
|
:bottom bottom})
|
||||||
|
|
||||||
(def toolbar-view
|
(def toolbar-view
|
||||||
{:flexDirection :row
|
{:flexDirection :row
|
||||||
:height 56
|
:height 56
|
||||||
|
|
|
@ -59,6 +59,9 @@
|
||||||
:backgroundColor color-white
|
:backgroundColor color-white
|
||||||
:borderRadius 5})
|
:borderRadius 5})
|
||||||
|
|
||||||
|
(def container
|
||||||
|
{:backgroundColor color-white})
|
||||||
|
|
||||||
(def drag-down-touchable
|
(def drag-down-touchable
|
||||||
{:height 22
|
{:height 22
|
||||||
:alignItems :center
|
:alignItems :center
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
(ns status-im.chat.subs
|
(ns status-im.chat.subs
|
||||||
(:require-macros [reagent.ratom :refer [reaction]])
|
(: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]
|
[status-im.db :as db]
|
||||||
;todo handlers in subs?...
|
;todo handlers in subs?...
|
||||||
[status-im.chat.suggestions :refer
|
[status-im.chat.suggestions :refer
|
||||||
[get-suggestions typing-command? get-content-suggestions]]
|
[get-suggestions typing-command?]]
|
||||||
[status-im.models.commands :as commands]
|
[status-im.models.commands :as commands]
|
||||||
|
[status-im.constants :refer [response-suggesstion-resize-duration]]
|
||||||
[status-im.handlers.content-suggestions :refer [get-content-suggestions]]))
|
[status-im.handlers.content-suggestions :refer [get-content-suggestions]]))
|
||||||
|
|
||||||
(register-sub :chat-properties
|
(register-sub :chat-properties
|
||||||
|
@ -82,6 +83,10 @@
|
||||||
(fn [db _]
|
(fn [db _]
|
||||||
(reaction (commands/get-chat-command-content @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
|
(register-sub :chat-command-request
|
||||||
(fn [db _]
|
(fn [db _]
|
||||||
(reaction (commands/get-chat-command-request @db))))
|
(reaction (commands/get-chat-command-request @db))))
|
||||||
|
@ -103,4 +108,4 @@
|
||||||
(fn [db _]
|
(fn [db _]
|
||||||
(let [command (reaction (commands/get-chat-command @db))
|
(let [command (reaction (commands/get-chat-command @db))
|
||||||
text (reaction (commands/get-chat-command-content @db))]
|
text (reaction (commands/get-chat-command-content @db))]
|
||||||
(reaction (get-content-suggestions @db @command @text)))))
|
(reaction (get-content-suggestions @command @text)))))
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
(ns status-im.chat.views.command
|
(ns status-im.chat.views.command
|
||||||
|
(:require-macros [status-im.utils.views :refer [defview]])
|
||||||
(:require [re-frame.core :refer [subscribe dispatch]]
|
(:require [re-frame.core :refer [subscribe dispatch]]
|
||||||
[status-im.components.react :refer [view
|
[status-im.components.react :refer [view
|
||||||
icon
|
icon
|
||||||
text
|
text
|
||||||
text-input
|
text-input
|
||||||
touchable-highlight]]
|
touchable-highlight]]
|
||||||
[status-im.chat.views.content-suggestions :refer
|
|
||||||
[content-suggestions-view]]
|
|
||||||
[status-im.chat.styles.input :as st]))
|
[status-im.chat.styles.input :as st]))
|
||||||
|
|
||||||
(defn cancel-command-input []
|
(defn cancel-command-input []
|
||||||
(dispatch [:cancel-command]))
|
(dispatch [:start-cancel-command]))
|
||||||
|
|
||||||
(defn set-input-message [message]
|
(defn set-input-message [message]
|
||||||
(dispatch [:set-chat-command-content message]))
|
(dispatch [:set-chat-command-content message]))
|
||||||
|
@ -24,7 +23,8 @@
|
||||||
(validator message)
|
(validator message)
|
||||||
(pos? (count 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])]
|
(let [message-atom (subscribe [:get-chat-command-content])]
|
||||||
(fn [command input-options & {:keys [validator]}]
|
(fn [command input-options & {:keys [validator]}]
|
||||||
(let [message @message-atom]
|
(let [message @message-atom]
|
||||||
|
@ -49,3 +49,19 @@
|
||||||
[touchable-highlight {:on-press cancel-command-input}
|
[touchable-highlight {:on-press cancel-command-input}
|
||||||
[view st/cancel-container
|
[view st/cancel-container
|
||||||
[icon :close-gray st/cancel-icon]]])]]))))
|
[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]]])
|
||||||
|
|
||||||
|
|
|
@ -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}])
|
|
|
@ -26,7 +26,7 @@
|
||||||
(defview content-suggestions-view []
|
(defview content-suggestions-view []
|
||||||
[suggestions [:get :current-suggestion]]
|
[suggestions [:get :current-suggestion]]
|
||||||
(when (seq suggestions)
|
(when (seq suggestions)
|
||||||
[view
|
[view st/container
|
||||||
[touchable-highlight {:style st/drag-down-touchable
|
[touchable-highlight {:style st/drag-down-touchable
|
||||||
;; TODO hide suggestions?
|
;; TODO hide suggestions?
|
||||||
:onPress (fn [])}
|
:onPress (fn [])}
|
||||||
|
|
|
@ -2,23 +2,28 @@
|
||||||
(:require-macros [status-im.utils.views :refer [defview]])
|
(:require-macros [status-im.utils.views :refer [defview]])
|
||||||
(:require [clojure.string :as s]
|
(:require [clojure.string :as s]
|
||||||
[re-frame.core :refer [subscribe dispatch]]
|
[re-frame.core :refer [subscribe dispatch]]
|
||||||
|
[reagent.core :as r]
|
||||||
[status-im.components.react :refer [view
|
[status-im.components.react :refer [view
|
||||||
text
|
text
|
||||||
image
|
image
|
||||||
|
animated-view
|
||||||
touchable-highlight]]
|
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.chat.styles.message :as st]
|
||||||
[status-im.models.commands :refer [parse-command-msg-content
|
[status-im.models.commands :refer [parse-command-msg-content
|
||||||
parse-command-request]]
|
parse-command-request]]
|
||||||
[status-im.resources :as res]
|
[status-im.resources :as res]
|
||||||
|
[status-im.utils.datetime :as time]
|
||||||
[status-im.constants :refer [text-content-type
|
[status-im.constants :refer [text-content-type
|
||||||
content-type-status
|
content-type-status
|
||||||
content-type-command
|
content-type-command
|
||||||
content-type-command-request]]))
|
content-type-command-request]]))
|
||||||
|
|
||||||
(defn message-date [{:keys [date]}]
|
(defn message-date [timestamp]
|
||||||
[view {}
|
[view {}
|
||||||
[view st/message-date-container
|
[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]}]
|
(defn contact-photo [{:keys [photo-path]}]
|
||||||
[view st/contact-photo-container
|
[view st/contact-photo-container
|
||||||
|
@ -79,7 +84,8 @@
|
||||||
(->> (when command (name command))
|
(->> (when command (name command))
|
||||||
(str "request-")))
|
(str "request-")))
|
||||||
|
|
||||||
(defview message-content-command-request
|
;; todo remove (merging leftover)
|
||||||
|
#_(defview message-content-command-request
|
||||||
[{:keys [msg-id content from incoming-group]}]
|
[{:keys [msg-id content from incoming-group]}]
|
||||||
[commands [:get-responses]]
|
[commands [:get-responses]]
|
||||||
(let [{:keys [command content]} (parse-command-request commands content)]
|
(let [{:keys [command content]} (parse-command-request commands content)]
|
||||||
|
@ -183,12 +189,50 @@
|
||||||
(when (and outgoing delivery-status)
|
(when (and outgoing delivery-status)
|
||||||
[message-delivery-status {:delivery-status 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
|
(defn chat-message
|
||||||
[{:keys [outgoing delivery-status date new-day group-chat]
|
[{:keys [outgoing delivery-status timestamp new-day group-chat]
|
||||||
:as message}]
|
:as message}]
|
||||||
[view {}
|
[message-container message
|
||||||
(when new-day [message-date {:date date}])
|
;; TODO there is no new-day info in message
|
||||||
[view {}
|
(when new-day
|
||||||
|
[message-date timestamp])
|
||||||
|
[view
|
||||||
(let [incoming-group (and group-chat (not outgoing))]
|
(let [incoming-group (and group-chat (not outgoing))]
|
||||||
[message-content
|
[message-content
|
||||||
(if incoming-group
|
(if incoming-group
|
||||||
|
|
|
@ -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])))]]))
|
|
@ -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}])
|
|
|
@ -1,14 +1,11 @@
|
||||||
(ns status-im.chat.views.new-message
|
(ns status-im.chat.views.new-message
|
||||||
|
(:require-macros [status-im.utils.views :refer [defview]])
|
||||||
(:require
|
(:require
|
||||||
[re-frame.core :refer [subscribe]]
|
[re-frame.core :refer [subscribe]]
|
||||||
[status-im.components.react :refer [view]]
|
[status-im.components.react :refer [view]]
|
||||||
[status-im.chat.views.plain-input :refer [plain-message-input-view]]
|
[status-im.chat.views.message-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.staged-command :refer [simple-command-staged-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]))
|
[status-im.chat.styles.message :as st]))
|
||||||
|
|
||||||
(defn staged-command-view [stage-command]
|
(defn staged-command-view [stage-command]
|
||||||
|
@ -19,27 +16,23 @@
|
||||||
(for [command staged-commands]
|
(for [command staged-commands]
|
||||||
^{:key command} [staged-command-view command])])
|
^{:key command} [staged-command-view command])])
|
||||||
|
|
||||||
(defn default-command-input-view [command]
|
(defn show-input [command]
|
||||||
[simple-command-input-view command {}])
|
[plain-message-input-view
|
||||||
|
(when command
|
||||||
(defn special-input-view [command]
|
|
||||||
(case (:command command)
|
(case (:command command)
|
||||||
:phone [phone-input-view command]
|
:phone {:input-options {:keyboardType :phone-pad}
|
||||||
:keypair-password [password-input-view command]
|
:validator valid-mobile-number?}
|
||||||
:confirmation-code [confirmation-code-input-view command]
|
:keypair {:input-options {:secureTextEntry true}}
|
||||||
:money [money-input-view command]
|
:confirmation-code {:input-options {:keyboardType :numeric}}
|
||||||
:request [money-input-view command]
|
:money {:input-options {:keyboardType :numeric}}
|
||||||
[default-command-input-view command]))
|
:request {:input-options {:keyboardType :numeric}}
|
||||||
|
;; todo maybe nil is finr for now :)
|
||||||
|
nil #_(throw (js/Error. "Uknown command type"))))])
|
||||||
|
|
||||||
(defn chat-message-new []
|
(defview chat-message-new []
|
||||||
(let [command-atom (subscribe [:get-chat-command])
|
[command [:get-chat-command]
|
||||||
staged-commands-atom (subscribe [:get-chat-staged-commands])]
|
staged-commands [:get-chat-staged-commands]]
|
||||||
(fn []
|
|
||||||
(let [command @command-atom
|
|
||||||
staged-commands @staged-commands-atom]
|
|
||||||
[view st/new-message-container
|
[view st/new-message-container
|
||||||
(when (and staged-commands (pos? (count staged-commands)))
|
(when (and staged-commands (pos? (count staged-commands)))
|
||||||
[staged-commands-view staged-commands])
|
[staged-commands-view staged-commands])
|
||||||
(if command
|
[show-input command]])
|
||||||
[special-input-view command]
|
|
||||||
[plain-message-input-view])]))))
|
|
||||||
|
|
|
@ -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}])
|
|
|
@ -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?])
|
|
|
@ -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]]])]]))))
|
|
|
@ -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]]])})))
|
|
@ -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)]])]))))
|
|
@ -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]])
|
|
@ -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}]]))
|
|
@ -30,10 +30,11 @@
|
||||||
(defn render-row [row _ _]
|
(defn render-row [row _ _]
|
||||||
(list-item [suggestion-list-item row]))
|
(list-item [suggestion-list-item row]))
|
||||||
|
|
||||||
|
|
||||||
(defview suggestions-view []
|
(defview suggestions-view []
|
||||||
[suggestions [:get-suggestions]]
|
[suggestions [:get-suggestions]]
|
||||||
(when (seq suggestions)
|
(when (seq suggestions)
|
||||||
[view
|
[view st/container
|
||||||
[touchable-highlight {:style st/drag-down-touchable
|
[touchable-highlight {:style st/drag-down-touchable
|
||||||
:onPress (fn []
|
:onPress (fn []
|
||||||
;; TODO hide suggestions?
|
;; TODO hide suggestions?
|
||||||
|
@ -43,4 +44,5 @@
|
||||||
[view (st/suggestions-container (count suggestions))
|
[view (st/suggestions-container (count suggestions))
|
||||||
[list-view {:dataSource (to-datasource suggestions)
|
[list-view {:dataSource (to-datasource suggestions)
|
||||||
:enableEmptySections true
|
:enableEmptySections true
|
||||||
|
:keyboardShouldPersistTaps true
|
||||||
:renderRow render-row}]]]))
|
:renderRow render-row}]]]))
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
[drawer-view
|
[drawer-view
|
||||||
[view st/chats-container
|
[view st/chats-container
|
||||||
[chats-list-toolbar]
|
[chats-list-toolbar]
|
||||||
[list-view {:dataSource (to-datasource (vals @chats))
|
[list-view {:dataSource (to-datasource @chats)
|
||||||
:renderRow (fn [row _ _]
|
:renderRow (fn [row _ _]
|
||||||
(list-item [chat-list-item row]))
|
(list-item [chat-list-item row]))
|
||||||
:style st/list-container}]
|
:style st/list-container}]
|
||||||
|
|
|
@ -8,10 +8,11 @@
|
||||||
[status-im.chats-list.views.inner-item :refer
|
[status-im.chats-list.views.inner-item :refer
|
||||||
[chat-list-item-inner-view]]))
|
[chat-list-item-inner-view]]))
|
||||||
|
|
||||||
(defn chat-list-item [{:keys [chat-id] :as chat}]
|
(defn chat-list-item [[chat-id chat]]
|
||||||
[touchable-highlight
|
[touchable-highlight
|
||||||
{:on-press #(dispatch [:navigate-to :chat chat-id])}
|
{:on-press #(dispatch [:navigate-to :chat chat-id])}
|
||||||
[view [chat-list-item-inner-view (merge chat
|
[view [chat-list-item-inner-view (merge chat
|
||||||
;; TODO stub data
|
;; TODO stub data
|
||||||
{:new-messages-count 3
|
{:chat-id chat-id
|
||||||
|
:new-messages-count 3
|
||||||
:online true})]]])
|
:online true})]]])
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
[status-im.utils.datetime :as time]))
|
[status-im.utils.datetime :as time]))
|
||||||
|
|
||||||
(defn chat-list-item-inner-view
|
(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}]
|
online group-chat contacts] :as chat}]
|
||||||
(let [last-message (first (:messages chat))]
|
(let [last-message (first (:messages chat))]
|
||||||
[view st/chat-container
|
[view st/chat-container
|
||||||
|
|
|
@ -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})))
|
|
@ -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))))
|
|
@ -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})))
|
|
@ -1,11 +1,16 @@
|
||||||
(ns status-im.components.main-tabs
|
(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]]
|
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
|
||||||
|
[reagent.core :as r]
|
||||||
[status-im.components.react :refer [view
|
[status-im.components.react :refer [view
|
||||||
|
animated-view
|
||||||
text-input
|
text-input
|
||||||
text
|
text
|
||||||
image
|
image
|
||||||
touchable-highlight]]
|
touchable-highlight
|
||||||
|
get-dimensions]]
|
||||||
|
[status-im.components.animation :as anim]
|
||||||
[status-im.chats-list.screen :refer [chats-list]]
|
[status-im.chats-list.screen :refer [chats-list]]
|
||||||
[status-im.discovery.screen :refer [discovery]]
|
[status-im.discovery.screen :refer [discovery]]
|
||||||
[status-im.contacts.screen :refer [contact-list]]
|
[status-im.contacts.screen :refer [contact-list]]
|
||||||
|
@ -14,33 +19,91 @@
|
||||||
[status-im.components.styles :as common-st]
|
[status-im.components.styles :as common-st]
|
||||||
[status-im.i18n :refer [label]]))
|
[status-im.i18n :refer [label]]))
|
||||||
|
|
||||||
|
(def window-width (:width (get-dimensions "window")))
|
||||||
|
|
||||||
(def tab-list
|
(def tab-list
|
||||||
[{:view-id :chat-list
|
[{:view-id :chat-list
|
||||||
:title (label :t/chats)
|
:title (label :t/chats)
|
||||||
:screen chats-list
|
:screen chats-list
|
||||||
:icon :icon_tab_chats}
|
:icon :icon_tab_chats
|
||||||
|
:index 0}
|
||||||
{:view-id :discovery
|
{:view-id :discovery
|
||||||
:title (label :t/discovery)
|
:title (label :t/discovery)
|
||||||
:screen discovery
|
:screen discovery
|
||||||
:icon :icon_tab_discovery}
|
:icon :icon_tab_discovery
|
||||||
|
:index 1}
|
||||||
{:view-id :contact-list
|
{:view-id :contact-list
|
||||||
:title (label :t/contacts)
|
:title (label :t/contacts)
|
||||||
:screen contact-list
|
:screen contact-list
|
||||||
:icon :icon_tab_contacts}])
|
:icon :icon_tab_contacts
|
||||||
|
:index 2}])
|
||||||
|
|
||||||
(defn show-view? [current-view view-id]
|
(defn animation-logic [{:keys [offsets val tab-id to-tab-id]}]
|
||||||
(let [key-map {:key view-id}]
|
(fn [_]
|
||||||
(if (= current-view view-id)
|
(when-let [offsets @offsets]
|
||||||
(merge st/show-tab key-map)
|
(let [from-value (:from offsets)
|
||||||
(merge st/hide-tab key-map))))
|
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]}]
|
(defn get-tab-index-by-id [id]
|
||||||
[view (show-view? current-view view-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]])
|
[screen]])
|
||||||
|
|
||||||
(defview main-tabs []
|
(defview main-tabs []
|
||||||
[view-id [:get :view-id]]
|
[view-id [:get :view-id]
|
||||||
[view common-st/flex
|
tab-animation? [:get :prev-tab-view-id]]
|
||||||
(doall (map #(tab-view view-id %1) tab-list))
|
[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
|
[tabs {:selected-view-id view-id
|
||||||
:tab-list tab-list}]])
|
:tab-list tab-list}]])
|
||||||
|
|
|
@ -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))))
|
|
@ -46,6 +46,14 @@
|
||||||
(when-let [picker (get-react-property "Picker")]
|
(when-let [picker (get-react-property "Picker")]
|
||||||
(adapt-class (.-Item 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
|
(defn icon
|
||||||
([n] (icon n {}))
|
([n] (icon n {}))
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
(def text1-color color-black)
|
(def text1-color color-black)
|
||||||
(def text2-color color-gray)
|
(def text2-color color-gray)
|
||||||
(def text3-color color-blue)
|
(def text3-color color-blue)
|
||||||
|
(def text4-color color-white)
|
||||||
(def online-color color-blue)
|
(def online-color color-blue)
|
||||||
(def new-messages-count-color color-blue-transparent)
|
(def new-messages-count-color color-blue-transparent)
|
||||||
(def chat-background color-light-gray)
|
(def chat-background color-light-gray)
|
||||||
|
@ -28,5 +29,73 @@
|
||||||
(def toolbar-background2 color-light-gray)
|
(def toolbar-background2 color-light-gray)
|
||||||
(def default-chat-color color-purple)
|
(def default-chat-color color-purple)
|
||||||
|
|
||||||
|
(def toolbar-height 56)
|
||||||
|
|
||||||
(def flex
|
(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})
|
|
@ -54,17 +54,11 @@
|
||||||
:justifyContent :center
|
:justifyContent :center
|
||||||
:alignItems :center})
|
:alignItems :center})
|
||||||
|
|
||||||
(def show-tab
|
(defn tab-view-container [offset-x]
|
||||||
{:flex 1
|
{:flex 1
|
||||||
:pointerEvents :auto
|
|
||||||
:position :absolute
|
:position :absolute
|
||||||
:top 0
|
:top 0
|
||||||
:left 0
|
:left 0
|
||||||
:right 0
|
:right 0
|
||||||
:bottom tab-height})
|
:bottom tab-height
|
||||||
|
:transform [{:translateX offset-x}]})
|
||||||
(def hide-tab
|
|
||||||
{:opacity 0
|
|
||||||
:pointerEvents :none
|
|
||||||
:overflow :hidden})
|
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,8 @@
|
||||||
|
|
||||||
(defview tab [{:keys [view-id title icon selected-view-id]}]
|
(defview tab [{:keys [view-id title icon selected-view-id]}]
|
||||||
[touchable-highlight {:style st/tab
|
[touchable-highlight {:style st/tab
|
||||||
:onPress #(dispatch [:navigate-to
|
:disabled (= view-id selected-view-id)
|
||||||
view-id])}
|
:onPress #(dispatch [:navigate-to-tab view-id])}
|
||||||
[view {:style st/tab-container}
|
[view {:style st/tab-container}
|
||||||
[image {:source {:uri icon}
|
[image {:source {:uri icon}
|
||||||
:style st/tab-icon}]
|
:style st/tab-icon}]
|
||||||
|
|
|
@ -12,13 +12,17 @@
|
||||||
color-purple
|
color-purple
|
||||||
text1-color
|
text1-color
|
||||||
text2-color
|
text2-color
|
||||||
toolbar-background1]]))
|
toolbar-background1
|
||||||
|
toolbar-title-container
|
||||||
|
toolbar-title-text
|
||||||
|
icon-back
|
||||||
|
toolbar-height]]))
|
||||||
|
|
||||||
(defn toolbar [{:keys [title nav-action hide-nav? action custom-action
|
(defn toolbar [{:keys [title nav-action hide-nav? action custom-action
|
||||||
background-color custom-content style]}]
|
background-color custom-content style]}]
|
||||||
(let [style (merge {:flexDirection :row
|
(let [style (merge {:flexDirection :row
|
||||||
:backgroundColor (or background-color toolbar-background1)
|
:backgroundColor (or background-color toolbar-background1)
|
||||||
:height 56
|
:height toolbar-height
|
||||||
:elevation 2} style)]
|
:elevation 2} style)]
|
||||||
[view {:style style}
|
[view {:style style}
|
||||||
(when (not hide-nav?)
|
(when (not hide-nav?)
|
||||||
|
@ -31,20 +35,14 @@
|
||||||
[image (:image nav-action)]]]
|
[image (:image nav-action)]]]
|
||||||
[touchable-highlight {:on-press #(dispatch [:navigate-back])}
|
[touchable-highlight {:on-press #(dispatch [:navigate-back])}
|
||||||
[view {:width 56
|
[view {:width 56
|
||||||
:height 56}
|
:height 56
|
||||||
[image {:source {:uri :icon_back}
|
|
||||||
:style {:marginTop 21
|
|
||||||
:marginLeft 23
|
|
||||||
:width 8
|
|
||||||
:height 14}}]]]))
|
|
||||||
(or custom-content
|
|
||||||
[view {:style {:flex 1
|
|
||||||
:alignItems :center
|
:alignItems :center
|
||||||
:justifyContent :center}}
|
:justifyContent :center}
|
||||||
[text {:style {:marginTop -2.5
|
[image {:source {:uri :icon_back}
|
||||||
:color text1-color
|
:style icon-back}]]]))
|
||||||
:fontSize 16
|
(or custom-content
|
||||||
:fontFamily font}}
|
[view {:style toolbar-title-container}
|
||||||
|
[text {:style toolbar-title-text}
|
||||||
title]])
|
title]])
|
||||||
custom-action
|
custom-action
|
||||||
(when action
|
(when action
|
||||||
|
|
|
@ -10,3 +10,8 @@
|
||||||
(def content-type-command "command")
|
(def content-type-command "command")
|
||||||
(def content-type-command-request "command-request")
|
(def content-type-command-request "command-request")
|
||||||
(def content-type-status "status")
|
(def content-type-status "status")
|
||||||
|
|
||||||
|
(def max-chat-name-length 20)
|
||||||
|
|
||||||
|
(def response-input-hiding-duration 300)
|
||||||
|
(def response-suggesstion-resize-duration 100)
|
||||||
|
|
|
@ -103,3 +103,21 @@
|
||||||
(register-handler :add-contacts
|
(register-handler :add-contacts
|
||||||
(after save-contacts!)
|
(after save-contacts!)
|
||||||
add-new-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)
|
||||||
|
|
|
@ -6,9 +6,18 @@
|
||||||
touchable-highlight
|
touchable-highlight
|
||||||
list-view
|
list-view
|
||||||
list-item]]
|
list-item]]
|
||||||
|
[status-im.components.action-button :refer [action-button
|
||||||
|
action-button-item]]
|
||||||
[status-im.contacts.views.contact :refer [contact-view]]
|
[status-im.contacts.views.contact :refer [contact-view]]
|
||||||
[status-im.components.styles :refer [toolbar-background2]]
|
[status-im.components.styles :refer [toolbar-background2]]
|
||||||
[status-im.components.toolbar :refer [toolbar]]
|
[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.contacts.styles :as st]
|
||||||
[status-im.utils.listview :as lw]
|
[status-im.utils.listview :as lw]
|
||||||
[status-im.i18n :refer [label]]))
|
[status-im.i18n :refer [label]]))
|
||||||
|
@ -17,14 +26,18 @@
|
||||||
(list-item [contact-view row]))
|
(list-item [contact-view row]))
|
||||||
|
|
||||||
(defn contact-list-toolbar []
|
(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
|
:background-color toolbar-background2
|
||||||
:action {:image {:source {:uri :icon_search}
|
:action {:image {:source {:uri :icon_search}
|
||||||
:style st/search-icon}
|
:style icon-search}
|
||||||
:handler (fn [])}}])
|
:handler (fn [])}}])
|
||||||
|
|
||||||
(defview contact-list []
|
(defview contact-list []
|
||||||
[contacts [:get-contacts]]
|
[contacts [:get-contacts]]
|
||||||
|
[drawer-view
|
||||||
[view st/contacts-list-container
|
[view st/contacts-list-container
|
||||||
[contact-list-toolbar]
|
[contact-list-toolbar]
|
||||||
;; todo what if there is no contacts, should we show some information
|
;; todo what if there is no contacts, should we show some information
|
||||||
|
@ -33,4 +46,14 @@
|
||||||
[list-view {:dataSource (lw/to-datasource contacts)
|
[list-view {:dataSource (lw/to-datasource contacts)
|
||||||
:enableEmptySections true
|
:enableEmptySections true
|
||||||
:renderRow render-row
|
:renderRow render-row
|
||||||
:style st/contacts-list}])])
|
: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}]]
|
||||||
|
]]])
|
||||||
|
|