@ -280,6 +280,13 @@
this . $ ( '.discussion-container' ) . append ( this . view . el ) ;
this . view . render ( ) ;
this . memberView = new Whisper . MemberListView ( {
el : this . $ ( '.member-list-container' ) ,
onClicked : this . selectMember . bind ( this ) ,
} ) ;
this . memberView . render ( ) ;
this . $messageField = this . $ ( '.send-message' ) ;
this . onResize = this . forceUpdateMessageFieldSize . bind ( this ) ;
@ -307,9 +314,9 @@
events : {
keydown : 'onKeyDown' ,
'submit .send' : ' checkUnverifiedSendMessage ',
'input .send-message' : ' updateMessageFieldSize ',
'keydown .send-message' : ' updateMessageFieldSize ',
'submit .send' : ' handleSubmitPressed ',
'input .send-message' : ' handleInputEvent ',
'keydown .send-message' : ' handleInputEvent ',
'keyup .send-message' : 'onKeyUp' ,
click : 'onClick' ,
'click .bottom-bar' : 'focusMessageField' ,
@ -1556,6 +1563,34 @@
dialog . focusCancel ( ) ;
} ,
selectMember ( member ) {
const stripQuery = input => {
const pos = input . lastIndexOf ( '@' ) ;
// This should never happen, but we check just in case
if ( pos === - 1 ) {
return input ;
}
return input . substr ( 0 , pos ) ;
} ;
const prev = stripQuery ( this . $messageField . val ( ) ) ;
const result = ` ${ prev } @ ${ member . authorPhoneNumber } ` ;
this . $messageField . val ( result ) ;
this . $messageField . trigger ( 'input' ) ;
} ,
async handleSubmitPressed ( e , options = { } ) {
if ( this . memberView . membersShown ( ) ) {
const member = this . memberView . selectedMember ( ) ;
this . selectMember ( member ) ;
} else {
await this . checkUnverifiedSendMessage ( e , options ) ;
}
} ,
async checkUnverifiedSendMessage ( e , options = { } ) {
e . preventDefault ( ) ;
this . sendStart = Date . now ( ) ;
@ -2112,7 +2147,10 @@
}
} ,
updateMessageFieldSize ( event ) {
// Note: not only input, but keypresses too (rename?)
handleInputEvent ( event ) {
this . maybeShowMembers ( event ) ;
const keyCode = event . which || event . keyCode ;
if (
@ -2126,6 +2164,41 @@
this . $ ( '.bottom-bar form' ) . submit ( ) ;
return ;
}
const keyPressedLeft = keyCode === 37 ;
const keyPressedUp = keyCode === 38 ;
const keyPressedRight = keyCode === 39 ;
const keyPressedDown = keyCode === 40 ;
const keyPressedTab = keyCode === 9 ;
const preventDefault = keyPressedUp || keyPressedDown || keyPressedTab ;
if ( this . memberView . membersShown ( ) && preventDefault ) {
if ( keyPressedDown ) {
this . memberView . selectDown ( ) ;
} else if ( keyPressedUp ) {
this . memberView . selectUp ( ) ;
} else if ( keyPressedTab ) {
// Tab is treated as Enter in this context
this . handleSubmitPressed ( ) ;
}
const $selected = this . $ ( '.member-selected' ) ;
if ( $selected . length ) {
$selected [ 0 ] . scrollIntoView ( { behavior : 'smooth' } ) ;
}
event . preventDefault ( ) ;
return ;
}
if ( keyPressedLeft || keyPressedRight ) {
this . $messageField . trigger ( 'input' ) ;
}
this . updateMessageFieldSize ( ) ;
} ,
updateMessageFieldSize ( ) {
this . toggleMicrophone ( ) ;
this . view . measureScrollPosition ( ) ;
@ -2150,6 +2223,63 @@
this . view . scrollToBottomIfNeeded ( ) ;
} ,
maybeShowMembers ( event ) {
const filterMembers = ( caseSensitiveQuery , member ) => {
const { authorPhoneNumber , authorProfileName } = member ;
const profileName = authorProfileName
? authorProfileName . toLowerCase ( )
: '' ;
const query = caseSensitiveQuery . toLowerCase ( ) ;
if ( authorPhoneNumber . includes ( query ) || profileName . includes ( query ) ) {
return true ;
}
return false ;
} ;
const getQuery = input => {
const atPos = input . lastIndexOf ( '@' ) ;
if ( atPos === - 1 ) {
return null ;
}
// Whitespace is required right before @
if ( atPos > 0 && /\w/ . test ( input . substr ( atPos - 1 , 1 ) ) ) {
return null ;
}
const query = input . substr ( atPos + 1 ) ;
// No whitespaces allowed in a query
if ( /\s/ . test ( query ) ) {
return null ;
}
return query ;
} ;
const query = getQuery ( event . target . value ) ;
// TODO: for now, extract members from the conversation,
// but change to use a server endpoint in the future
let allMembers = this . model . messageCollection . models . map (
d => d . propsForMessage
) ;
allMembers = allMembers . filter ( d => ! ! d ) ;
allMembers = _ . uniq ( allMembers , true , d => d . authorPhoneNumber ) ;
let membersToShow = [ ] ;
if ( query ) {
membersToShow =
query !== ''
? allMembers . filter ( m => filterMembers ( query , m ) )
: allMembers ;
}
this . memberView . updateMembers ( membersToShow ) ;
} ,
forceUpdateMessageFieldSize ( event ) {
if ( this . isHidden ( ) ) {
return ;