168168 v-show =" Object.keys(room).length && showFooter"
169169 >
170170 <transition name =" vac-slide-up" >
171- <div v-if =" messageReply" class =" vac-reply-container" >
171+ <div
172+ v-if =" messageReply"
173+ class =" vac-reply-container"
174+ :style =" { bottom: `${roomFooterHeight}px` }"
175+ >
172176 <div class =" vac-reply-box" >
173177 <img
174178 v-if =" isImageCheck(messageReply.file)"
191195 </div >
192196 </transition >
193197
194- <div class =" vac-box-footer" >
198+ <transition name =" vac-slide-up" >
199+ <div
200+ v-if =" filteredUsersTag.length"
201+ class =" vac-tags-container vac-app-box-shadow"
202+ :style =" { bottom: `${roomFooterHeight}px` }"
203+ >
204+ <div
205+ class =" vac-tags-box"
206+ v-for =" user in filteredUsersTag"
207+ :key =" user._id"
208+ @click =" selectUserTag(user)"
209+ >
210+ <div class =" vac-tags-info" >
211+ <div
212+ v-if =" user.avatar"
213+ class =" vac-room-avatar vac-tags-avatar"
214+ :style =" { 'background-image': `url('${user.avatar}')` }"
215+ ></div >
216+ <div class =" vac-tags-username" >
217+ {{ user.username }}
218+ </div >
219+ </div >
220+ </div >
221+ </div >
222+ </transition >
223+
224+ <div
225+ class =" vac-box-footer"
226+ :class =" { 'vac-app-box-shadow': filteredUsersTag.length }"
227+ >
195228 <div class =" vac-icon-textarea-left" v-if =" showAudio && !imageFile" >
196229 <div class =" vac-svg-button" @click =" recordAudio" >
197230 <slot
255288 }"
256289 v-model =" message"
257290 @input =" onChangeInput"
258- @keydown.esc =" resetMessage "
291+ @keydown.esc =" escapeTextarea "
259292 @keydown.enter.exact.prevent =" "
260293 ></textarea >
261294
@@ -339,6 +372,7 @@ import EmojiPicker from './EmojiPicker'
339372
340373const { messagesValid } = require (' ../utils/roomValidation' )
341374const { detectMobile , iOSDevice } = require (' ../utils/mobileDetection' )
375+ import filteredUsers from ' ../utils/filterItems'
342376import typingText from ' ../utils/typingText'
343377
344378export default {
@@ -402,33 +436,40 @@ export default {
402436 recorderStream: {},
403437 recorder: {},
404438 recordedChunks: [],
405- keepKeyboardOpen: false
439+ keepKeyboardOpen: false ,
440+ filteredUsersTag: [],
441+ textareaCursorPosition: null ,
442+ roomFooterHeight: 0
406443 }
407444 },
408445
409446 mounted () {
410447 this .newMessages = []
448+ const isMobile = detectMobile ()
411449
412450 window .addEventListener (' keyup' , e => {
413451 if (e .keyCode === 13 && ! e .shiftKey ) {
414- if (detectMobile () ) {
452+ if (isMobile ) {
415453 this .message = this .message + ' \n '
416454 setTimeout (() => this .onChangeInput (), 0 )
417455 } else {
418456 this .sendMessage ()
419457 }
420458 }
459+
460+ this .updateShowUsersTag ()
421461 })
422462
423- if (detectMobile ()) {
424- this .$refs [' roomTextarea' ].addEventListener (' blur' , () =>
425- setTimeout (() => (this .keepKeyboardOpen = false ), 0 )
426- )
427- this .$refs [' roomTextarea' ].addEventListener (
428- ' click' ,
429- () => (this .keepKeyboardOpen = true )
430- )
431- }
463+ this .$refs [' roomTextarea' ].addEventListener (' click' , () => {
464+ if (isMobile) this .keepKeyboardOpen = true
465+ this .updateShowUsersTag ()
466+ })
467+
468+ this .$refs [' roomTextarea' ].addEventListener (' blur' , () => {
469+ this .filteredUsersTag = []
470+ this .textareaCursorPosition = null
471+ if (isMobile) setTimeout (() => (this .keepKeyboardOpen = false ), 0 )
472+ })
432473
433474 this .$refs .scrollContainer .addEventListener (' scroll' , e => {
434475 this .hideOptions = true
@@ -545,6 +586,55 @@ export default {
545586 },
546587
547588 methods: {
589+ updateShowUsersTag () {
590+ if (this .$refs [' roomTextarea' ]) {
591+ if (
592+ this .textareaCursorPosition ===
593+ this .$refs [' roomTextarea' ].selectionStart
594+ ) {
595+ return
596+ }
597+
598+ this .textareaCursorPosition = this .$refs [' roomTextarea' ].selectionStart
599+
600+ let n = this .textareaCursorPosition
601+
602+ while (
603+ n > 0 &&
604+ this .message .charAt (n - 1 ) !== ' @' &&
605+ this .message .charAt (n - 1 ) !== ' '
606+ ) {
607+ n--
608+ }
609+
610+ const beforeTag = this .message .charAt (n - 2 )
611+ const notLetterNumber = ! beforeTag .match (/ ^ [0-9a-zA-Z ] + $ / )
612+
613+ if (
614+ this .message .charAt (n - 1 ) === ' @' &&
615+ (! beforeTag || beforeTag === ' ' || notLetterNumber)
616+ ) {
617+ const query = this .message .substring (n, this .textareaCursorPosition )
618+
619+ this .filteredUsersTag = filteredUsers (
620+ this .room .users ,
621+ ' username' ,
622+ query,
623+ true
624+ )
625+ } else {
626+ this .filteredUsersTag = []
627+ this .textareaCursorPosition = null
628+ }
629+ }
630+ },
631+ selectUserTag (user ) {
632+ const cursorPosition = this .$refs [' roomTextarea' ].selectionStart - 1
633+ this .message =
634+ this .message .substr (0 , cursorPosition + 1 ) +
635+ user .username +
636+ this .message .substr (cursorPosition + 1 )
637+ },
548638 onImgLoad () {
549639 let height = this .$refs .imageFile .height
550640 if (height < 30 ) height = 30
@@ -624,6 +714,10 @@ export default {
624714 addNewMessage (message ) {
625715 this .newMessages .push (message)
626716 },
717+ escapeTextarea () {
718+ if (this .filteredUsersTag .length ) this .filteredUsersTag = []
719+ else this .resetMessage ()
720+ },
627721 resetMessage (disableMobileFocus = null , editFile = null ) {
628722 this .$emit (' typing-message' , null )
629723
@@ -755,6 +849,10 @@ export default {
755849
756850 el .style .height = 0
757851 el .style .height = el .scrollHeight - padding * 2 + ' px'
852+
853+ setTimeout (() => {
854+ this .roomFooterHeight = this .$refs [' roomFooter' ].clientHeight
855+ }, 10 )
758856 },
759857 addEmoji (emoji ) {
760858 this .message += emoji .icon
@@ -942,7 +1040,7 @@ export default {
9421040}
9431041
9441042.vac-room-footer {
945- width : calc ( 100% - 1 px ) ;
1043+ width : 100% ;
9461044 border-bottom-right-radius : 4px ;
9471045 z-index : 10 ;
9481046}
@@ -954,12 +1052,56 @@ export default {
9541052 padding : 10px 8px 10px ;
9551053}
9561054
1055+ .vac-tags-container {
1056+ position : absolute ;
1057+ display : flex ;
1058+ flex-direction : column ;
1059+ align-items : center ;
1060+ width : 100% ;
1061+
1062+ .vac-tags-box {
1063+ display : flex ;
1064+ width : 100% ;
1065+ overflow : hidden ;
1066+ cursor : pointer ;
1067+ background : var (--chat-footer-bg-color );
1068+
1069+ & :hover {
1070+ background : var (--chat-footer-bg-color-tag-active );
1071+ transition : background-color 0.3s cubic-bezier (0.25 , 0.8 , 0.5 , 1 );
1072+ }
1073+
1074+ & :not (:hover ) {
1075+ transition : background-color 0.3s cubic-bezier (0.25 , 0.8 , 0.5 , 1 );
1076+ }
1077+ }
1078+
1079+ .vac-tags-info {
1080+ display : flex ;
1081+ overflow : hidden ;
1082+ padding : 10px 20px ;
1083+ align-items : center ;
1084+ }
1085+
1086+ .vac-tags-avatar {
1087+ height : 34px ;
1088+ width : 34px ;
1089+ min-height : 34px ;
1090+ min-width : 34px ;
1091+ }
1092+
1093+ .vac-tags-username {
1094+ font-size : 14px ;
1095+ }
1096+ }
1097+
9571098.vac-reply-container {
1099+ position : absolute ;
9581100 display : flex ;
9591101 padding : 10px 10px 0 10px ;
960- background : var (--chat-content -bg-color );
1102+ background : var (--chat-footer -bg-color );
9611103 align-items : center ;
962- max- width : 100% ;
1104+ width : calc ( 100% - 20 px ) ;
9631105
9641106 .vac-reply-box {
9651107 width : 100% ;
@@ -1252,6 +1394,11 @@ export default {
12521394
12531395 .vac-reply-container {
12541396 padding : 5px 8px ;
1397+ width : calc (100% - 16px );
1398+ }
1399+
1400+ .vac-tags-container .vac-tags-info {
1401+ padding : 8px 12px ;
12551402 }
12561403
12571404 .vac-icon-scroll {
0 commit comments