deleting attributes, closes #34

This commit is contained in:
azivner 2018-02-06 23:09:19 -05:00
parent 7c74c77a2c
commit e011b9ae63
8 changed files with 60 additions and 25 deletions

View file

@ -0,0 +1 @@
ALTER TABLE attributes ADD COLUMN isDeleted INT NOT NULL DEFAULT 0;

View file

@ -86,7 +86,8 @@ CREATE TABLE IF NOT EXISTS "attributes"
name TEXT NOT NULL,
value TEXT,
dateCreated TEXT NOT NULL,
dateModified TEXT NOT NULL
dateModified TEXT NOT NULL,
isDeleted INT NOT NULL
);
CREATE UNIQUE INDEX `IDX_sync_entityName_entityId` ON `sync` (
`entityName`,

View file

@ -24,7 +24,7 @@ class Note extends Entity {
}
async getAttributes() {
return this.repository.getEntities("SELECT * FROM attributes WHERE noteId = ?", [this.noteId]);
return this.repository.getEntities("SELECT * FROM attributes WHERE noteId = ? AND isDeleted = 0", [this.noteId]);
}
async getAttribute(name) {

View file

@ -26,6 +26,19 @@ const attributesDialog = (function() {
setTimeout(() => $(".attribute-name:last").focus(), 100);
};
this.deleteAttribute = function(data, event) {
const attr = self.getTargetAttribute(event);
const attrData = attr();
if (attrData) {
attrData.isDeleted = 1;
attr(attrData);
addLastEmptyRow();
}
};
function isValid() {
for (let attrs = self.attributes(), i = 0; i < attrs.length; i++) {
if (self.isEmptyName(i)) {
@ -65,26 +78,25 @@ const attributesDialog = (function() {
};
function addLastEmptyRow() {
const attrs = self.attributes();
const attrs = self.attributes().filter(attr => attr().isDeleted === 0);
const last = attrs.length === 0 ? null : attrs[attrs.length - 1]();
if (!last || last.name.trim() !== "" || last.value !== "") {
self.attributes.push(ko.observable({
attributeId: '',
name: '',
value: ''
value: '',
isDeleted: 0
}));
}
}
this.attributeChanged = function (row) {
this.attributeChanged = function (data, event) {
addLastEmptyRow();
for (const attr of self.attributes()) {
if (row.attributeId === attr().attributeId) {
attr.valueHasMutated();
}
}
const attr = self.getTargetAttribute(event);
attr.valueHasMutated();
};
this.isNotUnique = function(index) {
@ -109,6 +121,13 @@ const attributesDialog = (function() {
const cur = self.attributes()[index]();
return cur.name.trim() === "" && (cur.attributeId !== "" || cur.value !== "");
};
this.getTargetAttribute = function(event) {
const context = ko.contextFor(event.target);
const index = context.$index();
return self.attributes()[index];
}
}

View file

@ -12,7 +12,7 @@ const attributes = require('../../services/attributes');
router.get('/notes/:noteId/attributes', auth.checkApiAuth, wrap(async (req, res, next) => {
const noteId = req.params.noteId;
res.send(await sql.getRows("SELECT * FROM attributes WHERE noteId = ? ORDER BY dateCreated", [noteId]));
res.send(await sql.getRows("SELECT * FROM attributes WHERE isDeleted = 0 AND noteId = ? ORDER BY dateCreated", [noteId]));
}));
router.put('/notes/:noteId/attributes', auth.checkApiAuth, wrap(async (req, res, next) => {
@ -23,10 +23,15 @@ router.put('/notes/:noteId/attributes', auth.checkApiAuth, wrap(async (req, res,
await sql.doInTransaction(async () => {
for (const attr of attributes) {
if (attr.attributeId) {
await sql.execute("UPDATE attributes SET name = ?, value = ?, dateModified = ? WHERE attributeId = ?",
[attr.name, attr.value, now, attr.attributeId]);
await sql.execute("UPDATE attributes SET name = ?, value = ?, dateModified = ?, isDeleted = ? WHERE attributeId = ?",
[attr.name, attr.value, now, attr.isDeleted, attr.attributeId]);
}
else {
// if it was "created" and then immediatelly deleted, we just don't create it at all
if (attr.isDeleted) {
continue;
}
attr.attributeId = utils.newAttributeId();
await sql.insert("attributes", {
@ -35,7 +40,8 @@ router.put('/notes/:noteId/attributes', auth.checkApiAuth, wrap(async (req, res,
name: attr.name,
value: attr.value,
dateCreated: now,
dateModified: now
dateModified: now,
isDeleted: false
});
}
@ -43,11 +49,11 @@ router.put('/notes/:noteId/attributes', auth.checkApiAuth, wrap(async (req, res,
}
});
res.send(await sql.getRows("SELECT * FROM attributes WHERE noteId = ? ORDER BY dateCreated", [noteId]));
res.send(await sql.getRows("SELECT * FROM attributes WHERE isDeleted = 0 AND noteId = ? ORDER BY dateCreated", [noteId]));
}));
router.get('/attributes/names', auth.checkApiAuth, wrap(async (req, res, next) => {
const names = await sql.getColumn("SELECT DISTINCT name FROM attributes");
const names = await sql.getColumn("SELECT DISTINCT name FROM attributes WHERE isDeleted = 0");
for (const attr of attributes.BUILTIN_ATTRIBUTES) {
if (!names.includes(attr)) {
@ -63,7 +69,7 @@ router.get('/attributes/names', auth.checkApiAuth, wrap(async (req, res, next) =
router.get('/attributes/values/:attributeName', auth.checkApiAuth, wrap(async (req, res, next) => {
const attributeName = req.params.attributeName;
const values = await sql.getColumn("SELECT DISTINCT value FROM attributes WHERE name = ? AND value != '' ORDER BY value", [attributeName]);
const values = await sql.getColumn("SELECT DISTINCT value FROM attributes WHERE isDeleted = 0 AND name = ? AND value != '' ORDER BY value", [attributeName]);
res.send(values);
}));

View file

@ -3,7 +3,7 @@
const build = require('./build');
const packageJson = require('../../package');
const APP_DB_VERSION = 72;
const APP_DB_VERSION = 73;
module.exports = {
app_version: packageJson.version,

View file

@ -13,7 +13,10 @@ async function getNoteAttributeMap(noteId) {
async function getNoteIdWithAttribute(name, value) {
return await sql.getValue(`SELECT notes.noteId FROM notes JOIN attributes USING(noteId)
WHERE notes.isDeleted = 0 AND attributes.name = ? AND attributes.value = ?`, [name, value]);
WHERE notes.isDeleted = 0
AND attributes.isDeleted = 0
AND attributes.name = ?
AND attributes.value = ?`, [name, value]);
}
async function getNotesWithAttribute(dataKey, name, value) {
@ -23,11 +26,11 @@ async function getNotesWithAttribute(dataKey, name, value) {
if (value !== undefined) {
notes = await repository.getEntities(`SELECT notes.* FROM notes JOIN attributes USING(noteId)
WHERE notes.isDeleted = 0 AND attributes.name = ? AND attributes.value = ?`, [name, value]);
WHERE notes.isDeleted = 0 AND attributes.isDeleted = 0 AND attributes.name = ? AND attributes.value = ?`, [name, value]);
}
else {
notes = await repository.getEntities(`SELECT notes.* FROM notes JOIN attributes USING(noteId)
WHERE notes.isDeleted = 0 AND attributes.name = ?`, [name]);
WHERE notes.isDeleted = 0 AND attributes.isDeleted = 0 AND attributes.name = ?`, [name]);
}
return notes;
@ -41,7 +44,7 @@ async function getNoteWithAttribute(dataKey, name, value) {
async function getNoteIdsWithAttribute(name) {
return await sql.getColumn(`SELECT DISTINCT notes.noteId FROM notes JOIN attributes USING(noteId)
WHERE notes.isDeleted = 0 AND attributes.name = ?`, [name]);
WHERE notes.isDeleted = 0 AND attributes.isDeleted = 0 AND attributes.name = ? AND attributes.isDeleted = 0`, [name]);
}
async function createAttribute(noteId, name, value = null, sourceId = null) {
@ -54,7 +57,8 @@ async function createAttribute(noteId, name, value = null, sourceId = null) {
name: name,
value: value,
dateModified: now,
dateCreated: now
dateCreated: now,
isDeleted: false
});
await sync_table.addAttributeSync(attributeId, sourceId);

View file

@ -376,7 +376,7 @@
<div id="attributes-dialog" title="Note attributes" style="display: none; padding: 20px;">
<form data-bind="submit: save">
<div style="text-align: center">
<button class="btn btn-large" style="width: 200px;" id="save-attributes-button" type="submit">Save <kbd>enter</kbd></button>
<button class="btn btn-large" style="width: 200px;" id="save-attributes-button" type="submit">Save changes <kbd>enter</kbd></button>
</div>
<div style="height: 97%; overflow: auto">
@ -386,10 +386,11 @@
<th>ID</th>
<th>Name</th>
<th>Value</th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: attributes">
<tr>
<tr data-bind="if: isDeleted == 0">
<td data-bind="text: attributeId"></td>
<td>
<!-- Change to valueUpdate: blur is necessary because jQuery UI autocomplete hijacks change event -->
@ -400,6 +401,9 @@
<td>
<input type="text" class="attribute-value" data-bind="value: value, valueUpdate: 'blur', event: { blur: $parent.attributeChanged }" style="width: 300px"/>
</td>
<td title="Delete" style="padding: 13px;">
<span class="glyphicon glyphicon-trash" data-bind="click: $parent.deleteAttribute"></span>
</td>
</tr>
</tbody>
</table>