mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
DEV: refactor live notifications setting in user preferences (#28145)
This change is mainly a refactor of the desktop notifications service to improve readability and have standardised values for tracking state for current user in regards to the Notification API and Push API. Also improves readability when handling push notification jobs, especially in scenarios where the push_notification_time_window_mins site setting is set to 0, which will allow sending push notifications instantly.
This commit is contained in:
parent
ec46487870
commit
6ec8728ebf
@ -61,8 +61,7 @@ function init(messageBus) {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.warn(
|
console.warn(
|
||||||
"Unexpected error, Notification is defined on window but not a responding correctly " +
|
"Notification is defined on window but is not responding correctly " + e
|
||||||
e
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,7 +135,7 @@ function canUserReceiveNotifications(user) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keyValueStore.getItem("notifications-disabled")) {
|
if (keyValueStore.getItem("notifications-disabled", "disabled")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +167,7 @@ async function onNotification(data, siteSettings, user, appEvents) {
|
|||||||
siteSettings.site_logo_small_url || siteSettings.site_logo_url;
|
siteSettings.site_logo_small_url || siteSettings.site_logo_url;
|
||||||
|
|
||||||
const notificationTag =
|
const notificationTag =
|
||||||
"discourse-notification-" + siteSettings.title + "-" + data.topic_id;
|
"discourse-notification-" + siteSettings.title + "-" + (data.topic_id || 0);
|
||||||
|
|
||||||
await requestPermission();
|
await requestPermission();
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ import {
|
|||||||
const keyValueStore = new KeyValueStore(context);
|
const keyValueStore = new KeyValueStore(context);
|
||||||
const DISABLED = "disabled";
|
const DISABLED = "disabled";
|
||||||
const ENABLED = "enabled";
|
const ENABLED = "enabled";
|
||||||
|
const SUBSCRIBED = "subscribed";
|
||||||
|
|
||||||
@disableImplicitInjections
|
@disableImplicitInjections
|
||||||
export default class DesktopNotificationsService extends Service {
|
export default class DesktopNotificationsService extends Service {
|
||||||
@ -25,17 +26,19 @@ export default class DesktopNotificationsService extends Service {
|
|||||||
@service site;
|
@service site;
|
||||||
@service siteSettings;
|
@service siteSettings;
|
||||||
|
|
||||||
@tracked notificationsDisabled;
|
@tracked isEnabledBrowser = false;
|
||||||
@tracked isEnabledPush;
|
@tracked isEnabledPush = false;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super(...arguments);
|
super(...arguments);
|
||||||
this.notificationsDisabled =
|
|
||||||
keyValueStore.getItem("notifications-disabled") === DISABLED;
|
this.isEnabledBrowser = this.isGrantedPermission
|
||||||
|
? keyValueStore.getItem("notifications-disabled") === ENABLED
|
||||||
|
: false;
|
||||||
this.isEnabledPush = this.currentUser
|
this.isEnabledPush = this.currentUser
|
||||||
? pushNotificationKeyValueStore.getItem(
|
? pushNotificationKeyValueStore.getItem(
|
||||||
pushNotificationUserSubscriptionKey(this.currentUser)
|
pushNotificationUserSubscriptionKey(this.currentUser)
|
||||||
)
|
) === SUBSCRIBED
|
||||||
: false;
|
: false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,11 +50,6 @@ export default class DesktopNotificationsService extends Service {
|
|||||||
return this.isNotSupported ? "" : Notification.permission;
|
return this.isNotSupported ? "" : Notification.permission;
|
||||||
}
|
}
|
||||||
|
|
||||||
setNotificationsDisabled(value) {
|
|
||||||
keyValueStore.setItem("notifications-disabled", value);
|
|
||||||
this.notificationsDisabled = value === DISABLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isDeniedPermission() {
|
get isDeniedPermission() {
|
||||||
if (this.isNotSupported) {
|
if (this.isNotSupported) {
|
||||||
return false;
|
return false;
|
||||||
@ -68,30 +66,8 @@ export default class DesktopNotificationsService extends Service {
|
|||||||
return this.notificationsPermission === "granted";
|
return this.notificationsPermission === "granted";
|
||||||
}
|
}
|
||||||
|
|
||||||
get isEnabledDesktop() {
|
|
||||||
if (this.isGrantedPermission) {
|
|
||||||
return !this.notificationsDisabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsEnabledPush(value) {
|
|
||||||
const user = this.currentUser;
|
|
||||||
if (!user) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
pushNotificationKeyValueStore.setItem(
|
|
||||||
pushNotificationUserSubscriptionKey(user),
|
|
||||||
value
|
|
||||||
);
|
|
||||||
this.isEnabledPush = pushNotificationKeyValueStore.getItem(
|
|
||||||
pushNotificationUserSubscriptionKey(user)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get isEnabled() {
|
get isEnabled() {
|
||||||
return this.isEnabledDesktop || this.isEnabledPush;
|
return this.isEnabledPush || this.isEnabledBrowser;
|
||||||
}
|
}
|
||||||
|
|
||||||
get isSubscribed() {
|
get isSubscribed() {
|
||||||
@ -99,11 +75,9 @@ export default class DesktopNotificationsService extends Service {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isPushNotificationsPreferred) {
|
return this.isPushNotificationsPreferred
|
||||||
return this.isEnabledPush === "subscribed";
|
? this.isEnabledPush
|
||||||
} else {
|
: this.isEnabledBrowser;
|
||||||
return !this.notificationsDisabled;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get isPushNotificationsPreferred() {
|
get isPushNotificationsPreferred() {
|
||||||
@ -114,14 +88,36 @@ export default class DesktopNotificationsService extends Service {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setIsEnabledBrowser(value) {
|
||||||
|
const status = value ? ENABLED : DISABLED;
|
||||||
|
keyValueStore.setItem("notifications-disabled", status);
|
||||||
|
this.isEnabledBrowser = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsEnabledPush(value) {
|
||||||
|
const user = this.currentUser;
|
||||||
|
const status = value ? SUBSCRIBED : value;
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pushNotificationKeyValueStore.setItem(
|
||||||
|
pushNotificationUserSubscriptionKey(user),
|
||||||
|
status
|
||||||
|
);
|
||||||
|
|
||||||
|
this.isEnabledPush = value;
|
||||||
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
async disable() {
|
async disable() {
|
||||||
if (this.isEnabledDesktop) {
|
if (this.isEnabledBrowser) {
|
||||||
this.setNotificationsDisabled(DISABLED);
|
this.setIsEnabledBrowser(false);
|
||||||
}
|
}
|
||||||
if (this.isEnabledPush) {
|
if (this.isEnabledPush) {
|
||||||
await unsubscribePushNotification(this.currentUser, () => {
|
await unsubscribePushNotification(this.currentUser, () => {
|
||||||
this.setIsEnabledPush("");
|
this.setIsEnabledPush(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,16 +125,22 @@ export default class DesktopNotificationsService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
enable() {
|
async enable() {
|
||||||
if (this.isPushNotificationsPreferred) {
|
if (this.isPushNotificationsPreferred) {
|
||||||
return subscribePushNotification(() => {
|
await subscribePushNotification(() => {
|
||||||
this.setIsEnabledPush("subscribed");
|
this.setIsEnabledPush(true);
|
||||||
}, this.siteSettings.vapid_public_key_bytes);
|
}, this.siteSettings.vapid_public_key_bytes);
|
||||||
|
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
this.setNotificationsDisabled(ENABLED);
|
await Notification.requestPermission((permission) => {
|
||||||
return Notification.requestPermission((permission) => {
|
|
||||||
confirmNotification(this.siteSettings);
|
confirmNotification(this.siteSettings);
|
||||||
return permission === "granted";
|
if (permission === "granted") {
|
||||||
|
this.setIsEnabledBrowser(true);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,8 @@ module Jobs
|
|||||||
class SendPushNotification < ::Jobs::Base
|
class SendPushNotification < ::Jobs::Base
|
||||||
def execute(args)
|
def execute(args)
|
||||||
user = User.find_by(id: args[:user_id])
|
user = User.find_by(id: args[:user_id])
|
||||||
if !user || user.seen_since?(SiteSetting.push_notification_time_window_mins.minutes.ago)
|
push_window = SiteSetting.push_notification_time_window_mins
|
||||||
return
|
return if !user || (push_window > 0 && user.seen_since?(push_window.minutes.ago))
|
||||||
end
|
|
||||||
|
|
||||||
PushNotificationPusher.push(user, args[:payload])
|
PushNotificationPusher.push(user, args[:payload])
|
||||||
end
|
end
|
||||||
|
@ -84,7 +84,8 @@ class PostAlerter
|
|||||||
end
|
end
|
||||||
|
|
||||||
if user.push_subscriptions.exists?
|
if user.push_subscriptions.exists?
|
||||||
if user.seen_since?(SiteSetting.push_notification_time_window_mins.minutes.ago)
|
push_window = SiteSetting.push_notification_time_window_mins
|
||||||
|
if push_window > 0 && user.seen_since?(push_window.minutes.ago)
|
||||||
delay =
|
delay =
|
||||||
(SiteSetting.push_notification_time_window_mins - (Time.now - user.last_seen_at) / 60)
|
(SiteSetting.push_notification_time_window_mins - (Time.now - user.last_seen_at) / 60)
|
||||||
Jobs.enqueue_in(delay.minutes, :send_push_notification, user_id: user.id, payload: payload)
|
Jobs.enqueue_in(delay.minutes, :send_push_notification, user_id: user.id, payload: payload)
|
||||||
|
@ -27,7 +27,6 @@ class PushNotificationPusher
|
|||||||
tag: payload[:tag] || "#{Discourse.current_hostname}-#{payload[:topic_id]}",
|
tag: payload[:tag] || "#{Discourse.current_hostname}-#{payload[:topic_id]}",
|
||||||
base_url: Discourse.base_url,
|
base_url: Discourse.base_url,
|
||||||
url: payload[:post_url],
|
url: payload[:post_url],
|
||||||
hide_when_active: true,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
subscriptions(user).each { |subscription| send_notification(user, subscription, message) }
|
subscriptions(user).each { |subscription| send_notification(user, subscription, message) }
|
||||||
|
@ -10,23 +10,29 @@ RSpec.describe Jobs::SendPushNotification do
|
|||||||
SiteSetting.push_notification_time_window_mins = 10
|
SiteSetting.push_notification_time_window_mins = 10
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with active online user" do
|
context "with valid user" do
|
||||||
it "does not send push notification" do
|
it "does not send push notification when user is online" do
|
||||||
user.update!(last_seen_at: 5.minutes.ago)
|
user.update!(last_seen_at: 2.minutes.ago)
|
||||||
|
|
||||||
PushNotificationPusher.expects(:push).with(user, payload).never
|
PushNotificationPusher.expects(:push).with(user, payload).never
|
||||||
|
|
||||||
Jobs::SendPushNotification.new.execute(user_id: user, payload: payload)
|
Jobs::SendPushNotification.new.execute(user_id: user, payload: payload)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
context "with inactive offline user" do
|
it "sends push notification when user is offline" do
|
||||||
it "sends push notification" do
|
user.update!(last_seen_at: 20.minutes.ago)
|
||||||
user.update!(last_seen_at: 40.minutes.ago)
|
|
||||||
|
|
||||||
PushNotificationPusher.expects(:push).with(user, payload)
|
PushNotificationPusher.expects(:push).with(user, payload)
|
||||||
|
|
||||||
Jobs::SendPushNotification.new.execute(user_id: user, payload: payload)
|
Jobs::SendPushNotification.new.execute(user_id: user, payload: payload)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "with invalid user" do
|
||||||
|
it "does not send push notification" do
|
||||||
|
PushNotificationPusher.expects(:push).with(user, payload).never
|
||||||
|
|
||||||
|
Jobs::SendPushNotification.new.execute(user_id: -999, payload: payload)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user