2017-06-24 20:06:28 +02:00
|
|
|
import { BaseModel } from 'lib/base-model.js';
|
|
|
|
import { Note } from 'lib/models/note.js';
|
|
|
|
import { Folder } from 'lib/models/folder.js';
|
|
|
|
import { Setting } from 'lib/models/setting.js';
|
|
|
|
import { Database } from 'lib/database.js';
|
|
|
|
import { time } from 'lib/time-utils.js';
|
2017-06-15 20:18:48 +02:00
|
|
|
import moment from 'moment';
|
|
|
|
|
|
|
|
class BaseItem extends BaseModel {
|
|
|
|
|
|
|
|
static useUuid() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-06-20 21:18:19 +02:00
|
|
|
static systemPath(itemOrId) {
|
|
|
|
if (typeof itemOrId === 'string') return itemOrId + '.md';
|
|
|
|
return itemOrId.id + '.md';
|
2017-06-15 20:18:48 +02:00
|
|
|
}
|
|
|
|
|
2017-06-17 20:40:08 +02:00
|
|
|
static itemClass(item) {
|
|
|
|
if (!item) throw new Error('Item cannot be null');
|
2017-06-19 00:06:10 +02:00
|
|
|
|
|
|
|
if (typeof item === 'object') {
|
|
|
|
if (!('type_' in item)) throw new Error('Item does not have a type_ property');
|
2017-06-19 21:26:27 +02:00
|
|
|
return item.type_ == BaseModel.MODEL_TYPE_NOTE ? Note : Folder;
|
2017-06-19 00:06:10 +02:00
|
|
|
} else {
|
2017-06-19 21:26:27 +02:00
|
|
|
if (Number(item) === BaseModel.MODEL_TYPE_NOTE) return Note;
|
|
|
|
if (Number(item) === BaseModel.MODEL_TYPE_FOLDER) return Folder;
|
2017-06-19 00:06:10 +02:00
|
|
|
throw new Error('Unknown type: ' + item);
|
|
|
|
}
|
2017-06-17 20:40:08 +02:00
|
|
|
}
|
|
|
|
|
2017-07-01 12:30:50 +02:00
|
|
|
// Returns the IDs of the items that have been synced at least once
|
|
|
|
static async syncedItems() {
|
|
|
|
let folders = await Folder.modelSelectAll('SELECT id FROM folders WHERE sync_time > 0');
|
|
|
|
let notes = await Note.modelSelectAll('SELECT id FROM notes WHERE is_conflict = 0 AND sync_time > 0');
|
|
|
|
return folders.concat(notes);
|
|
|
|
}
|
|
|
|
|
2017-06-15 20:18:48 +02:00
|
|
|
static pathToId(path) {
|
|
|
|
let s = path.split('.');
|
|
|
|
return s[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
static loadItemByPath(path) {
|
|
|
|
let id = this.pathToId(path);
|
|
|
|
return Note.load(id).then((item) => {
|
|
|
|
if (item) return item;
|
|
|
|
return Folder.load(id);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-06-25 09:52:25 +02:00
|
|
|
static loadItemByField(itemType, field, value) {
|
|
|
|
let ItemClass = this.itemClass(itemType);
|
|
|
|
return ItemClass.loadByField(field, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static loadItem(itemType, id) {
|
|
|
|
let ItemClass = this.itemClass(itemType);
|
|
|
|
return ItemClass.load(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
static deleteItem(itemType, id) {
|
|
|
|
let ItemClass = this.itemClass(itemType);
|
|
|
|
return ItemClass.delete(id);
|
|
|
|
}
|
|
|
|
|
2017-06-19 00:06:10 +02:00
|
|
|
static serialize_format(propName, propValue) {
|
2017-06-15 20:18:48 +02:00
|
|
|
if (['created_time', 'updated_time'].indexOf(propName) >= 0) {
|
|
|
|
if (!propValue) return '';
|
2017-06-19 00:06:10 +02:00
|
|
|
propValue = moment.unix(propValue / 1000).utc().format('YYYY-MM-DDTHH:mm:ss.SSS') + 'Z';
|
2017-06-15 20:18:48 +02:00
|
|
|
} else if (propValue === null || propValue === undefined) {
|
|
|
|
propValue = '';
|
|
|
|
}
|
|
|
|
|
|
|
|
return propValue;
|
|
|
|
}
|
|
|
|
|
2017-06-19 00:06:10 +02:00
|
|
|
static unserialize_format(type, propName, propValue) {
|
2017-06-17 20:40:08 +02:00
|
|
|
if (propName == 'type_') return propValue;
|
2017-06-15 20:18:48 +02:00
|
|
|
|
2017-06-19 00:06:10 +02:00
|
|
|
let ItemClass = this.itemClass(type);
|
|
|
|
|
2017-06-15 20:18:48 +02:00
|
|
|
if (['created_time', 'updated_time'].indexOf(propName) >= 0) {
|
|
|
|
if (!propValue) return 0;
|
2017-06-19 00:06:10 +02:00
|
|
|
propValue = moment(propValue, 'YYYY-MM-DDTHH:mm:ss.SSSZ').format('x');
|
2017-06-15 20:18:48 +02:00
|
|
|
} else {
|
2017-06-19 00:06:10 +02:00
|
|
|
propValue = Database.formatValue(ItemClass.fieldType(propName), propValue);
|
2017-06-15 20:18:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return propValue;
|
|
|
|
}
|
|
|
|
|
2017-06-19 00:06:10 +02:00
|
|
|
static serialize(item, type = null, shownKeys = null) {
|
2017-06-29 22:52:52 +02:00
|
|
|
item = this.filter(item);
|
|
|
|
|
2017-06-15 20:18:48 +02:00
|
|
|
let output = [];
|
|
|
|
|
|
|
|
output.push(item.title);
|
|
|
|
output.push('');
|
|
|
|
output.push(type == 'note' ? item.body : '');
|
|
|
|
output.push('');
|
|
|
|
for (let i = 0; i < shownKeys.length; i++) {
|
|
|
|
let v = item[shownKeys[i]];
|
2017-06-19 00:06:10 +02:00
|
|
|
v = this.serialize_format(shownKeys[i], v);
|
2017-06-15 20:18:48 +02:00
|
|
|
output.push(shownKeys[i] + ': ' + v);
|
|
|
|
}
|
|
|
|
|
|
|
|
return output.join("\n");
|
|
|
|
}
|
|
|
|
|
2017-06-19 00:06:10 +02:00
|
|
|
static unserialize(content) {
|
2017-06-15 20:18:48 +02:00
|
|
|
let lines = content.split("\n");
|
|
|
|
let output = {};
|
|
|
|
let state = 'readingProps';
|
|
|
|
let body = [];
|
|
|
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
|
|
let line = lines[i];
|
|
|
|
|
|
|
|
if (state == 'readingProps') {
|
|
|
|
line = line.trim();
|
|
|
|
|
|
|
|
if (line == '') {
|
|
|
|
state = 'readingBody';
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let p = line.indexOf(':');
|
|
|
|
if (p < 0) throw new Error('Invalid property format: ' + line + ": " + content);
|
|
|
|
let key = line.substr(0, p).trim();
|
|
|
|
let value = line.substr(p + 1).trim();
|
2017-06-19 00:06:10 +02:00
|
|
|
output[key] = value;
|
2017-06-15 20:18:48 +02:00
|
|
|
} else if (state == 'readingBody') {
|
|
|
|
body.splice(0, 0, line);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (body.length < 3) throw new Error('Invalid body size: ' + body.length + ': ' + content);
|
|
|
|
|
|
|
|
let title = body.splice(0, 2);
|
|
|
|
output.title = title[0];
|
2017-06-19 00:06:10 +02:00
|
|
|
|
|
|
|
if (!output.type_) throw new Error('Missing required property: type_: ' + content);
|
|
|
|
output.type_ = Number(output.type_);
|
|
|
|
|
2017-06-19 21:26:27 +02:00
|
|
|
if (output.type_ == BaseModel.MODEL_TYPE_NOTE) output.body = body.join("\n");
|
2017-06-15 20:18:48 +02:00
|
|
|
|
2017-06-19 00:06:10 +02:00
|
|
|
for (let n in output) {
|
|
|
|
if (!output.hasOwnProperty(n)) continue;
|
|
|
|
output[n] = this.unserialize_format(output.type_, n, output[n]);
|
|
|
|
}
|
|
|
|
|
2017-06-15 20:18:48 +02:00
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
2017-06-19 00:06:10 +02:00
|
|
|
static itemsThatNeedSync(limit = 100) {
|
2017-06-27 21:26:29 +02:00
|
|
|
return Folder.modelSelectAll('SELECT * FROM folders WHERE sync_time < updated_time LIMIT ' + limit).then((items) => {
|
2017-06-19 00:06:10 +02:00
|
|
|
if (items.length) return { hasMore: true, items: items };
|
2017-06-27 21:26:29 +02:00
|
|
|
return Note.modelSelectAll('SELECT * FROM notes WHERE sync_time < updated_time AND is_conflict = 0 LIMIT ' + limit).then((items) => {
|
2017-06-19 00:06:10 +02:00
|
|
|
return { hasMore: items.length >= limit, items: items };
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-06-15 20:18:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export { BaseItem };
|