@@ -55,21 +55,43 @@ export default class ProfileExplorer extends React.PureComponent<Props, State> {
5555 this . init ( )
5656 }
5757
58+ private updateDebouncer : null | ReturnType < typeof setTimeout > = null
59+
5860 private readonly updateFn = ( ) => {
59- // slice to force a render; TODO we could do a comparison to avoid
60- // false re-renders if we want to get fancy
61- this . setState ( ( curState ) => {
62- const profiles = curState . watcher . profiles . slice ( )
63- if ( ! curState || ! curState . profiles || curState . profiles . length === 0 ) {
64- // sort the first time we get a list of profiles; TODO should
65- // we re-sort if the list changes? what we want to avoid is
66- // resorting simply because the selection changed
67- profiles . sort ( ( a , b ) => b . lastUsedTime - a . lastUsedTime )
68- }
69- return {
70- profiles,
71- }
72- } )
61+ if ( this . updateDebouncer ) {
62+ clearTimeout ( this . updateDebouncer )
63+ }
64+
65+ // hmm, this is imperfect... the watcher seems to give us [A],
66+ // then [A,B], then [A,B,C] in quick succession. is there any way
67+ // to know that we are done with the initial batch? for now, we do
68+ // some debouncing.
69+ this . updateDebouncer = setTimeout ( ( ) => {
70+ this . setState ( ( curState ) => {
71+ if ( JSON . stringify ( curState . watcher . profiles ) === JSON . stringify ( curState . profiles ) ) {
72+ return null
73+ }
74+
75+ const profiles = curState . watcher . profiles . slice ( )
76+
77+ let selectedProfile = curState . selectedProfile
78+ if ( ! curState || ! curState . profiles || curState . profiles . length === 0 ) {
79+ // sort the first time we get a list of profiles; TODO should
80+ // we re-sort if the list changes? what we want to avoid is
81+ // resorting simply because the selection changed
82+ profiles . sort ( ( a , b ) => b . lastUsedTime - a . lastUsedTime )
83+
84+ // also emit an initial profile selection event
85+ selectedProfile = profiles [ 0 ] . name
86+ emitSelectProfile ( selectedProfile )
87+ }
88+
89+ return {
90+ profiles,
91+ selectedProfile,
92+ }
93+ } )
94+ } , 100 )
7395 }
7496
7597 private async init ( ) {
@@ -103,6 +125,14 @@ export default class ProfileExplorer extends React.PureComponent<Props, State> {
103125 }
104126 }
105127
128+ private prettyMillis ( duration : number ) {
129+ if ( duration < 1000 ) {
130+ return "just now"
131+ } else {
132+ return prettyMillis ( duration , { compact : true } ) + " ago"
133+ }
134+ }
135+
106136 public render ( ) {
107137 if ( this . state && this . state . catastrophicError ) {
108138 return "Internal Error"
@@ -111,16 +141,16 @@ export default class ProfileExplorer extends React.PureComponent<Props, State> {
111141 } else {
112142 return (
113143 < Grid className = "codeflare--gallery-grid flex-fill sans-serif top-pad left-pad right-pad bottom-pad" hasGutter >
114- { this . state . profiles . map ( ( _ , idx ) => (
144+ { this . state . profiles . map ( ( _ ) => (
115145 < GridItem key = { _ . name } >
116146 < Tile
117147 className = "codeflare--tile"
118148 data-profile = { _ . name }
119149 title = { _ . name }
120- isSelected = { ! this . state . selectedProfile ? idx === 0 : this . state . selectedProfile === _ . name }
150+ isSelected = { this . state . selectedProfile === _ . name }
121151 onClick = { this . onSelect }
122152 >
123- { `Last used ${ prettyMillis ( Date . now ( ) - _ . lastUsedTime , { compact : true } ) } ago ` }
153+ { `Last used ${ this . prettyMillis ( Date . now ( ) - _ . lastUsedTime ) } ` }
124154 </ Tile >
125155 </ GridItem >
126156 ) ) }
0 commit comments