1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-01-29 19:13:59 +02:00

257 lines
7.4 KiB
JavaScript
Raw Normal View History

2017-06-24 19:06:28 +01:00
import { BaseModel } from 'lib/base-model.js';
import { Log } from 'lib/log.js';
import { Folder } from 'lib/models/folder.js';
import { BaseItem } from 'lib/models/base-item.js';
2017-06-29 21:52:52 +01:00
import { Setting } from 'lib/models/setting.js';
2017-07-10 18:09:58 +00:00
import { shim } from 'lib/shim.js';
2017-07-11 18:17:23 +00:00
import { time } from 'lib/time-utils.js';
2017-07-15 16:35:40 +01:00
import { _ } from 'lib/locale.js';
2017-06-11 22:11:14 +01:00
import moment from 'moment';
2017-06-29 21:52:52 +01:00
import lodash from 'lodash';
2017-05-10 19:21:09 +00:00
2017-06-15 19:18:48 +01:00
class Note extends BaseItem {
2017-05-10 19:21:09 +00:00
2017-05-10 19:51:43 +00:00
static tableName() {
return 'notes';
}
2017-07-02 16:46:03 +01:00
static async serialize(note, type = null, shownKeys = null) {
2017-06-29 21:52:52 +01:00
let fieldNames = this.fieldNames();
fieldNames.push('type_');
2017-07-15 16:35:40 +01:00
//lodash.pull(fieldNames, 'is_conflict', 'sync_time');
lodash.pull(fieldNames, 'sync_time');
2017-06-29 21:52:52 +01:00
return super.serialize(note, 'note', fieldNames);
2017-05-12 19:54:06 +00:00
}
2017-07-04 19:12:30 +00:00
static async serializeForEdit(note) {
return super.serialize(note, 'note', ['title', 'body']);
2017-07-04 19:12:30 +00:00
}
2017-07-05 19:31:11 +01:00
static async unserializeForEdit(content) {
content += "\n\ntype_: " + BaseModel.TYPE_NOTE;
2017-07-13 22:26:45 +01:00
let output = await super.unserialize(content);
if (!output.title) output.title = '';
if (!output.body) output.body = '';
return output;
2017-07-05 19:31:11 +01:00
}
static async serializeAllProps(note) {
let fieldNames = this.fieldNames();
fieldNames.push('type_');
lodash.pull(fieldNames, 'title', 'body');
return super.serialize(note, 'note', fieldNames);
}
2017-07-03 20:50:45 +01:00
static modelType() {
return BaseModel.TYPE_NOTE;
2017-05-18 19:58:01 +00:00
}
2017-05-20 00:16:50 +02:00
static new(parentId = '') {
let output = super.new();
output.parent_id = parentId;
return output;
2017-05-10 19:51:43 +00:00
}
2017-05-24 20:51:50 +00:00
static newTodo(parentId = '') {
let output = this.new(parentId);
output.is_todo = true;
return output;
}
2017-07-15 19:13:31 +01:00
static sortNotes(notes, order) {
return notes.sort((a, b) => {
let r = -1;
if (a[order.orderBy] < b[order.orderBy]) r = +1;
if (order.orderByDir == 'ASC') r = -r;
return r;
});
}
2017-06-25 13:49:46 +01:00
static previewFields() {
2017-07-12 21:39:47 +01:00
return ['id', 'title', 'body', 'is_todo', 'todo_completed', 'parent_id', 'updated_time', 'sync_time'];
2017-06-25 13:49:46 +01:00
}
static previewFieldsSql() {
2017-06-25 13:49:46 +01:00
return this.db().escapeFields(this.previewFields()).join(',');
}
2017-07-15 16:35:40 +01:00
static async loadFolderNoteByField(folderId, field, value) {
if (!folderId) throw new Error('folderId is undefined');
2017-07-15 16:35:40 +01:00
let options = {
conditions: ['`' + field + '` = ?'],
conditionsParams: [value],
fields: '*',
}
// TODO: add support for limits on .search()
let results = await this.previews(folderId, options);
return results.length ? results[0] : null;
2017-06-27 19:48:01 +00:00
}
2017-06-25 10:00:54 +01:00
static previews(parentId, options = null) {
if (!options) options = {};
if (!options.orderBy) options.orderBy = 'updated_time';
if (!options.orderByDir) options.orderByDir = 'DESC';
2017-07-03 18:58:01 +00:00
if (!options.conditions) options.conditions = [];
if (!options.conditionsParams) options.conditionsParams = [];
if (!options.fields) options.fields = this.previewFields();
2017-07-15 16:35:40 +01:00
if (parentId == Folder.conflictFolderId()) {
options.conditions.push('is_conflict = 1');
} else {
options.conditions.push('is_conflict = 0');
options.conditions.push('parent_id = ?');
options.conditionsParams.push(parentId);
}
2017-06-25 10:00:54 +01:00
if (options.itemTypes && options.itemTypes.length) {
if (options.itemTypes.indexOf('note') >= 0 && options.itemTypes.indexOf('todo') >= 0) {
// Fetch everything
} else if (options.itemTypes.indexOf('note') >= 0) {
2017-07-03 18:58:01 +00:00
options.conditions.push('is_todo = 0');
2017-06-25 10:00:54 +01:00
} else if (options.itemTypes.indexOf('todo') >= 0) {
2017-07-03 18:58:01 +00:00
options.conditions.push('is_todo = 1');
2017-06-25 10:00:54 +01:00
}
}
2017-06-25 13:49:46 +01:00
2017-07-03 18:58:01 +00:00
return this.search(options);
2017-05-11 20:14:01 +00:00
}
static preview(noteId) {
2017-06-23 22:32:24 +01:00
return this.modelSelectOne('SELECT ' + this.previewFieldsSql() + ' FROM notes WHERE is_conflict = 0 AND id = ?', [noteId]);
2017-06-20 19:18:19 +00:00
}
2017-06-20 19:25:01 +00:00
static conflictedNotes() {
2017-06-20 19:18:19 +00:00
return this.modelSelectAll('SELECT * FROM notes WHERE is_conflict = 1');
}
2017-07-15 16:35:40 +01:00
static async conflictedCount() {
let r = await this.db().selectOne('SELECT count(*) as total FROM notes WHERE is_conflict = 1');
return r && r.total ? r.total : 0;
}
2017-07-02 22:01:37 +01:00
static unconflictedNotes() {
return this.modelSelectAll('SELECT * FROM notes WHERE is_conflict = 0');
}
2017-07-11 18:17:23 +00:00
static async updateGeolocation(noteId) {
2017-07-10 20:59:58 +00:00
if (!Note.updateGeolocationEnabled_) return;
2017-07-11 18:17:23 +00:00
let startWait = time.unixMs();
while (true) {
if (!this.geolocationUpdating_) break;
this.logger().info('Waiting for geolocation update...');
await time.sleep(1);
if (startWait + 1000 * 20 < time.unixMs()) {
this.logger().warn('Failed to update geolocation for: timeout: ' + noteId);
return;
}
}
2017-05-15 19:46:34 +00:00
2017-05-23 19:01:37 +00:00
let geoData = null;
2017-07-11 18:17:23 +00:00
if (this.geolocationCache_ && this.geolocationCache_.timestamp + 1000 * 60 * 10 > time.unixMs()) {
geoData = Object.assign({}, this.geolocationCache_);
} else {
this.geolocationUpdating_ = true;
this.logger().info('Fetching geolocation...');
geoData = await shim.Geolocation.currentPosition();
2017-07-10 18:09:58 +00:00
this.logger().info('Got lat/long');
2017-07-11 18:17:23 +00:00
this.geolocationCache_ = geoData;
this.geolocationUpdating_ = false;
}
this.logger().info('Updating lat/long of note ' + noteId);
let note = await Note.load(noteId);
2017-07-11 18:17:23 +00:00
if (!note) return; // Race condition - note has been deleted in the meantime
note.longitude = geoData.coords.longitude;
note.latitude = geoData.coords.latitude;
note.altitude = geoData.coords.altitude;
return Note.save(note);
2017-05-15 19:46:34 +00:00
}
2017-06-24 18:40:03 +01:00
static filter(note) {
if (!note) return note;
2017-06-27 00:20:01 +01:00
let output = super.filter(note);
2017-06-24 18:40:03 +01:00
if ('longitude' in output) output.longitude = Number(!output.longitude ? 0 : output.longitude).toFixed(8);
if ('latitude' in output) output.latitude = Number(!output.latitude ? 0 : output.latitude).toFixed(8);
if ('altitude' in output) output.altitude = Number(!output.altitude ? 0 : output.altitude).toFixed(4);
return output;
}
2017-07-15 16:35:40 +01:00
static async copyToFolder(noteId, folderId) {
if (folderId == Folder.conflictFolderId()) throw new Error(_('Cannot copy note to "%s" notebook', Folder.conflictFolderIdTitle()));
return Note.duplicate(noteId, {
changes: {
parent_id: folderId,
is_conflict: 0, // Also reset the conflict flag in case we're moving the note out of the conflict folder
},
});
}
static async moveToFolder(noteId, folderId) {
if (folderId == Folder.conflictFolderId()) throw new Error(_('Cannot move note to "%s" notebook', Folder.conflictFolderIdTitle()));
return Note.save({
id: noteId,
parent_id: folderId,
is_conflict: 0,
});
}
2017-07-11 18:17:23 +00:00
static async duplicate(noteId, options = null) {
const changes = options && options.changes;
const originalNote = await Note.load(noteId);
if (!originalNote) throw new Error('Unknown note: ' + noteId);
let newNote = Object.assign({}, originalNote);
delete newNote.id;
newNote.sync_time = 0;
for (let n in changes) {
if (!changes.hasOwnProperty(n)) continue;
newNote[n] = changes[n];
}
return this.save(newNote);
}
2017-05-22 20:22:50 +00:00
static save(o, options = null) {
2017-06-29 21:52:52 +01:00
let isNew = this.isNew(o, options);
if (isNew && !o.source) o.source = Setting.value('appName');
if (isNew && !o.source_application) o.source_application = Setting.value('appId');
return super.save(o, options).then((result) => {
// 'result' could be a partial one at this point (if, for example, only one property of it was saved)
// so call this.preview() so that the right fields are populated.
2017-06-14 20:59:46 +01:00
return this.load(result.id);
}).then((note) => {
2017-05-22 20:22:50 +00:00
this.dispatch({
type: 'NOTES_UPDATE_ONE',
note: note,
});
return note;
});
}
2017-07-15 00:12:32 +01:00
static async delete(id, options = null) {
let r = await super.delete(id, options);
this.dispatch({
type: 'NOTES_DELETE',
noteId: id,
});
}
2017-05-10 19:21:09 +00:00
}
2017-07-10 20:59:58 +00:00
Note.updateGeolocationEnabled_ = true;
2017-07-11 18:17:23 +00:00
Note.geolocationUpdating_ = false;
2017-07-10 20:59:58 +00:00
2017-05-10 19:21:09 +00:00
export { Note };