UX: reduces idle time to 0 on chat (#27158)

We consider that you should always receive a notification sound when someone speaks directly with you in chat.

This commit also refactors the way we play audio in chat to make it simpler and throttle it to 3 seconds.

We also added a safeguard to ensure we won't play sounds for old messages, this case can happen when message bus is catching up the backlog (eg: in an inactive tab for example).
This commit is contained in:
Joffrey JAFFEUX 2024-05-24 11:18:11 +02:00 committed by GitHub
parent c39a4de139
commit d5066336ec
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 38 additions and 52 deletions

View File

@ -139,23 +139,23 @@ function setupNotifications(appEvents) {
}
function resetIdle() {
lastAction = Date.now();
lastAction = Date.now() - 10;
}
function isIdle() {
return lastAction + idleThresholdTime < Date.now();
function isIdle(idleThreshold = idleThresholdTime) {
return lastAction + idleThreshold <= Date.now();
}
function setLastAction(time) {
lastAction = time;
}
function canUserReceiveNotifications(user) {
function canUserReceiveNotifications(user, options = { idleThresholdTime }) {
if (!primaryTab) {
return false;
}
if (!isIdle()) {
if (!isIdle(options.idleThresholdTime)) {
return false;
}

View File

@ -8,15 +8,12 @@ export default {
name: "chat-audio",
initialize(container) {
const chatService = container.lookup("service:chat");
const chat = container.lookup("service:chat");
if (!chatService.userCanChat) {
if (!chat.userCanChat) {
return;
}
const chatAudioManager = container.lookup("service:chat-audio-manager");
chatAudioManager.setup();
withPluginApi("0.12.1", (api) => {
api.registerDesktopNotificationHandler((data, siteSettings, user) => {
if (user.isInDoNotDisturb()) {
@ -28,6 +25,9 @@ export default {
}
if (CHAT_NOTIFICATION_TYPES.includes(data.notification_type)) {
const chatAudioManager = container.lookup(
"service:chat-audio-manager"
);
chatAudioManager.play(user.chat_sound);
}
});

View File

@ -9,50 +9,26 @@ export const CHAT_SOUNDS = {
const DEFAULT_SOUND_NAME = "bell";
const createAudioCache = (sources) => {
const audio = new Audio();
audio.pause();
sources.forEach(({ type, src }) => {
const source = document.createElement("source");
source.type = type;
source.src = getURLWithCDN(src);
audio.appendChild(source);
});
return audio;
};
const THROTTLE_TIME = 3000; // 3 seconds
export default class ChatAudioManager extends Service {
_audioCache = {};
canPlay = true;
setup() {
Object.keys(CHAT_SOUNDS).forEach((soundName) => {
this._audioCache[soundName] = createAudioCache(CHAT_SOUNDS[soundName]);
});
async play(name) {
if (this.canPlay) {
await this.#tryPlay(name);
this.canPlay = false;
setTimeout(() => {
this.canPlay = true;
}, THROTTLE_TIME);
}
}
willDestroy() {
super.willDestroy(...arguments);
this._audioCache = {};
}
async play(soundName) {
if (isTesting()) {
return;
}
const audio =
this._audioCache[soundName] || this._audioCache[DEFAULT_SOUND_NAME];
if (!audio.paused) {
audio.pause();
if (typeof audio.fastSeek === "function") {
audio.fastSeek(0);
} else {
audio.currentTime = 0;
}
}
async #tryPlay(name) {
const src = getURLWithCDN(
(CHAT_SOUNDS[name] || CHAT_SOUNDS[DEFAULT_SOUND_NAME])[0].src
);
const audio = new Audio(src);
try {
await audio.play();
} catch (e) {

View File

@ -8,7 +8,11 @@ export default class ChatChannelNotificationSound extends Service {
@service site;
async play(channel) {
if (!canUserReceiveNotifications(this.currentUser)) {
if (
!canUserReceiveNotifications(this.currentUser, {
idleThresholdTime: 0,
})
) {
return false;
}

View File

@ -206,7 +206,13 @@ export default class ChatSubscriptionsManager extends Service {
channel.tracking.unreadCount++;
}
this.chatChannelNotificationSound.play(channel);
const secondsPassed = moment().diff(
moment(busData.message.created_at),
"seconds"
);
if (secondsPassed < 10) {
this.chatChannelNotificationSound.play(channel);
}
// Thread should be considered unread if not already.
if (busData.thread_id && channel.threadingEnabled) {

View File

@ -133,7 +133,7 @@ acceptance(
const channel = buildDirectMessageChannel(getOwner(this));
resetIdle();
assert.deepEqual(await this.subject.play(channel), false);
assert.deepEqual(await this.subject.play(channel), true);
});
test("notifications disabled", async function (assert) {