-
Notifications
You must be signed in to change notification settings - Fork 27
Tracing Storage Traversal
The SpiderOak HTML5 mobile client is implemented using a Model View ViewModel architecture, which significantly helps tame code complexity, but can obscure control flow. In this document we describe the control transitions that happen when traversing an account's storage content hierarchy, in order to provide orientation useful for understanding how content traversal works and how to extend it.
By using Backbone, Backstack, and other fairly lightweight Model View ViewModel facilities, we are able to interconnect model, view, server synchronization, and etc. code, without the inherent complexities that comes from tightly coupling functions across separate concerns.
While allowing us to write simpler, less conflated functions, that decoupling also presents a challenge to tracing control flow across the program. For orientation, particularly for people getting acquainted with Backbone/Backstack (including some of the authors), we offer this examination of user account storage navigation control flow.
We use the techniques described in the wiki Home page Running Testing and Debugging section to do the tracing.
(The subject code is under active development at time of writing, so the accuracy of this account is likely to drift. We hope it will remain useful, conveying the principles and techniques for understanding the control flow, even as the specifics change.)
- a leading '.' dot on function and element names means they are attributes of the
spiderOakApp
object - leading '↓' single down-arrow indicates that the described function directly invokes a subsequently described function
- a leading '⇓' double down-arrow indicates that the described function indirectly - asynchronously or via an event - triggers another function
- a leading '→' single right-arrow indicates that the described function was directly invoked by an above described function. (Possibly but not always the most recent one with a '↓' single down-arrow.)
- a leading '⇒' double right-arrow indicates that the described function was indirectly invoked - asynchronously or by event - by an above described function. (Possibly but not always the most recent one with a '⇓' double down-arrow.)
- All of the views are associated with DOM $el elements, usually in their .initialize() methods, and they present their renderings there. (This is regular Backbone stuff, but I skip over most of the initialize actions, so worth mentioning once.)
We trace a single path through the code, focusing on successful actions to traverse downward a few levels into a user's storage content hierarchy.
- » The system responds with an "Please Wait / Authenticating" busy dialog, and eventually returns with a page entitled "Spider Oak" and the app's slide-right menu button.
- Hitting the "Log in" button fires the
loginButton_tapHandler
event, associated with the button by the spiderOakApp.LoginView.events object (src/views/LoginView.js
) - That event triggers the .LoginView.loginButton_tapHandler(event) function:
- Prevent the default tap handling
- ↓ Invoke this.form_submitHandler(event)
- → .LoginView.form_submitHandler(event):
- use .dialogView.showWait(...) (from
src/views/DialogView.js
) to present a busy dialog - collect the form credentials and data, blur the document activeElement, etc
- ⇓ using .AccountModel() (
src/models/AccountModel.js
), do account.login() with the credentials, plus success and error callbacks from this object.
- use .dialogView.showWait(...) (from
- ⇒ successful account.login() invokes the form_submitHandler() success(apiRoot) function, passed-in by and residing in .LoginView.form_submitHandler():
- set some incidental login metadata (rememberme)
- ↓ do this.dismiss()
- → .LoginView.dismiss(): shuffles some css so the login form is dismissed
- ⇒ ⇓ successful .AccountModel.login() fires the
loginSuccess
event - ⇒ the
loginSuccess
event triggers .onLoginSuccess (fromsrc/app.js
):- hide class="modal" and class="wait-dialog" divs
- ↓ create the .MenuSheetView() and .MenuSheetView.render() it.
- → .MenuSheetView.render() (
src/views/MenuSheetView.js
):- create the .DevicesCollection() (
src/collections/DevicesCollection.js
) - assign the .DevicesCollection.url from accountModel.getStorageURL()
- ↓ create the devicesListView (
src/views/DevicesView.js
) with the devices collection- → .DevicesListView().initialize(), .render() (
src/views/DevicesView.js
):- fetch the account's devices and render them
- → .DevicesListView().initialize(), .render() (
- equip the MenuSheetView with iScroll, for scroll features absent outside of Android context.
- create the .DevicesCollection() (
- » Dismissing the login view (.LoginView.dismiss(), above) reveals the generic (index.html) main page
- » The main page has a menu button by which the user can expose the menu sheet.
- » The system slides the main page to the right, revealing the menu sheet (or slides the main page to the left, hiding the menu sheet, if the page was already to the right).
- ⇓ .MainView.events() (
src/views/MainView.js
) associated the index.html DOM element with class="menu-btn" with the menuButton_handler event, fired by tapping the button. - ↓ .MainView.menuButton_handler() calls .MainView.openMenu() (if it was closed; otherwise, it calls .Mainview.closeMenu()).
- → .MainView.openMenu() adjusts CSS to slide the main page right and mark the menuButton state as "open".
- ⇓ .MainView.openMenu() also triggers the "menuOpening" event.
- ⇒ the "menuOpening" event is caught by the .MenuSheetView object, invoking .MenuSheetView.menuOpening(), which invokes the iScroll .menuScroller.refresh() method, to refresh the menu sheets scrolling provisions.
- »: The main page is occupied by entries showing the contents of the tapped device, and made prominent by sliding the main page back to hide the menu sheet.
- ⇓ Rendering the .MenuSheetCollection includes rending of the devices collection. That associates each .DevicesView.DeviceItemView's "a_tapHandler" event with its entry, which is raised on tap.
- ⇒ the "a_tapHandler" event triggers the respective view's .DeviceItemView.a_tapHandler() method, which:
- calls .mainView.closeMenu() to adjust CSS that slides the main page left to cover the menu-sheet.
- !! » packages the specific device model, and some attributes, in an options object, to be passed to a folder view
- removes the "current" class from the li element within the id "menusheet" div, and adds the "current" class to the device entry's li element.
- uses the Backstack viewsStack to make and present a FolderView, passing the prepared options object to convey the specific device model to be presented by the folder view
- (The view is pushed on the stack if it's empty, not added to the stack at all if the current view currently presents the device's model, and made to replace the contents of the stack if there are other, different contents.)
- » The main page switches to present the contents of the tapped device's contents, subfolders and constitutent files alike.