Merge remote-tracking branch 'origin/develop' into feature/#124
|
@ -17,8 +17,11 @@
|
||||||
"dismissKeyboard",
|
"dismissKeyboard",
|
||||||
"react-native-linear-gradient",
|
"react-native-linear-gradient",
|
||||||
"react-native-android-sms-listener",
|
"react-native-android-sms-listener",
|
||||||
|
"react-native-status",
|
||||||
"react-native-camera",
|
"react-native-camera",
|
||||||
"react-native-qrcode"
|
"react-native-qrcode",
|
||||||
|
"react-native-orientation",
|
||||||
|
"identicon.js"
|
||||||
],
|
],
|
||||||
"imageDirs": [
|
"imageDirs": [
|
||||||
"images"
|
"images"
|
||||||
|
|
|
@ -131,8 +131,9 @@ dependencies {
|
||||||
compile project(':react-native-linear-gradient')
|
compile project(':react-native-linear-gradient')
|
||||||
compile project(':ReactNativeAndroidSmsListener')
|
compile project(':ReactNativeAndroidSmsListener')
|
||||||
compile project(':react-native-camera')
|
compile project(':react-native-camera')
|
||||||
// compile(name:'geth', ext:'aar')
|
compile project(':react-native-status')
|
||||||
compile(group: 'status-im', name: 'android-geth', version: '1.4.0-201604110816-a97a114', ext: 'aar')
|
compile project(':react-native-orientation')
|
||||||
|
compile(group: 'status-im', name: 'status-go', version: '0.1.0-201606301634-5d7b29', ext: 'aar')
|
||||||
|
|
||||||
compile fileTree(dir: "node_modules/realm/android/libs", include: ["*.jar"])
|
compile fileTree(dir: "node_modules/realm/android/libs", include: ["*.jar"])
|
||||||
}
|
}
|
||||||
|
@ -142,3 +143,4 @@ task copyDownloadableDepsToLibs(type: Copy) {
|
||||||
from configurations.compile
|
from configurations.compile
|
||||||
into 'libs'
|
into 'libs'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,11 @@
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
<service
|
||||||
|
android:name=".GethService"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="true"
|
||||||
|
android:process=":geth_process"/>
|
||||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
package com.statusim;
|
||||||
|
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.os.Messenger;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.os.Environment;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
|
import com.github.status_im.status_go.Statusgo;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
static class IncomingHandler extends Handler {
|
||||||
|
|
||||||
|
private final WeakReference<GethService> service;
|
||||||
|
|
||||||
|
IncomingHandler(GethService service) {
|
||||||
|
|
||||||
|
this.service = new WeakReference<GethService>(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message message) {
|
||||||
|
|
||||||
|
GethService service = this.service.get();
|
||||||
|
if (service != null) {
|
||||||
|
if (!service.handleMessage(message)) {
|
||||||
|
super.handleMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final Messenger serviceMessenger = new Messenger(new IncomingHandler(this));
|
||||||
|
|
||||||
|
protected class StartTask extends AsyncTask<Void, Void, Void> {
|
||||||
|
|
||||||
|
public StartTask() {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Void doInBackground(Void... args) {
|
||||||
|
startGeth();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onPostExecute(Void results) {
|
||||||
|
onGethStarted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onGethStarted() {
|
||||||
|
Log.d(TAG, "Geth Service started");
|
||||||
|
isGethStarted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void startGeth() {
|
||||||
|
Log.d(TAG, "Starting background Geth Service");
|
||||||
|
|
||||||
|
File extStore = Environment.getExternalStorageDirectory();
|
||||||
|
|
||||||
|
final String dataFolder = extStore.exists() ?
|
||||||
|
extStore.getAbsolutePath() :
|
||||||
|
getApplicationInfo().dataDir;
|
||||||
|
|
||||||
|
new Thread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
Statusgo.StartNode(dataFolder);
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void signalEvent(String jsonEvent) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
return serviceMessenger.getBinder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
System.loadLibrary("statusgo");
|
||||||
|
|
||||||
|
if (!isGethInitialized) {
|
||||||
|
isGethInitialized = true;
|
||||||
|
new StartTask().execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
//TODO: stop geth
|
||||||
|
isGethStarted = false;
|
||||||
|
isGethInitialized = false;
|
||||||
|
Log.d(TAG, "Geth Service stopped !");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
|
return Service.START_STICKY;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean handleMessage(Message message) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isRunning() {
|
||||||
|
return isGethInitialized;
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,70 +7,104 @@ import com.facebook.react.ReactPackage;
|
||||||
import com.facebook.react.shell.MainReactPackage;
|
import com.facebook.react.shell.MainReactPackage;
|
||||||
import com.rt2zz.reactnativecontacts.ReactNativeContacts;
|
import com.rt2zz.reactnativecontacts.ReactNativeContacts;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Environment;
|
import android.os.Handler;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.os.Messenger;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.DialogInterface.OnClickListener;
|
import android.content.DialogInterface.OnClickListener;
|
||||||
import android.content.DialogInterface.OnCancelListener;
|
import android.content.DialogInterface.OnCancelListener;
|
||||||
import com.github.ethereum.go_ethereum.cmd.Geth;
|
import android.content.ComponentName;
|
||||||
|
import android.content.ServiceConnection;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
import com.bitgo.randombytes.RandomBytesPackage;
|
import com.bitgo.randombytes.RandomBytesPackage;
|
||||||
import com.BV.LinearGradient.LinearGradientPackage;
|
import com.BV.LinearGradient.LinearGradientPackage;
|
||||||
import com.centaurwarchief.smslistener.SmsListener;
|
import com.centaurwarchief.smslistener.SmsListener;
|
||||||
|
import com.github.yamill.orientation.OrientationPackage;
|
||||||
|
|
||||||
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import com.statusim.Jail.JailPackage;
|
||||||
|
|
||||||
import com.lwansbrough.RCTCamera.*;
|
import com.lwansbrough.RCTCamera.*;
|
||||||
import com.i18n.reactnativei18n.ReactNativeI18n;
|
import com.i18n.reactnativei18n.ReactNativeI18n;
|
||||||
import io.realm.react.RealmReactPackage;
|
import io.realm.react.RealmReactPackage;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
|
||||||
public class MainActivity extends ReactActivity {
|
public class MainActivity extends ReactActivity {
|
||||||
|
|
||||||
final Handler handler = new Handler();
|
private static final String TAG = "MainActivity";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Incoming message handler. Calls to its binder are sequential!
|
||||||
|
*/
|
||||||
|
protected final IncomingHandler handler = new IncomingHandler();
|
||||||
|
|
||||||
|
/** Flag indicating if the service is bound. */
|
||||||
|
protected boolean isBound;
|
||||||
|
|
||||||
|
/** Sends messages to the service. */
|
||||||
|
protected Messenger serviceMessenger = null;
|
||||||
|
|
||||||
|
/** Receives messages from the service. */
|
||||||
|
protected Messenger clientMessenger = new Messenger(handler);
|
||||||
|
|
||||||
|
class IncomingHandler extends Handler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message message) {
|
||||||
|
boolean isClaimed = false;
|
||||||
|
Log.d(TAG, "!!!!!!!!!!!!!! Received Service Message !!!!!!!!!!!!!!");
|
||||||
|
super.handleMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ServiceConnection serviceConnection = new ServiceConnection() {
|
||||||
|
|
||||||
|
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||||
|
// This is called when the connection with the service has been
|
||||||
|
// established, giving us the object we can use to
|
||||||
|
// interact with the service. We are communicating with the
|
||||||
|
// service using a Messenger, so here we get a client-side
|
||||||
|
// representation of that from the raw IBinder object.
|
||||||
|
serviceMessenger = new Messenger(service);
|
||||||
|
isBound = true;
|
||||||
|
onConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onServiceDisconnected(ComponentName className) {
|
||||||
|
// This is called when the connection with the service has been
|
||||||
|
// unexpectedly disconnected -- that is, its process crashed.
|
||||||
|
serviceMessenger = null;
|
||||||
|
isBound = false;
|
||||||
|
Log.d(TAG, "!!!!!!!!!!!!!! Geth Service Disconnected !!!!!!!!!!!!!!");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
protected void onConnected() {
|
||||||
|
Log.d(TAG, "!!!!!!!!!!!!!! Geth Service Connected !!!!!!!!!!!!!!");
|
||||||
|
}
|
||||||
|
|
||||||
protected void startStatus() {
|
protected void startStatus() {
|
||||||
// Required because of crazy APN settings redirecting localhost (found in GB)
|
// Required because of crazy APN settings redirecting localhost (found in GB)
|
||||||
Properties properties = System.getProperties();
|
Properties properties = System.getProperties();
|
||||||
properties.setProperty("http.nonProxyHosts", "localhost|127.0.0.1");
|
properties.setProperty("http.nonProxyHosts", "localhost|127.0.0.1");
|
||||||
properties.setProperty("https.nonProxyHosts", "localhost|127.0.0.1");
|
properties.setProperty("https.nonProxyHosts", "localhost|127.0.0.1");
|
||||||
|
|
||||||
File extStore = Environment.getExternalStorageDirectory();
|
|
||||||
|
|
||||||
final String dataFolder = extStore.exists() ?
|
|
||||||
extStore.getAbsolutePath() :
|
|
||||||
getApplicationInfo().dataDir;
|
|
||||||
|
|
||||||
// Launch!
|
|
||||||
final Runnable addPeer = new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
Log.w("Geth", "adding peer");
|
|
||||||
Geth.run("--exec admin.addPeer(\"enode://e2f28126720452aa82f7d3083e49e6b3945502cb94d9750a15e27ee310eed6991618199f878e5fbc7dfa0e20f0af9554b41f491dc8f1dbae8f0f2d37a3a613aa@139.162.13.89:55555\") attach http://localhost:8545");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
new Thread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
Geth.run("--shh --ipcdisable --nodiscover --rpc --rpcapi db,eth,net,web3,shh,admin --fast --datadir=" + dataFolder);
|
|
||||||
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
handler.postDelayed(addPeer, 5000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
// Required for android-16 (???)
|
|
||||||
// Crash if put in startStatus() ?
|
|
||||||
System.loadLibrary("gethraw");
|
|
||||||
System.loadLibrary("geth");
|
|
||||||
|
|
||||||
if(!RootUtil.isDeviceRooted()) {
|
if(!RootUtil.isDeviceRooted()) {
|
||||||
startStatus();
|
startStatus();
|
||||||
} else {
|
} else {
|
||||||
|
@ -96,9 +130,27 @@ public class MainActivity extends ReactActivity {
|
||||||
}).create();
|
}).create();
|
||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
|
Intent intent = new Intent(this, GethService.class);
|
||||||
|
if (!GethService.isRunning()) {
|
||||||
|
startService(intent);
|
||||||
|
}
|
||||||
|
if (serviceConnection != null && GethService.isRunning()) {
|
||||||
|
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
try {
|
||||||
|
unbindService(serviceConnection);
|
||||||
|
}
|
||||||
|
catch (Throwable t) {
|
||||||
|
Log.e(TAG, "Failed to unbind from the geth service", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the name of the main component registered from JavaScript.
|
* Returns the name of the main component registered from JavaScript.
|
||||||
* This is used to schedule rendering of the component.
|
* This is used to schedule rendering of the component.
|
||||||
|
@ -125,6 +177,7 @@ public class MainActivity extends ReactActivity {
|
||||||
protected List<ReactPackage> getPackages() {
|
protected List<ReactPackage> getPackages() {
|
||||||
return Arrays.<ReactPackage>asList(
|
return Arrays.<ReactPackage>asList(
|
||||||
new MainReactPackage(),
|
new MainReactPackage(),
|
||||||
|
new JailPackage(),
|
||||||
new RealmReactPackage(),
|
new RealmReactPackage(),
|
||||||
new VectorIconsPackage(),
|
new VectorIconsPackage(),
|
||||||
new ReactNativeContacts(),
|
new ReactNativeContacts(),
|
||||||
|
@ -132,7 +185,16 @@ public class MainActivity extends ReactActivity {
|
||||||
new RandomBytesPackage(),
|
new RandomBytesPackage(),
|
||||||
new LinearGradientPackage(),
|
new LinearGradientPackage(),
|
||||||
new RCTCameraPackage(),
|
new RCTCameraPackage(),
|
||||||
new SmsListener(this)
|
new SmsListener(this),
|
||||||
|
new OrientationPackage(this)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConfigurationChanged(Configuration newConfig) {
|
||||||
|
super.onConfigurationChanged(newConfig);
|
||||||
|
Intent intent = new Intent("onConfigurationChanged");
|
||||||
|
intent.putExtra("newConfig", newConfig);
|
||||||
|
this.sendBroadcast(intent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
After Width: | Height: | Size: 253 B |
After Width: | Height: | Size: 776 B |
After Width: | Height: | Size: 151 B |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 469 B |
After Width: | Height: | Size: 201 B |
After Width: | Height: | Size: 481 B |
After Width: | Height: | Size: 120 B |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 247 B |
After Width: | Height: | Size: 329 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 145 B |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 333 B |
After Width: | Height: | Size: 401 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 187 B |
After Width: | Height: | Size: 5.1 KiB |
After Width: | Height: | Size: 651 B |
After Width: | Height: | Size: 546 B |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 223 B |
After Width: | Height: | Size: 6.9 KiB |
After Width: | Height: | Size: 823 B |
|
@ -13,10 +13,16 @@ project(':react-native-vector-icons').projectDir = new File(rootProject.projectD
|
||||||
include ':realm'
|
include ':realm'
|
||||||
project(':realm').projectDir = new File(rootProject.projectDir, '../node_modules/realm/android')
|
project(':realm').projectDir = new File(rootProject.projectDir, '../node_modules/realm/android')
|
||||||
include ':randombytes'
|
include ':randombytes'
|
||||||
project(':randombytes').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-randombytes/app')
|
project(':randombytes').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-randombytes/android')
|
||||||
include ':react-native-linear-gradient'
|
include ':react-native-linear-gradient'
|
||||||
project(':react-native-linear-gradient').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-linear-gradient/android')
|
project(':react-native-linear-gradient').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-linear-gradient/android')
|
||||||
include ':ReactNativeAndroidSmsListener'
|
include ':ReactNativeAndroidSmsListener'
|
||||||
project(':ReactNativeAndroidSmsListener').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-android-sms-listener/android')
|
project(':ReactNativeAndroidSmsListener').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-android-sms-listener/android')
|
||||||
|
|
||||||
|
include ':react-native-status'
|
||||||
|
project(':react-native-status').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-status/android')
|
||||||
include ':react-native-camera'
|
include ':react-native-camera'
|
||||||
project(':react-native-camera').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-camera/android')
|
project(':react-native-camera').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-camera/android')
|
||||||
|
|
||||||
|
include ':react-native-orientation', ':app'
|
||||||
|
project(':react-native-orientation').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-orientation/android')
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"awesome-phonenumber": "^1.0.13",
|
"awesome-phonenumber": "^1.0.13",
|
||||||
|
"identicon.js": "github:status-im/identicon.js",
|
||||||
"react": "^0.14.5",
|
"react": "^0.14.5",
|
||||||
"react-native": "^0.24.1",
|
"react-native": "^0.24.1",
|
||||||
"react-native-action-button": "^1.1.4",
|
"react-native-action-button": "^1.1.4",
|
||||||
|
@ -16,11 +17,13 @@
|
||||||
"react-native-contacts": "^0.2.4",
|
"react-native-contacts": "^0.2.4",
|
||||||
"react-native-i18n": "0.0.8",
|
"react-native-i18n": "0.0.8",
|
||||||
"react-native-invertible-scroll-view": "^1.0.0",
|
"react-native-invertible-scroll-view": "^1.0.0",
|
||||||
"react-native-linear-gradient": "^1.5.7",
|
"react-native-linear-gradient": "1.5.7",
|
||||||
"react-native-loading-spinner-overlay": "0.0.8",
|
"react-native-loading-spinner-overlay": "0.0.8",
|
||||||
"react-native-qrcode": "^0.2.2",
|
"react-native-qrcode": "^0.2.2",
|
||||||
"react-native-randombytes": "^2.0.0",
|
"react-native-randombytes": "^2.1.0",
|
||||||
"react-native-vector-icons": "^1.3.4",
|
"react-native-vector-icons": "^1.3.4",
|
||||||
"realm": "^0.11.1"
|
"react-native-orientation": "^1.17.0",
|
||||||
|
"realm": "^0.11.1",
|
||||||
|
"react-native-status": "git+ssh://git@github.com/status-im/react-native-status"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,209 @@
|
||||||
|
status.command({
|
||||||
|
name: "location",
|
||||||
|
description: "Send location",
|
||||||
|
color: "#9a5dcf",
|
||||||
|
preview: function (params) {
|
||||||
|
var text = status.components.text(
|
||||||
|
{
|
||||||
|
style: {
|
||||||
|
marginTop: 5,
|
||||||
|
marginHorizontal: 0,
|
||||||
|
fontSize: 14,
|
||||||
|
fontFamily: "font",
|
||||||
|
color: "black"
|
||||||
|
}
|
||||||
|
}, params.value);
|
||||||
|
var uri = "https://maps.googleapis.com/maps/api/staticmap?center="
|
||||||
|
+ params.value
|
||||||
|
+ "&size=100x100&maptype=roadmap&key=AIzaSyBNsj1qoQEYPb3IllmWMAscuXW0eeuYqAA&language=en"
|
||||||
|
+ "&markers=size:mid%7Ccolor:0xff0000%7Clabel:%7C"
|
||||||
|
+ params.value;
|
||||||
|
|
||||||
|
var image = status.components.image(
|
||||||
|
{
|
||||||
|
source: {uri: uri},
|
||||||
|
style: {
|
||||||
|
width: 100,
|
||||||
|
height: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return status.components.view({}, [text, image]);
|
||||||
|
}
|
||||||
|
}).param({
|
||||||
|
name: "address",
|
||||||
|
type: status.types.TEXT,
|
||||||
|
placeholder: "Address"
|
||||||
|
});
|
||||||
|
|
||||||
|
var phones = [
|
||||||
|
{
|
||||||
|
number: "89171111111",
|
||||||
|
description: "Number format 1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
number: "89371111111",
|
||||||
|
description: "Number format 1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
number: "+79171111111",
|
||||||
|
description: "Number format 2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
number: "9171111111",
|
||||||
|
description: "Number format 3"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
function suggestionsContainerStyle(suggestionsCount) {
|
||||||
|
return {
|
||||||
|
marginVertical: 1,
|
||||||
|
marginHorizontal: 0,
|
||||||
|
height: Math.min(150, (56 * suggestionsCount)),
|
||||||
|
backgroundColor: "white",
|
||||||
|
borderRadius: 5
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var suggestionContainerStyle = {
|
||||||
|
paddingLeft: 16,
|
||||||
|
backgroundColor: "white"
|
||||||
|
};
|
||||||
|
|
||||||
|
var suggestionSubContainerStyle = {
|
||||||
|
height: 56,
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: "#0000001f"
|
||||||
|
};
|
||||||
|
|
||||||
|
var valueStyle = {
|
||||||
|
marginTop: 9,
|
||||||
|
fontSize: 14,
|
||||||
|
fontFamily: "font",
|
||||||
|
color: "#000000de"
|
||||||
|
};
|
||||||
|
|
||||||
|
var descriptionStyle = {
|
||||||
|
marginTop: 1.5,
|
||||||
|
fontSize: 14,
|
||||||
|
fontFamily: "font",
|
||||||
|
color: "#838c93de"
|
||||||
|
};
|
||||||
|
|
||||||
|
function startsWith(str1, str2) {
|
||||||
|
// String.startsWith(...) doesn't work in otto
|
||||||
|
return str1.lastIndexOf(str2, 0) == 0 && str1 != str2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function phoneSuggestions(params) {
|
||||||
|
var ph, suggestions;
|
||||||
|
if (!params.value || params.value == "") {
|
||||||
|
ph = phones;
|
||||||
|
} else {
|
||||||
|
ph = phones.filter(function (phone) {
|
||||||
|
return startsWith(phone.number, params.value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ph.length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
suggestions = ph.map(function (phone) {
|
||||||
|
return status.components.touchable(
|
||||||
|
{onPress: [status.events.SET_VALUE, phone.number]},
|
||||||
|
status.components.view(suggestionContainerStyle,
|
||||||
|
[status.components.view(suggestionSubContainerStyle,
|
||||||
|
[
|
||||||
|
status.components.text(
|
||||||
|
{style: valueStyle},
|
||||||
|
phone.number
|
||||||
|
),
|
||||||
|
status.components.text(
|
||||||
|
{style: descriptionStyle},
|
||||||
|
phone.description
|
||||||
|
)
|
||||||
|
])])
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return status.components.scrollView(
|
||||||
|
suggestionsContainerStyle(ph.length),
|
||||||
|
suggestions
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
status.response({
|
||||||
|
name: "phone",
|
||||||
|
description: "Send phone number",
|
||||||
|
color: "#5fc48d",
|
||||||
|
params: [{
|
||||||
|
name: "phone",
|
||||||
|
type: status.types.PHONE,
|
||||||
|
suggestions: phoneSuggestions,
|
||||||
|
placeholder: "Phone number"
|
||||||
|
}],
|
||||||
|
handler: function (params) {
|
||||||
|
return {
|
||||||
|
event: "sign-up",
|
||||||
|
params: [params.value]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
status.command({
|
||||||
|
name: "help",
|
||||||
|
description: "Help",
|
||||||
|
color: "#7099e6",
|
||||||
|
params: [{
|
||||||
|
name: "query",
|
||||||
|
type: status.types.TEXT
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
status.response({
|
||||||
|
name: "confirmation-code",
|
||||||
|
color: "#7099e6",
|
||||||
|
description: "Confirmation code",
|
||||||
|
params: [{
|
||||||
|
name: "code",
|
||||||
|
type: status.types.NUMBER
|
||||||
|
}],
|
||||||
|
handler: function (params) {
|
||||||
|
return {
|
||||||
|
event: "confirm-sign-up",
|
||||||
|
params: [params.value]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
status.response({
|
||||||
|
name: "keypair",
|
||||||
|
color: "#7099e6",
|
||||||
|
description: "Keypair password",
|
||||||
|
icon: "icon_lock_white",
|
||||||
|
params: [{
|
||||||
|
name: "password",
|
||||||
|
type: status.types.PASSWORD
|
||||||
|
}],
|
||||||
|
handler: function (params) {
|
||||||
|
return {
|
||||||
|
event: "save-password",
|
||||||
|
params: [params.value]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
preview: function (params) {
|
||||||
|
return status.components.text(
|
||||||
|
{
|
||||||
|
style: {
|
||||||
|
marginTop: 5,
|
||||||
|
marginHorizontal: 0,
|
||||||
|
fontSize: 14,
|
||||||
|
fontFamily: "font",
|
||||||
|
color: "black"
|
||||||
|
}
|
||||||
|
}, "*****");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
var _status_catalog = {
|
||||||
|
commands: {},
|
||||||
|
responses: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
function Command() {
|
||||||
|
}
|
||||||
|
function Response() {
|
||||||
|
}
|
||||||
|
|
||||||
|
Command.prototype.addToCatalog = function () {
|
||||||
|
_status_catalog.commands[this.name] = this;
|
||||||
|
};
|
||||||
|
|
||||||
|
Command.prototype.param = function (parameter) {
|
||||||
|
this.params.push(parameter);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
Command.prototype.create = function (com) {
|
||||||
|
this.name = com.name;
|
||||||
|
this.description = com.description;
|
||||||
|
this.handler = com.handler;
|
||||||
|
this.color = com.color;
|
||||||
|
this.icon = com.icon;
|
||||||
|
this.params = com.params || [];
|
||||||
|
this.preview = com.preview;
|
||||||
|
this.addToCatalog();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Response.prototype = Object.create(Command.prototype);
|
||||||
|
Response.prototype.addToCatalog = function () {
|
||||||
|
_status_catalog.responses[this.name] = this;
|
||||||
|
};
|
||||||
|
Response.prototype.onReceiveResponse = function (handler) {
|
||||||
|
this.onReceive = handler;
|
||||||
|
};
|
||||||
|
|
||||||
|
function call(pathStr, paramsStr) {
|
||||||
|
var params = JSON.parse(paramsStr),
|
||||||
|
path = JSON.parse(pathStr),
|
||||||
|
fn, res;
|
||||||
|
|
||||||
|
fn = path.reduce(function (catalog, name) {
|
||||||
|
if (catalog && catalog[name]) {
|
||||||
|
return catalog[name];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_status_catalog
|
||||||
|
);
|
||||||
|
|
||||||
|
if(!fn) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = fn(params);
|
||||||
|
|
||||||
|
return JSON.stringify(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
function text(options, s) {
|
||||||
|
return ['text', options, s];
|
||||||
|
}
|
||||||
|
|
||||||
|
function view(options, elements) {
|
||||||
|
return ['view', options].concat(elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
function image(options) {
|
||||||
|
return ['image', options];
|
||||||
|
}
|
||||||
|
|
||||||
|
function touchable(options, element) {
|
||||||
|
return ['touchable', options, element];
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrollView(options, elements) {
|
||||||
|
return ['scroll-view', options].concat(elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
var status = {
|
||||||
|
command: function (n, d, h) {
|
||||||
|
var command = new Command();
|
||||||
|
return command.create(n, d, h);
|
||||||
|
},
|
||||||
|
response: function (n, d, h) {
|
||||||
|
var response = new Response();
|
||||||
|
return response.create(n, d, h);
|
||||||
|
},
|
||||||
|
types: {
|
||||||
|
TEXT: 'text',
|
||||||
|
NUMBER: 'number',
|
||||||
|
PHONE: 'phone',
|
||||||
|
PASSWORD: 'password'
|
||||||
|
},
|
||||||
|
events: {
|
||||||
|
SET_VALUE: 'set-value'
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
view: view,
|
||||||
|
text: text,
|
||||||
|
image: image,
|
||||||
|
touchable: touchable,
|
||||||
|
scrollView: scrollView
|
||||||
|
}
|
||||||
|
};
|
|
@ -5,9 +5,10 @@
|
||||||
[re-frame.core :refer [subscribe dispatch dispatch-sync]]
|
[re-frame.core :refer [subscribe dispatch dispatch-sync]]
|
||||||
[status-im.handlers]
|
[status-im.handlers]
|
||||||
[status-im.subs]
|
[status-im.subs]
|
||||||
[status-im.components.react :refer [navigator app-registry]]
|
[status-im.components.react :refer [navigator app-registry device-event-emitter
|
||||||
|
orientation]]
|
||||||
[status-im.components.main-tabs :refer [main-tabs]]
|
[status-im.components.main-tabs :refer [main-tabs]]
|
||||||
[status-im.contacts.screen :refer [contact-list]]
|
[status-im.contacts.views.contact-list :refer [contact-list] ]
|
||||||
[status-im.contacts.views.new-contact :refer [new-contact]]
|
[status-im.contacts.views.new-contact :refer [new-contact]]
|
||||||
[status-im.qr-scanner.screen :refer [qr-scanner]]
|
[status-im.qr-scanner.screen :refer [qr-scanner]]
|
||||||
[status-im.discovery.screen :refer [discovery]]
|
[status-im.discovery.screen :refer [discovery]]
|
||||||
|
@ -33,9 +34,33 @@
|
||||||
true)))]
|
true)))]
|
||||||
(add-event-listener "hardwareBackPress" new-listener)))
|
(add-event-listener "hardwareBackPress" new-listener)))
|
||||||
|
|
||||||
|
(defn orientation->keyword [o]
|
||||||
|
(keyword (.toLowerCase o)))
|
||||||
|
|
||||||
(defn app-root []
|
(defn app-root []
|
||||||
(let [signed-up (subscribe [:get :signed-up])
|
(let [signed-up (subscribe [:get :signed-up])
|
||||||
view-id (subscribe [:get :view-id])]
|
view-id (subscribe [:get :view-id])
|
||||||
|
keyboard-height (subscribe [:get :keyboard-height])]
|
||||||
|
(r/create-class
|
||||||
|
{:component-will-mount
|
||||||
|
(fn []
|
||||||
|
(let [o (orientation->keyword (.getInitialOrientation orientation))]
|
||||||
|
(dispatch [:set :orientation o]))
|
||||||
|
(.addOrientationListener
|
||||||
|
orientation
|
||||||
|
#(dispatch [:set :orientation (orientation->keyword %)]))
|
||||||
|
(.lockToPortrait orientation)
|
||||||
|
(.addListener device-event-emitter
|
||||||
|
"keyboardDidShow"
|
||||||
|
(fn [e]
|
||||||
|
(let [h (.. e -endCoordinates -height)]
|
||||||
|
(when-not (= h keyboard-height)
|
||||||
|
(dispatch [:set :keyboard-height h])))))
|
||||||
|
(.addListener device-event-emitter
|
||||||
|
"keyboardDidHide"
|
||||||
|
(when-not (= 0 keyboard-height)
|
||||||
|
#(dispatch [:set :keyboard-height 0]))))
|
||||||
|
:render
|
||||||
(fn []
|
(fn []
|
||||||
(case (if @signed-up @view-id :chat)
|
(case (if @signed-up @view-id :chat)
|
||||||
:discovery [main-tabs]
|
:discovery [main-tabs]
|
||||||
|
@ -46,11 +71,12 @@
|
||||||
:new-group [new-group]
|
:new-group [new-group]
|
||||||
:group-settings [group-settings]
|
:group-settings [group-settings]
|
||||||
:contact-list [main-tabs]
|
:contact-list [main-tabs]
|
||||||
|
:group-contacts [contact-list]
|
||||||
:new-contact [new-contact]
|
:new-contact [new-contact]
|
||||||
:qr-scanner [qr-scanner]
|
:qr-scanner [qr-scanner]
|
||||||
:chat [chat]
|
:chat [chat]
|
||||||
:profile [profile]
|
:profile [profile]
|
||||||
:my-profile [my-profile]))))
|
:my-profile [my-profile]))})))
|
||||||
|
|
||||||
(defn init []
|
(defn init []
|
||||||
(dispatch-sync [:initialize-db])
|
(dispatch-sync [:initialize-db])
|
||||||
|
@ -59,8 +85,6 @@
|
||||||
(dispatch [:initialize-protocol])
|
(dispatch [:initialize-protocol])
|
||||||
(dispatch [:load-user-phone-number])
|
(dispatch [:load-user-phone-number])
|
||||||
(dispatch [:load-contacts])
|
(dispatch [:load-contacts])
|
||||||
;; load commands from remote server (todo: uncomment)
|
|
||||||
;; (dispatch [:load-commands])
|
|
||||||
(dispatch [:init-console-chat])
|
(dispatch [:init-console-chat])
|
||||||
(dispatch [:init-chat])
|
(dispatch [:init-chat])
|
||||||
(init-back-button-handler!)
|
(init-back-button-handler!)
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
(ns status-im.chat.constants)
|
||||||
|
|
||||||
|
(def input-height 56)
|
||||||
|
(def request-info-height 61)
|
||||||
|
(def response-height-normal 211)
|
||||||
|
(def minimum-suggestion-height (+ input-height request-info-height))
|
||||||
|
(def suggestions-header-height 22)
|
||||||
|
(def minimum-command-suggestions-height
|
||||||
|
(+ input-height suggestions-header-height))
|
|
@ -1,5 +1,5 @@
|
||||||
(ns status-im.chat.handlers
|
(ns status-im.chat.handlers
|
||||||
(:require [re-frame.core :refer [register-handler enrich after debug dispatch]]
|
(:require [re-frame.core :refer [enrich after debug dispatch]]
|
||||||
[status-im.models.commands :as commands]
|
[status-im.models.commands :as commands]
|
||||||
[clojure.string :as str]
|
[clojure.string :as str]
|
||||||
[status-im.components.styles :refer [default-chat-color]]
|
[status-im.components.styles :refer [default-chat-color]]
|
||||||
|
@ -7,29 +7,39 @@
|
||||||
[status-im.protocol.api :as api]
|
[status-im.protocol.api :as api]
|
||||||
[status-im.models.messages :as messages]
|
[status-im.models.messages :as messages]
|
||||||
[status-im.constants :refer [text-content-type
|
[status-im.constants :refer [text-content-type
|
||||||
content-type-command]]
|
content-type-command
|
||||||
|
content-type-command-request
|
||||||
|
default-number-of-messages]]
|
||||||
[status-im.utils.random :as random]
|
[status-im.utils.random :as random]
|
||||||
[status-im.chat.sign-up :as sign-up-service]
|
[status-im.chat.sign-up :as sign-up-service]
|
||||||
[status-im.models.chats :as chats]
|
[status-im.models.chats :as chats]
|
||||||
[status-im.navigation.handlers :as nav]
|
[status-im.navigation.handlers :as nav]
|
||||||
[status-im.utils.handlers :as u]
|
[status-im.utils.handlers :refer [register-handler] :as u]
|
||||||
[status-im.persistence.realm :as r]
|
[status-im.persistence.realm :as r]
|
||||||
[status-im.handlers.server :as server]
|
[status-im.handlers.server :as server]
|
||||||
|
[status-im.handlers.content-suggestions :refer [get-content-suggestions]]
|
||||||
[status-im.utils.phone-number :refer [format-phone-number]]
|
[status-im.utils.phone-number :refer [format-phone-number]]
|
||||||
[status-im.utils.datetime :as time]))
|
[status-im.utils.datetime :as time]
|
||||||
|
[status-im.components.jail :as j]
|
||||||
|
[status-im.utils.types :refer [json->clj]]
|
||||||
|
[status-im.commands.utils :refer [generate-hiccup]]))
|
||||||
|
|
||||||
(register-handler :set-show-actions
|
(register-handler :set-show-actions
|
||||||
(fn [db [_ show-actions]]
|
(fn [db [_ show-actions]]
|
||||||
(assoc db :show-actions show-actions)))
|
(assoc db :show-actions show-actions)))
|
||||||
|
|
||||||
(register-handler :load-more-messages
|
(register-handler :load-more-messages
|
||||||
(fn [db _]
|
(fn [{:keys [current-chat-id] :as db} _]
|
||||||
|
(let [all-loaded? (get-in db [:chats current-chat-id :all-loaded?])]
|
||||||
|
(if all-loaded?
|
||||||
db
|
db
|
||||||
;; TODO implement
|
(let [messages-path [:chats current-chat-id :messages]
|
||||||
#_(let [chat-id (get-in db [:chat :current-chat-id])
|
messages (get-in db messages-path)
|
||||||
messages [:chats chat-id :messages]
|
new-messages (messages/get-messages current-chat-id (count messages))
|
||||||
new-messages (gen-messages 10)]
|
all-loaded? (> default-number-of-messages (count new-messages))]
|
||||||
(update-in db messages concat new-messages))))
|
(-> db
|
||||||
|
(update-in messages-path concat new-messages)
|
||||||
|
(assoc-in [:chats current-chat-id :all-loaded?] all-loaded?)))))))
|
||||||
|
|
||||||
(defn safe-trim [s]
|
(defn safe-trim [s]
|
||||||
(when (string? s)
|
(when (string? s)
|
||||||
|
@ -41,38 +51,131 @@
|
||||||
(assoc-in [:chats current-chat-id :command-input] {})
|
(assoc-in [:chats current-chat-id :command-input] {})
|
||||||
(update-in [:chats current-chat-id :input-text] safe-trim))))
|
(update-in [:chats current-chat-id :input-text] safe-trim))))
|
||||||
|
|
||||||
|
(defn invoke-suggestions-handler!
|
||||||
|
[{:keys [current-chat-id canceled-command] :as db} _]
|
||||||
|
(when-not canceled-command
|
||||||
|
(let [{:keys [command content]} (get-in db [:chats current-chat-id :command-input])
|
||||||
|
{:keys [name type]} command
|
||||||
|
path [(if (= :command type) :commands :responses)
|
||||||
|
name
|
||||||
|
:params
|
||||||
|
0
|
||||||
|
:suggestions]
|
||||||
|
params {:value content}]
|
||||||
|
(j/call current-chat-id
|
||||||
|
path
|
||||||
|
params
|
||||||
|
#(dispatch [:suggestions-handler {:command command
|
||||||
|
:content content
|
||||||
|
:chat-id current-chat-id} %])))))
|
||||||
|
|
||||||
|
(register-handler :start-cancel-command
|
||||||
|
(u/side-effect!
|
||||||
|
(fn [db _]
|
||||||
|
(dispatch [:animate-cancel-command]))))
|
||||||
|
|
||||||
|
(def command-prefix "c ")
|
||||||
|
|
||||||
|
(defn cancel-command!
|
||||||
|
[{:keys [canceled-command]}]
|
||||||
|
(when canceled-command
|
||||||
|
(dispatch [:start-cancel-command])))
|
||||||
|
|
||||||
(register-handler :set-chat-command-content
|
(register-handler :set-chat-command-content
|
||||||
(fn [db [_ content]]
|
[(after invoke-suggestions-handler!)
|
||||||
(commands/set-chat-command-content db content)))
|
(after cancel-command!)]
|
||||||
|
(fn [{:keys [current-chat-id] :as db} [_ content]]
|
||||||
|
(let [starts-as-command? (str/starts-with? content command-prefix)
|
||||||
|
path [:chats current-chat-id :command-input :command :type]
|
||||||
|
command? (= :command (get-in db path))]
|
||||||
|
(as-> db db
|
||||||
|
(commands/set-chat-command-content db content)
|
||||||
|
(assoc-in db [:chats current-chat-id :input-text] nil)
|
||||||
|
(assoc db :canceled-command (and command? (not starts-as-command?)))))))
|
||||||
|
|
||||||
(defn update-input-text
|
(defn update-input-text
|
||||||
[{:keys [current-chat-id] :as db} text]
|
[{:keys [current-chat-id] :as db} text]
|
||||||
(assoc-in db [:chats current-chat-id :input-text] text))
|
(assoc-in db [:chats current-chat-id :input-text] text))
|
||||||
|
|
||||||
|
(defn invoke-command-preview!
|
||||||
|
[{:keys [current-chat-id staged-command]} _]
|
||||||
|
(let [{:keys [command content]} staged-command
|
||||||
|
{:keys [name type]} command
|
||||||
|
path [(if (= :command type) :commands :responses)
|
||||||
|
name
|
||||||
|
:preview]
|
||||||
|
params {:value content}]
|
||||||
|
(j/call current-chat-id
|
||||||
|
path
|
||||||
|
params
|
||||||
|
#(dispatch [:command-preview current-chat-id %]))))
|
||||||
|
|
||||||
(register-handler :stage-command
|
(register-handler :stage-command
|
||||||
|
(after invoke-command-preview!)
|
||||||
(fn [{:keys [current-chat-id] :as db} _]
|
(fn [{:keys [current-chat-id] :as db} _]
|
||||||
(let [db (update-input-text db nil)
|
(let [db (update-input-text db nil)
|
||||||
{:keys [command content]}
|
{:keys [command content to-msg-id]}
|
||||||
(get-in db [:chats current-chat-id :command-input])
|
(get-in db [:chats current-chat-id :command-input])
|
||||||
|
content' (if (= :command (:type command))
|
||||||
|
(subs content 2)
|
||||||
|
content)
|
||||||
command-info {:command command
|
command-info {:command command
|
||||||
:content content
|
:content content'
|
||||||
:handler (:handler command)}]
|
:to-message to-msg-id}]
|
||||||
(commands/stage-command db command-info))))
|
(-> db
|
||||||
|
(commands/stage-command command-info)
|
||||||
|
(assoc :staged-command command-info)))))
|
||||||
|
|
||||||
|
(register-handler :set-message-input []
|
||||||
|
(fn [db [_ input]]
|
||||||
|
(assoc db :message-input input)))
|
||||||
|
|
||||||
|
(register-handler :blur-message-input
|
||||||
|
(u/side-effect!
|
||||||
|
(fn [db _]
|
||||||
|
(when-let [message-input (:message-input db)]
|
||||||
|
(.blur message-input)))))
|
||||||
|
|
||||||
(register-handler :set-response-chat-command
|
(register-handler :set-response-chat-command
|
||||||
|
[(after invoke-suggestions-handler!)
|
||||||
|
(after #(dispatch [:command-edit-mode]))
|
||||||
|
(after #(dispatch [:set-chat-input-text ""]))]
|
||||||
(fn [db [_ to-msg-id command-key]]
|
(fn [db [_ to-msg-id command-key]]
|
||||||
(commands/set-response-chat-command db to-msg-id command-key)))
|
(-> db
|
||||||
|
(commands/set-response-chat-command to-msg-id command-key)
|
||||||
|
(assoc :canceled-command false))))
|
||||||
|
|
||||||
(defn update-text
|
(defn update-text
|
||||||
[db [_ text]]
|
[{:keys [current-chat-id] :as db} [_ text]]
|
||||||
(update-input-text db text))
|
(let [suggestions (get-in db [:command-suggestions current-chat-id])]
|
||||||
|
(if-not (= 1 (count suggestions))
|
||||||
|
(update-input-text db text)
|
||||||
|
(assoc db :disable-input true))))
|
||||||
|
|
||||||
(defn update-command [db [_ text]]
|
(defn update-command [db [_ text]]
|
||||||
|
(if-not (commands/get-chat-command db)
|
||||||
(let [{:keys [command]} (suggestions/check-suggestion db text)]
|
(let [{:keys [command]} (suggestions/check-suggestion db text)]
|
||||||
(commands/set-chat-command db command)))
|
(if command
|
||||||
|
(commands/set-chat-command db command)
|
||||||
|
db))
|
||||||
|
db))
|
||||||
|
|
||||||
|
(defn check-suggestions
|
||||||
|
[{:keys [current-chat-id] :as db} [_ text]]
|
||||||
|
(let [suggestions (suggestions/get-suggestions db text)]
|
||||||
|
(assoc-in db [:command-suggestions current-chat-id] suggestions)))
|
||||||
|
|
||||||
|
(defn select-suggestion!
|
||||||
|
[{:keys [current-chat-id] :as db} [_ text]]
|
||||||
|
(let [suggestions (get-in db [:command-suggestions current-chat-id])]
|
||||||
|
(when (= 1 (count suggestions))
|
||||||
|
(dispatch [:set-chat-command (ffirst suggestions)]))))
|
||||||
|
|
||||||
(register-handler :set-chat-input-text
|
(register-handler :set-chat-input-text
|
||||||
((enrich update-command) update-text))
|
[(enrich update-command)
|
||||||
|
(after select-suggestion!)
|
||||||
|
(after #(dispatch [:animate-command-suggestions]))]
|
||||||
|
((enrich update-text) check-suggestions))
|
||||||
|
|
||||||
(defn console? [s]
|
(defn console? [s]
|
||||||
(= "console" s))
|
(= "console" s))
|
||||||
|
@ -94,12 +197,26 @@
|
||||||
(defn add-message-to-db
|
(defn add-message-to-db
|
||||||
[db chat-id message]
|
[db chat-id message]
|
||||||
(let [messages [:chats chat-id :messages]]
|
(let [messages [:chats chat-id :messages]]
|
||||||
(update-in db messages conj message)))
|
(update-in db messages conj (assoc message :chat-id chat-id
|
||||||
|
:new? true))))
|
||||||
|
|
||||||
|
(defn set-message-shown
|
||||||
|
[db chat-id msg-id]
|
||||||
|
(update-in db [:chats chat-id :messages] (fn [messages]
|
||||||
|
(map (fn [msg]
|
||||||
|
(if (= msg-id (:msg-id msg))
|
||||||
|
(assoc msg :new? false)
|
||||||
|
msg))
|
||||||
|
messages))))
|
||||||
|
|
||||||
|
(register-handler :set-message-shown
|
||||||
|
(fn [db [_ {:keys [chat-id msg-id]}]]
|
||||||
|
(set-message-shown db chat-id msg-id)))
|
||||||
|
|
||||||
(defn prepare-message
|
(defn prepare-message
|
||||||
[{:keys [identity current-chat-id] :as db} _]
|
[{:keys [identity current-chat-id] :as db} _]
|
||||||
(let [text (get-in db [:chats current-chat-id :input-text])
|
(let [text (get-in db [:chats current-chat-id :input-text])
|
||||||
{:keys [command]} (suggestions/check-suggestion db (str text " "))
|
[command] (suggestions/check-suggestion db (str text " "))
|
||||||
message (check-author-direction
|
message (check-author-direction
|
||||||
db current-chat-id
|
db current-chat-id
|
||||||
{:msg-id (random/id)
|
{:msg-id (random/id)
|
||||||
|
@ -114,17 +231,19 @@
|
||||||
(commands/set-chat-command db command)
|
(commands/set-chat-command db command)
|
||||||
(assoc db :new-message (when-not (str/blank? text) message)))))
|
(assoc db :new-message (when-not (str/blank? text) message)))))
|
||||||
|
|
||||||
(defn prepare-command [identity chat-id staged-command]
|
(defn prepare-command
|
||||||
(let [command-key (get-in staged-command [:command :command])
|
[identity chat-id {:keys [preview preview-string content command to-message]}]
|
||||||
content {:command (name command-key)
|
(let [content {:command (command :name)
|
||||||
:content (:content staged-command)}]
|
:content content}]
|
||||||
{:msg-id (random/id)
|
{:msg-id (random/id)
|
||||||
:from identity
|
:from identity
|
||||||
:to chat-id
|
:to chat-id
|
||||||
:content content
|
:content content
|
||||||
:content-type content-type-command
|
:content-type content-type-command
|
||||||
:outgoing true
|
:outgoing true
|
||||||
:handler (:handler staged-command)}))
|
:preview preview-string
|
||||||
|
:rendered-preview preview
|
||||||
|
:to-message to-message}))
|
||||||
|
|
||||||
(defn prepare-staged-commans
|
(defn prepare-staged-commans
|
||||||
[{:keys [current-chat-id identity] :as db} _]
|
[{:keys [current-chat-id identity] :as db} _]
|
||||||
|
@ -177,7 +296,29 @@
|
||||||
(defn save-commands-to-realm!
|
(defn save-commands-to-realm!
|
||||||
[{:keys [new-commands current-chat-id]} _]
|
[{:keys [new-commands current-chat-id]} _]
|
||||||
(doseq [new-command new-commands]
|
(doseq [new-command new-commands]
|
||||||
(messages/save-message current-chat-id (dissoc new-command :handler))))
|
(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 %])))))
|
||||||
|
|
||||||
(defn handle-commands
|
(defn handle-commands
|
||||||
[{:keys [new-commands]}]
|
[{:keys [new-commands]}]
|
||||||
|
@ -196,6 +337,9 @@
|
||||||
((after send-message!))
|
((after send-message!))
|
||||||
((after save-message-to-realm!))
|
((after save-message-to-realm!))
|
||||||
((after save-commands-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!))
|
||||||
((after handle-commands))))
|
((after handle-commands))))
|
||||||
|
|
||||||
(register-handler :unstage-command
|
(register-handler :unstage-command
|
||||||
|
@ -203,9 +347,13 @@
|
||||||
(commands/unstage-command db staged-command)))
|
(commands/unstage-command db staged-command)))
|
||||||
|
|
||||||
(register-handler :set-chat-command
|
(register-handler :set-chat-command
|
||||||
(fn [db [_ command-key]]
|
[(after invoke-suggestions-handler!)
|
||||||
;; todo what is going on there?!
|
(after #(dispatch [:command-edit-mode]))]
|
||||||
(commands/set-chat-command db command-key)))
|
(fn [{:keys [current-chat-id] :as db} [_ command-key]]
|
||||||
|
(-> db
|
||||||
|
(commands/set-chat-command command-key)
|
||||||
|
(assoc-in [:chats current-chat-id :command-input :content] "c ")
|
||||||
|
(assoc :disable-input true))))
|
||||||
|
|
||||||
(register-handler :init-console-chat
|
(register-handler :init-console-chat
|
||||||
(fn [db [_]]
|
(fn [db [_]]
|
||||||
|
@ -240,20 +388,23 @@
|
||||||
|
|
||||||
(defn load-messages!
|
(defn load-messages!
|
||||||
([db] (load-messages! db nil))
|
([db] (load-messages! db nil))
|
||||||
([db _]
|
([{:keys [current-chat-id] :as db} _]
|
||||||
(->> (:current-chat-id db)
|
(assoc db :messages (messages/get-messages current-chat-id))))
|
||||||
messages/get-messages
|
|
||||||
(assoc db :messages))))
|
|
||||||
|
|
||||||
(defn init-chat
|
(defn init-chat
|
||||||
([db] (init-chat db nil))
|
([db] (init-chat db nil))
|
||||||
([{:keys [messages current-chat-id] :as db} _]
|
([{:keys [messages current-chat-id] :as db} _]
|
||||||
(assoc-in db [:chats current-chat-id :messages] messages)))
|
(assoc-in db [:chats current-chat-id :messages] messages)))
|
||||||
|
|
||||||
|
(defn load-commands!
|
||||||
|
[{:keys [current-chat-id]}]
|
||||||
|
(dispatch [:load-commands! current-chat-id]))
|
||||||
|
|
||||||
(register-handler :init-chat
|
(register-handler :init-chat
|
||||||
|
(after #(dispatch [:load-requests!]))
|
||||||
(-> load-messages!
|
(-> load-messages!
|
||||||
((enrich init-chat))
|
((enrich init-chat))
|
||||||
debug))
|
((after load-commands!))))
|
||||||
|
|
||||||
(defn initialize-chats
|
(defn initialize-chats
|
||||||
[{:keys [loaded-chats] :as db} _]
|
[{:keys [loaded-chats] :as db} _]
|
||||||
|
@ -278,6 +429,11 @@
|
||||||
[{:keys [new-message]} [_ {chat-id :from}]]
|
[{:keys [new-message]} [_ {chat-id :from}]]
|
||||||
(messages/save-message chat-id new-message))
|
(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
|
(defn receive-message
|
||||||
[db [_ {chat-id :from :as message}]]
|
[db [_ {chat-id :from :as message}]]
|
||||||
(let [message' (check-author-direction db chat-id message)]
|
(let [message' (check-author-direction db chat-id message)]
|
||||||
|
@ -286,8 +442,9 @@
|
||||||
(assoc :new-message message'))))
|
(assoc :new-message message'))))
|
||||||
|
|
||||||
(register-handler :received-msg
|
(register-handler :received-msg
|
||||||
(-> receive-message
|
[(after store-message!)
|
||||||
((after store-message!))))
|
(after dispatch-request!)]
|
||||||
|
receive-message)
|
||||||
|
|
||||||
(register-handler :group-received-msg
|
(register-handler :group-received-msg
|
||||||
(u/side-effect!
|
(u/side-effect!
|
||||||
|
@ -299,6 +456,7 @@
|
||||||
(let [chat-id (or id current-chat-id)
|
(let [chat-id (or id current-chat-id)
|
||||||
messages (get-in db [:chats chat-id :messages])
|
messages (get-in db [:chats chat-id :messages])
|
||||||
db' (assoc db :current-chat-id chat-id)]
|
db' (assoc db :current-chat-id chat-id)]
|
||||||
|
(dispatch [:load-requests! chat-id])
|
||||||
(if (seq messages)
|
(if (seq messages)
|
||||||
db'
|
db'
|
||||||
(-> db'
|
(-> db'
|
||||||
|
@ -336,9 +494,11 @@
|
||||||
((after save-chat!))
|
((after save-chat!))
|
||||||
((after open-chat!))))
|
((after open-chat!))))
|
||||||
|
|
||||||
(register-handler :switch-command-suggestions
|
(register-handler :switch-command-suggestions!
|
||||||
(fn [db [_]]
|
(u/side-effect!
|
||||||
(suggestions/switch-command-suggestions db)))
|
(fn [db]
|
||||||
|
(let [text (if (suggestions/typing-command? db) "" "!")]
|
||||||
|
(dispatch [:set-chat-input-text text])))))
|
||||||
|
|
||||||
(defn remove-chat
|
(defn remove-chat
|
||||||
[{:keys [current-chat-id] :as db} _]
|
[{:keys [current-chat-id] :as db} _]
|
||||||
|
@ -381,3 +541,30 @@
|
||||||
;((after leaving-message!))
|
;((after leaving-message!))
|
||||||
((after delete-messages!))
|
((after delete-messages!))
|
||||||
((after delete-chat!))))
|
((after delete-chat!))))
|
||||||
|
|
||||||
|
(defn edit-mode-handler [mode]
|
||||||
|
(fn [{:keys [current-chat-id] :as db} _]
|
||||||
|
(assoc-in db [:edit-mode current-chat-id] mode)))
|
||||||
|
|
||||||
|
(register-handler :command-edit-mode
|
||||||
|
(edit-mode-handler :command))
|
||||||
|
|
||||||
|
(register-handler :text-edit-mode
|
||||||
|
(after #(dispatch [:set-chat-input-text ""]))
|
||||||
|
(edit-mode-handler :text))
|
||||||
|
|
||||||
|
(register-handler :set-layout-height
|
||||||
|
[(after
|
||||||
|
(fn [{:keys [current-chat-id] :as db}]
|
||||||
|
(let [suggestions (get-in db [:suggestions current-chat-id])
|
||||||
|
mode (get-in db [:edit-mode current-chat-id])]
|
||||||
|
(when (and (= :command mode) (seq suggestions))
|
||||||
|
(dispatch [:fix-response-height])))))
|
||||||
|
(after
|
||||||
|
(fn [{:keys [current-chat-id] :as db}]
|
||||||
|
(let [suggestions (get-in db [:command-suggestions current-chat-id])
|
||||||
|
mode (get-in db [:edit-mode current-chat-id])]
|
||||||
|
(when (and (= :text mode)) (seq suggestions)
|
||||||
|
(dispatch [:fix-commands-suggestions-height])))))]
|
||||||
|
(fn [db [_ h]]
|
||||||
|
(assoc db :layout-height h)))
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
(ns status-im.chat.handlers.animation
|
||||||
|
(:require [re-frame.core :refer [after dispatch debug path]]
|
||||||
|
[status-im.utils.handlers :refer [register-handler]]
|
||||||
|
[status-im.handlers.content-suggestions :refer [get-content-suggestions]]
|
||||||
|
[status-im.chat.constants :refer [input-height request-info-height
|
||||||
|
minimum-command-suggestions-height
|
||||||
|
response-height-normal minimum-suggestion-height]]
|
||||||
|
[status-im.constants :refer [response-input-hiding-duration]]))
|
||||||
|
|
||||||
|
;; todo magic value
|
||||||
|
(def middle-height 270)
|
||||||
|
|
||||||
|
(defn animation-handler
|
||||||
|
([name handler] (animation-handler name nil handler))
|
||||||
|
([name middleware handler]
|
||||||
|
(register-handler name [(path :animations) middleware] handler)))
|
||||||
|
|
||||||
|
(animation-handler :animate-cancel-command
|
||||||
|
(after #(dispatch [:text-edit-mode]))
|
||||||
|
(fn [db _]
|
||||||
|
(assoc db :to-response-height input-height)))
|
||||||
|
|
||||||
|
(def response-height (+ input-height response-height-normal))
|
||||||
|
|
||||||
|
(defn update-response-height [db]
|
||||||
|
(assoc-in db [:animations :to-response-height] response-height))
|
||||||
|
|
||||||
|
(register-handler :animate-command-suggestions
|
||||||
|
(fn [{:keys [current-chat-id] :as db} _]
|
||||||
|
(let [suggestions? (seq (get-in db [:command-suggestions current-chat-id]))
|
||||||
|
current (get-in db [:animations :command-suggestions-height])
|
||||||
|
height (if suggestions? middle-height input-height)
|
||||||
|
changed? (if (and suggestions?
|
||||||
|
(not (nil? current))
|
||||||
|
(not= input-height current))
|
||||||
|
identity inc)]
|
||||||
|
(-> db
|
||||||
|
(update :animations assoc :command-suggestions-height height)
|
||||||
|
(update-in [:animations :commands-height-changed] changed?)))))
|
||||||
|
|
||||||
|
(defn get-minimum-height
|
||||||
|
[{:keys [current-chat-id] :as db}]
|
||||||
|
(let [path [:chats current-chat-id :command-input :command :type]
|
||||||
|
type (get-in db path)]
|
||||||
|
(if (= :response type)
|
||||||
|
minimum-suggestion-height
|
||||||
|
input-height)))
|
||||||
|
|
||||||
|
(register-handler :animate-show-response
|
||||||
|
[(after #(dispatch [:command-edit-mode]))]
|
||||||
|
(fn [{:keys [current-chat-id] :as db}]
|
||||||
|
(let [suggestions? (seq (get-in db [:suggestions current-chat-id]))
|
||||||
|
height (if suggestions?
|
||||||
|
middle-height
|
||||||
|
(get-minimum-height db))]
|
||||||
|
(assoc-in db [:animations :to-response-height] height))))
|
||||||
|
|
||||||
|
(defn fix-height
|
||||||
|
[height-key height-signal-key suggestions-key minimum]
|
||||||
|
(fn [{:keys [current-chat-id] :as db} [_ vy current]]
|
||||||
|
(let [max-height (get-in db [:layout-height])
|
||||||
|
moving-down? (pos? vy)
|
||||||
|
moving-up? (not moving-down?)
|
||||||
|
under-middle-position? (<= current middle-height)
|
||||||
|
over-middle-position? (not under-middle-position?)
|
||||||
|
suggestions (get-in db [suggestions-key current-chat-id])
|
||||||
|
old-fixed (get-in db [:animations height-key])
|
||||||
|
|
||||||
|
new-fixed (cond (not suggestions)
|
||||||
|
(minimum db)
|
||||||
|
|
||||||
|
(and (nil? vy) (nil? current)
|
||||||
|
(> old-fixed middle-height))
|
||||||
|
max-height
|
||||||
|
|
||||||
|
(and (nil? vy) (nil? current)
|
||||||
|
(< old-fixed middle-height))
|
||||||
|
(minimum db)
|
||||||
|
|
||||||
|
(and under-middle-position? moving-up?)
|
||||||
|
middle-height
|
||||||
|
|
||||||
|
(and over-middle-position? moving-down?)
|
||||||
|
middle-height
|
||||||
|
|
||||||
|
(and over-middle-position? moving-up?)
|
||||||
|
max-height
|
||||||
|
|
||||||
|
(and under-middle-position? moving-down?)
|
||||||
|
(minimum db))]
|
||||||
|
(-> db
|
||||||
|
(assoc-in [:animations height-key] new-fixed)
|
||||||
|
(update-in [:animations height-signal-key] inc)))))
|
||||||
|
|
||||||
|
(defn commands-min-height
|
||||||
|
[{:keys [current-chat-id] :as db}]
|
||||||
|
(let [suggestions (get-in db [:command-suggestions current-chat-id])]
|
||||||
|
(if (seq suggestions)
|
||||||
|
minimum-command-suggestions-height
|
||||||
|
input-height)))
|
||||||
|
|
||||||
|
(register-handler :fix-commands-suggestions-height
|
||||||
|
(fix-height :command-suggestions-height
|
||||||
|
:commands-height-changed
|
||||||
|
:command-suggestions
|
||||||
|
commands-min-height))
|
||||||
|
|
||||||
|
(register-handler :fix-response-height
|
||||||
|
(fix-height :to-response-height
|
||||||
|
:response-height-changed
|
||||||
|
:suggestions
|
||||||
|
get-minimum-height))
|
|
@ -0,0 +1,57 @@
|
||||||
|
(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 :as realm]
|
||||||
|
[status-im.utils.handlers :as u]))
|
||||||
|
|
||||||
|
(defn store-request!
|
||||||
|
[{:keys [new-request]}]
|
||||||
|
(realm/write
|
||||||
|
(fn []
|
||||||
|
(realm/create :requests new-request))))
|
||||||
|
|
||||||
|
(defn add-request
|
||||||
|
[db [_ chat-id {:keys [msg-id content]}]]
|
||||||
|
(let [request {:chat-id chat-id
|
||||||
|
:message-id msg-id
|
||||||
|
:type (:command content)
|
||||||
|
:added (js/Date.)}
|
||||||
|
request' (update request :type keyword)]
|
||||||
|
(-> db
|
||||||
|
(update-in [:chats chat-id :requests] conj request')
|
||||||
|
(assoc :new-request request))))
|
||||||
|
|
||||||
|
(defn load-requests!
|
||||||
|
[{:keys [current-chat-id] :as db} [_ chat-id]]
|
||||||
|
(let [chat-id' (or chat-id current-chat-id)
|
||||||
|
requests (-> :requests
|
||||||
|
;; todo maybe limit is needed
|
||||||
|
(realm/get-by-fields {:chat-id chat-id'
|
||||||
|
:status "open"})
|
||||||
|
(realm/sorted :added :desc)
|
||||||
|
(realm/collection->map))
|
||||||
|
requests' (map #(update % :type keyword) requests)]
|
||||||
|
(assoc-in db [:chats chat-id' :requests] requests')))
|
||||||
|
|
||||||
|
(defn mark-request-as-answered!
|
||||||
|
[_ [_ chat-id message-id]]
|
||||||
|
(realm/write
|
||||||
|
(fn []
|
||||||
|
(-> :requests
|
||||||
|
(realm/get-by-fields
|
||||||
|
{:chat-id chat-id
|
||||||
|
:message-id message-id})
|
||||||
|
(realm/single)
|
||||||
|
(.-status)
|
||||||
|
(set! "answered")))))
|
||||||
|
|
||||||
|
(register-handler :add-request
|
||||||
|
(after store-request!)
|
||||||
|
add-request)
|
||||||
|
|
||||||
|
(register-handler :load-requests! load-requests!)
|
||||||
|
|
||||||
|
(register-handler :request-answered!
|
||||||
|
(after (fn [_ [_ chat-id]]
|
||||||
|
(dispatch [:load-requests! chat-id])))
|
||||||
|
(u/side-effect! mark-request-as-answered!))
|
|
@ -3,6 +3,7 @@
|
||||||
(:require [re-frame.core :refer [subscribe dispatch]]
|
(:require [re-frame.core :refer [subscribe dispatch]]
|
||||||
[clojure.string :as s]
|
[clojure.string :as s]
|
||||||
[status-im.components.react :refer [view
|
[status-im.components.react :refer [view
|
||||||
|
animated-view
|
||||||
text
|
text
|
||||||
image
|
image
|
||||||
icon
|
icon
|
||||||
|
@ -12,13 +13,17 @@
|
||||||
[status-im.components.chat-icon.screen :refer [chat-icon-view-action
|
[status-im.components.chat-icon.screen :refer [chat-icon-view-action
|
||||||
chat-icon-view-menu-item]]
|
chat-icon-view-menu-item]]
|
||||||
[status-im.chat.styles.screen :as st]
|
[status-im.chat.styles.screen :as st]
|
||||||
[status-im.utils.listview :refer [to-datasource]]
|
[status-im.utils.listview :refer [to-datasource-inverted]]
|
||||||
[status-im.utils.utils :refer [truncate-str]]
|
[status-im.utils.utils :refer [truncate-str]]
|
||||||
[status-im.components.invertible-scroll-view :refer [invertible-scroll-view]]
|
[status-im.components.invertible-scroll-view :refer [invertible-scroll-view]]
|
||||||
[status-im.components.toolbar :refer [toolbar]]
|
[status-im.components.toolbar :refer [toolbar]]
|
||||||
[status-im.chat.views.message :refer [chat-message]]
|
[status-im.chat.views.message :refer [chat-message]]
|
||||||
|
[status-im.chat.views.suggestions :refer [suggestion-container]]
|
||||||
|
[status-im.chat.views.response :refer [response-view]]
|
||||||
[status-im.chat.views.new-message :refer [chat-message-new]]
|
[status-im.chat.views.new-message :refer [chat-message-new]]
|
||||||
[status-im.i18n :refer [label label-pluralize]]))
|
[status-im.i18n :refer [label label-pluralize]]
|
||||||
|
[status-im.components.animation :as anim]
|
||||||
|
[reagent.core :as r]))
|
||||||
|
|
||||||
|
|
||||||
(defn contacts-by-identity [contacts]
|
(defn contacts-by-identity [contacts]
|
||||||
|
@ -55,12 +60,12 @@
|
||||||
(for [member ["Geoff" "Justas"]]
|
(for [member ["Geoff" "Justas"]]
|
||||||
^{:key member} [typing member])])
|
^{:key member} [typing member])])
|
||||||
|
|
||||||
(defn message-row [contact-by-identity group-chat]
|
(defn message-row [contact-by-identity group-chat messages-count]
|
||||||
(fn [row _ idx]
|
(fn [row _ idx]
|
||||||
(let [msg (-> row
|
(let [msg (-> row
|
||||||
(add-msg-color contact-by-identity)
|
(add-msg-color contact-by-identity)
|
||||||
(assoc :group-chat group-chat)
|
(assoc :group-chat group-chat)
|
||||||
(assoc :last-msg (zero? (js/parseInt idx))))]
|
(assoc :last-msg (= (js/parseInt idx) (dec messages-count))))]
|
||||||
(list-item [chat-message msg]))))
|
(list-item [chat-message msg]))))
|
||||||
|
|
||||||
(defn on-action-selected [position]
|
(defn on-action-selected [position]
|
||||||
|
@ -216,18 +221,53 @@
|
||||||
[messages [:chat :messages]
|
[messages [:chat :messages]
|
||||||
contacts [:chat :contacts]]
|
contacts [:chat :contacts]]
|
||||||
(let [contacts' (contacts-by-identity contacts)]
|
(let [contacts' (contacts-by-identity contacts)]
|
||||||
[list-view {:renderRow (message-row contacts' group-chat)
|
[list-view {:renderRow (message-row contacts' group-chat (count messages))
|
||||||
:renderScrollComponent #(invertible-scroll-view (js->clj %))
|
:renderScrollComponent #(invertible-scroll-view (js->clj %))
|
||||||
:onEndReached #(dispatch [:load-more-messages])
|
:onEndReached #(dispatch [:load-more-messages])
|
||||||
:enableEmptySections true
|
:enableEmptySections true
|
||||||
:dataSource (to-datasource messages)}]))
|
:keyboardShouldPersistTaps true
|
||||||
|
:dataSource (to-datasource-inverted messages)}]))
|
||||||
|
|
||||||
|
(defn messages-container-animation-logic
|
||||||
|
[{:keys [offset val]}]
|
||||||
|
(fn [_]
|
||||||
|
(anim/start (anim/spring val {:toValue @offset}))))
|
||||||
|
|
||||||
|
(defn messages-container [messages]
|
||||||
|
(let [offset (subscribe [:messages-offset])
|
||||||
|
messages-offset (anim/create-value 0)
|
||||||
|
context {:offset offset
|
||||||
|
:val messages-offset}
|
||||||
|
on-update (messages-container-animation-logic context)]
|
||||||
|
(r/create-class
|
||||||
|
{:component-did-mount
|
||||||
|
on-update
|
||||||
|
:component-did-update
|
||||||
|
on-update
|
||||||
|
:reagent-render
|
||||||
|
(fn [messages]
|
||||||
|
@offset
|
||||||
|
[animated-view {:style (st/messages-container messages-offset)}
|
||||||
|
messages])})))
|
||||||
|
|
||||||
(defview chat []
|
(defview chat []
|
||||||
[group-chat [:chat :group-chat]
|
[group-chat [:chat :group-chat]
|
||||||
show-actions-atom [:show-actions]]
|
show-actions-atom [:show-actions]
|
||||||
[view st/chat-view
|
command [:get-chat-command]
|
||||||
|
command? [:command?]
|
||||||
|
suggestions [:get-suggestions]
|
||||||
|
to-msg-id [:get-chat-command-to-msg-id]
|
||||||
|
layout-height [:get :layout-height]]
|
||||||
|
[view {:style st/chat-view
|
||||||
|
:onLayout (fn [event]
|
||||||
|
(let [height (.. event -nativeEvent -layout -height)]
|
||||||
|
(when (not= height layout-height)
|
||||||
|
(dispatch [:set-layout-height height]))))}
|
||||||
[chat-toolbar]
|
[chat-toolbar]
|
||||||
[messages-view group-chat]
|
[messages-container
|
||||||
|
[messages-view group-chat]]
|
||||||
(when group-chat [typing-all])
|
(when group-chat [typing-all])
|
||||||
|
[response-view]
|
||||||
|
(when-not command? [suggestion-container])
|
||||||
[chat-message-new]
|
[chat-message-new]
|
||||||
(when show-actions-atom [actions-view])])
|
(when show-actions-atom [actions-view])])
|
||||||
|
|
|
@ -173,7 +173,7 @@
|
||||||
(dispatch [:received-msg
|
(dispatch [:received-msg
|
||||||
{:msg-id msg-id
|
{:msg-id msg-id
|
||||||
:content (command-content
|
:content (command-content
|
||||||
:keypair-password
|
:keypair
|
||||||
(label :t/keypair-generated))
|
(label :t/keypair-generated))
|
||||||
:content-type content-type-command-request
|
:content-type content-type-command-request
|
||||||
:outgoing false
|
:outgoing false
|
||||||
|
@ -184,6 +184,9 @@
|
||||||
(def console-chat
|
(def console-chat
|
||||||
{:chat-id "console"
|
{:chat-id "console"
|
||||||
:name "console"
|
:name "console"
|
||||||
|
; todo remove/change dapp config fot console
|
||||||
|
:dapp-url "http://localhost:8185/resources"
|
||||||
|
:dapp-hash 858845357
|
||||||
:color default-chat-color
|
:color default-chat-color
|
||||||
:group-chat false
|
:group-chat false
|
||||||
:is-active true
|
:is-active true
|
||||||
|
|
|
@ -15,8 +15,7 @@
|
||||||
(def suggestion-height 56)
|
(def suggestion-height 56)
|
||||||
|
|
||||||
(def suggestion-container
|
(def suggestion-container
|
||||||
{:flexDirection :column
|
{:paddingLeft 16
|
||||||
:paddingLeft 16
|
|
||||||
:backgroundColor color-white})
|
:backgroundColor color-white})
|
||||||
|
|
||||||
(def suggestion-sub-container
|
(def suggestion-sub-container
|
||||||
|
@ -37,18 +36,12 @@
|
||||||
:color text2-color})
|
:color text2-color})
|
||||||
|
|
||||||
(defn suggestions-container [suggestions-count]
|
(defn suggestions-container [suggestions-count]
|
||||||
{:flexDirection :row
|
{:flex 1
|
||||||
:marginVertical 1
|
:marginVertical 1
|
||||||
:marginHorizontal 0
|
:marginHorizontal 0
|
||||||
:height (min 150 (* suggestion-height suggestions-count))
|
:height (min 150 (* suggestion-height suggestions-count))
|
||||||
:backgroundColor color-white
|
:backgroundColor color-white
|
||||||
:borderRadius 5})
|
:borderRadius 5})
|
||||||
|
|
||||||
(def drag-down-touchable
|
(def container
|
||||||
{:height 22
|
{:backgroundColor color-white})
|
||||||
:alignItems :center
|
|
||||||
:justifyContent :center})
|
|
||||||
|
|
||||||
(def drag-down-icon
|
|
||||||
{:width 16
|
|
||||||
:height 16})
|
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
(ns status-im.chat.styles.dragdown
|
||||||
|
(:require [status-im.components.styles :refer [color-white]]))
|
||||||
|
|
||||||
|
(def drag-down-touchable
|
||||||
|
{:height 22
|
||||||
|
:background-color color-white
|
||||||
|
:alignItems :center
|
||||||
|
:justifyContent :center})
|
||||||
|
|
||||||
|
(def drag-down-icon
|
||||||
|
{:width 16
|
||||||
|
:height 16})
|
|
@ -16,13 +16,18 @@
|
||||||
:backgroundColor color-white
|
:backgroundColor color-white
|
||||||
:elevation 4})
|
:elevation 4})
|
||||||
|
|
||||||
|
(def command-container
|
||||||
|
{:left 0
|
||||||
|
:backgroundColor :white
|
||||||
|
:position :absolute})
|
||||||
|
|
||||||
(defn command-text-container
|
(defn command-text-container
|
||||||
[{:keys [color]}]
|
[{:keys [color]}]
|
||||||
{:flexDirection :column
|
{:flexDirection :column
|
||||||
:marginTop 16
|
:marginTop 16
|
||||||
:marginBottom 16
|
:marginBottom 16
|
||||||
:marginLeft 16
|
:marginLeft 16
|
||||||
:marginRight 0
|
:marginRight 8
|
||||||
:backgroundColor color
|
:backgroundColor color
|
||||||
:height 24
|
:height 24
|
||||||
:borderRadius 50})
|
:borderRadius 50})
|
||||||
|
@ -37,7 +42,8 @@
|
||||||
(def command-input
|
(def command-input
|
||||||
{:flex 1
|
{:flex 1
|
||||||
:marginLeft 8
|
:marginLeft 8
|
||||||
:marginTop 7
|
:marginTop -2
|
||||||
|
:padding 0
|
||||||
:fontSize 14
|
:fontSize 14
|
||||||
:fontFamily font
|
:fontFamily font
|
||||||
:color text1-color})
|
:color text1-color})
|
||||||
|
|
|
@ -131,20 +131,35 @@
|
||||||
(def command-request-message-view
|
(def command-request-message-view
|
||||||
{:borderRadius 14
|
{:borderRadius 14
|
||||||
:padding 12
|
:padding 12
|
||||||
|
:paddingRight 28
|
||||||
:backgroundColor color-white})
|
:backgroundColor color-white})
|
||||||
|
|
||||||
(def command-request-from-text
|
(def command-request-from-text
|
||||||
(merge style-sub-text {:marginBottom 2}))
|
(merge style-sub-text {:marginBottom 2}))
|
||||||
|
|
||||||
(defn command-request-image-view
|
(def command-request-image-touchable
|
||||||
[command]
|
|
||||||
{:position :absolute
|
{:position :absolute
|
||||||
:top 12
|
:top 4
|
||||||
:right 0
|
:right -8
|
||||||
:width 32
|
:alignItems :center
|
||||||
|
:justifyContent :center
|
||||||
|
:width 48
|
||||||
|
:height 48})
|
||||||
|
|
||||||
|
(defn command-request-image-view [command scale]
|
||||||
|
{:width 32
|
||||||
:height 32
|
:height 32
|
||||||
:borderRadius 50
|
:borderRadius 50
|
||||||
:backgroundColor (:color command)})
|
:backgroundColor (:color command)
|
||||||
|
:transform [{:scale scale}]})
|
||||||
|
|
||||||
|
(def command-image-view
|
||||||
|
{:position :absolute
|
||||||
|
:top 0
|
||||||
|
:right 0
|
||||||
|
:width 24
|
||||||
|
:height 24
|
||||||
|
:alignItems :center})
|
||||||
|
|
||||||
(def command-request-image
|
(def command-request-image
|
||||||
{:position :absolute
|
{:position :absolute
|
||||||
|
@ -178,11 +193,10 @@
|
||||||
:color color-white})
|
:color color-white})
|
||||||
|
|
||||||
(def command-image
|
(def command-image
|
||||||
{:position :absolute
|
{:margin-top 5
|
||||||
:top 4
|
|
||||||
:right 0
|
|
||||||
:width 12
|
:width 12
|
||||||
:height 13})
|
:height 13
|
||||||
|
:tint-color :#a9a9a9cc})
|
||||||
|
|
||||||
(def command-text
|
(def command-text
|
||||||
(merge style-message-text
|
(merge style-message-text
|
||||||
|
@ -309,6 +323,9 @@
|
||||||
(def message-date-text
|
(def message-date-text
|
||||||
(assoc style-sub-text :textAlign :center))
|
(assoc style-sub-text :textAlign :center))
|
||||||
|
|
||||||
|
(defn message-container [height]
|
||||||
|
{:height height})
|
||||||
|
|
||||||
(def new-message-container
|
(def new-message-container
|
||||||
{:backgroundColor color-white
|
{:backgroundColor color-white
|
||||||
:elevation 4})
|
:elevation 4})
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
(ns status-im.chat.styles.message-input
|
||||||
|
(:require [status-im.components.styles :refer [color-white
|
||||||
|
color-blue]]
|
||||||
|
[status-im.chat.constants :refer [input-height]]))
|
||||||
|
|
||||||
|
(def message-input-container
|
||||||
|
{:flex 1
|
||||||
|
:marginRight 0})
|
||||||
|
|
||||||
|
(def input-container
|
||||||
|
{:flexDirection :column})
|
||||||
|
|
||||||
|
(def input-view
|
||||||
|
{:flexDirection :row
|
||||||
|
:height input-height
|
||||||
|
:backgroundColor color-white})
|
||||||
|
|
||||||
|
(def send-icon
|
||||||
|
{:marginTop 10.5
|
||||||
|
:marginLeft 12
|
||||||
|
:width 15
|
||||||
|
:height 15})
|
||||||
|
|
||||||
|
(def send-container
|
||||||
|
{:marginTop 10
|
||||||
|
:marginRight 10
|
||||||
|
:width 36
|
||||||
|
:height 36
|
||||||
|
:borderRadius 50
|
||||||
|
:backgroundColor color-blue})
|
|
@ -1,55 +0,0 @@
|
||||||
(ns status-im.chat.styles.plain-input
|
|
||||||
(:require [status-im.components.styles :refer [font
|
|
||||||
text2-color
|
|
||||||
color-white
|
|
||||||
color-blue]]))
|
|
||||||
|
|
||||||
(def input-container
|
|
||||||
{:flexDirection :column})
|
|
||||||
|
|
||||||
(def input-view
|
|
||||||
{:flexDirection :row
|
|
||||||
:height 56
|
|
||||||
:backgroundColor color-white})
|
|
||||||
|
|
||||||
(def switch-commands-touchable
|
|
||||||
{:width 56
|
|
||||||
:height 56
|
|
||||||
:alignItems :center
|
|
||||||
:justifyContent :center})
|
|
||||||
|
|
||||||
(def list-icon
|
|
||||||
{:width 13
|
|
||||||
:height 12})
|
|
||||||
|
|
||||||
(def close-icon
|
|
||||||
{:width 12
|
|
||||||
:height 12})
|
|
||||||
|
|
||||||
(def message-input
|
|
||||||
{:flex 1
|
|
||||||
:marginTop -2
|
|
||||||
:padding 0
|
|
||||||
:fontSize 14
|
|
||||||
:fontFamily font
|
|
||||||
:color text2-color})
|
|
||||||
|
|
||||||
(def smile-icon
|
|
||||||
{:marginTop 18
|
|
||||||
:marginRight 18
|
|
||||||
:width 20
|
|
||||||
:height 20})
|
|
||||||
|
|
||||||
(def send-icon
|
|
||||||
{:marginTop 10.5
|
|
||||||
:marginLeft 12
|
|
||||||
:width 15
|
|
||||||
:height 15})
|
|
||||||
|
|
||||||
(def send-container
|
|
||||||
{:marginTop 10
|
|
||||||
:marginRight 10
|
|
||||||
:width 36
|
|
||||||
:height 36
|
|
||||||
:borderRadius 50
|
|
||||||
:backgroundColor color-blue})
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
(ns status-im.chat.styles.plain-message
|
||||||
|
(:require [status-im.components.styles :refer [font
|
||||||
|
text2-color]]))
|
||||||
|
|
||||||
|
(defn message-input-button-touchable [w]
|
||||||
|
{:width w
|
||||||
|
:height 56
|
||||||
|
:alignItems :center
|
||||||
|
:justifyContent :center})
|
||||||
|
|
||||||
|
(defn message-input-button [scale]
|
||||||
|
{:transform [{:scale scale}]
|
||||||
|
:width 24
|
||||||
|
:height 24
|
||||||
|
:alignItems :center
|
||||||
|
:justifyContent :center})
|
||||||
|
|
||||||
|
(def list-icon
|
||||||
|
{:width 16
|
||||||
|
:height 16})
|
||||||
|
|
||||||
|
(def requests-icon
|
||||||
|
{:background-color :#7099e6
|
||||||
|
:width 8
|
||||||
|
:height 8
|
||||||
|
:border-radius 8
|
||||||
|
:left 0
|
||||||
|
:top 0
|
||||||
|
:position :absolute})
|
||||||
|
|
||||||
|
(def close-icon
|
||||||
|
{:width 12
|
||||||
|
:height 12})
|
||||||
|
|
||||||
|
(def message-input
|
||||||
|
{:flex 1
|
||||||
|
:marginTop -2
|
||||||
|
:padding 0
|
||||||
|
:fontSize 14
|
||||||
|
:fontFamily font
|
||||||
|
:color text2-color})
|
||||||
|
|
||||||
|
(def smile-icon
|
||||||
|
{:width 20
|
||||||
|
:height 20})
|
|
@ -0,0 +1,94 @@
|
||||||
|
(ns status-im.chat.styles.response
|
||||||
|
(:require [status-im.components.styles :refer [font
|
||||||
|
color-white
|
||||||
|
color-blue
|
||||||
|
text1-color
|
||||||
|
text2-color
|
||||||
|
chat-background
|
||||||
|
color-black]]
|
||||||
|
[status-im.chat.constants :refer [input-height request-info-height
|
||||||
|
response-height-normal]]))
|
||||||
|
|
||||||
|
(def drag-container
|
||||||
|
{:height 16
|
||||||
|
:alignItems :center
|
||||||
|
:justifyContent :center})
|
||||||
|
|
||||||
|
(def drag-icon
|
||||||
|
{:width 14
|
||||||
|
:height 3})
|
||||||
|
|
||||||
|
(def command-icon-container
|
||||||
|
{:marginTop 1
|
||||||
|
:marginLeft 12
|
||||||
|
:width 32
|
||||||
|
:height 32
|
||||||
|
:alignItems :center
|
||||||
|
:justifyContent :center
|
||||||
|
:borderRadius 50
|
||||||
|
:backgroundColor color-white})
|
||||||
|
|
||||||
|
(def command-icon
|
||||||
|
{:width 9
|
||||||
|
:height 15})
|
||||||
|
|
||||||
|
(def info-container
|
||||||
|
{:flex 1
|
||||||
|
:marginLeft 12})
|
||||||
|
|
||||||
|
(def command-name
|
||||||
|
{:marginTop 0
|
||||||
|
:fontSize 12
|
||||||
|
:fontFamily font
|
||||||
|
:color color-white})
|
||||||
|
|
||||||
|
(def message-info
|
||||||
|
{:marginTop 1
|
||||||
|
:fontSize 12
|
||||||
|
:fontFamily font
|
||||||
|
:opacity 0.69
|
||||||
|
:color color-white})
|
||||||
|
|
||||||
|
(defn response-view [height]
|
||||||
|
{:flexDirection :column
|
||||||
|
:position :absolute
|
||||||
|
:left 0
|
||||||
|
:right 0
|
||||||
|
:bottom 0
|
||||||
|
:height height
|
||||||
|
:backgroundColor color-white
|
||||||
|
:elevation 2})
|
||||||
|
|
||||||
|
(def input-placeholder
|
||||||
|
{:height input-height})
|
||||||
|
|
||||||
|
(defn request-info [color]
|
||||||
|
{:flexDirection :column
|
||||||
|
:height request-info-height
|
||||||
|
:backgroundColor color})
|
||||||
|
|
||||||
|
(def inner-container
|
||||||
|
{:flexDirection :row
|
||||||
|
:height 45})
|
||||||
|
|
||||||
|
(def cancel-container
|
||||||
|
{:marginTop 2.5
|
||||||
|
:marginRight 16
|
||||||
|
:width 24
|
||||||
|
:height 24})
|
||||||
|
|
||||||
|
(def cancel-icon
|
||||||
|
{:marginTop 6
|
||||||
|
:marginLeft 6
|
||||||
|
:width 12
|
||||||
|
:height 12})
|
||||||
|
|
||||||
|
(defn command-input [ml disbale?]
|
||||||
|
{:flex 1
|
||||||
|
:marginRight 16
|
||||||
|
:margin-left (- ml 5)
|
||||||
|
:marginTop -2
|
||||||
|
:padding 0
|
||||||
|
:fontSize 14
|
||||||
|
:fontFamily font
|
||||||
|
:color (if disbale? color-white text1-color)})
|
|
@ -14,6 +14,10 @@
|
||||||
{:flex 1
|
{:flex 1
|
||||||
:backgroundColor chat-background})
|
:backgroundColor chat-background})
|
||||||
|
|
||||||
|
(defn messages-container [bottom]
|
||||||
|
{:flex 1
|
||||||
|
:bottom bottom})
|
||||||
|
|
||||||
(def toolbar-view
|
(def toolbar-view
|
||||||
{:flexDirection :row
|
{:flexDirection :row
|
||||||
:height 56
|
:height 56
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
color-light-blue-transparent
|
color-light-blue-transparent
|
||||||
color-white
|
color-white
|
||||||
color-black
|
color-black
|
||||||
|
color-gray
|
||||||
color-blue
|
color-blue
|
||||||
color-blue-transparent
|
color-blue-transparent
|
||||||
selected-message-color
|
selected-message-color
|
||||||
|
@ -12,22 +13,32 @@
|
||||||
text2-color
|
text2-color
|
||||||
text3-color]]))
|
text3-color]]))
|
||||||
|
|
||||||
(def suggestion-height 88)
|
(def suggestion-height 60)
|
||||||
|
|
||||||
|
(def suggestion-highlight
|
||||||
|
{:margin-left 57})
|
||||||
|
|
||||||
(def suggestion-container
|
(def suggestion-container
|
||||||
{:flexDirection :column
|
{:backgroundColor color-white})
|
||||||
:paddingLeft 16
|
|
||||||
:backgroundColor color-white})
|
|
||||||
|
|
||||||
(def suggestion-sub-container
|
(def suggestion-sub-container
|
||||||
{:height suggestion-height
|
{:height suggestion-height
|
||||||
:borderBottomWidth 1
|
:borderBottomWidth 1
|
||||||
:borderBottomColor separator-color})
|
:borderBottomColor separator-color
|
||||||
|
:flex-direction :row})
|
||||||
|
|
||||||
|
(def command-description-container
|
||||||
|
{:flex 0.6})
|
||||||
|
|
||||||
|
(def command-label-container
|
||||||
|
{:flex 0.4
|
||||||
|
:flex-direction :column
|
||||||
|
:align-items :flex-end
|
||||||
|
:margin-right 16})
|
||||||
|
|
||||||
(defn suggestion-background
|
(defn suggestion-background
|
||||||
[{:keys [color]}]
|
[{:keys [color]}]
|
||||||
{:alignSelf :flex-start
|
{:marginTop 10
|
||||||
:marginTop 10
|
|
||||||
:height 24
|
:height 24
|
||||||
:backgroundColor color
|
:backgroundColor color
|
||||||
:borderRadius 50})
|
:borderRadius 50})
|
||||||
|
@ -39,6 +50,14 @@
|
||||||
:fontFamily font
|
:fontFamily font
|
||||||
:color color-white})
|
:color color-white})
|
||||||
|
|
||||||
|
(def title-container
|
||||||
|
{:margin-left 57
|
||||||
|
:margin-bottom 16})
|
||||||
|
|
||||||
|
(def title-text
|
||||||
|
{:font-size 13
|
||||||
|
:color :#8f838c93})
|
||||||
|
|
||||||
(def value-text
|
(def value-text
|
||||||
{:marginTop 6
|
{:marginTop 6
|
||||||
:fontSize 14
|
:fontSize 14
|
||||||
|
@ -51,19 +70,53 @@
|
||||||
:fontFamily font
|
:fontFamily font
|
||||||
:color text2-color})
|
:color text2-color})
|
||||||
|
|
||||||
(defn suggestions-container [suggestions-count]
|
(defn container [height]
|
||||||
{:flexDirection :row
|
{:flexDirection :column
|
||||||
:marginVertical 1
|
:position :absolute
|
||||||
:marginHorizontal 0
|
:left 0
|
||||||
:height (min 168 (* suggestion-height suggestions-count))
|
:right 0
|
||||||
|
:bottom 0
|
||||||
|
:height height
|
||||||
:backgroundColor color-white
|
:backgroundColor color-white
|
||||||
:borderRadius 5})
|
:elevation 2})
|
||||||
|
|
||||||
(def drag-down-touchable
|
(def request-container
|
||||||
{:height 22
|
{:height 56
|
||||||
:alignItems :center
|
:flex-direction :row})
|
||||||
|
|
||||||
|
(def request-icon-container
|
||||||
|
{:height 56
|
||||||
|
:width 57
|
||||||
|
:align-items :center
|
||||||
:justifyContent :center})
|
:justifyContent :center})
|
||||||
|
|
||||||
(def drag-down-icon
|
(defn request-icon-background [color]
|
||||||
{:width 16
|
{:height 32
|
||||||
:height 16})
|
:width 32
|
||||||
|
:border-radius 32
|
||||||
|
:background-color color
|
||||||
|
:align-items :center
|
||||||
|
:justifyContent :center})
|
||||||
|
|
||||||
|
(def request-icon
|
||||||
|
{:width 12
|
||||||
|
:height 12})
|
||||||
|
|
||||||
|
(def request-info-container
|
||||||
|
{:height 56
|
||||||
|
:padding-top 10})
|
||||||
|
|
||||||
|
(def request-info-description
|
||||||
|
{:font-size 12
|
||||||
|
:color color-black})
|
||||||
|
|
||||||
|
(def request-message-info
|
||||||
|
{:font-size 12
|
||||||
|
:margin-top 2
|
||||||
|
:color color-gray})
|
||||||
|
|
||||||
|
(def header-icon
|
||||||
|
{:background-color :#838c93
|
||||||
|
:width 14
|
||||||
|
:border-radius 1
|
||||||
|
:height 3})
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
(ns status-im.chat.subs
|
(ns status-im.chat.subs
|
||||||
(:require-macros [reagent.ratom :refer [reaction]])
|
(:require-macros [reagent.ratom :refer [reaction]])
|
||||||
(:require [re-frame.core :refer [register-sub]]
|
(:require [re-frame.core :refer [register-sub dispatch subscribe path]]
|
||||||
[status-im.db :as db]
|
|
||||||
;todo handlers in subs?...
|
|
||||||
[status-im.chat.suggestions :refer
|
|
||||||
[get-suggestions typing-command? get-content-suggestions]]
|
|
||||||
[status-im.models.commands :as commands]
|
[status-im.models.commands :as commands]
|
||||||
[status-im.handlers.content-suggestions :refer [get-content-suggestions]]))
|
[status-im.constants :refer [response-suggesstion-resize-duration]]
|
||||||
|
[status-im.chat.constants :as c]
|
||||||
|
[status-im.handlers.content-suggestions :refer [get-content-suggestions]]
|
||||||
|
[status-im.chat.views.plain-message :as plain-message]
|
||||||
|
[status-im.chat.views.command :as command]))
|
||||||
|
|
||||||
(register-sub :chat-properties
|
(register-sub :chat-properties
|
||||||
(fn [db [_ properties]]
|
(fn [db [_ properties]]
|
||||||
|
@ -40,16 +40,25 @@
|
||||||
|
|
||||||
(register-sub :get-suggestions
|
(register-sub :get-suggestions
|
||||||
(fn [db _]
|
(fn [db _]
|
||||||
(let [input-text (->> (:current-chat-id @db)
|
(let [chat-id (subscribe [:get-current-chat-id])]
|
||||||
db/chat-input-text-path
|
(reaction (get-in @db [:command-suggestions @chat-id])))))
|
||||||
(get-in @db)
|
|
||||||
(reaction))]
|
|
||||||
(reaction (get-suggestions @db @input-text)))))
|
|
||||||
|
|
||||||
(register-sub :get-commands
|
(register-sub :get-commands
|
||||||
(fn [db _]
|
(fn [db _]
|
||||||
(reaction (commands/get-commands @db))))
|
(reaction (commands/get-commands @db))))
|
||||||
|
|
||||||
|
(register-sub :get-responses
|
||||||
|
(fn [db _]
|
||||||
|
(let [current-chat (@db :current-chat-id)]
|
||||||
|
(reaction (or (get-in @db [:chats current-chat :responses]) {})))))
|
||||||
|
|
||||||
|
(register-sub :get-commands-and-responses
|
||||||
|
(fn [db _]
|
||||||
|
(let [current-chat (@db :current-chat-id)]
|
||||||
|
(reaction _ (or (->> (get-in @db [:chats current-chat])
|
||||||
|
((juxt :commands :responses))
|
||||||
|
(apply merge)) {})))))
|
||||||
|
|
||||||
(register-sub :get-chat-input-text
|
(register-sub :get-chat-input-text
|
||||||
(fn [db _]
|
(fn [db _]
|
||||||
(->> [:chats (:current-chat-id @db) :input-text]
|
(->> [:chats (:current-chat-id @db) :input-text]
|
||||||
|
@ -62,14 +71,39 @@
|
||||||
(get-in @db)
|
(get-in @db)
|
||||||
(reaction))))
|
(reaction))))
|
||||||
|
|
||||||
|
(register-sub :valid-plain-message?
|
||||||
|
(fn [_ _]
|
||||||
|
(let [input-message (subscribe [:get-chat-input-text])
|
||||||
|
staged-commands (subscribe [:get-chat-staged-commands])]
|
||||||
|
(reaction
|
||||||
|
(plain-message/message-valid? @staged-commands @input-message)))))
|
||||||
|
|
||||||
|
(register-sub :valid-command?
|
||||||
|
(fn [_ [_ validator]]
|
||||||
|
(let [input (subscribe [:get-chat-command-content])]
|
||||||
|
(reaction (command/valid? @input validator)))))
|
||||||
|
|
||||||
(register-sub :get-chat-command
|
(register-sub :get-chat-command
|
||||||
(fn [db _]
|
(fn [db _]
|
||||||
(reaction (commands/get-chat-command @db))))
|
(reaction (commands/get-chat-command @db))))
|
||||||
|
|
||||||
|
(register-sub :get-command-parameter
|
||||||
|
(fn [db]
|
||||||
|
(let [command (subscribe [:get-chat-command])
|
||||||
|
chat-id (subscribe [:get-current-chat-id])]
|
||||||
|
(reaction
|
||||||
|
(let [path [:chats @chat-id :command-input :parameter-idx]
|
||||||
|
n (get-in @db path)]
|
||||||
|
(when n (nth (:params @command) n)))))))
|
||||||
|
|
||||||
(register-sub :get-chat-command-content
|
(register-sub :get-chat-command-content
|
||||||
(fn [db _]
|
(fn [db _]
|
||||||
(reaction (commands/get-chat-command-content @db))))
|
(reaction (commands/get-chat-command-content @db))))
|
||||||
|
|
||||||
|
(register-sub :get-chat-command-to-msg-id
|
||||||
|
(fn [db _]
|
||||||
|
(reaction (commands/get-chat-command-to-msg-id @db))))
|
||||||
|
|
||||||
(register-sub :chat-command-request
|
(register-sub :chat-command-request
|
||||||
(fn [db _]
|
(fn [db _]
|
||||||
(reaction (commands/get-chat-command-request @db))))
|
(reaction (commands/get-chat-command-request @db))))
|
||||||
|
@ -83,12 +117,58 @@
|
||||||
(fn [db [_ chat-id]]
|
(fn [db [_ chat-id]]
|
||||||
(reaction (get-in @db [:chats chat-id]))))
|
(reaction (get-in @db [:chats chat-id]))))
|
||||||
|
|
||||||
(register-sub :typing-command?
|
|
||||||
(fn [db _]
|
|
||||||
(reaction (typing-command? @db))))
|
|
||||||
|
|
||||||
(register-sub :get-content-suggestions
|
(register-sub :get-content-suggestions
|
||||||
(fn [db _]
|
(fn [db _]
|
||||||
(let [command (reaction (commands/get-chat-command @db))
|
(reaction (get-in @db [:suggestions (:current-chat-id @db)]))))
|
||||||
text (reaction (commands/get-chat-command-content @db))]
|
|
||||||
(reaction (get-content-suggestions @db @command @text)))))
|
(register-sub :command?
|
||||||
|
(fn [db]
|
||||||
|
(->> (get-in @db [:edit-mode (:current-chat-id @db)])
|
||||||
|
(= :command)
|
||||||
|
(reaction))))
|
||||||
|
|
||||||
|
(register-sub :command-type
|
||||||
|
(fn []
|
||||||
|
(let [command (subscribe [:get-chat-command])]
|
||||||
|
(reaction (:type @command)))))
|
||||||
|
|
||||||
|
(register-sub :messages-offset
|
||||||
|
(fn []
|
||||||
|
(let [command? (subscribe [:command?])
|
||||||
|
type (subscribe [:command-type])
|
||||||
|
command-suggestions (subscribe [:get-content-suggestions])
|
||||||
|
suggestions (subscribe [:get-suggestions])]
|
||||||
|
(reaction
|
||||||
|
(cond (and @command? (= @type :response))
|
||||||
|
c/request-info-height
|
||||||
|
|
||||||
|
(and @command? (= @type :command) (seq @command-suggestions))
|
||||||
|
c/suggestions-header-height
|
||||||
|
|
||||||
|
(and (not @command?) (seq @suggestions))
|
||||||
|
c/suggestions-header-height
|
||||||
|
|
||||||
|
:else 0)))))
|
||||||
|
|
||||||
|
(register-sub :command-icon-width
|
||||||
|
(fn []
|
||||||
|
(let [width (subscribe [:get :command-icon-width])
|
||||||
|
type (subscribe [:command-type])]
|
||||||
|
(reaction (if (= :command @type)
|
||||||
|
@width
|
||||||
|
0)))))
|
||||||
|
|
||||||
|
(register-sub :get-requests
|
||||||
|
(fn [db]
|
||||||
|
(let [chat-id (subscribe [:get-current-chat-id])]
|
||||||
|
(reaction (get-in @db [:chats @chat-id :requests])))))
|
||||||
|
|
||||||
|
(register-sub :get-response
|
||||||
|
(fn [db [_ n]]
|
||||||
|
(let [chat-id (subscribe [:get-current-chat-id])]
|
||||||
|
(reaction (get-in @db [:chats @chat-id :responses n])))))
|
||||||
|
|
||||||
|
(register-sub :is-request-answered?
|
||||||
|
(fn [_ [_ message-id]]
|
||||||
|
(let [requests (subscribe [:get-requests])]
|
||||||
|
(reaction (not (some #(= message-id (:message-id %)) @requests))))))
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
(ns status-im.chat.suggestions
|
(ns status-im.chat.suggestions
|
||||||
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
|
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
|
||||||
[status-im.db :as db]
|
[status-im.db :as db]
|
||||||
[status-im.models.commands :refer [commands
|
[status-im.models.commands :refer [get-commands
|
||||||
suggestions
|
|
||||||
get-commands
|
|
||||||
get-chat-command-request
|
get-chat-command-request
|
||||||
get-chat-command-to-msg-id
|
get-chat-command-to-msg-id
|
||||||
clear-staged-commands]]
|
clear-staged-commands]]
|
||||||
|
@ -13,11 +11,16 @@
|
||||||
(defn suggestion? [text]
|
(defn suggestion? [text]
|
||||||
(= (get text 0) "!"))
|
(= (get text 0) "!"))
|
||||||
|
|
||||||
(defn get-suggestions [db text]
|
(defn can-be-suggested? [text]
|
||||||
|
(fn [{:keys [name]}]
|
||||||
|
(.startsWith (str "!" name) text)))
|
||||||
|
|
||||||
|
(defn get-suggestions
|
||||||
|
[{:keys [current-chat-id] :as db} text]
|
||||||
|
(let [commands (get-in db [:chats current-chat-id :commands])]
|
||||||
(if (suggestion? text)
|
(if (suggestion? text)
|
||||||
;; TODO change 'commands' to 'suggestions'
|
(filter (fn [[_ v]] ((can-be-suggested? text) v)) commands)
|
||||||
(filterv #(.startsWith (:text %) text) (get-commands db))
|
[])))
|
||||||
[]))
|
|
||||||
|
|
||||||
(defn get-command [db text]
|
(defn get-command [db text]
|
||||||
(when (suggestion? text)
|
(when (suggestion? text)
|
||||||
|
@ -45,22 +48,13 @@
|
||||||
staged-commands))
|
staged-commands))
|
||||||
(clear-staged-commands db)))
|
(clear-staged-commands db)))
|
||||||
|
|
||||||
(defn execute-commands-js [body]
|
|
||||||
(.eval js/window body)
|
|
||||||
(let [commands (.-commands js/window)]
|
|
||||||
(dispatch [:set-commands (map #(update % :command keyword)
|
|
||||||
(js->clj commands :keywordize-keys true))])))
|
|
||||||
|
|
||||||
(defn load-commands []
|
|
||||||
(http-get "chat-commands.js" execute-commands-js nil))
|
|
||||||
|
|
||||||
(defn check-suggestion [db message]
|
(defn check-suggestion [db message]
|
||||||
(when-let [suggestion-text (when (string? message)
|
(when-let [suggestion-text (when (string? message)
|
||||||
(re-matches #"^![^\s]+\s" message))]
|
(re-matches #"^![^\s]+\s" message))]
|
||||||
(let [suggestion-text' (s/trim suggestion-text)
|
(let [suggestion-text' (s/trim suggestion-text)]
|
||||||
[suggestion] (filter #(= suggestion-text' (:text %))
|
(->> (get-commands db)
|
||||||
(get-commands db))]
|
(filter #(= suggestion-text' (->> % second :name (str "!"))))
|
||||||
suggestion)))
|
first))))
|
||||||
|
|
||||||
(defn typing-command? [db]
|
(defn typing-command? [db]
|
||||||
(-> db
|
(-> db
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
(ns status-im.chat.suggestions-responder
|
||||||
|
(:require [status-im.components.drag-drop :as drag]
|
||||||
|
[status-im.components.animation :as anim]
|
||||||
|
[status-im.components.react :as react]
|
||||||
|
[re-frame.core :refer [dispatch]]))
|
||||||
|
|
||||||
|
;; todo bad name. Ideas?
|
||||||
|
(defn enough-dy [gesture]
|
||||||
|
(> (Math/abs (.-dy gesture)) 10))
|
||||||
|
|
||||||
|
(defn on-move [response-height kb-height orientation]
|
||||||
|
(fn [_ gesture]
|
||||||
|
(when (enough-dy gesture)
|
||||||
|
(let [w (react/get-dimensions "window")
|
||||||
|
;; depending on orientation use height or width of screen
|
||||||
|
prop (if (= :portrait @orientation)
|
||||||
|
:height
|
||||||
|
:width)
|
||||||
|
;; subtract keyboard height to get "real height" of screen
|
||||||
|
;; then subtract gesture position to get suggestions height
|
||||||
|
;; todo maybe it is better to use margin-top instead height
|
||||||
|
;; it is not obvious
|
||||||
|
to-value (- (prop w) @kb-height (.-moveY gesture))]
|
||||||
|
(anim/start
|
||||||
|
(anim/spring response-height {:toValue to-value}))))))
|
||||||
|
|
||||||
|
(defn on-release [response-height handler-name]
|
||||||
|
(fn [_ gesture]
|
||||||
|
(when (enough-dy gesture)
|
||||||
|
(dispatch [handler-name
|
||||||
|
(.-vy gesture)
|
||||||
|
;; todo access to "private" property
|
||||||
|
;; better to find another way...
|
||||||
|
(.-_value response-height)]))))
|
||||||
|
|
||||||
|
(defn pan-responder [response-height kb-height orientation handler-name]
|
||||||
|
(drag/create-pan-responder
|
||||||
|
{:on-move (on-move response-height kb-height orientation)
|
||||||
|
:on-release (on-release response-height handler-name)}))
|
|
@ -1,16 +1,15 @@
|
||||||
(ns status-im.chat.views.command
|
(ns status-im.chat.views.command
|
||||||
|
(:require-macros [status-im.utils.views :refer [defview]])
|
||||||
(:require [re-frame.core :refer [subscribe dispatch]]
|
(:require [re-frame.core :refer [subscribe dispatch]]
|
||||||
[status-im.components.react :refer [view
|
[status-im.components.react :refer [view
|
||||||
icon
|
icon
|
||||||
text
|
text
|
||||||
text-input
|
text-input
|
||||||
touchable-highlight]]
|
touchable-highlight]]
|
||||||
[status-im.chat.views.content-suggestions :refer
|
|
||||||
[content-suggestions-view]]
|
|
||||||
[status-im.chat.styles.input :as st]))
|
[status-im.chat.styles.input :as st]))
|
||||||
|
|
||||||
(defn cancel-command-input []
|
(defn cancel-command-input []
|
||||||
(dispatch [:cancel-command]))
|
(dispatch [:start-cancel-command]))
|
||||||
|
|
||||||
(defn set-input-message [message]
|
(defn set-input-message [message]
|
||||||
(dispatch [:set-chat-command-content message]))
|
(dispatch [:set-chat-command-content message]))
|
||||||
|
@ -24,28 +23,22 @@
|
||||||
(validator message)
|
(validator message)
|
||||||
(pos? (count message))))
|
(pos? (count message))))
|
||||||
|
|
||||||
(defn simple-command-input-view [command input-options & {:keys [validator]}]
|
(defn try-send [message validator]
|
||||||
(let [message-atom (subscribe [:get-chat-command-content])]
|
|
||||||
(fn [command input-options & {:keys [validator]}]
|
|
||||||
(let [message @message-atom]
|
|
||||||
[view st/command-input-and-suggestions-container
|
|
||||||
[content-suggestions-view]
|
|
||||||
[view st/command-input-container
|
|
||||||
[view (st/command-text-container command)
|
|
||||||
[text {:style st/command-text} (:text command)]]
|
|
||||||
[text-input (merge {:style st/command-input
|
|
||||||
:autoFocus true
|
|
||||||
:onChangeText set-input-message
|
|
||||||
:onSubmitEditing (fn []
|
|
||||||
(when (valid? message validator)
|
(when (valid? message validator)
|
||||||
(send-command)))
|
(send-command)))
|
||||||
:accessibility-label :command-input}
|
|
||||||
input-options)
|
(defview command-icon [command]
|
||||||
message]
|
[icon-width [:get :command-icon-width]]
|
||||||
(if (valid? message validator)
|
[view st/command-container
|
||||||
[touchable-highlight {:on-press send-command
|
[view {:style (st/command-text-container command)
|
||||||
:accessibility-label :stage-command}
|
:onLayout (fn [event]
|
||||||
[view st/send-container [icon :send st/send-icon]]]
|
(let [width (.. event -nativeEvent -layout -width)]
|
||||||
|
(when (not= icon-width width)
|
||||||
|
(dispatch [:set :command-icon-width width]))))}
|
||||||
|
[text {:style st/command-text} (str "!" (:name command))]]])
|
||||||
|
|
||||||
|
(defn cancel-button []
|
||||||
[touchable-highlight {:on-press cancel-command-input}
|
[touchable-highlight {:on-press cancel-command-input}
|
||||||
[view st/cancel-container
|
[view st/cancel-container
|
||||||
[icon :close-gray st/cancel-icon]]])]]))))
|
[icon :close-gray st/cancel-icon]]])
|
||||||
|
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
(ns status-im.chat.views.confirmation-code
|
|
||||||
(:require
|
|
||||||
[status-im.chat.views.command :refer [simple-command-input-view]]))
|
|
||||||
|
|
||||||
(defn confirmation-code-input-view [command]
|
|
||||||
[simple-command-input-view command {:keyboardType :numeric}])
|
|
|
@ -1,36 +0,0 @@
|
||||||
(ns status-im.chat.views.content-suggestions
|
|
||||||
(:require-macros [status-im.utils.views :refer [defview]])
|
|
||||||
(:require [re-frame.core :refer [subscribe dispatch]]
|
|
||||||
[status-im.components.react :refer [view
|
|
||||||
icon
|
|
||||||
text
|
|
||||||
touchable-highlight
|
|
||||||
list-view
|
|
||||||
list-item]]
|
|
||||||
[status-im.chat.styles.content-suggestions :as st]
|
|
||||||
[status-im.utils.listview :refer [to-datasource]]))
|
|
||||||
|
|
||||||
(defn set-command-content [content]
|
|
||||||
(dispatch [:set-chat-command-content content]))
|
|
||||||
|
|
||||||
(defn suggestion-list-item [{:keys [value description]}]
|
|
||||||
[touchable-highlight {:onPress #(set-command-content value)}
|
|
||||||
[view st/suggestion-container
|
|
||||||
[view st/suggestion-sub-container
|
|
||||||
[text {:style st/value-text} value]
|
|
||||||
[text {:style st/description-text} description]]]])
|
|
||||||
|
|
||||||
(defn render-row [row _ _]
|
|
||||||
(list-item [suggestion-list-item row]))
|
|
||||||
|
|
||||||
(defview content-suggestions-view []
|
|
||||||
[suggestions [:get-content-suggestions]]
|
|
||||||
(when (seq suggestions)
|
|
||||||
[view
|
|
||||||
[touchable-highlight {:style st/drag-down-touchable
|
|
||||||
;; TODO hide suggestions?
|
|
||||||
:onPress (fn [])}
|
|
||||||
[view [icon :drag_down st/drag-down-icon]]]
|
|
||||||
[view (st/suggestions-container (count suggestions))
|
|
||||||
[list-view {:dataSource (to-datasource suggestions)
|
|
||||||
:renderRow render-row}]]]))
|
|
|
@ -1,23 +1,29 @@
|
||||||
(ns status-im.chat.views.message
|
(ns status-im.chat.views.message
|
||||||
|
(:require-macros [status-im.utils.views :refer [defview]])
|
||||||
(:require [clojure.string :as s]
|
(:require [clojure.string :as s]
|
||||||
[re-frame.core :refer [subscribe dispatch]]
|
[re-frame.core :refer [subscribe dispatch]]
|
||||||
|
[reagent.core :as r]
|
||||||
[status-im.components.react :refer [view
|
[status-im.components.react :refer [view
|
||||||
text
|
text
|
||||||
image
|
image
|
||||||
|
animated-view
|
||||||
touchable-highlight]]
|
touchable-highlight]]
|
||||||
|
[status-im.components.animation :as anim]
|
||||||
|
[status-im.chat.views.request-message :refer [message-content-command-request]]
|
||||||
[status-im.chat.styles.message :as st]
|
[status-im.chat.styles.message :as st]
|
||||||
[status-im.models.commands :refer [parse-command-msg-content
|
[status-im.models.commands :refer [parse-command-msg-content
|
||||||
parse-command-request]]
|
parse-command-request]]
|
||||||
[status-im.resources :as res]
|
[status-im.resources :as res]
|
||||||
|
[status-im.utils.datetime :as time]
|
||||||
[status-im.constants :refer [text-content-type
|
[status-im.constants :refer [text-content-type
|
||||||
content-type-status
|
content-type-status
|
||||||
content-type-command
|
content-type-command
|
||||||
content-type-command-request]]))
|
content-type-command-request]]))
|
||||||
|
|
||||||
(defn message-date [{:keys [date]}]
|
(defn message-date [timestamp]
|
||||||
[view {}
|
[view {}
|
||||||
[view st/message-date-container
|
[view st/message-date-container
|
||||||
[text {:style st/message-date-text} date]]])
|
[text {:style st/message-date-text} (time/to-short-str timestamp)]]])
|
||||||
|
|
||||||
(defn contact-photo [{:keys [photo-path]}]
|
(defn contact-photo [{:keys [photo-path]}]
|
||||||
[view st/contact-photo-container
|
[view st/contact-photo-container
|
||||||
|
@ -50,55 +56,30 @@
|
||||||
[view st/track-mark]
|
[view st/track-mark]
|
||||||
[text {:style st/track-duration-text} "03:39"]]])
|
[text {:style st/track-duration-text} "03:39"]]])
|
||||||
|
|
||||||
(defn message-content-command [content]
|
(defview message-content-command [content preview]
|
||||||
(let [commands-atom (subscribe [:get-commands])]
|
[commands [:get-commands-and-responses]]
|
||||||
(fn [content]
|
(let [{:keys [command content]} (parse-command-msg-content commands content)
|
||||||
(let [commands @commands-atom
|
{:keys [name icon type]} command]
|
||||||
{:keys [command content]}
|
|
||||||
(parse-command-msg-content commands content)]
|
|
||||||
[view st/content-command-view
|
[view st/content-command-view
|
||||||
[view st/command-container
|
[view st/command-container
|
||||||
[view (st/command-view command)
|
[view (st/command-view command)
|
||||||
[text {:style st/command-name}
|
[text {:style st/command-name}
|
||||||
(:text command)]]]
|
(str (if (= :command type) "!" "") name)]]]
|
||||||
[image {:source (:icon command)
|
(when icon
|
||||||
:style st/command-image}]
|
[view st/command-image-view
|
||||||
[text {:style st/command-text}
|
[image {:source {:uri icon}
|
||||||
;; TODO isn't smart
|
:style st/command-image}]])
|
||||||
(if (= (:command command) :keypair-password)
|
(if preview
|
||||||
"******"
|
preview
|
||||||
content)]]))))
|
[text {:style st/command-text} content])]))
|
||||||
|
|
||||||
(defn set-chat-command [msg-id command]
|
(defn set-chat-command [msg-id command]
|
||||||
(dispatch [:set-response-chat-command msg-id (:command command)]))
|
(dispatch [:set-response-chat-command msg-id (keyword (:name command))]))
|
||||||
|
|
||||||
(defn label [{:keys [command]}]
|
(defn label [{:keys [command]}]
|
||||||
(->> (name command)
|
(->> (when command (name command))
|
||||||
(str "request-")))
|
(str "request-")))
|
||||||
|
|
||||||
(defn message-content-command-request
|
|
||||||
[{:keys [msg-id content from incoming-group]}]
|
|
||||||
(let [commands-atom (subscribe [:get-commands])]
|
|
||||||
(fn [{:keys [msg-id content from incoming-group]}]
|
|
||||||
(let [commands @commands-atom
|
|
||||||
{:keys [command content]} (parse-command-request commands content)]
|
|
||||||
[touchable-highlight {:onPress #(set-chat-command msg-id command)
|
|
||||||
:accessibility-label (label command)}
|
|
||||||
[view st/comand-request-view
|
|
||||||
[view st/command-request-message-view
|
|
||||||
(when incoming-group
|
|
||||||
[text {:style st/command-request-from-text}
|
|
||||||
from])
|
|
||||||
[text {:style st/style-message-text}
|
|
||||||
content]]
|
|
||||||
[view (st/command-request-image-view command)
|
|
||||||
[image {:source (:request-icon command)
|
|
||||||
:style st/command-request-image}]]
|
|
||||||
(when (:request-text command)
|
|
||||||
[view st/command-request-text-view
|
|
||||||
[text {:style st/style-sub-text}
|
|
||||||
(:request-text command)]])]]))))
|
|
||||||
|
|
||||||
(defn message-view
|
(defn message-view
|
||||||
[message content]
|
[message content]
|
||||||
[view (st/message-view message)
|
[view (st/message-view message)
|
||||||
|
@ -128,9 +109,9 @@
|
||||||
[message-content-status message])
|
[message-content-status message])
|
||||||
|
|
||||||
(defmethod message-content content-type-command
|
(defmethod message-content content-type-command
|
||||||
[wrapper {:keys [content] :as message}]
|
[wrapper {:keys [content rendered-preview] :as message}]
|
||||||
[wrapper message
|
[wrapper message
|
||||||
[message-view message [message-content-command content]]])
|
[message-view message [message-content-command content rendered-preview]]])
|
||||||
|
|
||||||
(defmethod message-content :default
|
(defmethod message-content :default
|
||||||
[wrapper {:keys [content-type content] :as message}]
|
[wrapper {:keys [content-type content] :as message}]
|
||||||
|
@ -185,12 +166,50 @@
|
||||||
(when (and outgoing delivery-status)
|
(when (and outgoing delivery-status)
|
||||||
[message-delivery-status {:delivery-status delivery-status}])]))
|
[message-delivery-status {:delivery-status delivery-status}])]))
|
||||||
|
|
||||||
|
(defn message-container-animation-logic [{:keys [to-value val callback]}]
|
||||||
|
(fn [_]
|
||||||
|
(let [to-value @to-value]
|
||||||
|
(when (< 0 to-value)
|
||||||
|
(anim/start
|
||||||
|
(anim/spring val {:toValue to-value
|
||||||
|
:friction 4
|
||||||
|
:tension 10})
|
||||||
|
(fn [arg]
|
||||||
|
(when (.-finished arg)
|
||||||
|
(callback))))))))
|
||||||
|
|
||||||
|
(defn message-container [message & children]
|
||||||
|
(if (:new? message)
|
||||||
|
(let [layout-height (r/atom 0)
|
||||||
|
anim-value (anim/create-value 1)
|
||||||
|
anim-callback #(dispatch [:set-message-shown message])
|
||||||
|
context {:to-value layout-height
|
||||||
|
:val anim-value
|
||||||
|
:callback anim-callback}
|
||||||
|
on-update (message-container-animation-logic context)]
|
||||||
|
(r/create-class
|
||||||
|
{:component-did-mount
|
||||||
|
on-update
|
||||||
|
:component-did-update
|
||||||
|
on-update
|
||||||
|
:reagent-render
|
||||||
|
(fn [message & children]
|
||||||
|
@layout-height
|
||||||
|
[animated-view {:style (st/message-container anim-value)}
|
||||||
|
(into [view {:onLayout (fn [event]
|
||||||
|
(let [height (.. event -nativeEvent -layout -height)]
|
||||||
|
(reset! layout-height height)))}]
|
||||||
|
children)])}))
|
||||||
|
(into [view] children)))
|
||||||
|
|
||||||
(defn chat-message
|
(defn chat-message
|
||||||
[{:keys [outgoing delivery-status date new-day group-chat]
|
[{:keys [outgoing delivery-status timestamp new-day group-chat]
|
||||||
:as message}]
|
:as message}]
|
||||||
[view {}
|
[message-container message
|
||||||
(when new-day [message-date {:date date}])
|
;; TODO there is no new-day info in message
|
||||||
[view {}
|
(when new-day
|
||||||
|
[message-date timestamp])
|
||||||
|
[view
|
||||||
(let [incoming-group (and group-chat (not outgoing))]
|
(let [incoming-group (and group-chat (not outgoing))]
|
||||||
[message-content
|
[message-content
|
||||||
(if incoming-group
|
(if incoming-group
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
(ns status-im.chat.views.message-input
|
||||||
|
(:require-macros [status-im.utils.views :refer [defview]])
|
||||||
|
(:require [re-frame.core :refer [subscribe]]
|
||||||
|
[status-im.components.react :refer [view
|
||||||
|
animated-view
|
||||||
|
icon
|
||||||
|
touchable-highlight
|
||||||
|
text-input]]
|
||||||
|
[status-im.chat.views.plain-message :as plain-message]
|
||||||
|
[status-im.chat.views.command :as command]
|
||||||
|
[status-im.chat.styles.message-input :as st]
|
||||||
|
[status-im.chat.styles.plain-message :as st-message]
|
||||||
|
[status-im.chat.styles.response :as st-response]))
|
||||||
|
|
||||||
|
(defn send-button [{:keys [on-press accessibility-label]}]
|
||||||
|
[touchable-highlight {:on-press on-press
|
||||||
|
:accessibility-label accessibility-label}
|
||||||
|
[view st/send-container
|
||||||
|
[icon :send st/send-icon]]])
|
||||||
|
|
||||||
|
(defn message-input-container [input]
|
||||||
|
[view st/message-input-container input])
|
||||||
|
|
||||||
|
(defn plain-input-options [disbale?]
|
||||||
|
{:style st-message/message-input
|
||||||
|
:onChangeText (when-not disbale? plain-message/set-input-message)
|
||||||
|
:editable (not disbale?)
|
||||||
|
:onSubmitEditing plain-message/send})
|
||||||
|
|
||||||
|
(defn command-input-options [icon-width disbale?]
|
||||||
|
{:style (st-response/command-input icon-width disbale?)
|
||||||
|
:onChangeText (when-not disbale? command/set-input-message)
|
||||||
|
:onSubmitEditing command/send-command})
|
||||||
|
|
||||||
|
(defview message-input [input-options]
|
||||||
|
[command? [:command?]
|
||||||
|
input-message [:get-chat-input-text]
|
||||||
|
input-command [:get-chat-command-content]
|
||||||
|
icon-width [:command-icon-width]
|
||||||
|
disbale? [:get :disable-input]]
|
||||||
|
[text-input (merge
|
||||||
|
(if command?
|
||||||
|
(command-input-options icon-width disbale?)
|
||||||
|
(plain-input-options disbale?))
|
||||||
|
{:autoFocus false
|
||||||
|
:blurOnSubmit false
|
||||||
|
:accessibility-label :input}
|
||||||
|
input-options)
|
||||||
|
(if command? input-command input-message)])
|
||||||
|
|
||||||
|
(defview plain-message-input-view [{:keys [input-options validator]}]
|
||||||
|
[command? [:command?]
|
||||||
|
{:keys [type] :as command} [:get-chat-command]
|
||||||
|
input-command [:get-chat-command-content]
|
||||||
|
valid-plain-message? [:valid-plain-message?]
|
||||||
|
valid-command? [:valid-command? validator]]
|
||||||
|
[view st/input-container
|
||||||
|
[view st/input-view
|
||||||
|
[plain-message/commands-button]
|
||||||
|
[message-input-container
|
||||||
|
[message-input input-options validator]]
|
||||||
|
;; TODO emoticons: not implemented
|
||||||
|
[plain-message/smile-button]
|
||||||
|
(when (if command? valid-command? valid-plain-message?)
|
||||||
|
(let [on-press (if command?
|
||||||
|
command/send-command
|
||||||
|
plain-message/send)]
|
||||||
|
[send-button {:on-press on-press
|
||||||
|
:accessibility-label :send-message}]))
|
||||||
|
(when (and command? (= :command type))
|
||||||
|
[command/command-icon command])]])
|
|
@ -1,7 +0,0 @@
|
||||||
(ns status-im.chat.views.money
|
|
||||||
(:require
|
|
||||||
[status-im.chat.views.command :refer [simple-command-input-view]]))
|
|
||||||
|
|
||||||
(defn money-input-view [command]
|
|
||||||
[simple-command-input-view command
|
|
||||||
{:keyboardType :numeric}])
|
|
|
@ -1,14 +1,11 @@
|
||||||
(ns status-im.chat.views.new-message
|
(ns status-im.chat.views.new-message
|
||||||
|
(:require-macros [status-im.utils.views :refer [defview]])
|
||||||
(:require
|
(:require
|
||||||
[re-frame.core :refer [subscribe]]
|
[re-frame.core :refer [subscribe]]
|
||||||
[status-im.components.react :refer [view]]
|
[status-im.components.react :refer [view]]
|
||||||
[status-im.chat.views.plain-input :refer [plain-message-input-view]]
|
[status-im.chat.views.message-input :refer [plain-message-input-view]]
|
||||||
[status-im.chat.views.command :refer [simple-command-input-view]]
|
|
||||||
[status-im.chat.views.phone :refer [phone-input-view]]
|
|
||||||
[status-im.chat.views.password :refer [password-input-view]]
|
|
||||||
[status-im.chat.views.confirmation-code :refer [confirmation-code-input-view]]
|
|
||||||
[status-im.chat.views.money :refer [money-input-view]]
|
|
||||||
[status-im.chat.views.staged-command :refer [simple-command-staged-view]]
|
[status-im.chat.views.staged-command :refer [simple-command-staged-view]]
|
||||||
|
[status-im.utils.phone-number :refer [valid-mobile-number?]]
|
||||||
[status-im.chat.styles.message :as st]))
|
[status-im.chat.styles.message :as st]))
|
||||||
|
|
||||||
(defn staged-command-view [stage-command]
|
(defn staged-command-view [stage-command]
|
||||||
|
@ -19,27 +16,30 @@
|
||||||
(for [command staged-commands]
|
(for [command staged-commands]
|
||||||
^{:key command} [staged-command-view command])])
|
^{:key command} [staged-command-view command])])
|
||||||
|
|
||||||
(defn default-command-input-view [command]
|
(defn get-options [{:keys [type placeholder]} command-type]
|
||||||
[simple-command-input-view command {}])
|
(let [options (case (keyword type)
|
||||||
|
:phone {:input-options {:keyboardType :phone-pad}
|
||||||
|
:validator valid-mobile-number?}
|
||||||
|
:password {:input-options {:secureTextEntry true}}
|
||||||
|
:number {:input-options {:keyboardType :numeric}}
|
||||||
|
;; todo maybe nil is fine for now :)
|
||||||
|
nil #_(throw (js/Error. "Uknown command type")))]
|
||||||
|
(if (= :response command-type)
|
||||||
|
(if placeholder
|
||||||
|
(assoc-in options [:input-options :placeholder] placeholder)
|
||||||
|
options)
|
||||||
|
(assoc-in options [:input-options :placeholder] ""))))
|
||||||
|
|
||||||
(defn special-input-view [command]
|
(defview show-input []
|
||||||
(case (:command command)
|
[parameter [:get-command-parameter]
|
||||||
:phone [phone-input-view command]
|
command? [:command?]
|
||||||
:keypair-password [password-input-view command]
|
type [:command-type]]
|
||||||
:confirmation-code [confirmation-code-input-view command]
|
[plain-message-input-view
|
||||||
:money [money-input-view command]
|
(when command? (get-options parameter type))])
|
||||||
:request [money-input-view command]
|
|
||||||
[default-command-input-view command]))
|
|
||||||
|
|
||||||
(defn chat-message-new []
|
(defview chat-message-new []
|
||||||
(let [command-atom (subscribe [:get-chat-command])
|
[staged-commands [:get-chat-staged-commands]]
|
||||||
staged-commands-atom (subscribe [:get-chat-staged-commands])]
|
|
||||||
(fn []
|
|
||||||
(let [command @command-atom
|
|
||||||
staged-commands @staged-commands-atom]
|
|
||||||
[view st/new-message-container
|
[view st/new-message-container
|
||||||
(when (and staged-commands (pos? (count staged-commands)))
|
(when (seq staged-commands)
|
||||||
[staged-commands-view staged-commands])
|
[staged-commands-view staged-commands])
|
||||||
(if command
|
[show-input]])
|
||||||
[special-input-view command]
|
|
||||||
[plain-message-input-view])]))))
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
(ns status-im.chat.views.password
|
|
||||||
(:require
|
|
||||||
[status-im.chat.views.command
|
|
||||||
:refer [simple-command-input-view]]))
|
|
||||||
|
|
||||||
(defn password-input-view [command]
|
|
||||||
[simple-command-input-view command {:secureTextEntry true}])
|
|
|
@ -1,9 +0,0 @@
|
||||||
(ns status-im.chat.views.phone
|
|
||||||
(:require
|
|
||||||
[status-im.chat.views.command
|
|
||||||
:refer [simple-command-input-view]]
|
|
||||||
[status-im.utils.phone-number :refer [valid-mobile-number?]]))
|
|
||||||
|
|
||||||
(defn phone-input-view [command]
|
|
||||||
[simple-command-input-view command {:keyboardType :phone-pad}
|
|
||||||
:validator valid-mobile-number?])
|
|
|
@ -1,54 +0,0 @@
|
||||||
(ns status-im.chat.views.plain-input
|
|
||||||
(:require [re-frame.core :refer [subscribe dispatch]]
|
|
||||||
[status-im.components.react :refer [view
|
|
||||||
icon
|
|
||||||
touchable-highlight
|
|
||||||
text-input]]
|
|
||||||
[status-im.chat.views.suggestions :refer [suggestions-view]]
|
|
||||||
[status-im.chat.styles.plain-input :as st]))
|
|
||||||
|
|
||||||
(defn set-input-message [message]
|
|
||||||
(dispatch [:set-chat-input-text message]))
|
|
||||||
|
|
||||||
(defn send [chat input-message]
|
|
||||||
(let [{:keys [group-chat chat-id]} chat]
|
|
||||||
(dispatch [:send-chat-msg])))
|
|
||||||
|
|
||||||
(defn message-valid? [staged-commands message]
|
|
||||||
(or (and (pos? (count message))
|
|
||||||
(not= "!" message))
|
|
||||||
(pos? (count staged-commands))))
|
|
||||||
|
|
||||||
(defn try-send [chat staged-commands message]
|
|
||||||
(when (message-valid? staged-commands message)
|
|
||||||
(send chat message)))
|
|
||||||
|
|
||||||
(defn plain-message-input-view []
|
|
||||||
(let [chat (subscribe [:get-current-chat])
|
|
||||||
input-message-atom (subscribe [:get-chat-input-text])
|
|
||||||
staged-commands-atom (subscribe [:get-chat-staged-commands])
|
|
||||||
typing-command? (subscribe [:typing-command?])]
|
|
||||||
(fn []
|
|
||||||
(let [input-message @input-message-atom]
|
|
||||||
[view st/input-container
|
|
||||||
[suggestions-view]
|
|
||||||
[view st/input-view
|
|
||||||
[touchable-highlight {:on-press #(dispatch [:switch-command-suggestions])
|
|
||||||
:style st/switch-commands-touchable}
|
|
||||||
[view nil
|
|
||||||
(if @typing-command?
|
|
||||||
[icon :close-gray st/close-icon]
|
|
||||||
[icon :list st/list-icon])]]
|
|
||||||
[text-input {:style st/message-input
|
|
||||||
:autoFocus (pos? (count @staged-commands-atom))
|
|
||||||
:onChangeText set-input-message
|
|
||||||
:onSubmitEditing #(try-send @chat @staged-commands-atom
|
|
||||||
input-message)}
|
|
||||||
input-message]
|
|
||||||
;; TODO emoticons: not implemented
|
|
||||||
[icon :smile st/smile-icon]
|
|
||||||
(when (message-valid? @staged-commands-atom input-message)
|
|
||||||
[touchable-highlight {:on-press #(send @chat input-message)
|
|
||||||
:accessibility-label :send-message}
|
|
||||||
[view st/send-container
|
|
||||||
[icon :send st/send-icon]]])]]))))
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
(ns status-im.chat.views.plain-message
|
||||||
|
(:require-macros [status-im.utils.views :refer [defview]])
|
||||||
|
(:require [re-frame.core :refer [subscribe dispatch]]
|
||||||
|
[reagent.core :as r]
|
||||||
|
[status-im.components.react :refer [view
|
||||||
|
animated-view
|
||||||
|
icon
|
||||||
|
text
|
||||||
|
touchable-highlight]]
|
||||||
|
[status-im.components.animation :as anim]
|
||||||
|
[status-im.chat.styles.plain-message :as st]
|
||||||
|
[status-im.constants :refer [response-input-hiding-duration]]))
|
||||||
|
|
||||||
|
(defn set-input-message [message]
|
||||||
|
(dispatch [:set-chat-input-text message]))
|
||||||
|
|
||||||
|
(defn send []
|
||||||
|
(dispatch [:send-chat-msg]))
|
||||||
|
|
||||||
|
(defn message-valid? [staged-commands message]
|
||||||
|
(or (and (pos? (count message))
|
||||||
|
(not= "!" message))
|
||||||
|
(pos? (count staged-commands))))
|
||||||
|
|
||||||
|
(defn button-animation-logic [{:keys [command? val]}]
|
||||||
|
(fn [_]
|
||||||
|
(let [to-scale (if @command? 0 1)]
|
||||||
|
(anim/start (anim/spring val {:toValue to-scale
|
||||||
|
:tension 30})))))
|
||||||
|
|
||||||
|
(defn list-container [min]
|
||||||
|
(fn [{:keys [command? width]}]
|
||||||
|
(let [n-width (if @command? min 56)
|
||||||
|
delay (if @command? 100 0)]
|
||||||
|
(anim/start (anim/timing width {:toValue n-width
|
||||||
|
:duration response-input-hiding-duration
|
||||||
|
:delay delay})
|
||||||
|
#(dispatch [:set :disable-input false])))))
|
||||||
|
|
||||||
|
(defn commands-button []
|
||||||
|
(let [command? (subscribe [:command?])
|
||||||
|
requests (subscribe [:get-requests])
|
||||||
|
suggestions (subscribe [:get-suggestions])
|
||||||
|
buttons-scale (anim/create-value (if @command? 1 0))
|
||||||
|
container-width (anim/create-value (if @command? 20 56))
|
||||||
|
context {:command? command?
|
||||||
|
:val buttons-scale
|
||||||
|
:width container-width}
|
||||||
|
on-update (fn [_]
|
||||||
|
((button-animation-logic context))
|
||||||
|
((list-container 20) context))]
|
||||||
|
(r/create-class
|
||||||
|
{:component-did-mount
|
||||||
|
on-update
|
||||||
|
:component-did-update
|
||||||
|
on-update
|
||||||
|
:reagent-render
|
||||||
|
(fn []
|
||||||
|
[touchable-highlight {:on-press #(dispatch [:switch-command-suggestions!])
|
||||||
|
:disabled @command?}
|
||||||
|
[animated-view {:style (st/message-input-button-touchable
|
||||||
|
container-width)}
|
||||||
|
[animated-view {:style (st/message-input-button buttons-scale)}
|
||||||
|
(if (seq @suggestions)
|
||||||
|
[icon :close_gray st/close-icon]
|
||||||
|
[icon :input_list st/list-icon])
|
||||||
|
(when (and (seq @requests)
|
||||||
|
(not (seq @suggestions)))
|
||||||
|
[view st/requests-icon])]]])})))
|
||||||
|
|
||||||
|
(defn smile-animation-logic [{:keys [command? val width]}]
|
||||||
|
(fn [_]
|
||||||
|
(let [to-scale (if @command? 0 1)]
|
||||||
|
(when-not @command? (anim/set-value width 56))
|
||||||
|
(anim/start (anim/spring val {:toValue to-scale
|
||||||
|
:tension 30})
|
||||||
|
(fn [e]
|
||||||
|
(when (and @command? (.-finished e))
|
||||||
|
(anim/set-value width 0.1)))))))
|
||||||
|
|
||||||
|
(defn smile-button []
|
||||||
|
(let [command? (subscribe [:command?])
|
||||||
|
buttons-scale (anim/create-value (if @command? 1 0))
|
||||||
|
container-width (anim/create-value (if @command? 0.1 56))
|
||||||
|
context {:command? command?
|
||||||
|
:val buttons-scale
|
||||||
|
:width container-width}
|
||||||
|
on-update (smile-animation-logic context)]
|
||||||
|
(r/create-class
|
||||||
|
{:component-did-mount
|
||||||
|
on-update
|
||||||
|
:component-did-update
|
||||||
|
on-update
|
||||||
|
:reagent-render
|
||||||
|
(fn []
|
||||||
|
[touchable-highlight {:on-press (fn []
|
||||||
|
;; TODO emoticons: not implemented
|
||||||
|
)
|
||||||
|
:disabled @command?}
|
||||||
|
[animated-view {:style (st/message-input-button-touchable
|
||||||
|
container-width)}
|
||||||
|
[animated-view {:style (st/message-input-button buttons-scale)}
|
||||||
|
[icon :smile st/smile-icon]]]])})))
|
|
@ -0,0 +1,84 @@
|
||||||
|
(ns status-im.chat.views.request-message
|
||||||
|
(:require [re-frame.core :refer [subscribe dispatch]]
|
||||||
|
[reagent.core :as r]
|
||||||
|
[status-im.components.react :refer [view
|
||||||
|
animated-view
|
||||||
|
text
|
||||||
|
image
|
||||||
|
touchable-highlight]]
|
||||||
|
[status-im.chat.styles.message :as st]
|
||||||
|
[status-im.models.commands :refer [parse-command-request]]
|
||||||
|
[status-im.components.animation :as anim]))
|
||||||
|
|
||||||
|
(def request-message-icon-scale-delay 600)
|
||||||
|
|
||||||
|
(defn set-chat-command [msg-id command]
|
||||||
|
(dispatch [:set-response-chat-command msg-id (keyword (:name command))]))
|
||||||
|
|
||||||
|
(defn label [{:keys [command]}]
|
||||||
|
(->> (name command)
|
||||||
|
(str "request-")))
|
||||||
|
|
||||||
|
(def min-scale 1)
|
||||||
|
(def max-scale 1.3)
|
||||||
|
|
||||||
|
(defn button-animation [val to-value loop? answered?]
|
||||||
|
(anim/anim-sequence
|
||||||
|
[(anim/anim-delay
|
||||||
|
(if (and @loop? (not @answered?))
|
||||||
|
request-message-icon-scale-delay
|
||||||
|
0))
|
||||||
|
(anim/spring val {:toValue to-value})]))
|
||||||
|
|
||||||
|
(defn request-button-animation-logic
|
||||||
|
[{:keys [to-value val loop? answered?] :as context}]
|
||||||
|
(anim/start
|
||||||
|
(button-animation val to-value loop? answered?)
|
||||||
|
#(if (and @loop? (not @answered?))
|
||||||
|
(let [new-value (if (= to-value min-scale) max-scale min-scale)
|
||||||
|
context' (assoc context :to-value new-value)]
|
||||||
|
(request-button-animation-logic context'))
|
||||||
|
(anim/start
|
||||||
|
(button-animation val min-scale loop? answered?)))))
|
||||||
|
|
||||||
|
(defn request-button [msg-id command]
|
||||||
|
(let [scale-anim-val (anim/create-value min-scale)
|
||||||
|
answered? (subscribe [:is-request-answered? msg-id])
|
||||||
|
loop? (r/atom true)
|
||||||
|
context {:to-value max-scale
|
||||||
|
:val scale-anim-val
|
||||||
|
:answered? answered?
|
||||||
|
:loop? loop?}]
|
||||||
|
(r/create-class
|
||||||
|
{:component-did-mount
|
||||||
|
(when-not @answered? #(request-button-animation-logic context))
|
||||||
|
:component-will-unmount
|
||||||
|
#(reset! loop? false)
|
||||||
|
:reagent-render
|
||||||
|
(fn [msg-id command]
|
||||||
|
[touchable-highlight
|
||||||
|
{:on-press (when-not @answered?
|
||||||
|
#(set-chat-command msg-id command))
|
||||||
|
:style st/command-request-image-touchable}
|
||||||
|
[animated-view {:style (st/command-request-image-view command scale-anim-val)}
|
||||||
|
[image {:source {:uri (:icon command)}
|
||||||
|
:style st/command-request-image}]]])})))
|
||||||
|
|
||||||
|
(defn message-content-command-request
|
||||||
|
[{:keys [msg-id content from incoming-group]}]
|
||||||
|
(let [commands-atom (subscribe [:get-responses])]
|
||||||
|
(fn [{:keys [msg-id content from incoming-group]}]
|
||||||
|
(let [commands @commands-atom
|
||||||
|
{:keys [command content]} (parse-command-request commands content)]
|
||||||
|
[view st/comand-request-view
|
||||||
|
[view st/command-request-message-view
|
||||||
|
(when incoming-group
|
||||||
|
[text {:style st/command-request-from-text}
|
||||||
|
from])
|
||||||
|
[text {:style st/style-message-text}
|
||||||
|
content]]
|
||||||
|
[request-button msg-id command]
|
||||||
|
(when (:request-text command)
|
||||||
|
[view st/command-request-text-view
|
||||||
|
[text {:style st/style-sub-text}
|
||||||
|
(:request-text command)]])]))))
|
|
@ -0,0 +1,96 @@
|
||||||
|
(ns status-im.chat.views.response
|
||||||
|
(:require-macros [reagent.ratom :refer [reaction]]
|
||||||
|
[status-im.utils.views :refer [defview]])
|
||||||
|
(:require [re-frame.core :refer [subscribe dispatch]]
|
||||||
|
[reagent.core :as r]
|
||||||
|
[status-im.components.react :refer [view
|
||||||
|
animated-view
|
||||||
|
icon
|
||||||
|
image
|
||||||
|
text
|
||||||
|
text-input
|
||||||
|
touchable-highlight]]
|
||||||
|
[status-im.components.drag-drop :as drag]
|
||||||
|
[status-im.chat.styles.response :as st]
|
||||||
|
[status-im.chat.styles.dragdown :as ddst]
|
||||||
|
[status-im.components.animation :as anim]
|
||||||
|
[status-im.chat.suggestions-responder :as resp]))
|
||||||
|
|
||||||
|
(defn drag-icon []
|
||||||
|
[view st/drag-container
|
||||||
|
[icon :drag-white st/drag-icon]])
|
||||||
|
|
||||||
|
(defn command-icon []
|
||||||
|
[view st/command-icon-container
|
||||||
|
;; TODO stub data: command icon
|
||||||
|
[icon :dollar-green st/command-icon]])
|
||||||
|
|
||||||
|
(defn info-container [command]
|
||||||
|
[view st/info-container
|
||||||
|
[text {:style st/command-name}
|
||||||
|
(:description command)]
|
||||||
|
[text {:style st/message-info}
|
||||||
|
;; TODO stub data: request message info
|
||||||
|
"By ???, MMM 1st at HH:mm"]])
|
||||||
|
|
||||||
|
(defn request-info [response-height]
|
||||||
|
(let [orientation (subscribe [:get :orientation])
|
||||||
|
kb-height (subscribe [:get :keyboard-height])
|
||||||
|
pan-responder (resp/pan-responder response-height
|
||||||
|
kb-height
|
||||||
|
orientation
|
||||||
|
:fix-response-height)
|
||||||
|
command (subscribe [:get-chat-command])]
|
||||||
|
(fn [response-height]
|
||||||
|
(if (= :response (:type @command))
|
||||||
|
[view (merge (drag/pan-handlers pan-responder)
|
||||||
|
{:style (st/request-info (:color @command))})
|
||||||
|
[drag-icon]
|
||||||
|
[view st/inner-container
|
||||||
|
[command-icon nil]
|
||||||
|
[info-container @command]
|
||||||
|
[touchable-highlight {:on-press #(dispatch [:start-cancel-command])}
|
||||||
|
[view st/cancel-container
|
||||||
|
[icon :close-white st/cancel-icon]]]]]
|
||||||
|
[view (merge (drag/pan-handlers pan-responder)
|
||||||
|
{:style ddst/drag-down-touchable})
|
||||||
|
[icon :drag_down ddst/drag-down-icon]]))))
|
||||||
|
|
||||||
|
(defn container-animation-logic [{:keys [to-value val]}]
|
||||||
|
(let [to-value @to-value]
|
||||||
|
(anim/start (anim/spring val {:toValue to-value}))))
|
||||||
|
|
||||||
|
(defn container [response-height & children]
|
||||||
|
(let [;; todo to-response-height, cur-response-height must be specific
|
||||||
|
;; for each chat
|
||||||
|
to-response-height (subscribe [:animations :to-response-height])
|
||||||
|
changed (subscribe [:animations :response-height-changed])
|
||||||
|
context {:to-value to-response-height
|
||||||
|
:val response-height}
|
||||||
|
on-update #(container-animation-logic context)]
|
||||||
|
(r/create-class
|
||||||
|
{:component-did-mount
|
||||||
|
on-update
|
||||||
|
:component-did-update
|
||||||
|
on-update
|
||||||
|
:reagent-render
|
||||||
|
(fn [response-height & children]
|
||||||
|
@to-response-height @changed
|
||||||
|
(into [animated-view {:style (st/response-view response-height)}]
|
||||||
|
children))})))
|
||||||
|
|
||||||
|
(defview placeholder []
|
||||||
|
[suggestions [:get-content-suggestions]]
|
||||||
|
(when (seq suggestions)
|
||||||
|
[view st/input-placeholder]))
|
||||||
|
|
||||||
|
(defview response-suggestions-view []
|
||||||
|
[suggestions [:get-content-suggestions]]
|
||||||
|
(when (seq suggestions) suggestions))
|
||||||
|
|
||||||
|
(defn response-view []
|
||||||
|
(let [response-height (anim/create-value 0)]
|
||||||
|
[container response-height
|
||||||
|
[request-info response-height]
|
||||||
|
[response-suggestions-view]
|
||||||
|
[placeholder]]))
|
|
@ -16,13 +16,12 @@
|
||||||
[view st/staged-command-background
|
[view st/staged-command-background
|
||||||
[view st/staged-command-info-container
|
[view st/staged-command-info-container
|
||||||
[view (st/staged-command-text-container command)
|
[view (st/staged-command-text-container command)
|
||||||
[text {:style st/staged-command-text} (:text command)]]
|
[text {:style st/staged-command-text} (str "!" (:name command))]]
|
||||||
[touchable-highlight {:style st/staged-command-cancel
|
[touchable-highlight {:style st/staged-command-cancel
|
||||||
:onPress #(cancel-command-input staged-command)}
|
:onPress #(cancel-command-input staged-command)}
|
||||||
[image {:source res/icon-close-gray
|
[image {:source res/icon-close-gray
|
||||||
:style st/staged-command-cancel-icon}]]]
|
:style st/staged-command-cancel-icon}]]]
|
||||||
|
(if-let [preview (:preview staged-command)]
|
||||||
|
preview
|
||||||
[text {:style st/staged-command-content}
|
[text {:style st/staged-command-content}
|
||||||
;; TODO isn't smart
|
(:content staged-command)])]]))
|
||||||
(if (= (:command command) :keypair-password)
|
|
||||||
"******"
|
|
||||||
(:content staged-command))]]]))
|
|
||||||
|
|
|
@ -1,46 +1,126 @@
|
||||||
(ns status-im.chat.views.suggestions
|
(ns status-im.chat.views.suggestions
|
||||||
|
(:require-macros [status-im.utils.views :refer [defview]])
|
||||||
(:require [re-frame.core :refer [subscribe dispatch]]
|
(:require [re-frame.core :refer [subscribe dispatch]]
|
||||||
[status-im.components.react :refer [view
|
[status-im.components.react :refer [view
|
||||||
|
scroll-view
|
||||||
text
|
text
|
||||||
icon
|
icon
|
||||||
|
image
|
||||||
touchable-highlight
|
touchable-highlight
|
||||||
list-view
|
list-view
|
||||||
list-item]]
|
list-item
|
||||||
|
animated-view]]
|
||||||
[status-im.utils.listview :refer [to-datasource]]
|
[status-im.utils.listview :refer [to-datasource]]
|
||||||
[status-im.chat.styles.suggestions :as st]))
|
[status-im.chat.styles.suggestions :as st]
|
||||||
|
[status-im.chat.styles.dragdown :as ddst]
|
||||||
|
[reagent.core :as r]
|
||||||
|
[status-im.components.animation :as anim]
|
||||||
|
[status-im.components.drag-drop :as drag]
|
||||||
|
[status-im.chat.suggestions-responder :as resp]
|
||||||
|
[status-im.chat.constants :as c]))
|
||||||
|
|
||||||
(defn set-command-input [command]
|
(defn set-command-input [command]
|
||||||
(dispatch [:set-chat-command command]))
|
(dispatch [:set-chat-command command]))
|
||||||
|
|
||||||
(defn suggestion-list-item
|
(defview request-item [{:keys [type message-id]}]
|
||||||
[{:keys [description command]
|
[{:keys [color icon description] :as response} [:get-response type]]
|
||||||
label :text
|
|
||||||
:as suggestion}]
|
|
||||||
[touchable-highlight
|
[touchable-highlight
|
||||||
{:onPress #(set-command-input (keyword command))}
|
{:on-press #(dispatch [:set-response-chat-command message-id type])}
|
||||||
|
[view st/request-container
|
||||||
|
[view st/request-icon-container
|
||||||
|
[view (st/request-icon-background color)
|
||||||
|
[image {:source {:uri icon}
|
||||||
|
:style st/request-icon}]]]
|
||||||
|
[view st/request-info-container
|
||||||
|
[text {:style st/request-info-description} description]
|
||||||
|
;; todo stub
|
||||||
|
[text {:style st/request-message-info}
|
||||||
|
"By console, today at 14:50"]]]])
|
||||||
|
|
||||||
|
(defn render-request-row
|
||||||
|
[{:keys [chat-id message-id] :as row} _ _]
|
||||||
|
(list-item
|
||||||
|
^{:key [chat-id message-id]}
|
||||||
|
[request-item row]))
|
||||||
|
|
||||||
|
(defn suggestion-list-item
|
||||||
|
[[command {:keys [description]
|
||||||
|
name :name
|
||||||
|
:as suggestion}]]
|
||||||
|
(let [label (str "!" name)]
|
||||||
|
[touchable-highlight
|
||||||
|
{:onPress #(set-command-input command)
|
||||||
|
:style st/suggestion-highlight}
|
||||||
[view st/suggestion-container
|
[view st/suggestion-container
|
||||||
[view st/suggestion-sub-container
|
[view st/suggestion-sub-container
|
||||||
[view (st/suggestion-background suggestion)
|
[view st/command-description-container
|
||||||
[text {:style st/suggestion-text} label]]
|
|
||||||
[text {:style st/value-text} label]
|
[text {:style st/value-text} label]
|
||||||
[text {:style st/description-text} description]]]])
|
[text {:style st/description-text} description]]
|
||||||
|
[view st/command-label-container
|
||||||
|
[view (st/suggestion-background suggestion)
|
||||||
|
[text {:style st/suggestion-text} label]]]]]]))
|
||||||
|
|
||||||
(defn render-row [row _ _]
|
(defn render-row [row _ _]
|
||||||
(list-item [suggestion-list-item row]))
|
(list-item [suggestion-list-item row]))
|
||||||
|
|
||||||
(defn suggestions-view []
|
(defn title [s]
|
||||||
(let [suggestions-atom (subscribe [:get-suggestions])]
|
[view st/title-container
|
||||||
(fn []
|
[text {:style st/title-text} s]])
|
||||||
(let [suggestions @suggestions-atom]
|
|
||||||
(when (seq suggestions)
|
(defview suggestions-view []
|
||||||
|
[suggestions [:get-suggestions]
|
||||||
|
requests [:get-requests]]
|
||||||
|
[scroll-view {:keyboardShouldPersistTaps true}
|
||||||
|
(when (seq requests) [title "Requests"])
|
||||||
|
(when (seq requests)
|
||||||
[view
|
[view
|
||||||
[touchable-highlight {:style st/drag-down-touchable
|
[list-view {:dataSource (to-datasource requests)
|
||||||
:onPress (fn []
|
:keyboardShouldPersistTaps true
|
||||||
;; TODO hide suggestions?
|
:renderRow render-request-row}]])
|
||||||
)}
|
[title "Commands"]
|
||||||
[view
|
[view
|
||||||
[icon :drag_down st/drag-down-icon]]]
|
|
||||||
[view (st/suggestions-container (count suggestions))
|
|
||||||
[list-view {:dataSource (to-datasource suggestions)
|
[list-view {:dataSource (to-datasource suggestions)
|
||||||
:enableEmptySections true
|
:keyboardShouldPersistTaps true
|
||||||
:renderRow render-row}]]])))))
|
:renderRow render-row}]]])
|
||||||
|
|
||||||
|
(defn header [h]
|
||||||
|
(let [orientation (subscribe [:get :orientation])
|
||||||
|
kb-height (subscribe [:get :keyboard-height])
|
||||||
|
pan-responder (resp/pan-responder h
|
||||||
|
kb-height
|
||||||
|
orientation
|
||||||
|
:fix-commands-suggestions-height)]
|
||||||
|
(fn [_]
|
||||||
|
[view
|
||||||
|
(merge (drag/pan-handlers pan-responder)
|
||||||
|
{:style ddst/drag-down-touchable})
|
||||||
|
[view st/header-icon]])))
|
||||||
|
|
||||||
|
(defn container-animation-logic [{:keys [to-value val]}]
|
||||||
|
(when-let [to-value @to-value]
|
||||||
|
(anim/start (anim/spring val {:toValue to-value}))))
|
||||||
|
|
||||||
|
(defn container [h & elements]
|
||||||
|
(let [;; todo to-response-height, cur-response-height must be specific
|
||||||
|
;; for each chat
|
||||||
|
to-response-height (subscribe [:animations :command-suggestions-height])
|
||||||
|
changed (subscribe [:animations :commands-height-changed])
|
||||||
|
context {:to-value to-response-height
|
||||||
|
:val h}
|
||||||
|
on-update #(container-animation-logic context)]
|
||||||
|
(r/create-class
|
||||||
|
{:component-did-mount
|
||||||
|
on-update
|
||||||
|
:component-did-update
|
||||||
|
on-update
|
||||||
|
:reagent-render
|
||||||
|
(fn [h & elements]
|
||||||
|
@to-response-height @changed
|
||||||
|
(into [animated-view {:style (st/container h)}] elements))})))
|
||||||
|
|
||||||
|
(defn suggestion-container []
|
||||||
|
(let [h (anim/create-value c/input-height)]
|
||||||
|
[container h
|
||||||
|
[header h]
|
||||||
|
[suggestions-view]
|
||||||
|
[view {:height c/input-height}]]))
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
(ns status-im.chats-list.screen
|
(ns status-im.chats-list.screen
|
||||||
|
(:require-macros [status-im.utils.views :refer [defview]])
|
||||||
(:require [re-frame.core :refer [subscribe dispatch]]
|
(:require [re-frame.core :refer [subscribe dispatch]]
|
||||||
[status-im.components.react :refer [list-view
|
[status-im.components.react :refer [list-view
|
||||||
list-item
|
list-item
|
||||||
view
|
view
|
||||||
|
animated-view
|
||||||
text
|
text
|
||||||
image
|
image
|
||||||
touchable-highlight]]
|
touchable-highlight]]
|
||||||
|
@ -11,42 +13,68 @@
|
||||||
[status-im.chats-list.views.chat-list-item :refer [chat-list-item]]
|
[status-im.chats-list.views.chat-list-item :refer [chat-list-item]]
|
||||||
[status-im.components.action-button :refer [action-button
|
[status-im.components.action-button :refer [action-button
|
||||||
action-button-item]]
|
action-button-item]]
|
||||||
[status-im.components.drawer.view :refer [drawer-view open-drawer]]
|
[status-im.components.drawer.view :refer [open-drawer]]
|
||||||
[status-im.components.styles :refer [color-blue
|
[status-im.components.styles :refer [color-blue
|
||||||
|
toolbar-background1
|
||||||
toolbar-background2]]
|
toolbar-background2]]
|
||||||
[status-im.components.toolbar :refer [toolbar]]
|
[status-im.components.toolbar :refer [toolbar]]
|
||||||
[status-im.components.icons.ionicons :refer [icon]]
|
[status-im.components.icons.ionicons :refer [icon]]
|
||||||
[status-im.i18n :refer [label]]
|
[status-im.i18n :refer [label]]
|
||||||
[status-im.chats-list.styles :as st]))
|
[status-im.chats-list.styles :as st]
|
||||||
|
[status-im.components.tabs.styles :refer [tabs-height]]))
|
||||||
|
|
||||||
(defn chats-list-toolbar []
|
(defview chats-list-toolbar []
|
||||||
|
[chats-scrolled? [:get :chats-scrolled?]]
|
||||||
[toolbar {:nav-action {:image {:source {:uri :icon_hamburger}
|
[toolbar {:nav-action {:image {:source {:uri :icon_hamburger}
|
||||||
:style st/hamburger-icon}
|
:style st/hamburger-icon}
|
||||||
:handler open-drawer}
|
:handler open-drawer}
|
||||||
:title (label :t/chats)
|
:title (label :t/chats)
|
||||||
:background-color toolbar-background2
|
:background-color (if chats-scrolled?
|
||||||
|
toolbar-background1
|
||||||
|
toolbar-background2)
|
||||||
;; TODO implement search
|
;; TODO implement search
|
||||||
:action {:image {:source {:uri :icon_search}
|
:action {:image {:source {:uri :icon_search}
|
||||||
:style st/search-icon}
|
:style st/search-icon}
|
||||||
:handler (fn [])}}])
|
:handler (fn [])}}])
|
||||||
|
|
||||||
(defn chats-list []
|
(defn chats-list []
|
||||||
(let [chats (subscribe [:get :chats])]
|
(let [chats (subscribe [:get :chats])
|
||||||
|
chats-scrolled? (subscribe [:get :chats-scrolled?])
|
||||||
|
animation? (subscribe [:animations :tabs-bar-animation?])
|
||||||
|
tabs-bar-value (subscribe [:animations :tabs-bar-value])
|
||||||
|
container-height (r/atom 0)
|
||||||
|
content-height (r/atom 0)]
|
||||||
|
(dispatch [:set :chats-scrolled? false])
|
||||||
(fn []
|
(fn []
|
||||||
[drawer-view
|
|
||||||
[view st/chats-container
|
[view st/chats-container
|
||||||
[chats-list-toolbar]
|
[chats-list-toolbar]
|
||||||
[list-view {:dataSource (to-datasource @chats)
|
[list-view {:dataSource (to-datasource @chats)
|
||||||
:renderRow (fn [row _ _]
|
:renderRow (fn [row _ _]
|
||||||
(list-item [chat-list-item row]))
|
(list-item [chat-list-item row]))
|
||||||
:style st/list-container}]
|
:style st/list-container
|
||||||
|
;;; if "maximazing" chat list will make scroll to 0,
|
||||||
|
;;; then disable maximazing
|
||||||
|
:onLayout (fn [event]
|
||||||
|
(when-not @chats-scrolled?
|
||||||
|
(let [height (.. event -nativeEvent -layout -height)]
|
||||||
|
(reset! container-height height))))
|
||||||
|
:onContentSizeChange (fn [width height]
|
||||||
|
(reset! content-height height))
|
||||||
|
:onScroll (fn [e]
|
||||||
|
(let [offset (.. e -nativeEvent -contentOffset -y)
|
||||||
|
min-content-height (+ @container-height tabs-height)
|
||||||
|
scrolled? (and (< 0 offset) (< min-content-height @content-height))]
|
||||||
|
(dispatch [:set :chats-scrolled? scrolled?])
|
||||||
|
(dispatch [:set-animation :tabs-bar-animation? true])))}]
|
||||||
|
[animated-view {:style (st/action-buttons-container @animation? (or @tabs-bar-value 0))
|
||||||
|
:pointerEvents :box-none}
|
||||||
[action-button {:buttonColor color-blue
|
[action-button {:buttonColor color-blue
|
||||||
:offsetY 16
|
:offsetY 16
|
||||||
:offsetX 16}
|
:offsetX 16}
|
||||||
[action-button-item
|
[action-button-item
|
||||||
{:title (label :t/new-chat)
|
{:title (label :t/new-chat)
|
||||||
:buttonColor :#9b59b6
|
:buttonColor :#9b59b6
|
||||||
:onPress #(dispatch [:navigate-to :contact-list])}
|
:onPress #(dispatch [:show-group-contacts :people])}
|
||||||
[icon {:name :android-create
|
[icon {:name :android-create
|
||||||
:style st/create-icon}]]
|
:style st/create-icon}]]
|
||||||
[action-button-item
|
[action-button-item
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
online-color
|
online-color
|
||||||
text1-color
|
text1-color
|
||||||
text2-color
|
text2-color
|
||||||
new-messages-count-color]]))
|
new-messages-count-color]]
|
||||||
|
[status-im.components.tabs.styles :refer [tabs-height]]))
|
||||||
|
|
||||||
(def chat-container
|
(def chat-container
|
||||||
{:flexDirection :row
|
{:flexDirection :row
|
||||||
|
@ -113,3 +114,11 @@
|
||||||
{:fontSize 20
|
{:fontSize 20
|
||||||
:height 22
|
:height 22
|
||||||
:color :white})
|
:color :white})
|
||||||
|
|
||||||
|
(defn action-buttons-container [animation? offset-y]
|
||||||
|
{:position :absolute
|
||||||
|
:left 0
|
||||||
|
:right 0
|
||||||
|
:top 0
|
||||||
|
:bottom 0
|
||||||
|
:transform [{:translateY (if animation? offset-y 1)}]})
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
[status-im.utils.datetime :as time]))
|
[status-im.utils.datetime :as time]))
|
||||||
|
|
||||||
(defn chat-list-item-inner-view
|
(defn chat-list-item-inner-view
|
||||||
[{:keys [chat-id name color photo-path new-messages-count
|
[{:keys [chat-id name color new-messages-count
|
||||||
online group-chat contacts] :as chat}]
|
online group-chat contacts] :as chat}]
|
||||||
(let [last-message (first (:messages chat))]
|
(let [last-message (first (:messages chat))]
|
||||||
[view st/chat-container
|
[view st/chat-container
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
(ns status-im.commands.handlers.jail
|
||||||
|
(:require [re-frame.core :refer [after dispatch subscribe trim-v debug]]
|
||||||
|
[status-im.utils.handlers :as u]
|
||||||
|
[status-im.utils.utils :refer [http-get toast]]
|
||||||
|
[status-im.components.jail :as j]
|
||||||
|
[status-im.utils.types :refer [json->clj]]
|
||||||
|
[status-im.commands.utils :refer [generate-hiccup reg-handler]]
|
||||||
|
[clojure.string :as s]))
|
||||||
|
|
||||||
|
(defn init-render-command!
|
||||||
|
[_ [chat-id command message-id data]]
|
||||||
|
(j/call chat-id [command :render] data
|
||||||
|
#(dispatch [::render-command chat-id message-id %])))
|
||||||
|
|
||||||
|
(defn render-command
|
||||||
|
[db [chat-id message-id markup]]
|
||||||
|
(let [hiccup (generate-hiccup markup)]
|
||||||
|
(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 %])})
|
||||||
|
|
||||||
|
(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)))))
|
||||||
|
|
||||||
|
(defn suggestions-handler!
|
||||||
|
[db [{:keys [chat-id]} {:keys [result]} ]]
|
||||||
|
(assoc-in db [:suggestions chat-id] (generate-hiccup result)))
|
||||||
|
|
||||||
|
(defn suggestions-events-handler!
|
||||||
|
[db [[n data]]]
|
||||||
|
(case (keyword n)
|
||||||
|
:set-value (dispatch [:set-chat-command-content data])
|
||||||
|
;; todo show error?
|
||||||
|
nil))
|
||||||
|
|
||||||
|
(defn command-preview
|
||||||
|
[db [chat-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
|
||||||
|
:preview (generate-hiccup result)
|
||||||
|
:preview-string (str result)))
|
||||||
|
db))
|
||||||
|
|
||||||
|
(defn print-error-message! [message]
|
||||||
|
(fn [_ params]
|
||||||
|
(when (:error (last params))
|
||||||
|
(toast (s/join "\n" [message params]))
|
||||||
|
(println message params))))
|
||||||
|
|
||||||
|
(reg-handler :init-render-command! init-render-command!)
|
||||||
|
(reg-handler ::render-command render-command)
|
||||||
|
|
||||||
|
(reg-handler :command-handler!
|
||||||
|
(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"))]
|
||||||
|
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)
|
|
@ -0,0 +1,102 @@
|
||||||
|
(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]]
|
||||||
|
[status-im.utils.handlers :as u]
|
||||||
|
[status-im.utils.utils :refer [http-get toast]]
|
||||||
|
[clojure.string :as s]
|
||||||
|
[status-im.persistence.realm :as realm]
|
||||||
|
[status-im.components.jail :as j]
|
||||||
|
[status-im.utils.types :refer [json->clj]]
|
||||||
|
[status-im.commands.utils :refer [reg-handler]]))
|
||||||
|
|
||||||
|
(def commands-js "commands.js")
|
||||||
|
|
||||||
|
(defn load-commands!
|
||||||
|
[_ [identity]]
|
||||||
|
(dispatch [::fetch-commands! identity])
|
||||||
|
;; todo uncomment
|
||||||
|
#_(if-let [{:keys [file]} (realm/get-one-by-field :commands :chat-id
|
||||||
|
identity)]
|
||||||
|
(dispatch [::parse-commands! identity file])
|
||||||
|
(dispatch [::fetch-commands! identity])))
|
||||||
|
|
||||||
|
(defn fetch-commands!
|
||||||
|
[db [identity]]
|
||||||
|
(when-let [url (:dapp-url (get-in db [:chats identity]))]
|
||||||
|
(if (= "console" identity)
|
||||||
|
(dispatch [::validate-hash identity (slurp "resources/commands.js")])
|
||||||
|
(http-get (s/join "/" [url commands-js])
|
||||||
|
#(dispatch [::validate-hash identity %])
|
||||||
|
#(dispatch [::loading-failed! identity ::file-was-not-found])))))
|
||||||
|
|
||||||
|
(defn dispatch-loaded!
|
||||||
|
[db [identity file]]
|
||||||
|
(if (::valid-hash db)
|
||||||
|
(dispatch [::parse-commands! identity file])
|
||||||
|
(dispatch [::loading-failed! identity ::wrong-hash])))
|
||||||
|
|
||||||
|
(defn get-hash-by-identity
|
||||||
|
[db identity]
|
||||||
|
(get-in db [:chats identity :dapp-hash]))
|
||||||
|
|
||||||
|
(defn get-hash-by-file
|
||||||
|
[file]
|
||||||
|
;; todo tbd hashing algorithm
|
||||||
|
(hash file))
|
||||||
|
|
||||||
|
(defn parse-commands! [_ [identity file]]
|
||||||
|
(j/parse identity file
|
||||||
|
(fn [result]
|
||||||
|
(let [{:keys [error result]} (json->clj result)]
|
||||||
|
(if error
|
||||||
|
(dispatch [::loading-failed! identity ::error-in-jail error])
|
||||||
|
(dispatch [::add-commands identity file result]))))))
|
||||||
|
|
||||||
|
(defn validate-hash
|
||||||
|
[db [identity file]]
|
||||||
|
(let [valid? true
|
||||||
|
;; todo check
|
||||||
|
#_(= (get-hash-by-identity db identity)
|
||||||
|
(get-hash-by-file file))]
|
||||||
|
(assoc db ::valid-hash valid?)))
|
||||||
|
|
||||||
|
(defn mark-as [as coll]
|
||||||
|
(->> coll
|
||||||
|
(map (fn [[k v]] [k (assoc v :type as)]))
|
||||||
|
(into {})))
|
||||||
|
|
||||||
|
(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))))
|
||||||
|
|
||||||
|
(defn save-commands-js!
|
||||||
|
[_ [id file]]
|
||||||
|
(realm/create-object :commands {:chat-id id :file file}))
|
||||||
|
|
||||||
|
(defn loading-failed!
|
||||||
|
[db [id reason details]]
|
||||||
|
(let [url (get-in db [:chats id :dapp-url])]
|
||||||
|
(let [m (s/join "\n" ["commands.js loading failed"
|
||||||
|
url
|
||||||
|
id
|
||||||
|
(name reason)
|
||||||
|
details])]
|
||||||
|
(toast m)
|
||||||
|
(println m))))
|
||||||
|
|
||||||
|
(reg-handler :load-commands! (u/side-effect! load-commands!))
|
||||||
|
(reg-handler ::fetch-commands! (u/side-effect! fetch-commands!))
|
||||||
|
|
||||||
|
(reg-handler ::validate-hash
|
||||||
|
(after dispatch-loaded!)
|
||||||
|
validate-hash)
|
||||||
|
|
||||||
|
(reg-handler ::parse-commands! (u/side-effect! parse-commands!))
|
||||||
|
|
||||||
|
(reg-handler ::add-commands
|
||||||
|
(after save-commands-js!)
|
||||||
|
add-commands)
|
||||||
|
|
||||||
|
(reg-handler ::loading-failed! (u/side-effect! loading-failed!))
|
|
@ -0,0 +1,48 @@
|
||||||
|
(ns status-im.commands.utils
|
||||||
|
(:require [clojure.set :as set]
|
||||||
|
[clojure.walk :as w]
|
||||||
|
[status-im.components.react :refer [text scroll-view view
|
||||||
|
image touchable-highlight]]
|
||||||
|
[re-frame.core :refer [dispatch trim-v debug]]
|
||||||
|
[status-im.utils.handlers :refer [register-handler]]))
|
||||||
|
|
||||||
|
(defn json->clj [json]
|
||||||
|
(if (= json "undefined")
|
||||||
|
nil
|
||||||
|
(js->clj (.parse js/JSON json) :keywordize-keys true)))
|
||||||
|
|
||||||
|
(def elements
|
||||||
|
{:text text
|
||||||
|
:view view
|
||||||
|
:scroll-view scroll-view
|
||||||
|
:image image
|
||||||
|
:touchable touchable-highlight})
|
||||||
|
|
||||||
|
(defn get-element [n]
|
||||||
|
(elements (keyword (.toLowerCase n))))
|
||||||
|
|
||||||
|
(def events #{:onPress})
|
||||||
|
|
||||||
|
(defn wrap-event [event]
|
||||||
|
#(dispatch [:suggestions-event! event]))
|
||||||
|
|
||||||
|
(defn check-events [m]
|
||||||
|
(let [ks (set (keys m))
|
||||||
|
evs (set/intersection ks events)]
|
||||||
|
(reduce #(update %1 %2 wrap-event) m evs)))
|
||||||
|
|
||||||
|
(defn generate-hiccup [markup]
|
||||||
|
;; todo implement validation
|
||||||
|
(w/prewalk
|
||||||
|
(fn [el]
|
||||||
|
(if (and (vector? el) (string? (first el)))
|
||||||
|
(-> el
|
||||||
|
(update 0 get-element)
|
||||||
|
(update 1 check-events))
|
||||||
|
el))
|
||||||
|
markup))
|
||||||
|
|
||||||
|
(defn reg-handler
|
||||||
|
([name handler] (reg-handler name nil handler))
|
||||||
|
([name middleware handler]
|
||||||
|
(register-handler name [trim-v middleware] handler)))
|
|
@ -0,0 +1,54 @@
|
||||||
|
(ns status-im.components.animation
|
||||||
|
(:require [status-im.components.react :refer [animated]]))
|
||||||
|
|
||||||
|
(defn start
|
||||||
|
([anim] (.start anim))
|
||||||
|
([anim callback] (.start anim callback)))
|
||||||
|
|
||||||
|
(defn timing [anim-value config]
|
||||||
|
(.timing animated anim-value (clj->js config)))
|
||||||
|
|
||||||
|
(defn spring [anim-value config]
|
||||||
|
(.spring animated anim-value (clj->js config)))
|
||||||
|
|
||||||
|
(defn decay [anim-value config]
|
||||||
|
(.decay animated anim-value (clj->js config)))
|
||||||
|
|
||||||
|
(defn anim-sequence [animations]
|
||||||
|
(.sequence animated (clj->js animations)))
|
||||||
|
|
||||||
|
(defn anim-delay [duration]
|
||||||
|
(.delay animated duration))
|
||||||
|
|
||||||
|
(defn event [config]
|
||||||
|
(.event animated (clj->js [nil, config])))
|
||||||
|
|
||||||
|
(defn add-listener [anim-value listener]
|
||||||
|
(.addListener anim-value listener))
|
||||||
|
|
||||||
|
(defn remove-all-listeners [anim-value]
|
||||||
|
(.removeAllListeners anim-value))
|
||||||
|
|
||||||
|
(defn stop-animation [anim-value]
|
||||||
|
(.stopAnimation anim-value))
|
||||||
|
|
||||||
|
(defn value [anim-value]
|
||||||
|
(.-value anim-value))
|
||||||
|
|
||||||
|
(defn set-value [anim-value value]
|
||||||
|
(.setValue anim-value value))
|
||||||
|
|
||||||
|
(defn create-value [value]
|
||||||
|
(js/React.Animated.Value. value))
|
||||||
|
|
||||||
|
(defn x [value-xy]
|
||||||
|
(.-x value-xy))
|
||||||
|
|
||||||
|
(defn y [value-xy]
|
||||||
|
(.-y value-xy))
|
||||||
|
|
||||||
|
(defn get-layout [value-xy]
|
||||||
|
(js->clj (.getLayout value-xy)))
|
||||||
|
|
||||||
|
(defn create-value-xy [x y]
|
||||||
|
(js/React.Animated.ValueXY. (clj->js {:x x, :y y})))
|
|
@ -6,7 +6,7 @@
|
||||||
image
|
image
|
||||||
icon]]
|
icon]]
|
||||||
[status-im.components.chat-icon.styles :as st]
|
[status-im.components.chat-icon.styles :as st]
|
||||||
[status-im.components.styles :refer [color-purple]]
|
[status-im.components.styles :refer [default-chat-color]]
|
||||||
[clojure.string :as s]))
|
[clojure.string :as s]))
|
||||||
|
|
||||||
(defn default-chat-icon [name styles]
|
(defn default-chat-icon [name styles]
|
||||||
|
@ -63,6 +63,26 @@
|
||||||
:default-chat-icon (st/default-chat-icon-menu-item color)
|
:default-chat-icon (st/default-chat-icon-menu-item color)
|
||||||
:default-chat-icon-text st/default-chat-icon-text}])
|
:default-chat-icon-text st/default-chat-icon-text}])
|
||||||
|
|
||||||
|
(defn contact-icon-view [contact styles]
|
||||||
|
(let [photo-path (:photo-path contact)
|
||||||
|
;; TODO stub data
|
||||||
|
online true]
|
||||||
|
[view (:container styles)
|
||||||
|
(if-not (s/blank? photo-path)
|
||||||
|
[chat-icon photo-path styles]
|
||||||
|
[default-chat-icon (:name contact) styles])
|
||||||
|
[contact-online online styles]]))
|
||||||
|
|
||||||
|
(defn contact-icon-contacts-tab [contact]
|
||||||
|
[contact-icon-view contact
|
||||||
|
{:container st/container-chat-list
|
||||||
|
:online-view st/online-view
|
||||||
|
:online-dot-left st/online-dot-left
|
||||||
|
:online-dot-right st/online-dot-right
|
||||||
|
:chat-icon st/chat-icon-chat-list
|
||||||
|
:default-chat-icon (st/default-chat-icon-chat-list default-chat-color)
|
||||||
|
:default-chat-icon-text st/default-chat-icon-text}])
|
||||||
|
|
||||||
(defn profile-icon-view [photo-path name color online]
|
(defn profile-icon-view [photo-path name color online]
|
||||||
(let [styles {:container st/container-profile
|
(let [styles {:container st/container-profile
|
||||||
:online-view st/online-view-profile
|
:online-view st/online-view-profile
|
||||||
|
@ -81,7 +101,7 @@
|
||||||
[contact [:contact]]
|
[contact [:contact]]
|
||||||
(let [;; TODO stub data
|
(let [;; TODO stub data
|
||||||
online true
|
online true
|
||||||
color color-purple]
|
color default-chat-color]
|
||||||
[profile-icon-view (:photo-path contact) (:name contact) color online]))
|
[profile-icon-view (:photo-path contact) (:name contact) color online]))
|
||||||
|
|
||||||
(defview my-profile-icon []
|
(defview my-profile-icon []
|
||||||
|
@ -89,5 +109,5 @@
|
||||||
photo-path [:get :photo-path]]
|
photo-path [:get :photo-path]]
|
||||||
(let [;; TODO stub data
|
(let [;; TODO stub data
|
||||||
online true
|
online true
|
||||||
color color-purple]
|
color default-chat-color]
|
||||||
[profile-icon-view photo-path name color online]))
|
[profile-icon-view photo-path name color online]))
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
(ns status-im.components.drag-drop
|
||||||
|
(:require [status-im.components.react :refer [animated pan-responder]]
|
||||||
|
[status-im.components.animation :as anim]))
|
||||||
|
|
||||||
|
(defn pan-handlers [pan-responder]
|
||||||
|
(js->clj (.-panHandlers pan-responder)))
|
||||||
|
|
||||||
|
(defn create-pan-responder [{:keys [on-move on-release]}]
|
||||||
|
(.create pan-responder
|
||||||
|
(clj->js {:onStartShouldSetPanResponder (fn [] true)
|
||||||
|
:onPanResponderMove on-move
|
||||||
|
:onPanResponderRelease on-release})))
|
|
@ -0,0 +1,27 @@
|
||||||
|
(ns status-im.components.jail
|
||||||
|
(:require-macros [status-im.utils.slurp :refer [slurp]])
|
||||||
|
(:require [status-im.components.react :as r]
|
||||||
|
[status-im.utils.types :as t]))
|
||||||
|
|
||||||
|
(def status-js (slurp "resources/status.js"))
|
||||||
|
|
||||||
|
(def jail
|
||||||
|
(when (exists? (.-NativeModules r/react))
|
||||||
|
(.-Jail (.-NativeModules r/react))))
|
||||||
|
|
||||||
|
(when jail
|
||||||
|
(.init jail status-js))
|
||||||
|
|
||||||
|
(defn parse [chat-id file callback]
|
||||||
|
(.parse jail chat-id file callback))
|
||||||
|
|
||||||
|
(defn cljs->json [data]
|
||||||
|
(.stringify js/JSON (clj->js data)))
|
||||||
|
|
||||||
|
(defn call [chat-id path params callback]
|
||||||
|
(println :call chat-id (cljs->json path) (cljs->json params))
|
||||||
|
(let [cb (fn [r]
|
||||||
|
(let [r' (t/json->clj r)]
|
||||||
|
(println r')
|
||||||
|
(callback r')))]
|
||||||
|
(.call jail chat-id (cljs->json path) (cljs->json params) cb)))
|
|
@ -1,11 +1,17 @@
|
||||||
(ns status-im.components.main-tabs
|
(ns status-im.components.main-tabs
|
||||||
(:require-macros [status-im.utils.views :refer [defview]])
|
(:require-macros [reagent.ratom :refer [reaction]]
|
||||||
|
[status-im.utils.views :refer [defview]])
|
||||||
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
|
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
|
||||||
|
[reagent.core :as r]
|
||||||
[status-im.components.react :refer [view
|
[status-im.components.react :refer [view
|
||||||
|
animated-view
|
||||||
text-input
|
text-input
|
||||||
text
|
text
|
||||||
image
|
image
|
||||||
touchable-highlight]]
|
touchable-highlight
|
||||||
|
get-dimensions]]
|
||||||
|
[status-im.components.drawer.view :refer [drawer-view]]
|
||||||
|
[status-im.components.animation :as anim]
|
||||||
[status-im.chats-list.screen :refer [chats-list]]
|
[status-im.chats-list.screen :refer [chats-list]]
|
||||||
[status-im.discovery.screen :refer [discovery]]
|
[status-im.discovery.screen :refer [discovery]]
|
||||||
[status-im.contacts.screen :refer [contact-list]]
|
[status-im.contacts.screen :refer [contact-list]]
|
||||||
|
@ -14,33 +20,92 @@
|
||||||
[status-im.components.styles :as common-st]
|
[status-im.components.styles :as common-st]
|
||||||
[status-im.i18n :refer [label]]))
|
[status-im.i18n :refer [label]]))
|
||||||
|
|
||||||
|
(def window-width (:width (get-dimensions "window")))
|
||||||
|
|
||||||
(def tab-list
|
(def tab-list
|
||||||
[{:view-id :chat-list
|
[{:view-id :chat-list
|
||||||
:title (label :t/chats)
|
:title (label :t/chats)
|
||||||
:screen chats-list
|
:screen chats-list
|
||||||
:icon :icon_tab_chats}
|
:icon :icon_tab_chats
|
||||||
|
:index 0}
|
||||||
{:view-id :discovery
|
{:view-id :discovery
|
||||||
:title (label :t/discovery)
|
:title (label :t/discovery)
|
||||||
:screen discovery
|
:screen discovery
|
||||||
:icon :icon_tab_discovery}
|
:icon :icon_tab_discovery
|
||||||
|
:index 1}
|
||||||
{:view-id :contact-list
|
{:view-id :contact-list
|
||||||
:title (label :t/contacts)
|
:title (label :t/contacts)
|
||||||
:screen contact-list
|
:screen contact-list
|
||||||
:icon :icon_tab_contacts}])
|
:icon :icon_tab_contacts
|
||||||
|
:index 2}])
|
||||||
|
|
||||||
(defn show-view? [current-view view-id]
|
(defn animation-logic [{:keys [offsets val tab-id to-tab-id]}]
|
||||||
(let [key-map {:key view-id}]
|
(fn [_]
|
||||||
(if (= current-view view-id)
|
(when-let [offsets @offsets]
|
||||||
(merge st/show-tab key-map)
|
(let [from-value (:from offsets)
|
||||||
(merge st/hide-tab key-map))))
|
to-value (:to offsets)
|
||||||
|
to-tab-id @to-tab-id]
|
||||||
|
(anim/set-value val from-value)
|
||||||
|
(when to-value
|
||||||
|
(anim/start
|
||||||
|
(anim/timing val {:toValue to-value
|
||||||
|
:duration 300})
|
||||||
|
(when (= tab-id to-tab-id)
|
||||||
|
(fn [arg]
|
||||||
|
(when (.-finished arg)
|
||||||
|
(dispatch [:on-navigated-to-tab]))))))))))
|
||||||
|
|
||||||
(defn tab-view [current-view {:keys [view-id screen]}]
|
(defn get-tab-index-by-id [id]
|
||||||
[view (show-view? current-view view-id)
|
(:index (first (filter #(= id (:view-id %)) tab-list))))
|
||||||
|
|
||||||
|
(defn get-offsets [tab-id from-id to-id]
|
||||||
|
(let [tab (get-tab-index-by-id tab-id)
|
||||||
|
from (get-tab-index-by-id from-id)
|
||||||
|
to (get-tab-index-by-id to-id)]
|
||||||
|
(if (or (= tab from) (= tab to))
|
||||||
|
(cond
|
||||||
|
(or (nil? from) (= from to)) {:from 0}
|
||||||
|
(< from to) (if (= tab to)
|
||||||
|
{:from window-width, :to 0}
|
||||||
|
{:from 0, :to (- window-width)})
|
||||||
|
(< to from) (if (= tab to)
|
||||||
|
{:from (- window-width), :to 0}
|
||||||
|
{:from 0, :to window-width}))
|
||||||
|
{:from (- window-width)})))
|
||||||
|
|
||||||
|
(defn tab-view-container [tab-id content]
|
||||||
|
(let [cur-tab-id (subscribe [:get :view-id])
|
||||||
|
prev-tab-id (subscribe [:get :prev-tab-view-id])
|
||||||
|
offsets (reaction (get-offsets tab-id @prev-tab-id @cur-tab-id))
|
||||||
|
anim-value (anim/create-value (- window-width))
|
||||||
|
context {:offsets offsets
|
||||||
|
:val anim-value
|
||||||
|
:tab-id tab-id
|
||||||
|
:to-tab-id cur-tab-id}
|
||||||
|
on-update (animation-logic context)]
|
||||||
|
(r/create-class
|
||||||
|
{:component-did-mount
|
||||||
|
on-update
|
||||||
|
:component-did-update
|
||||||
|
on-update
|
||||||
|
:reagent-render
|
||||||
|
(fn [tab-id content]
|
||||||
|
@offsets
|
||||||
|
[animated-view {:style (st/tab-view-container anim-value)}
|
||||||
|
content])})))
|
||||||
|
|
||||||
|
(defn tab-view [{:keys [view-id screen]}]
|
||||||
|
^{:key view-id}
|
||||||
|
[tab-view-container view-id
|
||||||
[screen]])
|
[screen]])
|
||||||
|
|
||||||
(defview main-tabs []
|
(defview main-tabs []
|
||||||
[view-id [:get :view-id]]
|
[view-id [:get :view-id]
|
||||||
[view common-st/flex
|
tab-animation? [:get :prev-tab-view-id]]
|
||||||
(doall (map #(tab-view view-id %1) tab-list))
|
[drawer-view
|
||||||
|
[view {:style common-st/flex}
|
||||||
|
[view {:style common-st/flex
|
||||||
|
:pointerEvents (if tab-animation? :none :auto)}
|
||||||
|
(doall (map #(tab-view %) tab-list))]
|
||||||
[tabs {:selected-view-id view-id
|
[tabs {:selected-view-id view-id
|
||||||
:tab-list tab-list}]])
|
:tab-list tab-list}]]])
|
||||||
|
|
|
@ -46,6 +46,14 @@
|
||||||
(when-let [picker (get-react-property "Picker")]
|
(when-let [picker (get-react-property "Picker")]
|
||||||
(adapt-class (.-Item picker))))
|
(adapt-class (.-Item picker))))
|
||||||
|
|
||||||
|
(def pan-responder (.-PanResponder js/React))
|
||||||
|
(def animated (.-Animated js/React))
|
||||||
|
(def animated-view (r/adapt-react-class (.-View animated)))
|
||||||
|
(def animated-text (r/adapt-react-class (.-Text animated)))
|
||||||
|
|
||||||
|
(def dimensions (.-Dimensions js/React))
|
||||||
|
(defn get-dimensions [name]
|
||||||
|
(js->clj (.get dimensions name) :keywordize-keys true))
|
||||||
|
|
||||||
(defn icon
|
(defn icon
|
||||||
([n] (icon n {}))
|
([n] (icon n {}))
|
||||||
|
@ -68,3 +76,5 @@
|
||||||
(r/as-element component))
|
(r/as-element component))
|
||||||
|
|
||||||
(def dismiss-keyboard! (u/require "dismissKeyboard"))
|
(def dismiss-keyboard! (u/require "dismissKeyboard"))
|
||||||
|
(def device-event-emitter (.-DeviceEventEmitter react))
|
||||||
|
(def orientation (u/require "react-native-orientation"))
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
(def color-black "#000000de")
|
(def color-black "#000000de")
|
||||||
(def color-purple "#a187d5")
|
(def color-purple "#a187d5")
|
||||||
(def color-gray "#838c93de")
|
(def color-gray "#838c93de")
|
||||||
|
(def color-gray2 "#8f838c93")
|
||||||
(def color-white :white)
|
(def color-white :white)
|
||||||
(def color-light-blue "#bbc4cb")
|
(def color-light-blue "#bbc4cb")
|
||||||
(def color-light-blue-transparent "#bbc4cb32")
|
(def color-light-blue-transparent "#bbc4cb32")
|
||||||
|
@ -20,6 +21,7 @@
|
||||||
(def text2-color color-gray)
|
(def text2-color color-gray)
|
||||||
(def text3-color color-blue)
|
(def text3-color color-blue)
|
||||||
(def text4-color color-white)
|
(def text4-color color-white)
|
||||||
|
(def text5-color "#838c938f")
|
||||||
(def online-color color-blue)
|
(def online-color color-blue)
|
||||||
(def new-messages-count-color color-blue-transparent)
|
(def new-messages-count-color color-blue-transparent)
|
||||||
(def chat-background color-light-gray)
|
(def chat-background color-light-gray)
|
||||||
|
@ -32,7 +34,7 @@
|
||||||
(def toolbar-height 56)
|
(def toolbar-height 56)
|
||||||
|
|
||||||
(def flex
|
(def flex
|
||||||
{:style {:flex 1}})
|
{:flex 1})
|
||||||
|
|
||||||
(def hamburger-icon
|
(def hamburger-icon
|
||||||
{:width 16
|
{:width 16
|
||||||
|
|
|
@ -10,21 +10,21 @@
|
||||||
text2-color
|
text2-color
|
||||||
toolbar-background1]]))
|
toolbar-background1]]))
|
||||||
|
|
||||||
|
(def tabs-height 59)
|
||||||
(def tab-height 56)
|
(def tab-height 56)
|
||||||
|
|
||||||
(def tabs
|
(defn tabs-container [hidden? animation? offset-y]
|
||||||
{:flex 1
|
{:height tabs-height
|
||||||
:position :absolute
|
:backgroundColor color-white
|
||||||
:bottom 0
|
:marginBottom (if (or hidden? animation?)
|
||||||
:right 0
|
(- tabs-height) 0)
|
||||||
:left 0
|
:transform [{:translateY (if animation? offset-y 1)}]})
|
||||||
})
|
|
||||||
|
|
||||||
(def top-gradient
|
(def top-gradient
|
||||||
{:flexDirection :row
|
{:flexDirection :row
|
||||||
:height 3})
|
:height 3})
|
||||||
|
|
||||||
(def tabs-container
|
(def tabs-inner-container
|
||||||
{:flexDirection :row
|
{:flexDirection :row
|
||||||
:height tab-height
|
:height tab-height
|
||||||
:opacity 1
|
:opacity 1
|
||||||
|
@ -54,17 +54,10 @@
|
||||||
:justifyContent :center
|
:justifyContent :center
|
||||||
:alignItems :center})
|
:alignItems :center})
|
||||||
|
|
||||||
(def show-tab
|
(defn tab-view-container [offset-x]
|
||||||
{:flex 1
|
{:position :absolute
|
||||||
:pointerEvents :auto
|
|
||||||
:position :absolute
|
|
||||||
:top 0
|
:top 0
|
||||||
:left 0
|
:left 0
|
||||||
:right 0
|
:right 0
|
||||||
:bottom tab-height})
|
:bottom 0
|
||||||
|
:transform [{:translateX offset-x}]})
|
||||||
(def hide-tab
|
|
||||||
{:opacity 0
|
|
||||||
:pointerEvents :none
|
|
||||||
:overflow :hidden})
|
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,8 @@
|
||||||
|
|
||||||
(defview tab [{:keys [view-id title icon selected-view-id]}]
|
(defview tab [{:keys [view-id title icon selected-view-id]}]
|
||||||
[touchable-highlight {:style st/tab
|
[touchable-highlight {:style st/tab
|
||||||
:onPress #(dispatch [:navigate-to
|
:disabled (= view-id selected-view-id)
|
||||||
view-id])}
|
:onPress #(dispatch [:navigate-to-tab view-id])}
|
||||||
[view {:style st/tab-container}
|
[view {:style st/tab-container}
|
||||||
[image {:source {:uri icon}
|
[image {:source {:uri icon}
|
||||||
:style st/tab-icon}]
|
:style st/tab-icon}]
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
(:require-macros [status-im.utils.views :refer [defview]])
|
(:require-macros [status-im.utils.views :refer [defview]])
|
||||||
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
|
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
|
||||||
[status-im.components.react :refer [view
|
[status-im.components.react :refer [view
|
||||||
|
animated-view
|
||||||
text-input
|
text-input
|
||||||
text
|
text
|
||||||
image
|
image
|
||||||
|
@ -9,7 +10,8 @@
|
||||||
linear-gradient]]
|
linear-gradient]]
|
||||||
[reagent.core :as r]
|
[reagent.core :as r]
|
||||||
[status-im.components.tabs.styles :as st]
|
[status-im.components.tabs.styles :as st]
|
||||||
[status-im.components.tabs.tab :refer [tab]]))
|
[status-im.components.tabs.tab :refer [tab]]
|
||||||
|
[status-im.components.animation :as anim]))
|
||||||
|
|
||||||
(defn create-tab [index data selected-view-id]
|
(defn create-tab [index data selected-view-id]
|
||||||
(let [data (merge data {:key index
|
(let [data (merge data {:key index
|
||||||
|
@ -17,10 +19,43 @@
|
||||||
:selected-view-id selected-view-id})]
|
:selected-view-id selected-view-id})]
|
||||||
[tab data]))
|
[tab data]))
|
||||||
|
|
||||||
(defview tabs [{:keys [style tab-list selected-view-id]}]
|
(defn animation-logic [{:keys [hidden? val]}]
|
||||||
(let [style (merge st/tabs style)]
|
(let [was-hidden? (atom (not @hidden?))]
|
||||||
[view {:style style}
|
(fn [_]
|
||||||
|
(when (not= @was-hidden? @hidden?)
|
||||||
|
(let [to-value (if @hidden? 0 (- st/tabs-height))]
|
||||||
|
(swap! was-hidden? not)
|
||||||
|
(anim/start
|
||||||
|
(anim/timing val {:toValue to-value
|
||||||
|
:duration 300})
|
||||||
|
(fn [e]
|
||||||
|
;; if to-value was changed, then new animation has started
|
||||||
|
(when (= to-value (if @hidden? 0 (- st/tabs-height)))
|
||||||
|
(dispatch [:set-animation :tabs-bar-animation? false])))))))))
|
||||||
|
|
||||||
|
(defn tabs-container [& children]
|
||||||
|
(let [chats-scrolled? (subscribe [:get :chats-scrolled?])
|
||||||
|
animation? (subscribe [:animations :tabs-bar-animation?])
|
||||||
|
tabs-bar-value (subscribe [:animations :tabs-bar-value])
|
||||||
|
context {:hidden? chats-scrolled?
|
||||||
|
:val @tabs-bar-value}
|
||||||
|
on-update (animation-logic context)]
|
||||||
|
(anim/set-value @tabs-bar-value 0)
|
||||||
|
(r/create-class
|
||||||
|
{:component-did-mount
|
||||||
|
on-update
|
||||||
|
:component-did-update
|
||||||
|
on-update
|
||||||
|
:reagent-render
|
||||||
|
(fn [& children]
|
||||||
|
@chats-scrolled?
|
||||||
|
(into [animated-view {:style (st/tabs-container @chats-scrolled? @animation? @tabs-bar-value)
|
||||||
|
:pointerEvents (if @chats-scrolled? :none :auto)}]
|
||||||
|
children))})))
|
||||||
|
|
||||||
|
(defn tabs [{:keys [tab-list selected-view-id]}]
|
||||||
|
[tabs-container
|
||||||
[linear-gradient {:colors ["rgba(24, 52, 76, 0.01)" "rgba(24, 52, 76, 0.085)" "rgba(24, 52, 76, 0.165)"]
|
[linear-gradient {:colors ["rgba(24, 52, 76, 0.01)" "rgba(24, 52, 76, 0.085)" "rgba(24, 52, 76, 0.165)"]
|
||||||
:style st/top-gradient}]
|
:style st/top-gradient}]
|
||||||
[view st/tabs-container
|
[view st/tabs-inner-container
|
||||||
(doall (map-indexed #(create-tab %1 %2 selected-view-id) tab-list))]]))
|
(doall (map-indexed #(create-tab %1 %2 selected-view-id) tab-list))]])
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
(def ethereum-rpc-url "http://localhost:8545")
|
(def ethereum-rpc-url "http://localhost:8545")
|
||||||
|
|
||||||
(def server-address "http://rpc0.status.im:20000/")
|
(def server-address "http://api.status.im/")
|
||||||
;; (def server-address "http://10.0.3.2:3000/")
|
;; (def server-address "http://10.0.3.2:3000/")
|
||||||
;; (def server-address "http://localhost:3000/")
|
;; (def server-address "http://localhost:3000/")
|
||||||
|
|
||||||
|
@ -10,3 +10,10 @@
|
||||||
(def content-type-command "command")
|
(def content-type-command "command")
|
||||||
(def content-type-command-request "command-request")
|
(def content-type-command-request "command-request")
|
||||||
(def content-type-status "status")
|
(def content-type-status "status")
|
||||||
|
|
||||||
|
(def max-chat-name-length 20)
|
||||||
|
|
||||||
|
(def response-input-hiding-duration 100)
|
||||||
|
(def response-suggesstion-resize-duration 100)
|
||||||
|
|
||||||
|
(def default-number-of-messages 5)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
(ns status-im.contacts.handlers
|
(ns status-im.contacts.handlers
|
||||||
(:require [re-frame.core :refer [register-handler after dispatch]]
|
(:require [re-frame.core :refer [after dispatch]]
|
||||||
|
[status-im.utils.handlers :refer [register-handler]]
|
||||||
[status-im.models.contacts :as contacts]
|
[status-im.models.contacts :as contacts]
|
||||||
[status-im.utils.crypt :refer [encrypt]]
|
[status-im.utils.crypt :refer [encrypt]]
|
||||||
[clojure.string :as s]
|
[clojure.string :as s]
|
||||||
|
@ -96,7 +97,6 @@
|
||||||
(remove #(identities (:whisper-identity %)))
|
(remove #(identities (:whisper-identity %)))
|
||||||
(map #(vector (:whisper-identity %) %))
|
(map #(vector (:whisper-identity %) %))
|
||||||
(into {}))]
|
(into {}))]
|
||||||
(println new-contacts')
|
|
||||||
(-> db
|
(-> db
|
||||||
(update :contacts merge new-contacts')
|
(update :contacts merge new-contacts')
|
||||||
(assoc :new-contacts (vals new-contacts')))))
|
(assoc :new-contacts (vals new-contacts')))))
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
(ns status-im.contacts.screen
|
(ns status-im.contacts.screen
|
||||||
(:require-macros [status-im.utils.views :refer [defview]])
|
(:require-macros [status-im.utils.views :refer [defview]])
|
||||||
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
|
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
|
||||||
|
[reagent.core :as r]
|
||||||
[status-im.components.react :refer [view text
|
[status-im.components.react :refer [view text
|
||||||
image
|
image
|
||||||
touchable-highlight
|
touchable-highlight
|
||||||
|
linear-gradient
|
||||||
|
scroll-view
|
||||||
list-view
|
list-view
|
||||||
list-item]]
|
list-item] :as react]
|
||||||
[status-im.components.action-button :refer [action-button
|
[status-im.components.action-button :refer [action-button
|
||||||
action-button-item]]
|
action-button-item]]
|
||||||
[status-im.contacts.views.contact :refer [contact-view]]
|
[status-im.contacts.views.contact :refer [contact-extended-view]]
|
||||||
[status-im.components.styles :refer [toolbar-background2]]
|
|
||||||
[status-im.components.toolbar :refer [toolbar]]
|
[status-im.components.toolbar :refer [toolbar]]
|
||||||
[status-im.components.drawer.view :refer [drawer-view open-drawer]]
|
[status-im.components.drawer.view :refer [open-drawer]]
|
||||||
[status-im.components.icons.ionicons :refer [icon]]
|
[status-im.components.icons.ionicons :refer [icon]]
|
||||||
[status-im.components.styles :refer [color-blue
|
[status-im.components.styles :refer [color-blue
|
||||||
hamburger-icon
|
hamburger-icon
|
||||||
|
@ -19,34 +21,73 @@
|
||||||
create-icon
|
create-icon
|
||||||
toolbar-background2]]
|
toolbar-background2]]
|
||||||
[status-im.contacts.styles :as st]
|
[status-im.contacts.styles :as st]
|
||||||
[status-im.utils.listview :as lw]
|
|
||||||
[status-im.i18n :refer [label]]))
|
[status-im.i18n :refer [label]]))
|
||||||
|
|
||||||
(defn render-row [row _ _]
|
|
||||||
(list-item [contact-view row]))
|
|
||||||
|
|
||||||
(defn contact-list-toolbar []
|
(defn contact-list-toolbar []
|
||||||
[toolbar {:nav-action {:image {:source {:uri :icon_hamburger}
|
[toolbar {:nav-action {:image {:source {:uri :icon_hamburger}
|
||||||
:style hamburger-icon}
|
:style hamburger-icon}
|
||||||
:handler open-drawer}
|
:handler open-drawer}
|
||||||
:title (label :t/contacts)
|
:title (label :t/contacts)
|
||||||
:background-color toolbar-background2
|
:background-color toolbar-background2
|
||||||
|
:style {:elevation 0}
|
||||||
:action {:image {:source {:uri :icon_search}
|
:action {:image {:source {:uri :icon_search}
|
||||||
:style icon-search}
|
:style icon-search}
|
||||||
:handler (fn [])}}])
|
:handler (fn [])}}])
|
||||||
|
|
||||||
(defview contact-list []
|
(def contacts-limit 10)
|
||||||
[contacts [:get-contacts]]
|
|
||||||
[drawer-view
|
(defn contact-group [contacts contacts-count title group top?]
|
||||||
[view st/contacts-list-container
|
[view st/contact-group
|
||||||
[contact-list-toolbar]
|
[view st/contact-group-header
|
||||||
|
(when-not top?
|
||||||
|
[linear-gradient {:style st/contact-group-header-gradient-top
|
||||||
|
:colors st/contact-group-header-gradient-top-colors}])
|
||||||
|
[view st/contact-group-header-inner
|
||||||
|
[text {:style st/contact-group-text} title]
|
||||||
|
[text {:style st/contact-group-size-text} (str contacts-count)]]
|
||||||
|
[linear-gradient {:style st/contact-group-header-gradient-bottom
|
||||||
|
:colors st/contact-group-header-gradient-bottom-colors}]]
|
||||||
;; todo what if there is no contacts, should we show some information
|
;; todo what if there is no contacts, should we show some information
|
||||||
;; about this?
|
;; about this?
|
||||||
(when contacts
|
[view {:flexDirection :column}
|
||||||
[list-view {:dataSource (lw/to-datasource contacts)
|
(for [contact contacts]
|
||||||
:enableEmptySections true
|
;; TODO not imlemented: contact more button handler
|
||||||
:renderRow render-row
|
^{:key contact} [contact-extended-view contact nil nil])]
|
||||||
:style st/contacts-list}])
|
(when (= contacts-limit (count contacts))
|
||||||
|
[view st/show-all
|
||||||
|
[touchable-highlight {:on-press #(dispatch [:show-group-contacts group])}
|
||||||
|
[text {:style st/show-all-text} (label :show-all)]]])])
|
||||||
|
|
||||||
|
(defn contact-list []
|
||||||
|
(let [contacts (subscribe [:get-contacts-with-limit contacts-limit])
|
||||||
|
contcats-count (subscribe [:contacts-count])
|
||||||
|
show-toolbar-shadow? (r/atom false)]
|
||||||
|
(fn []
|
||||||
|
[view st/contacts-list-container
|
||||||
|
[contact-list-toolbar]
|
||||||
|
[view {:style st/toolbar-shadow}
|
||||||
|
(when @show-toolbar-shadow?
|
||||||
|
[linear-gradient {:style st/contact-group-header-gradient-bottom
|
||||||
|
:colors st/contact-group-header-gradient-bottom-colors}])]
|
||||||
|
(if (pos? @contcats-count)
|
||||||
|
[scroll-view {:style st/contact-groups
|
||||||
|
:onScroll (fn [e]
|
||||||
|
(let [offset (.. e -nativeEvent -contentOffset -y)]
|
||||||
|
(reset! show-toolbar-shadow? (<= st/contact-group-header-height offset))))}
|
||||||
|
;; TODO not implemented: dapps and persons separation
|
||||||
|
[contact-group
|
||||||
|
@contacts
|
||||||
|
@contcats-count
|
||||||
|
(label :t/contacs-group-dapps)
|
||||||
|
:dapps true]
|
||||||
|
[contact-group
|
||||||
|
@contacts
|
||||||
|
@contcats-count
|
||||||
|
(label :t/contacs-group-people)
|
||||||
|
:people false]]
|
||||||
|
[view st/empty-contact-groups
|
||||||
|
[react/icon :group_big st/empty-contacts-icon]
|
||||||
|
[text {:style st/empty-contacts-text} (label :t/no-contacts)]])
|
||||||
[action-button {:buttonColor color-blue
|
[action-button {:buttonColor color-blue
|
||||||
:offsetY 16
|
:offsetY 16
|
||||||
:offsetX 16}
|
:offsetX 16}
|
||||||
|
@ -55,5 +96,4 @@
|
||||||
:buttonColor :#9b59b6
|
:buttonColor :#9b59b6
|
||||||
:onPress #(dispatch [:navigate-to :new-contact])}
|
:onPress #(dispatch [:navigate-to :new-contact])}
|
||||||
[icon {:name :android-create
|
[icon {:name :android-create
|
||||||
:style create-icon}]]
|
:style create-icon}]]]])))
|
||||||
]]])
|
|
||||||
|
|
|
@ -1,72 +1,158 @@
|
||||||
(ns status-im.contacts.styles
|
(ns status-im.contacts.styles
|
||||||
(:require [status-im.components.styles :refer [font
|
(:require [status-im.components.styles :refer [font
|
||||||
|
font-medium
|
||||||
title-font
|
title-font
|
||||||
text1-color
|
text1-color
|
||||||
|
text2-color
|
||||||
|
text3-color
|
||||||
|
text5-color
|
||||||
color-white
|
color-white
|
||||||
toolbar-background2
|
toolbar-background2
|
||||||
online-color]]))
|
online-color
|
||||||
|
color-gray2]]))
|
||||||
|
|
||||||
|
|
||||||
(def contacts-list-container
|
(def contacts-list-container
|
||||||
{:flex 1
|
{:flex 1
|
||||||
:backgroundColor :white})
|
:backgroundColor :white})
|
||||||
|
|
||||||
|
(def toolbar-shadow
|
||||||
|
{:height 2
|
||||||
|
:backgroundColor toolbar-background2})
|
||||||
|
|
||||||
|
(def contact-groups
|
||||||
|
{:flex 1
|
||||||
|
:backgroundColor toolbar-background2})
|
||||||
|
|
||||||
|
(def empty-contact-groups
|
||||||
|
(merge contact-groups
|
||||||
|
{:align-items :center
|
||||||
|
:padding-top 150}))
|
||||||
|
|
||||||
|
(def empty-contacts-icon
|
||||||
|
{:height 62
|
||||||
|
:width 62})
|
||||||
|
|
||||||
|
(def empty-contacts-text
|
||||||
|
{:margin-top 12
|
||||||
|
:font-size 16
|
||||||
|
:color color-gray2})
|
||||||
|
|
||||||
(def contacts-list
|
(def contacts-list
|
||||||
{:backgroundColor :white})
|
{:backgroundColor :white})
|
||||||
|
|
||||||
(def contact-photo-container
|
(def contact-group
|
||||||
{:borderRadius 50})
|
{:flexDirection :column})
|
||||||
|
|
||||||
(def photo-image
|
(def contact-group-header
|
||||||
{:borderRadius 50
|
{:flexDirection :column
|
||||||
:width 40
|
:backgroundColor toolbar-background2})
|
||||||
:height 40})
|
|
||||||
|
|
||||||
(def online-container
|
(def contact-group-header-inner
|
||||||
{:position :absolute
|
{:flexDirection :row
|
||||||
:top 24
|
:alignItems :center
|
||||||
:left 24
|
:height 48
|
||||||
:width 20
|
:backgroundColor toolbar-background2})
|
||||||
:height 20
|
|
||||||
:borderRadius 50
|
|
||||||
:backgroundColor online-color
|
|
||||||
:borderWidth 2
|
|
||||||
:borderColor color-white})
|
|
||||||
|
|
||||||
(def online-dot
|
(def contact-group-text
|
||||||
{:position :absolute
|
{:flex 1
|
||||||
:top 6
|
:marginLeft 16
|
||||||
:width 4
|
:fontSize 14
|
||||||
:height 4
|
:fontFamily font-medium
|
||||||
:borderRadius 50
|
:color text5-color})
|
||||||
|
|
||||||
|
(def contact-group-size-text
|
||||||
|
{:marginRight 14
|
||||||
|
:fontSize 12
|
||||||
|
:fontFamily font
|
||||||
|
:color text2-color})
|
||||||
|
|
||||||
|
(def contact-group-header-gradient-top
|
||||||
|
{:flexDirection :row
|
||||||
|
:height 3
|
||||||
|
:backgroundColor toolbar-background2})
|
||||||
|
|
||||||
|
(def contact-group-header-gradient-top-colors
|
||||||
|
["rgba(24, 52, 76, 0.165)"
|
||||||
|
"rgba(24, 52, 76, 0.03)"
|
||||||
|
"rgba(24, 52, 76, 0.01)"])
|
||||||
|
|
||||||
|
(def contact-group-header-gradient-bottom
|
||||||
|
{:flexDirection :row
|
||||||
|
:height 2
|
||||||
|
:backgroundColor toolbar-background2})
|
||||||
|
|
||||||
|
(def contact-group-header-gradient-bottom-colors
|
||||||
|
["rgba(24, 52, 76, 0.01)"
|
||||||
|
"rgba(24, 52, 76, 0.05)"])
|
||||||
|
|
||||||
|
(def contact-group-header-height (+ (:height contact-group-header-inner)
|
||||||
|
(:height contact-group-header-gradient-bottom)))
|
||||||
|
|
||||||
|
(def show-all
|
||||||
|
{:flexDirection :row
|
||||||
|
:alignItems :center
|
||||||
|
:height 56
|
||||||
:backgroundColor color-white})
|
:backgroundColor color-white})
|
||||||
|
|
||||||
(def online-dot-left
|
(def show-all-text
|
||||||
(assoc online-dot :left 3))
|
{:marginLeft 72
|
||||||
|
:fontSize 14
|
||||||
(def online-dot-right
|
:fontFamily font-medium
|
||||||
(assoc online-dot :left 9))
|
:color text3-color
|
||||||
|
;; ios only:
|
||||||
|
:letterSpacing 0.5})
|
||||||
|
|
||||||
(def contact-container
|
(def contact-container
|
||||||
{:flexDirection :row
|
{:flexDirection :row
|
||||||
:height 56})
|
:backgroundColor color-white})
|
||||||
|
|
||||||
(def photo-container
|
(def letter-container
|
||||||
{:marginTop 8
|
{:paddingTop 11
|
||||||
:marginLeft 16
|
:paddingLeft 20
|
||||||
:width 44
|
:width 56})
|
||||||
:height 44})
|
|
||||||
|
|
||||||
(def name-container
|
(def letter-text
|
||||||
{:justifyContent :center})
|
{:fontSize 24
|
||||||
|
:fontFamily font
|
||||||
|
:color text3-color})
|
||||||
|
|
||||||
|
(def contact-photo-container
|
||||||
|
{:marginTop 4
|
||||||
|
:marginLeft 12})
|
||||||
|
|
||||||
|
(def contact-inner-container
|
||||||
|
{:flex 1
|
||||||
|
:flexDirection :row
|
||||||
|
:height 56
|
||||||
|
:backgroundColor color-white})
|
||||||
|
|
||||||
|
(def info-container
|
||||||
|
{:flex 1
|
||||||
|
:flexDirection :column
|
||||||
|
:marginLeft 12
|
||||||
|
:justifyContent :center})
|
||||||
|
|
||||||
(def name-text
|
(def name-text
|
||||||
{:marginLeft 16
|
{:fontSize 16
|
||||||
:fontSize 16
|
|
||||||
:fontFamily font
|
:fontFamily font
|
||||||
:color text1-color})
|
:color text1-color})
|
||||||
|
|
||||||
|
(def info-text
|
||||||
|
{:marginTop 1
|
||||||
|
:fontSize 12
|
||||||
|
:fontFamily font
|
||||||
|
:color text2-color})
|
||||||
|
|
||||||
|
(def more-btn
|
||||||
|
{:width 56
|
||||||
|
:height 56
|
||||||
|
:alignItems :center
|
||||||
|
:justifyContent :center})
|
||||||
|
|
||||||
|
(def more-btn-icon
|
||||||
|
{:width 4
|
||||||
|
:height 16})
|
||||||
|
|
||||||
; new contact
|
; new contact
|
||||||
|
|
||||||
(def contact-form-container
|
(def contact-form-container
|
||||||
|
|
|
@ -1,16 +1,48 @@
|
||||||
(ns status-im.contacts.subs
|
(ns status-im.contacts.subs
|
||||||
(:require-macros [reagent.ratom :refer [reaction]])
|
(:require-macros [reagent.ratom :refer [reaction]])
|
||||||
(:require [re-frame.core :refer [register-sub]]))
|
(:require [re-frame.core :refer [register-sub subscribe]]))
|
||||||
|
|
||||||
(register-sub :get-contacts
|
(register-sub :get-contacts
|
||||||
(fn [db _]
|
(fn [db _]
|
||||||
(let [contacts (reaction (:contacts @db))]
|
(let [contacts (reaction (:contacts @db))]
|
||||||
(reaction (vals @contacts)))))
|
(reaction (vals @contacts)))))
|
||||||
|
|
||||||
|
(defn sort-contacts [contacts]
|
||||||
|
(sort-by :name #(compare (clojure.string/lower-case %1)
|
||||||
|
(clojure.string/lower-case %2)) (vals contacts)))
|
||||||
|
|
||||||
(register-sub :all-contacts
|
(register-sub :all-contacts
|
||||||
(fn [db _]
|
(fn [db _]
|
||||||
(let [contacts (reaction (:contacts @db))]
|
(let [contacts (reaction (:contacts @db))]
|
||||||
(reaction (sort-by :name (vals @contacts))))))
|
(reaction (sort-contacts @contacts)))))
|
||||||
|
|
||||||
|
(register-sub :get-contacts-with-limit
|
||||||
|
(fn [_ [_ limit]]
|
||||||
|
(let [contacts (subscribe [:all-contacts])]
|
||||||
|
(reaction (take limit @contacts)))))
|
||||||
|
|
||||||
|
(register-sub :contacts-count
|
||||||
|
(fn [_ _]
|
||||||
|
(let [contacts (subscribe [:all-contacts])]
|
||||||
|
(reaction (count @contacts)))))
|
||||||
|
|
||||||
|
(defn get-contact-letter [contact]
|
||||||
|
(when-let [letter (first (:name contact))]
|
||||||
|
(clojure.string/upper-case letter)))
|
||||||
|
|
||||||
|
(register-sub :contacts-with-letters
|
||||||
|
(fn [db _]
|
||||||
|
(let [contacts (reaction (:contacts @db))]
|
||||||
|
(reaction
|
||||||
|
(let [ordered (sort-contacts @contacts)]
|
||||||
|
(reduce (fn [prev cur]
|
||||||
|
(let [prev-letter (get-contact-letter (last prev))
|
||||||
|
cur-letter (get-contact-letter cur)]
|
||||||
|
(conj prev
|
||||||
|
(if (not= prev-letter cur-letter)
|
||||||
|
(assoc cur :letter cur-letter)
|
||||||
|
cur))))
|
||||||
|
[] ordered))))))
|
||||||
|
|
||||||
(defn contacts-by-chat [fn db chat-id]
|
(defn contacts-by-chat [fn db chat-id]
|
||||||
(let [chat (reaction (get-in @db [:chats chat-id]))
|
(let [chat (reaction (get-in @db [:chats chat-id]))
|
||||||
|
@ -33,6 +65,10 @@
|
||||||
(let [identity (:contact-identity @db)]
|
(let [identity (:contact-identity @db)]
|
||||||
(reaction (get-in @db [:contacts identity])))))
|
(reaction (get-in @db [:contacts identity])))))
|
||||||
|
|
||||||
|
(register-sub :contact-by-identity
|
||||||
|
(fn [db [_ identity]]
|
||||||
|
(reaction (get-in @db [:contacts identity]))))
|
||||||
|
|
||||||
(register-sub :all-new-contacts
|
(register-sub :all-new-contacts
|
||||||
(fn [db _]
|
(fn [db _]
|
||||||
(contacts-by-current-chat remove db)))
|
(contacts-by-current-chat remove db)))
|
||||||
|
|
|
@ -1,16 +1,35 @@
|
||||||
(ns status-im.contacts.views.contact
|
(ns status-im.contacts.views.contact
|
||||||
(:require-macros [status-im.utils.views :refer [defview]])
|
(:require-macros [status-im.utils.views :refer [defview]])
|
||||||
(:require [status-im.components.react :refer [view touchable-highlight]]
|
(:require [status-im.components.react :refer [view text icon touchable-highlight]]
|
||||||
[re-frame.core :refer [dispatch subscribe]]
|
[re-frame.core :refer [dispatch subscribe]]
|
||||||
|
[status-im.contacts.styles :as st]
|
||||||
[status-im.contacts.views.contact-inner :refer [contact-inner-view]]))
|
[status-im.contacts.views.contact-inner :refer [contact-inner-view]]))
|
||||||
|
|
||||||
|
(defn letter-view [letter]
|
||||||
|
[view st/letter-container
|
||||||
|
(when letter
|
||||||
|
[text {:style st/letter-text} letter])])
|
||||||
|
|
||||||
(defn on-press [chat whisper-identity]
|
(defn on-press [chat whisper-identity]
|
||||||
(if chat
|
(if chat
|
||||||
#(dispatch [:navigate-to :chat whisper-identity])
|
#(dispatch [:navigate-to :chat whisper-identity])
|
||||||
#(dispatch [:start-chat whisper-identity])))
|
#(dispatch [:start-chat whisper-identity])))
|
||||||
|
|
||||||
(defview contact-view [{:keys [whisper-identity] :as contact}]
|
(defview contact-with-letter-view [{:keys [whisper-identity letter] :as contact}]
|
||||||
[chat [:get-chat whisper-identity]]
|
[chat [:get-chat whisper-identity]]
|
||||||
[touchable-highlight
|
[touchable-highlight
|
||||||
{:onPress (on-press chat whisper-identity)}
|
{:onPress (on-press chat whisper-identity)}
|
||||||
[view {} [contact-inner-view contact]]])
|
[view st/contact-container
|
||||||
|
[letter-view letter]
|
||||||
|
[contact-inner-view contact]]])
|
||||||
|
|
||||||
|
(defview contact-extended-view [{:keys [whisper-identity] :as contact} info more-click-handler]
|
||||||
|
[chat [:get-chat whisper-identity]]
|
||||||
|
[touchable-highlight
|
||||||
|
{:onPress (on-press chat whisper-identity)}
|
||||||
|
[view st/contact-container
|
||||||
|
[contact-inner-view contact info]
|
||||||
|
[touchable-highlight
|
||||||
|
{:on-press more-click-handler}
|
||||||
|
[view st/more-btn
|
||||||
|
[icon :more-vertical st/more-btn-icon]]]]])
|
||||||
|
|
|
@ -1,31 +1,26 @@
|
||||||
(ns status-im.contacts.views.contact-inner
|
(ns status-im.contacts.views.contact-inner
|
||||||
(:require [clojure.string :as s]
|
(:require [clojure.string :as s]
|
||||||
[status-im.components.react :refer [view image text]]
|
[status-im.components.react :refer [view image text]]
|
||||||
[status-im.resources :as res]
|
[status-im.components.chat-icon.screen :refer [contact-icon-contacts-tab]]
|
||||||
[status-im.contacts.styles :as st]
|
[status-im.contacts.styles :as st]
|
||||||
[status-im.i18n :refer [label]]))
|
[status-im.i18n :refer [label]]))
|
||||||
|
|
||||||
(defn contact-photo [{:keys [photo-path]}]
|
(defn contact-photo [contact]
|
||||||
[view st/contact-photo-container
|
[view st/contact-photo-container
|
||||||
[image {:source (if (s/blank? photo-path)
|
[contact-icon-contacts-tab contact]])
|
||||||
res/user-no-photo
|
|
||||||
{:uri photo-path})
|
|
||||||
:style st/photo-image}]])
|
|
||||||
|
|
||||||
(defn contact-online [{:keys [online]}]
|
(defn contact-inner-view
|
||||||
(when online
|
([contact]
|
||||||
[view st/online-container
|
(contact-inner-view contact nil))
|
||||||
[view st/online-dot-left]
|
([{:keys [name] :as contact} info]
|
||||||
[view st/online-dot-right]]))
|
[view st/contact-inner-container
|
||||||
|
[contact-photo contact]
|
||||||
(defn contact-inner-view [{:keys [name photo-path online]}]
|
[view st/info-container
|
||||||
[view st/contact-container
|
|
||||||
[view st/photo-container
|
|
||||||
[contact-photo {:photo-path photo-path}]
|
|
||||||
[contact-online {:online online}]]
|
|
||||||
[view st/name-container
|
|
||||||
[text {:style st/name-text}
|
[text {:style st/name-text}
|
||||||
(if (pos? (count name))
|
(if (pos? (count (:name contact)))
|
||||||
name
|
name
|
||||||
;; todo is this correct behaviour?
|
;; todo is this correct behaviour?
|
||||||
(label :t/no-name))]]])
|
(label :t/no-name))]
|
||||||
|
(when info
|
||||||
|
[text {:style st/info-text}
|
||||||
|
info])]]))
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
(ns status-im.contacts.views.contact-list
|
||||||
|
(:require-macros [status-im.utils.views :refer [defview]])
|
||||||
|
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
|
||||||
|
[status-im.components.react :refer [view text
|
||||||
|
image
|
||||||
|
touchable-highlight
|
||||||
|
list-view
|
||||||
|
list-item]]
|
||||||
|
[status-im.contacts.views.contact :refer [contact-with-letter-view]]
|
||||||
|
[status-im.components.toolbar :refer [toolbar]]
|
||||||
|
[status-im.components.drawer.view :refer [drawer-view open-drawer]]
|
||||||
|
[status-im.components.icons.ionicons :refer [icon]]
|
||||||
|
[status-im.components.styles :refer [color-blue
|
||||||
|
hamburger-icon
|
||||||
|
icon-search
|
||||||
|
create-icon
|
||||||
|
toolbar-background1]]
|
||||||
|
[status-im.contacts.styles :as st]
|
||||||
|
[status-im.utils.listview :as lw]
|
||||||
|
[status-im.i18n :refer [label]]))
|
||||||
|
|
||||||
|
(defn render-row [row _ _]
|
||||||
|
(list-item [contact-with-letter-view row]))
|
||||||
|
|
||||||
|
(defview contact-list-toolbar []
|
||||||
|
[group [:get :contacts-group]]
|
||||||
|
[toolbar {:title (label (if (= group :dapps)
|
||||||
|
:t/contacs-group-dapps
|
||||||
|
:t/contacs-group-people))
|
||||||
|
:background-color toolbar-background1
|
||||||
|
:action {:image {:source {:uri :icon_search}
|
||||||
|
:style icon-search}
|
||||||
|
:handler (fn [])}}])
|
||||||
|
|
||||||
|
(defview contact-list []
|
||||||
|
[contacts [:contacts-with-letters]]
|
||||||
|
[drawer-view
|
||||||
|
[view st/contacts-list-container
|
||||||
|
[contact-list-toolbar]
|
||||||
|
;; todo what if there is no contacts, should we show some information
|
||||||
|
;; about this?
|
||||||
|
(when contacts
|
||||||
|
[list-view {:dataSource (lw/to-datasource contacts)
|
||||||
|
:enableEmptySections true
|
||||||
|
:renderRow render-row
|
||||||
|
:style st/contacts-list}])]])
|
|
@ -7,8 +7,8 @@
|
||||||
image
|
image
|
||||||
linear-gradient
|
linear-gradient
|
||||||
touchable-highlight]]
|
touchable-highlight]]
|
||||||
|
[status-im.utils.identicon :refer [identicon]]
|
||||||
[status-im.components.toolbar :refer [toolbar]]
|
[status-im.components.toolbar :refer [toolbar]]
|
||||||
[status-im.components.drawer.view :refer [drawer-view open-drawer]]
|
|
||||||
[status-im.components.styles :refer [color-purple
|
[status-im.components.styles :refer [color-purple
|
||||||
color-white
|
color-white
|
||||||
icon-search
|
icon-search
|
||||||
|
@ -56,7 +56,6 @@
|
||||||
|
|
||||||
(defview new-contact []
|
(defview new-contact []
|
||||||
[{:keys [name whisper-identity phone-number] :as new-contact} [:get :new-contact]]
|
[{:keys [name whisper-identity phone-number] :as new-contact} [:get :new-contact]]
|
||||||
[drawer-view
|
|
||||||
[view st/contact-form-container
|
[view st/contact-form-container
|
||||||
[linear-gradient {:colors ["rgba(182, 116, 241, 1)" "rgba(107, 147, 231, 1)" "rgba(43, 171, 238, 1)"]
|
[linear-gradient {:colors ["rgba(182, 116, 241, 1)" "rgba(107, 147, 231, 1)" "rgba(43, 171, 238, 1)"]
|
||||||
:start [0, 0]
|
:start [0, 0]
|
||||||
|
@ -71,8 +70,7 @@
|
||||||
:custom-content toolbar-title
|
:custom-content toolbar-title
|
||||||
:action {:image {:source {:uri :icon_add}
|
:action {:image {:source {:uri :icon_add}
|
||||||
:style icon-search}
|
:style icon-search}
|
||||||
:handler #(dispatch [:add-new-contact new-contact])}}]
|
:handler #(dispatch [:add-new-contact (merge {:photo-path (identicon whisper-identity)} new-contact)])}}]
|
||||||
[view st/form-container
|
[view st/form-container
|
||||||
[contact-whisper-id-input whisper-identity]
|
[contact-whisper-id-input whisper-identity]
|
||||||
[contact-name-input name]
|
[contact-name-input name]]])
|
||||||
]]])
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
(ns status-im.db
|
(ns status-im.db
|
||||||
(:require [schema.core :as s :include-macros true]))
|
(:require [schema.core :as s :include-macros true]
|
||||||
|
[status-im.components.react :refer [animated]]
|
||||||
|
[status-im.components.animation :as anim]))
|
||||||
|
|
||||||
;; schema of app-db
|
;; schema of app-db
|
||||||
(def schema {:greeting s/Str})
|
(def schema {:greeting s/Str})
|
||||||
|
@ -34,7 +36,10 @@
|
||||||
:address ""
|
:address ""
|
||||||
:whisper-identity ""
|
:whisper-identity ""
|
||||||
:phone-number ""}
|
:phone-number ""}
|
||||||
:disable-group-creation false})
|
:disable-group-creation false
|
||||||
|
:animations {:to-response-height 0.1
|
||||||
|
;; todo clear this
|
||||||
|
:tabs-bar-value (anim/create-value 0)}})
|
||||||
|
|
||||||
(def protocol-initialized-path [:protocol-initialized])
|
(def protocol-initialized-path [:protocol-initialized])
|
||||||
(defn chat-input-text-path [chat-id]
|
(defn chat-input-text-path [chat-id]
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
(ns status-im.discovery.handlers
|
(ns status-im.discovery.handlers
|
||||||
(:require [re-frame.core :refer [register-handler after dispatch enrich]]
|
(:require [re-frame.core :refer [after dispatch enrich]]
|
||||||
|
[status-im.utils.handlers :refer [register-handler]]
|
||||||
[status-im.protocol.api :as api]
|
[status-im.protocol.api :as api]
|
||||||
[status-im.navigation.handlers :as nav]
|
[status-im.navigation.handlers :as nav]
|
||||||
[status-im.discovery.model :as discoveries]
|
[status-im.discovery.model :as discoveries]
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
(:require [status-im.components.styles :refer [font
|
(:require [status-im.components.styles :refer [font
|
||||||
title-font
|
title-font
|
||||||
color-white
|
color-white
|
||||||
|
color-gray2
|
||||||
chat-background
|
chat-background
|
||||||
online-color
|
online-color
|
||||||
selected-message-color
|
selected-message-color
|
||||||
|
@ -49,7 +50,7 @@
|
||||||
:elevation 0})
|
:elevation 0})
|
||||||
|
|
||||||
(def discovery-subtitle
|
(def discovery-subtitle
|
||||||
{:color "#8f838c93"
|
{:color color-gray2
|
||||||
:fontFamily "sans-serif-medium"
|
:fontFamily "sans-serif-medium"
|
||||||
:fontSize 14})
|
:fontSize 14})
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
(ns status-im.group-settings.handlers
|
(ns status-im.group-settings.handlers
|
||||||
(:require [re-frame.core :refer [register-handler debug dispatch after
|
(:require [re-frame.core :refer [debug dispatch after enrich]]
|
||||||
enrich]]
|
[status-im.utils.handlers :refer [register-handler]]
|
||||||
[status-im.persistence.realm :as r]
|
[status-im.persistence.realm :as r]
|
||||||
[status-im.chat.handlers :refer [delete-messages!]]
|
[status-im.chat.handlers :refer [delete-messages!]]
|
||||||
[status-im.protocol.api :as api]
|
[status-im.protocol.api :as api]
|
||||||
|
|
|
@ -146,7 +146,9 @@
|
||||||
(defview chat-name []
|
(defview chat-name []
|
||||||
[name [:chat :name]
|
[name [:chat :name]
|
||||||
new-name [:get :new-chat-name]
|
new-name [:get :new-chat-name]
|
||||||
focused? [:get ::name-input-focused]]
|
validation-messages [:new-chat-name-validation-messages]
|
||||||
|
focused? [:get ::name-input-focused]
|
||||||
|
valid? [:new-chat-name-valid?]]
|
||||||
[view
|
[view
|
||||||
[text {:style st/chat-name-text} (label :t/chat-name)]
|
[text {:style st/chat-name-text} (label :t/chat-name)]
|
||||||
[view (st/chat-name-value-container focused?)
|
[view (st/chat-name-value-container focused?)
|
||||||
|
@ -157,12 +159,14 @@
|
||||||
:on-blur blur}
|
:on-blur blur}
|
||||||
name]
|
name]
|
||||||
(if (or focused? (not= name new-name))
|
(if (or focused? (not= name new-name))
|
||||||
[touchable-highlight {:style st/chat-name-btn-edit-container
|
[touchable-highlight {:style (st/chat-name-btn-edit-container valid?)
|
||||||
:on-press save}
|
:on-press save}
|
||||||
[view [icon :ok-purple st/add-members-icon]]]
|
[view [icon :ok-purple st/add-members-icon]]]
|
||||||
[touchable-highlight {:style st/chat-name-btn-edit-container
|
[touchable-highlight {:style (st/chat-name-btn-edit-container true)
|
||||||
:on-press focus}
|
:on-press focus}
|
||||||
[text {:style st/chat-name-btn-edit-text} (label :t/edit)]])]])
|
[text {:style st/chat-name-btn-edit-text} (label :t/edit)]])]
|
||||||
|
(when (pos? (count validation-messages))
|
||||||
|
[text {:style st/chat-name-validation-message} (first validation-messages)])])
|
||||||
|
|
||||||
(defview group-settings []
|
(defview group-settings []
|
||||||
[show-color-picker [:group-settings :show-color-picker]]
|
[show-color-picker [:group-settings :show-color-picker]]
|
||||||
|
|
|
@ -91,9 +91,15 @@
|
||||||
:fontFamily font
|
:fontFamily font
|
||||||
:color text1-color})
|
:color text1-color})
|
||||||
|
|
||||||
(def chat-name-btn-edit-container
|
(def chat-name-validation-message
|
||||||
|
{:marginTop 8
|
||||||
|
:marginLeft 16
|
||||||
|
:color :red})
|
||||||
|
|
||||||
|
(defn chat-name-btn-edit-container [enabled?]
|
||||||
{:padding 16
|
{:padding 16
|
||||||
:justifyContent :center})
|
:justifyContent :center
|
||||||
|
:opacity (if enabled? 1 0.3)})
|
||||||
|
|
||||||
(def chat-name-btn-edit-text
|
(def chat-name-btn-edit-text
|
||||||
{:marginTop -1
|
{:marginTop -1
|
||||||
|
|