diff --git a/bower.json b/bower.json index 1e86fa1e..0890f521 100644 --- a/bower.json +++ b/bower.json @@ -32,7 +32,8 @@ "eventEmitter": "~4.3.0", "angular-ladda": "~0.3.1", "d3": "~3.5.6", - "spin.js": "~2.3.2" + "spin.js": "~2.3.2", + "angular-xeditable": "~0.1.9" }, "private": true, "devDependencies": { diff --git a/lib/db/api.js b/lib/db/api.js index 8cc0d829..9079fec8 100644 --- a/lib/db/api.js +++ b/lib/db/api.js @@ -258,6 +258,12 @@ dbapi.setDeviceRotation = function(serial, rotation) { })) } +dbapi.setDeviceNote = function(serial, note) { + return db.run(r.table('devices').get(serial).update({ + notes: note + })) +} + dbapi.setDeviceReverseForwards = function(serial, forwards) { return db.run(r.table('devices').get(serial).update({ reverseForwards: forwards diff --git a/lib/units/websocket/index.js b/lib/units/websocket/index.js index f99ea509..b736bb7f 100644 --- a/lib/units/websocket/index.js +++ b/lib/units/websocket/index.js @@ -305,6 +305,28 @@ module.exports = function(options) { new Promise(function(resolve) { socket.on('disconnect', resolve) + // Global messages for all clients using socket.io + // + // Device note + .on('device.note', function(data) { + return dbapi.setDeviceNote(data.serial, data.note) + .then(function() { + return dbapi.loadDevice(data.serial) + }) + .then(function(device) { + if(device) { + io.emit('device.change', { + important: true + , data: { + serial: device.serial + , notes: device.notes + } + }) + } + }) + }) + // Client specific messages + // // Settings .on('user.settings.update', function(data) { dbapi.updateUserSettings(user.email, data) diff --git a/res/app/components/stf/device/device-service.js b/res/app/components/stf/device/device-service.js index d118cca3..53b5246c 100644 --- a/res/app/components/stf/device/device-service.js +++ b/res/app/components/stf/device/device-service.js @@ -210,5 +210,12 @@ module.exports = function DeviceServiceFactory($http, socket, EnhanceDeviceServi }) } + deviceService.updateNote = function (serial, note) { + socket.emit('device.note', { + serial: serial, + note: note + }) + } + return deviceService } diff --git a/res/app/device-list/column/device-column-service.js b/res/app/device-list/column/device-column-service.js index 4f183df6..7c4908c1 100644 --- a/res/app/device-list/column/device-column-service.js +++ b/res/app/device-list/column/device-column-service.js @@ -18,7 +18,7 @@ var filterOps = { } } -module.exports = function DeviceColumnService($filter, gettext) { +module.exports = function DeviceColumnService($filter, $compile, gettext) { // Definitions for all possible values. return { state: DeviceStatusCell({ @@ -252,8 +252,14 @@ module.exports = function DeviceColumnService($filter, gettext) { return device.provider ? device.provider.name : '' } }) - , notes: TextCell({ + , notes: XEditableCell({ title: gettext('Notes') + , compile: $compile + , scopeRequired: true + , attrs: { + model: 'device.notes' + , onbeforesave: 'updateNote(device.serial, $data)' + } , value: function(device) { return device.notes || '' } @@ -613,3 +619,38 @@ function DeviceStatusCell(options) { } }) } + +function XEditableCell(options) { + return _.defaults(options, { + title: options.title + , defaultOrder: 'asc' + , build: function (scope) { + var td = document.createElement('td') + , a = document.createElement('a') + + // Ref: http://vitalets.github.io/angular-xeditable/#text-simple + a.setAttribute('href', '#') + a.setAttribute('editable-text', options.attrs.model) + a.setAttribute('onbeforesave', options.attrs.onbeforesave) + + a.appendChild(document.createTextNode(options.attrs.model)) + td.appendChild(a) + + // compile with new scope + options.compile(td)(scope) + return td + } + , update: function(td, item) { + var a = td.firstChild + , t = a.firstChild + t.nodeValue = options.value(item) || 'click to add' + return td + } + , compare: function(a, b) { + return compareIgnoreCase(options.value(a), options.value(b)) + } + , filter: function(item, filter) { + return filterIgnoreCase(options.value(item), filter.query) + } + }) +} diff --git a/res/app/device-list/details/device-list-details-directive.js b/res/app/device-list/details/device-list-details-directive.js index d8b10c52..52e0ca5f 100644 --- a/res/app/device-list/details/device-list-details-directive.js +++ b/res/app/device-list/details/device-list-details-directive.js @@ -2,10 +2,12 @@ var patchArray = require('./../util/patch-array') module.exports = function DeviceListDetailsDirective( $filter +, $compile +, $rootScope , gettext , DeviceColumnService , GroupService -, $rootScope +, DeviceService , LightboxImageService , StandaloneService ) { @@ -277,6 +279,20 @@ module.exports = function DeviceListDetailsDirective( } })() + // check if new column needs a new scope or not + // and build accordingly + function buildColumn(col, device) { + var td + + if (col.scopeRequired) { + var childScope = scope.$new() + childScope.device = device + td = col.build(childScope) + } else { + td = col.build() + } + return td + } // Creates a completely new row for the device. Means that this is // the first time we see the device. function createRow(device) { @@ -291,8 +307,10 @@ module.exports = function DeviceListDetailsDirective( } for (var i = 0, l = activeColumns.length; i < l; ++i) { - td = scope.columnDefinitions[activeColumns[i]].build() - scope.columnDefinitions[activeColumns[i]].update(td, device) + var col = scope.columnDefinitions[activeColumns[i]] + + td = buildColumn(col, device) + col.update(td, device) tr.appendChild(td) } @@ -301,6 +319,10 @@ module.exports = function DeviceListDetailsDirective( return tr } + scope.updateNote = function(serial, note) { + DeviceService.updateNote(serial, note) + } + // Patches all rows. function patchAll(patch) { for (var i = 0, l = rows.length; i < l; ++i) { @@ -317,7 +339,7 @@ module.exports = function DeviceListDetailsDirective( switch (op[0]) { case 'insert': var col = scope.columnDefinitions[op[2]] - tr.insertBefore(col.update(col.build(), device), tr.cells[op[1]]) + tr.insertBefore(col.update(buildColumn(col, device), device), tr.cells[op[1]]) break case 'remove': tr.deleteCell(op[1]) diff --git a/res/app/device-list/index.js b/res/app/device-list/index.js index e00b4ba7..6cdd52e8 100644 --- a/res/app/device-list/index.js +++ b/res/app/device-list/index.js @@ -1,6 +1,7 @@ require('./device-list.css') module.exports = angular.module('device-list', [ + require('angular-xeditable').name, require('stf/device').name, require('stf/user/group').name, require('stf/control').name, @@ -21,4 +22,8 @@ module.exports = angular.module('device-list', [ controller: 'DeviceListCtrl' }) }]) + .run(function(editableOptions) { + // bootstrap3 theme for xeditables + editableOptions.theme = 'bs3' + }) .controller('DeviceListCtrl', require('./device-list-controller')) diff --git a/res/web_modules/angular-xeditable/index.js b/res/web_modules/angular-xeditable/index.js new file mode 100644 index 00000000..933f658c --- /dev/null +++ b/res/web_modules/angular-xeditable/index.js @@ -0,0 +1,7 @@ +require('angular-xeditable/dist/js/xeditable.js') +require('angular-xeditable/dist/css/xeditable.css') +require('./style.css') + +module.exports = { + name: 'xeditable' +} diff --git a/res/web_modules/angular-xeditable/style.css b/res/web_modules/angular-xeditable/style.css new file mode 100644 index 00000000..2f4242fb --- /dev/null +++ b/res/web_modules/angular-xeditable/style.css @@ -0,0 +1,17 @@ +a.editable-click { + text-decoration: none; + color: #167FFC; + border-bottom: none; +} + +a.editable-click:hover { + color: #808080; +} + +a.editable-empty { + color: transparent; +} + +a.editable-empty:focus { + color: #808080; +}