@@ -45,9 +45,11 @@ if (Graphics.prototype.setFontIntl) {
4545 fontVLarge = noScale ?"Intl" :"Intl:3" ;
4646}
4747
48- var active ; // active screen (undefined/"list"/"music"/"map"/"message "/"scroller"/"settings")
48+ var active ; // active screen (undefined/"list"/"music"/"map"/"overview "/"scroller"/"settings")
4949var openMusic = false ; // go back to music screen after we handle something else?
5050var replying = false ; // If we're replying to a message, don't interrupt
51+ var persist = "messagegui.new.js" !== globalThis . __FILE__ ;
52+
5153// hack for 2v10 firmware's lack of ':size' font handling
5254try {
5355 g . setFont ( "6x8:2" ) ;
@@ -90,7 +92,7 @@ var onMessagesModified = function(type,msg) {
9092 }
9193 if ( msg && msg . id == "nav" && msg . t == "modify" && active != "map" )
9294 return ; // don't show an updated nav message if we're just in the menu
93- showMessage ( msg && msg . id , false ) ;
95+ showMessageRouter ( msg , persist , "dependsOnActive" ) ;
9496} ;
9597Bangle . on ( "message" , onMessagesModified ) ;
9698
@@ -99,6 +101,39 @@ function saveMessages() {
99101}
100102E . on ( "kill" , saveMessages ) ;
101103
104+ function showMessageRouter ( msg , persist , explicitDestnation ) {
105+ //explicitDestnation (undefined/"scroller"/"overview"/"dependsOnActive")
106+
107+ ////var active; // active screen (undefined/"list"/"music"/"map"/"overview"/"scroller"/"settings")
108+ //if (active==undefined) { } else if (active=="list") ... //and so on.
109+
110+ if ( persist ) { cancelReloadTimeout ( ) ; } else if ( Bangle . isLocked ( ) ) { resetReloadTimeout ( ) ; }
111+
112+ if ( msg . id == "music" ) {
113+ cancelReloadTimeout ( ) ; // don't auto-reload to clock now
114+ return showMusicMessage ( msg ) ;
115+ }
116+ if ( msg . id == "nav" ) {
117+ cancelReloadTimeout ( ) ; // don't auto-reload to clock now
118+ return showMapMessage ( msg ) ;
119+ }
120+ if ( msg . id == "call" ) {
121+ return showMessageOverview ( msg . id ) ;
122+ }
123+ if ( "scroller" === explicitDestnation ) {
124+ return showMessagesScroller ( msg ) ;
125+ }
126+ if ( "overview" === explicitDestnation ) {
127+ return showMessageOverview ( msg . id ) ;
128+ }
129+ if ( "dependsOnActive" === explicitDestnation ) {
130+ if ( "scroller" === active ) { return showMessagesScroller ( msg ) ; } // reinit scroller with updated messages list.
131+ if ( "list" === active ) { return returnToMain ( ) ; }
132+ if ( "settings" === active || "overview" === active ) { return ; }
133+ }
134+ //if (false) {showMessageSettings(msg);}
135+ }
136+
102137function showMapMessage ( msg ) {
103138 active = "map" ;
104139 require ( "messages" ) . stopBuzz ( ) ; // stop repeated buzzing while the map is showing
@@ -246,44 +281,137 @@ function showMusicMessage(msg) {
246281 } , 400 ) ;
247282}
248283
249- function showMessageScroller ( msg ) {
250- cancelReloadTimeout ( ) ;
284+ function showMessagesScroller ( msg ) {
285+ const MSG_IDX = msg ? MESSAGES . findIndex ( ( m ) => m . id == msg . id ) : undefined ;
286+
287+ if ( replying ) { return ; }
251288 active = "scroller" ;
289+
290+ const WU = require ( "widget_utils" ) ;
291+ WU . hide ( ) ;
292+ const APP_RECT = Bangle . appRect ;
293+
252294 var bodyFont = fontBig ;
253295 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" ] ) ;
296+ const FONT_HEIGHT = g . getFontHeight ( ) ;
297+ let initScroll ;
298+ var titleLines = [ ] ;
299+ let allLines = [ ] ;
300+ let firstTitleLinePerMsg = [ ] ;
301+ for ( let i = 0 ; i < MESSAGES . length ; i ++ ) {
302+ if ( MSG_IDX === i ) { initScroll = allLines . length * FONT_HEIGHT ; }
303+ let msgIter = MESSAGES [ i ] ;
304+
305+ var lines = [ ] ;
306+ const TITLE_STRING = msgIter . title || msgIter . sender || msgIter . subject || msgIter . src || /*LANG*/ "No Title" ;
307+ lines = g . wrapString ( TITLE_STRING , APP_RECT . w - 10 ) ;
308+ firstTitleLinePerMsg . push ( allLines . length ) ;
309+ for ( let i = 0 ; i < lines . length ; i ++ ) {
310+ titleLines . push ( i + allLines . length ) ;
311+ }
312+ lines = lines . concat ( g . wrapString ( msgIter . body , APP_RECT . w - 10 ) ,
313+ [ "-" . repeat ( 12 ) ] ) ;
314+ allLines = allLines . concat ( lines ) ;
315+ }
316+
317+ if ( allLines . length == 0 ) {
318+ cancelReloadTimeout ( ) ;
319+ returnToClockIfEmpty ( ) ;
320+ }
321+
322+ let shownScrollIdxFirst = allLines . length ;
323+ let shownScrollIdxLast = 0 ;
324+
259325 E . showScroller ( {
260- h : g . getFontHeight ( ) , // height of each menu item in pixels
261- c : lines . length , // number of menu items
326+ scroll : initScroll ,
327+ h : FONT_HEIGHT , // height of each menu item in pixels
328+ c : allLines . length , // number of menu items
262329 // 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 ) ;
330+ draw : function ( scrollIdx , r ) { "ram" ;
331+ //print(scrollIdx)
332+ g . setBgColor ( titleLines . find ( e => e == scrollIdx ) !== undefined ? g . theme . bg2 : g . theme . bg ) .
333+ setColor ( titleLines . find ( e => e == scrollIdx ) !== undefined ? g . theme . fg2 : g . theme . fg ) .
334+ clearRect ( r ) ;
335+ g . setFont ( bodyFont ) . setFontAlign ( 0 , - 1 ) . drawString ( allLines [ scrollIdx ] , r . x + r . w / 2 , r . y ) ;
336+ if ( scrollIdx < shownScrollIdxFirst ) { shownScrollIdxFirst = scrollIdx ; }
337+ if ( scrollIdx > shownScrollIdxLast ) { shownScrollIdxLast = scrollIdx ; }
272338 } ,
273- back : ( ) => showMessage ( msg . id , true )
339+ select : function ( scrollIdx , touch ) {
340+ for ( let i = firstTitleLinePerMsg . length - 1 ; i >= 0 ; i -- ) {
341+ if ( scrollIdx >= firstTitleLinePerMsg [ i ] ) {
342+ if ( ! touch || touch . type === 0 ) {
343+ WU . show ( ) ;
344+ showMessageRouter ( MESSAGES [ i ] , true , "overview" ) ;
345+ updateReadMessages ( ) ;
346+ }
347+ break ;
348+ }
349+ }
350+ }
274351 } ) ;
352+
353+ // If Bangle.js 2 add an external select hw button handler.
354+ if ( 2 === process . env . HWVERSION ) {
355+ setWatch ( ( ) => {
356+ if ( "scroller" !== active ) { return ; }
357+ Bangle . emit ( "drag" , { dy :0 } ) ; // Compatibility with `kineticscroll`, stopping the scroller so it doesn't continue scrolling when the `showMessageOverview` screen is loaded.
358+ // Zero ms timeout as to not move on before the scroller has registered the emitted drag event.
359+ setTimeout ( ( ) => {
360+ if ( ! persist ) { return load ( ) ; }
361+ Bangle . emit ( "touch" , 1 , { x :APP_RECT . x2 / 2 , y :APP_RECT . y2 / 2 , type :0 } ) ;
362+ } , 0 ) ;
363+ } , BTN ) ;
364+ }
365+
366+ function updateReadMessages ( ) {
367+ let shownMsgIdxFirst , shownMsgIdxLast ;
368+ const LINES_PER_SCREEN = APP_RECT . h / FONT_HEIGHT ;
369+ //print(firstTitleLinePerMsg)
370+ //print(shownIdxFirst, shownIdxLast)
371+
372+ for ( let i = 0 ; i < firstTitleLinePerMsg . length - 1 ; i ++ ) {
373+ const FIRST_LINE_OF_MSG = firstTitleLinePerMsg [ i ] ;
374+ const LAST_LINE_OF_MSG = firstTitleLinePerMsg [ i + 1 ] - 1 ;
375+
376+ if (
377+ shownScrollIdxFirst
378+ <= FIRST_LINE_OF_MSG && FIRST_LINE_OF_MSG
379+ < shownScrollIdxFirst + LINES_PER_SCREEN
380+ ) {
381+ shownMsgIdxFirst = i ;
382+ }
383+
384+ if (
385+ shownScrollIdxLast - LINES_PER_SCREEN
386+ < LAST_LINE_OF_MSG && LAST_LINE_OF_MSG
387+ <= shownScrollIdxLast
388+ ) {
389+ shownMsgIdxLast = i ;
390+ //print(i)
391+ }
392+ }
393+ if ( shownScrollIdxLast === allLines . length - 1 ) { shownMsgIdxLast = MESSAGES . length - 1 ; }
394+
395+ //print(shownIdxFirst, shownIdxLast)
396+ //print(shownMsgIdxFirst, shownMsgIdxLast)
397+ //print(MESSAGES)
398+ for ( let i = shownMsgIdxFirst ; i < shownMsgIdxLast + 1 ; i ++ ) {
399+ MESSAGES [ i ] . new = false ;
400+ }
401+ //print(MESSAGES)
402+ }
275403}
276404
277405function showMessageSettings ( msg ) {
278406 active = "settings" ;
279407 var menu = { "" :{
280408 "title" :/*LANG*/ "Message" ,
281- back :( ) => showMessage ( msg . id , true )
409+ back :( ) => showMessageOverview ( msg . id )
282410 } ,
283411 } ;
284412
285413 if ( msg . id != "music" )
286- menu [ /*LANG*/ "View Message" ] = ( ) => showMessageScroller ( msg ) ;
414+ menu [ /*LANG*/ "View Message" ] = ( ) => showMessagesScroller ( msg ) ;
287415
288416 if ( msg . reply && reply ) {
289417 menu [ /*LANG*/ "Reply" ] = ( ) => {
@@ -292,11 +420,11 @@ function showMessageSettings(msg) {
292420 . then ( result => {
293421 Bluetooth . println ( JSON . stringify ( result ) ) ;
294422 replying = false ;
295- showMessage ( msg . id ) ;
423+ showMessageOverview ( msg . id ) ;
296424 } )
297425 . catch ( ( ) => {
298426 replying = false ;
299- showMessage ( msg . id ) ;
427+ showMessageOverview ( msg . id ) ;
300428 } ) ;
301429 } ;
302430 }
@@ -340,25 +468,16 @@ function showMessageSettings(msg) {
340468 E . showMenu ( menu ) ;
341469}
342470
343- function showMessage ( msgid , persist ) {
471+ function showMessageOverview ( msgid ) {
344472 if ( replying ) { return ; }
345- if ( ! persist ) resetReloadTimeout ( ) ;
346473 let idx = MESSAGES . findIndex ( m => m . id == msgid ) ;
347474 var msg = MESSAGES [ idx ] ;
348475 if ( updateLabelsInterval ) {
349476 clearInterval ( updateLabelsInterval ) ;
350477 updateLabelsInterval = undefined ;
351478 }
352479 if ( ! msg ) return returnToClockIfEmpty ( ) ; // go home if no message found
353- if ( msg . id == "music" ) {
354- cancelReloadTimeout ( ) ; // don't auto-reload to clock now
355- return showMusicMessage ( msg ) ;
356- }
357- if ( msg . id == "nav" ) {
358- cancelReloadTimeout ( ) ; // don't auto-reload to clock now
359- return showMapMessage ( msg ) ;
360- }
361- active = "message" ;
480+ active = "overview" ;
362481 // Normal text message display
363482 var title = msg . title , titleFont = fontLarge , lines ;
364483 var body = msg . body , bodyFont = fontLarge ;
@@ -426,7 +545,7 @@ function showMessage(msgid, persist) {
426545 . catch ( ( ) => {
427546 replying = false ;
428547 layout . render ( ) ;
429- showMessage ( msg . id ) ;
548+ showMessageOverview ( msg . id ) ;
430549 } ) ;
431550 } ; footer . push ( { type :"img" , src :atob ( "QRABAAAAAAAH//+AAAAABgP//8AAAAADgf//4AAAAAHg4ABwAAAAAPh8APgAAAAAfj+B////////geHv///////hf+f///////GPw///////8cGBwAAAAAPx/gDgAAAAAfD/gHAAAAAA8DngOAAAAABwDHP8AAAAADACGf4AAAAAAAAM/w==" ) , col :"#0f0" , cb :posHandler } ) ;
432551 }
@@ -456,16 +575,16 @@ function showMessage(msgid, persist) {
456575 ] } ,
457576 { type :"txt" , font :bodyFont , label :body , fillx :1 , filly :1 , pad :2 , cb :( ) => {
458577 // allow tapping to show a larger version
459- showMessageScroller ( msg ) ;
578+ showMessagesScroller ( msg ) ;
460579 } } ,
461580 { type :"h" , fillx :1 , c : footer }
462581 ] } , { back :goBack } ) ;
463582
464583 Bangle . swipeHandler = ( lr , ud ) => {
465584 if ( lr > 0 && posHandler ) posHandler ( ) ;
466585 if ( lr < 0 && negHandler ) negHandler ( ) ;
467- if ( ud > 0 && idx < MESSAGES . length - 1 ) showMessage ( MESSAGES [ idx + 1 ] . id , true ) ;
468- if ( ud < 0 && idx > 0 ) showMessage ( MESSAGES [ idx - 1 ] . id , true ) ;
586+ if ( ud > 0 && idx < MESSAGES . length - 1 ) showMessageOverview ( MESSAGES [ idx + 1 ] . id ) ;
587+ if ( ud < 0 && idx > 0 ) showMessageOverview ( MESSAGES [ idx - 1 ] . id ) ;
469588 } ;
470589 Bangle . on ( "swipe" , Bangle . swipeHandler ) ;
471590 g . reset ( ) . clearRect ( Bangle . appRect ) ;
@@ -502,20 +621,20 @@ function checkMessages(options) {
502621 // If we have a new message, show it
503622 if ( ! options . ignoreUnread && newMessages . length ) {
504623 delete newMessages [ 0 ] . show ; // stop us getting stuck here if we're called a second time
505- showMessage ( newMessages [ 0 ] . id , false ) ;
506- // buzz after showMessage , so being busy during layout doesn't affect the buzz pattern
507- if ( global . BUZZ_ON_NEW_MESSAGE ) {
624+ showMessagesScroller ( newMessages [ 0 ] ) ;
625+ // buzz after showMessagesScroller , so being busy during scroller setup doesn't affect the buzz pattern
626+ if ( globalThis . BUZZ_ON_NEW_MESSAGE ) {
508627 // this is set if we entered the messages app by loading `messagegui.new.js`
509628 // ... but only buzz the first time we view a new message
510- global . BUZZ_ON_NEW_MESSAGE = false ;
629+ globalThis . BUZZ_ON_NEW_MESSAGE = false ;
511630 // messages.buzz respects quiet mode - no need to check here
512631 require ( "messages" ) . buzz ( newMessages [ 0 ] . src ) ;
513632 }
514633 return ;
515634 }
516635 // no new messages: show playing music? Only if we have playing music, or state=="show" (set by messagesmusic)
517636 if ( options . openMusic && MESSAGES . some ( m => m . id == "music" && ( ( m . track && m . state == "play" ) || m . state == "show" ) ) )
518- return showMessage ( 'music' , true ) ;
637+ return showMessageOverview ( 'music' ) ;
519638 // no new messages - go to clock?
520639 if ( options . clockIfAllRead && newMessages . length == 0 )
521640 return load ( ) ;
@@ -524,7 +643,7 @@ function checkMessages(options) {
524643 E . showScroller ( {
525644 h : 48 ,
526645 c : Math . max ( MESSAGES . length , 3 ) , // workaround for 2v10.219 firmware (min 3 not needed for 2v11)
527- draw : function ( idx , r ) { "ram"
646+ draw : function ( idx , r ) { "ram" ;
528647 var msg = MESSAGES [ idx ] ;
529648 if ( msg && msg . new ) g . setBgColor ( g . theme . bgH ) . setColor ( g . theme . fgH ) ;
530649 else g . setBgColor ( g . theme . bg ) . setColor ( g . theme . fg ) ;
@@ -564,13 +683,13 @@ function checkMessages(options) {
564683 } ,
565684 select : idx => {
566685 if ( idx < MESSAGES . length )
567- showMessage ( MESSAGES [ idx ] . id , true ) ;
686+ showMessageRouter ( MESSAGES [ idx ] , true , "overview" ) ;
568687 } ,
569688 back : ( ) => load ( )
570689 } ) ;
571690}
572691
573- function returnToCheckMessages ( clock ) {
692+ function returnToCheckMessages ( ) {
574693 checkMessages ( { clockIfNoMsg :1 , clockIfAllRead :1 , ignoreUnread :settings . ignoreUnread , openMusic} ) ;
575694}
576695
@@ -611,8 +730,14 @@ setTimeout(() => {
611730} , 10 ) ; // if checkMessages wants to 'load', do that
612731
613732/* If the Bangle is unlocked by the user, treat that
614- as a queue to stop repeated buzzing */
733+ as a queue to stop repeated buzzing.
734+ Also suspend the reload timeout while the watch is unlocked. */
615735Bangle . on ( 'lock' , locked => {
616- if ( ! locked )
736+ if ( ! locked ) {
617737 require ( "messages" ) . stopBuzz ( ) ;
738+ cancelReloadTimeout ( ) ;
739+ }
740+ if ( locked ) {
741+ if ( ! persist ) { resetReloadTimeout ( ) ; }
742+ }
618743} ) ;
0 commit comments