Skip to content
This repository has been archived by the owner on Nov 16, 2022. It is now read-only.

Commit

Permalink
✨ Allow creation of tags
Browse files Browse the repository at this point in the history
  • Loading branch information
marvinroger committed Dec 7, 2016
1 parent 78bd81a commit 5eecc7d
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 44 deletions.
34 changes: 17 additions & 17 deletions app/components/pages/Devices.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@
<div class="level-left">
<div class="level-item">
<p class="control has-addons">
<input v-model="tagInput.value" class="input" type="text" placeholder="Filtrer par tag">
<ul v-if="tagInput.value !== '' && dropdownTags.length !== 0" id="autocomplete-dropdown">
<li v-for="tag in dropdownTags"><a href="" @click.prevent="addTag(tag)"><span class="tag" :class="getTagColorClass(tag.color)"><span class="icon is-small"><i class="fa" :class="getTagIconClass(tag.icon)"></i></span>{{ tag.id }}</span></a></li>
<input v-model="tagInput.value" @focus="tagInput.focus = true" @blur="blurFilterWorkaround" class="input" type="text" placeholder="Filtrer ou créer un tag">
<ul v-if="tagInput.focus == true" id="autocomplete-dropdown">
<li v-if="tagInput.value !== ''"><a href="" @click.prevent="createTag(tagInput.value)"><span class="icon is-small"><i class="fa fa-plus"></i></span> Créer le tag <b>{{ tagInput.value }}</b></a></li>
<li v-for="tag in dropdownTags"><a href="" @click.prevent="addTag(tag)"><span class="tag"><span class="icon is-small"><i class="fa fa-tag"></i></span>&nbsp;{{ tag.id }}</span></a></li>
</ul>
</p>
</div>
<div class="level-item">
<span v-for="id in selectedTagsIds" class="tag" :class="getTagColorClass(infrastructure.tags[id].color)"><span class="icon is-small"><i class="fa" :class="getTagIconClass(infrastructure.tags[id].icon)"></i></span>{{ infrastructure.tags[id].id }}<button @click="deleteTag(id)" class="delete is-small"></button></span>
<span v-for="id in selectedTagsIds" class="tag"><span class="icon is-small"><i class="fa fa-tag"></i></span>&nbsp;{{ infrastructure.tags[id].id }}<button @click="deleteTag(id)" class="delete is-small"></button></span>
</div>
</div>

Expand Down Expand Up @@ -49,7 +50,7 @@
</template>

<script>
import {mapState} from 'eva.js'
import {mapState, mapActions} from 'eva.js'
import SwitchDevice from '../devices/Switch'
import LightDevice from '../devices/Light'
Expand Down Expand Up @@ -83,7 +84,7 @@ export default {
'motion': MotionDevice,
'buzzer': BuzzerDevice
},
tagInput: { value: '' },
tagInput: { value: '', focus: false },
selectedTagsIds: [],
DEVICE_STATES: {
ONLINE: 'ONLINE',
Expand Down Expand Up @@ -124,22 +125,21 @@ export default {
...mapState(['infrastructure'])
},
methods: {
getTagColorClass (color) {
const obj = {}
obj[`is-${color}`] = true
return obj
},
getTagIconClass (icon) {
const obj = {}
obj[`fa-${icon}`] = true
return obj
},
addTag (tag) {
this.selectedTagsIds.push(tag.id)
},
deleteTag (tagId) {
this.selectedTagsIds.splice(this.selectedTagsIds.indexOf(tagId), 1)
}
},
async createTag (tagId) {
await this.createTagAction({ id: tagId })
},
blurFilterWorkaround () { // TODO
setTimeout(() => {
this.tagInput.focus = false
}, 200)
},
...mapActions({ createTagAction: 'createTag' })
}
}
Expand Down
11 changes: 11 additions & 0 deletions app/store/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,17 @@ export default function initializeStore (app) {
value: opts.value
}})

return result
},
async createTag ({commit}, opts) {
const result = await wsRequest({
ws,
method: 'createTag',
parameters: {
id: opts.id
}
})

return result
}
}
Expand Down
8 changes: 2 additions & 6 deletions migrations/002-tags.sql
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,9 @@
--------------------------------------------------------------------------------

CREATE TABLE tags (
id TEXT PRIMARY KEY NOT NULL,
color TEXT NOT NULL,
icon TEXT NOT NULL
id TEXT PRIMARY KEY NOT NULL

CONSTRAINT tags_id_ck CHECK(id <> ''),
CONSTRAINT tags_color_ck CHECK(color <> ''),
CONSTRAINT tags_icon_ck CHECK(icon <> '')
CONSTRAINT tags_id_ck CHECK(id <> '')
);

CREATE TABLE nodes_tags (
Expand Down
9 changes: 9 additions & 0 deletions server/lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {EventEmitter} from 'events'

import {generateMessage, parseMessage, MESSAGE_TYPES} from '../../common/ws-messages'
import {INFRASTRUCTURE} from '../../common/events'
import Tag from './infrastructure/tag'

/**
* This class handles WebSocket clients
Expand Down Expand Up @@ -62,6 +63,14 @@ export default class Client extends EventEmitter {

this.mqttClient.publish(`homie/${deviceId}/${nodeId}/${property}/set`, value, { qos: 1, retain: true })

this._sendResponse(message, true)
} else if (message.method === 'createTag') {
const tagId = message.parameters.id

const tag = new Tag()
tag.id = tagId
this.infrastructure.addTag(tag)

this._sendResponse(message, true)
}
}
Expand Down
11 changes: 7 additions & 4 deletions server/lib/infrastructure/infrastructure.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,25 +65,28 @@ class Infrastructure extends EventEmitter {

/**
* Adds a tag
* @param {Object} tag the tag
* @param {Tag} tag the tag
*/
addTag (tag) {
this._tags.set(tag.id, tag)
tag.on('update', (update) => {
this.emit('update', update)
})
this._wasUpdated()
}

/**
* Gets a tag
* @param {string} tagId the tag ID
* @returns {Object} the tag
* @returns {Tag} the tag
*/
getTag (tagId) {
return this._tags.get(tagId)
}

/**
* Gets all tags from the infrastructure
* @returns {Iterable.<Object>} the tags
* @returns {Iterable.<Tag>} the tags
*/
getTags () {
return this._tags.values()
Expand All @@ -101,7 +104,7 @@ class Infrastructure extends EventEmitter {
}

for (const tag of this.getTags()) {
representation.tags[tag.id] = tag
if (tag.isValid) representation.tags[tag.id] = tag.toJSON()
}

return representation
Expand Down
41 changes: 41 additions & 0 deletions server/lib/infrastructure/tag.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {EventEmitter} from 'events'

/**
* This class represents a tag
*/
export default class Tag extends EventEmitter {
constructor () {
super()

this._id = null

this.isValid = false

Object.seal(this)
}

get id () { return this._id }
set id (val) {
if (!val || this._id === val) return
this._id = val
this._wasUpdated()
}

_wasUpdated () {
const wasValid = this.isValid
this.isValid = (
this._id !== null
)

if (!wasValid && this.isValid) this.emit('valid')

if (this.isValid) this.emit('update', { type: 'tag' })
}

toJSON () {
const representation = {}
representation.id = this.id

return representation
}
}
42 changes: 25 additions & 17 deletions server/services/database.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {dateToSqlite} from '../helpers/sqlite'
import Device from '../lib/infrastructure/device'
import Node from '../lib/infrastructure/node'
import Property from '../lib/infrastructure/property'
import Tag from '../lib/infrastructure/tag'

/* Auth */

Expand Down Expand Up @@ -55,19 +56,6 @@ export async function deleteToken ({ db }, token) {

/* Tags */

/**
* This function creates a tag in the database
* @param {db: Database} $deps dependencies
* @param {string} tag the tag to create
* @returns {Promise} promise, to be resolved on success or rejected on failure
*/
export async function createTag ({ db }, tag) {
await db.run(
'INSERT INTO tags (id, color, icon) VALUES (?, ?, ?)',
tag.id, tag.color, tag.icon
)
}

/**
* This function deletes a tag in the database
* @param {db: Database} $deps dependencies
Expand Down Expand Up @@ -108,6 +96,20 @@ export async function deleteTagFromNode ({ db }, nodeTagId) {
* @returns {Promise} promise, to be resolved on success or rejected on failure
*/
export async function syncInfrastructure ({ db }, infrastructure) {
/* Synchronize tags */

for (let tag of infrastructure.getTags()) {
await db.run(
`INSERT INTO tags (id)
SELECT :id
WHERE (SELECT id FROM tags WHERE id = :id) IS NULL
`, {
':id': tag.id
})
}

/* Synchronize devices */

for (let device of infrastructure.getDevices()) {
if (!device.isValid) return

Expand Down Expand Up @@ -200,7 +202,7 @@ export async function syncInfrastructure ({ db }, infrastructure) {
WHERE (SELECT id FROM nodes_tags WHERE node_id = :node_id AND tag_id = :tag_id) IS NULL
`, {
':node_id': nodeId,
':tag_id': tag
':tag_id': tag.id
})
}

Expand Down Expand Up @@ -258,11 +260,17 @@ export async function getInfrastructure ({ db }, infrastructure) {
/* Tags */

const tags = await db.all(
`SELECT id, color, icon
`SELECT id
FROM tags
`)

for (const tag of tags) if (!infrastructure.hasTag(tag.id)) infrastructure.addTag(tag)
for (const tagInDb of tags) {
if (!infrastructure.hasTag(tagInDb['id'])) {
const tag = new Tag()
tag.id = tagInDb['id']
infrastructure.addTag(tag)
}
}

/* Devices */

Expand Down Expand Up @@ -324,7 +332,7 @@ export async function getInfrastructure ({ db }, infrastructure) {
node.id = value['n.device_node_id']
node.type = value['n.type']
node.propertiesDefinition = value['n.properties']
for (const tagPerNode of tagsPerNodes) if (tagPerNode.node_id === value['n.id']) node.addTag(tagPerNode.tag_id)
for (const tagPerNode of tagsPerNodes) if (tagPerNode.node_id === value['n.id']) node.addTag(infrastructure.getTag(tagPerNode.tag_id))
device.addNode(node)
} else node = device.getNode(value['n.device_node_id'])

Expand Down

0 comments on commit 5eecc7d

Please sign in to comment.