I have the following setup:
A rails server, which has a basic user login using the devise gem.
There I’ve defined a User
and a Device
model where each user may have multiple devices via a has_many
relationship on the user. The Device
model has a single attribute- a token which I would use to send push notifications to using the rpush gem.
A react-native app using the react-native-webview package and the react-native-firebase/messaging package. The thing is, that I don’t really know how to send the firebase device token to the server correctly.
What I’ve got so far is that I’ve overridden the rails devise registrations#new
view to the following:
<%= 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" %>
Here you can see that I’ve added a hidden field for the token, which I insert in the react-native app once I’ve landed on the /users/sign_up
page using the following code:
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;
And this actually works fine, but once I got around to testing the rails app and I tried setting the hidden field value in the specs I got an error:
Selenium::WebDriver::Error::ElementNotInteractableError:
element not interactable
Which made me think that this approach is “hacky”. So does anybody know a better approach?