diff --git a/.re-natal b/.re-natal index 657ba14566..44f7c2791b 100644 --- a/.re-natal +++ b/.re-natal @@ -19,7 +19,8 @@ "react-native-android-sms-listener", "react-native-status", "react-native-camera", - "react-native-qrcode" + "react-native-qrcode", + "identicon.js" ], "imageDirs": [ "images" diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index d8e1a14016..42b5ee2517 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -22,6 +22,11 @@ + diff --git a/android/app/src/main/java/com/statusim/GethService.java b/android/app/src/main/java/com/statusim/GethService.java new file mode 100644 index 0000000000..7967807cbd --- /dev/null +++ b/android/app/src/main/java/com/statusim/GethService.java @@ -0,0 +1,136 @@ +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.Looper; +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.ethereum.go_ethereum.cmd.Geth; + +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 service; + + IncomingHandler(GethService service) { + + this.service = new WeakReference(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 { + + 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; + + 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); + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return serviceMessenger.getBinder(); + } + + @Override + public void onCreate() { + super.onCreate(); + System.loadLibrary("gethraw"); + System.loadLibrary("geth"); + + 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; + } +} diff --git a/android/app/src/main/java/com/statusim/MainActivity.java b/android/app/src/main/java/com/statusim/MainActivity.java index a074f3f653..53bd8f65f8 100644 --- a/android/app/src/main/java/com/statusim/MainActivity.java +++ b/android/app/src/main/java/com/statusim/MainActivity.java @@ -7,17 +7,24 @@ import com.facebook.react.ReactPackage; import com.facebook.react.shell.MainReactPackage; import com.rt2zz.reactnativecontacts.ReactNativeContacts; 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.content.DialogInterface; import android.content.DialogInterface.OnClickListener; 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.BV.LinearGradient.LinearGradientPackage; import com.centaurwarchief.smslistener.SmsListener; -import android.os.Handler; + import android.util.Log; import java.util.Arrays; @@ -33,45 +40,69 @@ import io.realm.react.RealmReactPackage; 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() { // Required because of crazy APN settings redirecting localhost (found in GB) Properties properties = System.getProperties(); properties.setProperty("http.nonProxyHosts", "localhost|127.0.0.1"); properties.setProperty("https.nonProxyHosts", "localhost|127.0.0.1"); - - 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 protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - // Required for android-16 (???) - // Crash if put in startStatus() ? - System.loadLibrary("gethraw"); - System.loadLibrary("geth"); - if(!RootUtil.isDeviceRooted()) { startStatus(); } else { @@ -97,9 +128,27 @@ public class MainActivity extends ReactActivity { }).create(); 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. * This is used to schedule rendering of the component. diff --git a/android/app/src/main/res/drawable-hdpi/icon_group_big.png b/android/app/src/main/res/drawable-hdpi/icon_group_big.png new file mode 100644 index 0000000000..68687a6dfa Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_group_big.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_group_big.png b/android/app/src/main/res/drawable-mdpi/icon_group_big.png new file mode 100644 index 0000000000..c4bf8a7989 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_group_big.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_group_big.png b/android/app/src/main/res/drawable-xhdpi/icon_group_big.png new file mode 100644 index 0000000000..8b47926213 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_group_big.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_group_big.png b/android/app/src/main/res/drawable-xxhdpi/icon_group_big.png new file mode 100644 index 0000000000..a1bff88bb0 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_group_big.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_group_big.png b/android/app/src/main/res/drawable-xxxhdpi/icon_group_big.png new file mode 100644 index 0000000000..bf47ac4f7b Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_group_big.png differ diff --git a/android/settings.gradle b/android/settings.gradle index fa0064d1f4..e5688b8a60 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -13,7 +13,7 @@ project(':react-native-vector-icons').projectDir = new File(rootProject.projectD include ':realm' project(':realm').projectDir = new File(rootProject.projectDir, '../node_modules/realm/android') 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' project(':react-native-linear-gradient').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-linear-gradient/android') include ':ReactNativeAndroidSmsListener' diff --git a/package.json b/package.json index 59e41e8753..4143c141d3 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ }, "dependencies": { "awesome-phonenumber": "^1.0.13", + "identicon.js": "github:status-im/identicon.js", "react": "^0.14.5", "react-native": "^0.24.1", "react-native-action-button": "^1.1.4", @@ -16,10 +17,10 @@ "react-native-contacts": "^0.2.4", "react-native-i18n": "0.0.8", "react-native-invertible-scroll-view": "^1.0.0", - "react-native-linear-gradient": "^1.5.7", + "react-native-linear-gradient": "1.5.7", "react-native-loading-spinner-overlay": "0.0.8", "react-native-qrcode": "^0.2.2", - "react-native-randombytes": "^2.0.0", + "react-native-randombytes": "^2.1.0", "react-native-vector-icons": "^1.3.4", "realm": "^0.11.1", "react-native-status": "git+ssh://git@github.com/status-im/react-native-status" diff --git a/src/status_im/android/core.cljs b/src/status_im/android/core.cljs index 951c7d1b32..6be3ffbbe4 100644 --- a/src/status_im/android/core.cljs +++ b/src/status_im/android/core.cljs @@ -7,7 +7,7 @@ [status-im.subs] [status-im.components.react :refer [navigator app-registry]] [status-im.components.main-tabs :refer [main-tabs]] - [status-im.contacts.screen :refer [contact-list]] + [status-im.contacts.views.contact-list :refer [contact-list] ] [status-im.contacts.views.new-contact :refer [new-contact]] [status-im.qr-scanner.screen :refer [qr-scanner]] [status-im.discovery.screen :refer [discovery]] @@ -46,6 +46,7 @@ :new-group [new-group] :group-settings [group-settings] :contact-list [main-tabs] + :group-contacts [contact-list] :new-contact [new-contact] :qr-scanner [qr-scanner] :chat [chat] diff --git a/src/status_im/chat/views/message_input.cljs b/src/status_im/chat/views/message_input.cljs index 4c26e15eb8..e40cfa4f8f 100644 --- a/src/status_im/chat/views/message_input.cljs +++ b/src/status_im/chat/views/message_input.cljs @@ -6,8 +6,7 @@ animated-view icon touchable-highlight - text-input - dismiss-keyboard!]] + text-input]] [status-im.components.animation :as anim] [status-im.chat.views.plain-message :as plain-message] [status-im.chat.views.command :as command] @@ -61,8 +60,7 @@ staged-commands [:get-chat-staged-commands] typing-command? [:typing-command?] commands-input-is-switching? [:animations :commands-input-is-switching?]] - (let [dismiss-keyboard (not (or command typing-command?)) - response? (and command to-msg-id) + (let [response? (and command to-msg-id) message-input? (or (not command) commands-input-is-switching?) animation? commands-input-is-switching?] [text-input (merge {:style (cond @@ -72,7 +70,7 @@ :ref (fn [input] (dispatch [:set-message-input input])) :autoFocus false - :blurOnSubmit dismiss-keyboard + :blurOnSubmit false :onChangeText (fn [text] (when-not animation? ((if message-input? @@ -82,8 +80,7 @@ :onSubmitEditing #(when-not animation? (if message-input? (plain-message/try-send staged-commands - input-message - dismiss-keyboard) + input-message) (command/try-send input-command validator)))} (when command {:accessibility-label :command-input}) @@ -100,8 +97,7 @@ staged-commands [:get-chat-staged-commands] typing-command? [:typing-command?] commands-input-is-switching? [:animations :commands-input-is-switching?]] - (let [dismiss-keyboard (not (or command typing-command?)) - response? (and command to-msg-id) + (let [response? (and command to-msg-id) message-input? (or (not command) commands-input-is-switching?)] [view st/input-container [view st/input-view @@ -117,8 +113,7 @@ (if message-input? (when (plain-message/message-valid? staged-commands input-message) [send-button {:on-press #(plain-message/try-send staged-commands - input-message - dismiss-keyboard) + input-message) :accessibility-label :send-message}]) (if (command/valid? input-command validator) [send-button {:on-press command/send-command diff --git a/src/status_im/chat/views/plain_message.cljs b/src/status_im/chat/views/plain_message.cljs index 7c637ebbd0..0284e0e25f 100644 --- a/src/status_im/chat/views/plain_message.cljs +++ b/src/status_im/chat/views/plain_message.cljs @@ -5,8 +5,7 @@ [status-im.components.react :refer [view animated-view icon - touchable-highlight - dismiss-keyboard!]] + touchable-highlight]] [status-im.components.animation :as anim] [status-im.chat.styles.plain-message :as st] [status-im.constants :refer [response-input-hiding-duration]])) @@ -14,19 +13,14 @@ (defn set-input-message [message] (dispatch [:set-chat-input-text message])) -(defn send [dismiss-keyboard] - (when dismiss-keyboard - (dismiss-keyboard!)) - (dispatch [:send-chat-msg])) - (defn message-valid? [staged-commands message] (or (and (pos? (count message)) (not= "!" message)) (pos? (count staged-commands)))) -(defn try-send [staged-commands message dismiss-keyboard] +(defn try-send [staged-commands message] (when (message-valid? staged-commands message) - (send dismiss-keyboard))) + (dispatch [:send-chat-msg]))) (defn prepare-message-input [message-input] (when message-input diff --git a/src/status_im/chats_list/screen.cljs b/src/status_im/chats_list/screen.cljs index 572f0ac5a9..9e38f48307 100644 --- a/src/status_im/chats_list/screen.cljs +++ b/src/status_im/chats_list/screen.cljs @@ -1,8 +1,10 @@ (ns status-im.chats-list.screen + (:require-macros [status-im.utils.views :refer [defview]]) (:require [re-frame.core :refer [subscribe dispatch]] [status-im.components.react :refer [list-view list-item view + animated-view text image touchable-highlight]] @@ -11,42 +13,68 @@ [status-im.chats-list.views.chat-list-item :refer [chat-list-item]] [status-im.components.action-button :refer [action-button 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 + toolbar-background1 toolbar-background2]] [status-im.components.toolbar :refer [toolbar]] [status-im.components.icons.ionicons :refer [icon]] [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} :style st/hamburger-icon} :handler open-drawer} :title (label :t/chats) - :background-color toolbar-background2 + :background-color (if chats-scrolled? + toolbar-background1 + toolbar-background2) ;; TODO implement search :action {:image {:source {:uri :icon_search} :style st/search-icon} :handler (fn [])}}]) (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 [] - [drawer-view - [view st/chats-container - [chats-list-toolbar] - [list-view {:dataSource (to-datasource @chats) - :renderRow (fn [row _ _] - (list-item [chat-list-item row])) - :style st/list-container}] + [view st/chats-container + [chats-list-toolbar] + [list-view {:dataSource (to-datasource @chats) + :renderRow (fn [row _ _] + (list-item [chat-list-item row])) + :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 :offsetY 16 :offsetX 16} [action-button-item {:title (label :t/new-chat) :buttonColor :#9b59b6 - :onPress #(dispatch [:navigate-to :contact-list])} + :onPress #(dispatch [:show-group-contacts :people])} [icon {:name :android-create :style st/create-icon}]] [action-button-item diff --git a/src/status_im/chats_list/styles.cljs b/src/status_im/chats_list/styles.cljs index ca72f89042..6621aedfce 100644 --- a/src/status_im/chats_list/styles.cljs +++ b/src/status_im/chats_list/styles.cljs @@ -6,7 +6,8 @@ online-color text1-color text2-color - new-messages-count-color]])) + new-messages-count-color]] + [status-im.components.tabs.styles :refer [tabs-height]])) (def chat-container {:flexDirection :row @@ -113,3 +114,11 @@ {:fontSize 20 :height 22 :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)}]}) diff --git a/src/status_im/components/chat_icon/screen.cljs b/src/status_im/components/chat_icon/screen.cljs index 0d93e38cbb..3b6437c193 100644 --- a/src/status_im/components/chat_icon/screen.cljs +++ b/src/status_im/components/chat_icon/screen.cljs @@ -6,7 +6,7 @@ image icon]] [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])) (defn default-chat-icon [name styles] @@ -63,6 +63,26 @@ :default-chat-icon (st/default-chat-icon-menu-item color) :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] (let [styles {:container st/container-profile :online-view st/online-view-profile @@ -81,7 +101,7 @@ [contact [:contact]] (let [;; TODO stub data online true - color color-purple] + color default-chat-color] [profile-icon-view (:photo-path contact) (:name contact) color online])) (defview my-profile-icon [] @@ -89,5 +109,5 @@ photo-path [:get :photo-path]] (let [;; TODO stub data online true - color color-purple] + color default-chat-color] [profile-icon-view photo-path name color online])) diff --git a/src/status_im/components/main_tabs.cljs b/src/status_im/components/main_tabs.cljs index 01551e75da..d59b1d676b 100644 --- a/src/status_im/components/main_tabs.cljs +++ b/src/status_im/components/main_tabs.cljs @@ -10,6 +10,7 @@ image 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.discovery.screen :refer [discovery]] @@ -101,9 +102,10 @@ (defview main-tabs [] [view-id [:get :view-id] tab-animation? [:get :prev-tab-view-id]] - [view {:style common-st/flex} - [view {:style common-st/flex - :pointerEvents (if tab-animation? :none :auto)} - (doall (map #(tab-view %) tab-list))] - [tabs {:selected-view-id view-id - :tab-list tab-list}]]) + [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 + :tab-list tab-list}]]]) diff --git a/src/status_im/components/styles.cljs b/src/status_im/components/styles.cljs index a1176d46bf..aaa82f5a42 100644 --- a/src/status_im/components/styles.cljs +++ b/src/status_im/components/styles.cljs @@ -10,6 +10,7 @@ (def color-black "#000000de") (def color-purple "#a187d5") (def color-gray "#838c93de") +(def color-gray2 "#8f838c93") (def color-white :white) (def color-light-blue "#bbc4cb") (def color-light-blue-transparent "#bbc4cb32") @@ -20,6 +21,7 @@ (def text2-color color-gray) (def text3-color color-blue) (def text4-color color-white) +(def text5-color "#838c938f") (def online-color color-blue) (def new-messages-count-color color-blue-transparent) (def chat-background color-light-gray) @@ -98,4 +100,4 @@ (def button-input {:flex 1 :flexDirection :column - :height 50}) \ No newline at end of file + :height 50}) diff --git a/src/status_im/components/tabs/styles.cljs b/src/status_im/components/tabs/styles.cljs index 45af60e439..2d873e88cd 100644 --- a/src/status_im/components/tabs/styles.cljs +++ b/src/status_im/components/tabs/styles.cljs @@ -10,21 +10,21 @@ text2-color toolbar-background1]])) +(def tabs-height 59) (def tab-height 56) -(def tabs - {:flex 1 - :position :absolute - :bottom 0 - :right 0 - :left 0 - }) +(defn tabs-container [hidden? animation? offset-y] + {:height tabs-height + :backgroundColor color-white + :marginBottom (if (or hidden? animation?) + (- tabs-height) 0) + :transform [{:translateY (if animation? offset-y 1)}]}) (def top-gradient {:flexDirection :row :height 3}) -(def tabs-container +(def tabs-inner-container {:flexDirection :row :height tab-height :opacity 1 @@ -55,10 +55,9 @@ :alignItems :center}) (defn tab-view-container [offset-x] - {:flex 1 - :position :absolute + {:position :absolute :top 0 :left 0 :right 0 - :bottom tab-height + :bottom 0 :transform [{:translateX offset-x}]}) diff --git a/src/status_im/components/tabs/tabs.cljs b/src/status_im/components/tabs/tabs.cljs index bd12614f3f..a6d10d5d8d 100644 --- a/src/status_im/components/tabs/tabs.cljs +++ b/src/status_im/components/tabs/tabs.cljs @@ -2,6 +2,7 @@ (:require-macros [status-im.utils.views :refer [defview]]) (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] [status-im.components.react :refer [view + animated-view text-input text image @@ -9,7 +10,8 @@ linear-gradient]] [reagent.core :as r] [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] (let [data (merge data {:key index @@ -17,10 +19,43 @@ :selected-view-id selected-view-id})] [tab data])) -(defview tabs [{:keys [style tab-list selected-view-id]}] - (let [style (merge st/tabs style)] - [view {:style style} - [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}] - [view st/tabs-container - (doall (map-indexed #(create-tab %1 %2 selected-view-id) tab-list))]])) +(defn animation-logic [{:keys [hidden? val]}] + (let [was-hidden? (atom (not @hidden?))] + (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)"] + :style st/top-gradient}] + [view st/tabs-inner-container + (doall (map-indexed #(create-tab %1 %2 selected-view-id) tab-list))]]) diff --git a/src/status_im/contacts/screen.cljs b/src/status_im/contacts/screen.cljs index a763c2ea50..97342f9f01 100644 --- a/src/status_im/contacts/screen.cljs +++ b/src/status_im/contacts/screen.cljs @@ -1,17 +1,19 @@ (ns status-im.contacts.screen (:require-macros [status-im.utils.views :refer [defview]]) (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [reagent.core :as r] [status-im.components.react :refer [view text image touchable-highlight + linear-gradient + scroll-view list-view - list-item]] + list-item] :as react] [status-im.components.action-button :refer [action-button action-button-item]] - [status-im.contacts.views.contact :refer [contact-view]] - [status-im.components.styles :refer [toolbar-background2]] + [status-im.contacts.views.contact :refer [contact-extended-view]] [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.styles :refer [color-blue hamburger-icon @@ -19,41 +21,79 @@ create-icon toolbar-background2]] [status-im.contacts.styles :as st] - [status-im.utils.listview :as lw] [status-im.i18n :refer [label]])) -(defn render-row [row _ _] - (list-item [contact-view row])) - (defn contact-list-toolbar [] - [toolbar {:nav-action {:image {:source {:uri :icon_hamburger} - :style hamburger-icon} - :handler open-drawer} + [toolbar {:nav-action {:image {:source {:uri :icon_hamburger} + :style hamburger-icon} + :handler open-drawer} :title (label :t/contacts) :background-color toolbar-background2 + :style {:elevation 0} :action {:image {:source {:uri :icon_search} :style icon-search} :handler (fn [])}}]) -(defview contact-list [] - [contacts [:get-contacts]] - [drawer-view - [view st/contacts-list-container - [contact-list-toolbar] - ;; todo what if there is no contacts, should we show some information - ;; about this? - (when contacts - [list-view {:dataSource (lw/to-datasource contacts) - :enableEmptySections true - :renderRow render-row - :style st/contacts-list}]) - [action-button {:buttonColor color-blue - :offsetY 16 - :offsetX 16} - [action-button-item - {:title (label :t/new-contact) - :buttonColor :#9b59b6 - :onPress #(dispatch [:navigate-to :new-contact])} - [icon {:name :android-create - :style create-icon}]] - ]]]) +(def contacts-limit 10) + +(defn contact-group [contacts contacts-count title group top?] + [view st/contact-group + [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 + ;; about this? + [view {:flexDirection :column} + (for [contact contacts] + ;; TODO not imlemented: contact more button handler + ^{:key contact} [contact-extended-view contact nil nil])] + (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 + :offsetY 16 + :offsetX 16} + [action-button-item + {:title (label :t/new-contact) + :buttonColor :#9b59b6 + :onPress #(dispatch [:navigate-to :new-contact])} + [icon {:name :android-create + :style create-icon}]]]]))) diff --git a/src/status_im/contacts/styles.cljs b/src/status_im/contacts/styles.cljs index 4b64148fc9..4da8803d96 100644 --- a/src/status_im/contacts/styles.cljs +++ b/src/status_im/contacts/styles.cljs @@ -1,72 +1,158 @@ (ns status-im.contacts.styles (:require [status-im.components.styles :refer [font + font-medium title-font text1-color + text2-color + text3-color + text5-color color-white toolbar-background2 - online-color]])) - - + online-color + color-gray2]])) (def contacts-list-container {:flex 1 :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 {:backgroundColor :white}) -(def contact-photo-container - {:borderRadius 50}) +(def contact-group + {:flexDirection :column}) -(def photo-image - {:borderRadius 50 - :width 40 - :height 40}) +(def contact-group-header + {:flexDirection :column + :backgroundColor toolbar-background2}) -(def online-container - {:position :absolute - :top 24 - :left 24 - :width 20 - :height 20 - :borderRadius 50 - :backgroundColor online-color - :borderWidth 2 - :borderColor color-white}) +(def contact-group-header-inner + {:flexDirection :row + :alignItems :center + :height 48 + :backgroundColor toolbar-background2}) -(def online-dot - {:position :absolute - :top 6 - :width 4 - :height 4 - :borderRadius 50 +(def contact-group-text + {:flex 1 + :marginLeft 16 + :fontSize 14 + :fontFamily font-medium + :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}) -(def online-dot-left - (assoc online-dot :left 3)) - -(def online-dot-right - (assoc online-dot :left 9)) +(def show-all-text + {:marginLeft 72 + :fontSize 14 + :fontFamily font-medium + :color text3-color + ;; ios only: + :letterSpacing 0.5}) (def contact-container - {:flexDirection :row - :height 56}) + {:flexDirection :row + :backgroundColor color-white}) -(def photo-container - {:marginTop 8 - :marginLeft 16 - :width 44 - :height 44}) +(def letter-container + {:paddingTop 11 + :paddingLeft 20 + :width 56}) -(def name-container - {:justifyContent :center}) +(def letter-text + {: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 - {:marginLeft 16 - :fontSize 16 + {:fontSize 16 :fontFamily font :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 (def contact-form-container @@ -82,4 +168,4 @@ (def form-container {:marginLeft 16 - :margin-top 50}) \ No newline at end of file + :margin-top 50}) diff --git a/src/status_im/contacts/subs.cljs b/src/status_im/contacts/subs.cljs index 76803f74dd..969518e1dd 100644 --- a/src/status_im/contacts/subs.cljs +++ b/src/status_im/contacts/subs.cljs @@ -1,28 +1,60 @@ (ns status-im.contacts.subs (: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 (fn [db _] (let [contacts (reaction (:contacts @db))] (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 (fn [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] (let [chat (reaction (get-in @db [:chats chat-id])) contacts (reaction (:contacts @db))] (reaction - (when @chat - (let [current-participants (->> @chat - :contacts - (map :identity) - set)] - (fn #(current-participants (:whisper-identity %)) - (vals @contacts))))))) + (when @chat + (let [current-participants (->> @chat + :contacts + (map :identity) + set)] + (fn #(current-participants (:whisper-identity %)) + (vals @contacts))))))) (defn contacts-by-current-chat [fn db] (let [current-chat-id (:current-chat-id @db)] @@ -33,6 +65,10 @@ (let [identity (:contact-identity @db)] (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 (fn [db _] (contacts-by-current-chat remove db))) @@ -47,8 +83,8 @@ chat (reaction (get-in @db [:chats chat-id])) contacts (contacts-by-chat filter db chat-id)] (reaction - (when @chat - (if (:group-chat @chat) - ;; TODO return group chat icon - nil - (:photo-path (first @contacts)))))))) + (when @chat + (if (:group-chat @chat) + ;; TODO return group chat icon + nil + (:photo-path (first @contacts)))))))) diff --git a/src/status_im/contacts/views/contact.cljs b/src/status_im/contacts/views/contact.cljs index f253789990..b63ea9c31e 100644 --- a/src/status_im/contacts/views/contact.cljs +++ b/src/status_im/contacts/views/contact.cljs @@ -1,16 +1,35 @@ (ns status-im.contacts.views.contact (: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]] + [status-im.contacts.styles :as st] [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] (if chat #(dispatch [:navigate-to :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]] [touchable-highlight {: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]]]]]) diff --git a/src/status_im/contacts/views/contact_inner.cljs b/src/status_im/contacts/views/contact_inner.cljs index 05502d793b..4c27d5e1a1 100644 --- a/src/status_im/contacts/views/contact_inner.cljs +++ b/src/status_im/contacts/views/contact_inner.cljs @@ -1,31 +1,26 @@ (ns status-im.contacts.views.contact-inner (:require [clojure.string :as s] [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.i18n :refer [label]])) -(defn contact-photo [{:keys [photo-path]}] +(defn contact-photo [contact] [view st/contact-photo-container - [image {:source (if (s/blank? photo-path) - res/user-no-photo - {:uri photo-path}) - :style st/photo-image}]]) + [contact-icon-contacts-tab contact]]) -(defn contact-online [{:keys [online]}] - (when online - [view st/online-container - [view st/online-dot-left] - [view st/online-dot-right]])) - -(defn contact-inner-view [{:keys [name photo-path online]}] - [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} - (if (pos? (count name)) - name - ;; todo is this correct behaviour? - (label :t/no-name))]]]) +(defn contact-inner-view + ([contact] + (contact-inner-view contact nil)) + ([{:keys [name] :as contact} info] + [view st/contact-inner-container + [contact-photo contact] + [view st/info-container + [text {:style st/name-text} + (if (pos? (count (:name contact))) + name + ;; todo is this correct behaviour? + (label :t/no-name))] + (when info + [text {:style st/info-text} + info])]])) diff --git a/src/status_im/contacts/views/contact_list.cljs b/src/status_im/contacts/views/contact_list.cljs new file mode 100644 index 0000000000..9a346e3829 --- /dev/null +++ b/src/status_im/contacts/views/contact_list.cljs @@ -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}])]]) diff --git a/src/status_im/contacts/views/new_contact.cljs b/src/status_im/contacts/views/new_contact.cljs index 469ed6b914..a45de304a6 100644 --- a/src/status_im/contacts/views/new_contact.cljs +++ b/src/status_im/contacts/views/new_contact.cljs @@ -7,8 +7,8 @@ image linear-gradient touchable-highlight]] + [status-im.utils.identicon :refer [identicon]] [status-im.components.toolbar :refer [toolbar]] - [status-im.components.drawer.view :refer [drawer-view open-drawer]] [status-im.components.styles :refer [color-purple color-white icon-search @@ -56,23 +56,21 @@ (defview new-contact [] [{:keys [name whisper-identity phone-number] :as new-contact} [:get :new-contact]] - [drawer-view - [view st/contact-form-container - [linear-gradient {:colors ["rgba(182, 116, 241, 1)" "rgba(107, 147, 231, 1)" "rgba(43, 171, 238, 1)"] - :start [0, 0] - :end [0.5, 1] - :locations [0, 0.8 ,1] - :style st/gradient-background}] + [view st/contact-form-container + [linear-gradient {:colors ["rgba(182, 116, 241, 1)" "rgba(107, 147, 231, 1)" "rgba(43, 171, 238, 1)"] + :start [0, 0] + :end [0.5, 1] + :locations [0, 0.8, 1] + :style st/gradient-background}] - [toolbar {:background-color :transparent - :nav-action {:image {:source {:uri :icon_back_white} - :style icon-back} - :handler #(dispatch [:navigate-back])} - :custom-content toolbar-title - :action {:image {:source {:uri :icon_add} - :style icon-search} - :handler #(dispatch [:add-new-contact new-contact])}}] - [view st/form-container - [contact-whisper-id-input whisper-identity] - [contact-name-input name] - ]]]) + [toolbar {:background-color :transparent + :nav-action {:image {:source {:uri :icon_back_white} + :style icon-back} + :handler #(dispatch [:navigate-back])} + :custom-content toolbar-title + :action {:image {:source {:uri :icon_add} + :style icon-search} + :handler #(dispatch [:add-new-contact (merge {:photo-path (identicon whisper-identity)} new-contact)])}}] + [view st/form-container + [contact-whisper-id-input whisper-identity] + [contact-name-input name]]]) diff --git a/src/status_im/db.cljs b/src/status_im/db.cljs index 2b3ad6f88f..25772a58fa 100644 --- a/src/status_im/db.cljs +++ b/src/status_im/db.cljs @@ -44,7 +44,8 @@ :message-input-buttons-scale 1 :messages-offset 0 :commands-input-is-switching? false - :response-resize? false}}) + :response-resize? false + :tabs-bar-value (anim/create-value 0)}}) (def protocol-initialized-path [:protocol-initialized]) (defn chat-input-text-path [chat-id] diff --git a/src/status_im/discovery/styles.cljs b/src/status_im/discovery/styles.cljs index dd998b352d..540dc04a03 100644 --- a/src/status_im/discovery/styles.cljs +++ b/src/status_im/discovery/styles.cljs @@ -2,6 +2,7 @@ (:require [status-im.components.styles :refer [font title-font color-white + color-gray2 chat-background online-color selected-message-color @@ -49,7 +50,7 @@ :elevation 0}) (def discovery-subtitle - {:color "#8f838c93" + {:color color-gray2 :fontFamily "sans-serif-medium" :fontSize 14}) diff --git a/src/status_im/group_settings/styles/member.cljs b/src/status_im/group_settings/styles/member.cljs deleted file mode 100644 index e5ef3f9f58..0000000000 --- a/src/status_im/group_settings/styles/member.cljs +++ /dev/null @@ -1,78 +0,0 @@ -(ns status-im.group-settings.styles.member - (:require [status-im.components.styles :refer [font - title-font - text1-color - text2-color - color-white - online-color]])) - -(def contact-photo-container - {:borderRadius 50}) - -(def photo-image - {:borderRadius 50 - :width 40 - :height 40}) - -(def online-container - {:position :absolute - :top 24 - :left 24 - :width 20 - :height 20 - :borderRadius 50 - :backgroundColor online-color - :borderWidth 2 - :borderColor color-white}) - -(def online-dot - {:position :absolute - :top 6 - :width 4 - :height 4 - :borderRadius 50 - :backgroundColor color-white}) - -(def online-dot-left - (assoc online-dot :left 3)) - -(def online-dot-right - (assoc online-dot :left 9)) - -(def contact-container - {:flexDirection :row - :height 56}) - -(def photo-container - {:marginTop 8 - :marginLeft 16 - :width 44 - :height 44}) - -(def info-container - {:flex 1 - :flexDirection :column - :marginLeft 16 - :justifyContent :center}) - -(def name-text - {:marginTop -2 - :fontSize 16 - :fontFamily font - :color text1-color}) - -(def role-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}) diff --git a/src/status_im/group_settings/views/member.cljs b/src/status_im/group_settings/views/member.cljs index c5292daa36..e73d46cc2a 100644 --- a/src/status_im/group_settings/views/member.cljs +++ b/src/status_im/group_settings/views/member.cljs @@ -1,44 +1,9 @@ (ns status-im.group-settings.views.member - (:require [clojure.string :as s] - [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [status-im.components.react :refer [view - image - text - icon - touchable-highlight]] - [status-im.resources :as res] - [status-im.group-settings.styles.member :as st] + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [status-im.contacts.views.contact :refer [contact-extended-view]] [status-im.i18n :refer [label]])) -(defn contact-photo [{:keys [photo-path]}] - [view st/contact-photo-container - [image {:source (if (s/blank? photo-path) - res/user-no-photo - {:uri photo-path}) - :style st/photo-image}]]) - -(defn contact-online [{:keys [online]}] - (when online - [view st/online-container - [view st/online-dot-left] - [view st/online-dot-right]])) - -(defn member-view [{:keys [whisper-identity name photo-path online role]}] - [view st/contact-container - [view st/photo-container - [contact-photo {:photo-path photo-path}] - [contact-online {:online online}]] - [view st/info-container - [text {:style st/name-text} - (if (pos? (count name)) - name - ;; todo is this correct behaviour? - (label :t/no-name))] - ;; TODO implement :role property for group chat contact - (when role - [text {:style st/role-text} - role])] - [touchable-highlight - {:on-press #(dispatch [:set :selected-participants #{whisper-identity}])} - [view st/more-btn - [icon :more-vertical st/more-btn-icon]]]]) +(defn member-view [{:keys [whisper-identity role] :as contact}] + ;; TODO implement :role property for group chat contact + [contact-extended-view contact role + #(dispatch [:set :selected-participants #{whisper-identity}])]) diff --git a/src/status_im/models/contacts.cljs b/src/status_im/models/contacts.cljs index ec286b17a6..6c3ea62a7a 100644 --- a/src/status_im/models/contacts.cljs +++ b/src/status_im/models/contacts.cljs @@ -1,5 +1,6 @@ (ns status-im.models.contacts (:require [status-im.persistence.realm :as r] + [status-im.utils.identicon :refer [identicon]] [status-im.persistence.realm-queries :refer [include-query exclude-query]])) @@ -8,9 +9,9 @@ (r/sorted :name :asc) r/collection->map)) -(defn create-contact [{:keys [name photo-path] :as contact}] +(defn create-contact [{:keys [name photo-path whisper-identity] :as contact}] (->> {:name (or name "") - :photo-path (or photo-path "")} + :photo-path (or photo-path (identicon whisper-identity))} (merge contact) (r/create :contacts))) diff --git a/src/status_im/navigation/handlers.cljs b/src/status_im/navigation/handlers.cljs index e687ed45c6..cf73ae6434 100644 --- a/src/status_im/navigation/handlers.cljs +++ b/src/status_im/navigation/handlers.cljs @@ -66,6 +66,12 @@ (fn [db _] (push-view db :contact-list))) +(register-handler :show-group-contacts + (fn [db [_ group]] + (-> db + (assoc :contacts-group group) + (push-view :group-contacts)))) + (defn show-profile [db [_ identity]] (-> db diff --git a/src/status_im/translations/en.cljs b/src/status_im/translations/en.cljs index 8ec3ba3fb7..792dc4ebd5 100644 --- a/src/status_im/translations/en.cljs +++ b/src/status_im/translations/en.cljs @@ -72,6 +72,10 @@ :contacts "Contacts" :no-name "Noname" :new-contact "New Contact" + :show-all "SHOW ALL" + :contacs-group-dapps "Dapps" + :contacs-group-people "People" + :no-contacts "No contacts yet" ;group-settings :remove "Remove" @@ -127,6 +131,6 @@ :login "Login" ;users - :add-account "Add account" + :add-account "Add account" - }) \ No newline at end of file + }) diff --git a/src/status_im/utils/crypt.cljs b/src/status_im/utils/crypt.cljs index 0b3f2dd7d0..9179e7dfc0 100644 --- a/src/status_im/utils/crypt.cljs +++ b/src/status_im/utils/crypt.cljs @@ -20,7 +20,7 @@ (byteArrayToHex (.digest sha-256))) (defn gen-random-bytes [length cb] - #_(.randomBytes random-bytes length (fn [& [err buf]] + (.randomBytes random-bytes length (fn [& [err buf]] (if err (cb {:error err}) (cb {:buffer buf}))))) diff --git a/src/status_im/utils/identicon.cljs b/src/status_im/utils/identicon.cljs new file mode 100644 index 0000000000..2d8dac64e1 --- /dev/null +++ b/src/status_im/utils/identicon.cljs @@ -0,0 +1,13 @@ +(ns status-im.utils.identicon + (:require [clojure.string :as s] + [status-im.utils.utils :as u])) + +(def default-size 40) + +(def identicon-js (u/require "identicon.js")) + +(defn identicon + ([hash] (identicon hash default-size)) + ([hash options] + (str "data:image/png;base64," (.toString (new identicon-js hash options))))) +