From 5051d1ce9e3e7acae6266827092db735c0e31443 Mon Sep 17 00:00:00 2001 From: Andrey Shovkoplyas Date: Mon, 10 Jul 2017 13:29:58 +0300 Subject: [PATCH] /location command --- .re-natal | 3 +- android/app/build.gradle | 3 + android/app/src/main/AndroidManifest.xml | 1 + .../im/status/ethereum/MainApplication.java | 4 +- android/settings.gradle | 5 +- bots/mailman/bot.js | 79 ++++++-- bots/mailman/translations.js | 3 +- ios/Mapbox.framework | 1 + ios/StatusIm.xcodeproj/project.pbxproj | 140 ++++++++++---- package.json | 1 + postinstall.sh | 3 +- resources/status.js | 33 +++- src/status_im/chat/handlers/input.cljs | 57 ++++-- .../chat/views/geolocation/styles.cljs | 91 +++++++++ .../chat/views/geolocation/views.cljs | 173 ++++++++++++++++++ src/status_im/chat/views/input/input.cljs | 12 +- src/status_im/chat/views/message/message.cljs | 21 ++- src/status_im/commands/handlers/jail.cljs | 4 +- src/status_im/commands/handlers/loading.cljs | 8 +- src/status_im/commands/utils.cljs | 42 +++-- src/status_im/components/list_selection.cljs | 17 +- src/status_im/components/mapbox.cljs | 23 +++ src/status_im/contacts/handlers.cljs | 6 +- src/status_im/handlers.cljs | 26 +++ src/status_im/translations/en.cljs | 10 + src/status_im/utils/views.clj | 2 +- 26 files changed, 652 insertions(+), 116 deletions(-) create mode 120000 ios/Mapbox.framework create mode 100644 src/status_im/chat/views/geolocation/styles.cljs create mode 100644 src/status_im/chat/views/geolocation/views.cljs create mode 100644 src/status_im/components/mapbox.cljs diff --git a/.re-natal b/.re-natal index bf1cc9c8e9..52acfba22c 100644 --- a/.re-natal +++ b/.re-natal @@ -41,7 +41,8 @@ "instabug-reactnative", "nfc-react-native", "react-native-http-bridge", - "emojilib" + "emojilib", + "react-native-mapbox-gl" ], "imageDirs": [ "images" diff --git a/android/app/build.gradle b/android/app/build.gradle index 1d0e8daf83..18f9010bd3 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -107,6 +107,7 @@ android { applicationId "im.status.ethereum" minSdkVersion 18 targetSdkVersion 23 + multiDexEnabled true versionCode getVersionCode() versionName getVersionName() ndk { @@ -115,6 +116,7 @@ android { } dexOptions { jumboMode true + javaMaxHeapSize "4g" } signingConfigs { release { @@ -180,6 +182,7 @@ dependencies { compile project(':react-native-fs') compile project(':react-native-image-crop-picker') compile project(':react-native-webview-bridge') + compile project(':reactnativemapboxgl') compile 'testfairy:testfairy-android-sdk:1.+@aar' compile fileTree(dir: "node_modules/realm/android/libs", include: ["*.jar"]) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index e21a7b75b0..d285a9efc0 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -52,6 +52,7 @@ android:enabled="true" android:exported="true"/> + diff --git a/android/app/src/main/java/im/status/ethereum/MainApplication.java b/android/app/src/main/java/im/status/ethereum/MainApplication.java index c5d65a18dc..2e6dc607a2 100644 --- a/android/app/src/main/java/im/status/ethereum/MainApplication.java +++ b/android/app/src/main/java/im/status/ethereum/MainApplication.java @@ -24,6 +24,7 @@ import fr.bamlab.rnimageresizer.ImageResizerPackage; import im.status.ethereum.module.StatusPackage; import io.realm.react.RealmReactPackage; import me.alwx.HttpServer.HttpServerReactPackage; +import com.mapbox.reactnativemapboxgl.ReactNativeMapboxGLPackage; import java.util.ArrayList; import java.util.Arrays; @@ -58,7 +59,8 @@ public class MainApplication extends Application implements ReactApplication { new ReactNativeDialogsPackage(), new ImageResizerPackage(), new PickerPackage(), - new WebViewBridgePackage(BuildConfig.DEBUG) + new WebViewBridgePackage(BuildConfig.DEBUG), + new ReactNativeMapboxGLPackage() )); if (!BuildConfig.DEBUG) { diff --git a/android/settings.gradle b/android/settings.gradle index 928c18f584..806e75fc6c 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -40,4 +40,7 @@ include ':react-native-image-crop-picker' project(':react-native-image-crop-picker').projectDir = new File(settingsDir, '../node_modules/react-native-image-crop-picker/android') include ':react-native-webview-bridge' -project(':react-native-webview-bridge').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview-bridge/android') \ No newline at end of file +project(':react-native-webview-bridge').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview-bridge/android') + +include ':reactnativemapboxgl' +project(':reactnativemapboxgl').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-mapbox-gl/android') diff --git a/bots/mailman/bot.js b/bots/mailman/bot.js index 75f9df04cc..ae579d773f 100644 --- a/bots/mailman/bot.js +++ b/bots/mailman/bot.js @@ -1,44 +1,91 @@ +function locationsSuggestions (params) { + var result = {title: "Send location"}; + var seqArg = params["seq-arg"] ? params["seq-arg"] : ""; + + if (seqArg === "Dropped pin") + { + result.showBack = true; + result.height = "max"; + result.markup = ['view', {}, + ['dropped-pin']]; + } + else if (seqArg != "") + { + result.showBack = true; + result.markup = ['scroll-view', {keyboardShouldPersistTaps: "always"}, + ['view', {}, + ['places-search']]]; + } + else + { + result.markup = ['scroll-view', {keyboardShouldPersistTaps: "always"}, + ['view', {}, + ['current-location-map'], + ['current-location'], + ['separator'], + ['places-nearby']]]; + } + + return result; +} + status.command({ name: "location", - icon: "location", title: I18n.t('location_title'), description: I18n.t('location_description'), - color: "#a187d5", sequentialParams: true, - registeredOnly: true, + hideSendButton: true, params: [{ name: "address", type: status.types.TEXT, - placeholder: I18n.t('location_address') + placeholder: I18n.t('location_address'), + suggestions: locationsSuggestions }], preview: function (params) { + var address = params.address.split("&"); var text = status.components.text( { style: { - marginTop: 5, + marginTop: 0, marginHorizontal: 0, - fontSize: 14, + fontSize: 15, + lineHeight: 23, fontFamily: "font", color: "black" } - }, params.address); - var uri = "https://maps.googleapis.com/maps/api/staticmap?center=" - + params.address - + "&size=100x100&maptype=roadmap&key=AIzaSyBNsj1qoQEYPb3IllmWMAscuXW0eeuYqAA&language=en" - + "&markers=size:mid%7Ccolor:0xff0000%7Clabel:%7C" - + params.address; + }, address[0]); + var uri = "https://api.mapbox.com/styles/v1/mapbox/streets-v10/static/" + + address[1] + "," + address[2] + ",10,20" + + "/175x58?access_token=pk.eyJ1Ijoic3RhdHVzaW0iLCJhIjoiY2oydmtnZjRrMDA3czMzcW9kemR4N2lxayJ9.Rz8L6xdHBjfO8cR3CDf3Cw"; var image = status.components.image( { source: {uri: uri}, style: { - width: 100, - height: 100 + borderRadius: 5 + marginTop: 12 + height: 58 } } ); - return {markup: status.components.view({}, [text, image])}; + return {markup: ['view', {}, + text, + ['view', {}, + image, + ['view', {style: {position: "absolute", + top: 0, + right: 0, + bottom: 0, + left: 0, + justifyContent: "center", + alignItems: "center"}}, + ['view', {style: {borderColor: "#628fe3", + backgroundColor: "#FFFFFF", + borderWidth: 4, + borderRadius: 8, + height: 15, + width: 15}}]]]]}; }, shortPreview: function (params) { return { @@ -48,4 +95,4 @@ status.command({ ) }; } -}); +}); \ No newline at end of file diff --git a/bots/mailman/translations.js b/bots/mailman/translations.js index 3ebb67c6f0..7d6af988c5 100644 --- a/bots/mailman/translations.js +++ b/bots/mailman/translations.js @@ -1,8 +1,9 @@ I18n.translations = { en: { location_title: 'Location', + location_suggestions_title: 'Send location', location_description: 'Share your location', - location_address: 'Address' + location_address: 'address' }, ru: { location_title: 'Местоположение', diff --git a/ios/Mapbox.framework b/ios/Mapbox.framework new file mode 120000 index 0000000000..a9b2ed5381 --- /dev/null +++ b/ios/Mapbox.framework @@ -0,0 +1 @@ +../node_modules/react-native-mapbox-gl/ios/Mapbox.framework \ No newline at end of file diff --git a/ios/StatusIm.xcodeproj/project.pbxproj b/ios/StatusIm.xcodeproj/project.pbxproj index 330758aedf..425dd5d257 100644 --- a/ios/StatusIm.xcodeproj/project.pbxproj +++ b/ios/StatusIm.xcodeproj/project.pbxproj @@ -7,24 +7,24 @@ 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 */; }; - 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; }; - 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; }; - 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; }; + 00C302E51ABCBA2D00DB3ED1 /* ReferenceProxy in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; }; + 00C302E71ABCBA2D00DB3ED1 /* ReferenceProxy in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; }; + 00C302E81ABCBA2D00DB3ED1 /* ReferenceProxy in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; }; + 00C302E91ABCBA2D00DB3ED1 /* ReferenceProxy in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; }; + 00C302EA1ABCBA2D00DB3ED1 /* ReferenceProxy in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; }; 00E356F31AD99517003FC87E /* StatusImTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* StatusImTests.m */; }; 0F942CF509F74CCDB5CB35B0 /* MaterialIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 54E2B86FB12D4CC49DA05C69 /* MaterialIcons.ttf */; }; - 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; }; - 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; }; - 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; }; + 133E29F31AD74F7200F7D852 /* ReferenceProxy in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; }; + 139105C61AF99C1200B5F7CC /* ReferenceProxy in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; }; + 139FDEF61B0652A700C62182 /* ReferenceProxy in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; }; 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; + 146834051AC3E58100842450 /* ReferenceProxy in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; 2028DFF91D4275B600227DCD /* SF-UI-Display-Medium.otf in Resources */ = {isa = PBXBuildFile; fileRef = 2028DFF51D4275B600227DCD /* SF-UI-Display-Medium.otf */; }; 2028DFFA1D4275B600227DCD /* SF-UI-Display-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 2028DFF61D4275B600227DCD /* SF-UI-Display-Regular.otf */; }; 2028DFFB1D4275B600227DCD /* SF-UI-Display-Semibold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 2028DFF71D4275B600227DCD /* SF-UI-Display-Semibold.otf */; }; 2028DFFC1D4275B600227DCD /* SF-UI-Display-Thin.otf in Resources */ = {isa = PBXBuildFile; fileRef = 2028DFF81D4275B600227DCD /* SF-UI-Display-Thin.otf */; }; - 20AB9EC61D47CC0300E7FD9C /* libRCTStatus.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 201067C41D4789F700FA83B6 /* libRCTStatus.a */; }; + 20AB9EC61D47CC0300E7FD9C /* ReferenceProxy in Frameworks */ = {isa = PBXBuildFile; fileRef = 201067C41D4789F700FA83B6 /* libRCTStatus.a */; }; 20B6B6841D92C42600CC5C6A /* RSKImageCropper.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 20B6B6831D92C42600CC5C6A /* RSKImageCropper.framework */; }; 20B6B6851D92C42600CC5C6A /* RSKImageCropper.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 20B6B6831D92C42600CC5C6A /* RSKImageCropper.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 20B6B6871D92C42600CC5C6A /* QBImagePicker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 20B6B6861D92C42600CC5C6A /* QBImagePicker.framework */; }; @@ -41,24 +41,27 @@ 74242ACAF37A48D0BFACDE82 /* Zocial.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 2756305FAFF144C4A6B0A039 /* Zocial.ttf */; }; 81C6E6AE0AA739BE9D87C1D0 /* libPods-StatusImTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FC1CBCFE6C906043D6CCEEE1 /* libPods-StatusImTests.a */; }; 82E689BAF9FB43C8AC6FF1CA /* EvilIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = CEB0E2659D1A4F5FA842057A /* EvilIcons.ttf */; }; - 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; + 832341BD1AAA6AB300B99B32 /* ReferenceProxy in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; 8E55E6877F950B81C8D711C5 /* libPods-StatusIm.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 101A4045637A2ADF57D28EF5 /* libPods-StatusIm.a */; }; 9E0B01A11DDC5DA7002B0359 /* SF-UI-Text-Light.otf in Resources */ = {isa = PBXBuildFile; fileRef = 9E0B01A01DDC5DA7002B0359 /* SF-UI-Text-Light.otf */; }; - 9E3AB6D01D87DB2B008846B4 /* libReact-Native-Webview-Bridge.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E3AB6C61D87DA2B008846B4 /* libReact-Native-Webview-Bridge.a */; }; - 9E7C64731E03FDDE004C7042 /* libRCTCamera.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 20B7D1151D3F74CD00B70F14 /* libRCTCamera.a */; }; - 9E7C64991E03FDE5004C7042 /* libRCTContacts.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 20B7D10A1D3F74CD00B70F14 /* libRCTContacts.a */; }; + 9E3AB6D01D87DB2B008846B4 /* ReferenceProxy in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E3AB6C61D87DA2B008846B4 /* libReact-Native-Webview-Bridge.a */; }; + 9E7C64731E03FDDE004C7042 /* ReferenceProxy in Frameworks */ = {isa = PBXBuildFile; fileRef = 20B7D1151D3F74CD00B70F14 /* libRCTCamera.a */; }; + 9E7C64991E03FDE5004C7042 /* ReferenceProxy in Frameworks */ = {isa = PBXBuildFile; fileRef = 20B7D10A1D3F74CD00B70F14 /* libRCTContacts.a */; }; 9ED2F45E1D9D535A00B36508 /* SF-UI-Text-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 9ED2F45D1D9D52DD00B36508 /* SF-UI-Text-Regular.otf */; }; 9ED2F45F1D9D535A00B36508 /* SF-UI-Text-Medium.otf in Resources */ = {isa = PBXBuildFile; fileRef = 9ED2F45C1D9D52C100B36508 /* SF-UI-Text-Medium.otf */; }; 9ED2F4611D9D579900B36508 /* SF-UI-Text-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 9ED2F4601D9D577B00B36508 /* SF-UI-Text-Bold.otf */; }; - 9EE89E271E03FCB7007D3C25 /* libSplashScreen.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B24FC7F21DE718EF00D694FF /* libSplashScreen.a */; }; - 9EE89E2D1E03FD9F007D3C25 /* libimageCropPicker.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 20A5C9531D927137002C4965 /* libimageCropPicker.a */; }; + 9EE470511ED0079E0048FD10 /* Mapbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9EE470501ED0079E0048FD10 /* Mapbox.framework */; }; + 9EE470521ED0079E0048FD10 /* Mapbox.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9EE470501ED0079E0048FD10 /* Mapbox.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 9EE470541ED007E10048FD10 /* ReferenceProxy in Frameworks */ = {isa = PBXBuildFile; fileRef = 9EE4702F1ED0071A0048FD10 /* libRCTMapboxGL.a */; }; + 9EE89E271E03FCB7007D3C25 /* ReferenceProxy in Frameworks */ = {isa = PBXBuildFile; fileRef = B24FC7F21DE718EF00D694FF /* libSplashScreen.a */; }; + 9EE89E2D1E03FD9F007D3C25 /* ReferenceProxy in Frameworks */ = {isa = PBXBuildFile; fileRef = 20A5C9531D927137002C4965 /* libimageCropPicker.a */; }; A6AF670051B842249D520C7B /* Foundation.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7ED174A34D7D42358313368B /* Foundation.ttf */; }; AE97D4B08C9F4821B8E9C50B /* Ionicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 359B076A658B4FBAB5128B03 /* Ionicons.ttf */; }; B23B48FF1E76917B006D4535 /* RobotoMono-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B23B48FE1E76917B006D4535 /* RobotoMono-Medium.ttf */; }; B24FC7FD1DE7195700D694FF /* Social.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B24FC7FC1DE7195700D694FF /* Social.framework */; }; B24FC7FF1DE7195F00D694FF /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B24FC7FE1DE7195F00D694FF /* MessageUI.framework */; }; - B2A5F45C1DEC36BB00174F4D /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B2A5F4381DEC36B200174F4D /* libRCTAnimation.a */; }; - B2DEA0D01E49E33300FA28D6 /* libRCTHttpServer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B2DEA0B11E49E32000FA28D6 /* libRCTHttpServer.a */; }; + B2A5F45C1DEC36BB00174F4D /* ReferenceProxy in Frameworks */ = {isa = PBXBuildFile; fileRef = B2A5F4381DEC36B200174F4D /* libRCTAnimation.a */; }; + B2DEA0D01E49E33300FA28D6 /* ReferenceProxy in Frameworks */ = {isa = PBXBuildFile; fileRef = B2DEA0B11E49E32000FA28D6 /* libRCTHttpServer.a */; }; B2F2D1BC1D9D531B00B7B453 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B2F2D1BB1D9D531B00B7B453 /* Images.xcassets */; }; BA68A2377A20496EA737000D /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 4E586E1B0E544F64AA9F5BD1 /* libz.tbd */; }; C3EE9AEA6F77464588FBAA64 /* FontAwesome.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7B5870D9ED504F32B6A09C35 /* FontAwesome.ttf */; }; @@ -309,6 +312,20 @@ remoteGlobalIDString = 0974579A1D2A440A000D9368; remoteInfo = RCTWKWebView; }; + 9EE4702E1ED0071A0048FD10 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 9EE4701B1ED007190048FD10 /* RCTMapboxGL.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = C5DBB3401AF2EF2B00E611A9; + remoteInfo = RCTMapboxGL; + }; + 9EE470301ED0071A0048FD10 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 9EE4701B1ED007190048FD10 /* RCTMapboxGL.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = C5DBB34B1AF2EF2B00E611A9; + remoteInfo = RCTMapboxGLTests; + }; B24FC7F11DE718EF00D694FF /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 3A7EB0491DD9CABC00A4FCC8 /* SplashScreen.xcodeproj */; @@ -397,6 +414,7 @@ files = ( 20B6B6851D92C42600CC5C6A /* RSKImageCropper.framework in Embed Frameworks */, 20B6B6881D92C42600CC5C6A /* QBImagePicker.framework in Embed Frameworks */, + 9EE470521ED0079E0048FD10 /* Mapbox.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -467,6 +485,8 @@ 9ED2F45C1D9D52C100B36508 /* SF-UI-Text-Medium.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SF-UI-Text-Medium.otf"; sourceTree = ""; }; 9ED2F45D1D9D52DD00B36508 /* SF-UI-Text-Regular.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SF-UI-Text-Regular.otf"; sourceTree = ""; }; 9ED2F4601D9D577B00B36508 /* SF-UI-Text-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SF-UI-Text-Bold.otf"; sourceTree = ""; }; + 9EE4701B1ED007190048FD10 /* RCTMapboxGL.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTMapboxGL.xcodeproj; path = "../node_modules/react-native-mapbox-gl/ios/RCTMapboxGL.xcodeproj"; sourceTree = ""; }; + 9EE470501ED0079E0048FD10 /* Mapbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Mapbox.framework; sourceTree = ""; }; 9F1854E6D9654226B1FC8308 /* RCTCamera.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RCTCamera.xcodeproj; path = "../node_modules/react-native-camera/ios/RCTCamera.xcodeproj"; sourceTree = ""; }; ACA66A8F16CD2FE21F38738B /* Pods-StatusIm.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-StatusIm.debug.xcconfig"; path = "Pods/Target Support Files/Pods-StatusIm/Pods-StatusIm.debug.xcconfig"; sourceTree = ""; }; B23B48FE1E76917B006D4535 /* RobotoMono-Medium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "RobotoMono-Medium.ttf"; sourceTree = ""; }; @@ -501,29 +521,29 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - B2DEA0D01E49E33300FA28D6 /* libRCTHttpServer.a in Frameworks */, - 9EE89E271E03FCB7007D3C25 /* libSplashScreen.a in Frameworks */, - B2A5F45C1DEC36BB00174F4D /* libRCTAnimation.a in Frameworks */, + B2DEA0D01E49E33300FA28D6 /* ReferenceProxy in Frameworks */, + 9EE89E271E03FCB7007D3C25 /* ReferenceProxy in Frameworks */, + B2A5F45C1DEC36BB00174F4D /* ReferenceProxy in Frameworks */, B24FC7FF1DE7195F00D694FF /* MessageUI.framework in Frameworks */, B24FC7FD1DE7195700D694FF /* Social.framework in Frameworks */, - 9EE89E2D1E03FD9F007D3C25 /* libimageCropPicker.a in Frameworks */, - 9E3AB6D01D87DB2B008846B4 /* libReact-Native-Webview-Bridge.a in Frameworks */, + 9EE89E2D1E03FD9F007D3C25 /* ReferenceProxy in Frameworks */, + 9E3AB6D01D87DB2B008846B4 /* ReferenceProxy in Frameworks */, 20B6B6841D92C42600CC5C6A /* RSKImageCropper.framework in Frameworks */, CE4E31B31D8695250033ED64 /* Statusgo.framework in Frameworks */, - 20AB9EC61D47CC0300E7FD9C /* libRCTStatus.a in Frameworks */, + 20AB9EC61D47CC0300E7FD9C /* ReferenceProxy in Frameworks */, 20B6B6871D92C42600CC5C6A /* QBImagePicker.framework in Frameworks */, - 146834051AC3E58100842450 /* libReact.a in Frameworks */, - 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */, - 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */, - 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */, - 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */, - 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */, - 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */, - 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */, - 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */, - 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */, - 9E7C64731E03FDDE004C7042 /* libRCTCamera.a in Frameworks */, - 9E7C64991E03FDE5004C7042 /* libRCTContacts.a in Frameworks */, + 146834051AC3E58100842450 /* ReferenceProxy in Frameworks */, + 00C302E51ABCBA2D00DB3ED1 /* ReferenceProxy in Frameworks */, + 00C302E71ABCBA2D00DB3ED1 /* ReferenceProxy in Frameworks */, + 00C302E81ABCBA2D00DB3ED1 /* ReferenceProxy in Frameworks */, + 133E29F31AD74F7200F7D852 /* ReferenceProxy in Frameworks */, + 00C302E91ABCBA2D00DB3ED1 /* ReferenceProxy in Frameworks */, + 139105C61AF99C1200B5F7CC /* ReferenceProxy in Frameworks */, + 832341BD1AAA6AB300B99B32 /* ReferenceProxy in Frameworks */, + 00C302EA1ABCBA2D00DB3ED1 /* ReferenceProxy in Frameworks */, + 139FDEF61B0652A700C62182 /* ReferenceProxy in Frameworks */, + 9E7C64731E03FDDE004C7042 /* ReferenceProxy in Frameworks */, + 9E7C64991E03FDE5004C7042 /* ReferenceProxy in Frameworks */, FD4F213C3873473CB703B1D2 /* libRNFS.a in Frameworks */, 213311F38CA74CE280FD09AD /* libRNI18n.a in Frameworks */, 4D3D740D5EFA4F8592B048D7 /* libBVLinearGradient.a in Frameworks */, @@ -537,6 +557,9 @@ E0AD9E8F495A4907B65104BF /* libRCTImageResizer.a in Frameworks */, 5F8585D411844E5981B94F40 /* libRNInstabug.a in Frameworks */, 8E55E6877F950B81C8D711C5 /* libPods-StatusIm.a in Frameworks */, + EC8998BFE2C04023A860C065 /* libRNNetworkInfo.a in Frameworks */, + 9EE470511ED0079E0048FD10 /* Mapbox.framework in Frameworks */, + 9EE470541ED007E10048FD10 /* ReferenceProxy in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -846,6 +869,8 @@ 83CBB9F61A601CBA00E9B192 = { isa = PBXGroup; children = ( + 9EE470501ED0079E0048FD10 /* Mapbox.framework */, + 9EE4701B1ED007190048FD10 /* RCTMapboxGL.xcodeproj */, 20B6B6861D92C42600CC5C6A /* QBImagePicker.framework */, 20B6B6831D92C42600CC5C6A /* RSKImageCropper.framework */, 13B07FAE1A68108700A75B9A /* StatusIm */, @@ -893,6 +918,15 @@ name = Products; sourceTree = ""; }; + 9EE4701C1ED007190048FD10 /* Products */ = { + isa = PBXGroup; + children = ( + 9EE4702F1ED0071A0048FD10 /* libRCTMapboxGL.a */, + 9EE470311ED0071A0048FD10 /* RCTMapboxGLTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; A97BA941B2FB44B4B66EE6D3 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -979,6 +1013,7 @@ 9E71BA90038083A3D24E18E9 /* [CP] Embed Pods Frameworks */, E883D1F9B22B8292CC879292 /* [CP] Copy Pods Resources */, E3914A731DF919ED00EBB515 /* Run Script */, + 9EE470531ED007A80048FD10 /* ShellScript */, ); buildRules = ( ); @@ -1065,6 +1100,10 @@ ProductGroup = 78C398B11ACF4ADC00677621 /* Products */; ProjectRef = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; }, + { + ProductGroup = 9EE4701C1ED007190048FD10 /* Products */; + ProjectRef = 9EE4701B1ED007190048FD10 /* RCTMapboxGL.xcodeproj */; + }, { ProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */; ProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; @@ -1378,6 +1417,20 @@ remoteRef = 9EC013781E06FB1900155B5C /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 9EE4702F1ED0071A0048FD10 /* libRCTMapboxGL.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTMapboxGL.a; + remoteRef = 9EE4702E1ED0071A0048FD10 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 9EE470311ED0071A0048FD10 /* RCTMapboxGLTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = RCTMapboxGLTests.xctest; + remoteRef = 9EE470301ED0071A0048FD10 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; B24FC7F21DE718EF00D694FF /* libSplashScreen.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; @@ -1583,6 +1636,19 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-StatusIm/Pods-StatusIm-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; + 9EE470531ED007A80048FD10 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Mapbox.framework/strip-frameworks.sh\""; + }; E3914A731DF919ED00EBB515 /* Run Script */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -1736,6 +1802,7 @@ "$(PROJECT_DIR)/../modules/react-native-status/ios/RCTStatus", "$(inherited)", "$(PROJECT_DIR)/Pods/**", + "$(PROJECT_DIR)", ); HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -1784,6 +1851,7 @@ "$(PROJECT_DIR)/../modules/react-native-status/ios/RCTStatus", "$(inherited)", "$(PROJECT_DIR)/Pods/**", + "$(PROJECT_DIR)", ); HEADER_SEARCH_PATHS = ( "$(inherited)", diff --git a/package.json b/package.json index cdfe098dad..86f893d207 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "react-native-invertible-scroll-view": "^1.0.0", "react-native-level-fs": "^2.0.1", "react-native-linear-gradient": "2.0.0", + "react-native-mapbox-gl": "github:mapbox/react-native-mapbox-gl#master", "react-native-orientation": "github:youennPennarun/react-native-orientation", "react-native-popup-menu": "^0.7.1", "react-native-qrcode": "^0.2.2", diff --git a/postinstall.sh b/postinstall.sh index a933169e9e..767631bc02 100755 --- a/postinstall.sh +++ b/postinstall.sh @@ -5,10 +5,11 @@ ./node_modules/.bin/rn-nodeify --install "assert,zlib,buffer,inherits,console,constants,crypto,dns,domain,events,http,https,os,path,process,punycode,querystring,fs,stream,string_decoder,timers,tty,url,util,net,vm" --hack; npm install --save react@16.0.0-alpha.6; npm install --save react-native-tcp@3.2.1; +ln -s ../node_modules/react-native-mapbox-gl/ios/Mapbox.framework ios; # symlink for re-natal if ! [ -f re-natal ]; then ln -s ./node_modules/re-natal/index.js re-natal; else echo "re-natal exists" -fi \ No newline at end of file +fi diff --git a/resources/status.js b/resources/status.js index 4ccc139e45..a917113788 100644 --- a/resources/status.js +++ b/resources/status.js @@ -40,6 +40,7 @@ Command.prototype.create = function (com) { this.request = com.request; this["execute-immediately?"] = com.executeImmediately; this["sequential-params"] = com.sequentialParams; + this["hide-send-button"] = com.hideSendButton; this.addToCatalog(); return this; @@ -186,6 +187,30 @@ function chooseContact(titleText, botDbKey, argumentIndex) { }]; } +function droppedPin() { + return ['dropped-pin']; +} + +function placesSearch() { + return ['places-search']; +} + +function currentLocationMap() { + return ['current-location-map']; +} + +function currentLocation() { + return ['current-location']; +} + +function placesNearby() { + return ['places-nearby']; +} + +function separator() { + return ['separator']; +} + var status = { command: function (h) { var command = new Command(); @@ -245,7 +270,13 @@ var status = { bridgedWebView: bridgedWebView, chooseContact: chooseContact, subscribe: subscribe, - dispatch: dispatch + dispatch: dispatch, + droppedPin: droppedPin, + placesSearch: placesSearch, + currentLocationMap: currentLocationMap, + currentLocation: currentLocation, + placesNearby: placesNearby, + separator: separator }, showSuggestions: function (view) { statusSignals.showSuggestions(JSON.stringify(view)); diff --git a/src/status_im/chat/handlers/input.cljs b/src/status_im/chat/handlers/input.cljs index dc522c63bf..83f1287e9b 100644 --- a/src/status_im/chat/handlers/input.cljs +++ b/src/status_im/chat/handlers/input.cljs @@ -26,13 +26,13 @@ ;; TODO(alwx): need to understand the need in this #_(if-let [{command :command} (input-model/selected-chat-command db chat-id text)] - (let [{old-args :args} (input-model/selected-chat-command db chat-id) - text-splitted (input-model/split-command-args text) - new-input-text (input-model/make-input-text text-splitted old-args)] - (assoc-in db [:chats chat-id :input-text] new-input-text)) - (->> text - (input-model/text->emoji) - (assoc-in db [:chats chat-id :input-text])))))) + (let [{old-args :args} (input-model/selected-chat-command db chat-id) + text-splitted (input-model/split-command-args text) + new-input-text (input-model/make-input-text text-splitted old-args)] + (assoc-in db [:chats chat-id :input-text] new-input-text)) + (->> text + (input-model/text->emoji) + (assoc-in db [:chats chat-id :input-text])))))) (handlers/register-handler :add-to-chat-input-text @@ -108,6 +108,16 @@ (catch :default e (log/debug "Cannot focus the reference")))))) +(handlers/register-handler + :chat-input-blur + (handlers/side-effect! + (fn [{:keys [current-chat-id chat-ui-props] :as db} [_ ref]] + (try + (when-let [ref (get-in chat-ui-props [current-chat-id ref])] + (.blur ref)) + (catch :default e + (log/debug "Cannot blur the reference")))))) + (handlers/register-handler :update-suggestions (fn [{:keys [current-chat-id] :as db} [_ chat-id text]] @@ -146,9 +156,11 @@ args (-> (get-in db [:chats current-chat-id :input-text]) (input-model/split-command-args) (rest)) + seq-arg (get-in db [:chats current-chat-id :seq-argument-input-text]) to (get-in db [:contacts current-chat-id :address]) - params {:parameters {:args args - :bot-db bot-db} + params {:parameters {:args args + :bot-db bot-db + :seq-arg seq-arg} :context (merge {:data data :from current-account-id :to to} @@ -382,16 +394,23 @@ :select-prev-argument (handlers/side-effect! (fn [{:keys [chat-ui-props current-chat-id] :as db} _] - (let [arg-pos (input-model/argument-position db current-chat-id)] - (when (pos? arg-pos) - (let [input-text (get-in db [:chats current-chat-id :input-text]) - new-sel (->> (input-model/split-command-args input-text) - (take (inc arg-pos)) - (input-model/join-command-args) - (count)) - ref (get-in chat-ui-props [current-chat-id :input-ref])] - (.setNativeProps ref (clj->js {:selection {:start new-sel :end new-sel}})) - (dispatch [:update-text-selection new-sel]))))))) + (let [input-text (get-in db [:chats current-chat-id :input-text]) + command (input-model/selected-chat-command db current-chat-id input-text)] + (if (get-in command [:command :sequential-params]) + (do + (dispatch [:set-command-argument [0 "" false]]) + (dispatch [:set-chat-seq-arg-input-text ""]) + (dispatch [:load-chat-parameter-box (:command command)])) + (let [arg-pos (input-model/argument-position db current-chat-id)] + (when (pos? arg-pos) + (let [input-text (get-in db [:chats current-chat-id :input-text]) + new-sel (->> (input-model/split-command-args input-text) + (take (inc arg-pos)) + (input-model/join-command-args) + (count)) + ref (get-in chat-ui-props [current-chat-id :input-ref])] + (.setNativeProps ref (clj->js {:selection {:start new-sel :end new-sel}})) + (dispatch [:update-text-selection new-sel]))))))))) (handlers/register-handler :select-next-argument diff --git a/src/status_im/chat/views/geolocation/styles.cljs b/src/status_im/chat/views/geolocation/styles.cljs new file mode 100644 index 0000000000..3d3d48d25d --- /dev/null +++ b/src/status_im/chat/views/geolocation/styles.cljs @@ -0,0 +1,91 @@ +(ns status-im.chat.views.geolocation.styles + (:require [status-im.components.styles :as common])) + +(defn place-item-container [address] + {:height (if address 74 52) + :justify-content :center}) + +(def place-item-title-container + {:flex-direction :row + :align-items :center}) + +(defn place-item-circle-icon [pin-style] + (merge {:border-color common/color-light-blue + :border-width 3 + :border-radius 7 + :height 13 + :width 13} + pin-style)) + +(def black-pin + {:border-color common/color-black}) + +(def place-item-title + {:font-size 15 + :padding-left 9 + :padding-right 16 + :color common/color-black + :line-height 23}) + +(def place-item-address + {:font-size 15 + :padding-left 22 + :padding-right 16 + :color common/color-black + :line-height 23}) + +(def map-activity-indicator-container + {:align-items :center + :justify-content :center + :height 100}) + +(def map-view + {:height 100}) + +(def location-container + {:margin-top 11 + :margin-horizontal 16}) + +(def location-container-title + {:font-size 14 + :color common/color-gray4 + :letter-spacing -0.2}) + +(def location-container-title-count + (merge location-container-title + {:opacity 0.5})) + +(def separator + {:height 1 + :opacity 0.5 + :background-color "#c1c7cbb7"}) + +(def item-separator + (merge separator + {:margin-left 22})) + +(def pin-container + {:position :absolute + :top 0 + :right 0 + :bottom 0 + :left 0 + :justify-content :center + :align-items :center + :pointer-events :none}) + +(def pin-component + {:align-items :center}) + +(def pin-circle + {:border-color common/color-black + :background-color common/color-white + :border-width 3 + :border-radius 7 + :height 13 + :width 13}) + +(def pin-leg + {:height 7 + :width 2 + :background-color common/color-black}) \ No newline at end of file diff --git a/src/status_im/chat/views/geolocation/views.cljs b/src/status_im/chat/views/geolocation/views.cljs new file mode 100644 index 0000000000..e025fec19e --- /dev/null +++ b/src/status_im/chat/views/geolocation/views.cljs @@ -0,0 +1,173 @@ +(ns status-im.chat.views.geolocation.views + (:require-macros [status-im.utils.views :refer [defview letsubs]] + [reagent.ratom :refer [reaction]]) + (:require [status-im.components.react :refer [view image text touchable-highlight]] + [reagent.core :as r] + [goog.string :as gstr] + [status-im.utils.utils :refer [http-get]] + [status-im.utils.types :refer [json->clj]] + [status-im.chat.views.geolocation.styles :as st] + [status-im.components.mapbox :refer [mapview]] + [re-frame.core :refer [dispatch subscribe]] + [status-im.i18n :refer [label]] + [status-im.components.react :as components])) + +(def mapbox-api "https://api.mapbox.com/geocoding/v5/mapbox.places/") +(def access-token "pk.eyJ1Ijoic3RhdHVzaW0iLCJhIjoiY2oydmtnZjRrMDA3czMzcW9kemR4N2lxayJ9.Rz8L6xdHBjfO8cR3CDf3Cw") + +(defn get-places [coords cur-loc-geocoded & [poi?]] + (let [{:keys [latitude longitude]} coords] + (when (and latitude longitude) + (http-get (str mapbox-api longitude "," latitude + ".json?" (when poi? "types=poi&") "access_token=" access-token) + #(reset! cur-loc-geocoded (json->clj %)) + #(reset! cur-loc-geocoded nil)) + true))) + +(defn place-item [{:keys [title address pin-style] [latitude longitude] :center}] + [touchable-highlight {:on-press #(do + (dispatch [:set-command-argument [0 + (str (or address title) + "&" latitude + "&" longitude) + false]]) + (dispatch [:send-seq-argument]))} + [view (st/place-item-container address) + [view st/place-item-title-container + [view (st/place-item-circle-icon pin-style)] + [text {:style st/place-item-title + :number-of-lines 1 + :font :medium} + title]] + (when address + [text {:style st/place-item-address + :number-of-lines 1} + address])]]) + +(defview current-location-map-view [] + (letsubs [geolocation [:get :geolocation] + command [:selected-chat-command]] + {:component-will-mount #(dispatch [:request-geolocation-update]) + :component-did-mount (fn [_] (js/setTimeout #(dispatch [:chat-input-focus :seq-input-ref]) 400))} + (let [coord (select-keys (:coords geolocation) [:latitude :longitude])] + [view + (if (not (empty? coord)) + [view + [mapview {:onTap #(do + (dispatch [:set-command-argument [0 "Dropped pin" false]]) + (dispatch [:set-chat-seq-arg-input-text "Dropped pin"]) + (dispatch [:chat-input-blur :seq-input-ref]) + (dispatch [:load-chat-parameter-box (:command command)])) + :initialCenterCoordinate coord + :showsUserLocation true + :initialZoomLevel 10 + :logoIsHidden true + :rotateEnabled false + :scrollEnabled false + :zoomEnabled false + :pitchEnabled false + :style st/map-view}]] + [view st/map-activity-indicator-container + [components/activity-indicator {:animating true}]])]))) + +(defn current-location-view [] + (let [geolocation (subscribe [:get :geolocation]) + cur-loc-geocoded (r/atom nil) + result (reaction (when @geolocation (get-places (:coords @geolocation) cur-loc-geocoded)))] + (r/create-class + {:component-will-mount #(dispatch [:request-geolocation-update]) + :render + (fn [] + (let [_ @result] + (when (and @cur-loc-geocoded (> (count (:features @cur-loc-geocoded)) 0)) + [view st/location-container + [text {:style st/location-container-title} + (label :t/your-current-location)] + (let [{:keys [place_name center] :as feature} (get-in @cur-loc-geocoded [:features 0])] + [place-item {:title (:text feature) :address place_name :center center}])])))}))) + +(defn places-nearby-view [] + (let [geolocation (subscribe [:get :geolocation]) + cur-loc-geocoded (r/atom nil) + result (reaction (when @geolocation (get-places (:coords @geolocation) cur-loc-geocoded true)))] + (r/create-class + {:component-will-mount #(dispatch [:request-geolocation-update]) + :render + (fn [] + (let [_ @result] + (when (and @cur-loc-geocoded (> (count (:features @cur-loc-geocoded)) 0)) + [view st/location-container + [text {:style st/location-container-title} + (label :t/places-nearby)] + (doall + (map (fn [{:keys [text place_name center] :as feature}] + ^{:key feature} + [view + [place-item {:title text :address place_name :center center}] + (when (not= feature (last (:features @cur-loc-geocoded))) + [view st/item-separator])]) + (:features @cur-loc-geocoded)))])))}))) + +(defn places-search [] + (let [seq-arg-input-text (subscribe [:chat :seq-argument-input-text]) + places (r/atom nil) + result (reaction (http-get (str mapbox-api @seq-arg-input-text + ".json?access_token=" access-token) + #(reset! places (json->clj %)) + #(reset! places nil)))] + (fn [] + (let [_ @result] + (when @places + (let [features-count (count (:features @places))] + [view st/location-container + [text {:style st/location-container-title} + (label :t/search-results) " " [text {:style st/location-container-title-count} features-count]] + (doall + (map (fn [{:keys [place_name center] :as feature}] + ^{:key feature} + [view + [place-item {:title place_name :center center}] + (when (not= feature (last (:features @places))) + [view st/item-separator])]) + (:features @places)))])))))) + +(defn dropped-pin [] + (let [geolocation @(subscribe [:get :geolocation]) + pin-location (r/atom nil) + pin-geolocation (r/atom nil) + pin-nearby (r/atom nil) + result (reaction (when @pin-location (get-places @pin-location pin-geolocation))) + result2 (reaction (when @pin-location (get-places @pin-location pin-nearby true)))] + (fn [] + (let [_ @result _ @result2] + [view + [view + [mapview {:initial-center-coordinate (select-keys (:coords geolocation) [:latitude :longitude]) + :initialZoomLevel 10 + :onRegionDidChange #(reset! pin-location (js->clj % :keywordize-keys true)) + :logoIsHidden true + :style {:height 265}}] + [view st/pin-container + [view st/pin-component + [view st/pin-circle] + [view st/pin-leg]]]] + (when (and @pin-geolocation (> (count (:features @pin-geolocation)) 0)) + [view + [view st/location-container + [text {:style st/location-container-title} + (label :t/dropped-pin)] + (let [{:keys [place_name center] :as feature} (get-in @pin-geolocation [:features 0])] + [place-item {:title place_name :pin-style st/black-pin :center center}])] + [view st/separator]]) + (when (and @pin-nearby (> (count (:features @pin-nearby)) 0)) + [view st/location-container + [text {:style st/location-container-title} + (label :t/places-nearby)] + (doall + (map (fn [{:keys [text place_name center] :as feature}] + ^{:key feature} + [view + [place-item {:title text :address place_name :center center}] + (when (not= feature (last (:features @pin-nearby))) + [view st/item-separator])]) + (:features @pin-nearby)))])])))) \ No newline at end of file diff --git a/src/status_im/chat/views/input/input.cljs b/src/status_im/chat/views/input/input.cljs index 086437f432..ba1a6bcbb1 100644 --- a/src/status_im/chat/views/input/input.cljs +++ b/src/status_im/chat/views/input/input.cljs @@ -167,13 +167,15 @@ :style (style/seq-input-text command-width) :default-value (or @seq-arg-input-text "") :on-change-text #(do (dispatch [:set-chat-seq-arg-input-text %]) + (dispatch [:load-chat-parameter-box (:command @command)]) (dispatch [:set-chat-ui-props {:validation-messages nil}])) :placeholder placeholder :blur-on-submit false :editable (not @sending-in-progress?) :on-focus #(dispatch [:set-chat-ui-props {:show-emoji? false}]) :on-submit-editing (fn [] - (when-not (str/blank? @seq-arg-input-text) + (when-not (or (str/blank? @seq-arg-input-text) + (get-in @command [:command :hide-send-button])) (dispatch [:send-seq-argument])) (js/setTimeout #(dispatch [:chat-input-focus :seq-input-ref]) @@ -222,7 +224,8 @@ input-text [:chat :input-text] seq-arg-input-text [:chat :seq-argument-input-text] result-box [:chat-ui-props :result-box]] - (let [single-line-input? (:singleLineInput result-box)] + (let [single-line-input? (:singleLineInput result-box) + {:keys [hide-send-button sequential-params]} (:command selected-command)] [view style/input-container [input-view {:anim-margin anim-margin :single-line-input? single-line-input?}] @@ -230,8 +233,9 @@ [input-actions/input-actions-view] (when (and (not (str/blank? input-text)) (or (not selected-command) - (some #{:complete :less-than-needed} [command-completion]))) - [touchable-highlight {:on-press #(if (get-in selected-command [:command :sequential-params]) + (some #{:complete :less-than-needed} [command-completion])) + (not hide-send-button)) + [touchable-highlight {:on-press #(if sequential-params (do (when-not (str/blank? seq-arg-input-text) (dispatch [:send-seq-argument])) diff --git a/src/status_im/chat/views/message/message.cljs b/src/status_im/chat/views/message/message.cljs index 09a2f9f635..9db8b5cdeb 100644 --- a/src/status_im/chat/views/message/message.cljs +++ b/src/status_im/chat/views/message/message.cljs @@ -16,7 +16,7 @@ dismiss-keyboard!]] [status-im.components.animation :as anim] [status-im.chat.constants :as chat-consts] - [status-im.components.list-selection :refer [share browse]] + [status-im.components.list-selection :refer [share browse share-or-open-map]] [status-im.chat.views.message.request-message :refer [message-content-command-request]] [status-im.chat.styles.message.message :as st] [status-im.chat.styles.message.command-pill :as pill-st] @@ -139,11 +139,12 @@ {:keys [name type] icon-path :icon} command] [view st/content-command-view - [view st/command-container - [view (pill-st/pill command) - [text {:style pill-st/pill-text - :font :default} - (str (if (= :command type) chat-consts/command-char "?") name)]]] + (when (:color command) + [view st/command-container + [view (pill-st/pill command) + [text {:style pill-st/pill-text + :font :default} + (str (if (= :command type) chat-consts/command-char "?") name)]]]) (when icon-path [view st/command-image-view [icon icon-path st/command-image]]) @@ -402,8 +403,12 @@ (fn [{:keys [outgoing group-chat content-type content] :as message}] [message-container message [touchable-highlight {:on-press #(when platform/ios? (dismiss-keyboard!)) - :on-long-press (when (= content-type text-content-type) - #(share content (label :t/message)))} + :on-long-press #(cond (= content-type text-content-type) + (share content (label :t/message)) + (and (= content-type content-type-command) (= "location" (:content-command content))) + (let [params (str/split (get-in content [:params "address"]) #"&") + latlong (rest params)] + (share-or-open-map (first params) (first latlong) (second latlong))))} [view (let [incoming-group (and group-chat (not outgoing))] [message-content message-body (merge message diff --git a/src/status_im/commands/handlers/jail.cljs b/src/status_im/commands/handlers/jail.cljs index 1e8cacd3ab..ea4c9c78ec 100644 --- a/src/status_im/commands/handlers/jail.cljs +++ b/src/status_im/commands/handlers/jail.cljs @@ -38,12 +38,12 @@ (defn suggestions-handler! [{:keys [contacts chats] :as db} [{:keys [chat-id default-db command parameter-index result]}]] - (let [{:keys [markup] :as returned} (get-in result [:result :returned]) + (let [{:keys [markup height] :as returned} (get-in result [:result :returned]) contains-markup? (contains? returned :markup) path (if command [:chats chat-id :parameter-boxes (:name command) parameter-index] [:chats chat-id :parameter-boxes :message])] - (dispatch [:choose-predefined-expandable-height :parameter-box :default]) + (dispatch [:choose-predefined-expandable-height :parameter-box (or (keyword height) :default)]) (when (and contains-markup? (not= (get-in db path) markup)) (dispatch [:set-in path returned]) (when default-db diff --git a/src/status_im/commands/handlers/loading.cljs b/src/status_im/commands/handlers/loading.cljs index 4d2f3f3c10..7f23ee370b 100644 --- a/src/status_im/commands/handlers/loading.cljs +++ b/src/status_im/commands/handlers/loading.cljs @@ -140,16 +140,16 @@ commands' (filter-commands account chat commands) responses' (filter-commands account chat responses) global-command (:global commands') - commands'' (apply dissoc commands' [:init :global]) + commands'' (each-merge (apply dissoc commands' [:init :global]) + {:type :command + :owner-id id}) mailman-commands (get-mailmans-commands db)] (cond-> db true (update-in [:contacts id] assoc :commands-loaded? true - :commands (-> (merge mailman-commands commands'') - (each-merge {:type :command - :owner-id id})) + :commands (merge mailman-commands commands'') :responses (each-merge responses' {:type :response :owner-id id}) :subscriptions subscriptions) diff --git a/src/status_im/commands/utils.cljs b/src/status_im/commands/utils.cljs index 25df4c3c82..4cfa1a38ed 100644 --- a/src/status_im/commands/utils.cljs +++ b/src/status_im/commands/utils.cljs @@ -1,12 +1,13 @@ (ns status-im.commands.utils (:require [clojure.set :as set] [clojure.walk :as w] - [re-frame.core :refer [dispatch trim-v debug]] + [re-frame.core :refer [dispatch trim-v]] [status-im.components.react :as components] [status-im.chat.views.input.web-view :as chat-web-view] [status-im.chat.views.input.validation-messages :as chat-validation-messages] [status-im.chat.views.choosers.choose-contact :as choose-contact] [status-im.components.qr-code :as qr] + [status-im.chat.views.geolocation.views :as geolocation] [status-im.utils.handlers :refer [register-handler]] [taoensso.timbre :as log])) @@ -14,21 +15,30 @@ (when-not (= json "undefined") (js->clj (.parse js/JSON json) :keywordize-keys true))) +(defn parameter-box-separator [] + [components/view {:height 1 :background-color "#c1c7cbb7" :opacity 0.5}]) + (def elements - {:view components/view - :text components/text - :text-input components/text-input - :image components/image - :qr-code qr/qr-code - :linking components/linking - :slider components/slider - :scroll-view components/scroll-view - :web-view components/web-view - :touchable components/touchable-highlight - :activity-indicator components/activity-indicator - :bridged-web-view chat-web-view/bridged-web-view - :validation-message chat-validation-messages/validation-message - :choose-contact choose-contact/choose-contact-view}) + {:view components/view + :text components/text + :text-input components/text-input + :image components/image + :qr-code qr/qr-code + :linking components/linking + :slider components/slider + :scroll-view components/scroll-view + :web-view components/web-view + :touchable components/touchable-highlight + :activity-indicator components/activity-indicator + :bridged-web-view chat-web-view/bridged-web-view + :validation-message chat-validation-messages/validation-message + :choose-contact choose-contact/choose-contact-view + :separator parameter-box-separator + :current-location-map geolocation/current-location-map-view + :current-location geolocation/current-location-view + :places-nearby geolocation/places-nearby-view + :places-search geolocation/places-search + :dropped-pin geolocation/dropped-pin}) (defn get-element [n] (elements (keyword (.toLowerCase n)))) @@ -40,7 +50,7 @@ #(dispatch [:suggestions-event! (update event 0 keyword) %]))) (defn check-events [m] - (let [ks (set (keys m)) + (let [ks (set (keys m)) evs (set/intersection ks events)] (reduce #(update %1 %2 wrap-event) m evs))) diff --git a/src/status_im/components/list_selection.cljs b/src/status_im/components/list_selection.cljs index cf9b9d4caf..a4d4ae69a6 100644 --- a/src/status_im/components/list_selection.cljs +++ b/src/status_im/components/list_selection.cljs @@ -3,7 +3,7 @@ [status-im.components.react :refer [copy-to-clipboard sharing linking]] - [status-im.utils.platform :refer [platform-specific]] + [status-im.utils.platform :refer [platform-specific ios?]] [status-im.i18n :refer [label]])) (defn open-share [content] @@ -44,3 +44,18 @@ :default)) :cancel-text (label :t/browsing-cancel)}))) +(defn share-or-open-map [address lat lng] + (let [list-selection-fn (:list-selection-fn platform-specific)] + (list-selection-fn {:title (label :t/location) + :options [{:text (label :t/sharing-copy-to-clipboard-address)} + {:text (label :t/sharing-copy-to-clipboard-coordinates)} + {:text (label :t/open-map)}] + :callback (fn [index] + (case index + 0 (copy-to-clipboard address) + 1 (copy-to-clipboard (str lng "," lat)) + 2 (.openURL linking (if ios? + (str "http://maps.apple.com/?ll=" lng "," lat) + (str "geo:" lng "," lat))) + :default)) + :cancel-text (label :t/cancel)}))) diff --git a/src/status_im/components/mapbox.cljs b/src/status_im/components/mapbox.cljs new file mode 100644 index 0000000000..0b021ff556 --- /dev/null +++ b/src/status_im/components/mapbox.cljs @@ -0,0 +1,23 @@ +(ns status-im.components.mapbox + (:require [reagent.core :as r] + [status-im.components.styles :as common] + [status-im.i18n :refer [label]] + [status-im.utils.platform :refer [platform-specific ios?]] + [re-frame.core :refer [dispatch]] + [status-im.components.react :refer [view touchable-highlight text]])) + +(def react-native-mapbox-gl (js/require "react-native-mapbox-gl")) + +(defn get-property [name] + (aget react-native-mapbox-gl name)) + +(defn adapt-class [class] + (when class + (r/adapt-react-class class))) + +(defn get-class [name] + (adapt-class (get-property name))) + +(.setAccessToken react-native-mapbox-gl "pk.eyJ1Ijoic3RhdHVzaW0iLCJhIjoiY2oydmtnZjRrMDA3czMzcW9kemR4N2lxayJ9.Rz8L6xdHBjfO8cR3CDf3Cw") + +(def mapview (get-class "MapView")) diff --git a/src/status_im/contacts/handlers.cljs b/src/status_im/contacts/handlers.cljs index 3a54d7bda3..c2c83db60a 100644 --- a/src/status_im/contacts/handlers.cljs +++ b/src/status_im/contacts/handlers.cljs @@ -260,9 +260,9 @@ :bot-url bot-url :global-command global-command :dapp-hash dapp-hash}] - (dispatch [:add-contacts [contact]]) - (when bot-url - (dispatch [:load-commands! id'])))))))))) + (dispatch [:add-contacts [contact]]))) + (when bot-url + (dispatch [:load-commands! id'])))))))) (register-handler :add-contacts diff --git a/src/status_im/handlers.cljs b/src/status_im/handlers.cljs index 222c83d51c..49b321a975 100644 --- a/src/status_im/handlers.cljs +++ b/src/status_im/handlers.cljs @@ -199,6 +199,32 @@ then else)))) +(register-handler :request-geolocation-update + (u/side-effect! + (fn [_ _] + (dispatch [:request-permissions [:geolocation] + (fn [] + (let [watch-id (atom nil)] + (.getCurrentPosition + navigator.geolocation + #(dispatch [:update-geolocation (js->clj % :keywordize-keys true)]) + #(dispatch [:update-geolocation (js->clj % :keywordize-keys true)]) + (clj->js {:enableHighAccuracy true :timeout 20000 :maximumAge 1000})) + (when p/android? + (reset! watch-id + (.watchPosition + navigator.geolocation + #(do + (.clearWatch + navigator.geolocation + @watch-id) + (dispatch [:update-geolocation (js->clj % :keywordize-keys true)])))) + (dispatch [:set-in [:debug :watch-id] @watch-id]))))])))) + +(register-handler :update-geolocation + (fn [db [_ geolocation]] + (assoc db :geolocation geolocation))) + ;; -- User data -------------------------------------------------------------- (register-handler :load-user-phone-number (fn [db [_]] diff --git a/src/status_im/translations/en.cljs b/src/status_im/translations/en.cljs index 17999ee94d..943e35dd59 100644 --- a/src/status_im/translations/en.cljs +++ b/src/status_im/translations/en.cljs @@ -211,6 +211,16 @@ :chat-send-eth-to "{{amount}} ETH to {{chat-name}}" :chat-send-eth-from "{{amount}} ETH from {{chat-name}}" + ;location command + :your-current-location "Your current location" + :places-nearby "Places nearby" + :search-results "Search results" + :dropped-pin "Dropped pin" + :location "Location" + :open-map "Open Map" + :sharing-copy-to-clipboard-address "Copy the Address" + :sharing-copy-to-clipboard-coordinates "Copy coordinates" + ;new-group :group-chat-name "Chat name" :empty-group-chat-name "Please enter a name" diff --git a/src/status_im/utils/views.clj b/src/status_im/utils/views.clj index 28527a512a..92b7120e33 100644 --- a/src/status_im/utils/views.clj +++ b/src/status_im/utils/views.clj @@ -34,7 +34,7 @@ [form `(deref ~sym)])) pairs))])) -(defmacro letsubs [args body]) +(defmacro letsubs [args & body]) (defmacro defview [n params & rest-body]