Detect unsubscribe from Action Cable on page change - ruby-on-rails

I'm building a chat room type system in Rails and I need to detect when the user leaves the channel (i.e. closes the page) on the server so I can update the list of active users. I've searched the documentation and examples on GitHub and can't seem to find the answer.
I've used SignalR in the past, which triggers a disconnect event, but I can't find the equivalent (if it indeed exists) in Rails 5.
I can see examples with a unsubscribed method in the channel class, but this is never triggered whether I navigate to a new page or close the browser completely.
EDIT: Not sure if this helps but I'm not using turbolinks.

It was an issue with beta 2 of Rails 5. I've tried it in the latest version - beta 4 and it is now triggered as expected.
Here's the github issue for reference: https://github.com/rails/rails/pull/23715

Try this
App.chatroom.disconnected()
=> yourChannel stopped streaming from chatroom #rails terminal
App.chatroom = App.cable.subscriptions.create({channel:
'yourChannel'}, {
disconnected: function () {
App.cable.subscriptions.remove(this)
this.perform('unsubscribed') #in the channel have a method 'unsubscibed' to update users
},
)}

Related

Seaside redirect for render not working on physical iOS devices

We're having a problem with physical iOS devices (works fine on Chrome virtual device) where a final Seaside redirect is not happening after an Azure SSO redirect.
I'd like to understand what triggers the Seaside redirect: I can see it in normal rendering, but I've never had to dig into it like this before.
When I log from a non-iOS device I see...
1 - WAApplication>>handleFiltered: application URL
self requestContext redirectTo: 'https://login.microsoftonline.com/...'
redirects back to our app URL with an access token
2 -WAApplication>>handleFiltered: application URL with MS access token & no _s & _k values
validate token with Azure
save user info in new WASession
finish render
3 - WAApplication>>handleFiltered: application URL with _s & _k plus callback values like: &2=2160&1=3840&3=false
WAResponse>>location: application URL with _s & new _k and no callback values
4 - WAApplication>>handleFiltered: application URL with _s & _K
finish render
With iOS step 3 does not happen; I'd like to know what triggers it normally.
Just to add to the fun, we have a two WAApplication registered. The default application fails on iOS, the other works fine. I can see no obvious difference between the two.
Thanks for any help (I'll cross post on the the Seaside mailing list and Discord)
Problem is caused by a seven year old method that used a window.location.href= to gather device characteristics, like screen size and touch support. Removing that href fixes the problem with iOS. Lots of other & cleaner ways to get the data.
Quick fix is to wrap the href with a setTimeout for 200 ms.

The embedded player shows "Please click here to watch this video on YouTube" to some users under unclearly circumstances

We use the YouTube embedded player on a hybrid app based on Cordova. The app has a lot of traffic worldwide.
The player displays the message "Please click here to watch this video on YouTube" to some users when they try to play any video under unclearly circumstances.
We do not see a clear pattern, it doesn't seem to depend on the user's country nor the restrictions of the video.
We have seen in the player code (https://www.youtube.com/yts/jsbin/player_ias-vflrnurMS/en_US/base.js) that the message is assigned to a constant called "TOO_MANY_REQUESTS_WITH_LINK" but there is no high volume of requests per user and the quota cannot be configured as in other Google APIs.
We follow the iFrame API reference:
https://developers.google.com/youtube/iframe_api_reference?hl=en
<iframe id="player" src="https://www.youtube.com/embed/M7lc1UVf-VE?autoplay=1&cc_load_policy=0&controls=0&disablekb=1&enablejsapi=1&fs=0&iv_load_policy=3&loop=0&modestbranding=1&playsinline=1&rel=0&showinfo=0&wmode=transparent&origin=XXX" frameborder="0"></iframe>
<script type="text/javascript">
var player,
scriptTag = document.createElement('script'),
firstScriptTag = document.getElementsByTagName('script')[0];
scriptTag.src = "https://www.youtube.com/iframe_api";
firstScriptTag.parentNode.insertBefore(scriptTag, firstScriptTag);
function onYouTubeIframeAPIReady() {
var options = JSON.parse('{"autoplay":1,"cc_load_policy":0,"controls":0,"disablekb":1,"enablejsapi":1,"fs":0,"iv_load_policy":3,"loop":0,"modestbranding":1,"playsinline":1,"rel":0,"showinfo":0,"wmode":"transparent","origin":"XXX"}');
if (!options.origin) {
options.origin = location.origin ||
location.protocol + '//' + location.hostname + (location.port ? ':' + location.port : '');
}
player = new YTRemoteVPlayer('player', '<%-videoId%>', options);
}
</script>
Does anyone know what may be happening and how to fix it?
Me and my teammates managed to reproduce that behaviour locally.
To reproduce that behaviour, all you need to do is, under the same IP Address, to load the youtube JS multiple times and for different videos. Although I cannot clarify the exact amount of times it must be done (we left some devices aside doing it), the behavior certainly happened.
As an attempt of a workaround for that problem, we accessed the https://web.archive.org/web website and fetched an old YouTube JS script (https://www.youtube.com/iframe_api). We tried to use that old script, but with no success. The message continued to appear =/.
I really do not know if there is much we, developers, can do about it, but wait for an YouTube statement and/or revision of recent changes on their end.
Update
There is an issue opened on Google's issue tracker website:
https://issuetracker.google.com/issues/143091693
Update
It looks like that YouTube have resolved that problem on their end. We'll keep monitoring :)
Update (November 5th 2019)
The problem is happening again.
Update (November 7th 2019)
I have opened a new issue on issuetracker:
- https://issuetracker.google.com/issues/144057800
I don't have a fix for it, but I can add more information about the problem, this way maybe someone can help us with this issue.
I am getting the same error with some users, and like you said there is no a clear pattern. My devices are working properly, but I did some tests with the help of a user of my app that is getting this error. Following the results:
Demo used in my tests: https://developers.google.com/youtube/youtube_player_demo
Test 1 - Open the iframe player api demo on device's browser - WORKED
Test 2 - Open the iframe player api demo into a webview on my app - FAIL
Test 3 - Open the iframe player api demo into a webview on my app using a VPN connection - WORKED
So, it appears to be a combination of APP + IP is blocked. I don't know the reason, and it will be very helpful if someone repeat these tests. I am trying to confirm it with another user, but it's a little hard to find someone willing to help.
UPDATE: a second user tried to use a VPN and it worked to him too.
UPDATE 2: a user with this problem told me that the videos are working properly again now. It appears to be a temporary block by Youtube on server side.

Is there a way to recognize, that the app was installed thru firebase dynamic link in didFinishLaunchingWithOptions?

I am implementing firebase dynamic links in my iOS app and I can already parse the link, redirect to AppStore etc. Now I want to distinguish the first run of the app, when user installs it from the dynamic link - I want to skip the intro and show him the content that is expected to be shown.
Is there some parameter, that I could catch in application(_:didFinishLaunchingWithOptions:) so I could say that it was launched thru the dynamic link?
The method application(_:continueUserActivity:userActivity:restorationHandler:) is called later, so the intro is already launched.
This case is difficult to test, because you have to have your app published on the AppStore.
You actually don't need to have the app published in the App Store for this to work — clicking a link, closing the App Store, and then installing an app build through Xcode (or any other beta distribution platform like TestFlight or Fabric) has exactly the same effect.
According to the Firebase docs, the method that is called for the first install is openURL (no, this makes no sense to me either). The continueUserActivity method is for Universal Links, and is only used if the app is already installed when a link is opened.
I am not aware of any way to detect when the app is opening for the first time after install from a 'deferred' link, but you could simply route directly to the shared content (skipping the intro) whenever a deep link is present. If a deep link is NOT present, show the regular intro.
Alternative Option
You could check out Branch.io (full disclosure: I'm on the Branch team). Amongst other things, Branch is a great, free drop-in replacement for Firebase Dynamic Links with a ton of additional functionality. Here is an example of all the parameters Branch returns immediately in didFinishLaunchingWithOptions:
{
"branch_view_enabled" = 0;
"browser_fingerprint_id" = "<null>";
data = "{
\"+is_first_session\":false,
\"+clicked_branch_link\":true,
\"+match_guaranteed\":true,
\"$canonical_identifier\":\"room/OrangeOak\",
\"$exp_date\":0,
\"$identity_id\":\"308073965526600507\",
\"$og_title\":\"Orange Oak\",
\"$one_time_use\":false,
\"$publicly_indexable\":1,
\"room_name\":\"Orange Oak\", // this is a custom param, of which you may have an unlimited number
\"~channel\":\"pasteboard\",
\"~creation_source\":3,
\"~feature\":\"sharing\",
\"~id\":\"319180030632948530\",
\"+click_timestamp\":1477336707,
\"~referring_link\":\"https://branchmaps.app.link/qTLPNAJ0Jx\"
}";
"device_fingerprint_id" = 308073965409112574;
"identity_id" = 308073965526600507;
link = "https://branchmaps.app.link/?%24identity_id=308073965526600507";
"session_id" = 319180164046538734;
}
You can read more about these parameters on the Branch documentation here.
Hmm... as far as I'm aware, there's not really anything you can catch in the application:(_:didFinishLaunchingWithOptions) phase that would let you know the app was being opened by a dynamic link. You're going to have to wait until the continueUserActivity call, as you mentioned.
That said, FIRDynamicLinks.dynamicLinks()?.handleUniversalLink returns a boolean value nearly instantly, so you should be able to take advantage of that to short-circuit your into animation without it being a bad user experience. The callback itself might not happen until several milliseconds later, depending on if it's a shortened dynamic link (which requires a network call) or an expanded one (which doesn't).

Add message to chat component(react.js) via websockets

Context:
I am developing a Ruby On Rails application and I started using React.js to manage my javascript components.
My application provides a facebook-like chat: several chats are displayed at the bottom of the page.
Problem
I have a ChatList component that renders the chats. A chat is made of its messages and its form. When this form is submitted, an AJAX call is made to the server to POST the message and the message is added to the current chat.
this.setState({messages: this.state.messages.concat([newMessage])});
The server then broadcast Javascript code to the receiver.
This is where I'm stuck. How can I add the message to the correct chat? How can I select my React.js component and change its 'props'?
When I was not using react, I used to broadcast this code to the other user:
$("#chat-<%= chat.id %>").append("<%= message.content" %>);
I guess I have to find a way to select the React component (the chat instance) and change its property "messages". How?
Thank you for your help! :)
EDIT: I'm going to add more information, just in case:
My ChatList is a global variable that takes an array of Chats.
Each Chat takes an array of Message and a form.
When I submit the form of a chat, it adds the message to the Chat (locally and it also posts the new message to the server). When the server receives the POST event, it can render javascript code to the other user.
This code should add the message to the correct Chat for the correct user. There are two pieces missing:
I don't know how I can "select" the Chat.
I don't know how I can add a message to the "messages" array of this Chat.
You'll want to have your chat component listen to a WebSocket event for a new message and then call setState when a message is received.
The standard approach with Chatrooms is to use "channels" that each chat room subscribes to. That way it only hears about incoming messages from the conversation it cares about.
Check out Socket.io and its example on making a chat application. Even if you don't use Socket.io the example will be illustrative.
The solution, in my Chat class:
componentDidMount: function(){
this.props.privateChannel.bind('message.new', this.receiveMessage);
},
receiveMessage: function(message) {
if(message.user.id == this.props.recipient.id){
this.setState({messages: this.state.messages.concat([message])});
this.startBlink();
}
},

YouTube Subscribe Button Callback Event

Does anyone know if the YouTube scubscribe button (the one that appears in widgets on various websites) has a callback function once the user has clicked subscribe? I have looked into making my own but due to the fact they load in iframes I dont think its possibe.
It currently (2013-01-08) does not: https://developers.google.com/youtube/iframe_api_reference
Yes they recently added a callback function. In their GUI for creating a subscribe button, you can check "My application listens for button events." and it'll include the callback in the widget code: https://developers.google.com/youtube/youtube_subscribe_button

Resources