mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-27 10:32:58 +02:00
Adding service to keep track of note resources associations
This commit is contained in:
parent
eef106c99b
commit
f595be07d4
@ -366,7 +366,7 @@ class NoteTextComponent extends React.Component {
|
||||
webviewReady: true,
|
||||
});
|
||||
|
||||
if (Setting.value('env') === 'dev') this.webview_.openDevTools();
|
||||
// if (Setting.value('env') === 'dev') this.webview_.openDevTools();
|
||||
}
|
||||
|
||||
webview_ref(element) {
|
||||
|
@ -122,15 +122,6 @@ class BaseModel {
|
||||
return id.substr(0, 5);
|
||||
}
|
||||
|
||||
// static minimalPartialId(id) {
|
||||
// let length = 2;
|
||||
// while (true) {
|
||||
// const partialId = id.substr(0, length);
|
||||
// const r = await this.db().selectOne('SELECT count(*) as total FROM `' + this.tableName() + '` WHERE `id` LIKE ?', [partialId + '%']);
|
||||
// if (r['total'] <= 1) return partialId;
|
||||
// }
|
||||
// }
|
||||
|
||||
static loadByPartialId(partialId) {
|
||||
return this.modelSelectAll('SELECT * FROM `' + this.tableName() + '` WHERE `id` LIKE ?', [partialId + '%']);
|
||||
}
|
||||
@ -222,20 +213,6 @@ class BaseModel {
|
||||
}
|
||||
if ('type_' in newModel) output.type_ = newModel.type_;
|
||||
return output;
|
||||
// let output = {};
|
||||
// let type = null;
|
||||
// for (let n in newModel) {
|
||||
// if (!newModel.hasOwnProperty(n)) continue;
|
||||
// if (n == 'type_') {
|
||||
// type = newModel[n];
|
||||
// continue;
|
||||
// }
|
||||
// if (!(n in oldModel) || newModel[n] !== oldModel[n]) {
|
||||
// output[n] = newModel[n];
|
||||
// }
|
||||
// }
|
||||
// if (type !== null) output.type_ = type;
|
||||
// return output;
|
||||
}
|
||||
|
||||
static diffObjectsFields(oldModel, newModel) {
|
||||
@ -505,6 +482,8 @@ BaseModel.typeEnum_ = [
|
||||
['TYPE_SEARCH', 7],
|
||||
['TYPE_ALARM', 8],
|
||||
['TYPE_MASTER_KEY', 9],
|
||||
['TYPE_ITEM_CHANGE', 10],
|
||||
['TYPE_NOTE_RESOURCE', 11],
|
||||
];
|
||||
|
||||
for (let i = 0; i < BaseModel.typeEnum_.length; i++) {
|
||||
@ -512,16 +491,6 @@ for (let i = 0; i < BaseModel.typeEnum_.length; i++) {
|
||||
BaseModel[e[0]] = e[1];
|
||||
}
|
||||
|
||||
// BaseModel.TYPE_NOTE = 1;
|
||||
// BaseModel.TYPE_FOLDER = 2;
|
||||
// BaseModel.TYPE_SETTING = 3;
|
||||
// BaseModel.TYPE_RESOURCE = 4;
|
||||
// BaseModel.TYPE_TAG = 5;
|
||||
// BaseModel.TYPE_NOTE_TAG = 6;
|
||||
// BaseModel.TYPE_SEARCH = 7;
|
||||
// BaseModel.TYPE_ALARM = 8;
|
||||
// BaseModel.TYPE_MASTER_KEY = 9;
|
||||
|
||||
BaseModel.db_ = null;
|
||||
BaseModel.dispatch = function(o) {};
|
||||
BaseModel.saveMutexes_ = {};
|
||||
|
@ -123,19 +123,6 @@ class Database {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// return new Promise((resolve, reject) => {
|
||||
// let iid = setInterval(() => {
|
||||
// if (!this.inTransaction_) {
|
||||
// clearInterval(iid);
|
||||
// this.transactionExecBatch(queries).then(() => {
|
||||
// resolve();
|
||||
// }).catch((error) => {
|
||||
// reject(error);
|
||||
// });
|
||||
// }
|
||||
// }, 100);
|
||||
// });
|
||||
}
|
||||
|
||||
this.inTransaction_ = true;
|
||||
@ -149,56 +136,6 @@ class Database {
|
||||
}
|
||||
|
||||
this.inTransaction_ = false;
|
||||
|
||||
// return promiseChain(chain).then(() => {
|
||||
// this.inTransaction_ = false;
|
||||
// });
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// if (queries.length <= 0) return Promise.resolve();
|
||||
|
||||
// if (queries.length == 1) {
|
||||
// let q = this.wrapQuery(queries[0]);
|
||||
// return this.exec(q.sql, q.params);
|
||||
// }
|
||||
|
||||
// // There can be only one transaction running at a time so queue
|
||||
// // any new transaction here.
|
||||
// if (this.inTransaction_) {
|
||||
// return new Promise((resolve, reject) => {
|
||||
// let iid = setInterval(() => {
|
||||
// if (!this.inTransaction_) {
|
||||
// clearInterval(iid);
|
||||
// this.transactionExecBatch(queries).then(() => {
|
||||
// resolve();
|
||||
// }).catch((error) => {
|
||||
// reject(error);
|
||||
// });
|
||||
// }
|
||||
// }, 100);
|
||||
// });
|
||||
// }
|
||||
|
||||
// this.inTransaction_ = true;
|
||||
|
||||
// queries.splice(0, 0, 'BEGIN TRANSACTION');
|
||||
// queries.push('COMMIT'); // Note: ROLLBACK is currently not supported
|
||||
|
||||
// let chain = [];
|
||||
// for (let i = 0; i < queries.length; i++) {
|
||||
// let query = this.wrapQuery(queries[i]);
|
||||
// chain.push(() => {
|
||||
// return this.exec(query.sql, query.params);
|
||||
// });
|
||||
// }
|
||||
|
||||
// return promiseChain(chain).then(() => {
|
||||
// this.inTransaction_ = false;
|
||||
// });
|
||||
}
|
||||
|
||||
static enumId(type, s) {
|
||||
|
@ -202,7 +202,7 @@ class JoplinDatabase extends Database {
|
||||
// default value and thus might cause problems. In that case, the default value
|
||||
// must be set in the synchronizer too.
|
||||
|
||||
const existingDatabaseVersions = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
const existingDatabaseVersions = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
|
||||
let currentVersionIndex = existingDatabaseVersions.indexOf(fromVersion);
|
||||
|
||||
@ -298,6 +298,37 @@ class JoplinDatabase extends Database {
|
||||
queries.push('ALTER TABLE resources ADD COLUMN encryption_blob_encrypted INT NOT NULL DEFAULT 0');
|
||||
}
|
||||
|
||||
if (targetVersion == 10) {
|
||||
const itemChangesTable = `
|
||||
CREATE TABLE item_changes (
|
||||
id INTEGER PRIMARY KEY,
|
||||
item_type INT NOT NULL,
|
||||
item_id TEXT NOT NULL,
|
||||
type INT NOT NULL,
|
||||
created_time INT NOT NULL
|
||||
);
|
||||
`;
|
||||
|
||||
const noteResourcesTable = `
|
||||
CREATE TABLE note_resources (
|
||||
id INTEGER PRIMARY KEY,
|
||||
note_id TEXT NOT NULL,
|
||||
resource_id TEXT NOT NULL
|
||||
);
|
||||
`;
|
||||
|
||||
queries.push(this.sqlStringToLines(itemChangesTable)[0]);
|
||||
queries.push('CREATE INDEX item_changes_item_id ON item_changes (item_id)');
|
||||
queries.push('CREATE INDEX item_changes_created_time ON item_changes (created_time)');
|
||||
queries.push('CREATE INDEX item_changes_item_type ON item_changes (item_type)');
|
||||
|
||||
queries.push(this.sqlStringToLines(noteResourcesTable)[0]);
|
||||
queries.push('CREATE INDEX note_resources_note_id ON note_resources (note_id)');
|
||||
queries.push('CREATE INDEX note_resources_resource_id ON note_resources (resource_id)');
|
||||
|
||||
queries.push({ sql: 'INSERT INTO item_changes (item_type, item_id, type, created_time) SELECT 1, id, 1, ? FROM notes', params: [Date.now()] });
|
||||
}
|
||||
|
||||
queries.push({ sql: 'UPDATE version SET version = ?', params: [targetVersion] });
|
||||
await this.transactionExecBatch(queries);
|
||||
|
||||
|
35
ReactNativeClient/lib/models/ItemChange.js
Normal file
35
ReactNativeClient/lib/models/ItemChange.js
Normal file
@ -0,0 +1,35 @@
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Mutex = require('async-mutex').Mutex;
|
||||
|
||||
class ItemChange extends BaseModel {
|
||||
|
||||
static tableName() {
|
||||
return 'item_changes';
|
||||
}
|
||||
|
||||
static modelType() {
|
||||
return BaseModel.TYPE_ITEM_CHANGE;
|
||||
}
|
||||
|
||||
static async add(itemType, itemId, type) {
|
||||
const release = await ItemChange.addChangeMutex_.acquire();
|
||||
|
||||
try {
|
||||
await this.db().transactionExecBatch([
|
||||
{ sql: 'DELETE FROM item_changes WHERE item_id = ?', params: [itemId] },
|
||||
{ sql: 'INSERT INTO item_changes (item_type, item_id, type, created_time) VALUES (?, ?, ?, ?)', params: [itemType, itemId, type, Date.now()] },
|
||||
]);
|
||||
} finally {
|
||||
release();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ItemChange.addChangeMutex_ = new Mutex();
|
||||
|
||||
ItemChange.TYPE_CREATE = 1;
|
||||
ItemChange.TYPE_UPDATE = 2;
|
||||
ItemChange.TYPE_DELETE = 3;
|
||||
|
||||
module.exports = ItemChange;
|
@ -2,6 +2,7 @@ const BaseModel = require('lib/BaseModel.js');
|
||||
const { Log } = require('lib/log.js');
|
||||
const { sprintf } = require('sprintf-js');
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const ItemChange = require('lib/models/ItemChange.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { shim } = require('lib/shim.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
@ -398,6 +399,8 @@ class Note extends BaseItem {
|
||||
|
||||
const note = await super.save(o, options);
|
||||
|
||||
ItemChange.add(BaseModel.TYPE_NOTE, note.id, isNew ? ItemChange.TYPE_CREATE : ItemChange.TYPE_UPDATE);
|
||||
|
||||
this.dispatch({
|
||||
type: 'NOTE_UPDATE_ONE',
|
||||
note: note,
|
||||
@ -413,18 +416,22 @@ class Note extends BaseItem {
|
||||
return note;
|
||||
}
|
||||
|
||||
static async delete(id, options = null) {
|
||||
let r = await super.delete(id, options);
|
||||
// Not used?
|
||||
|
||||
this.dispatch({
|
||||
type: 'NOTE_DELETE',
|
||||
id: id,
|
||||
});
|
||||
}
|
||||
// static async delete(id, options = null) {
|
||||
// let r = await super.delete(id, options);
|
||||
|
||||
// this.dispatch({
|
||||
// type: 'NOTE_DELETE',
|
||||
// id: id,
|
||||
// });
|
||||
// }
|
||||
|
||||
static batchDelete(ids, options = null) {
|
||||
const result = super.batchDelete(ids, options);
|
||||
for (let i = 0; i < ids.length; i++) {
|
||||
ItemChange.add(BaseModel.TYPE_NOTE, ids[i], ItemChange.TYPE_DELETE);
|
||||
|
||||
this.dispatch({
|
||||
type: 'NOTE_DELETE',
|
||||
id: ids[i],
|
||||
|
32
ReactNativeClient/lib/models/NoteResource.js
Normal file
32
ReactNativeClient/lib/models/NoteResource.js
Normal file
@ -0,0 +1,32 @@
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
|
||||
class NoteResource extends BaseModel {
|
||||
|
||||
static tableName() {
|
||||
return 'note_resources';
|
||||
}
|
||||
|
||||
static modelType() {
|
||||
return BaseModel.TYPE_NOTE_RESOURCE;
|
||||
}
|
||||
|
||||
static async associate(noteId, resourceIds) {
|
||||
let queries = [];
|
||||
queries.push({ sql: 'DELETE FROM note_resources WHERE note_id = ?', params: [noteId] });
|
||||
|
||||
for (let i = 0; i < resourceIds.length; i++) {
|
||||
queries.push({ sql: 'INSERT INTO note_resources (note_id, resource_id) VALUES (?, ?)', params: [noteId, resourceIds[i]] });
|
||||
}
|
||||
|
||||
await this.db().transactionExecBatch(queries);
|
||||
}
|
||||
|
||||
static async remove(noteId) {
|
||||
let queries = [];
|
||||
queries.push({ sql: 'DELETE FROM note_resources WHERE note_id = ?', params: [noteId] });
|
||||
await this.db().transactionExecBatch(queries);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = NoteResource;
|
@ -59,10 +59,10 @@ reg.scheduleSync = async (delay = null, syncOptions = null) => {
|
||||
|
||||
reg.logger().info('Scheduling sync operation...');
|
||||
|
||||
// if (Setting.value("env") === "dev" && delay !== 0) {
|
||||
// reg.logger().info("Schedule sync DISABLED!!!");
|
||||
// return;
|
||||
// }
|
||||
if (Setting.value("env") === "dev" && delay !== 0) {
|
||||
reg.logger().info("Schedule sync DISABLED!!!");
|
||||
return;
|
||||
}
|
||||
|
||||
const timeoutCallback = async () => {
|
||||
reg.scheduleSyncId_ = null;
|
||||
|
54
ReactNativeClient/lib/services/ResourceService.js
Normal file
54
ReactNativeClient/lib/services/ResourceService.js
Normal file
@ -0,0 +1,54 @@
|
||||
const ItemChange = require('lib/models/ItemChange');
|
||||
const NoteResource = require('lib/models/NoteResource');
|
||||
const Note = require('lib/models/Note');
|
||||
const BaseModel = require('lib/BaseModel');
|
||||
|
||||
class ResourceService {
|
||||
|
||||
async indexNoteResources() {
|
||||
let lastId = 0;
|
||||
let lastCreatedTime = 0
|
||||
|
||||
while (true) {
|
||||
const changes = await ItemChange.modelSelectAll(`
|
||||
SELECT id, item_id, type, created_time
|
||||
FROM item_changes
|
||||
WHERE item_type = ?
|
||||
AND id > ?
|
||||
AND created_time >= ?
|
||||
ORDER BY id, created_time ASC
|
||||
LIMIT 10
|
||||
`, [BaseModel.TYPE_NOTE, lastId, lastCreatedTime]);
|
||||
|
||||
if (!changes.length) break;
|
||||
|
||||
const noteIds = changes.map(a => a.item_id);
|
||||
const changesByNoteId = {};
|
||||
for (let i = 0; i < changes.length; i++) {
|
||||
changesByNoteId[changes[i].item_id] = changes[i];
|
||||
}
|
||||
|
||||
const notes = await Note.modelSelectAll('SELECT id, title, body FROM notes WHERE id IN ("' + noteIds.join('","') + '")');
|
||||
|
||||
for (let i = 0; i < notes.length; i++) {
|
||||
const note = notes[i];
|
||||
const change = changesByNoteId[note.id];
|
||||
|
||||
if (change.type === ItemChange.TYPE_CREATE || change.type === ItemChange.TYPE_UPDATE) {
|
||||
const resourceIds = Note.linkedResourceIds(note.body);
|
||||
await NoteResource.associate(note.id, resourceIds);
|
||||
} else if (change.type === ItemChange.TYPE_DELETE) {
|
||||
await NoteResource.remove(note.id);
|
||||
} else {
|
||||
throw new Error('Invalid change type: ' + change.type);
|
||||
}
|
||||
|
||||
lastId = change.id;
|
||||
lastCreatedTime = change.created_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = ResourceService;
|
Loading…
Reference in New Issue
Block a user