diff --git a/app/controllers/chat.js b/app/controllers/chat.js index c39ff86..6b9e4cc 100644 --- a/app/controllers/chat.js +++ b/app/controllers/chat.js @@ -50,6 +50,12 @@ export default class ChatController extends SiteController { this.getRoomCreateView.bind(this), ); + router.get( + '/room/:roomId/join', + // limiterService.create(limiterService.config.chat.getRoomView), + this.getRoomJoin.bind(this), + ); + router.get( '/room/:roomId', // limiterService.create(limiterService.config.chat.getRoomView), @@ -86,6 +92,17 @@ export default class ChatController extends SiteController { res.render('chat/room/create'); } + async getRoomJoin (req, res, next) { + const { chat: chatService } = this.dtp.services; + try { + await chatService.joinRoom(res.locals.room, req.user); + res.status(200).json({ success: true, room: res.locals.room }); + } catch (error) { + this.log.error('failed to join chat room', { error }); + return next(error); + } + } + async getRoomView (req, res) { res.locals.currentView = 'chat-room'; res.render('chat/room/view'); diff --git a/app/models/chat-room.js b/app/models/chat-room.js index 475f1ea..88b8fcf 100644 --- a/app/models/chat-room.js +++ b/app/models/chat-room.js @@ -10,7 +10,7 @@ import mongoose from 'mongoose'; const Schema = mongoose.Schema; const ChatRoomSchema = new Schema({ - created: { type: Date, default: Date.now, required: true, index: 1, expires: '7d' }, + created: { type: Date, default: Date.now, required: true, index: 1 }, lastActivity: { type: Date, index: -1 }, owner: { type: Schema.ObjectId, required: true, index: 1, ref: 'User' }, name: { type: String, required: true }, @@ -18,7 +18,12 @@ const ChatRoomSchema = new Schema({ capacity: { type: Number, required: true, min: MIN_ROOM_CAPACITY, max: MAX_ROOM_CAPACITY }, invites: { type: [Schema.ObjectId], select: false }, members: { type: [Schema.ObjectId], select: false }, + present: { type: [Schema.ObjectId], select: false }, banned: { type: [Schema.ObjectId], select: false }, + stats: { + memberCount: { type: Number, default: 0, min: 0, max: MAX_ROOM_CAPACITY, required: true }, + presentCount: { type: Number, default: 0, min: 0, max: MAX_ROOM_CAPACITY, required: true }, + }, }); export default mongoose.model('ChatRoom', ChatRoomSchema); \ No newline at end of file diff --git a/app/services/chat.js b/app/services/chat.js index 18b1077..1d47152 100644 --- a/app/services/chat.js +++ b/app/services/chat.js @@ -10,6 +10,8 @@ const ChatRoom = mongoose.model('ChatRoom'); const ChatMessage = mongoose.model('ChatMessage'); // const ChatRoomInvite = mongoose.model('ChatRoomInvite'); +import numeral from 'numeral'; + import { SiteService, SiteError } from '../../lib/site-lib.js'; import { MAX_ROOM_CAPACITY } from '../models/lib/constants.js'; @@ -24,6 +26,11 @@ export default class ChatService extends SiteService { async start ( ) { const { user: userService } = this.dtp.services; + + this.templates = { + memberListItem: this.loadViewTemplate('chat/components/member-list-item-standalone.pug'), + }; + this.populateChatRoom = [ { path: 'owner', @@ -45,6 +52,7 @@ export default class ChatService extends SiteService { } room.capacity = MAX_ROOM_CAPACITY; room.members = [owner._id]; + room.stats.memberCount = 1; await room.save(); @@ -60,12 +68,141 @@ export default class ChatService extends SiteService { } async joinRoom (room, user) { - await ChatRoom.updateOne( + const roomData = await ChatRoom.findOne({ _id: room._id, banned: user._id }).lean(); + if (roomData) { + throw new SiteError(401, 'You are banned from this chat room'); + } + + const response = await ChatRoom.updateOne( { _id: room._id }, { $push: { members: user._id }, }, ); + + this.log.debug('joinRoom complete', { response }); + return response; + } + + async chatRoomCheckIn (room, member) { + const NOW = new Date(); + + // indicate presence in the chat room's Mongo document + const roomData = await ChatRoom.findOneAndUpdate( + { _id: room._id }, + { + $addToSet: { present: member._id }, + $inc: { 'stats.presentCount': 1 }, + }, + { + new: true, + }, + ); + + this.log.debug('member checking into chat room', { + room: { + _id: room._id, + name: room.name, + presentCount: roomData.stats.presentCount, + }, + member: { + _id: member._id, + username: member.username, + }, + }); + + /* + * Broadcast a control message to all room members that a new member has + * joined the room. + */ + const displayList = this.createDisplayList('chat-control'); + displayList.removeElement(`ul#present-members li[data-member-id="${member._id}"]`); + displayList.addElement( + `ul#chat-active-members[data-room-id="${room._id}"]`, + 'afterBegin', + this.templates.memberListItem({ room, member }), + ); + + displayList.setTextContent( + `.chat-present-count`, + numeral(roomData.stats.presentCount).format('0,0'), + ); + + const systemMessage = { + created: NOW.toISOString(), + content: `@${member.username} has connected to the room.`, + }; + + this.dtp.emitter.to(room._id.toString()).emit('chat-control', { displayList, systemMessages: [systemMessage] }); + + } + + async chatRoomCheckOut (room, member) { + const NOW = new Date(); + const roomData = await ChatRoom.findOneAndUpdate( + { _id: room._id }, + { + $pull: { present: member._id }, + $inc: { 'stats.presentCount': -1 }, + }, + { + new: true, + }, + ); + + this.log.debug('member checking out of chat room', { + room: { + _id: room._id, + name: room.name, + presentCount: roomData.stats.presentCount, + }, + member: { + _id: member._id, + username: member.username, + }, + }); + + /* + * Broadcast a control message to all room members that a new member has + * joined the room. + */ + const displayList = this.createDisplayList('chat-control'); + displayList.removeElement(`ul#chat-active-members li[data-member-id="${member._id}"]`); + + displayList.setTextContent( + `.chat-present-count`, + numeral(roomData.stats.presentCount).format('0,0'), + ); + + const systemMessage = { + created: NOW.toISOString(), + content: `@${member.username} has connected to the room.`, + }; + + this.dtp.emitter.to(room._id.toString()).emit('chat-control', { displayList, systemMessages: [systemMessage] }); + } + + async checkRoomMember (room, member) { + if (room.owner._id.equals(member._id)) { + return true; + } + + const search = { _id: room._id, members: member._id }; + const checkRoom = await ChatRoom.findOne(search).select('name').lean(); + if (!checkRoom) { + throw new SiteError(403, `You are not a member of ${checkRoom.name}`); + } + + return true; + } + + async isRoomMember (room, member) { + if (room.owner._id.equals(member._id)) { + return true; + } + const search = { _id: room._id, members: member._id }; + const checkRoom = await ChatRoom.findOne(search).select('name').lean(); + return !!checkRoom; } async leaveRoom (room, user) { diff --git a/app/views/chat/components/member-list-item-standalone.pug b/app/views/chat/components/member-list-item-standalone.pug new file mode 100644 index 0000000..5562967 --- /dev/null +++ b/app/views/chat/components/member-list-item-standalone.pug @@ -0,0 +1,2 @@ +include member-list-item ++renderChatMemberListItem (room, member, { isHost, isGuest }) \ No newline at end of file diff --git a/app/views/chat/components/member-list-item.pug b/app/views/chat/components/member-list-item.pug new file mode 100644 index 0000000..6f353a6 --- /dev/null +++ b/app/views/chat/components/member-list-item.pug @@ -0,0 +1,99 @@ +include ../../user/components/profile-picture +mixin renderChatMemberListItem (room, member, options) + - + options = Object.assign({ + isHost: false, + isGuest: false, + }, options); + + var isRoomOwner = room.owner._id.equals(member._id); + var isSystemMod = user && user.flags && (user.flags.isAdmin || user.flags.isModerator); + var memberName = member.displayName || member.username; + // var isChannelMod = user && Array.isArray(message.channel.moderators) && !!message.channel.moderators.find((moderator) => moderator._id.equals(user._id)); + + li(data-member-id= member._id, data-member-username= member.username) + div(uk-grid).uk-grid-collapse.uk-flex-middle.member-list-item + .uk-width-auto + img(src='/img/default-member.png').member-profile-icon + .uk-width-expand.uk-text-small + a(href=`/member/${member.username}`, uk-tooltip={ title: `Visit ${member.username}`}).uk-link-reset + .member-display-name= member.displayName || member.username + .member-username @#{member.username} + .uk-width-auto.chat-user-menu + button(type="button").dtp-button-dropdown + i.fas.fa-ellipsis-v + div( + data-room-id= room._id, + uk-dropdown={ animation: "uk-animation-scale-up", duration: 250 }, + data-mode="click", + ).dtp-chatmsg-menu + ul.uk-nav.uk-dropdown-nav + li.uk-nav-header= member.username + li + a( + href="", + data-username= member.username, + onclick="return dtp.app.mentionChatUser(event);", + ) Mention + li + a( + href="", + data-room-id= room._id, + data-room-name= room.name, + data-user-id= member._id, + data-username= member.username, + onclick="return dtp.app.muteChatUser(event);", + ) Mute + + if (options.isHost || options.isGuest) && !isRoomOwner + li.uk-nav-divider + if options.isHost + li + a( + href, + data-environment="ChatRoom", + data-room-id= room._id, + data-room-name= room.name, + data-user-id= member._id, + data-username= member.username, + data-display-name= member.displayName, + onclick="return dtp.app.removeRoomHost(event);", + ) Remove host + if options.isGuest + li + a( + href, + data-environment="ChatRoom", + data-room-id= room._id, + data-room-name= room.name, + data-user-id= member._id, + data-username= member.username, + data-display-name= member.displayName, + onclick="return dtp.app.removeRoomGuest(event);", + ) Remove guest + + if isSystemMod || isChannelMod + li.uk-nav-divider + li + a( + href="", + data-environment="ChatRoom", + data-room-id= room._id, + data-room-name= room.name, + data-user-id= member._id, + data-username= member.username, + data-display-name= member.displayName, + onclick="return dtp.app.confirmBanUserFromEnvironment(event);", + ) Ban from room + + if isSystemMod + li + a( + href="", + data-room-id= room._id, + data-room-name= room.name, + data-user-id= member._id, + data-username= member.username, + data-display-name= member.displayName, + onclick="return dtp.adminApp.confirmBanUser(event);", + ) Ban from #{site.name} \ No newline at end of file diff --git a/app/views/chat/room/view.pug b/app/views/chat/room/view.pug index a853031..68fb840 100644 --- a/app/views/chat/room/view.pug +++ b/app/views/chat/room/view.pug @@ -27,7 +27,7 @@ block view-content mixin renderLiveMember (member) div(data-user-id= member._id, data-username= member.username).stage-live-member - video(src="/static/video/gdl-crush.mp4", autoplay, muted, loop, disablepictureinpicture, disableremoteplayback) + video(poster="/img/default-poster.png", disablepictureinpicture, disableremoteplayback) .uk-flex.live-meta.no-select .live-username.uk-width-expand .uk-text-truncate= member.displayName || member.username @@ -38,30 +38,20 @@ block view-content i.fas.fa-cog .dtp-chat-stage - .chat-sidebar - .chat-stage-header Active Members + #room-member-panel.chat-sidebar + .chat-stage-header + div(uk-grid).uk-grid-small.uk-grid-middle + .uk-width-expand + .uk-text-truncate Active Members + .uk-width-auto + .chat-present-count.uk-text-small --- .sidebar-panel - ul(id="chat-active-members").uk-list.uk-list-collapse - +renderMemberListEntry(user) - +renderMemberListEntry(user) - +renderMemberListEntry(user) - +renderMemberListEntry(user) - +renderMemberListEntry(user) - +renderMemberListEntry(user) - +renderMemberListEntry(user) - +renderMemberListEntry(user) - +renderMemberListEntry(user) - +renderMemberListEntry(user) - +renderMemberListEntry(user) - +renderMemberListEntry(user) + ul(id="chat-active-members", data-room-id= room._id).uk-list.uk-list-collapse .chat-stage-header Idle Members .sidebar-panel - ul(id="chat-idle-members").uk-list.uk-list-collapse - +renderMemberListEntry(user, { idle: true }) - +renderMemberListEntry(user, { idle: true }) - +renderMemberListEntry(user, { idle: true }) - +renderMemberListEntry(user, { idle: true }) + .uk-text-italic.uk-text-muted There are no idle members. + ul(id="chat-idle-members", data-room-id= room._id, hidden).uk-list.uk-list-collapse .chat-container .chat-stage-header @@ -69,32 +59,40 @@ block view-content .uk-width-expand= room.name .chat-content-panel - div(uk-grid).uk-grid-collapse.live-content - .uk-width-expand - .chat-media - div(uk-grid) - div(class="uk-width-1-1 uk-width-1-2@m uk-width-1-3@l uk-width-1-4@xl") - +renderLiveMember(user) + .live-content + .chat-media + div(uk-grid) + div(class="uk-width-1-1 uk-width-1-2@m uk-width-1-3@l uk-width-1-4@xl") + +renderLiveMember(user) - .uk-width-auto - .chat-messages - - - var testMessage = { - created: new Date(), - author: user, - content: "This is the chat content panel. It should word-wrap and scroll correctly, and will be where individual chat messages will render as they arrive and are sent.", - }; + div(id="chat-message-list").chat-messages + - + var testMessage = { + created: new Date(), + author: user, + content: "This is the chat content panel. It should word-wrap and scroll correctly, and will be where individual chat messages will render as they arrive and are sent.", + }; + + +renderChatMessage(testMessage) + +renderChatMessage(testMessage) + +renderChatMessage(testMessage) - +renderChatMessage(testMessage) - +renderChatMessage(testMessage) - +renderChatMessage(testMessage) - +renderChatMessage(testMessage) + .chat-input-panel + form( + id="chat-input-form", + data-room-id= room._id, + onsubmit="return window.dtp.app.sendUserChat(event);", + hidden= user && user.flags && user.flags.isCloaked, + ).uk-form + textarea(id="chat-input-text", name="chatInput", rows=2).uk-textarea.uk-resize-none.uk-border-rounded + .uk-margin-small + .uk-flex + .uk-width-expand + .uk-width-auto + button(id="chat-send-btn", type="submit", uk-tooltip={ title: 'Send message' }).uk-button.uk-button-default.uk-button-small.uk-border-rounded + i.fas.fa-paper-plane - .chat-input-panel - textarea(id="chat-input", name="chatInput", rows=2).uk-textarea.uk-resize-none.uk-border-rounded - .uk-margin-small - .uk-flex - .uk-width-expand - .uk-width-auto - button(type="submit", uk-tooltip={ title: 'Send message' }).uk-button.uk-button-default.uk-button-small.uk-border-rounded - i.fas.fa-paper-plane \ No newline at end of file +block viewjs + script. + window.dtp = window.dtp || { }; + window.dtp.room = !{JSON.stringify(room)}; \ No newline at end of file diff --git a/client/css/dtp-site.less b/client/css/dtp-site.less index 780ef8c..f70619b 100644 --- a/client/css/dtp-site.less +++ b/client/css/dtp-site.less @@ -1,4 +1,5 @@ @import "site/main.less"; +@import "site/button.less"; @import "site/navbar.less"; @import "site/stage.less"; @import "site/image.less"; \ No newline at end of file diff --git a/client/css/site/button.less b/client/css/site/button.less index e69de29..13fad7a 100644 --- a/client/css/site/button.less +++ b/client/css/site/button.less @@ -0,0 +1,23 @@ +button.dtp-button-dropdown { + /* + * This is a series of settings that improve the "clickable area" of the + * button without making it take up more space in the layout. + */ + display: inline-block; + position: relative; + padding: 1em; + margin: -1em; + + &.always-on-top { + z-index: 1; + } + + /* + * The actual "style" of the thing + */ + background: none; + border: none; + outline: none; + color: #e8e8e8; + cursor: pointer; +} \ No newline at end of file diff --git a/client/css/site/stage.less b/client/css/site/stage.less index d276163..db6afed 100644 --- a/client/css/site/stage.less +++ b/client/css/site/stage.less @@ -7,11 +7,6 @@ display: flex; - .flex-row-break { - flex-basis: 100%; - height: 0; - } - .chat-stage-header { flex-grow: 0; flex-shrink: 0; @@ -42,37 +37,52 @@ } } + img.member-profile-icon { + width: 32px; + height: auto; + border-radius: 5px; + margin-right: 5px; + } + + .member-list-item { + line-height: 1.1em; + + .member-display-name { + line-height: 1.1em; + } + .member-username { + line-height: 1em; + } + } + .chat-container { box-sizing: border-box; display: flex; + flex-grow: 1; flex-direction: column; - align-items: stretch; background-color: #a8a8a8; color: #1a1a1a; .chat-content-panel { box-sizing: border-box; - position: relative; display: flex; - flex-wrap: wrap; - - height: 100%; + flex: 1; .live-content { - flex-grow: 1; + display: flex; + flex: 1; .chat-media { box-sizing: border-box; - flex-basis: 0; flex-grow: 1; - height: 100%; padding: @stage-panel-padding; background-color: #4a4a4a; + overflow-y: auto; .stage-live-member { padding: 4px 4px 0 4px; @@ -84,6 +94,11 @@ video { display: block; + + aspect-ratio: 16 / 9; + width: 100%; + height: auto; + border-radius: 3px; } @@ -104,26 +119,23 @@ flex-shrink: 0; width: 320px; - height: 100%; padding: @stage-panel-padding; overflow-y: scroll; .chat-message { - line-height: 1; - + padding: 5px; margin-bottom: 10px; - border-bottom: solid 1px #4a4a4a; - - &:last-child { - border-bottom: none; - } + + line-height: 1; + border-radius: 4px; + + background-color: #e8e8e8; + color: #1a1a1a; - img.member-profile-icon { - width: 32px; - height: auto; - border-radius: 5px; - margin-right: 5px; + &.system-message { + background-color: #d1f3db; + border-radius: 4px; } .message-attribution { @@ -154,15 +166,15 @@ } } - .chat-input-panel { - flex-basis: 100%; - flex-grow: 0; - flex-shrink: 0; - - padding: @stage-panel-padding; - background-color: #1a1a1a; - color: #e8e8e8; - } + } + + .chat-input-panel { + flex-grow: 0; + flex-shrink: 0; + + padding: @stage-panel-padding; + background-color: #1a1a1a; + color: #e8e8e8; } } diff --git a/client/img/default-poster.png b/client/img/default-poster.png new file mode 100644 index 0000000..f7dc2cf Binary files /dev/null and b/client/img/default-poster.png differ diff --git a/client/js/chat-client.js b/client/js/chat-client.js index 01ff773..bbc1439 100644 --- a/client/js/chat-client.js +++ b/client/js/chat-client.js @@ -5,13 +5,15 @@ 'use strict'; const DTP_COMPONENT_NAME = 'DtpChatApp'; -window.dtp = window.dtp || { }; +const dtp = window.dtp = window.dtp || { }; import DtpApp from 'lib/dtp-app.js'; import QRCode from 'qrcode'; import Cropper from 'cropperjs'; +import dayjs from 'dayjs'; + export class ChatApp extends DtpApp { constructor (user) { @@ -19,12 +21,134 @@ export class ChatApp extends DtpApp { this.loadSettings(); this.log.info('DTP app client online'); + this.chat = { + form: document.querySelector('#chat-input-form'), + messageList: document.querySelector('#chat-message-list'), + messages: [ ], + messageMenu: document.querySelector('.chat-message-menu'), + input: document.querySelector('#chat-input-text'), + sendButton: document.querySelector('#chat-send-btn'), + isAtBottom: true, + }; + window.addEventListener('dtp-load', this.onDtpLoad.bind(this)); + window.addEventListener('unload', this.onDtpUnload.bind(this)); } async onDtpLoad ( ) { this.log.info('dtp-load event received. Connecting to platform.'); - await this.connect(); + await this.connect({ + mode: 'User', + onSocketConnect: this.onChatSocketConnect.bind(this), + onSocketDisconnect: this.onChatSocketDisconnect.bind(this), + }); + } + + async onDtpUnload ( ) { + await this.socket.disconnect(); + } + + async onChatSocketConnect (socket) { + this.log.debug('onSocketConnect', 'attaching socket events'); + socket.on('chat-control', this.onChatControl.bind(this)); + socket.on('system-message', this.onSystemMessage.bind(this)); + + if (dtp.room) { + await this.joinChatChannel(dtp.room); + } + } + + async onChatSocketDisconnect (socket) { + this.log.debug('onSocketDisconnect', 'detaching socket events'); + socket.off('chat-control', this.onChatControl.bind(this)); + socket.off('system-message', this.onSystemMessage.bind(this)); + } + + async onChatControl (message) { + const isAtBottom = this.chat.isAtBottom; + + if (message.displayList) { + this.displayEngine.executeDisplayList(message.displayList); + } + + if (Array.isArray(message.systemMessages) && (message.systemMessages.length > 0)) { + for await (const sm of message.systemMessages) { + await this.onSystemMessage(sm); + } + } + + if (message.cmd) { + switch (message.cmd) { + case 'call-start': + if (message.mediaServer && !this.call) { + dtp.mediaServer = message.mediaServer; + setTimeout(this.joinWebCall.bind(this), Math.floor(Math.random() * 3000)); + } + break; + + case 'call-end': + if (this.chat) { + this.chat.closeCall(); + } + break; + } + } + + this.scrollChatToBottom(isAtBottom); + } + + async onSystemMessage (message) { + if (message.displayList) { + this.displayEngine.executeDisplayList(message.displayList); + } + + if (!message.created || !message.content) { + return; + } + + if (!this.chat || !this.chat.messageList) { + return; + } + + const systemMessage = document.createElement('div'); + systemMessage.classList.add('chat-message'); + systemMessage.classList.add('system-message'); + + const chatContent = document.createElement('div'); + chatContent.classList.add('chat-content'); + chatContent.classList.add('uk-text-break'); + chatContent.innerHTML = message.content; + systemMessage.appendChild(chatContent); + + const chatTimestamp = document.createElement('div'); + chatTimestamp.classList.add('chat-timestamp'); + chatTimestamp.classList.add('uk-text-small'); + chatTimestamp.setAttribute('data-dtp-timestamp', message.created); + chatTimestamp.innerHTML = dayjs(message.created).format('hh:mm:ss a'); + systemMessage.appendChild(chatTimestamp); + + this.chat.messageList.appendChild(systemMessage); + this.chat.messages.push(systemMessage); + + while (this.chat.messages.length > 50) { + const message = this.chat.messages.shift(); + this.chat.messageList.removeChild(message); + } + if (this.chat.isAtBottom) { + this.chat.messageList.scrollTo(0, this.chat.messageList.scrollHeight + 50000); + } + } + + async joinChatChannel (room) { + try { + const response = await fetch(`/chat/room/${dtp.room._id}/join`); + await this.processResponse(response); + + await this.socket.joinChannel(dtp.room._id, 'ChatRoom'); + } catch (error) { + this.log.error('failed to join chat room', { room, error }); + UIkit.modal.alert(`Failed to join chat room: ${error.message}`); + } } async confirmNavigation (event) { @@ -295,4 +419,47 @@ export class ChatApp extends DtpApp { this.log.info("createImageCropper", "Creating image cropper", { img }); this.cropper = new Cropper(img, options); } + + scrollChatToBottom (isAtBottom = true) { + if (this.chat && this.chat.messageList && isAtBottom) { + this.chat.messageList.scrollTo(0, this.chat.messageList.scrollHeight); + setTimeout(( ) => { + this.chat.isAtBottom = true; + this.chat.messageList.scrollTo(0, this.chat.messageList.scrollHeight); + this.chat.isModifying = false; + }, 25); + } + } + + async onChatMessageListScroll (event) { + const scrollPos = this.chat.messageList.scrollTop + this.chat.messageList.clientHeight; + + if (!this.chat.isModifying) { + this.chat.isAtBottom = (scrollPos >= (this.chat.messageList.scrollHeight - 10)); + this.chat.isAtTop = (scrollPos <= 0); + } + + if (event && (this.chat.isAtBottom || this.chat.isAtTop)) { + event.preventDefault(); + event.stopPropagation(); + } + + if (this.chat.isAtBottom) { + this.chat.messageMenu.classList.remove('chat-menu-visible'); + } else { + this.chat.messageMenu.classList.add('chat-menu-visible'); + } + } + + async resumeChatScroll ( ) { + this.chat.messageList.scrollTo(0, this.chat.messageList.scrollHeight + 50000); + this.chat.isAtBottom = true; + this.chat.messageMenu.classList.remove('chat-menu-visible'); + } + + async onWindowResize ( ) { + if (this.chat.messageList && this.chat.isAtBottom) { + this.chat.messageList.scrollTo(0, this.chat.messageList.scrollHeight + 50000); + } + } } \ No newline at end of file diff --git a/lib/client/js/dtp-log.js b/lib/client/js/dtp-log.js index 58f6d85..4e51b74 100644 --- a/lib/client/js/dtp-log.js +++ b/lib/client/js/dtp-log.js @@ -75,7 +75,7 @@ export default class DtpWebLog { }; const env = document.querySelector('body').getAttribute('data-dtp-env'); - if (env === 'local') { + if (env === 'development') { this.enable(); } } diff --git a/lib/client/js/dtp-socket.js b/lib/client/js/dtp-socket.js index cceebc5..7868e3d 100644 --- a/lib/client/js/dtp-socket.js +++ b/lib/client/js/dtp-socket.js @@ -89,6 +89,12 @@ export default class DtpWebSocket { } } + disconnect ( ) { + // remove disconnect handler since we're being deliberate + this.socket.off('disconnect', this.onSocketDisconnect.bind(this)); + this.socket.disconnect(); + } + async onSocketConnect ( ) { this.log.info('onSocketConnect', 'WebSocket connected'); this.isConnected = true; @@ -164,7 +170,8 @@ export default class DtpWebSocket { } this.joinChannel(message.user._id, 'User'); - document.dispatchEvent(new Event('socketConnected')); + this.log.info('onSocketUserAuthenticated', 'dispatching dtp-socket-connected'); + document.dispatchEvent(new Event('dtp-socket-connected')); } async onSocketWidgetAuthenticated (message) { @@ -175,8 +182,7 @@ export default class DtpWebSocket { this.options.onSocketConnect(this.socket); } - // this.joinChannel(message.channel._id, 'Channel'); - document.dispatchEvent(new Event('socketConnected')); + document.dispatchEvent(new Event('dtp-socket-connected')); } async joinChannel (channelId, channelType, passcode) { @@ -195,7 +201,7 @@ export default class DtpWebSocket { } const event = new CustomEvent('dtp-channel-joined', { detail: message }); - window.dispatchEvent(event); + document.dispatchEvent(event); } async leaveChannel (channelId) { diff --git a/lib/site-ioserver.js b/lib/site-ioserver.js index 0b6595b..a4790e5 100644 --- a/lib/site-ioserver.js +++ b/lib/site-ioserver.js @@ -259,7 +259,12 @@ export class SiteIoServer extends SiteCommon { async onSocketDisconnect (session, reason) { const { chat: chatService } = this.dtp.services; - this.log.debug('socket disconnect', { sid: session.socket.id, consumerId: (session.user || session.channel)._id, reason }); + this.log.debug('socket disconnect', { + sid: session.socket.id, + consumerId: (session.user || session.channel)._id, + joinedRooms: session.joinedRooms.size, + reason, + }); if (session.user && session.joinedRooms.size > 0) { for await (const room of session.joinedRooms) { diff --git a/webpack.config.js b/webpack.config.js index a2bd9d5..4edc359 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -68,8 +68,8 @@ export default { }, performance: { hints: 'warning', - maxEntrypointSize: 512 * 1024, - maxAssetSize: 512 * 1024, + maxEntrypointSize: 2 * 102412 * 1024, + maxAssetSize: 2 * 102412 * 1024, }, plugins, module: {