Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[IMPROVEMENT] Handle the network connection failure for back-channel polling requests #1582

Closed
mordochi opened this issue Mar 9, 2023 · 7 comments · Fixed by #1623 or #1640
Closed

Comments

@mordochi
Copy link
Contributor

mordochi commented Mar 9, 2023

Issue to be solved

Because of the restriction on iOS apps that they will lost network connections when they are in the background, back-channel polling requests on iOS browsers will fail once they can't access the network.

And the back-channel fetching mechanism in FCL will just close the wallet iframe once it catches any error, as we can see in the following code snippet.

} else if (resp.status === "PENDING") {
var canContinue = true
const [_, unmount] = await execLocal(normalizeLocalView(resp.local))
const close = () => {
try {
unmount()
canContinue = false
} catch (error) {
console.error("Frame Close Error", error)
}
}
return poll(resp.updates, () => canContinue)
.then(serviceResponse => {
close()
return serviceResponse
})
.catch(error => {
console.error(error)
close()
throw error
})

With this behavior, the back-channel login process fails from time to time, as Blocto requires users to input the OTP codes when logging in, and users might leave the browser app to check the OTP code in their mailboxes.

After users come back to the web app, the wallet iframe may have been closed due to the above connection error, and they have to start the login process all over again.

RPReplay_Final1678123421.MP4

Error logs
截圖 2023-03-08 下午11 52 36

Suggest A Solution

Make sure the requests are made only when the window has focus and the network is available. Wait to Fetch the polling requests again once the network is back rather than just close the iframe when the connection is lost.

What are you currently working on that this is blocking?

The back-channel communication between FCL and Blocto wallet

@scottphc
Copy link

@justinbarry @JeffreyDoyle Any ideas or directions for this issue?

@chasefleming
Copy link
Member

If we can differentiate between a network error and other errors then we can avoid closing the iFrame and the app can continue on. Thoughts?

@scottphc
Copy link

scottphc commented Mar 17, 2023

Sounds good. The error is different. Another idea is to stop polling when users leave the browser, so no errors are thrown.

@justinbarry justinbarry removed the status in 🌊 Flow 4D Mar 22, 2023
@justinbarry justinbarry self-assigned this Mar 24, 2023
@justinbarry justinbarry moved this to 🔖 Ready for Pickup in 🌊 Flow 4D Mar 27, 2023
@justinbarry
Copy link
Contributor

@scottphc Is this behavior specific to iOS or does it include Android as well?

@scottphc
Copy link

@scottphc Is this behavior specific to iOS or does it include Android as well?

This behavior is only on iOS.

@JeffreyDoyle
Copy link
Member

Hi @scottphc @mordochi, we have a proposed solution for this issue here: #1623

Please let us know what you think! Thank you again for raising this issue :)

@JeffreyDoyle JeffreyDoyle moved this from 🔖 Ready for Pickup to 🏗 In progress in 🌊 Flow 4D Apr 12, 2023
@github-project-automation github-project-automation bot moved this from 🏗 In progress to ✅ Done in 🌊 Flow 4D Apr 12, 2023
@mordochi
Copy link
Contributor Author

mordochi commented Apr 25, 2023

Hi @JeffreyDoyle, thanks a lot for helping with this issue!

I've just tested the fix but unfortunately it doesn't seem to fix all the login issue. It does reduce the chances that the wallet closes unintentionally(but still happens if you leave the browser for a longer time). However, when I proceed to type the OTP code and move on to the confirm page, the result is never resolved.

So I just dug into the behavior a little bit more.
And I found that it's actually the very first polling request sent when the browser's just back to the foreground was never getting its response, which means the polling function just stuck in

resp = await fetchService(service, {
method: serviceMethod(service),
}).then(normalizePollingResponse)
Thus the confirm page never got the approved result.

Also, for the case the iframe still closes by itself, it seems like document.visibilityState will be visible by the time we catch the connection error so it never goes in

if (
typeof document !== "undefined" &&
document.visibilityState === "hidden"
) {
await new Promise(r => setTimeout(r, 500))
return poll(service, canContinue)
} else {
Therefore, the connection error is still thrown and causes the iframe to close still.

But the good news is that I also found that we can get document.visibilityState as hidden before we make the fetch. So I suppose we can put the above check in L37-L43 before we call fetchService, like

try {
    if (
      typeof document !== "undefined" &&
      document.visibilityState === "hidden"
    ) {
      await new Promise(r => setTimeout(r, 500))
      return poll(service, canContinue)
    } 
    
    resp = await fetchService(service, {
      method: serviceMethod(service),
    }).then(normalizePollingResponse)
} catch (error) {
    throw error
}

Wonder if this looks good to you and please let me know if I can help with the PR :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment