166 lines
5.6 KiB
Plaintext
166 lines
5.6 KiB
Plaintext
Module FileCache
|
|
EnableExplicit
|
|
|
|
;- Constants
|
|
; Store names (also used in IDB::Init in General.sbi)
|
|
#Store_Content = "file_content"
|
|
#Store_Meta = "file_meta"
|
|
|
|
; Metadata JSON shape:
|
|
; { "path": "/home/alice/doc.txt", "dirty": false, "cached_at": 1234567890, "size": 42 }
|
|
|
|
;- Public procedures
|
|
|
|
; Store a file fetched from the server into the cache.
|
|
; Writes content and meta atomically in one transaction.
|
|
Procedure Cache(FileID.s, Path.s, Content.s, *Callback)
|
|
!var _cb = p_callback;
|
|
!var _id = v_fileid;
|
|
!var _path = v_path;
|
|
!var _content = v_content;
|
|
!var _db = window._kumos_idb;
|
|
!if (!_db) { if (_cb) _cb(0, 'IDB not open'); return; }
|
|
!
|
|
!var tx = _db.transaction(['file_content','file_meta'], 'readwrite');
|
|
!var contentStore = tx.objectStore('file_content');
|
|
!var metaStore = tx.objectStore('file_meta');
|
|
!
|
|
!var meta = JSON.stringify({
|
|
! path: _path,
|
|
! dirty: false,
|
|
! cached_at: Date.now(),
|
|
! size: _content.length
|
|
!});
|
|
!
|
|
!contentStore.put(_content, _id).onerror = function(e) { tx.abort(); };
|
|
!metaStore.put(meta, _id).onerror = function(e) { tx.abort(); };
|
|
!
|
|
!tx.oncomplete = function() { if (_cb) _cb(1, ''); };
|
|
!tx.onerror = function(e) { if (_cb) _cb(0, e.target.error ? e.target.error.message : 'cache failed'); };
|
|
!tx.onabort = function(e) { if (_cb) _cb(0, 'cache aborted'); };
|
|
EndProcedure
|
|
|
|
; Read cached content. Data = raw file content string.
|
|
; Data = "" if file is not in cache (Success is still #True).
|
|
Procedure Read_(FileID.s, *Callback)
|
|
IDB::Get(#Store_Content, FileID, *Callback)
|
|
EndProcedure
|
|
|
|
; Write updated content to the cache and mark the file dirty.
|
|
; Patches meta without replacing path or other fields.
|
|
Procedure Write(FileID.s, Content.s, *Callback)
|
|
!var _cb = p_callback;
|
|
!var _id = v_fileid;
|
|
!var _content = v_content;
|
|
!var _db = window._kumos_idb;
|
|
!if (!_db) { if (_cb) _cb(0, 'IDB not open'); return; }
|
|
!
|
|
!var tx = _db.transaction(['file_content','file_meta'], 'readwrite');
|
|
!var contentStore = tx.objectStore('file_content');
|
|
!var metaStore = tx.objectStore('file_meta');
|
|
!
|
|
!contentStore.put(_content, _id);
|
|
!
|
|
!var getReq = metaStore.get(_id);
|
|
!getReq.onsuccess = function(ev) {
|
|
! var meta = {};
|
|
! try { meta = JSON.parse(ev.target.result || '{}'); } catch(e) {}
|
|
! meta.dirty = true;
|
|
! meta.size = _content.length;
|
|
! meta.cached_at = Date.now();
|
|
! metaStore.put(JSON.stringify(meta), _id);
|
|
!};
|
|
!
|
|
!tx.oncomplete = function() { if (_cb) _cb(1, ''); };
|
|
!tx.onerror = function(e) { if (_cb) _cb(0, e.target.error ? e.target.error.message : 'write failed'); };
|
|
EndProcedure
|
|
|
|
; Get the metadata JSON string for a cached file.
|
|
Procedure GetMeta(FileID.s, *Callback)
|
|
IDB::Get(#Store_Meta, FileID, *Callback)
|
|
EndProcedure
|
|
|
|
; Mark a cached file as clean after a successful sync to the server.
|
|
Procedure MarkClean(FileID.s, *Callback)
|
|
!var _cb = p_callback;
|
|
!var _id = v_fileid;
|
|
!var _db = window._kumos_idb;
|
|
!if (!_db) { if (_cb) _cb(0, 'IDB not open'); return; }
|
|
!
|
|
!var tx = _db.transaction(['file_meta'], 'readwrite');
|
|
!var store = tx.objectStore('file_meta');
|
|
!var req = store.get(_id);
|
|
!req.onsuccess = function(ev) {
|
|
! var meta = {};
|
|
! try { meta = JSON.parse(ev.target.result || '{}'); } catch(e) {}
|
|
! meta.dirty = false;
|
|
! store.put(JSON.stringify(meta), _id);
|
|
!};
|
|
!
|
|
!tx.oncomplete = function() { if (_cb) _cb(1, ''); };
|
|
!tx.onerror = function(e) { if (_cb) _cb(0, e.target.error ? e.target.error.message : 'markclean failed'); };
|
|
EndProcedure
|
|
|
|
; Remove a file from the cache entirely (both content and meta).
|
|
Procedure Evict(FileID.s, *Callback)
|
|
!var _cb = p_callback;
|
|
!var _id = v_fileid;
|
|
!var _db = window._kumos_idb;
|
|
!if (!_db) { if (_cb) _cb(0, 'IDB not open'); return; }
|
|
!
|
|
!var tx = _db.transaction(['file_content','file_meta'], 'readwrite');
|
|
!tx.objectStore('file_content').delete(_id);
|
|
!tx.objectStore('file_meta').delete(_id);
|
|
!
|
|
!tx.oncomplete = function() { if (_cb) _cb(1, ''); };
|
|
!tx.onerror = function(e) { if (_cb) _cb(0, e.target.error ? e.target.error.message : 'evict failed'); };
|
|
EndProcedure
|
|
|
|
; Return all cached file IDs as a JSON array.
|
|
Procedure ListCached(*Callback)
|
|
IDB::GetAllKeys(#Store_Meta, *Callback)
|
|
EndProcedure
|
|
|
|
; Return only the dirty file IDs as a JSON array.
|
|
; These are files that have been written locally but not yet synced.
|
|
Procedure ListDirty(*Callback)
|
|
!var _cb = p_callback;
|
|
!var _db = window._kumos_idb;
|
|
!if (!_db) { if (_cb) _cb(0, 'IDB not open'); return; }
|
|
!
|
|
!var tx = _db.transaction(['file_meta'], 'readonly');
|
|
!var store = tx.objectStore('file_meta');
|
|
!var req = store.openCursor();
|
|
!var dirty = [];
|
|
!
|
|
!req.onsuccess = function(ev) {
|
|
! var cursor = ev.target.result;
|
|
! if (cursor) {
|
|
! try {
|
|
! var meta = JSON.parse(cursor.value);
|
|
! if (meta.dirty) dirty.push(String(cursor.key));
|
|
! } catch(e) {}
|
|
! cursor.continue();
|
|
! }
|
|
!};
|
|
!
|
|
!tx.oncomplete = function() { if (_cb) _cb(1, JSON.stringify(dirty)); };
|
|
!tx.onerror = function(e) { if (_cb) _cb(0, e.target.error ? e.target.error.message : 'listdirty failed'); };
|
|
EndProcedure
|
|
|
|
; Returns "1" if the file is in the cache, "0" if not.
|
|
Procedure Exists(FileID.s, *Callback)
|
|
IDB::Exists(#Store_Meta, FileID, *Callback)
|
|
EndProcedure
|
|
|
|
EndModule
|
|
|
|
; IDE Options = SpiderBasic 3.20 (Windows - x86)
|
|
; CursorPosition = 156
|
|
; Folding = Bw
|
|
; iOSAppOrientation = 0
|
|
; AndroidAppCode = 0
|
|
; AndroidAppOrientation = 0
|
|
; EnableXP
|
|
; DPIAware
|
|
; CompileSourceDirectory |