@@ -18,10 +18,11 @@ import React from "react"
18
18
import prettyMillis from "pretty-ms"
19
19
import { Box , Spacer , Text } from "ink"
20
20
21
- import type { GridSpec , UpdatePayload , Worker } from "./types.js"
21
+ import type { GridSpec , UpdatePayload , LogLineUpdate , WorkersUpdate , Worker } from "./types.js"
22
22
23
23
import Grid from "./Grid.js"
24
24
import Timeline from "./Timeline.js"
25
+ import { isWorkersUpdate } from "./types.js"
25
26
26
27
export type Props = {
27
28
/** CodeFlare Profile for this dashboard */
@@ -33,42 +34,39 @@ export type Props = {
33
34
/** Scale up the grid? [default: 1] */
34
35
scale ?: number
35
36
37
+ /** Grid models, where null means to insert a line break */
36
38
grids : ( null | GridSpec ) [ ]
37
39
}
38
40
39
- export type State = {
40
- /** millis since epoch of the first update */
41
- firstUpdate : number
41
+ export type State = Pick < WorkersUpdate , "events" > &
42
+ LogLineUpdate & {
43
+ /** millis since epoch of the first update */
44
+ firstUpdate : number
42
45
43
- /** millis since epoch of the last update */
44
- lastUpdate : number
46
+ /** millis since epoch of the last update */
47
+ lastUpdate : number
45
48
46
- /** iteration count to help us keep "last updated ago" UI fresh */
47
- iter : number
49
+ /** iteration count to help us keep "last updated ago" UI fresh */
50
+ iter : number
48
51
49
- /** interval to keep "last updated ago" UI fresh */
50
- agoInterval : ReturnType < typeof setInterval >
52
+ /** interval to keep "last updated ago" UI fresh */
53
+ agoInterval : ReturnType < typeof setInterval >
51
54
52
- /** Lines of raw output to be displayed */
53
- events : UpdatePayload [ "events" ]
55
+ /** Controller that allows us to shut down gracefully */
56
+ watchers : { quit : ( ) => void } [ ]
54
57
55
- /** Controller that allows us to shut down gracefully */
56
- watchers : { quit : ( ) => void } [ ]
57
-
58
- /**
59
- * Model of current workers; outer idx is grid index; inner idx is
60
- * worker idx, i.e. for each grid, we have an array of Workers.
61
- */
62
- workers : Worker [ ] [ ]
63
- }
58
+ /**
59
+ * Model of current workers; outer idx is grid index; inner idx is
60
+ * worker idx, i.e. for each grid, we have an array of Workers.
61
+ */
62
+ workers : Worker [ ] [ ]
63
+ }
64
64
65
65
export default class Dashboard extends React . PureComponent < Props , State > {
66
66
public componentDidMount ( ) {
67
67
this . setState ( {
68
68
workers : [ ] ,
69
- watchers : this . gridModels . map ( ( props , gridIdx ) =>
70
- props . initWatcher ( ( model : UpdatePayload ) => this . onUpdate ( gridIdx , model ) )
71
- ) ,
69
+ watchers : this . gridModels . map ( ( props , gridIdx ) => props . initWatcher ( ( model ) => this . onUpdate ( gridIdx , model ) ) ) ,
72
70
agoInterval : setInterval ( ( ) => this . setState ( ( curState ) => ( { iter : ( curState ?. iter || 0 ) + 1 } ) ) , 5 * 1000 ) ,
73
71
} )
74
72
}
@@ -87,8 +85,15 @@ export default class Dashboard extends React.PureComponent<Props, State> {
87
85
this . setState ( ( curState ) => ( {
88
86
firstUpdate : ( curState && curState . firstUpdate ) || Date . now ( ) , // TODO pull from the events
89
87
lastUpdate : Date . now ( ) , // TODO pull from the events
90
- events : ! model . events || model . events . length === 0 ? curState ?. events : model . events ,
91
- workers : ! curState ?. workers
88
+ events : ! isWorkersUpdate ( model )
89
+ ? curState ?. events
90
+ : ! model . events || model . events . length === 0
91
+ ? curState ?. events
92
+ : model . events ,
93
+ logLine : ! isWorkersUpdate ( model ) ? model . logLine : curState ?. logLine ,
94
+ workers : ! isWorkersUpdate ( model )
95
+ ? curState ?. workers
96
+ : ! curState ?. workers
92
97
? [ model . workers ]
93
98
: [ ...curState . workers . slice ( 0 , gridIdx ) , model . workers , ...curState . workers . slice ( gridIdx + 1 ) ] ,
94
99
} ) )
@@ -102,10 +107,15 @@ export default class Dashboard extends React.PureComponent<Props, State> {
102
107
}
103
108
104
109
/** @return current `events` model */
105
- private get events ( ) : UpdatePayload [ "events" ] {
110
+ private get events ( ) : State [ "events" ] {
106
111
return this . state ?. events
107
112
}
108
113
114
+ /** @return current `logLine` model */
115
+ private get logLine ( ) : State [ "logLine" ] {
116
+ return this . state ?. logLine
117
+ }
118
+
109
119
/** @return first update time */
110
120
private get firstUpdate ( ) {
111
121
return this . state ?. firstUpdate || Date . now ( )
@@ -184,17 +194,20 @@ export default class Dashboard extends React.PureComponent<Props, State> {
184
194
185
195
/** Render log lines and events */
186
196
private footer ( ) {
187
- if ( ! this . events ) {
197
+ if ( ! this . events && ! this . logLine ) {
188
198
return < React . Fragment />
189
199
} else {
190
- const rows = this . events . map ( ( { line, timestamp } ) => {
191
- // the controller (controller/dashboard/utilization/Live)
192
- // leaves a {timestamp} breadcrumb in the raw line text, so
193
- // that we,as the view, can inject a "5m ago" text, while
194
- // preserving the ansi formatting that surrounds the timestamp
195
- const txt = line . replace ( "{timestamp}" , ( ) => this . agos ( timestamp ) )
196
- return < Text key = { txt } > { txt } </ Text >
197
- } )
200
+ const rows = ( this . events || [ ] )
201
+ . map ( ( { line, timestamp } ) => {
202
+ // the controller (controller/dashboard/utilization/Live)
203
+ // leaves a {timestamp} breadcrumb in the raw line text, so
204
+ // that we,as the view, can inject a "5m ago" text, while
205
+ // preserving the ansi formatting that surrounds the timestamp
206
+ const txt = line . replace ( "{timestamp}" , ( ) => this . agos ( timestamp ) )
207
+ return < Text key = { txt } > { txt } </ Text >
208
+ } )
209
+ . concat ( ( this . logLine ? [ this . logLine ] : [ ] ) . map ( ( line ) => < Text key = { line } > { line } </ Text > ) )
210
+
198
211
return (
199
212
< Box marginTop = { 1 } flexDirection = "column" >
200
213
{ rows }
0 commit comments