diff --git a/app/controllers/home.js b/app/controllers/home.js index b6eb82e..17c8eee 100644 --- a/app/controllers/home.js +++ b/app/controllers/home.js @@ -35,6 +35,10 @@ export default class HomeController extends SiteController { async getHome (req, res, next) { const { chat: chatService } = this.dtp.services; try { + if (!req.user) { + return res.redirect('/welcome'); + } + res.locals.currentView = 'home'; res.locals.pageDescription = 'DTP Chat Home'; @@ -48,8 +52,5 @@ export default class HomeController extends SiteController { } catch (error) { return next(error); } - if (!req.user) { - return res.redirect('/welcome'); - } } } \ No newline at end of file diff --git a/app/views/chat/components/message.pug b/app/views/chat/components/message.pug new file mode 100644 index 0000000..a88243e --- /dev/null +++ b/app/views/chat/components/message.pug @@ -0,0 +1,16 @@ +mixin renderChatMessage (message) + .chat-message + .uk-flex + .uk-width-auto.no-select + img(src="/img/default-member.png").member-profile-icon + .uk-width-expand + .message-attribution.uk-margin-small.no-select + .uk-flex.uk-flex-top + .uk-width-expand + if (message.author.displayName && (message.author.displayName.length > 0)) + .author-display-name= message.author.displayName + .author-username @#{message.author.username} + .uk-width-auto + .message-timestamp= dayjs(message.created).format('h:mm a') + .message-content + div!= marked.parse(message.content) diff --git a/app/views/chat/room/view.pug b/app/views/chat/room/view.pug index f8f4d42..a853031 100644 --- a/app/views/chat/room/view.pug +++ b/app/views/chat/room/view.pug @@ -1,41 +1,100 @@ extends ../../layout/main block view-content + include ../components/message + + mixin renderMemberListEntry (member, options) + - + options = Object.assign({ + active: false, + idle: false, + audioActive: false, + audioIndicatorActive: false, + }, options); + + li( + data-user-id= member._id, + data-username= member.username, + class={ 'entry-active': options.active, 'entry-idle': options.idle, 'entry-audio-active': options.audioActive }, + ).member-list-entry + div(uk-grid).uk-grid-collapse.uk-flex-middle + .uk-width-auto + img(src="/img/default-member.png").member-profile-icon + .uk-width-expand= member.username + .uk-width-auto + div(data-member-id= member._id, class={ 'indicator-active': options.audioIndicatorActive }).member-audio-indicator + i.fas.fa-volume-off + + 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) + .uk-flex.live-meta.no-select + .live-username.uk-width-expand + .uk-text-truncate= member.displayName || member.username + .uk-width-auto + i.fas.fa-volume-off + .uk-width-auto + .uk-margin-small-left + i.fas.fa-cog + .dtp-chat-stage .chat-sidebar .chat-stage-header Active Members - .sidebar-panel This is a sidebar content panel. It should word-wrap correctly and will be used to display usernames in the room with status. + .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) .chat-stage-header Idle Members - .sidebar-panel This is a sidebar content panel. It should word-wrap correctly and will be used to display usernames in the room with status. + .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 }) .chat-container .chat-stage-header div(uk-grid) .uk-width-expand= room.name - .chat-content-panel - .chat-media - div(uk-grid) - .uk-width-1-4 - .live-member - video(src="/static/video/gdl-crush.mp4", autoplay, muted, loop, disablepictureinpicture, disableremoteplayback) - .uk-flex.live-meta - .live-username.uk-width-expand Rob Colbert - .uk-width-auto - i.fas.fa-volume-off - .uk-width-auto - .uk-margin-small-left - i.fas.fa-cog - - .chat-messages 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. - - .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 + 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) + + .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.", + }; + + +renderChatMessage(testMessage) + +renderChatMessage(testMessage) + +renderChatMessage(testMessage) + +renderChatMessage(testMessage) + + .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 diff --git a/client/css/site/stage.less b/client/css/site/stage.less index 1239aba..d276163 100644 --- a/client/css/site/stage.less +++ b/client/css/site/stage.less @@ -7,6 +7,11 @@ display: flex; + .flex-row-break { + flex-basis: 100%; + height: 0; + } + .chat-stage-header { flex-grow: 0; flex-shrink: 0; @@ -39,67 +44,164 @@ .chat-container { box-sizing: border-box; + display: flex; - flex-grow: 1; flex-direction: column; + align-items: stretch; - background-color: #e8e8e8; + background-color: #a8a8a8; color: #1a1a1a; .chat-content-panel { box-sizing: border-box; - flex-grow: 1; - display: flex; - - .chat-media { - box-sizing: border-box; - flex-grow: 1; - flex-shrink: 1; + position: relative; - height: 100%; - padding: @stage-panel-padding; - - background-color: #4a4a4a; - - .live-member { - padding: 4px 4px 0 4px; + display: flex; + flex-wrap: wrap; - border: solid 1px #e8e8e8; - border-radius: 4px; + height: 100%; - background-color: #1a1a1a; + .live-content { + flex-grow: 1; - video { - display: block; + .chat-media { + box-sizing: border-box; + flex-basis: 0; + flex-grow: 1; + + height: 100%; + padding: @stage-panel-padding; + + background-color: #4a4a4a; + + .stage-live-member { + padding: 4px 4px 0 4px; + + border: solid 1px #8a8a8a; + border-radius: 4px; + + background-color: #1a1a1a; + + video { + display: block; + border-radius: 3px; + } + + .live-meta { + color: #e8e8e8; + + .live-username { + color: inherit; + } + } } - - .live-meta { - color: #e8e8e8; - - .live-username { - color: #00ff00; + } + + .chat-messages { + box-sizing: border-box; + + flex-grow: 0; + flex-shrink: 0; + + width: 320px; + height: 100%; + padding: @stage-panel-padding; + + overflow-y: scroll; + + .chat-message { + line-height: 1; + + margin-bottom: 10px; + border-bottom: solid 1px #4a4a4a; + + &:last-child { + border-bottom: none; + } + + img.member-profile-icon { + width: 32px; + height: auto; + border-radius: 5px; + margin-right: 5px; + } + + .message-attribution { + + .author-display-name { + font-size: 1.05em; + font-weight: bold;; + } + + .author-username { + font-size: 0.8em; + } + + .message-timestamp { + font-size: 0.9em; + } + } + + .message-content { + + p { + line-height: 1.1em; + margin-bottom: 10px; + color: #2a2a2a; + } } } } } - .chat-messages { - box-sizing: border-box; + .chat-input-panel { + flex-basis: 100%; flex-grow: 0; flex-shrink: 0; - - width: 320px; - height: 100%; + padding: @stage-panel-padding; + background-color: #1a1a1a; + color: #e8e8e8; + } + } + } - overflow-y: scroll; + .member-list-entry { + background-color: transparent; + transition: background-color 0.25s; + padding: 5px 2px 2px 0; + line-height: 1em; + + & .member-audio-indicator { + display: none; + } + + &.entry-idle { + color: #8a8a8a; + } + + &.entry-active { + background-color: rgba(0, 160, 0, 0.2); + } + + &.entry-audio-active { + & .member-audio-indicator { + display: block; } } - .chat-input-panel { - padding: @stage-panel-padding; - background-color: #1a1a1a; - color: #e8e8e8; + .member-profile-icon { + height: 1em; + margin-right: 5px; + } + } + + .member-audio-indicator { + color: #8a8a8a; + transition: color 0.25s; + + &.indicator-active { + color: #00ff00; } } } \ No newline at end of file