1
+ /* eslint no-console:0 */
2
+
1
3
var pkg = require ( '../package.json' ) ;
2
4
var app = require ( 'ampersand-app' ) ;
3
5
app . extend ( {
@@ -8,7 +10,9 @@ app.extend({
8
10
'App Version' : pkg . version
9
11
}
10
12
} ) ;
11
- require ( './bugsnag' ) . listen ( app ) ;
13
+
14
+ var bugsnag = require ( './bugsnag' ) ;
15
+ bugsnag . listen ( app ) ;
12
16
13
17
var _ = require ( 'lodash' ) ;
14
18
var domReady = require ( 'domready' ) ;
@@ -17,6 +21,13 @@ var getOrCreateClient = require('scout-client');
17
21
var ViewSwitcher = require ( 'ampersand-view-switcher' ) ;
18
22
var View = require ( 'ampersand-view' ) ;
19
23
var localLinks = require ( 'local-links' ) ;
24
+
25
+ var QueryOptions = require ( './models/query-options' ) ;
26
+ var Connection = require ( './models/connection' ) ;
27
+ var MongoDBInstance = require ( './models/mongodb-instance' ) ;
28
+ var Router = require ( './router' ) ;
29
+ var Statusbar = require ( './statusbar' ) ;
30
+
20
31
var debug = require ( 'debug' ) ( 'scout:app' ) ;
21
32
22
33
// Inter-process communication with main process (Electron window)
@@ -78,24 +89,59 @@ var Application = View.extend({
78
89
/**
79
90
* Enable/Disable features with one global switch
80
91
*/
81
- features : 'object'
92
+ features : 'object' ,
93
+ clientStartedAt : 'date' ,
94
+ clientStalledTimeout : 'number'
82
95
} ,
83
96
events : {
84
97
'click a' : 'onLinkClick'
85
98
} ,
86
- /**
87
- * We have what we need, we can now start our router and show the appropriate page!
88
- */
89
- _onDOMReady : function ( ) {
90
- this . el = document . querySelector ( '#application ') ;
91
- this . render ( ) ;
99
+ onClientReady : function ( ) {
100
+ debug ( 'Client ready! Took %dms to become readable' ,
101
+ new Date ( ) - this . clientStartedAt ) ;
102
+
103
+ debug ( 'clearing client stall timeout... ') ;
104
+ clearTimeout ( this . clientStalledTimeout ) ;
92
105
106
+ debug ( 'initializing singleton models... ' ) ;
107
+ this . queryOptions = new QueryOptions ( ) ;
108
+ this . volatileQueryOptions = new QueryOptions ( ) ;
109
+ this . instance = new MongoDBInstance ( ) ;
110
+
111
+ this . startRouter ( ) ;
112
+ } ,
113
+ startRouter : function ( ) {
114
+ this . router = new Router ( ) ;
115
+ debug ( 'Listening for page changes from the router...' ) ;
93
116
this . listenTo ( this . router , 'page' , this . onPageChange ) ;
94
117
118
+ debug ( 'Starting router...' ) ;
95
119
this . router . history . start ( {
96
120
pushState : false ,
97
121
root : '/'
98
122
} ) ;
123
+ app . statusbar . hide ( ) ;
124
+ } ,
125
+ onFatalError : function ( id , err ) {
126
+ console . error ( 'Fatal Error!: ' , id , err ) ;
127
+ bugsnag . notifyException ( err , 'fatal!' + id ) ;
128
+ window . alert ( 'Fatal Error: ' + id + ': ' + err . message ) ;
129
+ } ,
130
+ // ms we'll wait for a `scout-client` instance
131
+ // to become readable before giving up and showing
132
+ // a fatal error message.
133
+ CLIENT_STALLED_REDLINE : 5 * 1000 ,
134
+ startClientStalledTimer : function ( ) {
135
+ this . clientStartedAt = new Date ( ) ;
136
+
137
+ debug ( 'Starting client stalled timer to bail in %dms...' ,
138
+ this . CLIENT_STALLED_REDLINE ) ;
139
+
140
+ this . clientStalledTimeout = setTimeout ( function ( ) {
141
+ this . onFatalError ( 'client stalled' ,
142
+ new Error ( 'Error connecting to MongoDB. '
143
+ + 'Please reload the page.' ) ) ;
144
+ } . bind ( this ) , this . CLIENT_STALLED_REDLINE ) ;
99
145
} ,
100
146
/**
101
147
* When you want to go to a different page in the app or just save
@@ -117,15 +163,25 @@ var Application = View.extend({
117
163
trigger : ! options . silent
118
164
} ) ;
119
165
} ,
166
+ /**
167
+ * Called a soon as the DOM is ready so we can
168
+ * start showing status indicators as
169
+ * quickly as possible.
170
+ */
120
171
render : function ( ) {
172
+ debug ( 'Rendering app container...' ) ;
173
+
174
+ this . el = document . querySelector ( '#application' ) ;
121
175
this . renderWithTemplate ( this ) ;
122
176
this . pageSwitcher = new ViewSwitcher ( this . queryByHook ( 'layout-container' ) , {
123
177
show : function ( ) {
124
178
document . scrollTop = 0 ;
125
179
}
126
180
} ) ;
127
-
128
- this . statusbar . el = this . queryByHook ( 'statusbar' ) ;
181
+ debug ( 'rendering statusbar...' ) ;
182
+ this . statusbar = new Statusbar ( {
183
+ el : this . queryByHook ( 'statusbar' )
184
+ } ) ;
129
185
this . statusbar . render ( ) ;
130
186
} ,
131
187
onPageChange : function ( view ) {
@@ -153,16 +209,6 @@ var state = new Application({
153
209
connection_id : connection_id
154
210
} ) ;
155
211
156
- var QueryOptions = require ( './models/query-options' ) ;
157
- var Connection = require ( './models/connection' ) ;
158
- var MongoDBInstance = require ( './models/mongodb-instance' ) ;
159
- var Router = require ( './router' ) ;
160
- var Statusbar = require ( './statusbar' ) ;
161
-
162
- function start ( ) {
163
- state . router = new Router ( ) ;
164
- state . _onDOMReady ( ) ;
165
- }
166
212
// @todo (imlucas): Feature flags can be overrideen
167
213
// via `window.localStorage`.
168
214
var FEATURES = {
@@ -188,29 +234,42 @@ app.extend({
188
234
} ,
189
235
init : function ( ) {
190
236
domReady ( function ( ) {
191
- state . statusbar = new Statusbar ( ) ;
192
-
193
- if ( connection_id ) {
194
- state . connection = new Connection ( {
195
- _id : connection_id
196
- } ) ;
197
-
198
-
199
- debug ( 'looking up connection `%s`...' , connection_id ) ;
200
- state . connection . fetch ( {
201
- success : function ( ) {
202
- debug ( 'got connection `%j`...' , state . connection . serialize ( ) ) ;
203
- app . client = getOrCreateClient ( app . endpoint , state . connection . serialize ( ) ) ;
204
-
205
- state . queryOptions = new QueryOptions ( ) ;
206
- state . volatileQueryOptions = new QueryOptions ( ) ;
207
- state . instance = new MongoDBInstance ( ) ;
208
- start ( ) ;
209
- }
210
- } ) ;
211
- } else {
212
- start ( ) ;
237
+ state . render ( ) ;
238
+
239
+ if ( ! connection_id ) {
240
+ // Not serving a part of the app which uses the client,
241
+ // so we can just start everything up now.
242
+ state . start ( ) ;
243
+ return ;
213
244
}
245
+
246
+ app . statusbar . show ( 'Retrieving connection details...' ) ;
247
+
248
+ state . connection = new Connection ( {
249
+ _id : connection_id
250
+ } ) ;
251
+
252
+ debug ( 'looking up connection `%s`...' , connection_id ) ;
253
+ state . connection . fetch ( {
254
+ success : function ( ) {
255
+ app . statusbar . show ( 'Connection details loaded! Initializing client...' ) ;
256
+
257
+ var endpoint = app . endpoint ;
258
+ var connection = state . connection . serialize ( ) ;
259
+
260
+ app . client = getOrCreateClient ( endpoint , connection )
261
+ . on ( 'readable' , state . onClientReady . bind ( state ) )
262
+ . on ( 'error' , state . onFatalError . bind ( state , 'create client' ) ) ;
263
+
264
+ state . startClientStalledTimer ( ) ;
265
+ } ,
266
+ error : function ( ) {
267
+ // @todo (imlucas) `ampersand-sync-localforage` currently drops
268
+ // the real error so for now just use a generic.
269
+ state . onFatalError ( state , 'fetch connection' ,
270
+ new Error ( 'Error retrieving connection. Please reload the page.' ) ) ;
271
+ }
272
+ } ) ;
214
273
} ) ;
215
274
// set up ipc
216
275
ipc . on ( 'message' , state . onMessageReceived . bind ( this ) ) ;
0 commit comments