From 5940af50ade48d2701bfb2110fdc2975a1e1465b Mon Sep 17 00:00:00 2001 From: Roman Volosovskyi Date: Thu, 30 Jun 2016 19:00:44 +0300 Subject: [PATCH] send-transaction in commands.js Former-commit-id: 871f8036516fd0fab72c2f17c7c0218c31ca022e --- android/app/build.gradle | 2 +- .../java/com/statusim/MainApplication.java | 2 +- .../src/main/java/com/statusim/RootUtil.java | 3 +- .../com/statusim/geth/module/GethModule.java | 69 +++--- .../geth/service/ConnectorHandler.java | 4 +- .../statusim/geth/service/GethConnector.java | 13 +- .../statusim/geth/service/GethMessages.java | 16 +- .../statusim/geth/service/GethService.java | 87 ++++--- .../geth/service/ServiceConnector.java | 39 ++- .../icon_ok_disabled_inversed.png | Bin 0 -> 443 bytes .../icon_ok_disabled_inversed.png | Bin 0 -> 307 bytes .../icon_ok_disabled_inversed.png | Bin 0 -> 607 bytes .../icon_ok_disabled_inversed.png | Bin 0 -> 886 bytes .../icon_ok_disabled_inversed.png | Bin 0 -> 1167 bytes env/dev/env/android/main.cljs | 4 +- project.clj | 11 +- resources/commands.js | 57 +++++ resources/status.js | 5 +- src/status_im/accounts/handlers.cljs | 29 +-- src/status_im/android/core.cljs | 2 + src/status_im/chat/handlers.cljs | 234 +++--------------- src/status_im/chat/handlers/animation.cljs | 2 +- src/status_im/chat/handlers/commands.cljs | 34 ++- .../chat/handlers/receive_message.cljs | 41 +++ src/status_im/chat/handlers/requests.cljs | 4 +- src/status_im/chat/handlers/send_message.cljs | 201 +++++++++++++++ src/status_im/chat/screen.cljs | 16 +- src/status_im/chat/sign_up.cljs | 41 +-- .../chat/styles/command_validation.cljs | 9 +- src/status_im/chat/subs.cljs | 6 + src/status_im/chat/suggestions.cljs | 16 +- src/status_im/chat/utils.cljs | 27 ++ src/status_im/chat/views/message.cljs | 11 +- src/status_im/chat/views/suggestions.cljs | 2 + .../chats_list/views/inner_item.cljs | 3 +- src/status_im/commands/handlers/jail.cljs | 72 +++--- src/status_im/commands/handlers/loading.cljs | 19 +- .../components/carousel/carousel.cljs | 8 +- src/status_im/components/carousel/styles.cljs | 2 +- .../components/chat_icon/screen.cljs | 6 +- src/status_im/components/geth.cljs | 19 +- src/status_im/components/styles.cljs | 4 + src/status_im/components/text_field/view.cljs | 7 +- src/status_im/contacts/handlers.cljs | 5 +- src/status_im/contacts/subs.cljs | 7 +- src/status_im/db.cljs | 5 +- src/status_im/handlers.cljs | 5 +- src/status_im/models/chats.cljs | 5 - src/status_im/models/commands.cljs | 28 ++- src/status_im/models/messages.cljs | 12 +- src/status_im/navigation/handlers.cljs | 6 +- src/status_im/protocol/handlers.cljs | 5 + src/status_im/protocol/protocol_handler.cljs | 3 +- ...{import-button.cljs => import_button.cljs} | 0 .../{scan-button.cljs => scan_button.cljs} | 0 src/status_im/subs.cljs | 3 +- src/status_im/transactions/handlers.cljs | 119 +++++++++ src/status_im/transactions/screen.cljs | 62 +++++ src/status_im/transactions/styles.cljs | 107 ++++++++ src/status_im/transactions/subs.cljs | 28 +++ .../transactions/views/transaction_page.cljs | 49 ++++ src/status_im/translations/en.cljs | 15 +- src/status_im/utils/datetime.cljs | 18 +- src/status_im/utils/handlers.cljs | 16 +- 64 files changed, 1132 insertions(+), 493 deletions(-) create mode 100644 android/app/src/main/res/drawable-hdpi/icon_ok_disabled_inversed.png create mode 100644 android/app/src/main/res/drawable-mdpi/icon_ok_disabled_inversed.png create mode 100644 android/app/src/main/res/drawable-xhdpi/icon_ok_disabled_inversed.png create mode 100644 android/app/src/main/res/drawable-xxhdpi/icon_ok_disabled_inversed.png create mode 100644 android/app/src/main/res/drawable-xxxhdpi/icon_ok_disabled_inversed.png create mode 100644 src/status_im/chat/handlers/receive_message.cljs create mode 100644 src/status_im/chat/handlers/send_message.cljs create mode 100644 src/status_im/chat/utils.cljs rename src/status_im/qr_scanner/views/{import-button.cljs => import_button.cljs} (100%) rename src/status_im/qr_scanner/views/{scan-button.cljs => scan_button.cljs} (100%) create mode 100644 src/status_im/transactions/handlers.cljs create mode 100644 src/status_im/transactions/screen.cljs create mode 100644 src/status_im/transactions/styles.cljs create mode 100644 src/status_im/transactions/subs.cljs create mode 100644 src/status_im/transactions/views/transaction_page.cljs diff --git a/android/app/build.gradle b/android/app/build.gradle index c5ec1d7c09..5cc175be39 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -138,7 +138,7 @@ dependencies { compile project(':react-native-fs') compile project(':react-native-image-crop-picker') //compile(name:'statusgo-android-16', ext:'aar') - compile(group: 'status-im', name: 'status-go', version: '0.1.0-201607011545-da53ec', ext: 'aar') + compile(group: 'status-im', name: 'status-go', version: 'unlock', ext: 'aar') compile fileTree(dir: "node_modules/realm/android/libs", include: ["*.jar"]) } diff --git a/android/app/src/main/java/com/statusim/MainApplication.java b/android/app/src/main/java/com/statusim/MainApplication.java index 69e184d6a1..1d5cb26cae 100644 --- a/android/app/src/main/java/com/statusim/MainApplication.java +++ b/android/app/src/main/java/com/statusim/MainApplication.java @@ -36,7 +36,7 @@ public class MainApplication extends Application implements ReactApplication { @Override protected List getPackages() { - return Arrays.asList( + return Arrays.asList( new MainReactPackage(), new JailPackage(), new RealmReactPackage(), diff --git a/android/app/src/main/java/com/statusim/RootUtil.java b/android/app/src/main/java/com/statusim/RootUtil.java index c5b9e88df0..664e9805a6 100644 --- a/android/app/src/main/java/com/statusim/RootUtil.java +++ b/android/app/src/main/java/com/statusim/RootUtil.java @@ -30,8 +30,7 @@ public class RootUtil { 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; + return in.readLine() != null; } catch (Throwable t) { return false; } finally { diff --git a/android/app/src/main/java/com/statusim/geth/module/GethModule.java b/android/app/src/main/java/com/statusim/geth/module/GethModule.java index 113e9ecf16..2263f829cd 100644 --- a/android/app/src/main/java/com/statusim/geth/module/GethModule.java +++ b/android/app/src/main/java/com/statusim/geth/module/GethModule.java @@ -3,6 +3,7 @@ package com.statusim.geth.module; import android.app.Activity; import android.os.Bundle; import android.os.Message; +import android.os.RemoteException; import com.facebook.react.bridge.*; import com.statusim.geth.service.ConnectorHandler; import com.statusim.geth.service.GethConnector; @@ -13,20 +14,19 @@ import android.util.Log; import java.util.HashMap; import java.util.UUID; -public class GethModule extends ReactContextBaseJavaModule implements LifecycleEventListener, ConnectorHandler { +class GethModule extends ReactContextBaseJavaModule implements LifecycleEventListener, ConnectorHandler { private static final String TAG = "GethModule"; - protected GethConnector geth = null; - protected String handlerIdentifier = createIdentifier(); + private GethConnector geth = null; - protected HashMap startNodeCallbacks = new HashMap<>(); - protected HashMap createAccountCallbacks = new HashMap<>(); - protected HashMap addAccountCallbacks = new HashMap<>(); - protected HashMap unlockAccountCallbacks = new HashMap<>(); + private HashMap startNodeCallbacks = new HashMap<>(); + private HashMap createAccountCallbacks = new HashMap<>(); + private HashMap unlockAccountCallbacks = new HashMap<>(); + private HashMap transactionCallbacks = new HashMap<>(); - public GethModule(ReactApplicationContext reactContext) { + GethModule(ReactApplicationContext reactContext) { super(reactContext); reactContext.addLifecycleEventListener(this); } @@ -67,12 +67,6 @@ public class GethModule extends ReactContextBaseJavaModule implements LifecycleE } } - @Override - public String getID() { - - return handlerIdentifier; - } - @Override public void onConnectorConnected() { } @@ -89,7 +83,7 @@ public class GethModule extends ReactContextBaseJavaModule implements LifecycleE Bundle data = message.getData(); String callbackIdentifier = data.getString(GethConnector.CALLBACK_IDENTIFIER); Log.d(TAG, "callback identifier: " + callbackIdentifier); - Callback callback = null; + Callback callback; switch (message.what) { case GethMessages.MSG_NODE_STARTED: Log.d(TAG, "handle startNodeCallbacks size: " + startNodeCallbacks.size()); @@ -108,18 +102,20 @@ public class GethModule extends ReactContextBaseJavaModule implements LifecycleE callback.invoke(data.getString("data")); } break; - case GethMessages.MSG_ACCOUNT_ADDED: - callback = addAccountCallbacks.remove(callbackIdentifier); - if (callback != null) { - callback.invoke(null, "{ \"address\": \"" + data.getString("address") + "\"}"); - } - break; case GethMessages.MSG_LOGGED_IN: callback = unlockAccountCallbacks.remove(callbackIdentifier); if (callback != null) { callback.invoke(data.getString("result")); } break; + case GethMessages.MSG_TRANSACTION_COMPLETED: + callback = transactionCallbacks.remove(callbackIdentifier); + String result = data.getString("result"); + Log.d(TAG, "Send result: " + result + (callback == null)); + if (callback != null) { + callback.invoke(result); + } + break; default: isClaimed = false; } @@ -130,7 +126,7 @@ public class GethModule extends ReactContextBaseJavaModule implements LifecycleE @ReactMethod public void startNode(Callback callback, Callback onAlreadyRunning) { - if(GethService.isRunning()){ + if (GethService.isRunning()) { onAlreadyRunning.invoke(); return; } @@ -197,9 +193,12 @@ public class GethModule extends ReactContextBaseJavaModule implements LifecycleE geth.createAccount(callbackIdentifier, password); } - @ReactMethod - public void addAccount(String privateKey, Callback callback) { + private String createIdentifier() { + return UUID.randomUUID().toString(); + } + @ReactMethod + public void completeTransaction(String hash, Callback callback) { Activity currentActivity = getCurrentActivity(); if (currentActivity == null) { @@ -212,13 +211,27 @@ public class GethModule extends ReactContextBaseJavaModule implements LifecycleE return; } + Log.d(TAG, "Complete transaction: " + hash); String callbackIdentifier = createIdentifier(); - addAccountCallbacks.put(callbackIdentifier, callback); - geth.addAccount(callbackIdentifier, privateKey); + transactionCallbacks.put(callbackIdentifier, callback); + + geth.completeTransaction(callbackIdentifier, hash); } - protected String createIdentifier() { - return UUID.randomUUID().toString(); + private static Callback signalEventCallback; + + //todo: move this method to GethService + public static void signalEvent(String jsonEvent) { + Log.d(TAG, "Signal event: " + jsonEvent); + if(signalEventCallback != null) { + signalEventCallback.invoke(jsonEvent); + } + } + + @ReactMethod + public void registerSignalEventCallback(Callback callback) { + Log.d(TAG, "registerSignalEventCallback"); + signalEventCallback = callback; } } diff --git a/android/app/src/main/java/com/statusim/geth/service/ConnectorHandler.java b/android/app/src/main/java/com/statusim/geth/service/ConnectorHandler.java index 0df2737a97..1a9077490a 100644 --- a/android/app/src/main/java/com/statusim/geth/service/ConnectorHandler.java +++ b/android/app/src/main/java/com/statusim/geth/service/ConnectorHandler.java @@ -4,9 +4,7 @@ package com.statusim.geth.service; import android.os.Message; public interface ConnectorHandler { - boolean handleMessage(Message message); void onConnectorConnected(); void onConnectorDisconnected(); - String getID(); -} \ No newline at end of file +} diff --git a/android/app/src/main/java/com/statusim/geth/service/GethConnector.java b/android/app/src/main/java/com/statusim/geth/service/GethConnector.java index 7083dff2d5..4751064e13 100644 --- a/android/app/src/main/java/com/statusim/geth/service/GethConnector.java +++ b/android/app/src/main/java/com/statusim/geth/service/GethConnector.java @@ -71,22 +71,21 @@ public class GethConnector extends ServiceConnector { } } - public void addAccount(String callbackIdentifier, String privateKey) { - + public void completeTransaction(String callbackIdentifier, String hash){ if (checkBound()) { Bundle data = new Bundle(); - data.putString("privateKey", privateKey); - Message msg = createMessage(callbackIdentifier, GethMessages.MSG_ADD_ACCOUNT, data); + data.putString("hash", hash); + Message msg = createMessage(callbackIdentifier, GethMessages.MSG_COMPLETE_TRANSACTION, data); try { serviceMessenger.send(msg); } catch (RemoteException e) { - Log.e(TAG, "Exception sending message(addAccount) to service: ", e); + Log.e(TAG, "Exception sending message(completeTransaction) to service: ", e); } } } - protected boolean checkBound() { + private boolean checkBound() { if (!isBound) { Log.d(TAG, "GethConnector not bound!"); @@ -95,7 +94,7 @@ public class GethConnector extends ServiceConnector { return true; } - protected Message createMessage(String callbackIdentifier, int idMessage, Bundle data) { + private Message createMessage(String callbackIdentifier, int idMessage, Bundle data) { Log.d(TAG, "Client messenger: " + clientMessenger.toString()); Message msg = Message.obtain(null, idMessage, 0, 0); diff --git a/android/app/src/main/java/com/statusim/geth/service/GethMessages.java b/android/app/src/main/java/com/statusim/geth/service/GethMessages.java index f6c3e24737..017bf5cbff 100644 --- a/android/app/src/main/java/com/statusim/geth/service/GethMessages.java +++ b/android/app/src/main/java/com/statusim/geth/service/GethMessages.java @@ -6,7 +6,7 @@ public class GethMessages { /** * Start the node */ - public static final int MSG_START_NODE = 1; + static final int MSG_START_NODE = 1; /** * Node started event @@ -16,7 +16,7 @@ public class GethMessages { /** * Stop the node */ - public static final int MSG_STOP_NODE = 3; + static final int MSG_STOP_NODE = 3; /** * Node stopped event @@ -26,7 +26,7 @@ public class GethMessages { /** * Unlock an account */ - public static final int MSG_LOGIN = 5; + static final int MSG_LOGIN = 5; /** * Account unlocked event @@ -36,7 +36,7 @@ public class GethMessages { /** * Create an account */ - public static final int MSG_CREATE_ACCOUNT = 7; + static final int MSG_CREATE_ACCOUNT = 7; /** * Account created event @@ -44,13 +44,13 @@ public class GethMessages { public static final int MSG_ACCOUNT_CREATED = 8; /** - * Add an account + * Account complete transaction event */ - public static final int MSG_ADD_ACCOUNT = 9; + static final int MSG_COMPLETE_TRANSACTION = 11; /** - * Account added event + * Account complete transaction event */ - public static final int MSG_ACCOUNT_ADDED = 10; + public static final int MSG_TRANSACTION_COMPLETED = 11; } diff --git a/android/app/src/main/java/com/statusim/geth/service/GethService.java b/android/app/src/main/java/com/statusim/geth/service/GethService.java index 71e15636f8..b72f73fe58 100644 --- a/android/app/src/main/java/com/statusim/geth/service/GethService.java +++ b/android/app/src/main/java/com/statusim/geth/service/GethService.java @@ -16,19 +16,18 @@ public class GethService extends Service { private static final String TAG = "GethService"; - private static boolean isGethStarted = false; private static boolean isGethInitialized = false; private final Handler handler = new Handler(); private static String dataFolder; - static class IncomingHandler extends Handler { + private static class IncomingHandler extends Handler { private final WeakReference service; IncomingHandler(GethService service) { - this.service = new WeakReference(service); + this.service = new WeakReference<>(service); } @Override @@ -43,11 +42,14 @@ public class GethService extends Service { } } - final Messenger serviceMessenger = new Messenger(new IncomingHandler(this)); + private final Messenger serviceMessenger = new Messenger(new IncomingHandler(this)); public static void signalEvent(String jsonEvent) { - System.out.println("\n\n\nIT WOOOOOORKS1111!!!!!!\n\n\n"); + Log.d(TAG, "Signal event: " + jsonEvent); + Bundle replyData = new Bundle(); + replyData.putString("event", jsonEvent); + //createAndSendReply(message, GethMessages.MSG_SIGNAL_EVENT, replyData); } @Nullable @@ -68,7 +70,6 @@ public class GethService extends Service { super.onDestroy(); //TODO: stop geth stopNode(null); - isGethStarted = false; isGethInitialized = false; Log.d(TAG, "Geth Service stopped !"); } @@ -78,7 +79,7 @@ public class GethService extends Service { return Service.START_STICKY; } - protected boolean handleMessage(Message message) { + private boolean handleMessage(Message message) { switch (message.what) { case GethMessages.MSG_START_NODE: @@ -94,14 +95,14 @@ public class GethService extends Service { createAccount(message); break; - case GethMessages.MSG_ADD_ACCOUNT: - addAccount(message); - break; - case GethMessages.MSG_LOGIN: login(message); break; + case GethMessages.MSG_COMPLETE_TRANSACTION: + completeTransaction(message); + break; + default: return false; } @@ -109,7 +110,7 @@ public class GethService extends Service { return true; } - protected void startNode(Message message) { + private void startNode(Message message) { if (!isGethInitialized) { isGethInitialized = true; Log.d(TAG, "Client messenger1: " + message.replyTo.toString()); @@ -120,12 +121,12 @@ public class GethService extends Service { } } - protected class StartTask extends AsyncTask { + private class StartTask extends AsyncTask { - protected String callbackIdentifier; - protected Messenger messenger; + String callbackIdentifier; + Messenger messenger; - public StartTask(Messenger messenger, String callbackIdentifier) { + StartTask(Messenger messenger, String callbackIdentifier) { this.messenger = messenger; this.callbackIdentifier = callbackIdentifier; } @@ -140,9 +141,8 @@ public class GethService extends Service { } } - protected void onGethStarted(Messenger messenger, String callbackIdentifier) { + private void onGethStarted(Messenger messenger, String callbackIdentifier) { Log.d(TAG, "Geth Service started"); - isGethStarted = true; Message replyMessage = Message.obtain(null, GethMessages.MSG_NODE_STARTED, 0, 0, null); Bundle replyData = new Bundle(); Log.d(TAG, "Callback identifier: " + callbackIdentifier); @@ -151,7 +151,7 @@ public class GethService extends Service { sendReply(messenger, replyMessage); } - protected void startGeth() { + private void startGeth() { File extStore = Environment.getExternalStorageDirectory(); @@ -160,28 +160,38 @@ public class GethService extends Service { extStore.getAbsolutePath() + "/ethereum" : getApplicationInfo().dataDir + "/ethereum"; Log.d(TAG, "Starting background Geth Service in folder: " + dataFolder); + try { final File newFile = new File(dataFolder); + // todo handle error? newFile.mkdir(); } catch (Exception e) { Log.e(TAG, "error making folder: " + dataFolder, e); } + final Runnable addPeer = new Runnable() { + public void run() { + Log.w("Geth", "adding peer"); + Statusgo.addPeer("enode://409772c7dea96fa59a912186ad5bcdb5e51b80556b3fe447d940f99d9eaadb51d4f0ffedb68efad232b52475dd7bd59b51cee99968b3cc79e2d5684b33c4090c@139.162.166.59:30303"); + } + }; + new Thread(new Runnable() { public void run() { - Statusgo.StartNode(dataFolder); } }).start(); + + handler.postDelayed(addPeer, 5000); } - protected void stopNode(Message message) { + private void stopNode(Message message) { // TODO: stop node createAndSendReply(message, GethMessages.MSG_NODE_STOPPED, null); } - protected void createAccount(Message message) { + private void createAccount(Message message) { Bundle data = message.getData(); String password = data.getString("password"); // TODO: remove second argument @@ -194,21 +204,7 @@ public class GethService extends Service { createAndSendReply(message, GethMessages.MSG_ACCOUNT_CREATED, replyData); } - protected void addAccount(Message message) { - Bundle data = message.getData(); - String privateKey = data.getString("privateKey"); - String password = data.getString("password"); - // TODO: add account - //String address = Statusgo.doAddAccount(privateKey, password); - String address = "added account address"; - Log.d(TAG, "Added account: " + address); - - Bundle replyData = new Bundle(); - replyData.putString("address", address); - createAndSendReply(message, GethMessages.MSG_ACCOUNT_ADDED, replyData); - } - - protected void login(Message message) { + private void login(Message message) { Bundle data = message.getData(); String address = data.getString("address"); String password = data.getString("password"); @@ -221,11 +217,24 @@ public class GethService extends Service { createAndSendReply(message, GethMessages.MSG_LOGGED_IN, replyData); } + private void completeTransaction(Message message){ + Bundle data = message.getData(); + String hash = data.getString("hash"); + + Log.d(TAG, "Before CompleteTransaction: " + hash); + String result = Statusgo.CompleteTransaction(hash); + Log.d(TAG, "After CompleteTransaction: " + result); + + Bundle replyData = new Bundle(); + replyData.putString("result", result); + createAndSendReply(message, GethMessages.MSG_TRANSACTION_COMPLETED, replyData); + } + public static boolean isRunning() { return isGethInitialized; } - protected void createAndSendReply(Message message, int replyIdMessage, Bundle replyData) { + private static void createAndSendReply(Message message, int replyIdMessage, Bundle replyData) { if (message == null) { return; @@ -243,7 +252,7 @@ public class GethService extends Service { sendReply(message.replyTo, replyMessage); } - protected void sendReply(Messenger messenger, Message message) { + private static void sendReply(Messenger messenger, Message message) { try { messenger.send(message); diff --git a/android/app/src/main/java/com/statusim/geth/service/ServiceConnector.java b/android/app/src/main/java/com/statusim/geth/service/ServiceConnector.java index f20e1fcf66..b2cd7996dd 100644 --- a/android/app/src/main/java/com/statusim/geth/service/ServiceConnector.java +++ b/android/app/src/main/java/com/statusim/geth/service/ServiceConnector.java @@ -11,39 +11,27 @@ import java.util.ArrayList; public class ServiceConnector { - private static final String TAG = "ServiceConnector"; - - /** - * Incoming message handler. Calls to its binder are sequential! - */ - protected final IncomingHandler handler; - - /** - * Handler thread to avoid running on the main UI thread - */ - protected final HandlerThread handlerThread; - /** Context of the activity from which this connector was launched */ - protected Context context; + private Context context; /** The class of the service we want to connect to */ - protected Class serviceClass; + private Class serviceClass; /** Flag indicating if the service is bound. */ - protected boolean isBound; + boolean isBound; /** Sends messages to the service. */ - protected Messenger serviceMessenger = null; + Messenger serviceMessenger = null; /** Receives messages from the service. */ - protected Messenger clientMessenger = null; + Messenger clientMessenger = null; - protected ArrayList handlers = new ArrayList<>(); + private ArrayList handlers = new ArrayList<>(); /** Handles incoming messages from service. */ - class IncomingHandler extends Handler { + private class IncomingHandler extends Handler { - public IncomingHandler(HandlerThread thread) { + IncomingHandler(HandlerThread thread) { super(thread.getLooper()); } @@ -72,7 +60,7 @@ public class ServiceConnector { /** * Class for interacting with the main interface of the service. */ - protected ServiceConnection serviceConnection = new ServiceConnection() { + private ServiceConnection serviceConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { @@ -100,13 +88,14 @@ public class ServiceConnector { } }; - public ServiceConnector(Context context, Class serviceClass) { - + ServiceConnector(Context context, Class serviceClass) { this.context = context; this.serviceClass = serviceClass; - handlerThread = new HandlerThread("HandlerThread"); + // Handler thread to avoid running on the main UI thread + HandlerThread handlerThread = new HandlerThread("HandlerThread"); handlerThread.start(); - handler = new IncomingHandler(handlerThread); + // Incoming message handler. Calls to its binder are sequential! + IncomingHandler handler = new IncomingHandler(handlerThread); clientMessenger = new Messenger(handler); } diff --git a/android/app/src/main/res/drawable-hdpi/icon_ok_disabled_inversed.png b/android/app/src/main/res/drawable-hdpi/icon_ok_disabled_inversed.png new file mode 100644 index 0000000000000000000000000000000000000000..5235f7f0780af8d37e28c1ffc2158e4ba40db066 GIT binary patch literal 443 zcmV;s0Yv_ZP)Px$bxA})R7efAl*>}XFbqYLvSGo7p}e~(l=Arhe}U4nfMF=*wPsil&NT`ZN!qk| zXhyLu%lAZ%ohWMAk|gQku`r!BSwQ;eU*ZD+H`;`;PcIgsMDDg&bR z+By$PD(v=T`99o|MK=fN!&JO?z;kU0;UF)pnr z)eheX;5~uIG)UXUhiQclwC8dJ8kcRI%m07$5zYPqf}p^-2R$>MtOF7>ck}^fKmI3q zn@TpT1k&0fm}1#Vj1x`i+^t(tx;O{Zgnc7jhxC*xJOwgQr>YfRVtexuqWWvgp6Y^r lWtKdtP?z3c-X+=PQ$Hj%nRhQFR3-ob002ovPDHLkV1mS_zZC!g literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable-mdpi/icon_ok_disabled_inversed.png b/android/app/src/main/res/drawable-mdpi/icon_ok_disabled_inversed.png new file mode 100644 index 0000000000000000000000000000000000000000..6052db98472a02cf0dbf0c5ecde4455f33d4d2f8 GIT binary patch literal 307 zcmV-30nGl1P)Px#?MXyIR45gtlT8bPKoEvqv7nDr)FFa`=+L1$^#6ZF-GWX*Q1`wM+w;n5DrRZ} z@6Nch^Dxe;=lx;U+5nA&2~P7#C|Ohg$)1;LC_tY4NE0+5OvZ)f^3X2k4m5n zLfa^j{2yL<`gN8 z%|SF+<*z6gq5_}yJmL>}zy#|l$bo3E@l13kY4L0*oRZL2YS9jjQ46V)LJ0ZvtpS^{ zH&nve1uw`e+W6+fTT3MO#3%P$8j`60gmh)l1nD;@ZEt^}XwD@-hBN>G002ovPDHLk FV1kHvflmMc literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable-xhdpi/icon_ok_disabled_inversed.png b/android/app/src/main/res/drawable-xhdpi/icon_ok_disabled_inversed.png new file mode 100644 index 0000000000000000000000000000000000000000..5d215f58a42c3a13a554a0b3185988d7ece735ad GIT binary patch literal 607 zcmV-l0-*hgP)|00001b5ch_0Itp) z=>Px%8A(JzR7efQmPt>;Fc5{CmQ_XU+m!=lD~JpK|3@Iv(n49wPC1kV7Zl!$>}Wbk zoYE%gNS5tqd*0WHNhp;j9KP>Y(L9S$wu4FZ09l6bDO$}!g=9qo8JHvZ+M>K-+^!T2 zLO;N)pp!6^7ts2ajzS?}U=CrXy>C3v>lOOQL}j!uVIEl#y^vd#955ZgbggkAK!90= zAs>_W8-O8{-aswWaKK!nGEdqpS0KQw!SDc0`^3T!PH&`^NjPAxP?_f>lU#rRvkt?3 zG@aiGLr8sq+F0R$X`?bPX~_p(PDnPLcEGi6VB+q~P%z9L0wLKJ=4a>^U8YHEIJbc{ z!suuXQyF_Gcw|Vn4Pn=azv$1Aq;EH+^#+D_Mh9W|(HuI;NXCzvh2cSdaO+0r+!@%n zi4Acum;Z&KQ5lE6(cf_*VhUqIRfQHA8koJvLa9WUw-`ND3hodN-(%;4+RY1o4AXg~ z2UF<0bD6?0ad@c#b&GkIq6k8rzm{N*`$&0b&*d#Ye)wAXj7xL!*I5W}WeC zY1Be<{c7%MWK|jK9Sg)o1J%T^XE%%9!h8xXlVKtdvm2-ehF`)hH<-Xv7$pvIrwCq0 t<9F@?pm9}8JfF(~pekrfxg-}!{{R_yzEritZ3qAW002ovPDHLkV1j-W0v-SW literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_ok_disabled_inversed.png b/android/app/src/main/res/drawable-xxhdpi/icon_ok_disabled_inversed.png new file mode 100644 index 0000000000000000000000000000000000000000..787972f4707ee802e2b51f0be53f638ad6b8f45b GIT binary patch literal 886 zcmV-+1Bv{JP)Px&FiAu~RA>d=nP-pGFc5}!kGm5^^v*}xaSguk|NjHJ$_Efth;kfV^yPgbJ94~n z*4a%q4kKA(j~#oSorx1?2ZR4WMx)Wt$5`+UeQOKqx_n-hF0hP|_6bZdH{|a%Ib1Kn1a(s+S1paF->JR7=oyo43Wz|EejzCQKcTYN8!!V!q^{t7 zK)%VorTgqxFH*54)K8$32b%^ec`yTYOO9T~*?@eNJxf2gKiP=Q0tH}_U%8p46tO+RKk^(bOEAs22{pCya7umMi(o6bsQ!-#Rpx7vW=LCUD5@1b3!P+^P zfx07KE(Csj%%4?Azlo8ub94&U78G!ufeDI(A(1+dBLS%$DLYRmV3AOte%@-i8>cRn1)$DWGcIQ@*bSFfDpS1J+gUwE^cqo%0=W+7{}MW2;@wQ_+Ds zZD#o){XnUJo>?NHK58M-Zzd?5&A_xM2hI(ZlNcTOt~zkp8OO2Lsv4*@U39kM!p5Lj zgZqhe+*I+Q=;vO$t1-(orjbaf51PA?NLe2j6pg=XytRfBeN47ZIk;5PS7|a(+_P1Y z0&2I3^mCobHV;kuLdK_VhgUzYHgM73NHo@NL}$FY+-BpEvXke#qTrs%_}q4sXukez z#K0LS?x`xML-mt>WK83dgE6xfc_Q_adO)}j*Nx+)Ln0#u6zSIr6pj_ZFle#IQm?27 zWFBE<@Dkx=h-U8|9@&gKtjYp`05(l_b5 z!-2-^sqh2w93+|{0g68jT1!87;UE};4BVu%65q;s&Q(G21MJTWN^4C%wiq~exT^O% z<_gK&p>U)!V%Af^k!q7(iR^G!Xa9UCQrUl2Zb#eo(B@HC*QC&{jJ(gqN@q|w7zM^a z1Gl3p$BIePDHP5{gE3fe94CO|$Q}~jJJ~4ao_?-FwZOPg4)djGTZK=|z5w;GYbczm z4aP+VZcA1EXtVN0l_a37LsM(*mdh&lRaR7@cG#&GoA-qRT1pY>AB&SY={C>59RL6T M07*qoM6N<$f~-}c@Bjb+ literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_ok_disabled_inversed.png b/android/app/src/main/res/drawable-xxxhdpi/icon_ok_disabled_inversed.png new file mode 100644 index 0000000000000000000000000000000000000000..1846eb534e50483a9aae2eddf88054c4432533e1 GIT binary patch literal 1167 zcmV;A1aSL_P)Px(Nl8RORA>e5n`cuLK@f)TR8&OFu`s7X5I^|;e}#Ywm_wg+-R-Tt ziA{9XFtangGu_X1&uut5oo(4B5J#Zb>y4>gQP~-#w=1f%Eq#KrTfWYz>{A)n@ttnB z`^Wcxhkwwf_5d7QKcq6F{yrw%HbHT|M!<1H)bCXm6-gT$v<^@R=carg2fzq5DavU7$bwyCG6%iD|A+7fM1Y+9v1?MH>jR%wkPU8Wk zfU}~=dF4k|V0_AaM*L$m@Trb$ax*5*tiJF6Ar){1t@PgjRGV% zT;H(gSkr1rX|)Q3IB7%#g>cw&xI4Fq2@Vt09#9H6_Y^sAEEH=GsAxE~1|&E<Wk$up z`=s`Q4EUY<>Jbxa{YiD-P_cY4z+ulR1PPqpPJ4C zlIGys(i%DJf57<=V_WV+E`mB*__=0(VqD>v^sZ%rR0lY)*aXCZAU##}jJ82eNat!A zNFJQN^+&AfB8P9%Eteq!1?jP@AmuD_Qc9OCicUvx_$3!h z!LJ)UV@EYl%jrv_2L1jJ&aC<(*Y$PP2F|=}DJ628EpdiEkW%F9Sa-5lPs#ZU-tkZ~ zB^<8eOO=s;^vUt)U`1usXyI8wI2Wa>el8EJk;5$wkyAPxd>ILd!4T52nrj>z< z@%2JD{=J<)JlL)j9E^(vghB{uNlojcgo}bQ#knDzq>=NPbE_ieD`GhdOd^4Fu?U;| zDHY2aE^;ab2PZAYqZddBiLYp`fb){d!ok5rfH*;rxS<3RH<(%FmnL9C{*rZ~qR#*C z9yGYHoZ{`IX}Y0-W1bPAlJ+ z)PR^oP6m;n<~M)Rn6`tH6l%- zj*?o!!I4}5F_9n (fn [db [_ address password]] - (geth/login address password (fn [result] - (let [data (json->clj result) - error (:error data) - success (zero? (count error))] - (log/debug "Logged in account: " address result) - (if success - (logged-in db address) - (dispatch [:set-in [:login :error] error]))))) - db))) + (u/side-effect! + (fn [db [_ address password]] + (geth/login address password + (fn [result] + (let [data (json->clj result) + error (:error data) + success (zero? (count error))] + (log/debug "Logged in account: ") + (if success + (logged-in db address) + (dispatch [:set-in [:login :error] error])))))))) (defn load-accounts! [db _] (let [accounts (->> (accounts/get-accounts) @@ -120,7 +121,7 @@ (defn console-create-account [db _] (let [msg-id (random/id)] - (dispatch [:received-msg + (dispatch [:received-message {:msg-id msg-id :content {:command (name :keypair) :content (label :t/keypair-generated)} @@ -130,4 +131,4 @@ :to "me"}]) db)) -(register-handler :console-create-account console-create-account) \ No newline at end of file +(register-handler :console-create-account console-create-account) diff --git a/src/status_im/android/core.cljs b/src/status_im/android/core.cljs index 0342c7cb8e..59a7c644a8 100644 --- a/src/status_im/android/core.cljs +++ b/src/status_im/android/core.cljs @@ -18,6 +18,7 @@ [status-im.chat.screen :refer [chat]] [status-im.accounts.login.screen :refer [login]] [status-im.accounts.screen :refer [accounts]] + [status-im.transactions.screen :refer [confirm]] [status-im.chats-list.screen :refer [chats-list]] [status-im.new-group.screen :refer [new-group]] [status-im.participants.views.add :refer [new-participants]] @@ -98,6 +99,7 @@ :profile-photo-capture profile-photo-capture :accounts accounts :login login + :confirm confirm :my-profile my-profile)] [component {:platform-specific {:styles styles :list-selection-fn show-dialog}}])))}))) diff --git a/src/status_im/chat/handlers.cljs b/src/status_im/chat/handlers.cljs index dfc396238b..d8334e0e13 100644 --- a/src/status_im/chat/handlers.cljs +++ b/src/status_im/chat/handlers.cljs @@ -1,5 +1,6 @@ (ns status-im.chat.handlers - (:require [re-frame.core :refer [enrich after debug dispatch]] + (:require-macros [cljs.core.async.macros :as am]) + (:require [re-frame.core :refer [enrich after debug dispatch path]] [status-im.models.commands :as commands] [clojure.string :as str] [status-im.components.styles :refer [default-chat-color]] @@ -20,31 +21,39 @@ [status-im.handlers.content-suggestions :refer [get-content-suggestions]] [status-im.utils.phone-number :refer [format-phone-number valid-mobile-number?]] - [status-im.utils.datetime :as time] [status-im.components.jail :as j] [status-im.utils.types :refer [json->clj]] - [status-im.commands.utils :refer [generate-hiccup]] [status-im.chat.handlers.commands :refer [command-prefix]] + [status-im.chat.utils :refer [console? not-console?]] status-im.chat.handlers.animation status-im.chat.handlers.requests - status-im.chat.handlers.unviewed-messages)) + status-im.chat.handlers.unviewed-messages + status-im.chat.handlers.send-message + status-im.chat.handlers.receive-message + [cljs.core.async :as a])) (register-handler :set-show-actions (fn [db [_ show-actions]] (assoc db :show-actions show-actions))) (register-handler :load-more-messages - (fn [{:keys [current-chat-id] :as db} _] + (fn [{:keys [current-chat-id loading-allowed] :as db} _] (let [all-loaded? (get-in db [:chats current-chat-id :all-loaded?])] - (if all-loaded? - db - (let [messages-path [:chats current-chat-id :messages] - messages (get-in db messages-path) - new-messages (messages/get-messages current-chat-id (count messages)) - all-loaded? (> default-number-of-messages (count new-messages))] - (-> db - (update-in messages-path concat new-messages) - (assoc-in [:chats current-chat-id :all-loaded?] all-loaded?))))))) + (if loading-allowed + (do (am/go + ( default-number-of-messages (count new-messages))] + (-> db + (assoc :loading-allowed false) + (update-in messages-path concat new-messages) + (assoc-in [:chats current-chat-id :all-loaded?] all-loaded?))))) + db)))) (defn safe-trim [s] (when (string? s) @@ -99,12 +108,6 @@ (dispatch [:set-chat-command (ffirst suggestions)]) (dispatch [::set-text chat-id text])))) -(defn console? [s] - (= "console" s)) - -(def not-console? - (complement console?)) - (register-handler :set-chat-input-text (u/side-effect! (fn [{:keys [current-chat-id]} [_ text]] @@ -142,23 +145,6 @@ (register-handler ::set-text update-text) -(defn check-author-direction - [db chat-id {:keys [from outgoing] :as message}] - (let [previous-message (first (get-in db [:chats chat-id :messages]))] - (merge message - {:same-author (if previous-message - (= (:from previous-message) from) - true) - :same-direction (if previous-message - (= (:outgoing previous-message) outgoing) - true)}))) - -(defn add-message-to-db - [db chat-id message] - (let [messages [:chats chat-id :messages]] - (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] @@ -172,141 +158,14 @@ (fn [db [_ {:keys [chat-id msg-id]}]] (set-message-shown db chat-id msg-id))) -(defn default-delivery-status [chat-id] - (if (console? chat-id) - :seen - :pending)) - -(defn prepare-message - [{:keys [identity current-chat-id] :as db} _] - (let [text (get-in db [:chats current-chat-id :input-text]) - [command] (suggestions/check-suggestion db (str text " ")) - message (check-author-direction - db current-chat-id - {:msg-id (random/id) - :chat-id current-chat-id - :content text - :to current-chat-id - :from identity - :content-type text-content-type - :delivery-status (default-delivery-status current-chat-id) - :outgoing true - :timestamp (time/now-ms)})] - _ (.log js/console "WOW3") - (if command - (commands/set-command-input db :commands command) - (assoc db :new-message (when-not (str/blank? text) message))))) - -(defn prepare-command - [identity chat-id {:keys [preview preview-string content command to-message]}] - (let [content {:command (command :name) - :content content}] - {:msg-id (random/id) - :from identity - :to chat-id - :content content - :content-type content-type-command - :delivery-status (default-delivery-status chat-id) - :outgoing true - :preview preview-string - :rendered-preview preview - :to-message to-message})) - -(defn prepare-staged-commans - [{:keys [current-chat-id identity] :as db} _] - (let [staged-commands (get-in db [:chats current-chat-id :staged-commands])] - (->> staged-commands - (map #(prepare-command identity current-chat-id %)) - ;todo this is wrong :( - (map #(check-author-direction db current-chat-id %)) - (assoc db :new-commands)))) - -(defn add-message - [{:keys [new-message current-chat-id] :as db}] - (if new-message - (add-message-to-db db current-chat-id new-message) - db)) - -(defn add-commands - [{:keys [new-commands current-chat-id] :as db}] - (reduce - #(add-message-to-db %1 current-chat-id %2) - db - new-commands)) - -(defn clear-input - [{:keys [current-chat-id new-message] :as db} _] - (if new-message - (assoc-in db [:chats current-chat-id :input-text] nil) - db)) - -(defn clear-staged-commands - [{:keys [current-chat-id] :as db} _] - (assoc-in db [:chats current-chat-id :staged-commands] [])) - -(defn send-message! - [{:keys [new-message current-chat-id] :as db} _] - (when (and new-message (not-console? current-chat-id)) - (let [{:keys [group-chat]} (get-in db [:chats current-chat-id]) - message (select-keys new-message [:content :msg-id])] - (if group-chat - (api/send-group-user-msg (assoc message :group-id current-chat-id)) - (api/send-user-msg (assoc message :to current-chat-id)))))) - -(defn save-message-to-realm! - [{:keys [new-message current-chat-id]} _] - (when new-message - (messages/save-message current-chat-id new-message))) - -(defn save-commands-to-realm! - [{:keys [new-commands current-chat-id]} _] - (doseq [new-command new-commands] - (messages/save-message - current-chat-id - (dissoc new-command :rendered-preview :to-message)))) - -(defn dispatch-responded-requests! - [{:keys [new-commands current-chat-id]} _] - (doseq [{:keys [to-message]} new-commands] - (when to-message - (dispatch [:request-answered! current-chat-id to-message])))) - -(defn invoke-commands-handlers! - [{:keys [new-commands current-chat-id]}] - (doseq [{:keys [content] :as com} new-commands] - (let [{:keys [command content]} content - type (:type command) - path [(if (= :command type) :commands :responses) - command - :handler] - params {:value content}] - (j/call current-chat-id - path - params - #(dispatch [:command-handler! com %]))))) - -(register-handler :send-chat-msg - (-> prepare-message - ((enrich prepare-staged-commans)) - ((enrich add-message)) - ((enrich add-commands)) - ((enrich clear-input)) - ((enrich clear-staged-commands)) - ((after send-message!)) - ((after save-message-to-realm!)) - ((after save-commands-to-realm!)) - ((after dispatch-responded-requests!)) - ;; todo maybe it is better to track if it was handled or not - ((after invoke-commands-handlers!)))) - (register-handler :init-console-chat (fn [db [_]] (sign-up-service/init db))) (register-handler :save-password (fn [db [_ password]] - (dispatch [:create-account password]) (sign-up-service/save-password password) + (dispatch [:create-account password]) (assoc db :password-saved true))) (register-handler :sign-up @@ -372,35 +231,6 @@ (after #(dispatch [:load-unviewed-messages!])) ((enrich initialize-chats) load-chats!)) -(defn store-message! - [{:keys [new-message]} [_ {chat-id :from}]] - (messages/save-message chat-id new-message)) - -(defn dispatch-request! - [{:keys [new-message]} [_ {chat-id :from}]] - (when (= (:content-type new-message) content-type-command-request) - (dispatch [:add-request chat-id new-message]))) - -(defn receive-message - [db [_ {chat-id :from :as message}]] - (let [message' (-> db - (check-author-direction chat-id message) - (assoc :delivery-status :pending))] - (-> db - (add-message-to-db chat-id message') - (assoc :new-message message')))) - -(defn dispatch-unviewed-message! - [{:keys [new-message]} [_ {chat-id :from}]] - (let [{:keys [msg-id]} new-message] - (dispatch [:add-unviewed-message chat-id msg-id]))) - -(register-handler :received-msg - [(after store-message!) - (after dispatch-request!) - (after dispatch-unviewed-message!)] - receive-message) - (register-handler :group-received-msg (u/side-effect! (fn [_ [_ {chat-id :group-id :as msg}]] @@ -412,6 +242,7 @@ messages (get-in db [:chats chat-id :messages]) db' (assoc db :current-chat-id chat-id)] (dispatch [:load-requests! chat-id]) + (dispatch [:load-commands! chat-id]) (if (seq messages) db' (-> db' @@ -419,15 +250,15 @@ init-chat)))) (defn prepare-chat - [{:keys [contacts] :as db} [_ contcat-id]] - (let [name (get-in contacts [contcat-id :name]) - chat {:chat-id contcat-id - :name name + [{:keys [contacts] :as db} [_ contact-id]] + (let [name (get-in contacts [contact-id :name]) + chat {:chat-id contact-id + :name (or name contact-id) :color default-chat-color :group-chat false :is-active true :timestamp (.getTime (js/Date.)) - :contacts [{:identity contcat-id}] + :contacts [{:identity contact-id}] :dapp-url nil :dapp-hash nil}] (assoc db :new-chat chat))) @@ -451,6 +282,11 @@ ((after save-chat!)) ((after open-chat!)))) +(register-handler :add-chat + (-> prepare-chat + ((enrich add-chat)) + ((after save-chat!)))) + (register-handler :switch-command-suggestions! (u/side-effect! (fn [db] diff --git a/src/status_im/chat/handlers/animation.cljs b/src/status_im/chat/handlers/animation.cljs index a0737067ac..a5c23a91c5 100644 --- a/src/status_im/chat/handlers/animation.cljs +++ b/src/status_im/chat/handlers/animation.cljs @@ -48,7 +48,7 @@ suggestion? (get-in db [:has-suggestions? current-chat-id]) custom-errors (get-in db [:custom-validation-errors current-chat-id]) validation-height (if (or (seq errors) (seq custom-errors)) - request-info-height + (+ suggestions-header-height request-info-height) 0)] (+ validation-height (if (= :response type) diff --git a/src/status_im/chat/handlers/commands.cljs b/src/status_im/chat/handlers/commands.cljs index e4a9c45bf8..3e73b91401 100644 --- a/src/status_im/chat/handlers/commands.cljs +++ b/src/status_im/chat/handlers/commands.cljs @@ -7,7 +7,9 @@ [clojure.string :as str] [status-im.commands.utils :as cu] [status-im.utils.phone-number :as pn] - [status-im.i18n :as i18n])) + [status-im.i18n :as i18n] + [status-im.utils.datetime :as time] + [status-im.utils.random :as random])) (def command-prefix "c ") @@ -61,7 +63,7 @@ (defn invoke-command-preview! [{:keys [staged-command]} [_ chat-id]] - (let [{:keys [command content]} staged-command + (let [{:keys [command content id]} staged-command {:keys [name type]} command path [(if (= :command type) :commands :responses) name @@ -70,7 +72,7 @@ (j/call chat-id path params - #(dispatch [:command-preview chat-id %])))) + #(dispatch [:command-preview chat-id id %])))) (defn command-input ([{:keys [current-chat-id] :as db}] @@ -102,7 +104,8 @@ path [(if (= :command type) :commands :responses) name :validator] - params {:value content}] + params {:value content + :command data}] (j/call chat-id path params @@ -110,13 +113,14 @@ (register-handler :stage-command (after start-validate!) - (fn [{:keys [current-chat-id] :as db}] + (fn [{:keys [current-chat-id current-account-id] :as db}] (let [{:keys [command content]} (command-input db) content' (content-by-command command content)] (-> db (assoc ::command {:content content' :command command - :chat-id current-chat-id}) + :chat-id current-chat-id + :address current-account-id}) (assoc-in [:disable-staging current-chat-id] true))))) (register-handler ::finish-command-staging @@ -128,7 +132,9 @@ content' (content-by-command command content) command-info {:command command :content content' - :to-message to-msg-id}] + :to-message to-msg-id + :created-at (time/now-ms) + :id (random/id)}] (-> db (commands/stage-command command-info) (assoc :staged-command command-info) @@ -172,14 +178,19 @@ (fn [db] (dissoc db :validation-errors :custom-validation-errors))) +(defn dispatch-error! + [chat-id title description] + (letfn [(wrap-params [p] (if (seqable? p) p [p]))] + (dispatch [::set-validation-error + chat-id + {:title (apply i18n/label (wrap-params title)) + :description (apply i18n/label (wrap-params description))}]))) + (def validation-handlers {:phone (fn [chat-id [number]] (if (pn/valid-mobile-number? number) (dispatch [::finish-command-staging chat-id]) - (dispatch [::set-validation-error - chat-id - {:title (i18n/label :t/phone-number) - :description (i18n/label :t/invalid-phone)}])))}) + (dispatch-error! chat-id :t/phone-number :t/invalid-phone)))}) (defn validator [name] (validation-handlers (keyword name))) @@ -190,7 +201,6 @@ (when-let [handler (validator name)] (handler chat-id params))))) - (register-handler ::set-validation-error (after #(dispatch [:fix-response-height])) (fn [db [_ chat-id error]] diff --git a/src/status_im/chat/handlers/receive_message.cljs b/src/status_im/chat/handlers/receive_message.cljs new file mode 100644 index 0000000000..1ec61560a8 --- /dev/null +++ b/src/status_im/chat/handlers/receive_message.cljs @@ -0,0 +1,41 @@ +(ns status-im.chat.handlers.receive-message + (:require [status-im.utils.handlers :refer [register-handler] :as u] + [re-frame.core :refer [enrich after debug dispatch path]] + [status-im.models.messages :as messages] + [status-im.chat.utils :as cu] + [status-im.commands.utils :refer [generate-hiccup]] + [status-im.constants :refer [content-type-command-request]] + [cljs.reader :refer [read-string]] + [status-im.models.chats :as c])) + +(defn check-previev [{:keys [content] :as message}] + (if-let [preview (:preview content)] + (let [rendered-preview (generate-hiccup (read-string preview))] + (assoc message + :preview preview + :rendered-preview rendered-preview)) + message)) + +(defn store-message [{chat-id :from :as message}] + (messages/save-message chat-id (dissoc message :rendered-preview :new?))) + +(register-handler :received-message + (u/side-effect! + (fn [{:keys [chats] :as db} [_ {chat-id :from :keys [msg-id] :as message}]] + (let [same-message (messages/get-message msg-id)] + (when-not same-message + (let [message' (assoc (->> message + (cu/check-author-direction db chat-id) + (check-previev)) + :delivery-status :pending)] + (store-message message') + (when-not (c/chat-exists? chat-id) + (dispatch [:add-chat chat-id])) + (dispatch [::add-message message']) + (when (= (:content-type message') content-type-command-request) + (dispatch [:add-request chat-id message'])) + (dispatch [:add-unviewed-message chat-id msg-id]))))))) + +(register-handler ::add-message + (fn [db [_ {chat-id :from :keys [new?] :as message}]] + (cu/add-message-to-db db chat-id message new?))) diff --git a/src/status_im/chat/handlers/requests.cljs b/src/status_im/chat/handlers/requests.cljs index 000ad07b8a..b1be9fb745 100644 --- a/src/status_im/chat/handlers/requests.cljs +++ b/src/status_im/chat/handlers/requests.cljs @@ -1,9 +1,9 @@ (ns status-im.chat.handlers.requests (:require [re-frame.core :refer [after dispatch enrich]] [status-im.utils.handlers :refer [register-handler]] - [status-im.persistence.realm.core :as realm] [status-im.models.requests :as requests] - [status-im.utils.handlers :as u])) + [status-im.utils.handlers :refer [register-handler] :as u] + [status-im.persistence.realm.core :as realm])) (defn store-request! [{:keys [new-request] :as db}] diff --git a/src/status_im/chat/handlers/send_message.cljs b/src/status_im/chat/handlers/send_message.cljs new file mode 100644 index 0000000000..966aefca5e --- /dev/null +++ b/src/status_im/chat/handlers/send_message.cljs @@ -0,0 +1,201 @@ +(ns status-im.chat.handlers.send-message + (:require [status-im.utils.handlers :refer [register-handler] :as u] + [clojure.string :as s] + [status-im.models.messages :as messages] + [status-im.components.jail :as j] + [status-im.utils.random :as random] + [status-im.utils.datetime :as time] + [re-frame.core :refer [enrich after debug dispatch path]] + [status-im.chat.suggestions :as suggestions] + [status-im.models.commands :as commands] + [status-im.chat.utils :as cu] + [status-im.constants :refer [text-content-type + content-type-command + content-type-command-request + default-number-of-messages]] + [status-im.protocol.api :as api])) + +(defn default-delivery-status [chat-id] + (if (cu/console? chat-id) + :seen + :pending)) + +(defn prepare-message + [{:keys [identity current-chat-id] :as db} _] + (let [text (get-in db [:chats current-chat-id :input-text]) + [command] (suggestions/check-suggestion db (str text " ")) + message (cu/check-author-direction + db current-chat-id + {:msg-id (random/id) + :chat-id current-chat-id + :content text + :to current-chat-id + :from identity + :content-type text-content-type + :delivery-status (default-delivery-status current-chat-id) + :outgoing true + :timestamp (time/now-ms)})] + (if command + (commands/set-command-input db :commands command) + (assoc db :new-message (when-not (s/blank? text) message))))) + +(defn prepare-command + [identity chat-id {:keys [preview preview-string content command to-message]}] + (let [content {:command (command :name) + :content content}] + {:msg-id (random/id) + :from identity + :to chat-id + :content (assoc content :preview preview-string) + :content-type content-type-command + :delivery-status (default-delivery-status chat-id) + :outgoing true + :preview preview-string + :rendered-preview preview + :to-message to-message + :type (:type command) + :has-handler (:has-handler command)})) + +(register-handler :send-chat-msg + (u/side-effect! + (fn [{:keys [current-chat-id identity current-account-id] :as db}] + (let [staged-commands (vals (get-in db [:chats current-chat-id :staged-commands])) + text (get-in db [:chats current-chat-id :input-text]) + data {:commands staged-commands + :message text + :chat-id current-chat-id + :identity identity + :address current-account-id}] + (dispatch [:clear-input current-chat-id]) + (cond + (seq staged-commands) + (dispatch [::check-commands-handlers! data]) + (not (s/blank? text)) + (dispatch [::prepare-message data])))))) + + +(register-handler ::check-commands-handlers! + (u/side-effect! + (fn [_ [_ {:keys [commands message] :as params}]] + (doseq [{:keys [command] :as message} commands] + (let [params' (assoc params :command message)] + (if (:pending message) + (dispatch [:navigate-to :confirm]) + (if (:has-handler command) + (dispatch [::invoke-command-handlers! params']) + (dispatch [:prepare-command! params']))))) + (when-not (s/blank? message) + (dispatch [::prepare-message params]))))) + +(register-handler :clear-input + (path :chats) + (fn [db [_ chat-id]] + (assoc-in db [chat-id :input-text] nil))) + +(register-handler :prepare-command! + (u/side-effect! + (fn [db [_ {:keys [chat-id command identity] :as params}]] + (let [command' (->> command + (prepare-command identity chat-id) + (cu/check-author-direction db chat-id))] + (dispatch [::clear-command chat-id (:id command)]) + (dispatch [::send-command! (assoc params :command command')]))))) + +(register-handler ::clear-command + (fn [db [_ chat-id id]] + (update-in db [:chats chat-id :staged-commands] dissoc id))) + +(register-handler ::send-command! + (u/side-effect! + (fn [_ [_ params]] + (dispatch [::add-command params]) + (dispatch [::save-command! params]) + (dispatch [::dispatch-responded-requests! params]) + (dispatch [::send-command-protocol! params])))) + +(register-handler ::add-command + (after (fn [_ [_ {:keys [handler]}]] + (when handler (handler)))) + (fn [db [_ {:keys [chat-id command]}]] + (cu/add-message-to-db db chat-id command))) + +(register-handler ::save-command! + (u/side-effect! + (fn [_ [_ {:keys [command chat-id]}]] + (messages/save-message + chat-id + (dissoc command :rendered-preview :to-message :has-handler))))) + +(register-handler ::dispatch-responded-requests! + (u/side-effect! + (fn [_ [_ {:keys [command chat-id]}]] + (let [{:keys [to-message]} command] + (when to-message + (dispatch [:request-answered! chat-id to-message])))))) + +(register-handler ::invoke-command-handlers! + (u/side-effect! + (fn [db [_ {:keys [chat-id address] :as parameters}]] + (let [{:keys [command content]} (:command parameters) + {:keys [type name]} command + path [(if (= :command type) :commands :responses) + name + :handler] + to (get-in db [:contacts chat-id :address]) + params {:value content + :command {:from address + :to to}}] + (j/call chat-id + path + params + #(dispatch [:command-handler! chat-id parameters %])))))) + +(register-handler ::prepare-message + (u/side-effect! + (fn [db [_ {:keys [chat-id identity message] :as params}]] + (let [message' (cu/check-author-direction + db chat-id + {:msg-id (random/id) + :chat-id chat-id + :content message + :to chat-id + :from identity + :content-type text-content-type + :delivery-status (default-delivery-status chat-id) + :outgoing true + :timestamp (time/now-ms)}) + params' (assoc params :message message')] + (dispatch [::add-message params']) + (dispatch [::save-message! params']) + (dispatch [::send-message! params']))))) + +(register-handler ::add-message + (fn [db [_ {:keys [chat-id message]}]] + (cu/add-message-to-db db chat-id message))) + +(register-handler ::save-message! + (u/side-effect! + (fn [_ [_ {:keys [chat-id message]}]] + (messages/save-message chat-id message)))) + +(register-handler ::send-message! + (u/side-effect! + (fn [db [_ {:keys [message chat-id]}]] + (when (and message (cu/not-console? chat-id)) + (let [{:keys [group-chat]} (get-in db [:chats chat-id]) + message' (select-keys message [:content :msg-id])] + (if group-chat + (api/send-group-user-msg (assoc message' :group-id chat-id)) + (api/send-user-msg (assoc message' :to chat-id)))))))) + +(register-handler ::send-command-protocol! + (u/side-effect! + (fn [db [_ {:keys [chat-id command]}]] + (let [{:keys [content]} command] + (when (cu/not-console? chat-id) + (let [{:keys [group-chat]} (get-in db [:chats chat-id]) + message {:content content + :content-type content-type-command}] + (if group-chat + (api/send-group-user-msg (assoc message :group-id chat-id)) + (api/send-user-msg (assoc message :to chat-id))))))))) diff --git a/src/status_im/chat/screen.cljs b/src/status_im/chat/screen.cljs index e2fc084f96..fa0fc594eb 100644 --- a/src/status_im/chat/screen.cljs +++ b/src/status_im/chat/screen.cljs @@ -123,7 +123,7 @@ (defn actions-list-view [{styles :styles :as platform-specific}] (let [{:keys [group-chat chat-id]} (subscribe [:chat-properties [:group-chat :chat-id]]) - members (subscribe [:current-chat-contacts]) + members (subscribe [:current-chat-contacts]) status-bar-height (get-in styles [:components :status-bar :default :height])] (when-let [actions (if @group-chat [{:title (label :t/members-title) @@ -203,9 +203,10 @@ (label :t/active-unknown)))) (defn toolbar-content [platform-specific] - (let [{:keys [group-chat chat-id name contacts]} (subscribe [:chat-properties [:group-chat :chat-id :name :contacts]]) - contact (subscribe [:get-in [:contacts @chat-id]]) - show-actions (subscribe [:show-actions])] + (let [{:keys [group-chat name contacts chat-id]} + (subscribe [:chat-properties [:group-chat :name :contacts :chat-id]]) + show-actions (subscribe [:show-actions]) + contact (subscribe [:get-in [:contacts @chat-id]])] (fn [] [view (st/chat-name-view @show-actions) [text {:style st/chat-name-text @@ -242,7 +243,7 @@ (defn chat-toolbar [platform-specific] (let [{:keys [group-chat name contacts]} (subscribe [:chat-properties [:group-chat :name :contacts]]) - show-actions (subscribe [:show-actions])] + show-actions (subscribe [:show-actions])] [view [status-bar {:platform-specific platform-specific}] [toolbar {:hide-nav? @show-actions @@ -252,14 +253,15 @@ (defview messages-view [platform-specific group-chat] [messages [:chat :messages] - contacts [:chat :contacts]] + contacts [:chat :contacts] + loaded? [:all-messages-loaded?]] (let [contacts' (contacts-by-identity contacts)] [list-view {:renderRow (message-row {:contact-by-identity contacts' :platform-specific platform-specific :group-chat group-chat :messages-count (count messages)}) :renderScrollComponent #(invertible-scroll-view (js->clj %)) - :onEndReached #(dispatch [:load-more-messages]) + :onEndReached (when-not loaded? #(dispatch [:load-more-messages])) :enableEmptySections true :keyboardShouldPersistTaps true :dataSource (to-datasource-inverted messages)}])) diff --git a/src/status_im/chat/sign_up.cljs b/src/status_im/chat/sign_up.cljs index c721c18b60..6f42aafaca 100644 --- a/src/status_im/chat/sign_up.cljs +++ b/src/status_im/chat/sign_up.cljs @@ -37,7 +37,7 @@ ;; -- Send phone number ---------------------------------------- (defn on-sign-up-response [& [message]] (let [msg-id (random/id)] - (dispatch [:received-msg + (dispatch [:received-message {:msg-id msg-id :content (command-content :confirmation-code @@ -62,7 +62,7 @@ ;; -- Send confirmation code and synchronize contacts--------------------------- (defn on-sync-contacts [] - (dispatch [:received-msg + (dispatch [:received-message {:msg-id (random/id) :content (label :t/contacts-syncronized) :content-type text-content-type @@ -76,7 +76,7 @@ (dispatch [:sync-contacts on-sync-contacts])) (defn on-send-code-response [body] - (dispatch [:received-msg + (dispatch [:received-message {:msg-id (random/id) :content (:message body) :content-type text-content-type @@ -96,47 +96,52 @@ ;; -- Saving password ---------------------------------------- (defn save-password [password] ;; TODO validate and save password - (dispatch [:received-msg + (dispatch [:received-message {:msg-id (random/id) :content (label :t/password-saved) :content-type text-content-type :outgoing false :from "console" - :to "me"}]) - (dispatch [:received-msg + :to "me" + :new? false}]) + (dispatch [:received-message {:msg-id (random/id) :content (label :t/generate-passphrase) :content-type text-content-type :outgoing false :from "console" - :to "me"}]) - (dispatch [:received-msg + :to "me" + :new? false}]) + (dispatch [:received-message {:msg-id (random/id) :content (label :t/passphrase) :content-type text-content-type :outgoing false :from "console" - :to "me"}]) + :to "me" + :new? false}]) ;; TODO generate passphrase (let [passphrase (str "The brash businessman's braggadocio and public squabbing with " "candidates in the US presidential election")] - (dispatch [:received-msg + (dispatch [:received-message {:msg-id (random/id) :content passphrase :content-type text-content-type :outgoing false :from "console" - :to "me"}])) - (dispatch [:received-msg + :to "me" + :new? false}])) + (dispatch [:received-message {:msg-id "8" :content (label :t/written-down) :content-type text-content-type :outgoing false :from "console" - :to "me"}]) + :to "me" + :new? false}]) ;; TODO highlight '!phone' (let [msg-id (random/id)] - (dispatch [:received-msg + (dispatch [:received-message {:msg-id msg-id :content (command-content :phone @@ -157,15 +162,15 @@ :to "me"}) (defn intro [db] - (dispatch [:received-msg intro-status]) - (dispatch [:received-msg + (dispatch [:received-message intro-status]) + (dispatch [:received-message {:msg-id "intro-message1" :content (label :t/intro-message1) :content-type text-content-type :outgoing false :from "console" :to "me"}]) - (dispatch [:received-msg + (dispatch [:received-message {:msg-id "intro-message2" :content (label :t/intro-message2) :content-type text-content-type @@ -173,7 +178,7 @@ :from "console" :to "me"}]) (let [msg-id "into-message3"] - (dispatch [:received-msg + (dispatch [:received-message {:msg-id msg-id :content (command-content :keypair diff --git a/src/status_im/chat/styles/command_validation.cljs b/src/status_im/chat/styles/command_validation.cljs index 2ffb23cc84..90d196b753 100644 --- a/src/status_im/chat/styles/command_validation.cljs +++ b/src/status_im/chat/styles/command_validation.cljs @@ -3,15 +3,16 @@ [status-im.chat.constants :as constants])) (def messages-container - {:background-color :red + {:background-color :#d50000 :height constants/request-info-height :padding-left 16 - :padding-top 14}) + :padding-top 12}) (def title {:color :white - :font-size 12 + :font-size 14 :font-family st/font}) (def description - (assoc title :opacity 0.69)) + (assoc title :opacity 0.9 + :font-size 12)) diff --git a/src/status_im/chat/subs.cljs b/src/status_im/chat/subs.cljs index 18614dd638..81bdbc0c5d 100644 --- a/src/status_im/chat/subs.cljs +++ b/src/status_im/chat/subs.cljs @@ -70,6 +70,7 @@ (fn [db _] (->> [:chats (:current-chat-id @db) :staged-commands] (get-in @db) + vals (reaction)))) (register-sub :valid-plain-message? @@ -232,3 +233,8 @@ 20 :else 0))))) + +(register-sub :all-messages-loaded? + (fn [db] + (let [chat-id (subscribe [:get-current-chat-id])] + (reaction (get-in @db [:chats @chat-id :all-loaded?]))))) diff --git a/src/status_im/chat/suggestions.cljs b/src/status_im/chat/suggestions.cljs index 73f90e884d..4ba362322f 100644 --- a/src/status_im/chat/suggestions.cljs +++ b/src/status_im/chat/suggestions.cljs @@ -3,8 +3,7 @@ [status-im.db :as db] [status-im.models.commands :refer [get-commands get-chat-command-request - get-chat-command-to-msg-id - clear-staged-commands]] + get-chat-command-to-msg-id]] [status-im.utils.utils :refer [log on-error http-get]] [clojure.string :as s])) @@ -39,15 +38,6 @@ (fn [] (command-handler to-msg-id command-key content))))) -(defn apply-staged-commands [db] - (let [staged-commands (get-in db (db/chat-staged-commands-path - (:current-chat-id db)))] - (dorun (map (fn [staged-command] - (when-let [handler (:handler staged-command)] - (handler))) - staged-commands)) - (clear-staged-commands db))) - (defn check-suggestion [db message] (when-let [suggestion-text (when (string? message) (re-matches #"^![^\s]+\s" message))] @@ -60,7 +50,3 @@ (-> db (get-in [:chats (:current-chat-id db) :input-text]) suggestion?)) - -(defn switch-command-suggestions [db] - (let [text (if (typing-command? db) nil "!")] - (assoc-in db [:chats (:current-chat-id db) :input-text] text))) diff --git a/src/status_im/chat/utils.cljs b/src/status_im/chat/utils.cljs new file mode 100644 index 0000000000..66d2d0682d --- /dev/null +++ b/src/status_im/chat/utils.cljs @@ -0,0 +1,27 @@ +(ns status-im.chat.utils) + +(defn console? [s] + (= "console" s)) + +(def not-console? + (complement console?)) + +(defn add-message-to-db + ([db chat-id message] (add-message-to-db db chat-id message true)) + ([db chat-id message new?] + (let [messages [:chats chat-id :messages]] + (update-in db messages conj (assoc message :chat-id chat-id + :new? (if (nil? new?) + true + new?)))))) + +(defn check-author-direction + [db chat-id {:keys [from outgoing] :as message}] + (let [previous-message (first (get-in db [:chats chat-id :messages]))] + (merge message + {:same-author (if previous-message + (= (:from previous-message) from) + true) + :same-direction (if previous-message + (= (:outgoing previous-message) outgoing) + true)}))) diff --git a/src/status_im/chat/views/message.cljs b/src/status_im/chat/views/message.cljs index 2248746dcd..5b5772e941 100644 --- a/src/status_im/chat/views/message.cljs +++ b/src/status_im/chat/views/message.cljs @@ -88,7 +88,7 @@ [text {:style st/command-text :platform-specific platform-specific :font :default} - content])])) + (str content)])])) (defn set-chat-command [msg-id command] (dispatch [:set-response-chat-command msg-id (keyword (:name command))])) @@ -114,7 +114,7 @@ [text {:style (st/text-message message) :platform-specific platform-specific :font :default} - content]]) + (str content)]]) (defmethod message-content text-content-type [wrapper message platform-specific] @@ -213,9 +213,7 @@ :callback anim-callback} on-update (message-container-animation-logic context)] (r/create-class - {:component-did-mount - on-update - :component-did-update + {:component-did-update on-update :reagent-render (fn [message & children] @@ -241,7 +239,8 @@ (dispatch [:send-seen! chat-id msg-id]))) :reagent-render (fn [{:keys [outgoing delivery-status timestamp new-day group-chat] - :as message}] + :as message} + platform-specific] [message-container message ;; TODO there is no new-day info in message (when new-day diff --git a/src/status_im/chat/views/suggestions.cljs b/src/status_im/chat/views/suggestions.cljs index b7f5540a3b..2d84b0b812 100644 --- a/src/status_im/chat/views/suggestions.cljs +++ b/src/status_im/chat/views/suggestions.cljs @@ -72,12 +72,14 @@ [suggestions [:get-suggestions] requests [:get-requests]] [scroll-view {:keyboardShouldPersistTaps true} + ;; todo translations (when (seq requests) [title "Requests"]) (when (seq requests) [view [list-view {:dataSource (to-datasource requests) :keyboardShouldPersistTaps true :renderRow render-request-row}]]) + ;; todo translations [title "Commands"] [view [list-view {:dataSource (to-datasource suggestions) diff --git a/src/status_im/chats_list/views/inner_item.cljs b/src/status_im/chats_list/views/inner_item.cljs index 64462c8a49..1e305b8af3 100644 --- a/src/status_im/chats_list/views/inner_item.cljs +++ b/src/status_im/chats_list/views/inner_item.cljs @@ -12,7 +12,8 @@ [{:keys [chat-id name color new-messages-count online group-chat contacts] :as chat}] [unviewed-messages [:unviewed-messages-count chat-id]] - (let [last-message (first (:messages chat))] + (let [last-message (first (:messages chat)) + name (or name chat-id)] [view st/chat-container [view st/chat-icon-container [chat-icon-view-chat-list chat-id group-chat name color online]] diff --git a/src/status_im/commands/handlers/jail.cljs b/src/status_im/commands/handlers/jail.cljs index 619298694e..a042ca0b79 100644 --- a/src/status_im/commands/handlers/jail.cljs +++ b/src/status_im/commands/handlers/jail.cljs @@ -19,24 +19,37 @@ (assoc-in db [:rendered-commands chat-id message-id] hiccup))) (def console-events - {:save-password #(dispatch [:save-password %]) - :sign-up #(dispatch [:sign-up %]) - :confirm-sign-up #(dispatch [:sign-up-confirm %])}) + {:save-password (fn [[parameter]] + (dispatch [:save-password parameter])) + :sign-up (fn [[parameter]] + (dispatch [:sign-up parameter])) + :confirm-sign-up (fn [[parameter]] + (dispatch [:sign-up-confirm parameter]))}) (def regular-events {}) (defn command-hadler! - [_ [{:keys [to]} {:keys [result]} ]] - (when result - (let [{:keys [event params]} result - events (if (= "console" to) - (merge regular-events console-events) - regular-events)] - (when-let [handler (events (keyword event))] - (apply handler params))))) + [_ [chat-id {:keys [command] :as parameters} {:keys [result error]}]] + (cond + result + (let [{:keys [event params transaction-hash]} result + command' (assoc command :handler-data result) + parameters' (assoc parameters :command command')] + (if transaction-hash + (dispatch [:wait-for-transaction transaction-hash parameters']) + (let [events (if (= "console" chat-id) + (merge regular-events console-events) + regular-events) + parameters'' (if-let [handler (events (keyword event))] + (assoc parameters' :handler #(handler params command')) + parameters')] + (dispatch [:prepare-command! parameters''])))) + (not error) + (dispatch [:prepare-command! parameters]) + :else nil)) (defn suggestions-handler! - [db [{:keys [chat-id]} {:keys [result]} ]] + [db [{:keys [chat-id]} {:keys [result]}]] (let [{:keys [markup webViewUrl]} result hiccup (generate-hiccup markup)] (-> db @@ -52,13 +65,10 @@ nil)) (defn command-preview - [db [chat-id {:keys [result]}]] + [db [chat-id command-id {:keys [result]}]] (if result - (let [path [:chats chat-id :staged-commands] - commands-cnt (count (get-in db path))] - ;; todo (dec commands-cnt) looks like hack have to find better way to - ;; do this - (update-in db (conj path (dec commands-cnt)) assoc + (let [path [:chats chat-id :staged-commands command-id]] + (update-in db path assoc :preview (generate-hiccup result) :preview-string (str result))) db)) @@ -73,18 +83,20 @@ (reg-handler ::render-command render-command) (reg-handler :command-handler! - (after (print-error-message! "Error on command handling")) - (u/side-effect! command-hadler!)) + (after (print-error-message! "Error on command handling")) + (u/side-effect! command-hadler!)) + (reg-handler :suggestions-handler - [(after #(dispatch [:animate-show-response])) - (after (print-error-message! "Error on param suggestions")) - (after (fn [_ [{:keys [command]} {:keys [result]}]] - (when (= :on-send (keyword (:suggestions-trigger command))) - (when (:webViewUrl result) - (dispatch [:set-soft-input-mode :pan])) - (r/dismiss-keyboard!))))] - suggestions-handler!) + [(after #(dispatch [:animate-show-response])) + (after (print-error-message! "Error on param suggestions")) + (after (fn [_ [{:keys [command]} {:keys [result]}]] + (when (= :on-send (keyword (:suggestions-trigger command))) + (when (:webViewUrl result) + (dispatch [:set-soft-input-mode :pan])) + (r/dismiss-keyboard!))))] + suggestions-handler!) (reg-handler :suggestions-event! (u/side-effect! suggestions-events-handler!)) + (reg-handler :command-preview - (after (print-error-message! "Error on command preview")) - command-preview) + (after (print-error-message! "Error on command preview")) + command-preview) diff --git a/src/status_im/commands/handlers/loading.cljs b/src/status_im/commands/handlers/loading.cljs index 569ed20866..acea64c314 100644 --- a/src/status_im/commands/handlers/loading.cljs +++ b/src/status_im/commands/handlers/loading.cljs @@ -1,6 +1,6 @@ (ns status-im.commands.handlers.loading (:require-macros [status-im.utils.slurp :refer [slurp]]) - (:require [re-frame.core :refer [after dispatch subscribe trim-v debug]] + (:require [re-frame.core :refer [path after dispatch subscribe trim-v debug]] [status-im.utils.handlers :as u] [status-im.utils.utils :refer [http-get toast]] [clojure.string :as s] @@ -22,10 +22,13 @@ (defn fetch-commands! [db [identity]] - (when-let [url (:dapp-url (get-in db [:chats identity]))] - (if (= "console" identity) + (when true + ;;when-let [url (:dapp-url (get-in db [:chats identity]))] + ;; todo fix this after demo + (if true + ;(= "console" identity) (dispatch [::validate-hash identity (slurp "resources/commands.js")]) - (http-get (s/join "/" [url commands-js]) + #_(http-get (s/join "/" [url commands-js]) #(dispatch [::validate-hash identity %]) #(dispatch [::loading-failed! identity ::file-was-not-found]))))) @@ -68,8 +71,9 @@ (defn add-commands [db [id _ {:keys [commands responses]}]] (-> db - (update-in [:chats id :commands] merge (mark-as :command commands)) - (update-in [:chats id :responses] merge (mark-as :response responses)))) + (update-in [id :commands] merge (mark-as :command commands)) + (update-in [id :responses] merge (mark-as :response responses)) + (assoc-in [id :commands-loaded] true))) (defn save-commands-js! [_ [id file]] @@ -96,7 +100,8 @@ (reg-handler ::parse-commands! (u/side-effect! parse-commands!)) (reg-handler ::add-commands - (after save-commands-js!) + [(path :chats) + (after save-commands-js!)] add-commands) (reg-handler ::loading-failed! (u/side-effect! loading-failed!)) diff --git a/src/status_im/components/carousel/carousel.cljs b/src/status_im/components/carousel/carousel.cljs index 86eeb23ddf..8c6c329c73 100644 --- a/src/status_im/components/carousel/carousel.cljs +++ b/src/status_im/components/carousel/carousel.cljs @@ -20,8 +20,10 @@ (defn get-active-page [data] (get data :activePage 0)) -(defn get-sneak [data] - (get data :sneak (:sneak defaults))) +(defn get-sneak [{:keys [sneak count] }] + (if (> (or count 2) 1) + (or sneak (:sneak defaults)) + 0)) (defn get-gap [data] (get data :gap (:gap defaults))) @@ -64,7 +66,7 @@ state (reagent.core/state component) page-width (get-page-width state) gap (get-gap state) - page-position (* page (+ page-width gap))] + page-position (+ (* page page-width) (* (- page 1) gap))] (log/debug "go-to-page: props-page-width=" page-width "; gap=" gap "; page-position=" page-position) (scroll-to component page-position 0) diff --git a/src/status_im/components/carousel/styles.cljs b/src/status_im/components/carousel/styles.cljs index a1ccc020bf..3610731870 100644 --- a/src/status_im/components/carousel/styles.cljs +++ b/src/status_im/components/carousel/styles.cljs @@ -14,7 +14,7 @@ {:flex 1}) (defn content-container [sneak gap] - {:paddingLeft (+ sneak (quot gap 2)) + {:paddingLeft (+ 0 (quot gap 2)) :paddingRight (+ sneak (quot gap 2))}) (defn page [page-width margin] diff --git a/src/status_im/components/chat_icon/screen.cljs b/src/status_im/components/chat_icon/screen.cljs index 653131a74f..1fe5a4555d 100644 --- a/src/status_im/components/chat_icon/screen.cljs +++ b/src/status_im/components/chat_icon/screen.cljs @@ -31,9 +31,9 @@ :style st/photo-pencil}]])])) (defview chat-icon-view [chat-id group-chat name online styles] - [photo-path [:chat-photo chat-id]] - [view (:container styles) - (if-not (s/blank? photo-path) + [photo-path [:chat-photo chat-id]] + [view (:container styles) + (if-not (or (s/blank? photo-path) (= chat-id "console")) [chat-icon photo-path styles] [default-chat-icon name styles]) (when-not group-chat diff --git a/src/status_im/components/geth.cljs b/src/status_im/components/geth.cljs index 94141d278f..6d7ca4acaa 100644 --- a/src/status_im/components/geth.cljs +++ b/src/status_im/components/geth.cljs @@ -1,10 +1,20 @@ (ns status-im.components.geth - (:require [status-im.components.react :as r])) + (:require [status-im.components.react :as r] + [re-frame.core :refer [dispatch]])) (def geth (when (exists? (.-NativeModules r/react-native)) (.-Geth (.-NativeModules r/react-native)))) +(defn register-signal-callback [] + (when geth + (.registerSignalEventCallback + geth + #(do (dispatch [:signal-event %]) + (register-signal-callback))))) + +(register-signal-callback) + (defn start-node [on-result on-already-running] (when geth (.startNode geth on-result on-already-running))) @@ -15,4 +25,9 @@ (defn login [address password on-result] (when geth - (.login geth address password on-result))) \ No newline at end of file + (.login geth address password on-result))) + +(defn complete-transaction + [hash callback] + (when geth + (.completeTransaction geth hash callback))) diff --git a/src/status_im/components/styles.cljs b/src/status_im/components/styles.cljs index b534ad840b..dfb7417092 100644 --- a/src/status_im/components/styles.cljs +++ b/src/status_im/components/styles.cljs @@ -79,6 +79,10 @@ {:width 18 :height 18}) +(def icon-close + {:width 12 + :height 12}) + (def form-text-input {:marginLeft -4 :fontSize 14 diff --git a/src/status_im/components/text_field/view.cljs b/src/status_im/components/text_field/view.cljs index 0a3c6e2d6f..8b99815392 100644 --- a/src/status_im/components/text_field/view.cljs +++ b/src/status_im/components/text_field/view.cljs @@ -29,6 +29,7 @@ :lineColor "#0000001f" :focusLineColor "#0000001f" :errorColor "#d50000" + :secureTextEntry false :onFocus #() :onBlur #() :onChangeText #() @@ -144,19 +145,19 @@ label-font-size line-width max-line-width] :as state} (r/state component) - {:keys [wrapperStyle inputStyle lineColor focusLineColor + {:keys [wrapperStyle inputStyle lineColor focusLineColor secureTextEntry labelColor errorColor error label value onFocus onBlur onChangeText onChange editable] :as props} (merge default-props (r/props component)) lineColor (if error errorColor lineColor) focusLineColor (if error errorColor focusLineColor) labelColor (if (and error (not float-label?)) errorColor labelColor) label (if error (str label " *") label)] - ;(log/debug "reagent-render: " data) [view (merge st/text-field-container wrapperStyle) [animated-text {:style (st/label label-top label-font-size labelColor)} label] [text-input {:style (merge st/text-input inputStyle) :placeholder "" :editable editable + :secureTextEntry secureTextEntry :onFocus #(on-focus {:component component :animation {:top label-top :to-top (:label-top config) @@ -192,4 +193,4 @@ :display-name "text-field" :reagent-render reagent-render}] ;(log/debug "Creating text-field component: " data) - (r/create-class component-data))) \ No newline at end of file + (r/create-class component-data))) diff --git a/src/status_im/contacts/handlers.cljs b/src/status_im/contacts/handlers.cljs index 912d16e4d9..2d2b184445 100644 --- a/src/status_im/contacts/handlers.cljs +++ b/src/status_im/contacts/handlers.cljs @@ -78,9 +78,10 @@ (into {}))) (defn add-identity [contacts-by-hash contacts] - (map (fn [{:keys [phone-number-hash whisper-identity]}] + (map (fn [{:keys [phone-number-hash whisper-identity address]}] (let [contact (contacts-by-hash phone-number-hash)] - (assoc contact :whisper-identity whisper-identity))) + (assoc contact :whisper-identity whisper-identity + :address address))) (js->clj contacts))) (defn request-stored-contacts [contacts] diff --git a/src/status_im/contacts/subs.cljs b/src/status_im/contacts/subs.cljs index 969518e1dd..94eda51f17 100644 --- a/src/status_im/contacts/subs.cljs +++ b/src/status_im/contacts/subs.cljs @@ -1,6 +1,7 @@ (ns status-im.contacts.subs (:require-macros [reagent.ratom :refer [reaction]]) - (:require [re-frame.core :refer [register-sub subscribe]])) + (:require [re-frame.core :refer [register-sub subscribe]] + [status-im.utils.identicon :refer [identicon]])) (register-sub :get-contacts (fn [db _] @@ -87,4 +88,6 @@ (if (:group-chat @chat) ;; TODO return group chat icon nil - (:photo-path (first @contacts)))))))) + (if (pos? (count @contacts)) + (:photo-path (first @contacts)) + (identicon chat-id)))))))) diff --git a/src/status_im/db.cljs b/src/status_im/db.cljs index c2bff7494e..563ab6e8d4 100644 --- a/src/status_im/db.cljs +++ b/src/status_im/db.cljs @@ -41,11 +41,10 @@ :keyboard-height 0 :disable-group-creation false :animations {;; todo clear this - :tabs-bar-value (anim/create-value 0)}}) + :tabs-bar-value (anim/create-value 0)} + :loading-allowed true}) (def protocol-initialized-path [:protocol-initialized]) -(defn chat-input-text-path [chat-id] - [:chats chat-id :input-text]) (defn chat-staged-commands-path [chat-id] [:chats chat-id :staged-commands]) (defn chat-command-path [chat-id] diff --git a/src/status_im/handlers.cljs b/src/status_im/handlers.cljs index a3d02f06d5..53412a0640 100644 --- a/src/status_im/handlers.cljs +++ b/src/status_im/handlers.cljs @@ -23,7 +23,8 @@ status-im.qr-scanner.handlers status-im.accounts.handlers status-im.protocol.handlers - [status-im.utils.datetime :as time])) + [status-im.utils.datetime :as time] + status-im.transactions.handlers)) ;; -- Middleware ------------------------------------------------------------ ;; @@ -103,7 +104,7 @@ (dispatch [:crypt-initialized])))))))) (defn node-started [db result] - (log/debug "Started Node: " result)) + (log/debug "Started Node: ")) (register-handler :initialize-geth (u/side-effect! diff --git a/src/status_im/models/chats.cljs b/src/status_im/models/chats.cljs index ce41b09df4..c84677992c 100644 --- a/src/status_im/models/chats.cljs +++ b/src/status_im/models/chats.cljs @@ -101,11 +101,6 @@ (r/single-cljs) (r/list-to-array :contacts))) -(defn chat-by-id2 [chat-id] - (-> (r/get-by-field :account :chat :chat-id chat-id) - r/collection->map - first)) - (defn chat-add-participants [chat-id identities] (r/write :account (fn [] diff --git a/src/status_im/models/commands.cljs b/src/status_im/models/commands.cljs index 41fb7a85e1..39d2e98114 100644 --- a/src/status_im/models/commands.cljs +++ b/src/status_im/models/commands.cljs @@ -1,5 +1,6 @@ (ns status-im.models.commands - (:require [status-im.db :as db])) + (:require [status-im.db :as db] + [tailrecursion.priority-map :refer [priority-map-by]])) (defn get-commands [{:keys [current-chat-id] :as db}] (or (get-in db [:chats current-chat-id :commands]) {})) @@ -37,21 +38,22 @@ [{:keys [current-chat-id] :as db}] (get-in db (db/chat-command-to-msg-id-path current-chat-id))) +(defn compare-commands + [{created-at-1 :created-at} {created-at-2 :created-at}] + (compare created-at-1 created-at-2)) + (defn stage-command - [{:keys [current-chat-id] :as db} command-info] - (update-in db (db/chat-staged-commands-path current-chat-id) - #(if % - (conj % command-info) - [command-info]))) + [{:keys [current-chat-id] :as db} {:keys [id] :as command-info}] + (let [path (db/chat-staged-commands-path current-chat-id) + staged-commands (get-in db path) + staged-coomands' (if (seq staged-commands) + staged-commands + (priority-map-by compare-commands))] + (assoc-in db path (assoc staged-coomands' id command-info)))) -(defn unstage-command [db staged-command] +(defn unstage-command [db {:keys [id]}] (update-in db (db/chat-staged-commands-path (:current-chat-id db)) - (fn [staged-commands] - (filterv #(not= % staged-command) staged-commands)))) - -(defn clear-staged-commands - [{:keys [current-chat-id] :as db}] - (assoc-in db (db/chat-staged-commands-path current-chat-id) [])) + dissoc id)) (defn get-chat-command-request [{:keys [current-chat-id] :as db}] diff --git a/src/status_im/models/messages.cljs b/src/status_im/models/messages.cljs index e2b1f2c5e8..bbe240bac9 100644 --- a/src/status_im/models/messages.cljs +++ b/src/status_im/models/messages.cljs @@ -57,9 +57,13 @@ (r/collection->map)) (into '()) reverse - (keep (fn [{:keys [content-type] :as message}] + (keep (fn [{:keys [content-type preview] :as message}] (if (command-type? content-type) - (update message :content str-to-map) + (-> message + (update :content str-to-map) + (assoc :rendered-preview + (when preview + (generate-hiccup (read-string preview))))) message)))))) (defn update-message! [{:keys [msg-id] :as msg}] @@ -68,3 +72,7 @@ (fn [] (when (r/exists? :account :message :msg-id msg-id) (r/create :account :message msg true))))) + +(defn get-message [id] + (r/get-one-by-field :account :message :msg-id id)) + diff --git a/src/status_im/navigation/handlers.cljs b/src/status_im/navigation/handlers.cljs index 25767b3b6d..270738d551 100644 --- a/src/status_im/navigation/handlers.cljs +++ b/src/status_im/navigation/handlers.cljs @@ -25,8 +25,10 @@ (register-handler :navigate-to (enrich preload-data!) - (fn [db [_ view-id]] - (push-view db view-id))) + (fn [{:keys [view-id] :as db} [_ new-view-id]] + (if (= view-id new-view-id) + db + (push-view db new-view-id)))) (register-handler :navigation-replace (enrich preload-data!) diff --git a/src/status_im/protocol/handlers.cljs b/src/status_im/protocol/handlers.cljs index 182752e46d..a892b84e44 100644 --- a/src/status_im/protocol/handlers.cljs +++ b/src/status_im/protocol/handlers.cljs @@ -128,3 +128,8 @@ (after (fn [_ [_ chat-id]] (dispatch [:remove-unviewed-messages chat-id])))] (update-message-status :seen)) + +(register-handler :send-transaction! + (u/side-effect! + (fn [_ [_ amount message]] + (println :send-transacion! amount message)))) diff --git a/src/status_im/protocol/protocol_handler.cljs b/src/status_im/protocol/protocol_handler.cljs index 4bde5d223b..2658535fc1 100644 --- a/src/status_im/protocol/protocol_handler.cljs +++ b/src/status_im/protocol/protocol_handler.cljs @@ -14,12 +14,11 @@ :active-group-ids (active-group-chats) :storage kv/kv-store :handler (fn [{:keys [event-type] :as event}] - (log/info "Event:" (clj->js event)) (case event-type :initialized (let [{:keys [identity]} event] (dispatch [:protocol-initialized identity])) :new-msg (let [{:keys [from to payload]} event] - (dispatch [:received-msg (assoc payload :from from :to to)])) + (dispatch [:received-message (assoc payload :from from :to to)])) :msg-acked (let [{:keys [msg-id from]} event] (dispatch [:acked-msg from msg-id])) :msg-seen (let [{:keys [msg-id from]} event] diff --git a/src/status_im/qr_scanner/views/import-button.cljs b/src/status_im/qr_scanner/views/import_button.cljs similarity index 100% rename from src/status_im/qr_scanner/views/import-button.cljs rename to src/status_im/qr_scanner/views/import_button.cljs diff --git a/src/status_im/qr_scanner/views/scan-button.cljs b/src/status_im/qr_scanner/views/scan_button.cljs similarity index 100% rename from src/status_im/qr_scanner/views/scan-button.cljs rename to src/status_im/qr_scanner/views/scan_button.cljs diff --git a/src/status_im/subs.cljs b/src/status_im/subs.cljs index a79747cb2d..5f4a8edd39 100644 --- a/src/status_im/subs.cljs +++ b/src/status_im/subs.cljs @@ -6,7 +6,8 @@ status-im.discovery.subs status-im.contacts.subs status-im.new-group.subs - status-im.participants.subs)) + status-im.participants.subs + status-im.transactions.subs)) (register-sub :get (fn [db [_ k]] diff --git a/src/status_im/transactions/handlers.cljs b/src/status_im/transactions/handlers.cljs new file mode 100644 index 0000000000..ed686683cc --- /dev/null +++ b/src/status_im/transactions/handlers.cljs @@ -0,0 +1,119 @@ +(ns status-im.transactions.handlers + (:require [re-frame.core :refer [after dispatch debug enrich]] + [status-im.utils.handlers :refer [register-handler]] + [status-im.navigation.handlers :as nav] + [status-im.utils.handlers :as u] + [status-im.utils.types :as t] + [status-im.components.geth :as g] + cljsjs.web3 + [clojure.string :as s])) + +(defmethod nav/preload-data! :confirm + [{:keys [transactions-queue] :as db} _] + (assoc db :transactions transactions-queue)) + +(defn on-unlock [hashes] + (fn [result-str] + (let [{:keys [error]} (t/json->clj result-str)] + ;; todo: add message about wrong password + (if (s/blank? error) + (do + (dispatch [:set :wrong-password? false]) + (doseq [hash hashes] + (g/complete-transaction + hash + #(dispatch [:transaction-completed hash %]))) + (dispatch [:navigate-back])) + (dispatch [:set :wrong-password? true]))))) + +(register-handler :accept-transactions + (u/side-effect! + (fn [{:keys [transactions current-account-id]} [_ password]] + (let [hashes (keys transactions)] + (g/login current-account-id password (on-unlock hashes)))))) + +(register-handler :deny-transactions + (u/side-effect! + (fn [{:keys [transactions]}] + (let [hashes (keys transactions)] + (dispatch [::remove-pending-messages hashes]) + (dispatch [::remove-trqqansactions hashes]) + (dispatch [:navigate-back]))))) + +(register-handler :deny-transaction + (u/side-effect! + (fn [_ [_ hash]] + (dispatch [::remove-pending-message hash]) + (dispatch [::remove-transaction hash])))) + +(register-handler ::remove-transactions + (fn [db [_ hashes]] + (-> db + (dissoc :transactions) + (update :transactions-queue #(apply dissoc % hashes))))) + +(register-handler ::remove-transaction + (fn [db [_ hash]] + (-> db + (update :transactions dissoc hash) + (update :transactions-queue dissoc hash)))) + +(register-handler :wait-for-transaction + (fn [db [_ hash {:keys [chat-id command] :as params}]] + (let [id (:id command)] + (-> db + (update-in [:chats chat-id :staged-commands id] assoc :pending true) + (assoc-in [:transaction-subscribers hash] params))))) + +(defn remove-pending-message [db hash] + (let [{:keys [chat-id command]} (get-in db [:transaction-subscribers hash]) + path [:chats chat-id :staged-commands]] + (-> db + (update :transaction-subscribers dissoc hash) + (update-in path dissoc (:id command))))) + +(register-handler ::remove-pending-messages + (fn [db [_ hashes]] + (reduce remove-pending-message db hashes))) + +(register-handler ::remove-pending-message + (fn [db [_ hash]] + (remove-pending-message db hash))) + +(register-handler :signal-event + (u/side-effect! + (fn [_ [_ event-str]] + (let [{:keys [type event]} (t/json->clj event-str)] + (case type + "sendTransactionQueued" (dispatch [:transaction-queued event])))))) + +(register-handler :transaction-queued + (after #(dispatch [:navigate-to :confirm])) + (fn [db [_ {:keys [hash args]}]] + (let [{:keys [from to value]} args + transaction {:hash hash + :from from + :to to + :value (.toDecimal js/Web3.prototype value)}] + (assoc-in db [:transactions-queue hash] transaction)))) + +(register-handler :transaction-completed + (u/side-effect! + (fn [db [_ old-hash result-str]] + (let [{:keys [hash error]} (t/json->clj result-str)] + ;; todo: handle error + (when hash + (dispatch [::send-pending-message old-hash hash]) + (dispatch [::remove-transaction old-hash])))))) + +(register-handler ::send-pending-message + (u/side-effect! + (fn [{:keys [transaction-subscribers] :as db} [_ old-hash new-hash]] + (when-let [params (transaction-subscribers old-hash)] + (let [params' (assoc-in params [:handler-data :transaction-hash] new-hash)] + (dispatch [:prepare-command! params'])) + (dispatch [::remove-transaction-subscriber old-hash]))))) + +(register-handler ::remove-transaction-subscriber + (fn [db [_ old-hash]] + (update db :transaction-subscribers dissoc old-hash))) diff --git a/src/status_im/transactions/screen.cljs b/src/status_im/transactions/screen.cljs new file mode 100644 index 0000000000..bcce69049d --- /dev/null +++ b/src/status_im/transactions/screen.cljs @@ -0,0 +1,62 @@ +(ns status-im.transactions.screen + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch]] + [status-im.components.react :refer [view + text + image + icon + scroll-view + touchable-highlight + touchable-opacity]] + [status-im.components.styles :refer [icon-ok + icon-close + toolbar-title-container]] + [status-im.components.carousel.carousel :refer [carousel]] + [status-im.components.toolbar :refer [toolbar]] + [status-im.components.text-field.view :refer [text-field]] + [status-im.transactions.views.transaction-page :refer [transaction-page]] + [status-im.transactions.styles :as st] + [status-im.i18n :refer [label label-pluralize]] + [clojure.string :as s])) + + +(defview confirm [] + [transactions [:transactions] + {:keys [password]} [:get :confirm-transactions] + wrong-password? [:wrong-password?]] + [view st/transactions-screen + [toolbar + {:style st/transactions-toolbar + :nav-action {:image {:source {:uri :icon_close_white} + :style icon-close} + :handler #(dispatch [:deny-transactions])} + :custom-content [view {:style toolbar-title-container} + [text {:style st/toolbar-title-text} + (label-pluralize (count transactions) :t/confirm-transactions)]] + :action {:image {:source {:uri (if-not (s/blank? password) + :icon_ok + :icon_ok_disabled_inversed)} + :style icon-ok} + :handler #(dispatch [:accept-transactions password])}}] + [view st/carousel-container + [carousel {:pageStyle st/carousel-page-style + :gap 16 + :count (count transactions) + :sneak 20} + (when transactions + (for [transaction transactions] + [transaction-page transaction]))]] + [view st/form-container + [text-field + {:inputStyle st/password-style + :secureTextEntry true + :error (when wrong-password? (label :t/wrong-password)) + :errorColor :#ffffff80 #_:#7099e6 + :lineColor :white + :labelColor :#ffffff80 + :value password + :label (label :t/password) + :onChangeText #(dispatch [:set-in [:confirm-transactions :password] %])}]]]) + + +;(re-frame.core/dispatch [:set :view-id :confirm]) diff --git a/src/status_im/transactions/styles.cljs b/src/status_im/transactions/styles.cljs new file mode 100644 index 0000000000..1b105fedac --- /dev/null +++ b/src/status_im/transactions/styles.cljs @@ -0,0 +1,107 @@ +(ns status-im.transactions.styles + (:require [status-im.components.styles :refer [toolbar-height + color-white]])) + + +(def transactions-screen + {:flex 1 + :backgroundColor "#828b92"}) + +(def transactions-toolbar + {:backgroundColor "#828b92" + :elevation 0}) + +(def toolbar-title-text + {:color :white + :fontSize 16}) + +(def carousel-page-style + {}) + +(def form-container + {:flex 1 + :paddingLeft 16}) + +(def password-style + {:color :white}) + +;transaction-page + +(def transaction-page + {:flex 1 + :backgroundColor "#f3f4f4"}) + +(def title-bar + {:backgroundColor :white + :height 39 + :justifyContent :center}) + +(def title-bar-text + {:fontFamily "sans-serif-medium" + :color "#838c93" + :fontSize 13 + :marginLeft 12}) + +(def icon-close-container + {:position :absolute + :right 12 + :top 13}) + +(def icon-close + {:width 12 + :height 12}) + +(def transaction-info-container + {:flex 1 + :paddingTop 6}) + +(def scroll-view-container + {:flex 1}) + +(def scroll-view + {:flex 1 + :height 175}) + +(def scroll-view-content + {:paddingVertical 6}) + +(def transaction-info-row + {:flex 1 + :flexDirection :row + :height 20 + }) + +(def transaction-info-column-title + {:flex 0.4 + :flexDirection :column + :paddingHorizontal 6}) + +(def transaction-info-column-value + {:flex 0.6 + :flexDirection :column + :paddingHorizontal 6}) + +(def transaction-info-item + {:flex 1 + :padding 6}) + +(def transaction-info-title + {:textAlign :right + :color "#838c93de" + :fontSize 14 + :lineHeight 20}) + +(def transaction-info-value + {:color "#000000de" + :fontSize 14 + :lineHeight 20 +}) + +(def scroll-view-item + {:flex 1 + :height 20 + :padding 6 }) + +(def carousel-container + {:min-height 215 + :flex 1}) diff --git a/src/status_im/transactions/subs.cljs b/src/status_im/transactions/subs.cljs new file mode 100644 index 0000000000..5240d0575d --- /dev/null +++ b/src/status_im/transactions/subs.cljs @@ -0,0 +1,28 @@ +(ns status-im.transactions.subs + (:require-macros [reagent.ratom :refer [reaction]]) + (:require [re-frame.core :refer [register-sub subscribe]] + [clojure.string :as s])) + +(register-sub :transactions + (fn [db] + (reaction (vals (:transactions @db))))) + +(register-sub :contacts-by-address + (fn [db] + (reaction (into {} (map (fn [[_ {:keys [address] :as contact}]] + [address contact]) + + (:contacts @db) + ))))) + +(register-sub :contact-by-address + (fn [_ [_ address]] + (let [contacts (subscribe [:contacts-by-address]) + address' (when address + (if (s/starts-with? address "0x") + (subs address 2) + address))] + (reaction (@contacts address'))))) + +(register-sub :wrong-password? + (fn [db] (reaction (:wrong-password? @db)))) diff --git a/src/status_im/transactions/views/transaction_page.cljs b/src/status_im/transactions/views/transaction_page.cljs new file mode 100644 index 0000000000..016de5b7b1 --- /dev/null +++ b/src/status_im/transactions/views/transaction_page.cljs @@ -0,0 +1,49 @@ +(ns status-im.transactions.views.transaction-page + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch]] + [status-im.components.react :refer [view + text + image + icon + scroll-view + touchable-highlight + touchable-opacity]] + [status-im.components.styles :refer [icon-ok + icon-close]] + [status-im.transactions.styles :as st] + [status-im.i18n :refer [label label-pluralize]] + cljsjs.web3)) + +(defn title-bar [title hash] + [view st/title-bar + [text {:style st/title-bar-text} title] + [touchable-highlight {:style st/icon-close-container + :on-press #(dispatch [:deny-transaction hash])} + [view [image {:source {:uri :icon_close_gray} + :style st/icon-close}]]]]) + +(defn transaction-info [index [name value]] + [view {:style st/transaction-info-item + :key index} + [view {:style st/transaction-info-row} + [view st/transaction-info-column-title + [text {:style st/transaction-info-title} name]] + [view st/transaction-info-column-value + [text {:style st/transaction-info-value} value]]]]) + +(defview transaction-page [{:keys [hash from to value] :as transaction}] + [{:keys [name] :as contact} [:contact-by-address to]] + (let [eth-value (.fromWei js/Web3.prototype value "ether") + title (str eth-value " ETH to " name) + transactions-info [[(label :t/status) (label :t/pending-confirmation)] + [(label :t/recipient) name] + [(label :t/value) (str eth-value " ETH")]]] + [view {:style st/transaction-page + :key hash} + [title-bar title hash] + [view st/scroll-view-container + [scroll-view {:style st/scroll-view + :contentContainerStyle st/scroll-view-content + :showsVerticalScrollIndicator true + :scrollEnabled true} + (map-indexed transaction-info transactions-info)]]])) diff --git a/src/status_im/translations/en.cljs b/src/status_im/translations/en.cljs index a21e6cd797..07187c9ce6 100644 --- a/src/status_im/translations/en.cljs +++ b/src/status_im/translations/en.cljs @@ -160,9 +160,22 @@ :login "Login" :wrong-password "Wrong password" - ;users + ;accounts :add-account "Add account" ;validation :invalid-phone "Invalid phone number" + :amount "Amount" + :not-enough-eth (str "Not enough ETH on balance " + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "Confirm transaction" + :other "Confirm {{count}} transactions" + :zero "No transactions"} + :status "Status" + :pending-confirmation "Pending confirmation" + :recipient "Recipient" + :one-more-item "One more item" + :fee "Fee" + :value "Value" }) diff --git a/src/status_im/utils/datetime.cljs b/src/status_im/utils/datetime.cljs index 9dd4468820..01748b8ef2 100644 --- a/src/status_im/utils/datetime.cljs +++ b/src/status_im/utils/datetime.cljs @@ -4,15 +4,17 @@ [cljs-time.format :refer [formatters formatter unparse]] - [status-im.i18n :refer [label label-pluralize]])) + [status-im.i18n :refer [label label-pluralize]] + [goog.string :as gstring] + goog.string.format)) (def hour (* 1000 60 60)) (def day (* hour 24)) (def week (* 7 day)) -(def units [{:name (label :t/datetime-second) :limit 60 :in-second 1} - {:name (label :t/datetime-minute) :limit 3600 :in-second 60} - {:name (label :t/datetime-hour) :limit 86400 :in-second 3600} - {:name (label :t/datetime-day) :limit nil :in-second 86400}]) +(def units [{:name :t/datetime-second :limit 60 :in-second 1} + {:name :t/datetime-minute :limit 3600 :in-second 60} + {:name :t/datetime-hour :limit 86400 :in-second 3600} + {:name :t/datetime-day :limit nil :in-second 86400}]) (def time-zone-offset (hours (- (/ (.getTimezoneOffset (js/Date.)) 60)))) @@ -29,6 +31,10 @@ (before? local today) (label :t/datetime-yesterday) :else (unparse (formatters :hour-minute) local)))) +(defn format-time-ago [diff unit] + (let [name (label-pluralize diff (:name unit))] + (gstring/format "%s %s %s" diff name (label :t/datetime-ago)))) + (defn time-ago [time] (let [diff (t/in-seconds (t/interval time (t/now)))] (if (< diff 60) @@ -39,7 +45,7 @@ (-> (/ diff (:in-second unit)) Math/floor int - (#(str % " " (label-pluralize % (:name unit)) " " (label :t/datetime-ago)))))))) + (format-time-ago unit)))))) (defn to-date [ms] (from-long ms)) diff --git a/src/status_im/utils/handlers.cljs b/src/status_im/utils/handlers.cljs index 083196375e..f5d5006d1a 100644 --- a/src/status_im/utils/handlers.cljs +++ b/src/status_im/utils/handlers.cljs @@ -1,5 +1,6 @@ (ns status-im.utils.handlers - (:require [re-frame.core :refer [after dispatch debug] :as re-core])) + (:require [re-frame.core :refer [after dispatch debug] :as re-core] + [re-frame.utils :refer [log]])) (defn side-effect! "Middleware for handlers that will not affect db." @@ -8,7 +9,18 @@ (handler db params) db)) +(defn debug-handlers-names + "Middleware which logs debug information to js/console for each event. + Includes a clojure.data/diff of the db, before vs after, showing the changes + caused by the event." + [handler] + (fn debug-handler + [db v] + (log "Handling re-frame event: " (first v)) + (let [new-db (handler db v)] + new-db))) + (defn register-handler ([name handler] (register-handler name nil handler)) ([name middleware handler] - (re-core/register-handler name [#_debug middleware] handler))) + (re-core/register-handler name [debug-handlers-names middleware] handler)))