mirror of
https://github.com/zoldar/jenot.git
synced 2026-01-05 07:02:55 +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.
|
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 reminders
|
||||||
- Implement masonry layout
|
- Implement masonry layout
|
||||||
- Implement color coding
|
- 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 "./service-worker.js";
|
||||||
import { renderText } from "./dom.js";
|
import { renderText } from "./dom.js";
|
||||||
import { NoteStore } from "./store.js";
|
import { NoteStore } from "./store.js";
|
||||||
|
import { DBNoteStore } from "./db-store.js";
|
||||||
import "./components.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 newNote = document.querySelector("#new-note");
|
||||||
const editNote = document.querySelector("#edit-note");
|
const editNote = document.querySelector("#edit-note");
|
||||||
|
|
@ -12,28 +17,27 @@ Notes.addEventListener("save", render.bind(this));
|
||||||
|
|
||||||
render();
|
render();
|
||||||
|
|
||||||
newNote.addEventListener("addNote", (e) => {
|
newNote.addEventListener("addNote", async (e) => {
|
||||||
console.log(e.detail);
|
await Notes.add(e.detail);
|
||||||
Notes.add(e.detail);
|
|
||||||
Notes.saveStorage();
|
Notes.saveStorage();
|
||||||
});
|
});
|
||||||
|
|
||||||
editNote.addEventListener("updateNote", (e) => {
|
editNote.addEventListener("updateNote", async (e) => {
|
||||||
newNote.classList.remove("hidden");
|
newNote.classList.remove("hidden");
|
||||||
editNote.classList.add("hidden");
|
editNote.classList.add("hidden");
|
||||||
Notes.update(e.detail);
|
await Notes.update(e.detail);
|
||||||
Notes.saveStorage();
|
Notes.saveStorage();
|
||||||
});
|
});
|
||||||
|
|
||||||
editNote.addEventListener("deleteNote", (e) => {
|
editNote.addEventListener("deleteNote", async (e) => {
|
||||||
newNote.classList.remove("hidden");
|
newNote.classList.remove("hidden");
|
||||||
editNote.classList.add("hidden");
|
editNote.classList.add("hidden");
|
||||||
Notes.remove(e.detail);
|
await Notes.remove(e.detail);
|
||||||
Notes.saveStorage();
|
Notes.saveStorage();
|
||||||
});
|
});
|
||||||
|
|
||||||
function render() {
|
async function render() {
|
||||||
const notes = Notes.all();
|
const notes = await Notes.all();
|
||||||
const notesContainer = document.querySelector("#notes");
|
const notesContainer = document.querySelector("#notes");
|
||||||
notesContainer.replaceChildren();
|
notesContainer.replaceChildren();
|
||||||
|
|
||||||
|
|
@ -64,10 +68,11 @@ function render() {
|
||||||
|
|
||||||
notesContainer.appendChild(container);
|
notesContainer.appendChild(container);
|
||||||
|
|
||||||
container.addEventListener("click", (e) => {
|
container.addEventListener("click", async (e) => {
|
||||||
newNote.classList.add("hidden");
|
newNote.classList.add("hidden");
|
||||||
editNote.classList.remove("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 {
|
export class NoteStore extends EventTarget {
|
||||||
localStorageKey;
|
localStorageKey;
|
||||||
notes = [];
|
notes = [];
|
||||||
editedNoteId = "none";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Note structure:
|
Note structure:
|
||||||
|
|
@ -31,7 +30,6 @@ export class NoteStore extends EventTarget {
|
||||||
|
|
||||||
all = () => this.notes;
|
all = () => this.notes;
|
||||||
get = (id) => this.notes.find((note) => note.id === id);
|
get = (id) => this.notes.find((note) => note.id === id);
|
||||||
getEditedNoteId = () => this.editedNoteId;
|
|
||||||
|
|
||||||
add(note) {
|
add(note) {
|
||||||
this.notes.unshift({
|
this.notes.unshift({
|
||||||
|
|
@ -54,19 +52,11 @@ export class NoteStore extends EventTarget {
|
||||||
this.notes = this.notes.map((n) => (n.id === note.id ? note : n));
|
this.notes = this.notes.map((n) => (n.id === note.id ? note : n));
|
||||||
}
|
}
|
||||||
|
|
||||||
setEditedNoteId(id) {
|
|
||||||
this.editedNoteId = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
saveStorage() {
|
saveStorage() {
|
||||||
window.localStorage.setItem(
|
window.localStorage.setItem(
|
||||||
this.localStorageKey + "_notes",
|
this.localStorageKey + "_notes",
|
||||||
JSON.stringify(this.notes),
|
JSON.stringify(this.notes),
|
||||||
);
|
);
|
||||||
window.localStorage.setItem(
|
|
||||||
this.localStorageKey + "_editedNoteId",
|
|
||||||
this.editedNoteId,
|
|
||||||
);
|
|
||||||
this.dispatchEvent(new CustomEvent("save"));
|
this.dispatchEvent(new CustomEvent("save"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,8 +64,5 @@ export class NoteStore extends EventTarget {
|
||||||
this.notes = JSON.parse(
|
this.notes = JSON.parse(
|
||||||
window.localStorage.getItem(this.localStorageKey + "_notes") || "[]",
|
window.localStorage.getItem(this.localStorageKey + "_notes") || "[]",
|
||||||
);
|
);
|
||||||
this.editedNoteId =
|
|
||||||
window.localStorage.getItem(this.localStorageKey + "_editedNoteId") ||
|
|
||||||
"none";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue