KUMOS/Client/Includes/FileCache.sbi
2026-05-02 15:49:06 +02:00

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