Push notification ServiceWorker WindowClient.navigate承诺被拒绝

Push notification ServiceWorker WindowClient.navigate承诺被拒绝,push-notification,firebase-cloud-messaging,service-worker,Push Notification,Firebase Cloud Messaging,Service Worker,我正在使用Firebase Cloud Messaging+服务工作者处理后台推送通知 单击通知(包含一些数据+URL)时,我想: 如果窗口已经位于所需的URL上,请将其聚焦 导航到URL,如果已打开活动选项卡,则将其聚焦 如果上述两个条件都不满足,请打开指向URL的新窗口 第1点和第3点适用于以下软件代码 出于某种原因,第2点不起作用。client.navigate()承诺被拒绝,原因是: Uncaught(in promise)类型错误:无法导航到URL:http://localhost

我正在使用Firebase Cloud Messaging+服务工作者处理后台推送通知

单击通知(包含一些数据+URL)时,我想:

  • 如果窗口已经位于所需的URL上,请将其聚焦
  • 导航到URL,如果已打开活动选项卡,则将其聚焦
  • 如果上述两个条件都不满足,请打开指向URL的新窗口
第1点和第3点适用于以下软件代码

出于某种原因,第2点不起作用。
client.navigate()
承诺被拒绝,原因是:

Uncaught(in promise)类型错误:无法导航到URL:http://localhost:4200/tasks/-KMcCHZdQ2YKCgTA4ddd

我认为这可能是由于缺少https,但从我的阅读来看,似乎localhost在使用SW开发时被列入了白名单

firebase-messaging-sw.js:

// Give the service worker access to Firebase Messaging.
// Note that you can only use Firebase Messaging here, other Firebase libraries
// are not available in the service worker.
importScripts('https://www.gstatic.com/firebasejs/3.5.3/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.5.3/firebase-messaging.js');

// Initialize the Firebase app in the service worker by passing in the
// messagingSenderId.
firebase.initializeApp({
  'messagingSenderId': 'XXXX'
});

const messaging = firebase.messaging();

messaging.setBackgroundMessageHandler(payload => {
  console.log('[firebase-messaging-sw.js] Received background message ', payload);
  let notificationData = JSON.parse(payload.data.notification);
  const notificationOptions = {
    body: notificationData.body,
    data: {
      clickUrl: notificationData.clickUrl
    }
  };

  return self.registration.showNotification(notificationData.title,
    notificationOptions);
});

self.addEventListener('notificationclick', event => {
  console.log('[firebase-messaging-sw.js] Notification OnClick: ', event);

  // Android doesn’t close the notification when you click on it
  // See: http://crbug.com/463146
  event.notification.close();

  // This looks to see if the current is already open and
  // focuses if it is
  event.notification.close();
  let validUrls = /localhost:4200/;
  let newUrl = event.notification.data.clickUrl || '';

  function endsWith(str, suffix) {
    return str.indexOf(suffix, str.length - suffix.length) !== -1;
  }

  event.waitUntil(
    clients.matchAll({
      includeUncontrolled: true,
      type: 'window'
    })
    .then(windowClients => {
      for (let i = 0; i < windowClients.length; i++) {
        let client = windowClients[i];
        if (validUrls.test(client.url) && 'focus' in client) {
          if (endsWith(client.url, newUrl)) {
            console.log('URL already open, focusing.');
            return client.focus();
          } else {
            console.log('Navigate to URL and focus', client.url, newUrl);
            return client.navigate(newUrl).then(client => client.focus());
          }
        }
      }

      if (clients.openWindow) {
        console.log('Opening new window', newUrl);
        return clients.openWindow(newUrl);
      }
    })
  );

});
//授予服务人员访问Firebase消息的权限。
//请注意,您只能在此处使用Firebase消息传递和其他Firebase库
//在服务人员中不可用。
进口文件('https://www.gstatic.com/firebasejs/3.5.3/firebase-app.js');
进口文件('https://www.gstatic.com/firebasejs/3.5.3/firebase-messaging.js');
//通过传入
//messagingSenderId。
firebase.initializeApp({
“messagingSenderId”:“XXXX”
});
const messaging=firebase.messaging();
messageing.setBackgroundMessageHandler(有效负载=>{
log(“[firebase messaging sw.js]接收到后台消息”,有效负载);
让notificationData=JSON.parse(payload.data.notification);
const notificationOptions={
body:notificationData.body,
数据:{
clickUrl:notificationData.clickUrl
}
};
返回self.registration.showNotification(notificationData.title,
通知选项);
});
self.addEventListener('notificationclick',event=>{
log(“[firebase messaging sw.js]通知OnClick:”,事件);
//当你点击通知时,Android不会关闭它
//见:http://crbug.com/463146
event.notification.close();
//这将查看电流是否已打开,并且
//如果是的话
event.notification.close();
设validUrls=/localhost:4200/;
让newUrl=event.notification.data.clickUrl | |“”;
函数endsWith(str,后缀){
返回str.indexOf(后缀,str.length-suffix.length)!=-1;
}
event.waitill(
客户。匹配球({
includeUncontrolled:正确,
键入:“窗口”
})
。然后(WindowClient=>{
for(设i=0;iclient.focus());
}
}
}
if(clients.openWindow){
console.log('打开新窗口',新建URL);
返回clients.openWindow(newUrl);
}
})
);
});
我的绝大多数软件代码取自:

我建议不要使用
客户端的
includeUncontrolled:true
。matchAll()

您正在操作的
WindowClient
可能没有当前的服务辅助程序作为其活动服务辅助程序。根据以下文件第4项:

如果上下文对象的关联服务工作者客户端处于活动状态 服务工作者不是上下文对象的相关全局对象的 服务人员,返回被拒绝的承诺,并带有
TypeError

如果您可以在确定客户端当前由服务人员控制时重现该问题,则可能会发生其他情况,但这是我尝试的第一步。

这对我来说很有效:

1-创建一个可观察的,并确保在解析之前不要调用消息传递API。

2-自己注册服务人员,首先检查其是否已注册

3-调用event.waitill(clients.claim());在您的服务人员中

private isMessagingInitialized$: Subject<void>;

constructor(private firebaseApp: firebase.app.App) {
    navigator.serviceWorker.getRegistration('/').then(registration => {
        if (registration) {
            // optionally update your service worker to the latest firebase-messaging-sw.js
            registration.update().then(() => { 
                firebase.messaging(this.firebaseApp).useServiceWorker(registration);
                this.isMessagingInitialized$.next();
            });
        }
        else {
            navigator.serviceWorker.register('firebase-messaging-sw.js', { scope:'/'}).then(
                registration => {
                    firebase.messaging(this.firebaseApp).useServiceWorker(registration);
                    this.isMessagingInitialized$.next();
                }
            );

        }
    });
    
    this.isMessagingInitialized$.subscribe(
        () => {
            firebase.messaging(this.firebaseApp).usePublicVapidKey('Your public api key');
            firebase.messaging(this.firebaseApp).onTokenRefresh(() => {
                this.getToken().subscribe((token: string) => {

                })
            });
            firebase.messaging(this.firebaseApp).onMessage((payload: any) => {

            });
        }
    );
}
}


为了进一步了解Jeff的评论,firebase-messaging-sw.js肯定不会控制页面,这是出于设计。如果需要此行为,可以将自定义服务工作人员注册传递到
messaging.useServiceWorker()
。此服务人员注册可以控制源站上的所有页面,并允许使用navigate()调用。@Jeff-我发现
客户端。matchAll()
没有返回任何WindowClient,除非我包含不受控制的窗口,这在@Gaunt刚刚提到的情况下是有意义的。谢谢你们两位的建议,我会让你们知道这是怎么回事,一旦我的答案生效,我可能会把它标记为可接受的答案。@Gaunface你们能详细说明一下吗?我已将自己的serviceworker传递给messaging.useServiceWorker,但收到了相同的错误。请打开一个新问题,列出您收到的示例文件位置、文件内容和错误消息。@rabhw您是否找到了此问题的解决方案,我们面临着类似的情况,它在firefox中工作,但在Chrome浏览器中出现了类似的错误。非常感谢您的帮助,谢谢
  self.addEventListener('notificationclick', function (event) {

  event.notification.close();

  switch (event.action) {
      case 'close': {
          break;
      }
      default: {
          event.waitUntil(clients.claim());// this 
          event.waitUntil(clients.matchAll({
              includeUncontrolled: true,
              type: "window"
          }).then(function (clientList) {
          ...
          clientList[i].navigate('you url');
          ...
          }
      }
  }