@@ -90,7 +90,7 @@ var onMessagesModified = function(type,msg) {
9090 }
9191 if ( msg && msg . id == "nav" && msg . t == "modify" && active != "map" )
9292 return ; // don't show an updated nav message if we're just in the menu
93- showMessage ( msg && msg . id , false ) ;
93+ showMessagesScroller ( msg , false )
9494} ;
9595Bangle . on ( "message" , onMessagesModified ) ;
9696
@@ -246,32 +246,169 @@ function showMusicMessage(msg) {
246246 } , 400 ) ;
247247}
248248
249- function showMessageScroller ( msg ) {
250- cancelReloadTimeout ( ) ;
249+ function showMessagesScroller ( msg , persist , alreadyProcessed ) {
250+ if ( persist === undefined ) { persist = true ; }
251+ const MSG_IDX = msg ? MESSAGES . findIndex ( ( m ) => m . id == msg . id ) : 0 ;
252+ const INITIATED_FROM = (
253+ alreadyProcessed === undefined ? "other function" :
254+ ( MSG_IDX <= alreadyProcessed . idxSpan . start ? "scrolling up" :
255+ ( MSG_IDX >= alreadyProcessed . idxSpan . stop - 1 ? "scrolling down" :
256+ undefined ) )
257+ ) ;
258+ if ( ! alreadyProcessed ) { alreadyProcessed = { idxSpan :{ } } ; } // So `alreadyProcessed.idxSpan.start/stop` can be looked for on first run.
259+
260+ const WU = require ( "widget_utils" ) ;
261+ WU . hide ( ) ;
262+
263+ if ( replying ) { return ; }
264+ if ( persist ) { cancelReloadTimeout ( ) ; } else { resetReloadTimeout ( ) ; }
265+
266+ let idxSpan = (
267+ INITIATED_FROM === "other function" ?
268+ { start : Math . max ( MSG_IDX - 1 , 0 ) , stop : Math . min ( MSG_IDX + 2 , MESSAGES . length ) } :
269+ ( INITIATED_FROM === "scrolling up" ?
270+ { start : MSG_IDX , stop : alreadyProcessed . idxSpan . start } :
271+ ( INITIATED_FROM === "scrolling down" ?
272+ { start : alreadyProcessed . idxSpan . stop , stop : Math . min ( MSG_IDX + 1 , MESSAGES . length ) } :
273+ undefined ) )
274+ ) ;
275+
251276 active = "scroller" ;
252277 var bodyFont = fontBig ;
253278 g . setFont ( bodyFont ) ;
254- var lines = [ ] ;
255- if ( msg . title ) lines = g . wrapString ( msg . title , g . getWidth ( ) - 10 ) ;
256- var titleCnt = lines . length ;
257- if ( titleCnt ) lines . push ( "" ) ; // add blank line after title
258- lines = lines . concat ( g . wrapString ( msg . body , g . getWidth ( ) - 10 ) , [ "" , /*LANG*/ "< Back" ] ) ;
279+ var titleLines = [ ] ;
280+ var messagesWrapped = [ ] ;
281+ for ( let i = idxSpan . start ; i < idxSpan . stop ; i ++ ) {
282+ let msgLocal = MESSAGES [ i ] ;
283+ msgLocal . new = false ;
284+
285+ if ( msgLocal . id == "music" || msgLocal . source == "maps" || msgLocal . id == "call" ) {
286+ idxSpan . stop ++
287+ if ( idxSpan . stop >= MESSAGES . length ) { break ; }
288+ continue ;
289+ }
290+
291+ var lines = [ ] ;
292+ const TITLE_STRING = msgLocal . title || msgLocal . sender || msgLocal . subject || msgLocal . src || "No Title" ;
293+ //const TITLE_STRING = "".concat(msgLocal.title, msgLocal.title&&"\n",
294+ // msgLocal.sender, msgLocal.sender&&"\n",
295+ // msgLocal.subject, msgLocal.subject&&"\n", msgLocal.src) || "No Title";
296+ lines = g . wrapString ( TITLE_STRING , g . getWidth ( ) - 10 ) ;
297+ for ( let i = 0 ; i < lines . length ; i ++ ) {
298+ titleLines . push ( i + ( messagesWrapped [ 0 ] ?messagesWrapped [ 0 ] . length :0 ) +
299+ ( messagesWrapped [ 1 ] ?messagesWrapped [ 1 ] . length :0 ) ) ;
300+ }
301+ lines = lines . concat ( g . wrapString ( msgLocal . body , g . getWidth ( ) - 10 ) ,
302+ [ "-" . repeat ( 12 ) ] ) ;
303+ messagesWrapped . push ( lines ) ;
304+ }
305+
306+ let allLines = [ ] ;
307+ for ( let i = 0 ; i < messagesWrapped . length ; i ++ ) {
308+ allLines = allLines . concat ( messagesWrapped [ i ] ) ;
309+ }
310+
311+ var initScroll = messagesWrapped [ 0 ] . length ;
312+ if ( INITIATED_FROM === "other function" ) {
313+ if ( MSG_IDX == 0 ) { initScroll = 0 ; }
314+ } else if ( INITIATED_FROM === "scrolling up" ) {
315+ titleLines = titleLines . concat ( alreadyProcessed . titleLines .
316+ map ( ( x ) => x + allLines . length ) ) ;
317+ allLines = allLines . concat ( alreadyProcessed . lines ) ;
318+ } else if ( INITIATED_FROM === "scrolling down" ) {
319+ initScroll = alreadyProcessed . lines . length - ( g . getHeight ( ) / g . getFontHeight ( ) ) ;
320+ titleLines = alreadyProcessed . titleLines . concat ( titleLines .
321+ map ( ( x ) => x + alreadyProcessed . lines . length ) ) ;
322+ allLines = alreadyProcessed . lines . concat ( allLines ) ;
323+ }
324+
325+ if ( allLines . length == 0 && alreadyProcessed . lines . length == 0 ) {
326+ cancelReloadTimeout ( ) ;
327+ returnToClockIfEmpty ( ) ;
328+ }
329+
330+ alreadyProcessed = { // Update with the newly processed messages.
331+ lines : allLines ,
332+ titleLines : titleLines ,
333+ idxSpan : {
334+ start : Math . min ( idxSpan . start ,
335+ ( alreadyProcessed . idxSpan . start === undefined ) ?
336+ MESSAGES . length : alreadyProcessed . idxSpan . start ) ,
337+ stop : Math . max ( idxSpan . stop , alreadyProcessed . idxSpan . stop || 0 ) }
338+ } ;
339+
340+ function identifyDisplayedMsg ( scrollIdx ) {
341+ let firstTitleLinePerMsg = [ titleLines [ 0 ] ] ;
342+ for ( let i = 1 ; i < titleLines . length ; i ++ ) {
343+ if ( titleLines [ i ] - titleLines [ i - 1 ] === 1 ) { continue ; }
344+ firstTitleLinePerMsg . push ( titleLines [ i ] ) ;
345+ }
346+ for ( let i = titleLines . length - 1 ; i >= 0 ; i -- ) {
347+ if ( scrollIdx >= firstTitleLinePerMsg [ i ] ) {
348+ return MESSAGES [ i + alreadyProcessed . idxSpan . start ] ;
349+ }
350+ }
351+ }
352+
353+ let prevScrollIdx ; // Used for stopping repeated triggering of next message by the scroller.
354+ let prevPrevScrollIdx ; // Used to chose how to identify displayed message when selecting with button.
355+
259356 E . showScroller ( {
357+ scroll : initScroll * g . getFontHeight ( ) ,
260358 h : g . getFontHeight ( ) , // height of each menu item in pixels
261- c : lines . length , // number of menu items
359+ c : allLines . length , // number of menu items
262360 // a function to draw a menu item
263- draw : function ( idx , r ) {
264- // FIXME: in 2v13 onwards, clearRect(r) will work fine. There's a bug in 2v12
265- g . setBgColor ( idx < titleCnt ? g . theme . bg2 : g . theme . bg ) .
266- setColor ( idx < titleCnt ? g . theme . fg2 : g . theme . fg ) .
267- clearRect ( r . x , r . y , r . x + r . w , r . y + r . h ) ;
268- g . setFont ( bodyFont ) . setFontAlign ( 0 , - 1 ) . drawString ( lines [ idx ] , r . x + r . w / 2 , r . y ) ;
269- } , select : function ( idx ) {
270- if ( idx >= lines . length - 2 )
271- showMessage ( msg . id , true ) ;
361+ draw : function ( scrollIdx , r ) {
362+ "ram" ;
363+ g . setBgColor ( titleLines . find ( e => e == scrollIdx ) !== undefined ? g . theme . bg2 : g . theme . bg ) .
364+ setColor ( titleLines . find ( e => e == scrollIdx ) !== undefined ? g . theme . fg2 : g . theme . fg ) .
365+ clearRect ( r ) ;
366+ g . setFont ( bodyFont ) . setFontAlign ( 0 , - 1 ) . drawString ( allLines [ scrollIdx ] , r . x + r . w / 2 , r . y ) ;
367+ // Load in next/previous message on demand by reinitializing showMessagesScroller while passing on the processed messages.
368+ if ( scrollIdx >= allLines . length - 1 && scrollIdx != prevScrollIdx &&
369+ alreadyProcessed . idxSpan . stop < MESSAGES . length ) {
370+ setTimeout ( ( ) => {
371+ E . showScroller ( ) ;
372+ if ( BTN_WATCH ) { clearWatch ( BTN_WATCH ) ; }
373+ showMessagesScroller ( MESSAGES [ alreadyProcessed . idxSpan . stop ] ,
374+ true , alreadyProcessed ) ;
375+ } , 40 ) ;
376+ }
377+ if ( scrollIdx == 0 && scrollIdx != prevScrollIdx && alreadyProcessed . idxSpan . start > 0 ) {
378+ setTimeout ( ( ) => {
379+ E . showScroller ( ) ;
380+ if ( BTN_WATCH ) { clearWatch ( BTN_WATCH ) ; }
381+ showMessagesScroller ( MESSAGES [ alreadyProcessed . idxSpan . start - 1 ] ,
382+ true , alreadyProcessed ) ;
383+ } , 40 ) ;
384+ }
385+ if ( prevPrevScrollIdx !== prevScrollIdx ) { prevPrevScrollIdx = prevScrollIdx ; }
386+ prevScrollIdx = scrollIdx ;
272387 } ,
273- back : ( ) => showMessage ( msg . id , true )
388+ select : function ( scrollIdx , touch ) {
389+ const MSG_SELECT = identifyDisplayedMsg ( scrollIdx ) ;
390+ if ( touch . type == 0 ) {
391+ WU . show ( ) ;
392+ if ( BTN_WATCH ) { clearWatch ( BTN_WATCH ) ; }
393+ showMessage ( MSG_SELECT . id , true ) ;
394+ }
395+ if ( touch . type == 2 ) {
396+ WU . show ( ) ;
397+ if ( BTN_WATCH ) { clearWatch ( BTN_WATCH ) ; }
398+ showMessageSettings ( MSG_SELECT ) ;
399+ }
400+ }
274401 } ) ;
402+
403+ const BTN_WATCH = setWatch ( ( ) => {
404+ Bangle . emit ( "drag" , { dy :0 } ) ; // Compatibility with `kineticscroll`, stopping the scroller so it doesn't continue scrolling when the `showMessage` screen is loaded.
405+ // Zero ms timeout as to not move on before the scroller has registered the emitted drag event.
406+ setTimeout ( ( ) => { const SCROLL_IDX_CENTER_SCREEN = prevScrollIdx > prevPrevScrollIdx ?
407+ prevScrollIdx - 5 : prevScrollIdx + 5 ; // FIXME: `±5` should depend on screen height and font height.
408+ WU . show ( ) ;
409+ showMessage ( identifyDisplayedMsg ( SCROLL_IDX_CENTER_SCREEN ) . id , true ) ;
410+ } , 0 )
411+ } , BTN , { edge :'rising' } ) ;
275412}
276413
277414function showMessageSettings ( msg ) {
@@ -283,7 +420,7 @@ function showMessageSettings(msg) {
283420 } ;
284421
285422 if ( msg . id != "music" )
286- menu [ /*LANG*/ "View Message" ] = ( ) => showMessageScroller ( msg ) ;
423+ menu [ /*LANG*/ "View Message" ] = ( ) => showMessagesScroller ( msg ) ;
287424
288425 if ( msg . reply && reply ) {
289426 menu [ /*LANG*/ "Reply" ] = ( ) => {
@@ -456,7 +593,7 @@ function showMessage(msgid, persist) {
456593 ] } ,
457594 { type :"txt" , font :bodyFont , label :body , fillx :1 , filly :1 , pad :2 , cb :( ) => {
458595 // allow tapping to show a larger version
459- showMessageScroller ( msg ) ;
596+ showMessagesScroller ( msg ) ;
460597 } } ,
461598 { type :"h" , fillx :1 , c : footer }
462599 ] } , { back :goBack } ) ;
@@ -502,7 +639,7 @@ function checkMessages(options) {
502639 // If we have a new message, show it
503640 if ( ! options . ignoreUnread && newMessages . length ) {
504641 delete newMessages [ 0 ] . show ; // stop us getting stuck here if we're called a second time
505- showMessage ( newMessages [ 0 ] . id , false ) ;
642+ showMessagesScroller ( newMessages [ 0 ] , false ) ;
506643 // buzz after showMessage, so being busy during layout doesn't affect the buzz pattern
507644 if ( global . BUZZ_ON_NEW_MESSAGE ) {
508645 // this is set if we entered the messages app by loading `messagegui.new.js`
0 commit comments