1+ /* eslint no-console:0 */
2+
13var pkg = require ( '../package.json' ) ;
24var app = require ( 'ampersand-app' ) ;
35app . extend ( {
@@ -8,7 +10,9 @@ app.extend({
810 'App Version' : pkg . version
911 }
1012} ) ;
11- require ( './bugsnag' ) . listen ( app ) ;
13+
14+ var bugsnag = require ( './bugsnag' ) ;
15+ bugsnag . listen ( app ) ;
1216
1317var _ = require ( 'lodash' ) ;
1418var domReady = require ( 'domready' ) ;
@@ -17,6 +21,13 @@ var getOrCreateClient = require('scout-client');
1721var ViewSwitcher = require ( 'ampersand-view-switcher' ) ;
1822var View = require ( 'ampersand-view' ) ;
1923var 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+
2031var debug = require ( 'debug' ) ( 'scout:app' ) ;
2132
2233// Inter-process communication with main process (Electron window)
@@ -78,24 +89,59 @@ var Application = View.extend({
7889 /**
7990 * Enable/Disable features with one global switch
8091 */
81- features : 'object'
92+ features : 'object' ,
93+ clientStartedAt : 'date' ,
94+ clientStalledTimeout : 'number'
8295 } ,
8396 events : {
8497 'click a' : 'onLinkClick'
8598 } ,
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 ) ;
92105
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...' ) ;
93116 this . listenTo ( this . router , 'page' , this . onPageChange ) ;
94117
118+ debug ( 'Starting router...' ) ;
95119 this . router . history . start ( {
96120 pushState : false ,
97121 root : '/'
98122 } ) ;
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 ) ;
99145 } ,
100146 /**
101147 * When you want to go to a different page in the app or just save
@@ -117,15 +163,25 @@ var Application = View.extend({
117163 trigger : ! options . silent
118164 } ) ;
119165 } ,
166+ /**
167+ * Called a soon as the DOM is ready so we can
168+ * start showing status indicators as
169+ * quickly as possible.
170+ */
120171 render : function ( ) {
172+ debug ( 'Rendering app container...' ) ;
173+
174+ this . el = document . querySelector ( '#application' ) ;
121175 this . renderWithTemplate ( this ) ;
122176 this . pageSwitcher = new ViewSwitcher ( this . queryByHook ( 'layout-container' ) , {
123177 show : function ( ) {
124178 document . scrollTop = 0 ;
125179 }
126180 } ) ;
127-
128- this . statusbar . el = this . queryByHook ( 'statusbar' ) ;
181+ debug ( 'rendering statusbar...' ) ;
182+ this . statusbar = new Statusbar ( {
183+ el : this . queryByHook ( 'statusbar' )
184+ } ) ;
129185 this . statusbar . render ( ) ;
130186 } ,
131187 onPageChange : function ( view ) {
@@ -153,16 +209,6 @@ var state = new Application({
153209 connection_id : connection_id
154210} ) ;
155211
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- }
166212// @todo (imlucas): Feature flags can be overrideen
167213// via `window.localStorage`.
168214var FEATURES = {
@@ -188,29 +234,42 @@ app.extend({
188234 } ,
189235 init : function ( ) {
190236 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 ;
213244 }
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+ } ) ;
214273 } ) ;
215274 // set up ipc
216275 ipc . on ( 'message' , state . onMessageReceived . bind ( this ) ) ;
0 commit comments