Rails 带有 react-native-webview 应用程序和 firebase 消息传递的 turbolinks 服务器

Rails turbolinks server with react-native-webview app and firebase messaging

我有以下设置:

一个 rails 服务器,它有一个使用 devise gem 的基本用户登录。 我在那里定义了一个 User 和一个 Device 模型,其中每个用户可以通过用户的 has_many 关系拥有多个设备。 Device 模型有一个单一的属性——一个令牌,我会用它来发送推送通知给 rpush gem.

使用 react-native-webview package and the react-native-firebase/messaging 包的 react-native 应用程序。问题是,我真的不知道如何将 firebase 设备令牌正确发送到服务器。

到目前为止,我已经将 rails 设计 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" %>

在这里你可以看到我为令牌添加了一个隐藏字段,当我使用以下代码进入 /users/sign_up 页面后,我将其插入到 react-native 应用程序中:

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

这让我觉得这种方法很“hacky”。那么有人知道更好的方法吗?

对于同样遇到这个问题的人,我早就想通了,但还没来得及回答这个问题。

我开始使用 firebase 通道,而不是必须在服务器上注册每个设备,而只是将消息发送到通道,这样我就不需要在服务器上保存每个设备 ID。像这样:

  • 对于 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