@fleetbase/ember-ui
Version:
Fleetbase UI provides all the interface components, helpers, services and utilities for building a Fleetbase extension into the Console.
228 lines (204 loc) • 7.89 kB
JavaScript
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { inject as service } from '@ember/service';
import { isNone } from '@ember/utils';
import { task } from 'ember-concurrency';
import calculatePosition from 'ember-basic-dropdown/utils/calculate-position';
import noop from '../utils/noop';
export default class ChatTrayComponent extends Component {
chat;
socket;
fetch;
store;
modalsManager;
currentUser;
media;
channels = [];
unreadCount = 0;
notificationSound = new Audio('/sounds/message-notification-sound.mp3');
constructor() {
super(...arguments);
this.chat.loadChannels.perform({
withChannels: (channels) => {
this.channels = channels;
this.countUnread(channels);
this.listenAllChatChannels(channels);
this.listenUserChannel();
},
});
}
/**
* Calculate dropdown content position.
*
* @param {HTMLElement} trigger
* @param {HTMLElement} content
* @return {Object}
* @memberof ChatTrayComponent
*/
calculatePosition(trigger, content) {
if (this.media.isMobile) {
content.classList.add('is-mobile');
const triggerRect = trigger.getBoundingClientRect();
const top = triggerRect.height + triggerRect.top;
return { style: { left: '0px', right: '0px', top, width: '100%' } };
}
return calculatePosition(...arguments);
}
willDestroy() {
this.chat.off('chat.feed_updated', this.reloadChannelWithDelay.bind(this));
super.willDestroy(...arguments);
}
listenAllChatChannels(channels) {
channels.forEach((chatChannelRecord) => {
this.listenChatChannel(chatChannelRecord);
});
}
async listenUserChannel() {
this.socket.listen(`user.${this.currentUser.id}`, (socketEvent) => {
const { event, data } = socketEvent;
switch (event) {
case 'chat.participant_added':
case 'chat_participant.created':
this.reloadChannels();
break;
case 'chat.participant_removed':
case 'chat_participant.deleted':
this.reloadChannels();
this.closeChannelIfRemovedFromParticipants(data);
break;
case 'chat_channel.created':
this.reloadChannels({ relisten: true });
this.openNewChannelAsParticipant(data);
break;
case 'chat_channel.deleted':
this.reloadChannels({ relisten: true });
this.closeChannelIfOpen(data);
break;
}
});
}
async listenChatChannel(chatChannelRecord) {
this.socket.listen(`chat.${chatChannelRecord.public_id}`, (socketEvent) => {
const { event, data } = socketEvent;
switch (event) {
case 'chat_message.created':
this.reloadChannels();
this.playSoundForIncomingMessage(chatChannelRecord, data);
break;
case 'chat.added_participant':
this.reloadChannels();
break;
case 'chat_participant.deleted':
case 'chat.removed_participant':
this.reloadChannels();
this.closeChannelIfRemovedFromParticipants(data);
break;
case 'chat_channel.created':
this.reloadChannels({ relisten: true });
this.openNewChannelAsParticipant(data);
break;
case 'chat_channel.deleted':
this.reloadChannels({ relisten: true });
this.closeChannelIfOpen(data);
break;
case 'chat_receipt.created':
this.reloadChannels({ relisten: true });
break;
}
});
}
openChannel(chatChannelRecord) {
this.chat.openChannel(chatChannelRecord);
this.reloadChannels({ relisten: true });
}
startChat() {
this.chat.createChatChannel('Untitled Chat').then((chatChannelRecord) => {
this.openChannel(chatChannelRecord);
});
}
removeChannel(chatChannelRecord) {
this.modalsManager.confirm({
title: `Are you sure you wish to end this chat (${chatChannelRecord.title})?`,
body: 'Once this chat is ended, it will no longer be accessible for anyone.',
confirm: (modal) => {
modal.startLoading();
this.chat.closeChannel(chatChannelRecord);
this.chat.deleteChatChannel(chatChannelRecord);
return this.reloadChannels();
},
});
}
updateChatChannel(chatChannelRecord) {
this.chat.deleteChatChannel(chatChannelRecord);
this.reloadChannels();
}
async unlockAudio() {
this.reloadChannels();
try {
this.notificationSound.play().catch(noop);
this.notificationSound.pause();
this.notificationSound.currentTime = 0;
} catch (error) {
noop();
}
}
*getUnreadCount() {
const { unreadCount } = yield this.fetch.get('chat-channels/unread-count');
if (!isNone(unreadCount)) {
this.unreadCount = unreadCount;
}
}
playSoundForIncomingMessage(chatChannelRecord, data) {
const sender = this.getSenderFromParticipants(chatChannelRecord);
const isNotSender = sender ? sender.id !== data.sender_uuid : false;
if (isNotSender) {
this.notificationSound.play();
}
}
getSenderFromParticipants(channel) {
const participants = channel.participants ?? [];
return participants.find((chatParticipant) => {
return chatParticipant.user_uuid === this.currentUser.id;
});
}
countUnread(channels) {
this.unreadCount = channels.reduce((total, channel) => total + channel.unread_count, 0);
}
reloadChannels(options = {}) {
return this.chat.loadChannels.perform({
withChannels: (channels) => {
this.channels = channels;
this.countUnread(channels);
if (options && options.relisten === true) {
this.listenAllChatChannels(channels);
}
},
});
}
openNewChannelAsParticipant(data) {
const normalized = this.store.normalize('chat-channel', data);
const channel = this.store.push(normalized);
if (channel && this.getSenderFromParticipants(channel)) {
this.notificationSound.play();
this.openChannel(channel);
}
}
closeChannelIfOpen(data) {
const normalized = this.store.normalize('chat-channel', data);
const channel = this.store.push(normalized);
if (channel) {
this.chat.closeChannel(channel);
}
}
closeChannelIfRemovedFromParticipants(data) {
const normalized = this.store.normalize('chat-participant', data);
const removedChatParticipant = this.store.push(normalized);
if (removedChatParticipant) {
const channel = this.store.peekRecord('chat-channel', removedChatParticipant.chat_channel_uuid);
if (channel) {
this.chat.closeChannel(channel);
}
}
}
}