mirror of
https://github.com/zoldar/jenot.git
synced 2026-01-03 14:32:54 +00:00
Implement indexedDB backed store making app offline friendly
This commit is contained in:
parent
274ef4c217
commit
599004206b
4 changed files with 156 additions and 26 deletions
1
TODO.md
1
TODO.md
|
|
@ -1,6 +1,5 @@
|
|||
Only immediate next things to do are listed here, without any far-fetching plans.
|
||||
|
||||
- Implement syncing local storage with service worker (either via postMessage or using IndexedDB)
|
||||
- Implement reminders
|
||||
- Implement masonry layout
|
||||
- Implement color coding
|
||||
|
|
|
|||
139
js/db-store.js
Normal file
139
js/db-store.js
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
export class DBNoteStore extends EventTarget {
|
||||
constructor(dbName, storeName) {
|
||||
super();
|
||||
this.dbName = dbName;
|
||||
this.storeName = storeName;
|
||||
this.db = null;
|
||||
}
|
||||
|
||||
async all() {
|
||||
const that = this;
|
||||
return this.#connect().then(
|
||||
(db) =>
|
||||
new Promise((resolve, reject) => {
|
||||
db
|
||||
.transaction([that.storeName], "readonly")
|
||||
.objectStore(that.storeName)
|
||||
.getAll().onsuccess = (data) => resolve(data.target.result);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async get(id) {
|
||||
const that = this;
|
||||
let result;
|
||||
|
||||
return this.#connect().then(
|
||||
(db) =>
|
||||
new Promise(
|
||||
(resolve, reject) =>
|
||||
(db
|
||||
.transaction([that.storeName], "readonly")
|
||||
.objectStore(that.storeName)
|
||||
.get(id).onsuccess = (data) => resolve(data.target.result)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
async add(note) {
|
||||
const that = this;
|
||||
|
||||
const entry = {
|
||||
id: "id_" + Date.now(),
|
||||
type: note.type,
|
||||
content: note.content,
|
||||
created: new Date(),
|
||||
};
|
||||
|
||||
return this.#connect().then(
|
||||
(db) =>
|
||||
new Promise(
|
||||
(resolve, reject) =>
|
||||
(db
|
||||
.transaction([that.storeName], "readwrite")
|
||||
.objectStore(that.storeName)
|
||||
.add(entry).onsuccess = () => resolve(null)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
async reset() {
|
||||
const that = this;
|
||||
|
||||
return this.#connect().then(
|
||||
(db) =>
|
||||
new Promise(
|
||||
(resolve, reject) =>
|
||||
(db
|
||||
.transaction([that.storeName], "readwrite")
|
||||
.objectStore(that.storeName)
|
||||
.clear().onsuccess = () => resolve(null)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
async remove({ id }) {
|
||||
const that = this;
|
||||
|
||||
return this.#connect().then(
|
||||
(db) =>
|
||||
new Promise(
|
||||
(resolve, reject) =>
|
||||
(db
|
||||
.transaction([that.storeName], "readwrite")
|
||||
.objectStore(that.storeName)
|
||||
.delete(id).onsuccess = () => resolve(null)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
async update(note) {
|
||||
const that = this;
|
||||
|
||||
return this.#connect().then(
|
||||
(db) =>
|
||||
new Promise(
|
||||
(resolve, reject) =>
|
||||
(db
|
||||
.transaction([that.storeName], "readwrite")
|
||||
.objectStore(that.storeName)
|
||||
.put(note).onsuccess = () => resolve(null)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
saveStorage() {
|
||||
this.dispatchEvent(new CustomEvent("save"));
|
||||
}
|
||||
|
||||
async #connect() {
|
||||
if (!this.db) {
|
||||
this.db = await this.#dbConnect();
|
||||
}
|
||||
|
||||
return this.db;
|
||||
}
|
||||
|
||||
#dbConnect() {
|
||||
const that = this;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = indexedDB.open(this.dbName, 1);
|
||||
|
||||
request.onsuccess = (e) => {
|
||||
resolve(e.target.result);
|
||||
};
|
||||
|
||||
request.onerror = (e) => {
|
||||
console.error(`indexedDB error: ${e.target.errorCode}`);
|
||||
};
|
||||
|
||||
request.onupgradeneeded = (e) => {
|
||||
const db = e.target.result;
|
||||
db.createObjectStore(that.storeName, {
|
||||
keyPath: "id",
|
||||
});
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
29
js/jenot.js
29
js/jenot.js
|
|
@ -1,9 +1,14 @@
|
|||
import "./service-worker.js";
|
||||
import { renderText } from "./dom.js";
|
||||
import { NoteStore } from "./store.js";
|
||||
import { DBNoteStore } from "./db-store.js";
|
||||
import "./components.js";
|
||||
|
||||
const Notes = new NoteStore("jenot-app");
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
|
||||
const Notes = urlParams.has("localStorage")
|
||||
? new NoteStore("jenot-app")
|
||||
: new DBNoteStore("jenot-app", "notes");
|
||||
|
||||
const newNote = document.querySelector("#new-note");
|
||||
const editNote = document.querySelector("#edit-note");
|
||||
|
|
@ -12,28 +17,27 @@ Notes.addEventListener("save", render.bind(this));
|
|||
|
||||
render();
|
||||
|
||||
newNote.addEventListener("addNote", (e) => {
|
||||
console.log(e.detail);
|
||||
Notes.add(e.detail);
|
||||
newNote.addEventListener("addNote", async (e) => {
|
||||
await Notes.add(e.detail);
|
||||
Notes.saveStorage();
|
||||
});
|
||||
|
||||
editNote.addEventListener("updateNote", (e) => {
|
||||
editNote.addEventListener("updateNote", async (e) => {
|
||||
newNote.classList.remove("hidden");
|
||||
editNote.classList.add("hidden");
|
||||
Notes.update(e.detail);
|
||||
await Notes.update(e.detail);
|
||||
Notes.saveStorage();
|
||||
});
|
||||
|
||||
editNote.addEventListener("deleteNote", (e) => {
|
||||
editNote.addEventListener("deleteNote", async (e) => {
|
||||
newNote.classList.remove("hidden");
|
||||
editNote.classList.add("hidden");
|
||||
Notes.remove(e.detail);
|
||||
await Notes.remove(e.detail);
|
||||
Notes.saveStorage();
|
||||
});
|
||||
|
||||
function render() {
|
||||
const notes = Notes.all();
|
||||
async function render() {
|
||||
const notes = await Notes.all();
|
||||
const notesContainer = document.querySelector("#notes");
|
||||
notesContainer.replaceChildren();
|
||||
|
||||
|
|
@ -64,10 +68,11 @@ function render() {
|
|||
|
||||
notesContainer.appendChild(container);
|
||||
|
||||
container.addEventListener("click", (e) => {
|
||||
container.addEventListener("click", async (e) => {
|
||||
newNote.classList.add("hidden");
|
||||
editNote.classList.remove("hidden");
|
||||
editNote.load(Notes.get(container.id));
|
||||
const note = await Notes.get(container.id);
|
||||
editNote.load(note);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
13
js/store.js
13
js/store.js
|
|
@ -1,7 +1,6 @@
|
|||
export class NoteStore extends EventTarget {
|
||||
localStorageKey;
|
||||
notes = [];
|
||||
editedNoteId = "none";
|
||||
|
||||
/*
|
||||
Note structure:
|
||||
|
|
@ -31,7 +30,6 @@ export class NoteStore extends EventTarget {
|
|||
|
||||
all = () => this.notes;
|
||||
get = (id) => this.notes.find((note) => note.id === id);
|
||||
getEditedNoteId = () => this.editedNoteId;
|
||||
|
||||
add(note) {
|
||||
this.notes.unshift({
|
||||
|
|
@ -54,19 +52,11 @@ export class NoteStore extends EventTarget {
|
|||
this.notes = this.notes.map((n) => (n.id === note.id ? note : n));
|
||||
}
|
||||
|
||||
setEditedNoteId(id) {
|
||||
this.editedNoteId = id;
|
||||
}
|
||||
|
||||
saveStorage() {
|
||||
window.localStorage.setItem(
|
||||
this.localStorageKey + "_notes",
|
||||
JSON.stringify(this.notes),
|
||||
);
|
||||
window.localStorage.setItem(
|
||||
this.localStorageKey + "_editedNoteId",
|
||||
this.editedNoteId,
|
||||
);
|
||||
this.dispatchEvent(new CustomEvent("save"));
|
||||
}
|
||||
|
||||
|
|
@ -74,8 +64,5 @@ export class NoteStore extends EventTarget {
|
|||
this.notes = JSON.parse(
|
||||
window.localStorage.getItem(this.localStorageKey + "_notes") || "[]",
|
||||
);
|
||||
this.editedNoteId =
|
||||
window.localStorage.getItem(this.localStorageKey + "_editedNoteId") ||
|
||||
"none";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue