mirror of
https://github.com/Retrospring/retrospring.git
synced 2025-01-31 09:49:17 +01:00
Improve UX for push subscription management
This commit is contained in:
parent
8ff213af4e
commit
66b1dac3b9
7 changed files with 91 additions and 42 deletions
|
@ -17,18 +17,23 @@ class Ajax::WebPushController < AjaxController
|
||||||
|
|
||||||
@response[:status] = :okay
|
@response[:status] = :okay
|
||||||
@response[:success] = true
|
@response[:success] = true
|
||||||
|
@response[:message] = t(".subscription_count", count: current_user.web_push_subscriptions.count)
|
||||||
end
|
end
|
||||||
|
|
||||||
def unsubscribe
|
def unsubscribe
|
||||||
params.permit(:endpoint)
|
params.permit(:endpoint)
|
||||||
|
|
||||||
if params.key?(:endpoint)
|
if params.key?(:endpoint)
|
||||||
current_user.web_push_subscriptions.where("subscription ->> 'endpoint' = ?", params[:endpoint]).destroy
|
current_user.web_push_subscriptions.where("subscription ->> 'endpoint' = ?", params[:endpoint]).destroy_all
|
||||||
else
|
else
|
||||||
current_user.web_push_subscriptions.destroy_all
|
current_user.web_push_subscriptions.destroy_all
|
||||||
end
|
end
|
||||||
|
|
||||||
|
count = current_user.web_push_subscriptions.count
|
||||||
|
|
||||||
@response[:status] = :okay
|
@response[:status] = :okay
|
||||||
@response[:success] = true
|
@response[:success] = true
|
||||||
|
@response[:message] = t(".subscription_count", count:)
|
||||||
|
@response[:count] = count
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { showNotification } from "utilities/notifications";
|
||||||
|
|
||||||
export function enableHandler (event: Event): void {
|
export function enableHandler (event: Event): void {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
const sender = event.target as HTMLButtonElement;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
installServiceWorker()
|
installServiceWorker()
|
||||||
|
@ -26,6 +27,13 @@ export function enableHandler (event: Event): void {
|
||||||
new Notification(I18n.translate("frontend.push_notifications.subscribe.success.title"), {
|
new Notification(I18n.translate("frontend.push_notifications.subscribe.success.title"), {
|
||||||
body: I18n.translate("frontend.push_notifications.subscribe.success.body")
|
body: I18n.translate("frontend.push_notifications.subscribe.success.body")
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll<HTMLButtonElement>('button[data-action="push-disable"], button[data-action="push-remove-all"]')
|
||||||
|
.forEach(button => button.classList.remove('d-none'));
|
||||||
|
|
||||||
|
sender.classList.add('d-none');
|
||||||
|
|
||||||
|
document.getElementById('subscription-count').textContent = data.message;
|
||||||
} else {
|
} else {
|
||||||
new Notification(I18n.translate("frontend.push_notifications.fail.title"), {
|
new Notification(I18n.translate("frontend.push_notifications.fail.title"), {
|
||||||
body: I18n.translate("frontend.push_notifications.fail.body")
|
body: I18n.translate("frontend.push_notifications.fail.body")
|
||||||
|
|
|
@ -8,17 +8,30 @@ export default (): void => {
|
||||||
const notificationCapable = document.body.classList.contains('cap-notification');
|
const notificationCapable = document.body.classList.contains('cap-notification');
|
||||||
|
|
||||||
if (swCapable && notificationCapable) {
|
if (swCapable && notificationCapable) {
|
||||||
navigator.serviceWorker.getRegistration().then(registration => {
|
|
||||||
if (!registration && localStorage.getItem('dismiss-push-settings-prompt') == null) {
|
|
||||||
document.querySelector('.push-settings').classList.remove('d-none');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
registerEvents([
|
navigator.serviceWorker.getRegistration().then(registration => {
|
||||||
{type: 'click', target: '[data-action="push-enable"]', handler: enableHandler, global: true},
|
return registration.pushManager.getSubscription().then(subscription => {
|
||||||
{type: 'click', target: '[data-action="push-dismiss"]', handler: dismissHandler, global: true},
|
if (!subscription) {
|
||||||
{type: 'click', target: '[data-action="push-disable"]', handler: () => unsubscribeHandler(false), global: true},
|
document.querySelector('button[data-action="push-enable"]').classList.remove('d-none');
|
||||||
{type: 'click', target: '[data-action="push-remove-all"]', handler: () => unsubscribeHandler(true), global: true},
|
} else {
|
||||||
]);
|
document.querySelector('[data-action="push-disable"]').classList.remove('d-none');
|
||||||
|
if (localStorage.getItem('dismiss-push-settings-prompt') == null) {
|
||||||
|
document.querySelector('.push-settings')?.classList.remove('d-none');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerEvents([
|
||||||
|
{type: 'click', target: '[data-action="push-enable"]', handler: enableHandler, global: true},
|
||||||
|
{type: 'click', target: '[data-action="push-dismiss"]', handler: dismissHandler, global: true},
|
||||||
|
{type: 'click', target: '[data-action="push-disable"]', handler: unsubscribeHandler, global: true},
|
||||||
|
{
|
||||||
|
type: 'click',
|
||||||
|
target: '[data-action="push-remove-all"]',
|
||||||
|
handler: unsubscribeHandler,
|
||||||
|
global: true
|
||||||
|
},
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,35 +2,52 @@ import { destroy } from '@rails/request.js';
|
||||||
import { showErrorNotification, showNotification } from "utilities/notifications";
|
import { showErrorNotification, showNotification } from "utilities/notifications";
|
||||||
import I18n from "retrospring/i18n";
|
import I18n from "retrospring/i18n";
|
||||||
|
|
||||||
export function unsubscribeHandler(all = false): void {
|
export function unsubscribeHandler(): void {
|
||||||
getSubscription().then(subscription => {
|
navigator.serviceWorker.getRegistration()
|
||||||
const body = all ? { endpoint: subscription.endpoint } : undefined;
|
.then(registration => registration.pushManager.getSubscription())
|
||||||
|
.then(subscription => unsubscribeClient(subscription))
|
||||||
destroy('/ajax/webpush', {
|
.then(subscription => unsubscribeServer(subscription))
|
||||||
body,
|
.then()
|
||||||
contentType: 'application/json',
|
.catch(error => {
|
||||||
}).then(async response => {
|
|
||||||
const data = await response.json;
|
|
||||||
|
|
||||||
if (data.success) {
|
|
||||||
subscription?.unsubscribe().then(() => {
|
|
||||||
showNotification(I18n.translate("frontend.push_notifications.unsubscribe.success"));
|
|
||||||
}).catch(error => {
|
|
||||||
console.error("Tried to unsubscribe this browser but was unable to.\n" +
|
|
||||||
"As we've been unsubscribed on the server-side, this should not be an issue.",
|
|
||||||
error);
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
showErrorNotification(I18n.translate("frontend.push_notifications.unsubscribe.fail"));
|
|
||||||
}
|
|
||||||
}).catch(error => {
|
|
||||||
showErrorNotification(I18n.translate("frontend.push_notifications.unsubscribe.error"));
|
showErrorNotification(I18n.translate("frontend.push_notifications.unsubscribe.error"));
|
||||||
console.error(error);
|
console.error(error);
|
||||||
});
|
});
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getSubscription(): Promise<PushSubscription> {
|
async function unsubscribeClient(subscription?: PushSubscription): Promise<PushSubscription|null> {
|
||||||
const registration = await navigator.serviceWorker.getRegistration('/');
|
subscription?.unsubscribe().then(success => {
|
||||||
return await registration.pushManager.getSubscription();
|
if (!success) {
|
||||||
|
throw new Error("Failed to unsubscribe.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return subscription;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function unsubscribeServer(subscription?: PushSubscription): Promise<void> {
|
||||||
|
const body = subscription != null ? { endpoint: subscription.endpoint } : undefined;
|
||||||
|
|
||||||
|
return destroy('/ajax/webpush', {
|
||||||
|
body,
|
||||||
|
contentType: 'application/json',
|
||||||
|
}).then(async response => {
|
||||||
|
const data = await response.json;
|
||||||
|
|
||||||
|
if (data.success) {
|
||||||
|
showNotification(I18n.translate("frontend.push_notifications.unsubscribe.success"));
|
||||||
|
|
||||||
|
document.getElementById('subscription-count').textContent = data.message;
|
||||||
|
|
||||||
|
if (data.count == 0) {
|
||||||
|
document.querySelectorAll<HTMLButtonElement>('button[data-action="push-disable"], button[data-action="push-remove-all"]')
|
||||||
|
.forEach(button => button.classList.add('d-none'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.body.classList.contains('cap-service-worker') && document.body.classList.contains('cap-notification')) {
|
||||||
|
document.querySelector<HTMLButtonElement>('button[data-action="push-enable"]')?.classList.remove('d-none');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showErrorNotification(I18n.translate("frontend.push_notifications.unsubscribe.fail"));
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
.card.push-notifications-settings
|
.card.push-notifications-settings
|
||||||
.card-body
|
.card-body
|
||||||
%p= t('.description')
|
%p= t('.description')
|
||||||
%p= t('.subscription_count', count: subscriptions.count)
|
%p#subscription-count= t('.subscription_count', count: subscriptions.count)
|
||||||
|
|
||||||
.push-notifications-unavailable.text-danger
|
.push-notifications-unavailable.text-danger
|
||||||
%i.fa.fa-warning
|
%i.fa.fa-warning
|
||||||
|
@ -12,6 +12,6 @@
|
||||||
= t('.current_target')
|
= t('.current_target')
|
||||||
|
|
||||||
.button-group{ role: 'group' }
|
.button-group{ role: 'group' }
|
||||||
%button.btn.btn-primary{ data: { action: 'push-enable' } }= t('.subscribe')
|
%button.btn.btn-primary{ data: { action: 'push-enable' }, class: 'd-none' }= t('.subscribe')
|
||||||
%button.btn.btn-primary{ data: { action: 'push-disable' } }= t('.unsubscribe_current')
|
%button.btn.btn-primary{ data: { action: 'push-disable' }, class: 'd-none' }= t('.unsubscribe_current')
|
||||||
%button.btn.btn-danger{ data: { action: 'push-remove-all' } }= t('.unsubscribe_all')
|
%button.btn.btn-danger{ data: { action: 'push-remove-all' }, class: subscriptions.empty? ? 'd-none' : '' }= t('.unsubscribe_all')
|
||||||
|
|
|
@ -139,6 +139,11 @@ en:
|
||||||
destroy_comment:
|
destroy_comment:
|
||||||
success: "Successfully unsmiled comment."
|
success: "Successfully unsmiled comment."
|
||||||
error: "You have not smiled that comment."
|
error: "You have not smiled that comment."
|
||||||
|
web_push:
|
||||||
|
subscription_count:
|
||||||
|
zero: "You are not currently subscribed to push notifications on any devices."
|
||||||
|
one: "You are currently receiving push notifications on one device."
|
||||||
|
other: "You are currently receiving push notifications on %{count} devices."
|
||||||
inbox:
|
inbox:
|
||||||
author:
|
author:
|
||||||
info: "No questions from @%{author} found, showing entries from all users instead!"
|
info: "No questions from @%{author} found, showing entries from all users instead!"
|
||||||
|
|
|
@ -543,6 +543,7 @@ en:
|
||||||
subscribe: "Enable on this device"
|
subscribe: "Enable on this device"
|
||||||
unsubscribe_current: "Disable on this device"
|
unsubscribe_current: "Disable on this device"
|
||||||
unsubscribe_all: "Disable on all devices"
|
unsubscribe_all: "Disable on all devices"
|
||||||
|
description: "Here you can set up or disable push notifications for new questions in your inbox."
|
||||||
shared:
|
shared:
|
||||||
links:
|
links:
|
||||||
about: "About"
|
about: "About"
|
||||||
|
|
Loading…
Reference in a new issue