Рубрики
Без рубрики

Создание приложения для бронирования езды с реактивным родным и толкателем

В этом руководстве вы узнаете, как использовать функции геолокации в Nect Nate и Pusher, чтобы создать приложение для бронирования поездки.

Автор оригинала: Wern Ancheta.

В этом руководстве мы создадим приложение для бронирования езды с реактивным нативным и толкателем. Приложение, которое мы создадим, будет похоже на популярные приложения для бронирования езды, такие как Uber, Lyft или Grab. React Nature будет использоваться для создания приложения для Android для драйвера, так и для пассажира. Толкатель будет использоваться для связи в реальном времени между двумя.

Что вы будете создавать

Как и любое другое приложение для бронирования поездки там, будет приложение драйвера и пассажирское приложение. Пассажирское приложение будет использоваться для бронирования езды, а приложение водителя просто получает любой запрос, который поступает из пассажирского приложения. Для целей консистенции мы просто ссылаемся на приложение как «GRABCLONE».

Поток приложения

Клон, который мы собираемся создать, в значительной степени, в значительной степени одинаковый поток, что и любое приложение для бронирования поездки: пассажирские книги Ride → приложение ищет драйвер → драйвер принимает запрос → водитель подбирает пассажиров → драйверы до назначения → Пассажир платит водителю.

Здесь я просто хочу показать вам, как этот процесс будет выглядеть внутри приложения. Таким образом, у вас будет четкая картина того, что вы собираетесь создавать.

  1. Приложение определяет местоположение пользователя и показывает его на карте (Примечание: на данный момент необходимо включить GPS).

  2. От пассажирского приложения пользователь нажимает на «Бронировать поездку».

  3. Модаль откроется, что позволит пассажиру выбрать место, где они хотят пойти.

  4. Приложение просит пассажира подтвердить свой пункт назначения.

  5. После подтверждения приложение отправляет запрос на приложение драйвера, чтобы забрать пассажир. Анимация загрузки отображается, пока приложение ждет драйвера, чтобы принять запрос.

  6. Приложение водителя получает запрос. Отсюда водитель может принимать или отклонить запрос.

  7. Как только драйвер принимает запрос, данные драйвера отображаются в пассажирском приложении.

  8. Пассажирское приложение показывает текущее местоположение драйвера на карте.

  9. После того, как водитель находится в 50 метрах от местоположения пассажира, они увидят предупреждение о том, что водитель рядом.

  10. После того, как водитель находится в пределах 20 метров от местоположения пассажира, приложение водителя отправляет сообщение в приложение пассажира, что водитель практически там.

  11. После подъема пассажира водитель приводит к месту назначения.

  12. После того, как водитель находится в пределах 20 метров от своего назначения, приложение драйвера отправляет сообщение пассажирству, которое они очень близко к месту назначения.

На данный момент езды заканчиваются, и пассажир может забронировать другую поездку. Водитель также может принимать любую входящую проездную поездку.

Предпосылки

  • Учетная учетная записьРегистрация для учетной записи Pusher или Войти с вашим существующим Отказ После того, как вы создали учетную запись, создайте новое приложение → Выбрать «Реагирование» для интерфейсных технологий → Выберите «Node.js» для задней технологии.

    Затем нажмите на вкладку «Настройки приложения» и проверьте «Включить клиентские события». Это позволяет нам иметь приложение водителя и пассажира напрямую общаться друг с другом.

    Последнее, нажмите на «клавиши приложений» и скопируйте учетные данные. Если вы беспокоитесь о ценах, Pusher Plan Sandbox довольно щедрый Таким образом, вы можете использовать его бесплатно при тестировании приложения.

  • Установите Android Studio – Вам не нужна Android-студия, но она поставляется с Android SDK, который нам нужен. Google также больше не предлагает отдельная загрузка для него.

  • Установить React Native – Метод, который я рекомендую для этого, является строительным проектам народный путь. Когда вы находитесь на севере RACT NONED, нажмите «Строительные проекты с нативным кодом» вкладки и следуйте инструкциям там. Экспо Клиент отлично подходит для быстрого прототипов приложений, но он на самом деле не предлагает быстрый способ для того, чтобы мы проверили функции геолокации, которые нам нужны для этого приложения.

  • Genymotion – Для тестирования приложения водителя. Мы используем это вместо эмулятора Android по умолчанию, потому что он поставляется с инструментом моделирования GPS, который позволяет нам искать определенное местоположение и использовать его в качестве расположения эмулируемого устройства. Он использует Google Maps в качестве интерфейса, и вы также можете перемещать маркер. Это позволяет имитировать движущийся транспортный автомобиль. Как только Genymotion установлен, вам необходимо войти в свою учетную запись, чтобы добавить устройство. Для меня я установил Google Nexus 5X для тестирования.

  • Android-устройство – Это будет использоваться для тестирования пассажирского приложения. Обязательно проверьте версию вашего телефона Android. Если это что-то более низкое, чем 4,2, то вам нужно установить дополнительные пакеты через Android SDK Manager. Это связано с тем, что по умолчанию реагируйте нативные цели API версии 23 или выше. Это означает, что Android-версия вашего телефона должна быть версии 6.0, по крайней мере, или приложение не будет работать. Если вы установили Android Studio, вы можете получить доступ к менеджеру SDK, открывая Android Studio → Нажмите «Настроить» → Выбрать «SDK Manager». Затем под «платформами SDK» проверьте версии Android, которые вы хотите поддержать.

Пока вы там, нажмите на «SDK Tools» и убедитесь, что у вас также есть те же инструменты, установленные как мой:

Android SDK Tools
  • Дополнительный компьютер – Это необязательно. Я только что включал его здесь, потому что arent React может запустить приложение на одном устройстве или эмуляторе одновременно. Таким образом, вам необходимо сделать дополнительную работу, чтобы запускать два приложения, как вы увидите позже.

Создание сервера auth

Теперь пришло время загрязнеть наши руки. Во-первых, давайте будем работать на сервере auth. Это требуется, потому что мы будем отправлять События клиента Из приложения, клиентские события требуют, чтобы канал толкателя быть частным, а частные каналы имеют ограниченный доступ. Вот где находится сервер auth. Он служит способом для того, чтобы позвонить, чтобы знать, если пользователь, который пытается подключиться, действительно является зарегистрированным пользователем приложения.

Начните с установки зависимостей:

npm install --save express body-parser pusher

Далее создайте server.js Файл и добавьте следующий код:

var express = require('express');
var bodyParser = require('body-parser');
var Pusher = require('pusher');

var app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

var pusher = new Pusher({ // connect to pusher
  appId: process.env.APP_ID, 
  key: process.env.APP_KEY, 
  secret:  process.env.APP_SECRET,
  cluster: process.env.APP_CLUSTER, 
});

app.get('/', function(req, res){ // for testing if the server is running
  res.send('all is well...');
});

// for authenticating users
app.get("/pusher/auth", function(req, res) {
  var query = req.query;
  var socketId = query.socket_id;
  var channel = query.channel_name;
  var callback = query.callback;

  var auth = JSON.stringify(pusher.authenticate(socketId, channel));
  var cb = callback.replace(/\"/g,"") + "(" + auth + ");";

  res.set({
    "Content-Type": "application/javascript"
  });

  res.send(cb);
});

app.post('/pusher/auth', function(req, res) {
  var socketId = req.body.socket_id;
  var channel = req.body.channel_name;
  var auth = pusher.authenticate(socketId, channel);
  res.send(auth);
});

var port = process.env.PORT || 5000;
app.listen(port);

Я больше не собираюсь вносить в подробности, какой код выше, поскольку он уже объясняется в документах для Аутентификация пользователей Отказ Чтобы сохранить все возможное, я на самом деле не добавил код, чтобы проверить, существует ли пользователь в базе данных. Вы можете сделать это в /pusher/auth Конечная точка, проверяя, существует ли имя пользователя. Вот пример:

var users = ['luz', 'vi', 'minda'];
var username = req.body.username;

if(users.indexOf(username) !== -1){
  var socketId = req.body.socket_id;
  var channel = req.body.channel_name;
  var auth = pusher.authenticate(socketId, channel);
  res.send(auth);
}

// otherwise: return error

Не забудьте пройти в Имя пользователя При подключении к тошеру на стороне клиента позже.

Попробуйте запустить сервер, когда это сделано:

node server.js

Доступ http://localhost: 5000 В вашем браузере, чтобы увидеть, работает ли это.

Развертывание сервера auth

Поскольку pusher придется подключаться к серверу auth, он должен быть доступен из Интернета. Вы можете использовать сейчас .sh Развернуть сервер auth. Вы можете установить его со следующей командой:

npm install now

После установки вы можете теперь перейти к папке, где у вас есть server.js Файл и выполнить сейчас Отказ Вас попросят ввести свою электронную почту и подтвердить свою учетную запись.

Как только ваш аккаунт проверен, выполните следующее, чтобы добавить настройки приложения PUSHER в качестве переменных среды на свою учетную запись New.sh, чтобы вы могли использовать его изнутри сервера:

now secret add pusher_app_id YOUR_PUSHER_APP_ID
now secret add pusher_app_key YOUR_PUSHER_APP_KEY
now secret add pusher_app_secret YOUR_PUSHER_APP_SECRET
now secret add pusher_app_cluster YOUR_PUSHER_APP_CLUSTER

Далее разверните сервер при поставке секретных значений, которые вы добавили:

now -e APP_ID=@pusher_app_id -e APP_KEY=@pusher_app_key -e APP_SECRET=@pusher_app_secret APP_CLUSTER=@pusher_app_cluster

Это позволяет вам получить доступ к настройкам приложения Pusher изнутри сервера, как так:

process.env.APP_ID

URL-адрес развертывания, который Now.sh возвращает URL, который вы будете использовать позже, чтобы подключить приложение к серверу auth.

Создание приложения водителя

Теперь вы готовы начать создавать приложение водителя.

Во-первых, создайте новое реагирование нативное приложение:

react-native init grabDriver

Установка зависимостей

Как только это сделано, навигация внутри Грабдривер Справочник и установите библиотеки, которые нам понадобятся. Это включает в себя PUSHER-JS Для работы с толкателем Реагировать нативные карты Для отображения карты и Реагировать родной геокодирование Для координат обратного геокодирования к фактическому названию места:

npm install --save pusher-js react-native-maps react-native-geocoding

Как только все библиотеки установлены, реагируйте нативные карты, нуждаются в некоторых дополнительных шагах для его работы. Сначала связывает проектные ресурсы:

react-native link react-native-maps

Далее необходимо создать проект Google, получить ключ API из Консоль разработчика Google и включить Google Maps Android API и Google Maps Geocoding API Отказ После этого открыть Android \ app \ src \ main \ androidmanifest.xml Файл в вашем каталоге проекта. Под <Приложение> Теги, добавьте содержащий ключ API сервера.


    

Пока вы там, добавьте следующее ниже разрешений по умолчанию. Это позволяет проверить состояние сети и запрос данных геолокации с устройства.



Также убедитесь, что его нацеливание на ту же версию API, что и устройство, которое вы установили с помощью Genymotion. Как я уже говорил ранее, если его версия 23 или выше, вам действительно не нужно ничего делать, но если его ниже, то, он должен быть точно для работы приложения.


Наконец, поскольку мы будем в первую очередь использовать Genymotion для тестирования приложения водителя, вам нужно следовать за Инструкции здесь Отказ В случае, если ссылка становится сломанной, вот что вам нужно сделать:

  1. Посетить OpenGapps.org Отказ
  2. Выберите X86 в качестве платформы.
  3. Выберите версию Android, соответствующую вашему виртуальному устройству.
  4. Выберите Nano как вариант.
  5. Загрузите ZIP-файл.
  6. Перетащите установщик ZIP в новом виртуальном устройстве Genymotion (только 2.7.2 и выше).
  7. Следуйте за всплывающим инструкциям.

Нам нужно сделать это, потому что библиотека активных карт RACT Noated Maps в основном использует карты Google. Нам нужно добавить сервисы Google Play для этого для работы. В отличие от большинства телефонов Android, которые уже поставляются с этим, Genymotion не имеет его по умолчанию из-за причин интеллектуальной собственности. Таким образом, нам нужно вручную установить его.

Если вы читаете это некоторое время после того, как он был опубликован, обязательно проверьте Монтажные документы чтобы убедиться, что вы не пропустите ничего.

Кодирование приложения водителя

Теперь вы готовы начать кодировать приложение. Начните с открытия index.android.js Файл и замените код по умолчанию следующим образом:

import { AppRegistry } from 'react-native';
import App from './App';
AppRegistry.registerComponent('grabDriver', () => App);

Что это делает, импортирует Приложение Компонент, который является основным компонентом для приложения. Затем он зарегистрирован как компонент по умолчанию, поэтому он будет отображен на экране.

Далее создайте App.js Файл и импортировать вещи, которые нам нужны от нативного пакета raction:

import React, { Component } from 'react';
import {
  StyleSheet,
  Text,
  View,
  Alert
} from 'react-native';

Также импортируйте сторонние библиотеки, которые мы установили ранее:

import Pusher from 'pusher-js/react-native';
import MapView from 'react-native-maps';

import Geocoder from 'react-native-geocoding';
Geocoder.setApiKey('YOUR GOOGLE SERVER API KEY');

Наконец, импортировать помощники файл:

import { regionFrom, getLatLonDiffInMeters } from './helpers';

помощники.js Файл содержит следующее:

export function regionFrom(lat, lon, accuracy) {
  const oneDegreeOfLongitudeInMeters = 111.32 * 1000;
  const circumference = (40075 / 360) * 1000;

  const latDelta = accuracy * (1 / (Math.cos(lat) * circumference));
  const lonDelta = (accuracy / oneDegreeOfLongitudeInMeters);

  return {
    latitude: lat,
    longitude: lon,
    latitudeDelta: Math.max(0, latDelta),
    longitudeDelta: Math.max(0, lonDelta)
  };
} 

export function getLatLonDiffInMeters(lat1, lon1, lat2, lon2) {
  var R = 6371; // Radius of the earth in km
  var dLat = deg2rad(lat2-lat1);  // deg2rad below
  var dLon = deg2rad(lon2-lon1); 
  var a = 
    Math.sin(dLat/2) * Math.sin(dLat/2) +
    Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * 
    Math.sin(dLon/2) * Math.sin(dLon/2)
    ; 
  var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
  var d = R * c; // Distance in km
  return d * 1000;
}

function deg2rad(deg) {
  return deg * (Math.PI/180)
}

Эти функции используются для получения ценностей дельта-дельты широты и долготы, необходимые библиотекой NACT NOADY MAPS для отображения карты. Другая функция ( getLatlondiffinmers ) используется для определения расстояния в метрах между двумя координатами. Позже это позволит нам сообщить, что они уже рядом друг с другом или когда они возле своего назначения.

Далее создайте основной компонент приложений и заявляете, что заявляют о штатах по умолчанию:

export default class grabDriver extends Component {

  state = {
    passenger: null, // for storing the passenger info
    region: null, // for storing the current location of the driver
    accuracy: null, // for storing the accuracy of the location
    nearby_alert: false, // whether the nearby alert has already been issued
    has_passenger: false, // whether the driver has a passenger (once they agree to a request, this becomes true)
    has_ridden: false // whether the passenger has already ridden the vehicle
  }
}
// next: add constructor code

Внутри конструктора инициализируйте переменные, которые будут использоваться во всем приложении:

constructor() {
  super();

  this.available_drivers_channel = null; // this is where passengers will send a request to any available driver
  this.ride_channel = null; // the channel used for communicating the current location
  // for a specific ride. Channel name is the username of the passenger
 
  this.pusher = null; // the pusher client
}

// next: add code for connecting to pusher

Перед установленным компонентом подключитесь к серверу auth, который вы создали ранее. Обязательно замените значения для клавиши толкателя, AuthencePoint и кластер Отказ

componentWillMount() {
  this.pusher = new Pusher('YOUR PUSHER KEY', {
    authEndpoint: 'YOUR PUSHER AUTH SERVER ENDPOINT',
    cluster: 'YOUR PUSHER CLUSTER',
    encrypted: true
  });
  
  // next: add code for listening to passenger requests
}

Теперь, когда вы подключили к серверу auth, теперь вы можете начать слушать запросы, исходящие из пассажирского приложения. Первый шаг – подписаться на Частный канал Отказ Этот канал находится в том, что все пассажиры и водители подписываются на. В этом случае его используются водителями для прослушивания запросов на поездки. Это должно быть частным каналом, потому что События клиента Может быть вызван только на частных каналах и присутствиях из-за соображений безопасности. Вы знаете, что это частный канал из-за частные- приставка.

this.available_drivers_channel = this.pusher.subscribe('private-available-drivers'); // subscribe to "available-drivers" channel

Далее, слушай Клиент-водитель-запрос мероприятие. Вы знаете, что это клиентское событие из-за клиент- приставка. События клиента не требуют вмешательства сервера для работы, сообщения отправляются непосредственно от клиента клиенту. Вот почему нам нужен сервер auth, чтобы убедиться, что все пользователи, которые пытаются подключить, являются реальными пользователями приложения.

Возвращаясь к коду, мы слушаем клиентские события, позвонив привязывать Метод на канале, который мы подписались и передаваем имя события в качестве первого аргумента. Второй аргумент – это функция, которую вы хотите выполнить, как только это событие срабатывает с другого клиента (от кого-либо используя пассажирское приложение, чтобы запросить поездку). В приведенном ниже коде мы показываем сообщение о предупреждении, спрашиваемое водителю, если они хотят принять пассажир. Обратите внимание, что приложение предполагает, что может быть только один пассажир в любое время.

// listen to the "driver-request" event
this.available_drivers_channel.bind('client-driver-request', (passenger_data) => {
  
  if(!this.state.has_passenger){ // if the driver has currently no passenger
    // alert the driver that they have a request
    Alert.alert(
      "You got a passenger!", // alert title
      "Pickup: " + passenger_data.pickup.name + "\nDrop off: " + passenger_data.dropoff.name, // alert body
      [
        {
          text: "Later bro", // text for rejecting the request
          onPress: () => {
            console.log('Cancel Pressed');
          },
          style: 'cancel'
        },
        {
          text: 'Gotcha!', // text for accepting the request
          onPress: () => {
            // next: add code for when driver accepts the request
          }  
        },
      ],
      { cancelable: false } // no cancel button
    );

  }

});

Как только водитель соглашается забрать пассажир, мы подписываемся на свой частный канал. Этот канал зарезервирован только для связи между водителем и пассажиром, поэтому мы используем уникальное имя пользователя пассажира как часть имени канала.

this.ride_channel = this.pusher.subscribe('private-ride-' + passenger_data.username);

Не в отличие от Доступные водители Канал, нам нужно будет слушать, когда подписка фактически удалась ( Pusher: подписку_suceeded ) Прежде чем мы сделаем все остальное. Это потому, что мы собираемся немедленно вызвать событие клиента, которое будет отправлено пассажиру. Это событие ( клиент-драйвер-ответ ) – это событие рукопожатия, чтобы пассажир знал, что водитель, которого они отправили их запрос, все еще доступно. Если пассажир по-прежнему не ездил в то время, пассажирское приложение вызывает то же событие, чтобы водитель знал, что они все еще доступны для подбора. На данный момент мы обновляем состояние так, чтобы пользовательские пользователи менялись соответственно.

this.ride_channel.bind('pusher:subscription_succeeded', () => {
   // send a handshake event to the passenger
  this.ride_channel.trigger('client-driver-response', {
    response: 'yes' // yes, I'm available
  });
  
  // listen for the acknowledgement from the passenger
  this.ride_channel.bind('client-driver-response', (driver_response) => {
    
    if(driver_response.response == 'yes'){ // passenger says yes

      //passenger has no ride yet
      this.setState({
        has_passenger: true,
        passenger: {
          username: passenger_data.username,
          pickup: passenger_data.pickup,
          dropoff: passenger_data.dropoff
        }
      });
      
      // next: reverse-geocode the driver location to the actual name of the place
      
    }else{
      // alert that passenger already has a ride
      Alert.alert(
        "Too late bro!",
        "Another driver beat you to it.",
        [
          {
            text: 'Ok'
          },
        ],
        { cancelable: false }
      );
    }

  });

});

Далее мы используем библиотеку геокодирования, чтобы определить имя места, где водитель в настоящее время находится. За кулисами это использует API Google GeoCoding, и он обычно возвращает имя улицы. Как только мы получим ответ на ответ, мы вызываем найденный водитель Событие, чтобы сообщить пассажиру, что приложение нашло для них драйвер. Это содержит информацию драйвера, такую как имя и текущее местоположение.

Geocoder.getFromLatLng(this.state.region.latitude, this.state.region.longitude).then(
  (json) => {
    var address_component = json.results[0].address_components[0];
    
    // inform passenger that it has found a driver
    this.ride_channel.trigger('client-found-driver', { 
      driver: {
        name: 'John Smith'
      },
      location: { 
        name: address_component.long_name,
        latitude: this.state.region.latitude,
        longitude: this.state.region.longitude,
        accuracy: this.state.accuracy
      }
    });

  },
  (error) => {
    console.log('err geocoding: ', error);
  }
);  
// next: add componentDidMount code

Как только компонент установлен, мы используем РЕАКТИРОВАТЬ НАРОДНОГО Геолокации API смотреть за обновления местоположения. Функция, которую вы переходите к Вид на видимость Функция выполняется каждый раз, когда изменения местоположения.

componentDidMount() {
  this.watchId = navigator.geolocation.watchPosition(
    (position) => {
     
      var region = regionFrom(
        position.coords.latitude, 
        position.coords.longitude, 
        position.coords.accuracy
      );
      // update the UI
      this.setState({
        region: region,
        accuracy: position.coords.accuracy
      });
      
      if(this.state.has_passenger && this.state.passenger){
        // next: add code for sending driver's current location to passenger
      }
    },
    (error) => this.setState({ error: error.message }),
    { 
      enableHighAccuracy: true, // allows you to get the most accurate location
      timeout: 20000, // (milliseconds) in which the app has to wait for location before it throws an error
      maximumAge: 1000, // (milliseconds) if a previous location exists in the cache, how old for it to be considered acceptable 
      distanceFilter: 10 // (meters) how many meters the user has to move before a location update is triggered
    },
  );
}

Далее отправьте текущее местоположение водителя в пассажир. Это обновит UI в приложении пассажира, чтобы показать текущее местоположение драйвера. Вы увидите, как приложение пассажира связывается с этим событием позже, когда мы перейдем к кодированию пассажирского приложения.

this.ride_channel.trigger('client-driver-location', { 
  latitude: position.coords.latitude,
  longitude: position.coords.longitude,
  accuracy: position.coords.accuracy
});

Далее мы хотим сообщить как пассажир, так и водитель, что они уже рядом друг с другом. Для этого мы используем getlatlondiffinmers Функция из помощники.js файл для определения количества метров между пассажиром и водителем. Поскольку водитель уже получил местоположение пассажира, когда они приняли запрос, это только вопрос получения текущего местоположения водителя и передает его на Гетланондифторметры Функция, чтобы получить разницу в метрах. Оттуда мы просто информируем водитель или пассажир на основе количества метров. Позже вы увидите, как эти события принимаются в пассажирском приложении.

var diff_in_meter_pickup = getLatLonDiffInMeters(
  position.coords.latitude, position.coords.longitude, 
  this.state.passenger.pickup.latitude, this.state.passenger.pickup.longitude);

if(diff_in_meter_pickup <= 20){
  
  if(!this.state.has_ridden){
    // inform the passenger that the driver is very near
    this.ride_channel.trigger('client-driver-message', {
      type: 'near_pickup',
      title: 'Just a heads up',
      msg: 'Your driver is near, let your presence be known!'
    });

    /*
    we're going to go ahead and assume that the passenger has rode 
    the vehicle at this point
    */
    this.setState({
      has_ridden: true
    });
  }

}else if(diff_in_meter_pickup <= 50){
  
  if(!this.state.nearby_alert){
    this.setState({
      nearby_alert: true
    });
    /* 
    since the location updates every 10 meters, this alert will be triggered 
    at least five times unless we do this
    */
    Alert.alert(
      "Slow down",
      "Your passenger is just around the corner",
      [
        {
          text: 'Gotcha!'
        },
      ],
      { cancelable: false }
    );

  }

}

// next: add code for sending messages when near the destination

На данный момент мы предполагаем, что водитель поднял пассажир и что теперь они направляются в пункт назначения. Так что на этот раз мы получаем расстояние между текущим местоположением и точкой выпадания. После того, как они 20 метров до точки высадки, приложение водителя отправляет сообщение пассажиру, что они очень близки к месту назначения. Как только это сделано, мы предполагаем, что пассажир сойдет через несколько секунд. Поэтому мы развиваем события, которые мы слушаем и отписываемся от частного канала пассажира. Это эффективно режет соединение между приложением водителя и пассажира. Единственное соединение, которое остается открытым, это Доступные водители канал.

var diff_in_meter_dropoff = getLatLonDiffInMeters(
  position.coords.latitude, position.coords.longitude, 
  this.state.passenger.dropoff.latitude, this.state.passenger.dropoff.longitude);

if(diff_in_meter_dropoff <= 20){
  this.ride_channel.trigger('client-driver-message', {
    type: 'near_dropoff',
    title: "Brace yourself",
    msg: "You're very close to your destination. Please prepare your payment."
  });

  // unbind from passenger event
  this.ride_channel.unbind('client-driver-response');
  // unsubscribe from passenger channel 
  this.pusher.unsubscribe('private-ride-' + this.state.passenger.username);

  this.setState({
    passenger: null,
    has_passenger: false,
    has_ridden: false
  });

}

// next: add code for rendering the UI

UI для приложения водителя отображается только карта и маркеры для водителя и пассажира.

render() {
  return (
    
      {
        this.state.region && 
        
            
            {
              this.state.passenger && !this.state.has_ridden && 
              
            }
        
      }
    
  );
}
// next: add code when component unmounts

Перед компонентами размонтирует, мы остановим наблюдателя местоположения, позвонив на ClearWatch Метод:

componentWillUnmount() {
  navigator.geolocation.clearWatch(this.watchId);
} 

Наконец, добавьте стили:

const styles = StyleSheet.create({
  container: {
    ...StyleSheet.absoluteFillObject,
    justifyContent: 'flex-end',
    alignItems: 'center',
  },
  map: {
    ...StyleSheet.absoluteFillObject,
  },
});

Создание пассажирского приложения

Приложение пассажирского пассажира будет довольно похоже на приложение водителя, поэтому я больше не буду учитывать детали на деталях, которые похожи. Продолжай и создайте новое приложение:

react-native init grabClone

Установка зависимостей

Тебе также нужно будет установить те же библиотеки плюс еще пару:

npm install --save pusher-js react-native-geocoding github:geordasche/react-native-google-place-picker react-native-loading-spinner-overlay react-native-maps

Два других библиотеки Google Place Picker и Загрузка прядильника наложения Отказ Хотя мы использовали Вилка Из сборщика Google Place из-за проблемы совместимости с реактивными нативными картами, которые еще не были зафиксированы в оригинальном репо.

Поскольку мы установили те же библиотеки, вы можете вернуться в раздел, где мы выполнили дополнительную конфигурацию для работы библиотеки. Вернитесь здесь, как только вы сделаете это.

Затем сборщик Google Place также нужна дополнительная конфигурация для нее для работы. Во-первых, откройте Android/app/src/main/java/com/grabclone/mainapplication.java Файл и добавьте следующее ниже последнего импорта:

import com.reactlibrary.RNGooglePlacePickerPackage;

Добавьте библиотеку, которую вы просто импортировали под GetPackages () функция. Пока вы там, также убедитесь, что Mappackage () перечислены также.

protected List getPackages() {
  return Arrays.asList(
      new MainReactPackage(),
      new MapsPackage(),
      new RNGooglePlacePickerPackage() // <- add this
  );
}

Далее откройте Android/Settings.gradle Файл и добавьте это прямо над включить ': приложение' Директива:

include ':react-native-google-place-picker'
project(':react-native-google-place-picker').projectDir = new File(rootProject.projectDir,         '../node_modules/react-native-google-place-picker/android')

Пока вы там, также убедитесь, что ресурсы для реагирования нативных отображений также добавлены:

include ':react-native-maps'
project(':react-native-maps').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-maps/lib/android')

Далее откройте Android/App/build.gradle Файл и добавьте следующее под зависимости :

dependencies {
  compile project(':react-native-google-place-picker') // <- add this
}

Наконец, убедитесь, что реагирование нативные карты также скомпилированы:

compile project(':react-native-maps')

Кодирование пассажирского приложения

Открыть index.android.js Файл и добавьте следующее:

import { AppRegistry } from 'react-native';
import App from './App';
AppRegistry.registerComponent('grabClone', () => App);

Так же, как приложение водителя, он также использует App.js как основной компонент. Продолжай и импортируйте библиотеки. Это также использует то же самое помощники.js Файл, чтобы вы могли скопировать его из приложения водителя.

import React, { Component } from 'react';
import { StyleSheet, Text, View, Button, Alert } from 'react-native';

import Pusher from 'pusher-js/react-native';
import RNGooglePlacePicker from 'react-native-google-place-picker';
import Geocoder from 'react-native-geocoding';
import MapView from 'react-native-maps';
import Spinner from 'react-native-loading-spinner-overlay';

import { regionFrom, getLatLonDiffInMeters } from './helpers'; 

Geocoder.setApiKey('YOUR GOOGLE SERVER API KEY');

Создайте компонент и заявляйте состояния по умолчанию:

export default class App extends Component {
  state = {
    location: null, // current location of the passenger
    error: null, // for storing errors
    has_ride: false, // whether the passenger already has a driver which accepted their request
    destination: null, // for storing the destination / dropoff info
    driver: null, // the driver info
    origin: null, // for storing the location where the passenger booked a ride
    is_searching: false, // if the app is currently searching for a driver
    has_ridden: false // if the passenger has already been picked up by the driver
  };
  
  // next: add constructor code
}

Чтобы сохранить вещи простыми, мы объявляем имя пользователя пассажира в конструкторе. Мы также инициализируем каналы толкателя:

constructor() {
  super();
  this.username = 'wernancheta'; // the unique username of the passenger
  this.available_drivers_channel = null; // the pusher channel where all drivers and passengers are subscribed to
  this.user_ride_channel = null; // the pusher channel exclusive to the passenger and driver in a given ride
  this.bookRide = this.bookRide.bind(this); // bind the function for booking a ride
}
// next: add bookRide() function

Bookride () Функция выполняется, когда пользовательские краны на кнопке «Поездка книги». Это открывает сортирую место, которое позволяет пользователю выбирать пункт назначения. Как только местоположение будет выбрано, приложение отправляет запрос на проезд во всех драйверов. Как вы видели в приложении водителя ранее, это вызывает оповещение, чтобы показать в приложении водителя, который спрашивает, хочет ли водитель принять запрос или нет. В этот момент загрузчик будет продолжать вращаться до тех пор, пока водитель не примет запрос.

bookRide() {

  RNGooglePlacePicker.show((response) => {
    if(response.didCancel){
      console.log('User cancelled GooglePlacePicker');
    }else if(response.error){
      console.log('GooglePlacePicker Error: ', response.error);
    }else{
      this.setState({
        is_searching: true, // show the loader
        destination: response // update the destination, this is used in the UI to display the name of the place
      });
      
      // the pickup location / origin
      let pickup_data = {
        name: this.state.origin.name,
        latitude: this.state.location.latitude,
        longitude: this.state.location.longitude
      };
      
      // the dropoff / destination
      let dropoff_data = {
        name: response.name,
        latitude: response.latitude,
        longitude: response.longitude
      };
      
      // send a ride request to all drivers
      this.available_drivers_channel.trigger('client-driver-request', {
        username: this.username,
        pickup: pickup_data,
        dropoff: dropoff_data
      });

    }
  });
}
// next: add _setCurrentLocation() function

_SetcurrentLocation () Функция получает текущее местоположение пассажира. Обратите внимание, что здесь мы используем GetCurrentPosition () в отличие от Видимость () который мы использовали в приложении водителя ранее. Единственная разница между ними в том, что GetCurrentPosition () Только получают место один раз.

_setCurrentLocation() {

  navigator.geolocation.getCurrentPosition(
    (position) => {
      var region = regionFrom(
        position.coords.latitude, 
        position.coords.longitude, 
        position.coords.accuracy
      );
      
      // get the name of the place by supplying the coordinates      
      Geocoder.getFromLatLng(position.coords.latitude, position.coords.longitude).then(
        (json) => {
          var address_component = json.results[0].address_components[0];
          
          this.setState({
            origin: { // the passenger's current location
              name: address_component.long_name, // the name of the place
              latitude: position.coords.latitude,
              longitude: position.coords.longitude
            },
            location: region, // location to be used for the Map
            destination: null, 
            has_ride: false, 
            has_ridden: false,
            driver: null    
          });

        },
        (error) => {
          console.log('err geocoding: ', error);
        }
      );

    },
    (error) => this.setState({ error: error.message }),
    { enableHighAccuracy: false, timeout: 10000, maximumAge: 3000 },
  );

}

// next: add componentDidMount() function

Когда компонент монтируется, мы хотим установить текущее местоположение пассажира, подключиться к серверу AUTH AUTH и подписывайтесь на два канала: доступные драйверы и личный канал пассажира для связи только с водителем, в котором был отправлен запрос на поездки.

componentDidMount() {

  this._setCurrentLocation(); // set current location of the passenger
  // connect to the auth server
  var pusher = new Pusher('YOUR PUSHER API KEY', {
    authEndpoint: 'YOUR AUTH SERVER ENDPOINT',
    cluster: 'YOUR PUSHER CLUSTER',
    encrypted: true
  });
  
  // subscribe to the available drivers channel
  this.available_drivers_channel = pusher.subscribe('private-available-drivers');
  
  // subscribe to the passenger's private channel
  this.user_ride_channel = pusher.subscribe('private-ride-' + this.username);
  
  // next: add code for listening to handshake responses
  
}

Далее добавьте код для прослушивания ответа рукопожатия водителем. Это отправляется из приложения водителя, когда водитель принимает запрос на поездку. Это позволяет нам убедиться, что пассажир все еще ищет поездку. Если пассажир отвечает «да», то это единственное время, когда водитель отправляет свою информацию.

this.user_ride_channel.bind('client-driver-response', (data) => {
  let passenger_response = 'no';
  if(!this.state.has_ride){ // passenger is still looking for a ride
    passenger_response = 'yes';
  }

  // passenger responds to driver's response
  this.user_ride_channel.trigger('client-driver-response', {
    response: passenger_response
  });
});

// next: add listener for when a driver is found

Драйвер отправляет свою информацию, вызвав Найденный клиент-драйвер мероприятие. Как вы видели в приложении водителя ранее, это содержит имя драйвера, а также их текущее местоположение.

this.user_ride_channel.bind('client-found-driver', (data) => {
  // the driver's location info  
  let region = regionFrom(
    data.location.latitude,
    data.location.longitude,
    data.location.accuracy 
  );

  this.setState({
    has_ride: true, // passenger has already a ride
    is_searching: false, // stop the loading UI from spinning
    location: region, // display the driver's location in the map
    driver: { // the driver location details
      latitude: data.location.latitude,
      longitude: data.location.longitude,
      accuracy: data.location.accuracy
    }
  });
  
  // alert the passenger that a driver was found
  Alert.alert(
    "Orayt!",
    "We found you a driver. \nName: " + data.driver.name + "\nCurrent location: " + data.location.name,
    [
      {
        text: 'Sweet!'
      },
    ],
    { cancelable: false }
  );      
});
// next: add code for listening to driver's current location

На данный момент пассажир теперь может слушать изменения местоположения от водителя. Мы просто обновляем UI каждый раз, когда это событие срабатывает:

this.user_ride_channel.bind('client-driver-location', (data) => {
  let region = regionFrom(
    data.latitude,
    data.longitude,
    data.accuracy
  );
  
  // update the Map to display the current location of the driver
  this.setState({
    location: region, // the driver's location
    driver: {
      latitude: data.latitude,
      longitude: data.longitude
    }
  });

});

Далее – это событие, которое запускается на конкретные экземпляры. Это главная цель – отправить обновления пассажиру в отношении расположения водителя ( refer_pickup ), а также, когда они уже рядом с местоположением выпадают ( refer_dropoff ).

this.user_ride_channel.bind('client-driver-message', (data) => {
  if(data.type == 'near_pickup'){ // the driver is very near the pickup location
    // remove passenger marker since we assume that the passenger has rode the vehicle at this point
    this.setState({
      has_ridden: true 
    });
  }

  if(data.type == 'near_dropoff'){ // they're near the dropoff location
    this._setCurrentLocation(); // assume that the ride is over, so reset the UI to the current location of the passenger
  }
  
  // display the message sent from the driver app
  Alert.alert(
    data.title,
    data.msg,
    [
      {
        text: 'Aye sir!'
      },
    ],
    { cancelable: false }
  );        
});

// next: render the UI

Уэй, состоящий из загрузки спина (видимый только тогда, когда приложение ищет драйвер), заголовок, кнопка для бронирования поездки, местоположение пассажира ( происхождение ) и их назначение, а также карта, которая изначально отображается Текущее местоположение пользователя, а затем отображает текущее местоположение драйвера после того, как поездка была забронирована.

render() {

  return (
    
      
      
        GrabClone
      
      {
        !this.state.has_ride && 
        
          
        
      }
      
        
      {
        this.state.origin && this.state.destination &&
        
          Origin: 
          {this.state.origin.name}
         
          Destination: 
          {this.state.destination.name}
          
      }
      {
        this.state.location &&
        
          {
            this.state.origin && !this.state.has_ridden &&
            
          }
  
          {
            this.state.driver &&
            
          }
        
      }
      
    
  );
}

Наконец, добавьте стили:

const styles = StyleSheet.create({
  container: {
    ...StyleSheet.absoluteFillObject,
    justifyContent: 'flex-end'
  },
  form_container: {
    flex: 1,
    justifyContent: 'center',
    padding: 20
  },
  header: {
    padding: 20,
    backgroundColor: '#333',
  },
  header_text: {
    color: '#FFF',
    fontSize: 20,
    fontWeight: 'bold'
  },  
  origin_destination: {
    alignItems: 'center',
    padding: 10
  },
  label: {
    fontSize: 18
  },
  text: {
    fontSize: 18,
    fontWeight: 'bold',
  },
  map_container: {
    flex: 9
  },
  map: {
   flex: 1
  },
});

Запуск приложения

Теперь вы готовы запустить приложение. Как я уже упоминал в Предпосылки Раздел ранее, вы собираетесь необязательно нужны две машины, один для запуска каждого из приложений. Это позволит вам включить ведение журнала ( Console.log ) для обоих. Но если у вас есть только одна машина, то вам нужно запустить их в определенном порядке: Passenger App сначала и затем приложение водителя.

Продолжайте и подключите устройство Android к компьютеру и запустите следующую команду:

react-native run-android

Это будет компилировать, установить и запустить приложение на вашем устройстве. Как только его запустите, завершите наблюдение и отсоедините свое устройство с компьютера.

Компилировать приложение

Далее открывайте Genymotion и запустите устройство, которое вы установили ранее. На этот раз запустите приложение водителя. Как только приложение запускается, вы увидите пустой экран. Это нормально, потому что приложение нужно место для того, чтобы что-то сделать. Вы можете сделать это, нажав на «GPS», расположенный на верхней правой части эмулятора UI, затем включите GPS.

Включить GPS на Genymotion

Вы также можете нажать на кнопку карты и выберите определенное местоположение, если хотите:

Изменить местоположение

Как только вы выбрали местоположение, карта UI в приложении должна показать то же место, которое вы выбрали.

Далее вы можете теперь выполнить шаги на Поток приложения раздел ранее. Обратите внимание, что вы можете эмулировать движущееся транспортное средство, щелкнув вокруг интернет-интерфейса MENYMOTION. Если пассажир уже забронировал поездку, и водитель принял запрос, он должен начать обновлять как пассажирское приложение, так и приложение драйвера текущего местоположения драйвера.

Если вы используете две машины, то вы можете просто запустить React - родной Run-Android на обоих. Один должен быть подключен к вашему устройству, а другой должен открывать эмулятор геньмоции.

Заключение

Это оно! В этом руководстве вы узнали, как использовать Pusher, чтобы создать приложение для бронирования езды. Как вы видели, приложение, которое вы построили, довольно голые кости. Мы придерживаемся только для создания самых важных частей приложения для бронирования езды. Если вы хотите, вы можете добавить больше функций в приложение и, возможно, использовать его в своих собственных проектах. Вы можете найти исходный код, используемый в этом приложении на его Github Repo Отказ

Первоначально опубликовано на Pusher Blog Отказ