Implement a wrapper around IndexedDB

This commit is contained in:
Jeremy Dormitzer 2018-02-18 09:13:08 -05:00
parent f745cb22d1
commit c4c50565b6
No known key found for this signature in database
GPG Key ID: 04F17C0F5A32C320

View File

@ -0,0 +1,143 @@
(ns looped-in.indexeddb
(:require [cljs.core.async :refer [go chan >!]]
[looped-in.logging :as log]))
(defn add-data
"Adds data to IndexedDB"
[db object-store data]
(let [channel (chan)
transaction (.transaction db (clj->js [object-store]) "readwrite")
store (.objectStore transaction object-store)
data-coll (if (coll? data) data [data])]
(doseq [datum data-coll]
(.add store datum))
(set! (.-onerror transaction)
(fn [event] (log/error "Error adding data to IndexedDB: "
(.-errorCode (.-target event)))))
(set! (.-oncomplete transaction)
(fn [event] (go (>! channel (.-result (.-target event))))))
channel))
(defn get-data
"Gets data from IndexedDB."
[db object-store index]
(let [channel (chan)
request (-> db
(.transaction (clj->js [object-store]))
(.objectStore objectStore)
(.get index))]
(set! (.-onerror request)
(fn [event] (log/error "Error getting data from IndexedDB: "
(.-errorCode (.-target event)))))
(set! (.-onsuccess request)
(fn [event] (go (>! channel (.-result (.-target event))))))
channel))
(defn update-data
"Updates data in IndexedDB.
`updated-values` is map of key-value pairs to update. It does not need to contain
every field in the object schema, just those to update."
[db object-store index updated-values]
(let [channel (chan)
store (-> db
(.transaction (clj->js [object-store]) "readwrite")
(.objectStore object-store))
get-request (.get store index)]
(set! (.-onerror get-request)
(fn [event] (log/error "Error getting data from IndexedDB: "
(.-errorCode (.-target event)))))
(set! (.-onsuccess get-request)
(fn [event]
(let [data (js->clj (.-result (.-target event)) :keywordize-keys true)
update-request (.put store (clj->js (merge data updated-values)))]
(set! (.-onerror update-request)
(fn [event] (log/error "Error updating data in IndexedDB: "
(.-errorCode (.-target event)))))
(set! (.-onsuccess update-request)
(fn [event] (go (>! channel (.-result (.-target event)))))))))
channel))
(defn delete-data
"Deletes data from IndexedDB"
[db object-store index]
(let [channel (chan)
request (-> db
(.transaction (clj->js [object-store]) "readwrite")
(.objectStore object-store)
(.delete index))]
(set! (.onerror request)
(fn [event] (log/error "Error deleting data from IndexedDB: "
(.-errorCode (.-target event)))))
(set! (.onsuccess request)
(fn [event] (go (>! channel (.-result (.-target event))))))
channel))
(defn open-database
"Opens an IndexedDB database.
The database should have already been created."
([name] (open-database name 1))
([name version]
(let [channel (chan)
request (.open js/window.indexedDB name version)]
(set! (.-onerror request)
(fn [event] (log/error "Error opening IndexedDB: "
(.-errorCode (.-target event)))))
(set! (.-onsuccess request)
(fn [event] (go (>! channel (.-result (.-target event))))))
channel)))
(defn create-database
"Creates an IndexedDB database and returns a channel that resolves with a database instance.
If the database has already been created with the specified version,
just opens the existing one. `object-stores` is a sequence of object
store maps created via `looped-in.indexeddb/create-object-store`."
([name object-stores]
(create-database name 1 object-stores))
([name version object-stores]
(let [channel (chan)
request (.open js/window.indexedDB name version)]
(set! (.-onerror request)
(fn [event]
(log/error "Error opening IndexedDB: "
(.-errorCode (.-target event)))))
(set! (.-onupgradeneeded request)
(fn [event]
(let [db (.-result (.-target event))]
(doseq [object-store object-stores]
(let [store (.createObjectStore db
(:name object-store)
(clj->js
{:key-path
(:key-path object-store)
:auto-increment
(:auto-increment? object-store)}))]
(doseq [index (:indices object-store)]
(.createIndex store
(:name index)
(:key-path index)
(clj->js (:opts index)))))))))
(set! (.-onsuccess request)
(go (>! channel (.-result (.-target event)))))
channel)))
(defn create-object-store
"Creates an object store data structure.
`indices` is a sequence of index maps
created via `looped-in.indexeddb/create-index`."
([store-name key-path]
(create-object-store store-name key-path []))
([store-name key-path indices]
(create-object-store store-name key-path indices false))
([store-name key-path indices auto-increment?]
{:name store-name :key-path key-path :indices indices :auto-increment? auto-increment?}))
(defn create-index
"Creates an index for use with `looped-in.indexeddb/create-object-store`.
`opts` is a map of options. Valid keys are :unique and :multi-entry, as
documented at https://developer.mozilla.org/en-US/docs/Web/API/IDBObjectStore/createIndex."
([name]
(create-index name name))
([name key-path]
(create-index name key-path {}))
([name key-path opts]
{:name name :key-path key-path :opts opts}))