Restructure omni services and add Chatwoot research snapshot
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
import { MESSAGE_TYPE } from 'shared/constants/messages';
|
||||
import { showBadgeOnFavicon } from './faviconHelper';
|
||||
import { initFaviconSwitcher } from './faviconHelper';
|
||||
|
||||
import { EVENT_TYPES } from 'dashboard/routes/dashboard/settings/profile/constants.js';
|
||||
import GlobalStore from 'dashboard/store';
|
||||
import AudioNotificationStore from './AudioNotificationStore';
|
||||
import {
|
||||
isConversationAssignedToMe,
|
||||
isConversationUnassigned,
|
||||
isMessageFromCurrentUser,
|
||||
} from './AudioMessageHelper';
|
||||
import WindowVisibilityHelper from './WindowVisibilityHelper';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
|
||||
const NOTIFICATION_TIME = 30000;
|
||||
const ALERT_DURATION = 10000;
|
||||
const ALERT_PATH_PREFIX = '/audio/dashboard/';
|
||||
const DEFAULT_TONE = 'ding';
|
||||
const DEFAULT_ALERT_TYPE = ['none'];
|
||||
|
||||
export class DashboardAudioNotificationHelper {
|
||||
constructor(store) {
|
||||
if (!store) {
|
||||
throw new Error('store is required');
|
||||
}
|
||||
this.store = new AudioNotificationStore(store);
|
||||
|
||||
this.notificationConfig = {
|
||||
audioAlertType: DEFAULT_ALERT_TYPE,
|
||||
playAlertOnlyWhenHidden: true,
|
||||
alertIfUnreadConversationExist: false,
|
||||
};
|
||||
|
||||
this.recurringNotificationTimer = null;
|
||||
|
||||
this.audioConfig = {
|
||||
audio: null,
|
||||
tone: DEFAULT_TONE,
|
||||
hasSentSoundPermissionsRequest: false,
|
||||
};
|
||||
|
||||
this.currentUser = null;
|
||||
}
|
||||
|
||||
intializeAudio = () => {
|
||||
const resourceUrl = `${ALERT_PATH_PREFIX}${this.audioConfig.tone}.mp3`;
|
||||
this.audioConfig.audio = new Audio(resourceUrl);
|
||||
return this.audioConfig.audio.load();
|
||||
};
|
||||
|
||||
playAudioAlert = async () => {
|
||||
try {
|
||||
await this.audioConfig.audio.play();
|
||||
} catch (error) {
|
||||
if (
|
||||
error.name === 'NotAllowedError' &&
|
||||
!this.hasSentSoundPermissionsRequest
|
||||
) {
|
||||
this.hasSentSoundPermissionsRequest = true;
|
||||
useAlert(
|
||||
'PROFILE_SETTINGS.FORM.AUDIO_NOTIFICATIONS_SECTION.SOUND_PERMISSION_ERROR',
|
||||
{ usei18n: true, duration: ALERT_DURATION }
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
set = ({
|
||||
currentUser,
|
||||
alwaysPlayAudioAlert,
|
||||
alertIfUnreadConversationExist,
|
||||
audioAlertType = DEFAULT_ALERT_TYPE,
|
||||
audioAlertTone = DEFAULT_TONE,
|
||||
}) => {
|
||||
this.notificationConfig = {
|
||||
...this.notificationConfig,
|
||||
audioAlertType: audioAlertType.split('+').filter(Boolean),
|
||||
playAlertOnlyWhenHidden: !alwaysPlayAudioAlert,
|
||||
alertIfUnreadConversationExist: alertIfUnreadConversationExist,
|
||||
};
|
||||
|
||||
this.currentUser = currentUser;
|
||||
|
||||
const previousAudioTone = this.audioConfig.tone;
|
||||
this.audioConfig = {
|
||||
...this.audioConfig,
|
||||
tone: audioAlertTone,
|
||||
};
|
||||
|
||||
if (previousAudioTone !== audioAlertTone) {
|
||||
this.intializeAudio();
|
||||
}
|
||||
|
||||
initFaviconSwitcher();
|
||||
this.clearRecurringTimer();
|
||||
this.playAudioEvery30Seconds();
|
||||
};
|
||||
|
||||
shouldPlayAlert = () => {
|
||||
if (this.notificationConfig.playAlertOnlyWhenHidden) {
|
||||
return !WindowVisibilityHelper.isWindowVisible();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
executeRecurringNotification = () => {
|
||||
if (this.store.hasUnreadConversation() && this.shouldPlayAlert()) {
|
||||
this.playAudioAlert();
|
||||
showBadgeOnFavicon();
|
||||
}
|
||||
this.resetRecurringTimer();
|
||||
};
|
||||
|
||||
clearRecurringTimer = () => {
|
||||
if (this.recurringNotificationTimer) {
|
||||
clearTimeout(this.recurringNotificationTimer);
|
||||
}
|
||||
};
|
||||
|
||||
resetRecurringTimer = () => {
|
||||
this.clearRecurringTimer();
|
||||
this.recurringNotificationTimer = setTimeout(
|
||||
this.executeRecurringNotification,
|
||||
NOTIFICATION_TIME
|
||||
);
|
||||
};
|
||||
|
||||
playAudioEvery30Seconds = () => {
|
||||
const { audioAlertType, alertIfUnreadConversationExist } =
|
||||
this.notificationConfig;
|
||||
|
||||
// Audio alert is disabled dismiss the timer
|
||||
if (audioAlertType.includes('none')) return;
|
||||
|
||||
// If unread conversation flag is disabled, dismiss the timer
|
||||
if (!alertIfUnreadConversationExist) return;
|
||||
|
||||
this.resetRecurringTimer();
|
||||
};
|
||||
|
||||
shouldNotifyOnMessage = message => {
|
||||
const { audioAlertType } = this.notificationConfig;
|
||||
if (audioAlertType.includes('none')) return false;
|
||||
if (audioAlertType.includes('all')) return true;
|
||||
|
||||
const assignedToMe = isConversationAssignedToMe(
|
||||
message,
|
||||
this.currentUser.id
|
||||
);
|
||||
const isUnassigned = isConversationUnassigned(message);
|
||||
|
||||
const shouldPlayAudio = [];
|
||||
|
||||
if (
|
||||
audioAlertType.includes(EVENT_TYPES.ASSIGNED) ||
|
||||
audioAlertType.includes('mine')
|
||||
) {
|
||||
shouldPlayAudio.push(assignedToMe);
|
||||
}
|
||||
if (audioAlertType.includes(EVENT_TYPES.UNASSIGNED)) {
|
||||
shouldPlayAudio.push(isUnassigned);
|
||||
}
|
||||
if (audioAlertType.includes(EVENT_TYPES.NOTME)) {
|
||||
shouldPlayAudio.push(!isUnassigned && !assignedToMe);
|
||||
}
|
||||
|
||||
return shouldPlayAudio.some(Boolean);
|
||||
};
|
||||
|
||||
onNewMessage = message => {
|
||||
// If the user does not have the permission to view the conversation, then dismiss the alert
|
||||
// FIX ME: There shouldn't be a new message if the user has no access to the conversation.
|
||||
if (!this.store.hasConversationPermission(this.currentUser)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the conversation status is pending, then dismiss the alert
|
||||
// This case is common for all audio event types
|
||||
if (this.store.isMessageFromPendingConversation(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the message is sent by the current user then dismiss the alert
|
||||
if (isMessageFromCurrentUser(message, this.currentUser.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.shouldNotifyOnMessage(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the message type is not incoming or private, then dismiss the alert
|
||||
const { message_type: messageType, private: isPrivate } = message;
|
||||
if (messageType !== MESSAGE_TYPE.INCOMING && !isPrivate) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (WindowVisibilityHelper.isWindowVisible()) {
|
||||
// If the user looking at the conversation, then dismiss the alert
|
||||
if (this.store.isMessageFromCurrentConversation(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the user has disabled alerts when active on the dashboard, the dismiss the alert
|
||||
if (this.notificationConfig.playAlertOnlyWhenHidden) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.playAudioAlert();
|
||||
showBadgeOnFavicon();
|
||||
this.playAudioEvery30Seconds();
|
||||
};
|
||||
}
|
||||
|
||||
export default new DashboardAudioNotificationHelper(GlobalStore);
|
||||
Reference in New Issue
Block a user