Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

navigator.share won't resolve nor reject when user cancels native selector on android

So, we are implementing navigator.share() (https://developers.google.com/web/updates/2016/09/navigator-share) in an Ionic v3 PWA.

It works, but there's a little problem we encountered which we don't know how to fix: when the navite share selector pops up (user can select betwen inbox, gmail, twitter, etc) and the user then presses the back button on android (dismissing the native modal that came up), the function doesn't trigger any response in the promise. Not success, not error.

The problem here is that we show a loading spinner before we call the navigator.share function and if the user presses that back button at that precise moment, we can't trigger the function that hides the loading spinner.

This is the part of the code where we implement the feature:

public share(title: string, message: string, url: string) {
    // if it's mobile & web (ie: chrome on android)
    if (this.isMobileWeb === true) {
        this.showLoading();
        if ((navigator as any).share) {
            (navigator as any).share({
                title,
                message,
                url,
            })
                .then(() => {
                    console.log('Success share');
                    this.hideLoading();
                })
                .catch((error) => {
                    console.log('Error share', error);
                    this.hideLoading();
                });
        }
        else {
            console.log('navigator.share() not supported.');
            this.hideLoading();
        }
    }
}

With this implementation, no success or error is thrown if the user presses the back button when the native share modal comes up. Is there something wrong with this code?

I've also tried adding another callback function inside the .then() function, as in: .then(successFn, errorFn). The errorFn is never called either.

Thanks for your time.-

like image 543
Ariel Avatar asked Apr 05 '18 02:04

Ariel


1 Answers

This is a long-standing bug in Android. Here is my solution for it, but I can't take credit for the technique. It was mentioned in the bug tracker linked below. Pardon the use of ES6 syntax. The idea is that you would call this share function instead of the 'navigator.share' function directly.

export default options => new Promise((resolve, reject) => {
  navigator.share(options).then(resolve).catch(error => {
    // Differentiate between user 'AbortError' and internal errors.
    // E.g. Internal error: could not connect to Web Share interface.
    if (error.message.startsWith('Internal error:'))
      error.name = 'InternalError';

    reject(error);
  });

  /*
   * https://bugs.chromium.org/p/chromium/issues/detail?id=636274
   * If the share promise is not resolved or rejected when focus is returned to
   * the window, then reject it after a 100ms delay.
   */
  let cancel = () => setTimeout(() => {
    window.removeEventListener('focus', cancel);

    let error = new Error('Share cancelled');
    error.name = 'ShareTimeout';
    reject(error);
  }, 100);

  window.addEventListener('focus', cancel);
});
like image 82
pongstylin Avatar answered Oct 26 '22 19:10

pongstylin