-
-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathgallery_store.js
More file actions
148 lines (133 loc) · 4.94 KB
/
Copy pathgallery_store.js
File metadata and controls
148 lines (133 loc) · 4.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// IndexedDB-backed gallery storage for Flickgame projects.
(function () {
'use strict';
var DB_NAME = 'flickgame';
var DB_VERSION = 1;
var STORE_PROJECTS = 'projects';
var dbPromise = null;
function nowMs() { return Date.now(); }
function uuid() {
if (typeof crypto !== 'undefined' && crypto.randomUUID) return crypto.randomUUID();
// Fallback UUIDv4-ish
var s = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0;
var v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
return s;
}
function openDb() {
if (dbPromise) return dbPromise;
dbPromise = new Promise(function (resolve, reject) {
if (typeof indexedDB === 'undefined') {
reject(new Error('IndexedDB not available'));
return;
}
var req = indexedDB.open(DB_NAME, DB_VERSION);
req.onerror = function () { reject(req.error || new Error('Failed to open IndexedDB')); };
req.onupgradeneeded = function () {
var db = req.result;
if (!db.objectStoreNames.contains(STORE_PROJECTS)) {
var store = db.createObjectStore(STORE_PROJECTS, { keyPath: 'id' });
store.createIndex('updatedAt', 'updatedAt', { unique: false });
store.createIndex('createdAt', 'createdAt', { unique: false });
}
};
req.onsuccess = function () { resolve(req.result); };
});
return dbPromise;
}
function withStore(mode, fn) {
return openDb().then(function (db) {
return new Promise(function (resolve, reject) {
var tx = db.transaction(STORE_PROJECTS, mode);
var store = tx.objectStore(STORE_PROJECTS);
var result;
try { result = fn(store, tx); } catch (e) { reject(e); return; }
var txDone = false;
var txError = null;
tx.oncomplete = function () { txDone = true; maybeResolve(); };
tx.onerror = function () { reject(tx.error || new Error('IndexedDB transaction failed')); };
tx.onabort = function () { reject(tx.error || new Error('IndexedDB transaction aborted')); };
var resultPromise = Promise.resolve(result);
var resultDone = false;
var resultValue;
resultPromise.then(function (v) { resultDone = true; resultValue = v; maybeResolve(); }, function (e) {
// If the caller's request promise fails, surface that error (tx may still abort too).
txError = e || new Error('IndexedDB request failed');
reject(txError);
});
function maybeResolve() {
if (!txDone) return;
if (txError) return;
if (!resultDone) return;
resolve(resultValue);
}
});
});
}
function reqToPromise(req) {
return new Promise(function (resolve, reject) {
req.onsuccess = function () { resolve(req.result); };
req.onerror = function () { reject(req.error || new Error('IndexedDB request failed')); };
});
}
function listProjects() {
return openDb().then(function (db) {
return new Promise(function (resolve, reject) {
var tx = db.transaction(STORE_PROJECTS, 'readonly');
var store = tx.objectStore(STORE_PROJECTS);
var idx = store.index('updatedAt');
var out = [];
var cursorReq = idx.openCursor(null, 'prev');
cursorReq.onerror = function () { reject(cursorReq.error || new Error('Failed to iterate projects')); };
cursorReq.onsuccess = function () {
var cur = cursorReq.result;
if (!cur) { resolve(out); return; }
out.push(cur.value);
cur.continue();
};
});
});
}
function getProject(id) {
return withStore('readonly', function (store) {
return reqToPromise(store.get(id));
});
}
function putProject(project) {
var p = Object.assign({}, project);
if (!p.id) p.id = uuid();
if (!p.title) p.title = 'Untitled';
if (!p.createdAt) p.createdAt = nowMs();
p.updatedAt = nowMs();
return withStore('readwrite', function (store) {
return reqToPromise(store.put(p)).then(function () { return p; });
});
}
function deleteProject(id) {
return withStore('readwrite', function (store) {
return reqToPromise(store.delete(id)).then(function () { return true; });
});
}
function isEmpty() {
return openDb().then(function (db) {
return new Promise(function (resolve, reject) {
var tx = db.transaction(STORE_PROJECTS, 'readonly');
var store = tx.objectStore(STORE_PROJECTS);
var countReq = store.count();
countReq.onerror = function () { reject(countReq.error || new Error('Failed to count projects')); };
countReq.onsuccess = function () { resolve((countReq.result || 0) === 0); };
});
});
}
window.FlickGalleryStore = {
openDb: openDb,
listProjects: listProjects,
getProject: getProject,
putProject: putProject,
deleteProject: deleteProject,
isEmpty: isEmpty,
_uuid: uuid
};
})();