Сервер turbolinks Rails с приложением react-native-webview и сообщениями firebase

# #ruby-on-rails #firebase #react-native #devise #firebase-cloud-messaging

Вопрос:

У меня есть следующая настройка:

Сервер rails, который имеет базовый логин пользователя с использованием драгоценного камня devise. Там я определил User и Device модель, в которой у каждого пользователя может быть несколько устройств через has_many связь с пользователем. Device Модель имеет один атрибут — токен, который я бы использовал для отправки push-уведомлений с помощью драгоценного камня rpush.


Приложение react-native, использующее пакет react-native-webview и пакет react-native-firebase/обмена сообщениями. Дело в том, что я действительно не знаю, как правильно отправить маркер устройства firebase на сервер.

Что у меня есть до сих пор, так это то, что я переопределил registrations#new представление о разработке рельсов следующим образом:

 <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
  <%= render "devise/shared/error_messages", resource: resource %>

  <div class="field">
    <%= f.label :email %><br />
    <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
  </div>

  <div class="field">
    <%= f.label :password %>
    <% if @minimum_password_length %>
    <em>(<%= @minimum_password_length %> characters minimum)</em>
    <% end %><br />
    <%= f.password_field :password, autocomplete: "new-password" %>
  </div>

  <div class="field">
    <%= f.label :password_confirmation %><br />
    <%= f.password_field :password_confirmation, autocomplete: "new-password" %>
  </div>

  <%= f.fields_for :devices, @user do |f| %>
    <div class="field">
      <%= f.hidden_field :token, {value: nil} %>
    </div>
  <% end %>

  <div class="actions">
    <%= f.submit "Sign up" %>
  </div>
<% end %>

<%= render "devise/shared/links" %>

 

Здесь вы можете видеть, что я добавил скрытое поле для токена, которое я вставляю в приложение react-native, как только я попал на /users/sign_up страницу, используя следующий код:

 import React, { useState } from "react";
import { WebView } from "react-native-webview";
import messaging from '@react-native-firebase/messaging';

const MyWebView = () => {
  const [uri] = useState("http://10.0.2.2:3000");
  const [webViewRef, setWebViewRef] = useState(null);

  const postMessageThatTurbolinksLoadOccured = `
    document.addEventListener("turbolinks:load", () => {
      window.ReactNativeWebView.postMessage("turbolinks:load");
    })`;

  function onMessage(obj) {
    const event = obj.nativeEvent;

    if(event.data != "turbolinks:load") return;

    if(event.url==`${uri}/users/sign_in` || event.url==`${uri}/users/sign_up`) {
      messaging().getToken().then(token => {
        const insertTokenCode = `
          document.getElementById("user_devices_attributes_0_token").value = "${token}";
        `;
        webViewRef.injectJavaScript(insertTokenCode);
      })
    }
  }

  // This is here only temporarily
  messaging().onMessage(remoteMessage => {
    Alert.alert(remoteMessage.notification.body);
  })

  return (
    <WebView
      ref={setWebViewRef}
      source={{ uri: `${uri}/users/sign_in` }}
      injectedJavaScript={postMessageThatTurbolinksLoadOccured}
      onMessage={onMessage}
    />
    );
};

export default MyWebView;
 

И это на самом деле работает нормально, но как только я приступил к тестированию приложения rails и попытался установить значение скрытого поля в спецификациях, я получил ошибку:

      Selenium::WebDriver::Error::ElementNotInteractableError:
       element not interactable
 

Что заставило меня подумать, что этот подход «халтурный». Итак, кто-нибудь знает лучший подход?

Ответ №1:

Для всех,кто также сталкивается с этой проблемой, я понял это некоторое время назад, но не удосужился ответить на этот вопрос.

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

  • Для приложения react-native
 import { Alert } from "react-native";
import { WebView } from "react-native-webview";
import messaging from '@react-native-firebase/messaging';

const MyView = () => {
  const messagingInstance = messaging()
  messagingInstance.subscribeToTopic("welcome").then(() => {
    messagingInstance.onMessage(message => {
      if(message.from == "/topics/welcome") {
        Alert.alert(message.notification.body);
        messagingInstance.unsubscribeFromTopic("welcome");
      }
    });
  });

  return (
    <WebView
      source={{ uri: `http://10.0.2.2:3000` }}
    />
    );
};
export default MyView;
 
  • на сервере у меня есть задание, которое отправляет уведомление по каналу с помощью fcm gem
 class SendGreetingPushNotificationJob < ApplicationJob
  queue_as :default

  def perform(*args)
    fcm = FCM.new(Rails.application.config.FIREBASE_SERVER_KEY)
    fcm.send_to_topic("welcome", notification: {body: "Welcome to MyApp!"})
  end
end