import { makeSessionContentId } from "../utils/id"; import Collection from "./collection"; import SessionContent from "./session-content"; /** * @typedef Session * @property {string} id * @property {string} noteId * @property {string} sessionContentId * @property {string} dateModified * @property {string} dateCreated * @property {boolean} locked */ /** * @typedef Content * @property {string} data * @property {string} type */ export default class NoteHistory extends Collection { async init() { await super.init(); this.versionsLimit = 100; /** * @type {SessionContent} */ this.sessionContent = await SessionContent.new( this._db, "sessioncontent", false ); } /** * Get complete session history of a note. * @param noteId id of the note * @returns {Promise} An array of session ids of a note */ async get(noteId) { if (!noteId) return []; let indices = this._collection.indexer.getIndices(); let sessionIds = indices.filter((id) => id.startsWith(noteId)); if (sessionIds.length === 0) return []; let history = (await this._getSessions(sessionIds)) || []; return history.sort(function (a, b) { return b.dateModified - a.dateModified; }); } /** * Add and update a session content * @param {string} noteId id of the note * @param {string} dateEdited edited date of the note * @param {Content} content * * @returns {Promise} */ async add(noteId, dateEdited, content) { if (!noteId || !dateEdited || !content) return; let sessionId = `${noteId}_${dateEdited}`; let oldSession = await this._collection.getItem(sessionId); let locked = this._db.notes.note(noteId)?.data?.locked; let session = { id: sessionId, sessionContentId: makeSessionContentId(sessionId), noteId, dateCreated: oldSession ? oldSession.dateCreated : Date.now(), localOnly: true, }; if (locked) { session.locked = true; } await this._collection.addItem(session); await this.sessionContent.add(sessionId, content, session.locked); await this._cleanup(noteId); return session; } async _cleanup(noteId, limit = this.versionsLimit) { let history = await this.get(noteId); if (history.length === 0 || history.length < limit) return; history.sort(function (a, b) { return a.dateModified - b.dateModified; }); let deleteCount = history.length - limit; for (let i = 0; i < deleteCount; i++) { let session = history[i]; await this._remove(session); } } /** * Get content of a session * @param {string} sessionId session id * * @returns {Promise} */ async content(sessionId) { if (!sessionId) return; /** * @type {Session} */ let session = await this._collection.getItem(sessionId); return await this.sessionContent.get(session.sessionContentId); } /** * Remove a session from storage * @param {string} sessionId */ async remove(sessionId) { if (!sessionId) return; /** * @type {Session} */ let session = await this._collection.getItem(sessionId); await this._remove(session); } /** * Remove all sessions of a note from storage * @param {string} noteId */ async clearSessions(noteId) { if (!noteId) return; let history = await this.get(noteId); for (let item of history) { await this._remove(item); } } /** * * @param {Session} session */ async _remove(session) { await this._collection.deleteItem(session.id); await this.sessionContent.remove(session.sessionContentId); } /** * * @param {string} sessionId */ async restore(sessionId) { /** * @type {Session} */ let session = await this._collection.getItem(sessionId); let content = await this.sessionContent.get(session.sessionContentId); let note = this._db.notes.note(session.noteId).data; if (session.locked) { await this._db.content.add({ id: note.contentId, data: content.data, type: content.type, }); } else { await this._db.notes.add({ id: session.noteId, content: { data: content.data, type: content.type, }, }); } } /** * * @returns A json string containing all sessions with content */ async serialize() { return JSON.stringify({ sessions: await this.all(), sessionContents: await this.sessionContent.all(), }); } async all() { return this._getSessions(this._collection.indexer.getIndices()); } async _getSessions(sessionIds) { let items = await this._collection.getItems(sessionIds); return Object.values(items); } /** * Restore session history from a serialized json string. * @param {string} data * @returns */ async deserialize(data) { if (!data) return; let deserialized = JSON.parse(data); if (!deserialized.sessions || !deserialized.sessionContents) return; for (let session of deserialized.sessions) { let sessionContent = deserialized.sessionContents.find((v) => v.id.includes(session.id) ); if (sessionContent) { await this._collection.addItem(session); await this.sessionContent._collection.addItem(sessionContent); } } } }