Skip to content

Commit

Permalink
UI elements (#232)
Browse files Browse the repository at this point in the history
* Make near optional for autocomplete

* autocomplete ui pass 1

* cleanup

* Change export style

* onSelect for autocomplete

* v1 of Map UI element

* Cleanup helper.js

* 2 line address format, better styling

* More autocomplete styling

* Map, minimal configuration

* Add README

* Readme nits

* Merge styles, use formattedAddress for the second part of address

* Put createStyleUrl behind a useeffect

* Match Beryl's figma (sans icons)

* Get the icons working

* Fix debounce logic

* Bump to 3.7.6-beta.1

* Pop into full screen modal

* Back icon & done return key

* Change styling to new styling

* Clean up file

* Fix bug from keyboard handling interupting the flatlist selection

* Live changes with Beryl

* Cleanup

* Nit

* Make maplibre-react-native optional

* Conditionally use react native maplibre

* Fixes

* Map fixes

* Update autocomplete instructions

* Map fixes

* Correct docs

* Add map customization

* Typo

* Map updates for Android

* Autocomplete cleanup

* Version bumps

* Bump android version
  • Loading branch information
lmeier authored Jul 27, 2023
1 parent 34f3741 commit b1f879c
Show file tree
Hide file tree
Showing 19 changed files with 2,078 additions and 27 deletions.
4 changes: 2 additions & 2 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ android {
minSdkVersion 16
targetSdkVersion 31
versionCode 1
versionName '3.8.1'
versionName '3.8.2'
}
lintOptions {
abortOnError false
Expand All @@ -45,5 +45,5 @@ repositories {

dependencies {
api 'com.facebook.react:react-native:+'
api 'io.radar:sdk:3.8.4'
api 'io.radar:sdk:3.8.5'
}
18 changes: 18 additions & 0 deletions android/src/main/java/io/radar/react/RNRadarModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,24 @@ public void setAnonymousTrackingEnabled(boolean enabled) {
Radar.setAnonymousTrackingEnabled(enabled);
}

@ReactMethod
public void getHost(final Promise promise) {
if (promise == null) {
return;
}

promise.resolve(Radar.getHost());
}

@ReactMethod
public void getPublishableKey(final Promise promise) {
if (promise == null) {
return;
}

promise.resolve(Radar.getPublishableKey());
}

@ReactMethod
public void getPermissionsStatus(final Promise promise) {
if (promise == null) {
Expand Down
1 change: 1 addition & 0 deletions ios/RNRadar.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#import <RadarSDK/RadarSDK.h>
#import <RadarSDK/RadarSettings.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>

Expand Down
25 changes: 15 additions & 10 deletions ios/RNRadar.m
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,14 @@ - (void)didLogMessage:(NSString *)message {
[Radar setAnonymousTrackingEnabled:enabled];
}

RCT_EXPORT_METHOD(getHost:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
resolve([RadarSettings host]);
}

RCT_EXPORT_METHOD(getPublishableKey:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
resolve([RadarSettings publishableKey]);
}

RCT_REMAP_METHOD(getPermissionsStatus, getPermissionsStatusWithResolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
NSString *statusStr;
Expand Down Expand Up @@ -729,19 +737,16 @@ - (void)didLogMessage:(NSString *)message {
}

NSDictionary *nearDict = optionsDict[@"near"];
if (nearDict == nil || ![nearDict isKindOfClass:[NSDictionary class]]) {
if (reject) {
reject([Radar stringForStatus:RadarStatusErrorBadRequest], [Radar stringForStatus:RadarStatusErrorBadRequest], nil);
}

return;
CLLocation *near = nil;
if (nearDict && [nearDict isKindOfClass:[NSDictionary class]]) {
double latitude = [RCTConvert double:nearDict[@"latitude"]];
double longitude = [RCTConvert double:nearDict[@"longitude"]];
NSDate *timestamp = [NSDate new];
near = [[CLLocation alloc] initWithCoordinate:CLLocationCoordinate2DMake(latitude, longitude) altitude:-1 horizontalAccuracy:5 verticalAccuracy:-1 timestamp:timestamp];
}


NSString *query = optionsDict[@"query"];
double latitude = [RCTConvert double:nearDict[@"latitude"]];
double longitude = [RCTConvert double:nearDict[@"longitude"]];
NSDate *timestamp = [NSDate new];
CLLocation *near = [[CLLocation alloc] initWithCoordinate:CLLocationCoordinate2DMake(latitude, longitude) altitude:-1 horizontalAccuracy:5 verticalAccuracy:-1 timestamp:timestamp];
NSNumber *limitNumber = optionsDict[@"limit"];
int limit;
if (limitNumber != nil && [limitNumber isKindOfClass:[NSNumber class]]) {
Expand Down
15 changes: 15 additions & 0 deletions js/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { NativeModules, Platform } from 'react-native';

if (!NativeModules.RNRadar && (Platform.OS === 'ios' || Platform.OS === 'android')) {
throw new Error('NativeModules.RNRadar is undefined');
}

const getHost = () => (
NativeModules.RNRadar.getHost()
);

const getPublishableKey = () => (
NativeModules.RNRadar.getPublishableKey()
);

export { getHost, getPublishableKey };
11 changes: 7 additions & 4 deletions js/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { Platform } from 'react-native';

let module = {};
if (Platform.OS === 'web')
module = require('./index.web').default;
else
if (Platform.OS === 'web') {
module = require('./index.web').default;
} else {
module = require('./index.native').default;

}
export default module;

export { default as Autocomplete } from './ui/autocomplete';
export { default as Map } from './ui/map';
157 changes: 157 additions & 0 deletions js/ui/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
## Example usage

We provide UI elements for autocomplete & maps to make building easy.

### Adding an address autocomplete

Adding an address search autocomplete is straightforward. Our `<Autocomplete>` element is comprised of a TextInput and Flatlist with the results.

The example below provides optional `location` and `onSelect` props to the component. Providing a location will improve autocomplete result quality. Without it, the API utilizes the IP address location to rank results.

```
import { View } from 'react-native';
import { useState, useEffect } from 'react';
import Radar, { Autocomplete } from 'react-native-radar';
export default function App() {
const [location, setLocation] = useState(null);
useEffect(() => {
Radar.initialize('prj_live_pk_...');
Radar.trackOnce().then((result) => {
setLocation({
latitude: result.location.latitude,
longitude: result.location.longitude,
});
});
}, []);
const onSelect = (selectedAddress) => {
// Do something with the selected address
}
return (
<View style={{ marginTop: 50}}>
<Autocomplete location={location} onSelect={onSelect} />
</View>
);
}
```

### Adding a map

If you're using the Map element, you'll need to install [Maplibre React Native](https://github.com/maplibre/maplibre-react-native), which `react-native-radar` has an optional peer dependency.
```
npm install @maplibre/maplibre-react-native
```

Then make sure to complete required [platform specific installation steps](https://github.com/maplibre/maplibre-react-native/blob/main/docs/GettingStarted.md#review-platform-specific-info) as well.


We've taken care of linking the map tile server to the map, so all you need to do is make sure you've initialized the Radar SDK and use `<Map>`. Here's a minimal example:

```
import {View} from 'react-native';
import Radar, { Map } from 'react-native-radar';
import MapLibreGL from '@maplibre/maplibre-react-native';
// A quirk of Map Libre requires us to set their deprecated access token to null
MapLibreGL.setAccessToken(null);
export default function App() {
useEffect(() => {
Radar.initialize('prj_live_pk_...');
}, []);
return (
<View style={{ width: '100%', height: '95%'}}>
<Map />
</View>
);
}
```

And here's how you might add a custom pin to the map and control the camera:
```
// ... rest of your file
const [cameraConfig, setCameraConfig] = useState({
triggerKey: Date.now(),
centerCoordinate: [-73.9911, 40.7342],
animationMode: 'flyTo',
animationDuration: 600,
zoomLevel: 12,
});
const onRegionDidChange = (event) => {
// handle region change
}
const mapOptions = {
onRegionDidChange: onRegionDidChange,
}
const onSelect = (selectedAddress) => {
// Do something with the selected address
}
const pointsCollection = {
type: "FeatureCollection",
features: [{
type: "Feature",
properties: {
_id: '123',
},
geometry: {
type: "Point",
coordinates: [-73.9911, 40.7342]
}
}]
};
const onPressIcon = (event) => {
// do something with the symbol, such as scrolling to the geofence
// associated with the icon in the list
}
return (
<View style={{ width: '100%', marginTop: '10%', height: '90%'}}>
<Map mapOptions={mapOptions}>
<MapLibreGL.Camera
{...cameraConfig}
/>
<MapLibreGL.Images
images={{
icon: require('./assets/marker.png'),
}}
/>
<MapLibreGL.ShapeSource
id="points"
shape={pointsCollection}
onPress={onPressIcon}
>
<MapLibreGL.SymbolLayer
id="symbol"
style={{
iconImage: 'icon',
iconSize: [
'interpolate',
['linear'],
['zoom'],
0, 0.2, // Adjust the icon size for zoom level 0
12, 0.4, // Adjust the icon size for zoom level 12
22, 0.8, // Adjust the icon size for zoom level 22
],
iconAllowOverlap: true,
}}
/>
</MapLibreGL.ShapeSource>
</Map>
</View>
);
```
Loading

0 comments on commit b1f879c

Please sign in to comment.