Merge lp:~stephen-stewart/snapweb/install-as-behaviour into lp:~snappy-dev/snapweb/trunk

Proposed by Stephen Stewart on 2015-05-06
Status: Merged
Approved by: John Lenton on 2015-05-07
Approved revision: 163
Merged at revision: 124
Proposed branch: lp:~stephen-stewart/snapweb/install-as-behaviour
Merge into: lp:~snappy-dev/snapweb/trunk
Prerequisite: lp:~stephen-stewart/snapweb/json-error-responses
Diff against target: 514 lines (+168/-158)
9 files modified
www/src/css/installer.css (+1/-2)
www/src/css/snaplist.css (+18/-53)
www/src/js/behaviors/install.js (+100/-0)
www/src/js/models/snap.js (+9/-6)
www/src/js/templates/snap-layout.hbs (+0/-4)
www/src/js/templates/snaplist-item.hbs (+16/-3)
www/src/js/views/snap-layout.js (+5/-84)
www/src/js/views/snaplist-item.js (+12/-1)
www/tests/snapLayoutViewSpec.js (+7/-5)
To merge this branch: bzr merge lp:~stephen-stewart/snapweb/install-as-behaviour
Reviewer Review Type Date Requested Status
John Lenton 2015-05-06 Approve on 2015-05-07
Review via email: mp+258424@code.launchpad.net

Commit Message

make install a behavior, use it in snaplist

To post a comment you must log in.
John Lenton (chipaca) :
review: Approve
Snappy Tarmac (snappydevtarmac) wrote :

The attempt to merge lp:~stephen-stewart/webdm/install-as-behaviour into lp:webdm failed. Below is the output from the failed tests.

Checking formatting
Installing godeps
Install golint

# we always run in a fresh dir in tarmac
export GOPATH=$(mktemp -d)
trap 'rm -rf "$GOPATH"' EXIT

# this is a hack, but not sure tarmac is golang friendly
mkdir -p $GOPATH/src/launchpad.net/webdm
cp -a . $GOPATH/src/launchpad.net/webdm/
cd $GOPATH/src/launchpad.net/webdm

./run-checks
# cd .; git clone https://go.googlesource.com/tools /tmp/tmp.NEcYIte9FM/src/golang.org/x/tools
Cloning into '/tmp/tmp.NEcYIte9FM/src/golang.org/x/tools'...
error: RPC failed; result=7, HTTP code = 0
fatal: The remote end hung up unexpectedly
package github.com/golang/lint/golint
 imports golang.org/x/tools/go/gcimporter: exit status 128
package github.com/golang/lint/golint
 imports golang.org/x/tools/go/types
 imports golang.org/x/tools/go/types
 imports golang.org/x/tools/go/types: cannot find package "golang.org/x/tools/go/types" in any of:
 /usr/lib/go/src/pkg/golang.org/x/tools/go/types (from $GOROOT)
 /tmp/tmp.NEcYIte9FM/src/golang.org/x/tools/go/types (from $GOPATH)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'www/src/css/installer.css'
2--- www/src/css/installer.css 2015-05-05 15:17:29 +0000
3+++ www/src/css/installer.css 2015-05-07 08:17:05 +0000
4@@ -1,7 +1,6 @@
5 .b-installer {
6 position:relative;
7 width:300px;
8- padding-bottom:9px;
9 }
10
11 .b-installer__button {
12@@ -62,6 +61,6 @@
13 transition: right 0.1s;
14 }
15
16-.b-installer--thinking .b-installer__progress {
17+.b-installer--thinking.b-installer--install .b-installer__progress {
18 display:block;
19 }
20
21=== modified file 'www/src/css/snaplist.css'
22--- www/src/css/snaplist.css 2015-04-30 14:44:46 +0000
23+++ www/src/css/snaplist.css 2015-05-07 08:17:05 +0000
24@@ -25,14 +25,14 @@
25 width:100%;
26 }
27
28-.b-snaplist--grid .b-snaplist__label {
29+.b-snaplist--grid .b-snaplist__name {
30 text-align: center;
31 white-space: nowrap;
32 overflow: hidden;
33 text-overflow: ellipsis;
34 }
35
36-.b-snaplist--grid .b-snaplist__label ~ * {
37+.b-snaplist--grid .b-snaplist__name ~ * {
38 display:none;
39 }
40
41@@ -62,10 +62,10 @@
42 height: 40px;
43 }
44
45-.b-snaplist--row .b-snaplist__label,
46-.b-snaplist--row .b-snaplist__author,
47-.b-snaplist--row .b-snaplist__title {
48- flex:2;
49+.b-snaplist--row .b-snaplist__name,
50+.b-snaplist--row .b-snaplist__type,
51+.b-snaplist--row .b-snaplist__origin {
52+ flex:1 1;
53 margin:0 1em;
54 white-space: nowrap;
55 width: 100%;
56@@ -73,50 +73,15 @@
57 text-overflow: ellipsis;
58 }
59
60-.b-snaplist--row .b-snaplist__label {
61- min-width:100px;
62-}
63-
64-.b-snaplist--row .b-snaplist__title {
65- flex:4 1;
66-}
67-
68-.b-snaplist--row .b-snaplist__install {
69- flex:3;
70- margin:0 1em;
71- min-width:100px;
72-}
73-
74-/**
75-
76-.b-snaplist__item a,
77-.b-snaplist__item a:link,
78-.b-snaplist__item a:hover,
79-.b-snaplist__item a:visited {
80- color: #333333;
81-}
82-
83-.b-snaplist__snap-icon {
84- text-align:center;
85-}
86-
87-.b-snaplist__snap-icon img {
88- border-radius:5%;
89-}
90-
91-.b-snaplist__snap-icon:hover {
92- background: #efefef;
93-}
94-
95-.b-snaplist__snap-label {
96- font-weight: 400;
97- text-align: center;
98- display: block;
99- cursor: pointer;
100- font-size: 1em;
101- white-space: nowrap;
102- overflow: hidden;
103- text-overflow: ellipsis;
104-}
105-
106-**/
107+.b-snaplist--row .b-snaplist__version {
108+ min-width:100px;
109+}
110+
111+.b-snaplist--row .b-snaplist__type {
112+ text-align:center;
113+}
114+
115+.b-snaplist--row .b-installer__message {
116+ /** XXX hmm **/
117+ display:none;
118+}
119
120=== added directory 'www/src/js/behaviors'
121=== added file 'www/src/js/behaviors/install.js'
122--- www/src/js/behaviors/install.js 1970-01-01 00:00:00 +0000
123+++ www/src/js/behaviors/install.js 2015-05-07 08:17:05 +0000
124@@ -0,0 +1,100 @@
125+// install behavior
126+var _ = require('lodash');
127+var Backbone = require('backbone');
128+var Marionette = require('backbone.marionette');
129+var CONF = require('../config.js');
130+
131+module.exports = Marionette.Behavior.extend({
132+ events: {
133+ 'click @ui.installerButton': 'onInstallClick'
134+ },
135+
136+ modelEvents: {
137+ 'change:installHTMLClass': 'onHTMLClassChange',
138+ 'change:status': 'onStatusChange',
139+ 'change:progress': 'onProgressChange'
140+ },
141+
142+ ui: {
143+ errorMessage: '.b-installer__error',
144+ statusMessage: '.b-installer__message',
145+ installer: '.b-installer',
146+ installerButton: '.b-installer__button',
147+ installerProgress: '.b-installer__value'
148+ },
149+
150+ onHTMLClassChange: function(model) {
151+ var installer = this.ui.installer;
152+ installer.removeClass(model.previous('installHTMLClass'))
153+ .addClass(model.get('installHTMLClass'));
154+ },
155+
156+ onStatusChange: function(model) {
157+ var oldState = model.previous('status');
158+ var state = model.get('status');
159+ var msg = model.get('installActionString');
160+ var installer = this.ui.installer;
161+ var installerButton = this.ui.installerButton;
162+
163+ // reset progress
164+ this.ui.installerProgress.css('right', '100%');
165+
166+ if (_.contains(CONF.INSTALL_STATE, state)) {
167+ installerButton.text(msg);
168+ } else {
169+ // in the rare case that a status isn't one we're expecting,
170+ // remove the install button
171+ installer.remove();
172+ }
173+
174+ if (
175+ state === CONF.INSTALL_STATE.INSTALLED &&
176+ oldState === CONF.INSTALL_STATE.INSTALLING
177+ ) {
178+ this.ui.statusMessage.text('Install successful!');
179+ } else {
180+ this.ui.statusMessage.text('');
181+ }
182+
183+ if (
184+ state === CONF.INSTALL_STATE.UNINSTALLED &&
185+ oldState === CONF.INSTALL_STATE.UNINSTALLING
186+ ) {
187+ this.ui.statusMessage.text('Uninstall successful!');
188+ }
189+ },
190+
191+ onProgressChange: function(model) {
192+ var state = model.get('status');
193+ var progress;
194+
195+ if (state === CONF.INSTALL_STATE.INSTALLING) {
196+ progress = (100 - (model.get('progress') | 0)) + '%';
197+ this.ui.installerProgress.css('right', progress);
198+ }
199+ },
200+
201+ onInstallClick: function(e) {
202+ var model = this.view.model;
203+ var status = model.get('status');
204+
205+ if (status === CONF.INSTALL_STATE.INSTALLED) {
206+ // uninstall
207+ model.set({
208+ status: CONF.INSTALL_STATE.UNINSTALLING
209+ });
210+ model.destroy({
211+ dataType : 'html'
212+ });
213+ } else if (status === CONF.INSTALL_STATE.UNINSTALLED) {
214+ // install
215+ model.save({
216+ status: CONF.INSTALL_STATE.INSTALLING
217+ }, {
218+ dataType : 'html'
219+ });
220+ } else {
221+ e.preventDefault();
222+ }
223+ }
224+});
225
226=== modified file 'www/src/js/models/snap.js'
227--- www/src/js/models/snap.js 2015-05-06 15:01:46 +0000
228+++ www/src/js/models/snap.js 2015-05-07 08:17:05 +0000
229@@ -50,15 +50,11 @@
230 chan.command('alert:error', model);
231 });
232
233- this.on('change:message', this.onMessageChange);
234+ this.on('add change:message', this.onMessageChange);
235
236 this.on('add change:status', this.handleStatusChange);
237 },
238
239- onMessageChange: function(model) {
240- console.log('message:', model.get('message'));
241- },
242-
243 handleStatusChange: function(model) {
244 this.setInstallActionString(model);
245 this.setInstallHTMLClass(model);
246@@ -116,15 +112,22 @@
247 },
248
249 parse: function(response) {
250+
251 if (response.hasOwnProperty('icon') && !response.icon.length) {
252 response.icon = this.defaults.icon;
253 }
254+
255+ if (response.hasOwnProperty('origin') && !response.origin.length) {
256+ response.origin = this.defaults.origin;
257+ }
258+
259 return response;
260 },
261
262 defaults: {
263 icon: '/public/images/default-package-icon.svg',
264- installActionString: false
265+ installActionString: false,
266+ origin: '-'
267 }
268
269 });
270
271=== modified file 'www/src/js/templates/snap-layout.hbs'
272--- www/src/js/templates/snap-layout.hbs 2015-05-05 15:17:29 +0000
273+++ www/src/js/templates/snap-layout.hbs 2015-05-07 08:17:05 +0000
274@@ -18,9 +18,5 @@
275 </div>
276 </div>
277 </div>
278-<div class="b-installer__error">{{ message }}</div>
279-
280 <div class="region-menu"></div>
281-
282-
283 <div class="region-tab"></div>
284
285=== modified file 'www/src/js/templates/snaplist-item.hbs'
286--- www/src/js/templates/snaplist-item.hbs 2015-05-04 16:44:28 +0000
287+++ www/src/js/templates/snaplist-item.hbs 2015-05-07 08:17:05 +0000
288@@ -1,6 +1,19 @@
289 <div class="b-snaplist__icon">
290 <img src="{{ icon }}" alt="{{ name }} icon">
291 </div>
292-<div class="b-snaplist__label">{{ name }}</div>
293-<div class="b-snaplist__title">{{ version }}</div>
294-<div class="b-snaplist__author">{{ origin }}</div>
295+<div class="b-snaplist__name">{{ name }}</div>
296+{{!-- only name is shown in grid view, everything after is hidden --}}
297+<div class="b-snaplist__version">{{ version }}</div>
298+<div class="b-snaplist__origin">{{ origin }}</div>
299+<div class="b-snaplist__type">{{ type }}</div>
300+<div class="b-snaplist__actions">
301+ <div class="b-installer {{ installHTMLClass }}">
302+ {{#if installActionString}}
303+ <div class="b-installer__button">{{ installActionString }}</div>
304+ <div class="b-installer__progress" title="download progress">
305+ <div class="b-installer__value"></div>
306+ </div>
307+ {{/if}}
308+ <div class="b-installer__message"></div>
309+ </div>
310+</div>
311
312=== modified file 'www/src/js/views/snap-layout.js'
313--- www/src/js/views/snap-layout.js 2015-05-06 15:01:46 +0000
314+++ www/src/js/views/snap-layout.js 2015-05-07 08:17:05 +0000
315@@ -6,89 +6,29 @@
316 var SnapDetailView = require('./snap-detail.js');
317 var SnapReviewsView = require('./snap-reviews.js');
318 var SnapSettingsView = require('./snap-settings.js');
319+var InstallBehavior = require('../behaviors/install.js');
320 var template = require('../templates/snap-layout.hbs');
321 var CONF = require('../config.js');
322
323 module.exports = Marionette.LayoutView.extend({
324
325- initialize: function() {
326- this.listenTo(
327- this.model, 'change:installHTMLClass', this.onModelHTMLClassChange
328- );
329- this.listenTo(
330- this.model, 'change:status', this.onModelStatusChange
331- );
332- this.listenTo(
333- this.model, 'change:progress', this.onProgressChange
334- );
335+ behaviors: {
336+ InstallBehavior: {
337+ behaviorClass: InstallBehavior
338+ }
339 },
340
341 onShow: function() {
342 window.scrollTo(0, 0);
343 },
344
345- onProgressChange: function(model) {
346- var state = model.get('status');
347- var progress;
348-
349- if (state === CONF.INSTALL_STATE.INSTALLING) {
350- progress = (100 - (model.get('progress') | 0)) + '%';
351- this.ui.installerProgress.css('right', progress);
352- }
353- },
354-
355- onModelHTMLClassChange: function(model) {
356- var installer = this.ui.installer;
357- installer.removeClass(model.previous('installHTMLClass'))
358- .addClass(model.get('installHTMLClass'));
359- },
360-
361- onModelStatusChange: function(model) {
362- var oldState = model.previous('status');
363- var state = model.get('status');
364- var msg = model.get('installActionString');
365- var installer = this.ui.installer;
366- var installerButton = this.ui.installerButton;
367-
368- // reset progress
369- this.ui.installerProgress.css('right', '100%');
370-
371- if (_.contains(CONF.INSTALL_STATE, state)) {
372- installerButton.text(msg);
373- } else {
374- // in the rare case that a status isn't one we're expecting,
375- // remove the install button
376- installer.remove();
377- }
378-
379- if (
380- state === CONF.INSTALL_STATE.INSTALLED &&
381- oldState === CONF.INSTALL_STATE.INSTALLING
382- ) {
383- this.ui.statusMessage.text('Install successful!');
384- }
385-
386- if (
387- state === CONF.INSTALL_STATE.UNINSTALLED &&
388- oldState === CONF.INSTALL_STATE.UNINSTALLING
389- ) {
390- this.ui.statusMessage.text('Uninstall successful!');
391- }
392- },
393-
394 className: 'b-snap',
395
396 ui: {
397- errorMessage: '.b-installer__error',
398- statusMessage: '.b-installer__message',
399- installer: '.b-installer',
400- installerButton: '.b-installer__button',
401- installerProgress: '.b-installer__value',
402 menu: '.b-snap__nav-item'
403 },
404
405 events: {
406- 'click @ui.installerButton': 'install',
407 'click @ui.menu': 'section'
408 },
409
410@@ -117,25 +57,6 @@
411 tabRegion: '.region-tab'
412 },
413
414- install: function(e) {
415- var status = this.model.get('status');
416-
417- if (status === CONF.INSTALL_STATE.INSTALLED) {
418- // uninstall
419- this.model.set({
420- status: CONF.INSTALL_STATE.UNINSTALLING
421- });
422- this.model.destroy();
423- } else if (status === CONF.INSTALL_STATE.UNINSTALLED) {
424- // install
425- this.model.save({
426- status: CONF.INSTALL_STATE.INSTALLING
427- });
428- } else {
429- e.preventDefault();
430- }
431- },
432-
433 _getSectionView: function(section) {
434 var view;
435 switch (section) {
436
437=== modified file 'www/src/js/views/snaplist-item.js'
438--- www/src/js/views/snaplist-item.js 2015-04-24 11:15:41 +0000
439+++ www/src/js/views/snaplist-item.js 2015-05-07 08:17:05 +0000
440@@ -1,7 +1,10 @@
441 // snap item view
442+var $ = require('jquery');
443 var Backbone = require('backbone');
444+Backbone.$ = $;
445 var Marionette = require('backbone.marionette');
446 var Radio = require('backbone.radio');
447+var InstallBehavior = require('../behaviors/install.js');
448 var template = require('../templates/snaplist-item.hbs');
449 var snapChannel = Radio.channel('snap');
450
451@@ -17,8 +20,16 @@
452 'click': 'showSnap'
453 },
454
455+ behaviors: {
456+ InstallBehavior: {
457+ behaviorClass: InstallBehavior
458+ }
459+ },
460+
461 showSnap: function(e) {
462 e.preventDefault();
463- snapChannel.command('show', this.model);
464+ if (!$(e.target).is('.b-installer__button')) {
465+ snapChannel.command('show', this.model);
466+ }
467 }
468 });
469
470=== modified file 'www/tests/snapLayoutViewSpec.js'
471--- www/tests/snapLayoutViewSpec.js 2015-05-05 14:40:14 +0000
472+++ www/tests/snapLayoutViewSpec.js 2015-05-07 08:17:05 +0000
473@@ -14,6 +14,8 @@
474 model: this.model
475 });
476 this.view.render();
477+
478+ this.uiInstaller = this.view.$el.find('.b-installer');
479 });
480
481 afterEach(function() {
482@@ -29,27 +31,27 @@
483
484 it('should be thinking when installing', function() {
485 this.model.set('status', CONF.INSTALL_STATE.INSTALLING);
486- expect(this.view.ui.installer.hasClass('b-installer--thinking')).toBeTruthy();
487+ expect(this.uiInstaller.hasClass('b-installer--thinking')).toBeTruthy();
488 });
489
490 it('should be thinking when uninstalling', function() {
491 this.model.set('status', CONF.INSTALL_STATE.UNINSTALLING);
492- expect(this.view.ui.installer.hasClass('b-installer--thinking')).toBeTruthy();
493+ expect(this.uiInstaller.hasClass('b-installer--thinking')).toBeTruthy();
494 });
495
496 it('should not be thinking when installed', function() {
497 this.model.set('status', CONF.INSTALL_STATE.INSTALLED);
498- expect(this.view.ui.installer.hasClass('b-installer--thinking')).toBeFalsy();
499+ expect(this.uiInstaller.hasClass('b-installer--thinking')).toBeFalsy();
500 });
501
502 it('should not be thinking when uninstalled', function() {
503 this.model.set('status', CONF.INSTALL_STATE.UNINSTALLED);
504- expect(this.view.ui.installer.hasClass('b-installer--thinking')).toBeFalsy();
505+ expect(this.uiInstaller.hasClass('b-installer--thinking')).toBeFalsy();
506 });
507
508 it('should deactivate install button if model has unrecognised status', function() {
509 this.model.set('status', '');
510- expect(this.view.ui.installer).not.toBe();
511+ expect(this.uiInstaller).not.toBe();
512 });
513
514 xit('should inform user when install succeeds', function() {

Subscribers

People subscribed via source and target branches