@@ -15,9 +15,14 @@ module.exports = baseVw.extend({
1515 'keyup textarea' : 'onKeyupMessage' ,
1616 'click .js-conversationSettings' : 'toggleConvoSettings' ,
1717 'click .chatConversationMenu' : 'closeConvoSettings' ,
18- 'click .js-blockUser' : 'onBlockClick'
18+ 'click .js-blockUser' : 'onBlockClick' ,
19+ 'click .js-clearConvo' : 'onClearConvoClick'
1920 } ,
2021
22+ // This is ignoered by the server. There is an issue with SQLite and
23+ // it's using a hard-coded value of 20.
24+ MESSAGES_PER_FETCH : 20 ,
25+
2126 initialize : function ( options ) {
2227 this . options = options || { } ;
2328
@@ -34,35 +39,77 @@ module.exports = baseVw.extend({
3439 }
3540
3641 this . user = this . options . user ;
42+ this . fetch = this . options . initialFetch ;
3743
38- this . collection . fetch ( {
39- data : {
40- guid : this . model . get ( 'guid' )
41- } ,
42- reset : true
43- } ) ;
44+ this . listenTo ( this . collection , 'reset' , this . render ) ;
4445
45- this . listenTo ( this . collection , 'reset' , ( ) => {
46- this . renderMessages ( ) ;
47- } ) ;
46+ this . listenTo ( this . collection , 'request' , ( cl , xhr , options ) => {
47+ var clLen = cl . length ;
4848
49- this . listenTo ( this . collection , 'request' , ( ) => {
50- this . $messagesContainer . empty ( ) ;
49+ this . fetch = xhr ;
5150 this . $loadingSpinner . removeClass ( 'hide' ) ;
51+
52+ xhr . done ( ( ) => this . $loadingSpinner . addClass ( 'hide' ) ) ;
53+ } ) ;
54+
55+ this . listenTo ( this . collection , 'update' , ( cl , options ) => {
56+ var $msgPage = $ ( '<div />' ) ,
57+ md ;
58+
59+ if ( ! cl . at ( 0 ) . viewCreated ) {
60+ // new page of messages
61+ cl . every ( ( md ) => {
62+ var processed = md . viewCreated ;
63+
64+ ! processed && $msgPage . append ( this . createMsg ( md ) . render ( ) . el ) ;
65+
66+ return ! processed ;
67+ } ) ;
68+
69+ this . addMessagesToDom ( $msgPage , true ) ;
70+ } else {
71+ // new socket message or via text area
72+ __ . filter ( cl . models , ( md ) => {
73+ return ! md . viewCreated ;
74+ } ) . forEach ( ( md ) => {
75+ this . addMessagesToDom (
76+ this . createMsg ( md ) . render ( ) . el
77+ ) ;
78+ } ) ;
79+ }
5280 } ) ;
5381
54- this . listenTo ( this . collection , 'add' , ( md ) => {
55- var el = this . $messagesScrollContainer [ 0 ] ,
56- scolledToBot = el . scrollTop >= ( el . scrollHeight - el . offsetHeight ) - 5 ;
57-
58- this . $msgWrap . append (
59- this . createMsg ( md ) . render ( ) . el
60- ) ;
82+ this . scrollHandler = __ . bind (
83+ __ . throttle ( this . onScroll , 100 ) , this
84+ ) ;
85+ } ,
6186
62- if ( scolledToBot ) {
63- el . scrollTop = el . scrollHeight ;
64- } ;
65- } ) ;
87+ onScroll : function ( e ) {
88+ var startId ;
89+
90+ if ( ! this . collection . length ) return ;
91+
92+ startId = this . collection . at ( 0 ) . id ;
93+
94+ if (
95+ ! this . fetchedAll &&
96+ ! ( this . fetch && this . fetch . state ( ) === 'pending' ) &&
97+ this . $messagesScrollContainer [ 0 ] . scrollTop === 0
98+ ) {
99+ this . collection . fetch ( {
100+ remove : false ,
101+ data : {
102+ guid : this . model . get ( 'guid' ) ,
103+ start : startId ,
104+ // backend is hard-coding limit at 20 for now (SQLite issue)
105+ limit : typeof this . options . messagesPerFetch === 'undefined' ? this . MESSAGES_PER_FETCH : this . options . messagesPerFetch
106+ }
107+ } ) . done ( ( ) => {
108+ if ( this . collection . at ( 0 ) . id === startId ) {
109+ this . fetchedAll = true ;
110+ }
111+ } ) ;
112+ }
66113 } ,
67114
68115 onClickClose : function ( ) {
@@ -96,16 +143,8 @@ module.exports = baseVw.extend({
96143 return this . $msgTextArea ;
97144 } ,
98145
99- createMsg : function ( md ) {
100- var vw = new ChatMessageVw ( {
101- model : md ,
102- user : this . user
103- } ) ;
104-
105- this . msgViews . push ( vw ) ;
106- this . registerChild ( vw ) ;
107-
108- return vw ;
146+ getScrollContainer : function ( ) {
147+ return this . $messagesScrollContainer [ 0 ] ;
109148 } ,
110149
111150 closeConvoSettings : function ( ) {
@@ -120,45 +159,100 @@ module.exports = baseVw.extend({
120159 this . user . blockUser ( this . model . get ( 'guid' ) ) ;
121160 } ,
122161
123- renderMessages : function ( ) {
124- this . $msgWrap = $ ( '<div />' ) ;
162+ onClearConvoClick : function ( ) {
163+ var formData = new FormData ( ) ;
125164
126- this . $loadingSpinner . addClass ( 'hide' ) ;
165+ formData . append ( 'guid' , this . model . get ( 'guid' ) ) ;
127166
128- if ( this . msgViews ) {
129- this . msgViews . forEach ( ( vw , index ) => {
130- vw . remove ( ) ;
131- } ) ;
132- }
167+ $ . ajax ( {
168+ url : app . serverConfig . getServerBaseUrl ( ) + '/chat_conversation?guid=' + this . model . get ( 'guid' ) ,
169+ type : 'DELETE'
170+ } ) ;
133171
134- this . msgViews = [ ] ;
172+ this . collection . reset ( ) ;
173+ this . trigger ( 'clear-conversation' ) ;
174+ } ,
135175
136- this . collection . forEach ( ( md , index ) => {
137- this . $msgWrap . append (
138- this . createMsg ( md ) . render ( ) . el
139- ) ;
176+ createMsg : function ( md ) {
177+ var vw = new ChatMessageVw ( {
178+ model : md ,
179+ user : this . user
140180 } ) ;
141181
142- this . $messagesContainer . html ( this . $msgWrap ) ;
143- this . $messagesScrollContainer [ 0 ] . scrollTop = this . $messagesScrollContainer [ 0 ] . scrollHeight ;
182+ md . viewCreated = true ;
183+ this . msgViews . push ( vw ) ;
184+ this . registerChild ( vw ) ;
185+
186+ return vw ;
187+ } ,
188+
189+ addMessagesToDom : function ( $messages , prepend , scrollTop ) {
190+ var prevScroll = { } ,
191+ $scroll = this . $messagesScrollContainer ;
192+
193+ prevScroll . height = $scroll [ 0 ] . scrollHeight ;
194+ prevScroll . top = $scroll [ 0 ] . scrollTop ;
195+
196+ if ( ! prepend ) {
197+ this . $messagesContainer . append ( $messages ) ;
198+
199+ if ( __ . isNumber ( scrollTop ) ) {
200+ $scroll [ 0 ] . scrollTop = scrollTop ;
201+ } else if ( prevScroll . top >= prevScroll . height - $scroll [ 0 ] . clientHeight - 10 ) {
202+ $scroll [ 0 ] . scrollTop = $scroll [ 0 ] . scrollHeight ;
203+ }
204+ } else {
205+ this . $messagesContainer . prepend ( $messages ) ;
206+ $scroll [ 0 ] . scrollTop = prevScroll . top + ( $scroll [ 0 ] . scrollHeight - prevScroll . height ) ;
207+ }
144208 } ,
145209
146210 render : function ( ) {
147211 loadTemplate ( './js/templates/chatConversation.html' , ( tmpl ) => {
212+ var $msgWrap = $ ( '<div />' ) ;
213+
214+ if ( this . msgViews ) {
215+ this . msgViews . forEach ( ( vw , index ) => {
216+ vw . remove ( ) ;
217+ } ) ;
218+ }
219+
220+ this . msgViews = [ ] ;
221+
148222 this . $el . html (
149223 tmpl ( __ . extend ( this . model . toJSON ( ) , {
150- serverUrl : app . serverConfig . getServerBaseUrl ( ) ,
151- moment : moment
224+ isFetching : this . fetch && this . fetch . state ( ) === 'pending' ,
225+ messages : this . collection . toJSON ( )
152226 } ) )
153227 ) ;
154228
155229 this . $ ( '.chatConversationMessage textarea' ) . focus ( )
156230 this . $messagesScrollContainer = this . $ ( '.chatConversationContent' ) ;
231+ this . $messagesScrollContainer . on ( 'scroll' , this . scrollHandler ) ;
157232 this . $loadingSpinner = this . $messagesScrollContainer . find ( '.js-loadingSpinner' ) ;
158233 this . $messagesContainer = this . $messagesScrollContainer . find ( '.js-messagesContainer' ) ;
159234 this . $msgTextArea = this . $ ( 'textarea' ) ;
235+
236+ if ( this . collection . length ) {
237+ this . collection . forEach ( ( md ) => {
238+ $msgWrap . append (
239+ this . createMsg ( md ) . render ( ) . el
240+ ) ;
241+ } ) ;
242+
243+ setTimeout ( ( ) => {
244+ this . addMessagesToDom ( $msgWrap , null ,
245+ __ . isNumber ( this . options . initialScroll ) ? this . options . initialScroll : 9999 ) ;
246+ } , 0 ) ;
247+ }
160248 } ) ;
161249
162250 return this ;
163- }
251+ } ,
252+
253+ remove : function ( ) {
254+ this . $scrollContainer && this . $scrollContainer . off ( 'scroll' , this . scrollHandler ) ;
255+
256+ baseVw . prototype . remove . apply ( this , arguments ) ;
257+ }
164258} ) ;
0 commit comments