From 1b480a1a58ea752980b15262612b5a1d1bb79294 Mon Sep 17 00:00:00 2001 From: alwxndr Date: Mon, 1 Aug 2016 13:29:10 +0300 Subject: [PATCH] * new my-profile screen * ability to edit profile data * ability to update status (we save it internally for now) * ability to change profile picture (from camera or gallery) * enhanced way of storing profile-related data in realm and internal state db Former-commit-id: 91d8ecc4bdf8407bc95b2fe703113bebccbbd885 --- .re-natal | 6 +- android/app/build.gradle | 5 +- .../java/com/statusim/MainApplication.java | 8 +- android/app/src/main/res/values/strings.xml | 2 + android/build.gradle | 14 +- android/settings.gradle | 6 + ios/StatusIm.xcodeproj/project.pbxproj | 12 +- package.json | 3 + project.clj | 2 +- src/status_im/accounts/handlers.cljs | 56 ++--- src/status_im/accounts/login/screen.cljs | 32 +-- src/status_im/accounts/views/account.cljs | 2 +- src/status_im/android/core.cljs | 14 +- src/status_im/chat/handlers.cljs | 3 +- src/status_im/chats_list/screen.cljs | 10 +- src/status_im/components/camera.cljs | 15 +- .../components/chat_icon/screen.cljs | 50 +++-- .../components/chat_icon/styles.cljs | 21 +- src/status_im/components/drawer/styles.cljs | 93 ++++---- src/status_im/components/drawer/view.cljs | 91 ++++---- .../components/icons/custom_icons.cljs | 8 + src/status_im/components/icons/ionicons.cljs | 4 - src/status_im/components/main_tabs.cljs | 18 +- src/status_im/components/qr_code.cljs | 4 +- src/status_im/components/react.cljs | 29 ++- src/status_im/components/toolbar.cljs | 2 +- src/status_im/contacts/screen.cljs | 6 +- .../contacts/views/contact_list.cljs | 3 +- src/status_im/db.cljs | 19 +- src/status_im/handlers.cljs | 7 +- src/status_im/handlers/server.cljs | 3 +- src/status_im/ios/core.cljs | 14 +- src/status_im/models/accounts.cljs | 17 +- src/status_im/models/protocol.cljs | 4 +- src/status_im/navigation/handlers.cljs | 6 + src/status_im/persistence/realm/schemas.cljs | 55 ++--- src/status_im/profile/handlers.cljs | 33 +++ .../profile/photo_capture/screen.cljs | 53 +++++ .../profile/photo_capture/styles.cljs | 5 + src/status_im/profile/screen.cljs | 197 +++++++++++++---- src/status_im/profile/styles.cljs | 198 ++++++++++-------- src/status_im/protocol/handlers.cljs | 5 +- .../qr_scanner/views/import-button.cljs | 1 - .../qr_scanner/views/scan-button.cljs | 1 - src/status_im/subs.cljs | 5 + src/status_im/translations/en.cljs | 9 + src/status_im/utils/fs.cljs | 7 +- src/status_im/utils/image_processing.cljs | 25 +++ 48 files changed, 786 insertions(+), 397 deletions(-) create mode 100644 src/status_im/components/icons/custom_icons.cljs delete mode 100644 src/status_im/components/icons/ionicons.cljs create mode 100644 src/status_im/profile/handlers.cljs create mode 100644 src/status_im/profile/photo_capture/screen.cljs create mode 100644 src/status_im/profile/photo_capture/styles.cljs create mode 100644 src/status_im/utils/image_processing.cljs diff --git a/.re-natal b/.re-natal index 29e05a1828..d4461b52f3 100644 --- a/.re-natal +++ b/.re-natal @@ -11,6 +11,7 @@ "realm/react-native", "react-native-action-button", "react-native-vector-icons/Ionicons", + "react-native-vector-icons/Octicons", "react-native-circle-checkbox", "react-native-randombytes", "dismissKeyboard", @@ -21,7 +22,10 @@ "react-native-qrcode", "react-native-orientation", "identicon.js", - "react-native-fs" + "react-native-fs", + "react-native-dialogs", + "react-native-image-resizer", + "react-native-image-crop-picker" ], "imageDirs": [ "images" diff --git a/android/app/build.gradle b/android/app/build.gradle index a1d00ae137..c5ec1d7c09 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -82,7 +82,7 @@ android { defaultConfig { applicationId "com.statusim" - minSdkVersion 16 + minSdkVersion 18 targetSdkVersion 22 versionCode 1 versionName "1.0" @@ -120,6 +120,8 @@ android { } dependencies { + compile project(':react-native-image-resizer') + compile project(':react-native-dialogs') compile project(':react-native-randombytes') compile project(':react-native-android-sms-listener') compile project(':realm') @@ -134,6 +136,7 @@ dependencies { compile project(':react-native-status') compile project(':react-native-orientation') compile project(':react-native-fs') + compile project(':react-native-image-crop-picker') //compile(name:'statusgo-android-16', ext:'aar') compile(group: 'status-im', name: 'status-go', version: '0.1.0-201607011545-da53ec', ext: 'aar') diff --git a/android/app/src/main/java/com/statusim/MainApplication.java b/android/app/src/main/java/com/statusim/MainApplication.java index ae3045e59a..69e184d6a1 100644 --- a/android/app/src/main/java/com/statusim/MainApplication.java +++ b/android/app/src/main/java/com/statusim/MainApplication.java @@ -19,6 +19,9 @@ import com.centaurwarchief.smslistener.SmsListenerPackage; import com.github.yamill.orientation.OrientationPackage; import com.rnfs.RNFSPackage; import com.statusim.geth.module.GethPackage; +import com.aakashns.reactnativedialogs.ReactNativeDialogsPackage; +import fr.bamlab.rnimageresizer.ImageResizerPackage; +import com.reactnative.picker.PickerPackage; import java.util.Arrays; import java.util.List; @@ -46,7 +49,10 @@ public class MainApplication extends Application implements ReactApplication { new SmsListenerPackage(), new OrientationPackage(), new RNFSPackage(), - new GethPackage() + new GethPackage(), + new ReactNativeDialogsPackage(), + new ImageResizerPackage(), + new PickerPackage() ); } }; diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 995c84454f..5df11c0412 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -3,6 +3,8 @@ + + Status Your phone appears to be ROOTED, by pressing CONTINUE you understand and accept the risks in using this software. Continue diff --git a/android/build.gradle b/android/build.gradle index fec9574948..5ab7181ea6 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -16,16 +16,10 @@ allprojects { repositories { mavenLocal() jcenter() - maven { - // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm - url "$rootDir/../node_modules/react-native/android" - } + maven { url "$rootDir/../node_modules/react-native/android" } // for geth - flatDir { - dirs 'libs' - } - maven { - url "http://185.90.37.89:8081/artifactory/libs-release-local" - } + flatDir { dirs 'libs' } + maven { url "http://185.90.37.89:8081/artifactory/libs-release-local" } + maven { url "https://jitpack.io" } } } diff --git a/android/settings.gradle b/android/settings.gradle index df86d401f0..f45aecd942 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,6 +1,10 @@ rootProject.name = 'StatusIm' include ':app' +include ':react-native-image-resizer' +project(':react-native-image-resizer').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-image-resizer/android') +include ':react-native-dialogs' +project(':react-native-dialogs').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-dialogs/android') include ':react-native-randombytes' project(':react-native-randombytes').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-randombytes/android') include ':react-native-android-sms-listener' @@ -23,3 +27,5 @@ include ':react-native-orientation', ':app' project(':react-native-orientation').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-orientation/android') include ':react-native-fs' project(':react-native-fs').projectDir = new File(settingsDir, '../node_modules/react-native-fs/android') +include ':react-native-image-crop-picker' +project(':react-native-image-crop-picker').projectDir = new File(settingsDir, '../node_modules/react-native-image-crop-picker/android') diff --git a/ios/StatusIm.xcodeproj/project.pbxproj b/ios/StatusIm.xcodeproj/project.pbxproj index 9e042d902c..d1ce2be680 100644 --- a/ios/StatusIm.xcodeproj/project.pbxproj +++ b/ios/StatusIm.xcodeproj/project.pbxproj @@ -5,7 +5,6 @@ }; objectVersion = 46; objects = { - /* Begin PBXBuildFile section */ 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; }; 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; }; @@ -49,6 +48,7 @@ D28AEFB4C39548EB80416889 /* Entypo.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 52E205D210BC48B7A553BB62 /* Entypo.ttf */; }; EF2B5857B4A34E0C9707FB3F /* Octicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B3B19223008342D096AA356E /* Octicons.ttf */; }; FD4F213C3873473CB703B1D2 /* libRNFS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 674B3D9595A047AB8D518F4E /* libRNFS.a */; }; + E0AD9E8F495A4907B65104BF /* libRCTImageResizer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2BEE3436791D42248F853999 /* libRCTImageResizer.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -284,6 +284,8 @@ CEB0E2659D1A4F5FA842057A /* EvilIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = EvilIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf"; sourceTree = ""; }; DF1CD4C3D1254774ACCAE4E8 /* libBVLinearGradient.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libBVLinearGradient.a; sourceTree = ""; }; F090E261B9854867A728CE4F /* RealmReact.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RealmReact.xcodeproj; path = "../node_modules/realm/react-native/ios/RealmReact.xcodeproj"; sourceTree = ""; }; + 5E5A7625B76441D984EA8C0D /* RCTImageResizer.xcodeproj */ = {isa = PBXFileReference; name = "RCTImageResizer.xcodeproj"; path = "../node_modules/react-native-image-resizer/ios/RCTImageResizer.xcodeproj"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; }; + 2BEE3436791D42248F853999 /* libRCTImageResizer.a */ = {isa = PBXFileReference; name = "libRCTImageResizer.a"; path = "libRCTImageResizer.a"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -323,6 +325,7 @@ BA68A2377A20496EA737000D /* libz.tbd in Frameworks */, 3E15DFEC1F6F4D7CAE088F49 /* libTcpSockets.a in Frameworks */, AD5063BC2B2A4C52ACE0A0B4 /* libUdpSockets.a in Frameworks */, + E0AD9E8F495A4907B65104BF /* libRCTImageResizer.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -581,6 +584,7 @@ 38E1A2C8D0734EE99E2B16CE /* TcpSockets.xcodeproj */, 2F0276A9E90843E996A0E762 /* UdpSockets.xcodeproj */, 439B6B4B407A4E2AACAFE5BE /* RCTJail.xcodeproj */, + 5E5A7625B76441D984EA8C0D /* RCTImageResizer.xcodeproj */, ); name = Libraries; sourceTree = ""; @@ -1074,6 +1078,7 @@ "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/StatusIm.app/StatusIm"; @@ -1107,6 +1112,7 @@ "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/StatusIm.app/StatusIm"; @@ -1140,6 +1146,7 @@ "$(SRCROOT)/../node_modules/react-native-udp/ios/**", "$(SRCROOT)/../node_modules/react-native-status/ios/RCTJail/RCTJail/**", "$(SRCROOT)/../node_modules/react-native-status/ios/RCTJail/RCTJail/**", + "$(SRCROOT)/../node_modules/react-native-image-resizer/ios/RCTImageResizer", ); INFOPLIST_FILE = StatusIm/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; @@ -1180,6 +1187,7 @@ "$(SRCROOT)/../node_modules/react-native-udp/ios/**", "$(SRCROOT)/../node_modules/react-native-status/ios/RCTJail/RCTJail/**", "$(SRCROOT)/../node_modules/react-native-status/ios/RCTJail/RCTJail/**", + "$(SRCROOT)/../node_modules/react-native-image-resizer/ios/RCTImageResizer", ); INFOPLIST_FILE = StatusIm/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; @@ -1244,6 +1252,7 @@ "$(SRCROOT)/../node_modules/react-native-udp/ios/**", "$(SRCROOT)/../node_modules/react-native-status/ios/RCTJail/RCTJail/**", "$(SRCROOT)/../node_modules/react-native-status/ios/RCTJail/RCTJail/**", + "$(SRCROOT)/../node_modules/react-native-image-resizer/ios/RCTImageResizer", ); IPHONEOS_DEPLOYMENT_TARGET = 7.0; MTL_ENABLE_DEBUG_INFO = YES; @@ -1297,6 +1306,7 @@ "$(SRCROOT)/../node_modules/react-native-udp/ios/**", "$(SRCROOT)/../node_modules/react-native-status/ios/RCTJail/RCTJail/**", "$(SRCROOT)/../node_modules/react-native-status/ios/RCTJail/RCTJail/**", + "$(SRCROOT)/../node_modules/react-native-image-resizer/ios/RCTImageResizer", ); IPHONEOS_DEPLOYMENT_TARGET = 7.0; MTL_ENABLE_DEBUG_INFO = NO; diff --git a/package.json b/package.json index 75ed14a03f..5cb7b77261 100644 --- a/package.json +++ b/package.json @@ -34,9 +34,12 @@ "react-native-circle-checkbox": "github:paramoshkinandrew/ReactNativeCircleCheckbox", "react-native-contacts": "^0.2.4", "react-native-crypto": "^2.0.1", + "react-native-dialogs": "0.0.16", "react-native-fs": "^1.5.1", "react-native-http": "github:tradle/react-native-http#834492d", "react-native-i18n": "0.0.8", + "react-native-image-crop-picker": "^0.5.4", + "react-native-image-resizer": "github:danieldunderfelt/react-native-image-resizer", "react-native-invertible-scroll-view": "^1.0.0", "react-native-level-fs": "^2.0.1", "react-native-linear-gradient": "1.5.7", diff --git a/project.clj b/project.clj index 7d8c65b347..6c28586918 100644 --- a/project.clj +++ b/project.clj @@ -9,7 +9,7 @@ [re-frame "0.7.0"] [prismatic/schema "1.0.4"] ^{:voom {:repo "git@github.com:status-im/status-lib.git" - :branch "master"}} + :branch "discover-rework"}} [status-im/protocol "0.1.1-20160706_085008-ge61756a"] [natal-shell "0.3.0"] [com.andrewmcveigh/cljs-time "0.4.0"]] diff --git a/src/status_im/accounts/handlers.cljs b/src/status_im/accounts/handlers.cljs index e9117410fd..99a4e0308f 100644 --- a/src/status_im/accounts/handlers.cljs +++ b/src/status_im/accounts/handlers.cljs @@ -17,56 +17,64 @@ (defn save-account [_ [_ account]] - (accounts/save-accounts [account])) + (accounts/save-accounts [account] false)) -(register-handler :add-account +(register-handler + :add-account (-> (fn [db [_ {:keys [address] :as account}]] - (update db :accounts assoc address account)) + (update db :accounts assoc address account)) ((after save-account)))) (defn save-password [password] (storage/put kv/kv-store :password password)) (defn account-created [db result password] - (let [data (json->clj result) + (let [data (json->clj result) public-key (:pubkey data) - address (:address data) - account {:public-key public-key - :address address - :name address - :photo-path (identicon address)} - ] + address (:address data) + account {:public-key public-key + :address address + :name address + :photo-path (identicon address)}] (log/debug "account-created: " account) (when (not (str/blank? public-key)) (do - ;(save-password password) (dispatch-sync [:add-account account]) (dispatch [:login-account address password]))))) -(register-handler :create-account - (-> (fn [db [_ password]] - (geth/create-account password (fn [result] (account-created db result password))) - db))) +(register-handler + :create-account + (fn [db [_ password]] + (geth/create-account password (fn [result] (account-created db result password))) + db)) -(defn initialize-account [db account] +(register-handler + :account-update + (fn [db [_ data]] + (let [current-account-id (get db :current-account-id) + account (-> (get-in db [:accounts current-account-id]) + (merge data))] + (accounts/save-accounts [account] true) + (assoc-in db [:accounts current-account-id] account)))) + +(defn initialize-account [db address] (let [is-login-screen? (= (:view-id db) :login)] (dispatch [:set :login {}]) - (dispatch [:set :is-logged-in true]) - (dispatch [:set :user-identity account]) - (dispatch [:initialize-account account]) + (dispatch [:set :current-account-id address]) + (dispatch [:initialize-account address]) (when is-login-screen? (dispatch [:navigate-to-clean default-view])))) (defn logged-in [db address] - (let [account (get-in db [:accounts address]) - is-login-screen? (= (:view-id db) :login) + (let [is-login-screen? (= (:view-id db) :login) new-account? (not is-login-screen?)] - (log/debug "Logged in: " address account) + (log/debug "Logged in: " address) (realm/change-account-realm address new-account? #(if (nil? %) - (initialize-account db account) + (initialize-account db address) (log/debug "Error changing acount realm: " %))))) -(register-handler :login-account +(register-handler + :login-account (-> (fn [db [_ address password]] (geth/login address password (fn [result] (let [data (json->clj result) diff --git a/src/status_im/accounts/login/screen.cljs b/src/status_im/accounts/login/screen.cljs index 765794b907..7ff96acf30 100644 --- a/src/status_im/accounts/login/screen.cljs +++ b/src/status_im/accounts/login/screen.cljs @@ -59,7 +59,8 @@ (dispatch [:set-in [:login :error] ""]))}]]) (defview login [{platform-specific :platform-specific}] - [{:keys [address password error]} [:get :login]] + [{:keys [address password error]} [:get :login] + keyboard-height [:get :keyboard-height]] [view st/screen-container [linear-gradient {:colors ["rgba(182, 116, 241, 1)" "rgba(107, 147, 231, 1)" "rgba(43, 171, 238, 1)"] :start [0, 0] @@ -79,20 +80,19 @@ [view st/form-container-inner [address-input (or address "")] [password-input error]]] - (let [keyboard-height @(subscribe [:get :keyboard-height])] - [view st/bottom-actions-container - (when (= keyboard-height 0) - [view st/recover-button-container - [touchable-highlight - {:on-press #()} - [view st/recover-button - [text {:style st/recover-button-text - :platform-specific platform-specific} - (label :t/recover-access)]]]]) - [view st/connect-button-container + [view st/bottom-actions-container + (when (= keyboard-height 0) + [view st/recover-button-container [touchable-highlight - {:on-press #(dispatch [:login-account address password])} - [view st/connect-button - [text {:style st/connect-button-text + {:on-press #()} + [view st/recover-button + [text {:style st/recover-button-text :platform-specific platform-specific} - (label :t/connect)]]]]])]) + (label :t/recover-access)]]]]) + [view st/connect-button-container + [touchable-highlight + {:on-press #(dispatch [:login-account address password])} + [view st/connect-button + [text {:style st/connect-button-text + :platform-specific platform-specific} + (label :t/connect)]]]]]]) diff --git a/src/status_im/accounts/views/account.cljs b/src/status_im/accounts/views/account.cljs index 63afee013e..70c6990832 100644 --- a/src/status_im/accounts/views/account.cljs +++ b/src/status_im/accounts/views/account.cljs @@ -16,7 +16,7 @@ (dispatch [:set-in [:login :address] address])) (defview account-view [{:keys [address photo-path name] :as account}] - [current-account [:get :user-identity]] + [current-account [:get-current-account]] [touchable-highlight {:onPress #(on-press address)} [view st/account-container diff --git a/src/status_im/android/core.cljs b/src/status_im/android/core.cljs index e0b65f4aa6..76ecddabc4 100644 --- a/src/status_im/android/core.cljs +++ b/src/status_im/android/core.cljs @@ -8,7 +8,8 @@ [status-im.android.styles :refer [styles]] [status-im.components.react :refer [app-registry keyboard - orientation]] + orientation + show-dialog]] [status-im.components.main-tabs :refer [main-tabs]] [status-im.contacts.views.contact-list :refer [contact-list]] [status-im.contacts.views.new-contact :refer [new-contact]] @@ -24,6 +25,7 @@ [status-im.participants.views.remove :refer [remove-participants]] [status-im.group-settings.screen :refer [group-settings]] [status-im.profile.screen :refer [profile my-profile]] + [status-im.profile.photo-capture.screen :refer [profile-photo-capture]] [status-im.utils.utils :refer [toast]] [status-im.utils.encryption] status-im.persistence.realm.core @@ -48,9 +50,9 @@ (let [signed-up (subscribe [:get :signed-up]) _ (log/debug "signed up: " @signed-up) view-id (subscribe [:get :view-id]) - account (subscribe [:get :user-identity]) + account-id (subscribe [:get :current-account-id]) keyboard-height (subscribe [:get :keyboard-height])] - (log/debug "Current account: " @account) + (log/debug "Current account: " @account-id) (r/create-class {:component-will-mount (fn [] @@ -72,7 +74,7 @@ (dispatch [:set :keyboard-height 0])))) :render (fn [] - (let [startup-view (if @account + (let [startup-view (if @account-id (if @signed-up @view-id :chat) @@ -94,10 +96,12 @@ :qr-scanner qr-scanner :chat chat :profile profile + :profile-photo-capture profile-photo-capture :accounts accounts :login login :my-profile my-profile)] - [component {:platform-specific {:styles styles}}])))}))) + [component {:platform-specific {:styles styles + :list-selection-fn show-dialog}}])))}))) (defn init [& [env]] (dispatch-sync [:reset-app]) diff --git a/src/status_im/chat/handlers.cljs b/src/status_im/chat/handlers.cljs index 5966c78926..db2d0a76d1 100644 --- a/src/status_im/chat/handlers.cljs +++ b/src/status_im/chat/handlers.cljs @@ -310,8 +310,9 @@ (assoc db :password-saved true))) (register-handler :sign-up + (after (fn [_ [_ phone-number]] + (dispatch [:account-update {:phone phone-number}]))) (fn [db [_ phone-number]] - ;; todo save phone number to db (let [formatted (format-phone-number phone-number)] (-> db (assoc :user-phone-number formatted) diff --git a/src/status_im/chats_list/screen.cljs b/src/status_im/chats_list/screen.cljs index c5347f57b2..b181b3b2a9 100644 --- a/src/status_im/chats_list/screen.cljs +++ b/src/status_im/chats_list/screen.cljs @@ -19,7 +19,7 @@ toolbar-background2]] [status-im.components.status-bar :refer [status-bar]] [status-im.components.toolbar :refer [toolbar]] - [status-im.components.icons.ionicons :refer [icon]] + [status-im.components.icons.custom-icons :refer [ion-icon]] [status-im.i18n :refer [label]] [status-im.chats-list.styles :as st] [status-im.components.styles :as cst] @@ -80,12 +80,12 @@ {:title (label :t/new-chat) :buttonColor :#9b59b6 :onPress #(dispatch [:show-group-contacts :people])} - [icon {:name :md-create - :style st/create-icon}]] + [ion-icon {:name :md-create + :style st/create-icon}]] [action-button-item {:title (label :t/new-group-chat) :buttonColor :#1abc9c :onPress #(dispatch [:show-group-new])} - [icon {:name :md-person - :style st/person-stalker-icon}]]]] + [ion-icon {:name :md-person + :style st/person-stalker-icon}]]]] [bottom-gradient]]))) diff --git a/src/status_im/components/camera.cljs b/src/status_im/components/camera.cljs index f53b70ffae..d49fb7caf0 100644 --- a/src/status_im/components/camera.cljs +++ b/src/status_im/components/camera.cljs @@ -1,7 +1,16 @@ (ns status-im.components.camera - (:require [reagent.core :as r])) + (:require [reagent.core :as r] + [clojure.walk :refer [keywordize-keys]])) -(def class (.-default (js/require "react-native-camera"))) +(def camera-class (js/require "react-native-camera")) + +(defn constants [t] + (-> (aget camera-class "default" "constants" t) + (js->clj) + (keywordize-keys))) + +(def aspects (constants "Aspect")) +(def capture-targets (constants "CaptureTarget")) (defn camera [props] - (r/create-element class (clj->js (merge {:inverted true} props)))) \ No newline at end of file + (r/create-element (.-default camera-class) (clj->js (merge {:inverted true} props)))) diff --git a/src/status_im/components/chat_icon/screen.cljs b/src/status_im/components/chat_icon/screen.cljs index 3b6437c193..653131a74f 100644 --- a/src/status_im/components/chat_icon/screen.cljs +++ b/src/status_im/components/chat_icon/screen.cljs @@ -5,6 +5,7 @@ text image icon]] + [status-im.components.icons.custom-icons :refer [oct-icon]] [status-im.components.chat-icon.styles :as st] [status-im.components.styles :refer [default-chat-color]] [clojure.string :as s])) @@ -18,20 +19,25 @@ [image {:source {:uri photo-path} :style (:chat-icon styles)}]) -(defn contact-online [online styles] - (when online +(defn contact-badge [type styles] + (when (= type :edit) [view (:online-view styles) - [view (:online-dot-left styles)] - [view (:online-dot-right styles)]])) + (case type + :online [view + [view (:online-dot-left styles)] + [view (:online-dot-right styles)]] + :edit [view + [oct-icon {:name :pencil + :style st/photo-pencil}]])])) (defview chat-icon-view [chat-id group-chat name online styles] - [photo-path [:chat-photo chat-id]] - [view (:container styles) + [photo-path [:chat-photo chat-id]] + [view (:container styles) (if-not (s/blank? photo-path) [chat-icon photo-path styles] [default-chat-icon name styles]) (when-not group-chat - [contact-online online styles])]) + [contact-badge (if online :online :blank) styles])]) (defn chat-icon-view-chat-list [chat-id group-chat name color online] [chat-icon-view chat-id group-chat name online @@ -65,13 +71,13 @@ (defn contact-icon-view [contact styles] (let [photo-path (:photo-path contact) - ;; TODO stub data - online true] + ;; TODO: stub + type :online] [view (:container styles) (if-not (s/blank? photo-path) [chat-icon photo-path styles] [default-chat-icon (:name contact) styles]) - [contact-online online styles]])) + [contact-badge type styles]])) (defn contact-icon-contacts-tab [contact] [contact-icon-view contact @@ -83,7 +89,7 @@ :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 badge-type] (let [styles {:container st/container-profile :online-view st/online-view-profile :online-dot-left st/online-dot-left-profile @@ -95,19 +101,17 @@ (if (and photo-path (not (empty? photo-path))) [chat-icon photo-path styles] [default-chat-icon name styles]) - [contact-online online styles]])) + [contact-badge badge-type styles]])) (defview profile-icon [] [contact [:contact]] - (let [;; TODO stub data - online true - color default-chat-color] - [profile-icon-view (:photo-path contact) (:name contact) color online])) + (let [;; TODO: stub + type :online + color default-chat-color] + [profile-icon-view (:photo-path @contact) (:name @contact) color type])) -(defview my-profile-icon [] - [name [:get :username] - photo-path [:get :photo-path]] - (let [;; TODO stub data - online true - color default-chat-color] - [profile-icon-view photo-path name color online])) +(defn my-profile-icon [{{:keys [photo-path name]} :account + edit? :edit?}] + (let [type (if edit? :edit :blank) + color default-chat-color] + [profile-icon-view photo-path name color type])) diff --git a/src/status_im/components/chat_icon/styles.cljs b/src/status_im/components/chat_icon/styles.cljs index 977af624d6..af42ecd269 100644 --- a/src/status_im/components/chat_icon/styles.cljs +++ b/src/status_im/components/chat_icon/styles.cljs @@ -59,8 +59,9 @@ (def chat-icon-profile (merge chat-icon - {:width 64 - :height 64})) + {:width 64 + :height 64 + :border-radius 32})) (def online-view {:position :absolute @@ -75,13 +76,15 @@ (def online-view-menu-item (merge online-view - {:width 15 - :height 15})) + {:width 14 + :height 14 + :border-radius 7})) (def online-view-profile (merge online-view - {:width 24 - :height 24})) + {:width 24 + :height 24 + :border-radius 12})) (def online-dot {:position :absolute @@ -93,6 +96,12 @@ (def online-dot-left (merge online-dot {:left 3})) (def online-dot-right (merge online-dot {:left 9})) +(def photo-pencil + {:margin-left 6 + :margin-top 3 + :font-size 12 + :color :white}) + (def online-dot-menu-item (merge online-dot {:top 4 diff --git a/src/status_im/components/drawer/styles.cljs b/src/status_im/components/drawer/styles.cljs index 15a974152b..a6b780ad22 100644 --- a/src/status_im/components/drawer/styles.cljs +++ b/src/status_im/components/drawer/styles.cljs @@ -1,21 +1,43 @@ (ns status-im.components.drawer.styles (:require [status-im.components.styles :refer [font - color-light-blue-transparent - color-white - color-black - color-blue - color-blue-transparent - selected-message-color - online-color - separator-color - text1-color - text2-color - text3-color]])) + color-light-blue-transparent + color-white + color-black + color-blue + color-blue-transparent + selected-message-color + online-color + separator-color + text1-color + text2-color + text3-color]])) + +(def drawer-menu + {:flex 1 + :background-color color-white + :flex-direction :column}) + +(def user-photo-container + {:margin-top 40 + :align-items :center + :justify-content :center}) (def user-photo - {:borderRadius 32 - :width 64 - :height 64}) + {:border-radius 32 + :width 64 + :height 64}) + +(def name-container + {:margin-top 20 + :margin-left 16 + :margin-right 16 + :align-items :center}) + +(def menu-items-container + {:flex 1 + :margin-top 50 + :align-items :stretch + :flex-direction :column}) (def menu-item-touchable {:height 48 @@ -23,42 +45,19 @@ :paddingTop 14}) (def menu-item-text - {:fontSize 14 - :fontFamily font - :lineHeight 21 - :color text1-color}) - -(def drawer-menu - {:flex 1 - :backgroundColor color-white - :flexDirection :column}) - -(def user-photo-container - {:marginTop 40 - :alignItems :center - :justifyContent :center}) - -(def name-container - {:marginTop 20 - :alignItems :center}) + {:font-size 14 + :line-height 21 + :color text1-color}) (def name-text - {:marginTop -2.5 - :color text1-color - :fontSize 16}) - -(def menu-items-container - {:flex 1 - :marginTop 80 - :alignItems :stretch - :flexDirection :column}) + {:color text1-color + :font-size 16}) (def switch-users-container - {:paddingVertical 36 - :alignItems :center}) + {:padding-vertical 36 + :align-items :center}) (def switch-users-text - {:fontSize 14 - :fontFamily font - :lineHeight 21 - :color text3-color}) + {:font-size 14 + :line-height 21 + :color text3-color}) diff --git a/src/status_im/components/drawer/view.cljs b/src/status_im/components/drawer/view.cljs index dc2cf8d46d..bf8b5e63bd 100644 --- a/src/status_im/components/drawer/view.cljs +++ b/src/status_im/components/drawer/view.cljs @@ -1,4 +1,5 @@ (ns status-im.components.drawer.view + (:require-macros [status-im.utils.views :refer [defview]]) (:require [clojure.string :as s] [re-frame.core :refer [subscribe dispatch dispatch-sync]] [reagent.core :as r] @@ -27,53 +28,67 @@ {:uri photo-path}) :style st/user-photo}]) -(defn menu-item [{:keys [name handler]}] +(defn menu-item [{:keys [name handler platform-specific]}] [touchable-opacity {:style st/menu-item-touchable :onPress (fn [] (close-drawer) (handler))} - [text {:style st/menu-item-text} + [text {:style st/menu-item-text + :platform-specific platform-specific + :font :default} name]]) -(defn drawer-menu [] - (let [username (subscribe [:get :username])] - (fn [] - [view st/drawer-menu - [view st/user-photo-container - [user-photo {}]] - [view st/name-container - [text {:style st/name-text} - @username]] - [view st/menu-items-container - [menu-item {:name (label :t/profile) - :handler #(dispatch [:navigate-to :my-profile])}] - [menu-item {:name (label :t/settings) - :handler (fn [] - ;; TODO not implemented - )}] - [menu-item {:name (label :t/discovery) - :handler #(dispatch [:navigate-to :discovery])}] - [menu-item {:name (label :t/contacts) - :handler #(dispatch [:show-contacts navigator])}] - [menu-item {:name (label :t/invite-friends) - :handler (fn [] - ;; TODO not implemented - )}] - [menu-item {:name (label :t/faq) - :handler (fn [])}]] - [view st/switch-users-container - [touchable-opacity {:onPress (fn [] - (close-drawer) - (dispatch [:navigate-to :accounts]) - ;; TODO not implemented - )} - [text {:style st/switch-users-text} - (label :t/switch-users)]]]]))) +(defview drawer-menu [{platform-specific :platform-specific}] + [{:keys [name address photo-path]} [:get-current-account]] + [view st/drawer-menu + [view st/user-photo-container + [user-photo {:photo-path photo-path}]] + [view st/name-container + [text {:style st/name-text + :platform-specific platform-specific + :number-of-lines 1 + :font :default} + (if (= name address) + (label :t/user-anonymous) + name)]] + [view st/menu-items-container + [menu-item {:name (label :t/profile) + :handler #(dispatch [:navigate-to :my-profile]) + :platform-specific platform-specific}] + [menu-item {:name (label :t/settings) + :handler (fn [] + ;; TODO not implemented + ) + :platform-specific platform-specific}] + [menu-item {:name (label :t/discovery) + :handler #(dispatch [:navigate-to :discovery]) + :platform-specific platform-specific}] + [menu-item {:name (label :t/contacts) + :handler #(dispatch [:show-contacts navigator]) + :platform-specific platform-specific}] + [menu-item {:name (label :t/invite-friends) + :handler (fn [] + ;; TODO not implemented + ) + :platform-specific platform-specific}] + [menu-item {:name (label :t/faq) + :handler (fn []) + :platform-specific platform-specific}]] + [view st/switch-users-container + [touchable-opacity {:onPress (fn [] + (close-drawer) + (dispatch [:navigate-to :accounts]) + ;; TODO not implemented + )} + [text {:style st/switch-users-text + :platform-specific platform-specific + :font :default} + (label :t/switch-users)]]]]) -(defn drawer-view [items] +(defn drawer-view [opts items] [drawer-layout-android {:drawerWidth 260 :drawerPosition js/ReactNative.DrawerLayoutAndroid.positions.Left - :render-navigation-view #(r/as-element [drawer-menu]) + :render-navigation-view #(r/as-element [drawer-menu opts]) :ref (fn [drawer] (reset! drawer-atom drawer))} items]) diff --git a/src/status_im/components/icons/custom_icons.cljs b/src/status_im/components/icons/custom_icons.cljs new file mode 100644 index 0000000000..8ce0b571ae --- /dev/null +++ b/src/status_im/components/icons/custom_icons.cljs @@ -0,0 +1,8 @@ +(ns status-im.components.icons.custom-icons + (:require [reagent.core :as r])) + +(def ion-icon + (r/adapt-react-class (js/require "react-native-vector-icons/Ionicons"))) + +(def oct-icon + (r/adapt-react-class (js/require "react-native-vector-icons/Octicons"))) \ No newline at end of file diff --git a/src/status_im/components/icons/ionicons.cljs b/src/status_im/components/icons/ionicons.cljs deleted file mode 100644 index 5bc4a06ddc..0000000000 --- a/src/status_im/components/icons/ionicons.cljs +++ /dev/null @@ -1,4 +0,0 @@ -(ns status-im.components.icons.ionicons - (:require [reagent.core :as r])) - -(def icon (r/adapt-react-class (js/require "react-native-vector-icons/Ionicons"))) diff --git a/src/status_im/components/main_tabs.cljs b/src/status_im/components/main_tabs.cljs index c12b746898..a6bbd2a054 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.status-bar :refer [status-bar]] [status-im.components.drawer.view :refer [drawer-view]] [status-im.components.animation :as anim] [status-im.chats-list.screen :refer [chats-list]] @@ -99,12 +100,15 @@ [tab-view-container view-id [screen]]) -(defview main-tabs [] +(defview main-tabs [{platform-specific :platform-specific}] [view-id [:get :view-id] tab-animation? [:get :prev-tab-view-id]] - [drawer-view - [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}]]]) + [view common-st/flex + [status-bar {:platform-specific platform-specific}] + [view common-st/flex + [drawer-view {:platform-specific platform-specific} + [view {:style common-st/flex + :pointerEvents (if tab-animation? :none :auto)} + (doall (map #(tab-view %) tab-list)) + [tabs {:selected-view-id view-id + :tab-list tab-list}]]]]]) diff --git a/src/status_im/components/qr_code.cljs b/src/status_im/components/qr_code.cljs index 6c1d7d066d..e06b1e25ab 100644 --- a/src/status_im/components/qr_code.cljs +++ b/src/status_im/components/qr_code.cljs @@ -4,4 +4,6 @@ (def class (js/require "react-native-qrcode")) (defn qr-code [props] - (r/create-element class (clj->js (merge {:inverted true} props)))) \ No newline at end of file + (r/create-element + class + (clj->js (merge {:inverted true} props)))) \ No newline at end of file diff --git a/src/status_im/components/react.cljs b/src/status_im/components/react.cljs index aefd1cba23..4654774612 100644 --- a/src/status_im/components/react.cljs +++ b/src/status_im/components/react.cljs @@ -6,6 +6,7 @@ (def react-native (u/require "react-native")) (def native-modules (.-NativeModules react-native)) (def geth (.-Geth native-modules)) +(def react-native-dialogs (u/require "react-native-dialogs")) (def linear-gradient-module (u/require "react-native-linear-gradient")) (def dismiss-keyboard! (u/require "dismissKeyboard")) @@ -57,7 +58,6 @@ (def dimensions (.-Dimensions js/ReactNative)) (def keyboard (.-Keyboard react-native)) - ;; Accessor methods for React Components (defn text @@ -110,6 +110,33 @@ (vec (concat [linear-gradient-class (merge {:inverted true} props)] children))) +;; List dialogs + +(defn show-dialog [{:keys [title options callback]}] + (let [dialog (new react-native-dialogs)] + (.set dialog (clj->js {:title title + :items options + :itemsCallback callback})) + (.show dialog))) + +(defn show-action-sheet [{:keys [options callback cancel-text]}] + (.showActionSheetWithOptions (get-class "ActionSheetIOS") + (clj->js {:options (conj options cancel-text) + :cancelButtonIndex (count options)}) + callback)) + + +;; Image picker + +(def image-picker-class (u/require "react-native-image-crop-picker")) + +(defn show-image-picker [images-fn] + (let [image-picker (.-default image-picker-class)] + (-> image-picker + (.openPicker (clj->js {:multiple false})) + (.then images-fn)))) + + ;; Platform (def platform diff --git a/src/status_im/components/toolbar.cljs b/src/status_im/components/toolbar.cljs index 4204f8c3af..98f7815174 100644 --- a/src/status_im/components/toolbar.cljs +++ b/src/status_im/components/toolbar.cljs @@ -38,7 +38,7 @@ :alignItems :center :justifyContent :center} [image (:image nav-action)]]] - [touchable-highlight {:on-press #(dispatch [:navigate-back]) + [touchable-highlight {:on-press #(dispatch [:navigate-back]) :accessibility-label :navigate-back} [view {:width 56 :height 56 diff --git a/src/status_im/contacts/screen.cljs b/src/status_im/contacts/screen.cljs index 1d5c54c260..12b64cbc16 100644 --- a/src/status_im/contacts/screen.cljs +++ b/src/status_im/contacts/screen.cljs @@ -15,7 +15,7 @@ [status-im.components.status-bar :refer [status-bar]] [status-im.components.toolbar :refer [toolbar]] [status-im.components.drawer.view :refer [open-drawer]] - [status-im.components.icons.ionicons :refer [icon]] + [status-im.components.icons.custom-icons :refer [ion-icon]] [status-im.components.styles :refer [color-blue hamburger-icon icon-search @@ -101,6 +101,6 @@ {:title (label :t/new-contact) :buttonColor :#9b59b6 :onPress #(dispatch [:navigate-to :new-contact])} - [icon {:name :md-create - :style create-icon}]]]] + [ion-icon {:name :md-create + :style create-icon}]]]] [bottom-gradient]]))) diff --git a/src/status_im/contacts/views/contact_list.cljs b/src/status_im/contacts/views/contact_list.cljs index 5453ddede6..5387794b19 100644 --- a/src/status_im/contacts/views/contact_list.cljs +++ b/src/status_im/contacts/views/contact_list.cljs @@ -10,7 +10,6 @@ [status-im.components.status-bar :refer [status-bar]] [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 @@ -39,7 +38,7 @@ (defview contact-list [{platform-specific :platform-specific}] [contacts [:contacts-with-letters]] - [drawer-view + [drawer-view {:platform-specific platform-specific} [view st/contacts-list-container [contact-list-toolbar platform-specific] ;; todo what if there is no contacts, should we show some information diff --git a/src/status_im/db.cljs b/src/status_im/db.cljs index 730415ac69..65b5eda641 100644 --- a/src/status_im/db.cljs +++ b/src/status_im/db.cljs @@ -11,9 +11,16 @@ ;; initial state of app-db (def app-db {:identity-password "replace-me-with-user-entered-password" :identity "me" - :is-logged-in false + :accounts {} - :user-identity nil + :current-account-id nil + + :profile-edit {:edit? false + :name nil + :email nil + :status nil + :photo-path nil} + :contacts [] :contacts-ids #{} :selected-contacts #{} @@ -27,12 +34,6 @@ :signed-up false :view-id default-view :navigation-stack (list default-view) - ;; TODO fix hardcoded values - :photo-path nil - :username "My Name" - :phone-number "3147984309" - :email "myemail@gmail.com" - :status "Hi, this is my status" :current-tag nil :qr-codes {} :new-contact {:name "" @@ -42,7 +43,7 @@ :keyboard-height 0 :disable-group-creation false :animations {;; todo clear this - :tabs-bar-value (anim/create-value 0)}}) + :tabs-bar-value (anim/create-value 0)}}) (def protocol-initialized-path [:protocol-initialized]) (defn chat-input-text-path [chat-id] diff --git a/src/status_im/handlers.cljs b/src/status_im/handlers.cljs index 58d6a406a8..cf28e0c160 100644 --- a/src/status_im/handlers.cljs +++ b/src/status_im/handlers.cljs @@ -17,6 +17,7 @@ status-im.discovery.handlers status-im.new-group.handlers status-im.participants.handlers + status-im.profile.handlers status-im.commands.handlers.loading status-im.commands.handlers.jail status-im.qr-scanner.handlers @@ -56,7 +57,7 @@ (register-handler :initialize-db (fn [_ _] (realm/reset-account) - (assoc app-db :user-identity nil))) + (assoc app-db :current-account-id nil))) (register-handler :initialize-account-db (fn [db _] @@ -66,8 +67,8 @@ (register-handler :initialize-account (u/side-effect! - (fn [_ [_ account]] - (dispatch [:initialize-protocol account]) + (fn [_ [_ address]] + (dispatch [:initialize-protocol address]) (dispatch [:initialize-account-db]) (dispatch [:initialize-chats]) (dispatch [:load-contacts]) diff --git a/src/status_im/handlers/server.cljs b/src/status_im/handlers/server.cljs index 565f6f90c0..8c18d4437e 100644 --- a/src/status_im/handlers/server.cljs +++ b/src/status_im/handlers/server.cljs @@ -5,7 +5,8 @@ (defn sign-up [db phone-number handler] - (let [{:keys [public-key address] :as account} (get-in db [:user-identity])] + (let [current-account-id (get db :current-account-id) + {:keys [public-key address]} (get-in db [:accounts current-account-id])] ;(user-data/save-phone-number phone-number) (log/debug "signing up with public-key" public-key "and phone " phone-number) (http-post "sign-up" {:phone-number phone-number diff --git a/src/status_im/ios/core.cljs b/src/status_im/ios/core.cljs index 1e3bcd8ad0..0c4473e8fd 100644 --- a/src/status_im/ios/core.cljs +++ b/src/status_im/ios/core.cljs @@ -6,7 +6,8 @@ [status-im.ios.styles :refer [styles]] [status-im.components.react :refer [app-registry keyboard - orientation]] + orientation + show-action-sheet]] [status-im.components.main-tabs :refer [main-tabs]] [status-im.contacts.views.contact-list :refer [contact-list]] [status-im.contacts.views.new-contact :refer [new-contact]] @@ -22,6 +23,7 @@ [status-im.participants.views.remove :refer [remove-participants]] [status-im.group-settings.screen :refer [group-settings]] [status-im.profile.screen :refer [profile my-profile]] + [status-im.profile.photo-capture.screen :refer [profile-photo-capture]] [status-im.utils.utils :refer [toast]] [status-im.utils.encryption] status-im.persistence.realm.core @@ -34,9 +36,9 @@ (let [signed-up (subscribe [:get :signed-up]) _ (log/debug "signed up: " @signed-up) view-id (subscribe [:get :view-id]) - account (subscribe [:get :user-identity]) + account-id (subscribe [:get :current-account-id]) keyboard-height (subscribe [:get :keyboard-height])] - (log/debug "Current account: " @account) + (log/debug "Current account: " @account-id) (r/create-class {:component-will-mount (fn [] @@ -58,7 +60,7 @@ #(dispatch [:set :keyboard-height 0])))) :render (fn [] - (let [startup-view (if @account + (let [startup-view (if @account-id (if @signed-up @view-id :chat) @@ -80,10 +82,12 @@ :qr-scanner qr-scanner :chat chat :profile profile + :profile-photo-capture profile-photo-capture :accounts accounts :login login :my-profile my-profile)] - [component {:platform-specific {:styles styles}}])))}))) + [component {:platform-specific {:styles styles + :list-selection-fn show-action-sheet}}])))}))) (defn init [] (dispatch-sync [:reset-app]) diff --git a/src/status_im/models/accounts.cljs b/src/status_im/models/accounts.cljs index 834db43e94..a19a00cf6c 100644 --- a/src/status_im/models/accounts.cljs +++ b/src/status_im/models/accounts.cljs @@ -2,21 +2,20 @@ (:require [status-im.persistence.realm.core :as r])) (defn get-accounts [] - (-> (r/get-all :base :accounts) - r/collection->map)) + (-> (r/get-all :base :accounts) + r/collection->map)) -(defn create-account [{:keys [address public-key] :as account}] - (->> account - (r/create :base :accounts))) +(defn save-account [update?] + #(r/create :base :accounts % update?)) -(defn save-accounts [accounts] - (r/write :base #(mapv create-account accounts))) +(defn save-accounts [accounts update?] + (r/write :base #(mapv (save-account update?) accounts))) ;;;;;;;;;;;;;;;;;;;;---------------------------------------------- (defn accounts-list [] - (r/get-all :base :accounts)) + (r/get-all :base :accounts)) (defn account-by-address [address] - (r/single-cljs (r/get-by-field :base :accounts :address address))) + (r/single-cljs (r/get-by-field :base :accounts :address address))) diff --git a/src/status_im/models/protocol.cljs b/src/status_im/models/protocol.cljs index 441d433688..e85863b20c 100644 --- a/src/status_im/models/protocol.cljs +++ b/src/status_im/models/protocol.cljs @@ -11,10 +11,10 @@ (defn set-initialized [db initialized?] (assoc-in db db/protocol-initialized-path initialized?)) -(defn update-identity [db identity] +(defn update-identity [db {:keys [address] :as identity}] (let [identity-string (to-edn-string identity)] (s/put kv/kv-store :identity identity-string) - (assoc db :user-identity identity))) + (assoc-in db [:accounts address] identity))) (defn stored-identity [db] (let [identity (s/get kv/kv-store :identity)] diff --git a/src/status_im/navigation/handlers.cljs b/src/status_im/navigation/handlers.cljs index 5d61fe3163..25767b3b6d 100644 --- a/src/status_im/navigation/handlers.cljs +++ b/src/status_im/navigation/handlers.cljs @@ -81,6 +81,12 @@ (register-handler :show-profile show-profile) +(defn show-profile-photo-capture + [db [_ image-captured-fn]] + (push-view db :profile-photo-capture)) + +(register-handler :show-profile-photo-capture show-profile-photo-capture) + (defn navigate-to-clean [db [_ view-id]] (-> db diff --git a/src/status_im/persistence/realm/schemas.cljs b/src/status_im/persistence/realm/schemas.cljs index 1506f3ad96..34a5fd94fc 100644 --- a/src/status_im/persistence/realm/schemas.cljs +++ b/src/status_im/persistence/realm/schemas.cljs @@ -1,32 +1,35 @@ (ns status-im.persistence.realm.schemas (:require [status-im.components.styles :refer [default-chat-color]])) -(def base {:schema [{:name :accounts - :primaryKey :address - :properties {:address "string" - :public-key "string" - :name "string" - :photo-path "string"}} - {:name :tag - :primaryKey :name - :properties {:name "string" - :count {:type "int" - :optional true - :default 0}}} - {:name :discoveries - :primaryKey :whisper-id - :properties {:name "string" - :status "string" - :whisper-id "string" - :photo "string" - :location "string" - :tags {:type "list" - :objectType "tag"} - :last-updated "date"}} - {:name :kv-store - :primaryKey :key - :properties {:key "string" - :value "string"}}] +(def base {:schema [{:name :accounts + :primaryKey :address + :properties {:address "string" + :public-key "string" + :name "string" + :phone {:type "string" :optional true} + :email {:type "string" :optional true} + :status {:type "string" :optional true} + :photo-path "string"}} + {:name :tag + :primaryKey :name + :properties {:name "string" + :count {:type "int" + :optional true + :default 0}}} + {:name :discoveries + :primaryKey :whisper-id + :properties {:name "string" + :status "string" + :whisper-id "string" + :photo "string" + :location "string" + :tags {:type "list" + :objectType "tag"} + :last-updated "date"}} + {:name :kv-store + :primaryKey :key + :properties {:key "string" + :value "string"}}] :schemaVersion 0}) (def account {:schema [{:name :contacts diff --git a/src/status_im/profile/handlers.cljs b/src/status_im/profile/handlers.cljs new file mode 100644 index 0000000000..ef3186000d --- /dev/null +++ b/src/status_im/profile/handlers.cljs @@ -0,0 +1,33 @@ +(ns status-im.profile.handlers + (:require [re-frame.core :refer [subscribe dispatch]] + [status-im.utils.handlers :refer [register-handler]] + [status-im.components.react :refer [show-image-picker]] + [status-im.utils.image-processing :refer [img->base64]] + [status-im.i18n :refer [label]] + [status-im.utils.handlers :as u])) + +(register-handler :open-image-picker + (u/side-effect! + (fn [_ _] + (show-image-picker + (fn [image] + (let [path (-> (js->clj image) + (get "path") + (subs 7)) + on-success (fn [base64] + (dispatch [:set-in [:profile-edit :photo-path] (str "data:image/jpeg;base64," base64)])) + on-error (fn [type error] + (.log js/console type error))] + (img->base64 path on-success on-error))))))) + +(register-handler :open-image-source-selector + (u/side-effect! + (fn [_ [_ list-selection-fn]] + (list-selection-fn {:title (label :t/image-source-title) + :options [(label :t/image-source-make-photo) (label :t/image-source-gallery)] + :callback (fn [index] + (case index + 0 (dispatch [:show-profile-photo-capture]) + 1 (dispatch [:open-image-picker]) + :default)) + :cancel-text (label :t/image-source-cancel)})))) \ No newline at end of file diff --git a/src/status_im/profile/photo_capture/screen.cljs b/src/status_im/profile/photo_capture/screen.cljs new file mode 100644 index 0000000000..429dedd5ee --- /dev/null +++ b/src/status_im/profile/photo_capture/screen.cljs @@ -0,0 +1,53 @@ +(ns status-im.profile.photo-capture.screen + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [clojure.walk :refer [keywordize-keys]] + [status-im.components.react :refer [view + image + touchable-highlight]] + [status-im.components.camera :refer [camera + aspects + capture-targets]] + [status-im.components.styles :refer [toolbar-background1 + icon-search + icon-back]] + [status-im.components.icons.custom-icons :refer [ion-icon]] + [status-im.components.status-bar :refer [status-bar]] + [status-im.components.toolbar :refer [toolbar]] + [status-im.utils.image-processing :refer [img->base64]] + [status-im.profile.photo-capture.styles :as st] + [status-im.i18n :refer [label]] + [reagent.core :as r])) + +(defn image-captured [path] + (let [path (subs path 5) + on-success (fn [base64] + (dispatch [:set-in [:profile-edit :photo-path] (str "data:image/jpeg;base64," base64)]) + (dispatch [:navigate-back])) + on-error (fn [type error] + (.log js/console type error))] + (img->base64 path on-success on-error))) + +(defn profile-photo-capture [{platform-specific :platform-specific}] + (let [camera-ref (r/atom nil)] + [view st/container + [status-bar {:platform-specific platform-specific}] + [toolbar {:title (label :t/image-source-title) + :nav-action {:image {:source {:uri :icon_back} + :style icon-back} + :handler #(dispatch [:navigate-back])} + :background-color toolbar-background1}] + [camera {:style {:flex 1} + :aspect (:fill aspects) + :captureTarget (:disk capture-targets) + :type "front" + :ref #(reset! camera-ref %)}] + [view {:style {:padding 10 + :background-color toolbar-background1}} + [touchable-highlight {:style {:align-self "center"} + :on-press (fn [] + (let [camera @camera-ref] + (-> (.capture camera) + (.then image-captured))))} + [view + [ion-icon {:name :md-camera + :style {:font-size 36}}]]]]])) \ No newline at end of file diff --git a/src/status_im/profile/photo_capture/styles.cljs b/src/status_im/profile/photo_capture/styles.cljs new file mode 100644 index 0000000000..efd151170c --- /dev/null +++ b/src/status_im/profile/photo_capture/styles.cljs @@ -0,0 +1,5 @@ +(ns status-im.profile.photo-capture.styles) + +(def container + {:flex 1 + :background-color :white}) \ No newline at end of file diff --git a/src/status_im/profile/screen.cljs b/src/status_im/profile/screen.cljs index e4ee787838..fb5637b5b4 100644 --- a/src/status_im/profile/screen.cljs +++ b/src/status_im/profile/screen.cljs @@ -1,30 +1,127 @@ (ns status-im.profile.screen (:require-macros [status-im.utils.views :refer [defview]]) - (:require [re-frame.core :refer [subscribe dispatch]] + (:require [reagent.core :as r] + [re-frame.core :refer [subscribe dispatch]] [status-im.components.react :refer [view text + text-input image icon scroll-view touchable-highlight - touchable-opacity]] + touchable-opacity + show-image-picker]] + [status-im.components.icons.custom-icons :refer [oct-icon]] [status-im.components.chat-icon.screen :refer [profile-icon my-profile-icon]] + [status-im.components.status-bar :refer [status-bar]] [status-im.profile.styles :as st] [status-im.components.qr-code :refer [qr-code]] + [status-im.utils.phone-number :refer [format-phone-number + valid-mobile-number?]] + [status-im.utils.fs :refer [read-file]] [status-im.utils.types :refer [clj->json]] - [status-im.i18n :refer [label]])) + [status-im.utils.image-processing :refer [img->base64]] + [status-im.i18n :refer [label]] + [clojure.string :as str])) -(defn profile-property-view [{:keys [name value]}] - [view st/profile-property-view-container - [view st/profile-property-view-sub-container - [text {:style st/profile-property-view-label} name] - [text {:style st/profile-property-view-value} value]]]) +(defn- get-hashtags [status] + (let [hashtags (map #(subs % 1) (re-seq #"#[^ !?,;:.]+" status))] + (or hashtags []))) -(defn message-user [identity] +(defn- message-user [identity] (when identity (dispatch [:navigate-to :chat identity]))) +(defn- update-profile [{name :name + email :email + photo-path :photo-path + status :status} + {new-name :name + new-email :email + new-status :status + new-photo-path :photo-path}] + (let [new-name (if (or (not new-name) (str/blank? new-name)) name new-name) + status-updated? (and (not= new-status nil) + (not= status new-status))] + (when status-updated? + (dispatch [:broadcast-status new-status (get-hashtags new-status)])) + (dispatch [:account-update {:name new-name + :email (or new-email email) + :status (or new-status status) + :photo-path (or new-photo-path photo-path)}]))) + +(defview toolbar [{:keys [account profile-edit-data edit?]}] + [view + [touchable-highlight {:style st/back-btn-touchable + :on-press (fn [] + (dispatch [:set :profile-edit {:edit? false + :name nil + :email nil + :status nil + :photo-path nil}]) + (dispatch [:navigate-back]))} + [view st/back-btn-container + [icon :back st/back-btn-icon]]] + [touchable-highlight {:style st/actions-btn-touchable + :on-press (fn [] + (when edit? + (update-profile account profile-edit-data)) + (dispatch [:set-in [:profile-edit :edit?] (not edit?)]))} + [view st/actions-btn-container + (if edit? + [oct-icon {:name :check + :style st/ok-btn-icon}] + [icon :dots st/edit-btn-icon])]]]) + +(defview status-image-view [{{:keys [list-selection-fn]} :platform-specific + {address :address + username :name} :account + photo-path :photo-path + status :status + edit? :edit?}] + [view st/status-block + [view st/user-photo-container + (if edit? + [touchable-highlight {:on-press (fn [] + (dispatch [:open-image-source-selector list-selection-fn]))} + [view + [my-profile-icon {:account {:photo-path photo-path + :name username} + :edit? edit?}]]] + [my-profile-icon {:account {:photo-path photo-path + :name username} + :edit? edit?}])] + [text {:style st/username + :platform-specific platform-specific + :font :default} + (if (= username address) + (label :t/user-anonymous) + username)] + [text-input {:style st/status-input + :editable edit? + :placeholder (label :t/profile-no-status) + :on-change-text #(dispatch [:set-in [:profile-edit :status] %])} + status]]) + + +(defview profile-property-view [{name :name + value :value + empty-value :empty-value + on-change-text :on-change-text + {edit-mode? :edit?} :profile-data + platform-specific :platform-specific}] + [view st/profile-property-view-container + [view st/profile-property-view-sub-container + [text {:style st/profile-property-view-label + :platform-specific platform-specific + :font :medium} + name] + [text-input {:style st/profile-property-view-value + :editable (and on-change-text edit-mode?) + :on-change-text on-change-text} + (or value (when-not edit-mode? empty-value))]]]) + (defview profile [] [{:keys [name whisper-identity phone-number]} [:contact]] [scroll-view {:style st/profile} @@ -35,9 +132,9 @@ [view st/status-block [view st/user-photo-container [profile-icon]] - [text {:style st/user-name} name] + [text {:style st/username} name] ;; TODO stub data - [text {:style st/status} (label :t/not-implemented)] + [text {:style st/status-input} (label :t/not-implemented)] [view st/btns-container [touchable-highlight {:onPress #(message-user whisper-identity)} [view st/message-btn @@ -61,37 +158,53 @@ )} [view [text {:style st/report-user-text} (label :t/report-user)]]]]]]) -(defview my-profile [_] - [username [:get :username] - photo-path [:get :photo-path] - phone-number [:get :phone-number] - email [:get :email] - status [:get :status] - identity [:get-in [:user-identity :public-key]]] +(defview my-profile [{platform-specific :platform-specific}] + [{public-key :public-key + address :address + username :name + email :email + photo-path :photo-path + phone :phone + status :status + :as account} [:get-current-account] + {edit? :edit? + new-status :status + new-photo-path :photo-path + :as profile-edit-data} [:get :profile-edit]] [scroll-view {:style st/profile} - [touchable-highlight {:style st/back-btn-touchable - :on-press #(dispatch [:navigate-back])} - [view st/back-btn-container - [icon :back st/back-btn-icon]]] - [touchable-highlight {:style st/actions-btn-touchable - :on-press (fn [] - ;; TODO not implemented - )} - [view st/actions-btn-container - [icon :dots st/actions-btn-icon]]] - [view st/status-block - [view st/user-photo-container - [my-profile-icon]] - [text {:style st/user-name} username] - [text {:style st/status} status]] + [status-bar {:platform-specific platform-specific}] + [toolbar {:account account + :profile-edit-data profile-edit-data + :edit? edit?}] + + [status-image-view {:platform-specific platform-specific + :account account + :photo-path (or new-photo-path photo-path) + :status (if (and new-status (not (str/blank? new-status))) new-status status) + :edit? edit?}] + [scroll-view st/profile-properties-container - [profile-property-view {:name (label :t/username) - :value username}] - [profile-property-view {:name (label :t/phone-number) - :value phone-number}] - [profile-property-view {:name (label :t/email) - :value email}] + [profile-property-view {:name (label :t/username) + :value (if (not= username address) + username) + :empty-value (label :t/not-specified) + :on-change-text #(dispatch [:set-in [:profile-edit :name] %]) + :profile-data profile-edit-data + :platform-specific platform-specific}] + [profile-property-view {:name (label :t/phone-number) + :value (if-not (or (not phone) (str/blank? phone)) + (format-phone-number phone)) + :empty-value (label :t/not-specified) + :profile-data profile-edit-data + :platform-specific platform-specific}] + [profile-property-view {:name (label :t/email) + :value (if-not (or (not email) (str/blank? email)) + email) + :empty-value (label :t/not-specified) + :on-change-text #(dispatch [:set-in [:profile-edit :email] %]) + :profile-data profile-edit-data + :platform-specific platform-specific}] [view st/qr-code-container - [qr-code {:value (clj->json {:name username - :whisper-identity identity}) - :size 200}]]]]) + [qr-code {:value (clj->js {:name username + :whisper-identity public-key}) + :size 150}]]]]) diff --git a/src/status_im/profile/styles.cljs b/src/status_im/profile/styles.cljs index 1650bb20a2..ea52973aae 100644 --- a/src/status_im/profile/styles.cljs +++ b/src/status_im/profile/styles.cljs @@ -1,41 +1,20 @@ (ns status-im.profile.styles (:require [status-im.components.styles :refer [font - color-light-blue-transparent - color-white - color-black - color-blue - color-blue-transparent - selected-message-color - online-color - separator-color - text1-color - text2-color]])) - -(def profile-property-view-container - {:height 85 - :paddingHorizontal 16}) - -(def profile-property-view-sub-container - {:borderBottomWidth 1 - :borderBottomColor separator-color}) - -(def profile-property-view-label - {:marginTop 16 - :fontSize 14 - :fontFamily font - :color text2-color}) - -(def profile-property-view-value - {:marginTop 11 - :height 40 - :fontSize 16 - :fontFamily font - :color text1-color}) + color-light-blue-transparent + color-white + color-black + color-blue + color-blue-transparent + selected-message-color + online-color + separator-color + text1-color + text2-color]])) (def profile - {:flex 1 - :backgroundColor color-white - :flexDirection :column}) + {:flex 1 + :background-color color-white + :flex-direction :column}) (def back-btn-touchable {:position :absolute}) @@ -45,98 +24,133 @@ :height 56}) (def back-btn-icon - {:marginTop 21 - :marginLeft 23 - :width 8 - :height 14}) + {:margin-top 21 + :margin-left 23 + :width 8 + :height 14}) (def actions-btn-touchable {:position :absolute :right 0}) (def actions-btn-container - {:width 56 - :height 56 - :alignItems :center - :justifyContent :center}) + {:width 56 + :height 56 + :align-items :center + :justify-content :center}) -(def actions-btn-icon +(def edit-btn-icon {:width 4 :height 16}) -(def status-block - {:alignSelf :center - :alignItems :center - :width 249}) +(def ok-btn-icon + {:font-size 22 + :color :black}) (def user-photo-container - {:marginTop 22}) + {:margin-top 22}) -(def user-name - {:marginTop 16 - :fontSize 18 - :fontFamily font +(def username + {:margin-top 12 + :font-size 18 :color text1-color}) -(def status - {:marginTop 10 - :fontFamily font - :fontSize 14 - :lineHeight 20 - :textAlign :center - :color text2-color}) +(def username-input + {:align-self "stretch" + :margin-top -8 + :margin-bottom -22 + :font-size 18 + :text-align :center + :color text1-color}) + +(def status-block + {:flex-direction "column" + :align-items "center" + :justifyContent "center"}) + +(def status-input + {:align-self "stretch" + :margin-left 16 + :margin-right 16 + :height 40 + :margin-top -4 + :font-size 14 + :line-height 20 + :text-align :center + :color text2-color}) (def btns-container - {:marginTop 18 - :flexDirection :row}) + {:margin-top 18 + :flex-direction :row}) (def message-btn - {:height 40 - :justifyContent :center - :backgroundColor color-blue - :paddingLeft 25 - :paddingRight 25 - :borderRadius 20}) + {:height 40 + :justify-content :center + :background-color color-blue + :padding-left 25 + :padding-right 25 + :border-radius 20}) (def message-btn-text - {:marginTop -2.5 - :fontSize 14 - :fontFamily font - :color color-white}) + {:margin-top -2.5 + :font-size 14 + :font-family font + :color color-white}) (def more-btn - {:marginLeft 10 - :width 40 - :height 40 - :alignItems :center - :justifyContent :center - :backgroundColor color-blue-transparent - :padding 8 - :borderRadius 20}) + {:margin-left 10 + :width 40 + :height 40 + :align-items :center + :justify-content :center + :background-color color-blue-transparent + :padding 8 + :border-radius 20}) (def more-btn-image {:width 4 :height 16}) (def profile-properties-container - {:marginTop 20 - :alignItems :stretch - :flexDirection :column}) + {:margin-top 20 + :align-items :stretch + :flex-firection :column}) + +(def profile-property-view-container + {:padding-left 16}) + +(def profile-property-view-sub-container + {:border-bottom-width 1 + :border-bottom-color separator-color + :padding-right 16}) + +(def profile-property-view-label + {:margin-top 18 + :font-size 14 + :color text2-color}) + +(def profile-property-view-value + {:margin-top 8 + :margin-bottom 8 + :padding 0 + :height 40 + :font-size 16 + :color text1-color}) (def report-user-container - {:marginTop 50 - :marginBottom 43 - :alignItems :center}) + {:margin-top 50 + :margin-bottom 43 + :align-items :center}) (def report-user-text - {:fontSize 14 - :fontFamily font - :lineHeight 21 - :color text2-color + {:font-size 14 + :font-family font + :line-height 21 + :color text2-color ;; IOS: - :letterSpacing 0.5}) + :letter-spacing 0.5}) (def qr-code-container - {:flex 1 + {:flex 1 :alignItems :center - :margin 15}) + :margin 32}) diff --git a/src/status_im/protocol/handlers.cljs b/src/status_im/protocol/handlers.cljs index 1ff462d03a..182752e46d 100644 --- a/src/status_im/protocol/handlers.cljs +++ b/src/status_im/protocol/handlers.cljs @@ -18,8 +18,9 @@ (register-handler :initialize-protocol (u/side-effect! - (fn [{:keys [user-identity] :as db} [_ account]] - (init-protocol (or account user-identity) (make-handler db))))) + (fn [db [_ current-account-id]] + (let [current-account (get-in db [:accounts current-account-id])] + (init-protocol current-account (make-handler db)))))) (register-handler :protocol-initialized (fn [db [_ identity]] diff --git a/src/status_im/qr_scanner/views/import-button.cljs b/src/status_im/qr_scanner/views/import-button.cljs index 99b7dbc7b7..a66f17c9b9 100644 --- a/src/status_im/qr_scanner/views/import-button.cljs +++ b/src/status_im/qr_scanner/views/import-button.cljs @@ -6,7 +6,6 @@ image touchable-highlight]] [status-im.components.toolbar :refer [toolbar]] - [status-im.components.drawer.view :refer [drawer-view open-drawer]] [status-im.components.styles :refer [icon-qr]] [status-im.i18n :refer [label]] [status-im.qr-scanner.styles :as st])) diff --git a/src/status_im/qr_scanner/views/scan-button.cljs b/src/status_im/qr_scanner/views/scan-button.cljs index 81dae39990..6f5db52c8a 100644 --- a/src/status_im/qr_scanner/views/scan-button.cljs +++ b/src/status_im/qr_scanner/views/scan-button.cljs @@ -6,7 +6,6 @@ image touchable-highlight]] [status-im.components.toolbar :refer [toolbar]] - [status-im.components.drawer.view :refer [drawer-view open-drawer]] [status-im.components.styles :refer [icon-scan]] [status-im.i18n :refer [label]] [status-im.qr-scanner.styles :as st])) diff --git a/src/status_im/subs.cljs b/src/status_im/subs.cljs index f31dc4ebe2..a79747cb2d 100644 --- a/src/status_im/subs.cljs +++ b/src/status_im/subs.cljs @@ -12,6 +12,11 @@ (fn [db [_ k]] (reaction (k @db)))) +(register-sub :get-current-account + (fn [db [_ _]] + (reaction (let [current-account-id (:current-account-id @db)] + (get-in @db [:accounts current-account-id]))))) + (register-sub :get-in (fn [db [_ path]] (reaction (get-in @db path)))) diff --git a/src/status_im/translations/en.cljs b/src/status_im/translations/en.cljs index 8093e85814..5236b1ef0e 100644 --- a/src/status_im/translations/en.cljs +++ b/src/status_im/translations/en.cljs @@ -27,8 +27,17 @@ :report-user "REPORT USER" :message "Message" :username "Username" + :user-anonymous "Anonymous" + :not-specified "Not specified" :phone-number "Phone number" :email "Email" + :profile-no-status "No status" + + ;;make_photo + :image-source-title "Profile image" + :image-source-make-photo "Capture" + :image-source-gallery "Select from gallery" + :image-source-cancel "Cancel" ;sign-up :contacts-syncronized "Your contacts have been synchronized" diff --git a/src/status_im/utils/fs.cljs b/src/status_im/utils/fs.cljs index c81ab9dae8..a68f8c07d6 100644 --- a/src/status_im/utils/fs.cljs +++ b/src/status_im/utils/fs.cljs @@ -8,4 +8,9 @@ (let [result (.moveFile fs src dst) result (.then result #(handler nil %)) result (.catch result #(handler % nil))] - result)) \ No newline at end of file + result)) + +(defn read-file [path encoding on-read on-error] + (-> (.readFile fs path encoding) + (.then on-read) + (.catch on-error))) \ No newline at end of file diff --git a/src/status_im/utils/image_processing.cljs b/src/status_im/utils/image_processing.cljs new file mode 100644 index 0000000000..075e1c9b80 --- /dev/null +++ b/src/status_im/utils/image_processing.cljs @@ -0,0 +1,25 @@ +(ns status-im.utils.image-processing + (:require [reagent.core :as r] + [status-im.utils.fs :refer [read-file]])) + +(def resizer-class (js/require "react-native-image-resizer")) + +(defn- resize [path max-width max-height on-resize on-error] + (let [resize-fn (aget resizer-class "default" "createResizedImage")] + (-> (resize-fn path max-width max-height "JPEG" 75 0 nil) + (.then on-resize) + (.catch on-error)))) + +(defn- image-base64-encode [path on-success on-error] + (let [on-encoded (fn [data] + (on-success data)) + on-error (fn [error] + (on-error :base64 error))] + (read-file path "base64" on-encoded on-error))) + +(defn img->base64 [path on-success on-error] + (let [on-resized (fn [path] + (image-base64-encode (subs path 5) on-success on-error)) + on-error (fn [error] + (on-error :resize error))] + (resize path 150 150 on-resized on-error))) \ No newline at end of file