Skip to content
This repository was archived by the owner on May 16, 2019. It is now read-only.

Commit d053bad

Browse files
committed
Merge pull request #1629 from rmisio/cache-dom-views
Cache dom views
2 parents 0f0e03d + 3d16eb4 commit d053bad

File tree

9 files changed

+341
-151
lines changed

9 files changed

+341
-151
lines changed

js/router.js

Lines changed: 180 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,20 @@ var ipcRenderer = require('ipc-renderer'),
1414

1515
module.exports = Backbone.Router.extend({
1616
initialize: function(options){
17-
var self = this;
18-
19-
var routes;
17+
var self = this,
18+
routes,
19+
originalHistoryBack,
20+
originalHistoryForward;
2021

2122
this.options = options || {};
2223

2324
routes = [
2425
["", "index"],
2526
["home", "home"],
2627
["home/:state(/:searchText)", "home"],
27-
["myPage", "userPage"],
2828
["userPage", "userPage"],
2929
["userPage/:userID(/:state)(/:itemHash)(/:skipNSFWmodal)", "userPage"],
3030
[/^@([^\/]+)(.*)$/, "userPageViaHandle"],
31-
["userPageViaHandle", "userPageViaHandle"],
3231
["transactions", "transactions"],
3332
["transactions/:state(/:orderID)(/:tabState)", "transactions"],
3433
["settings", "settings"],
@@ -52,21 +51,50 @@ module.exports = Backbone.Router.extend({
5251
});
5352
});
5453

55-
var originalHistoryBack = history.back;
54+
originalHistoryBack = history.back;
5655
history.back = function() {
5756
self.historyAction = 'back';
58-
return originalHistoryBack(arguments);
57+
return originalHistoryBack.apply(this, arguments);
5958
};
6059

61-
var originalHistoryForward = history.forward;
60+
originalHistoryForward = history.forward;
6261
history.forward = function() {
6362
self.historyAction = 'forward';
64-
return originalHistoryForward(arguments);
63+
return originalHistoryForward.apply(this, arguments);
6564
};
6665

6766
this.historySize = -1;
6867
this.historyPosition = -1;
6968
this.historyAction = 'default';
69+
70+
this.$obContainer = $('#obContainer');
71+
this.viewCache = {};
72+
73+
window.setInterval(() => {
74+
var cached;
75+
76+
for (var key in this.viewCache) {
77+
if (this.viewCache.hasOwnProperty(key)) {
78+
cached = this.viewCache[key];
79+
80+
if (Date.now() - cached.cachedAt >= cached.view.cacheExpires) {
81+
delete this.viewCache[key];
82+
}
83+
}
84+
}
85+
}, this.cleanCacheInterval);
86+
},
87+
88+
// how often to clean out expired cached views
89+
cleanCacheInterval: 1 * 60 * 1000,
90+
91+
refresh: function() {
92+
if (this.view) {
93+
// clear any cache for the view, so a fresh view is created
94+
delete this.viewCache[this.view.constructor.getCacheIndex(Backbone.history.getFragment())];
95+
}
96+
97+
Backbone.history.loadUrl();
7098
},
7199

72100
translateRoute: function(route) {
@@ -159,43 +187,108 @@ module.exports = Backbone.Router.extend({
159187
window.obEventBus.trigger('cleanNav');
160188
},
161189

162-
newView: function(view, bodyID, addressBarText, bodyClass){
163-
var loadingConfig;
190+
cacheView: function(view, fragment) {
191+
var index;
192+
193+
fragment = fragment || Backbone.history.getFragment();
194+
index = view.constructor.getCacheIndex(fragment);
195+
196+
this.viewCache[index] = {
197+
cachedAt: Date.now(),
198+
view: this.view
199+
}
200+
},
201+
202+
newView: function(View, options) {
203+
var now = Date.now(),
204+
cached = this.viewCache[View.getCacheIndex(Backbone.history.getFragment())],
205+
requestedRoute = Backbone.history.getFragment(),
206+
loadingConfig;
207+
208+
options = __.extend({
209+
// viewArgs can be an array of args to pass into the view or a single
210+
// arg (most likely an options object)
211+
viewArgs: [{}],
212+
addressBarText: '',
213+
bodyID: '',
214+
bodyClass: ''
215+
}, options || {});
216+
217+
options.viewArgs = options.viewArgs.length ? options.viewArgs : [options.viewArgs];
164218

165219
this.cleanup();
166-
//clear address bar. This will be replaced on the user page
167-
addressBarText = addressBarText || "";
168-
window.obEventBus.trigger("setAddressBar", addressBarText);
220+
window.obEventBus.trigger('setAddressBar', options.addressBarText);
169221

170-
if ($('body').attr('id') != bodyID){
171-
$('body').attr("id", bodyID || "");
172-
}
173-
if (bodyClass){
174-
$('body').attr('class', bodyClass);
175-
}
176-
$('#obContainer').removeClass("customizeUserPage"); //remove customization styling if present
222+
$('body').attr('id', options.bodyID);
223+
$('body').attr('class', options.bodyClass);
224+
$('#obContainer').removeClass('customizeUserPage'); //remove customization styling if present
177225

178226
this.pageConnectModal && this.pageConnectModal.remove();
179227
this.pageConnectModal = null;
180228

181-
if (
182-
(loadingConfig = __.result(view, 'loadingConfig')) &&
183-
loadingConfig.promise &&
184-
typeof loadingConfig.promise.then === 'function') {
185-
this.launchPageConnectModal(loadingConfig);
229+
// let's update the cache of our existing view (if it's cached && not expired) so
230+
// cachedAt is updated and the user has up until the view's 'cacheExpires' amount of
231+
// time to come back to it in it's current state.
232+
for (var key in this.viewCache) {
233+
if (this.viewCache.hasOwnProperty(key)) {
234+
let cached = this.viewCache[key];
235+
236+
if (
237+
cached.view === this.view &&
238+
(Date.now() - cached.cachedAt < cached.view.cacheExpires)
239+
) {
240+
cached.cachedAt = Date.now();
241+
}
242+
}
243+
}
244+
245+
// remove / detach any existing view
246+
if (this.view) {
247+
if (this.view.cacheExpires) {
248+
this.trigger('cache-will-detach', { view: this.view });
249+
this.view.$el.detach();
250+
this.trigger('cache-detached', { view: this.view });
251+
} else {
252+
this.view.close ? this.view.close() : this.view.remove()
253+
}
186254
}
187-
188-
this.view && (this.view.close ? this.view.close() : this.view.remove());
189-
this.view = view;
190255

191-
$('#obContainer')[0].scrollTop = 0;
256+
if (cached && (now - cached.cachedAt < cached.view.cacheExpires)) {
257+
// we have an un-expired cached view, let's reattach it
258+
this.view = cached.view;
259+
260+
$('#content').html(this.view.$el);
261+
this.view.delegateEvents();
262+
this.$obContainer[0].scrollTop = 0;
263+
264+
this.trigger('cache-reattached', {
265+
view: this.view,
266+
route: requestedRoute
267+
});
268+
} else {
269+
this.view = new (Function.prototype.bind.apply(View, [null].concat(options.viewArgs)));
270+
$('#content').html(this.view.$el);
271+
this.$obContainer[0].scrollTop = 0;
272+
273+
if (
274+
(loadingConfig = __.result(this.view, 'loadingConfig')) &&
275+
loadingConfig.promise &&
276+
typeof loadingConfig.promise.then === 'function') {
277+
this.launchPageConnectModal(loadingConfig).done(() => {
278+
this.view.cacheExpires && this.cacheView(this.view);
279+
});
280+
} else {
281+
this.view.cacheExpires && this.cacheView(this.view);
282+
}
283+
}
192284
},
193285

194286
launchPageConnectModal: function(config) {
195287
var defaults = {
196-
connectText: 'Connecting...',
197-
failedText: 'Unable to Connect.'
198-
};
288+
connectText: 'Connecting...',
289+
failedText: 'Unable to Connect.'
290+
},
291+
deferred = $.Deferred();
199292

200293
if (!(
201294
config &&
@@ -218,6 +311,7 @@ module.exports = Backbone.Router.extend({
218311

219312
this.pageConnectModal.on('back', () => {
220313
history.back();
314+
deferred.reject();
221315
});
222316

223317
this.pageConnectModal.on('retry', () => {
@@ -230,42 +324,49 @@ module.exports = Backbone.Router.extend({
230324

231325
this.pageConnectModal.on('cancel', () => {
232326
typeof config.promise.cancel === 'function' && config.promise.cancel();
327+
deferred.reject();
233328
history.back();
234329
});
235330

236331
config.promise.done(() => {
237332
this.pageConnectModal && this.pageConnectModal.remove();
238333
this.pageConnectModal = null;
334+
deferred.resolve();
239335
}).fail(() => {
240-
this.pageConnectModal.setState({
336+
this.pageConnectModal && this.pageConnectModal.setState({
241337
statusText: config.failedText,
242338
mode: 'failed-connect',
243339
tooltip: config.failedTooltip
244340
});
341+
deferred.reject();
245342
});
343+
344+
return deferred.promise();
246345
},
247346

248347
index: function(){
249-
if (localStorage.getItem("route")){
348+
if(localStorage.getItem("route")){
250349
this.navigate('#' + localStorage.getItem("route"), {trigger: true});
251-
} else if (this.userProfile.get('profile').beenSet == true){
252-
this.home();
253350
} else {
254-
this.userPage();
351+
this.navigate('#home', {trigger: true});
255352
}
256353
},
257354

258355
home: function(state, searchText){
259-
this.newView(new homeView({
260-
userModel: this.userModel,
261-
userProfile: this.userProfile,
262-
socketView: this.socketView,
263-
state: state,
264-
searchItemsText: searchText
265-
}), '', {'addressText': searchText ? "#" + searchText : ""});
356+
this.newView(homeView, {
357+
viewArgs: {
358+
userModel: this.userModel,
359+
userProfile: this.userProfile,
360+
socketView: this.socketView,
361+
state: state,
362+
searchItemsText: searchText
363+
}
364+
});
266365

267366
// hide the discover onboarding callout
268-
$('.js-OnboardingIntroDiscoverHolder').addClass('hide');
367+
this.$discoverHolder = this.$discoverHolder || $('.js-OnboardingIntroDiscoverHolder');
368+
this.$discoverHolder.addClass('hide');
369+
269370
app.appBar.setTitle(window.polyglot.t('Discover'));
270371
},
271372

@@ -282,7 +383,16 @@ module.exports = Backbone.Router.extend({
282383

283384
if (handle) options.handle = handle;
284385

285-
this.newView(new userPageView(options), "userPage", '', 'onPage');
386+
if (!userID) {
387+
this.navigate(`userPage/${this.userModel.get('guid')}`, { replace: true });
388+
}
389+
390+
this.newView(userPageView, {
391+
viewArgs: options,
392+
bodyID: 'userPage',
393+
bodyClass: 'onPage'
394+
});
395+
286396
app.appBar.setTitle(handle ? handle : options.userId || this.userModel.get('guid'));
287397
},
288398

@@ -302,6 +412,7 @@ module.exports = Backbone.Router.extend({
302412
// we want this to happen after the launchPageConnectModal processes
303413
// the resolution of the promise, hence the timeout.
304414
setTimeout(() => {
415+
this.navigate(`userPage/${guid}${subPath ? '/' + subPath : ''}`, { replace: true });
305416
this.userPage(guid, state, itemHash, skipNSFWmodal, '@' + handle);
306417
}, 0);
307418
});
@@ -317,25 +428,33 @@ module.exports = Backbone.Router.extend({
317428
},
318429

319430
transactions: function(state, orderID, tabState){
320-
this.newView(new transactionsView({
321-
userModel: this.userModel,
322-
userProfile: this.userProfile,
323-
socketView: this.socketView,
324-
state: state,
325-
orderID: orderID,
326-
tabState: tabState //opens a tab in the order modal
327-
}), "userPage");
431+
this.newView(transactionsView, {
432+
viewArgs: {
433+
userModel: this.userModel,
434+
userProfile: this.userProfile,
435+
socketView: this.socketView,
436+
state: state,
437+
orderID: orderID,
438+
tabState: tabState //opens a tab in the order modal
439+
},
440+
bodyID: 'transactionsPage'
441+
});
442+
328443
app.appBar.setTitle(window.polyglot.t('Transactions'));
329444
},
330445

331446
settings: function(state){
332447
$('.js-loadingModal').addClass('show');
333-
this.newView(new settingsView({
334-
userModel: this.userModel,
335-
userProfile: this.userProfile,
336-
state: state,
337-
socketView: this.socketView
338-
}), "userPage");
448+
449+
this.newView(settingsView, {
450+
viewArgs: {
451+
userModel: this.userModel,
452+
userProfile: this.userProfile,
453+
state: state,
454+
socketView: this.socketView
455+
}
456+
});
457+
339458
app.appBar.setTitle(window.polyglot.t('Settings'));
340459
}
341460
});

js/start.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ $(window).bind('keydown', function(e) {
331331
Backbone.history.loadUrl();
332332
break;
333333
case window.config.keyShortcuts.restart:
334-
location.reload();
334+
app.router.refresh();
335335
break;
336336
}
337337

@@ -403,7 +403,7 @@ var loadProfile = function(landingRoute, onboarded) {
403403
setCurrentBitCoin(cCode, user, function() {
404404
newSocketView = new socketView();
405405

406-
newPageNavView = new pageNavView({
406+
app.pageNav = newPageNavView = new pageNavView({
407407
model: user,
408408
socketView: newSocketView,
409409
userProfile: userProfile,

0 commit comments

Comments
 (0)