logo

mastofe

My custom branche(s) on git.pleroma.social/pleroma/mastofe git clone https://hacktivis.me/git/mastofe.git

web_push_notifications.js (5606B)


  1. const MAX_NOTIFICATIONS = 5;
  2. const GROUP_TAG = 'tag';
  3. // Avoid loading intl-messageformat and dealing with locales in the ServiceWorker
  4. const formatGroupTitle = (message, count) => message.replace('%{count}', count);
  5. const notify = options =>
  6. self.registration.getNotifications().then(notifications => {
  7. if (notifications.length === MAX_NOTIFICATIONS) {
  8. // Reached the maximum number of notifications, proceed with grouping
  9. const group = {
  10. title: formatGroupTitle(options.data.message, notifications.length + 1),
  11. body: notifications
  12. .sort((n1, n2) => n1.timestamp < n2.timestamp)
  13. .map(notification => notification.title).join('\n'),
  14. badge: '/badge.png',
  15. icon: '/android-chrome-192x192.png',
  16. tag: GROUP_TAG,
  17. data: {
  18. url: (new URL('/web/notifications', self.location)).href,
  19. count: notifications.length + 1,
  20. message: options.data.message,
  21. },
  22. };
  23. notifications.forEach(notification => notification.close());
  24. return self.registration.showNotification(group.title, group);
  25. } else if (notifications.length === 1 && notifications[0].tag === GROUP_TAG) {
  26. // Already grouped, proceed with appending the notification to the group
  27. const group = cloneNotification(notifications[0]);
  28. group.title = formatGroupTitle(group.data.message, group.data.count + 1);
  29. group.body = `${options.title}\n${group.body}`;
  30. group.data = { ...group.data, count: group.data.count + 1 };
  31. return self.registration.showNotification(group.title, group);
  32. }
  33. return self.registration.showNotification(options.title, options);
  34. });
  35. const handlePush = (event) => {
  36. const options = event.data.json();
  37. options.body = options.data.nsfw || options.data.content;
  38. options.dir = options.data.dir;
  39. options.image = options.image || undefined; // Null results in a network request (404)
  40. options.timestamp = options.timestamp && new Date(options.timestamp);
  41. const expandAction = options.data.actions.find(action => action.todo === 'expand');
  42. if (expandAction) {
  43. options.actions = [expandAction];
  44. options.hiddenActions = options.data.actions.filter(action => action !== expandAction);
  45. options.data.hiddenImage = options.image;
  46. options.image = undefined;
  47. } else {
  48. options.actions = options.data.actions;
  49. }
  50. event.waitUntil(notify(options));
  51. };
  52. const cloneNotification = (notification) => {
  53. const clone = { };
  54. for(var k in notification) {
  55. clone[k] = notification[k];
  56. }
  57. return clone;
  58. };
  59. const expandNotification = (notification) => {
  60. const nextNotification = cloneNotification(notification);
  61. nextNotification.body = notification.data.content;
  62. nextNotification.image = notification.data.hiddenImage;
  63. nextNotification.actions = notification.data.actions.filter(action => action.todo !== 'expand');
  64. return self.registration.showNotification(nextNotification.title, nextNotification);
  65. };
  66. const makeRequest = (notification, action) =>
  67. fetch(action.action, {
  68. headers: {
  69. 'Authorization': `Bearer ${notification.data.access_token}`,
  70. 'Content-Type': 'application/json',
  71. },
  72. method: action.method,
  73. credentials: 'include',
  74. });
  75. const findBestClient = clients => {
  76. const focusedClient = clients.find(client => client.focused);
  77. const visibleClient = clients.find(client => client.visibilityState === 'visible');
  78. return focusedClient || visibleClient || clients[0];
  79. };
  80. const openUrl = url =>
  81. self.clients.matchAll({ type: 'window' }).then(clientList => {
  82. if (clientList.length !== 0) {
  83. const webClients = clientList.filter(client => /\/web\//.test(client.url));
  84. if (webClients.length !== 0) {
  85. const client = findBestClient(webClients);
  86. const { pathname } = new URL(url);
  87. if (pathname.startsWith('/web/')) {
  88. return client.focus().then(client => client.postMessage({
  89. type: 'navigate',
  90. path: pathname.slice('/web/'.length - 1),
  91. }));
  92. }
  93. } else if ('navigate' in clientList[0]) { // Chrome 42-48 does not support navigate
  94. const client = findBestClient(clientList);
  95. return client.navigate(url).then(client => client.focus());
  96. }
  97. }
  98. return self.clients.openWindow(url);
  99. });
  100. const removeActionFromNotification = (notification, action) => {
  101. const actions = notification.actions.filter(act => act.action !== action.action);
  102. const nextNotification = cloneNotification(notification);
  103. nextNotification.actions = actions;
  104. return self.registration.showNotification(nextNotification.title, nextNotification);
  105. };
  106. const handleNotificationClick = (event) => {
  107. const reactToNotificationClick = new Promise((resolve, reject) => {
  108. if (event.action) {
  109. const action = event.notification.data.actions.find(({ action }) => action === event.action);
  110. if (action.todo === 'expand') {
  111. resolve(expandNotification(event.notification));
  112. } else if (action.todo === 'request') {
  113. resolve(makeRequest(event.notification, action)
  114. .then(() => removeActionFromNotification(event.notification, action)));
  115. } else {
  116. reject(`Unknown action: ${action.todo}`);
  117. }
  118. } else {
  119. event.notification.close();
  120. resolve(openUrl(event.notification.data.url));
  121. }
  122. });
  123. event.waitUntil(reactToNotificationClick);
  124. };
  125. self.addEventListener('push', handlePush);
  126. self.addEventListener('notificationclick', handleNotificationClick);