2017-07-11 03:09:31 +00:00
< template lang = "pug" >
b - modal # inbox - modal ( title = "" , : hide - footer = "true" , size = 'lg' )
2018-04-29 16:08:09 +00:00
. header - wrap . container . align - items - center ( slot = "modal-header" )
. row . align - items - center
2017-07-11 03:09:31 +00:00
. col - 4
2018-04-29 16:08:09 +00:00
. row . align - items - center
2017-07-11 03:09:31 +00:00
. col - 2
. svg - icon . envelope ( v - html = "icons.messageIcon" )
. col - 6
h2 . text - center ( v - once ) { { $t ( 'messages' ) } }
// @TODO: Implement this after we fix username bug
// .col-2.offset-1
// button.btn.btn-secondary(@click='toggleClick()') +
2017-09-21 23:53:45 +00:00
. col - 4. offset - 4
. svg - icon . close ( v - html = "icons.svgClose" , @ click = 'close()' )
2018-04-29 16:08:09 +00:00
toggle - switch . float - right (
2018-04-29 13:31:23 +00:00
: label = "optTextSet.switchDescription" ,
: checked = "!this.user.inbox.optOut"
: hoverText = "optTextSet.popoverText" ,
@ change = "toggleOpt()"
2018-04-29 01:05:31 +00:00
)
2017-07-11 03:09:31 +00:00
// .col-8.to-form(v-if='displayCreate')
// strong To:
// b-form-input
. row
. col - 4. sidebar
. search - section
b - form - input ( : placeholder = "$t('search')" , v - model = 'search' )
. empty - messages . text - center ( v - if = 'filtersConversations.length === 0' )
. svg - icon . envelope ( v - html = "icons.messageIcon" )
h4 ( v - once ) { { $t ( 'emptyMessagesLine1' ) } }
2018-05-04 21:04:06 +00:00
p ( v - if = "!user.flags.chatRevoked" ) { { $t ( 'emptyMessagesLine2' ) } }
2017-07-11 03:09:31 +00:00
. conversations ( v - if = 'filtersConversations.length > 0' )
2017-09-19 20:35:32 +00:00
. conversation ( v - for = 'conversation in filtersConversations' , @ click = 'selectConversation(conversation.key)' ,
2017-10-13 04:40:53 +00:00
: class = "{active: selectedConversation.key === conversation.key}" )
2017-07-11 03:09:31 +00:00
div
2017-07-29 22:08:36 +00:00
span ( : class = "userLevelStyle(conversation)" ) { { conversation . name } }
2017-08-26 02:56:21 +00:00
span . timeago { { conversation . date | timeAgo } }
2018-01-25 20:46:49 +00:00
div { { conversation . lastMessageText ? conversation . lastMessageText . substring ( 0 , 30 ) : '' } }
2018-05-02 16:08:43 +00:00
. col - 8. messages . d - flex . flex - column . justify - content - between
2017-10-13 04:40:53 +00:00
. empty - messages . text - center ( v - if = 'activeChat.length === 0 && !selectedConversation.key' )
2017-09-01 21:26:10 +00:00
. svg - icon . envelope ( v - html = "icons.messageIcon" )
2018-05-04 21:04:06 +00:00
h4 { { placeholderTexts . title } }
p ( v - html = "placeholderTexts.description" )
2017-10-13 04:40:53 +00:00
. empty - messages . text - center ( v - if = 'activeChat.length === 0 && selectedConversation.key' )
p { { $t ( 'beginningOfConversation' , { userName : selectedConversation . name } ) } }
2018-05-02 16:08:43 +00:00
chat - message . message - scroll ( v - if = "activeChat.length > 0" , : chat . sync = 'activeChat' , : inbox = 'true' , ref = "chatscroll" )
. pm - disabled - caption . text - center ( v - if = "user.inbox.optOut && selectedConversation.key" )
h4 { { $t ( 'PMDisabledCaptionTitle' ) } }
p { { $t ( 'PMDisabledCaptionText' ) } }
2017-07-11 03:09:31 +00:00
// @TODO: Implement new message header here when we fix the above
2018-05-04 21:04:06 +00:00
. new - message - row ( v - if = 'selectedConversation.key && !user.flags.chatRevoked' )
2018-08-10 13:46:46 +00:00
textarea (
v - model = 'newMessage' ,
@ keyup . ctrl . enter = 'sendPrivateMessage()' ,
maxlength = '3000'
)
button . btn . btn - secondary ( @ click = 'sendPrivateMessage()' ) { { $t ( 'send' ) } }
. row
span . ml - 3 { { currentLength } } / 3000
2017-07-11 03:09:31 +00:00
< / template >
< style lang = "scss" scoped >
@ import '~client/assets/scss/colors.scss' ;
2018-04-29 16:08:09 +00:00
. header - wrap {
padding : 0.5 em ;
h2 {
margin : 0 ;
line - height : 1 ;
}
}
2017-07-11 03:09:31 +00:00
. envelope {
color : $gray - 400 ! important ;
2018-04-29 16:08:09 +00:00
margin : 0 ;
2017-07-11 03:09:31 +00:00
}
2017-09-21 23:53:45 +00:00
. close {
margin - top : .5 em ;
width : 15 px ;
2018-04-29 16:08:09 +00:00
position : absolute ;
top : - 1.9 em ;
right : 0.3 em ;
2017-07-11 03:09:31 +00:00
}
. sidebar {
background - color : $gray - 700 ;
min - height : 600 px ;
padding : 0 ;
. search - section {
padding : 1 em ;
box - shadow : 0 1 px 2 px 0 rgba ( 26 , 24 , 29 , 0.24 ) ;
}
}
. messages {
position : relative ;
padding - left : 0 ;
2017-07-29 22:08:36 +00:00
padding - bottom : 6 em ;
2017-07-11 03:09:31 +00:00
}
2017-08-29 20:23:09 +00:00
. message - scroll {
max - height : 500 px ;
2018-05-02 16:08:43 +00:00
overflow - x : scroll ;
@ media ( min - width : 992 px ) {
overflow - x : hidden ;
overflow - y : scroll ;
}
2017-08-29 20:23:09 +00:00
}
2017-07-11 03:09:31 +00:00
. to - form input {
width : 60 % ;
display : inline - block ;
margin - left : 1 em ;
}
. empty - messages {
margin - top : 10 em ;
color : $gray - 400 ;
padding : 1 em ;
h4 {
color : $gray - 400 ;
margin - top : 1 em ;
}
. envelope {
width : 30 px ;
margin : 0 auto ;
}
}
2018-05-02 16:08:43 +00:00
. pm - disabled - caption {
padding - top : 1 em ;
background - color : $gray - 700 ;
z - index : 2 ;
h4 , p {
color : $gray - 300 ;
}
h4 {
margin - top : 0 ;
margin - bottom : 0.4 em ;
}
p {
font - size : 12 px ;
margin - bottom : 0 ;
}
}
2017-07-11 03:09:31 +00:00
. new - message - row {
background - color : $gray - 700 ;
position : absolute ;
bottom : 0 ;
height : 88 px ;
width : 100 % ;
padding : 1 em ;
2017-09-21 23:53:45 +00:00
textarea {
height : 80 % ;
2017-07-11 03:09:31 +00:00
display : inline - block ;
2017-09-21 23:53:45 +00:00
vertical - align : bottom ;
2017-07-11 03:09:31 +00:00
width : 80 % ;
}
button {
2017-09-21 23:53:45 +00:00
vertical - align : bottom ;
display : inline - block ;
2017-07-11 03:09:31 +00:00
box - shadow : none ;
margin - left : 1 em ;
}
}
2017-08-30 21:19:17 +00:00
. conversations {
max - height : 400 px ;
2018-05-02 16:08:43 +00:00
overflow - x : hidden ;
overflow - y : scroll ;
2017-08-30 21:19:17 +00:00
}
2017-07-11 03:09:31 +00:00
. conversation {
padding : 1.5 em ;
background : $white ;
height : 80 px ;
. timeago {
margin - left : 1 em ;
}
}
. conversation . active {
border : 1 px solid $purple - 400 ;
}
. conversation : hover {
cursor : pointer ;
}
< / style >
< script >
2017-08-30 21:19:17 +00:00
import Vue from 'vue' ;
2017-07-11 03:09:31 +00:00
import moment from 'moment' ;
import filter from 'lodash/filter' ;
2017-09-01 21:26:10 +00:00
import sortBy from 'lodash/sortBy' ;
2018-02-25 15:04:55 +00:00
import groupBy from 'lodash/groupBy' ;
2017-07-11 03:09:31 +00:00
import { mapState } from 'client/libs/store' ;
2017-07-29 22:08:36 +00:00
import styleHelper from 'client/mixins/styleHelper' ;
2018-04-29 13:31:23 +00:00
import toggleSwitch from 'client/components/ui/toggleSwitch' ;
2017-07-11 03:09:31 +00:00
import messageIcon from 'assets/svg/message.svg' ;
2017-07-29 22:08:36 +00:00
import chatMessage from '../chat/chatMessages' ;
2017-09-21 23:53:45 +00:00
import svgClose from 'assets/svg/close.svg' ;
2017-07-11 03:09:31 +00:00
export default {
2017-07-29 22:08:36 +00:00
mixins : [ styleHelper ] ,
2017-07-11 03:09:31 +00:00
components : {
2017-07-29 22:08:36 +00:00
chatMessage ,
2018-04-29 13:31:23 +00:00
toggleSwitch ,
2017-07-11 03:09:31 +00:00
} ,
2017-10-13 04:40:53 +00:00
mounted ( ) {
this . $root . $on ( 'habitica::new-inbox-message' , ( data ) => {
2017-11-08 17:40:37 +00:00
this . $root . $emit ( 'bv::show::modal' , 'inbox-modal' ) ;
2017-10-13 04:40:53 +00:00
const conversation = this . conversations . find ( convo => {
return convo . key === data . userIdToMessage ;
} ) ;
if ( conversation ) {
this . selectConversation ( data . userIdToMessage ) ;
return ;
}
const newMessage = {
text : '' ,
timestamp : new Date ( ) ,
user : data . userName ,
uuid : data . userIdToMessage ,
id : '' ,
} ;
this . $set ( this . user . inbox . messages , data . userIdToMessage , newMessage ) ;
this . selectConversation ( data . userIdToMessage ) ;
} ) ;
} ,
destroyed ( ) {
this . $root . $off ( 'habitica::new-inbox-message' ) ;
} ,
2017-07-11 03:09:31 +00:00
data ( ) {
return {
icons : Object . freeze ( {
messageIcon ,
2017-09-21 23:53:45 +00:00
svgClose ,
2017-07-11 03:09:31 +00:00
} ) ,
displayCreate : true ,
2017-10-13 04:40:53 +00:00
selectedConversation : { } ,
2017-07-11 03:09:31 +00:00
search : '' ,
newMessage : '' ,
2017-07-29 22:08:36 +00:00
activeChat : [ ] ,
2018-04-29 01:05:31 +00:00
showPopover : false ,
2017-07-11 03:09:31 +00:00
} ;
} ,
2017-08-26 02:56:21 +00:00
filters : {
timeAgo ( value ) {
return moment ( new Date ( value ) ) . fromNow ( ) ;
} ,
} ,
2017-07-11 03:09:31 +00:00
computed : {
... mapState ( { user : 'user.data' } ) ,
conversations ( ) {
2018-02-25 15:04:55 +00:00
const inboxGroup = groupBy ( this . user . inbox . messages , 'uuid' ) ;
// Create conversation objects
const convos = [ ] ;
for ( let key in inboxGroup ) {
const convoSorted = sortBy ( inboxGroup [ key ] , [ ( o ) => {
return o . timestamp ;
} ] ) ;
// Fix poor inbox chat models
const newChatModels = convoSorted . map ( chat => {
let newChat = Object . assign ( { } , chat ) ;
if ( newChat . sent ) {
newChat . toUUID = newChat . uuid ;
newChat . toUser = newChat . user ;
newChat . uuid = this . user . _id ;
newChat . user = this . user . profile . name ;
newChat . contributor = this . user . contributor ;
newChat . backer = this . user . backer ;
}
return newChat ;
} ) ;
const recentMessage = newChatModels [ newChatModels . length - 1 ] ;
// Special case where we have placeholder message because conversations are just grouped messages for now
if ( ! recentMessage . text ) {
newChatModels . splice ( newChatModels . length - 1 , 1 ) ;
2017-07-11 03:09:31 +00:00
}
2018-02-25 15:04:55 +00:00
const convoModel = {
name : recentMessage . toUser ? recentMessage . toUser : recentMessage . user , // Handles case where from user sent the only message or the to user sent the only message
key : recentMessage . toUUID ? recentMessage . toUUID : recentMessage . uuid ,
messages : newChatModels ,
lastMessageText : recentMessage . text ,
date : recentMessage . timestamp ,
2017-09-01 21:26:10 +00:00
} ;
2018-02-25 15:04:55 +00:00
convos . push ( convoModel ) ;
2017-07-11 03:09:31 +00:00
}
2018-02-25 15:04:55 +00:00
// Sort models by most recent
const conversations = sortBy ( convos , [ ( o ) => {
2017-09-19 20:35:32 +00:00
return moment ( o . date ) . toDate ( ) ;
} ] ) ;
2018-02-28 15:17:54 +00:00
return conversations . reverse ( ) ;
2017-07-11 03:09:31 +00:00
} ,
filtersConversations ( ) {
2017-09-19 20:35:32 +00:00
if ( ! this . search ) return this . conversations ;
2017-07-11 03:09:31 +00:00
return filter ( this . conversations , ( conversation ) => {
return conversation . name . toLowerCase ( ) . indexOf ( this . search . toLowerCase ( ) ) !== - 1 ;
} ) ;
} ,
2018-08-10 13:46:46 +00:00
currentLength ( ) {
return this . newMessage . length ;
} ,
2018-05-04 21:04:06 +00:00
placeholderTexts ( ) {
if ( this . user . flags . chatRevoked ) {
return {
title : this . $t ( 'PMPlaceholderTitleRevoked' ) ,
description : this . $t ( 'PMPlaceholderDescriptionRevoked' ) ,
} ;
}
return {
title : this . $t ( 'PMPlaceholderTitle' ) ,
description : this . $t ( 'PMPlaceholderDescription' ) ,
} ;
} ,
2018-04-29 13:48:10 +00:00
optTextSet ( ) {
2018-04-29 13:31:23 +00:00
if ( ! this . user . inbox . optOut ) {
return {
2018-06-27 17:13:13 +00:00
switchDescription : this . $t ( 'PMReceive' ) ,
2018-04-29 13:31:23 +00:00
popoverText : this . $t ( 'PMEnabledOptPopoverText' ) ,
} ;
}
return {
2018-06-27 17:13:13 +00:00
switchDescription : this . $t ( 'PMReceive' ) ,
2018-04-29 13:31:23 +00:00
popoverText : this . $t ( 'PMDisabledOptPopoverText' ) ,
} ;
2018-04-29 13:48:10 +00:00
} ,
2017-07-11 03:09:31 +00:00
} ,
methods : {
toggleClick ( ) {
this . displayCreate = ! this . displayCreate ;
} ,
2018-04-29 13:48:10 +00:00
toggleOpt ( ) {
this . $store . dispatch ( 'user:togglePrivateMessagesOpt' ) ;
2018-04-29 00:07:03 +00:00
} ,
2017-07-11 03:09:31 +00:00
selectConversation ( key ) {
2017-09-19 23:05:30 +00:00
let convoFound = this . conversations . find ( ( conversation ) => {
return conversation . key === key ;
} ) ;
2017-10-13 04:40:53 +00:00
this . selectedConversation = convoFound ;
2017-09-19 23:05:30 +00:00
let activeChat = convoFound . messages ;
2017-09-01 21:26:10 +00:00
activeChat = sortBy ( activeChat , [ ( o ) => {
return moment ( o . timestamp ) . toDate ( ) ;
} ] ) ;
2017-08-26 02:56:21 +00:00
this . $set ( this , 'activeChat' , activeChat ) ;
2017-09-01 21:26:10 +00:00
2017-08-30 21:19:17 +00:00
Vue . nextTick ( ( ) => {
2018-01-21 00:00:54 +00:00
if ( ! this . $refs . chatscroll ) return ;
2017-08-30 21:19:17 +00:00
let chatscroll = this . $refs . chatscroll . $el ;
chatscroll . scrollTop = chatscroll . scrollHeight ;
} ) ;
2017-07-11 03:09:31 +00:00
} ,
sendPrivateMessage ( ) {
2017-09-21 23:53:45 +00:00
if ( ! this . newMessage ) return ;
2017-09-19 23:05:30 +00:00
let convoFound = this . conversations . find ( ( conversation ) => {
2017-10-13 04:40:53 +00:00
return conversation . key === this . selectedConversation . key ;
2017-09-19 23:05:30 +00:00
} ) ;
2017-07-11 03:09:31 +00:00
this . $store . dispatch ( 'members:sendPrivateMessage' , {
2017-10-13 04:40:53 +00:00
toUserId : this . selectedConversation . key ,
2017-07-11 03:09:31 +00:00
message : this . newMessage ,
} ) ;
2017-09-19 23:05:30 +00:00
convoFound . messages . push ( {
2017-07-11 03:09:31 +00:00
text : this . newMessage ,
timestamp : new Date ( ) ,
2017-09-01 21:26:10 +00:00
user : this . user . profile . name ,
uuid : this . user . _id ,
2018-02-05 18:41:28 +00:00
contributor : this . user . contributor ,
2017-07-11 03:09:31 +00:00
} ) ;
2017-07-29 22:08:36 +00:00
2017-09-19 23:05:30 +00:00
this . activeChat = convoFound . messages ;
2017-07-29 22:08:36 +00:00
2017-09-19 23:05:30 +00:00
convoFound . lastMessageText = this . newMessage ;
convoFound . date = new Date ( ) ;
2017-07-11 03:09:31 +00:00
this . newMessage = '' ;
2017-08-30 21:19:17 +00:00
Vue . nextTick ( ( ) => {
2018-01-21 00:00:54 +00:00
if ( ! this . $refs . chatscroll ) return ;
2017-08-30 21:19:17 +00:00
let chatscroll = this . $refs . chatscroll . $el ;
chatscroll . scrollTop = chatscroll . scrollHeight ;
} ) ;
2017-07-11 03:09:31 +00:00
} ,
2017-09-21 23:53:45 +00:00
close ( ) {
2017-11-08 17:40:37 +00:00
this . $root . $emit ( 'bv::hide::modal' , 'inbox-modal' ) ;
2017-09-21 23:53:45 +00:00
} ,
2017-07-11 03:09:31 +00:00
} ,
} ;
< / script >