Skip to content

Commit def6e01

Browse files
committed
WIP
1 parent daa8767 commit def6e01

5 files changed

Lines changed: 61 additions & 19 deletions

File tree

src/Avalonia.Samples/CompleteApps/AdvancedToDoList/AdvancedToDoList.Browser/Services/BrowserDatabaseService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public partial class BrowserDbService : IDbService
1010
public string GetDatabasePath()
1111
{
1212
// This path must match the DB_FILENAME in storage.js
13-
return "/todo.db";
13+
return "todo.db";
1414
}
1515

1616
public async Task SaveAsync()

src/Avalonia.Samples/CompleteApps/AdvancedToDoList/AdvancedToDoList.Browser/wwwroot/main.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { dotnet } from './_framework/dotnet.js'
2-
import {loadSQLite} from "./storage.js";
2+
import {loadSQLite, idbGet, IDB_KEY} from "./storage.js";
33

44
const is_browser = typeof window != "undefined";
55
if (!is_browser) throw new Error(`Expected to be running in a browser`);
@@ -11,6 +11,26 @@ const dotnetRuntime = await dotnet
1111
.withApplicationArgumentsFromQuery()
1212
.create();
1313

14+
// Expose runtime for storage synchronization
15+
window.dotnetRuntime = dotnetRuntime;
16+
17+
// Restore database into .NET VFS if it exists in IndexedDB
18+
try {
19+
const savedBytes = await idbGet(IDB_KEY);
20+
if (savedBytes && savedBytes.byteLength > 0) {
21+
console.log(`Restoring database to .NET VFS (${savedBytes.byteLength} bytes)...`);
22+
// Try different ways to access FS as it might depend on the runtime version
23+
const FS = (typeof dotnetRuntime.getModule === 'function' ? dotnetRuntime.getModule().FS : dotnetRuntime.Module?.FS) || dotnetRuntime.FS;
24+
if (!FS) throw new Error("Could not find Emscripten FS in dotnetRuntime");
25+
FS.writeFile('todo.db', savedBytes);
26+
console.log('Database restored to .NET VFS.');
27+
} else {
28+
console.log('No saved database found in IndexedDB to restore to .NET VFS.');
29+
}
30+
} catch (err) {
31+
console.error('Failed to restore database to .NET VFS:', err);
32+
}
33+
1434
const config = dotnetRuntime.getConfig();
1535

1636
await dotnetRuntime.runMain(config.mainAssemblyName, [globalThis.location.href]);

src/Avalonia.Samples/CompleteApps/AdvancedToDoList/AdvancedToDoList.Browser/wwwroot/sqlite3-worker1.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,21 +46,26 @@
4646
if (data && data.type === 'upload') {
4747
const args = data.args;
4848
try {
49+
console.log('Worker: Handling upload for', args.filename, 'data size:', args.deserialize?.byteLength);
4950
// Use the internal capi to create the file in MEMFS
5051
sqlite3.capi.sqlite3_js_posix_create_file(args.filename, args.deserialize);
52+
console.log('Worker: Upload successful for', args.filename);
5153
globalThis.postMessage({
5254
type: 'upload',
5355
messageId: data.messageId,
5456
result: { filename: args.filename }
5557
});
5658
} catch (e) {
59+
console.error('Worker: Upload failed for', args.filename, 'Error:', e.message);
5760
globalThis.postMessage({
5861
type: 'error',
5962
messageId: data.messageId,
60-
error: e.message
63+
error: e.message,
64+
stack: e.stack
6165
});
6266
}
6367
} else {
68+
// console.log('Worker: Delegating message to original handler:', data.type);
6469
return oldOnMessage(ev);
6570
}
6671
};

src/Avalonia.Samples/CompleteApps/AdvancedToDoList/AdvancedToDoList.Browser/wwwroot/storage.js

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ globalThis.saveDatabase = async () => {
1818
};
1919

2020
// IndexedDB helpers
21-
const IDB_DB = 'avalonia-sqlite3';
22-
const IDB_STORE = 'dbfiles';
23-
const IDB_KEY = 'todo.db';
21+
export const IDB_DB = 'avalonia-sqlite3';
22+
export const IDB_STORE = 'dbfiles';
23+
export const IDB_KEY = 'todo.db';
2424

25-
function idbOpen() {
25+
export function idbOpen() {
2626
return new Promise((resolve, reject) => {
2727
const req = indexedDB.open(IDB_DB, 1);
2828
req.onupgradeneeded = () => req.result.createObjectStore(IDB_STORE);
@@ -31,7 +31,7 @@ function idbOpen() {
3131
});
3232
}
3333

34-
async function idbPut(key, uint8) {
34+
export async function idbPut(key, uint8) {
3535
const db = await idbOpen();
3636
return new Promise((resolve, reject) => {
3737
const tx = db.transaction(IDB_STORE, 'readwrite');
@@ -44,7 +44,7 @@ async function idbPut(key, uint8) {
4444
});
4545
}
4646

47-
async function idbGet(key) {
47+
export async function idbGet(key) {
4848
const db = await idbOpen();
4949
return new Promise((resolve, reject) => {
5050
const tx = db.transaction(IDB_STORE, 'readonly');
@@ -112,36 +112,55 @@ export async function loadSQLite() {
112112
// Restore saved DB (if any) into worker FS
113113
try {
114114
const saved = await idbGet(IDB_KEY);
115-
const DB_FILENAME = '/todo.db';
115+
const DB_FILENAME = 'todo.db';
116116

117117
if (saved && saved.byteLength > 0) {
118118
console.log('Found saved DB in IndexedDB (' + saved.byteLength + ' bytes) — restoring into worker...');
119119
// We use our custom 'upload' message to populate the worker's MEMFS
120-
await callWorker({
120+
const uploadResp = await callWorker({
121121
type: 'upload',
122122
args: { filename: DB_FILENAME, deserialize: saved }
123123
});
124-
console.log('DB bytes uploaded to worker VFS.');
124+
console.log('DB bytes uploaded to worker VFS:', uploadResp);
125125
} else {
126126
console.log('No prior DB found in IndexedDB — worker will start with an empty DB.');
127127
}
128128

129129
// send 'open' message to worker
130+
console.log('Sending open message for:', DB_FILENAME);
130131
const resp = await callWorker({ type: 'open', args: { filename: DB_FILENAME } });
131132
if (resp && resp.type === 'open') {
132-
console.log('DB opened in worker VFS.');
133+
console.log('DB opened in worker VFS:', resp);
133134
} else {
134-
console.warn('Worker open response:', resp);
135+
console.warn('Worker open response (unexpected type):', resp);
135136
}
136137
} catch (e) {
137-
console.error('Error opening/restoring DB in worker:', e);
138+
console.error('Error opening/restoring DB in worker. Full error object:', JSON.stringify(e, null, 2));
138139
}
139140

140141
// Replace global saveDatabase with one that asks the worker to export the DB and then persists to IndexedDB
141142
globalThis.saveDatabase = async () => {
142143
try {
143-
const DB_FILENAME = '/todo.db';
144-
console.log('Saving DB: requesting export from worker...');
144+
const DB_FILENAME = 'todo.db';
145+
146+
// Priority: Try to save from the main thread's .NET VFS first
147+
const runtime = globalThis.window?.dotnetRuntime;
148+
const dotnetFS = runtime && ((typeof runtime.getModule === 'function' ? runtime.getModule().FS : runtime.Module?.FS) || runtime.FS);
149+
if (dotnetFS) {
150+
try {
151+
console.log('Saving DB: reading from .NET main thread VFS...');
152+
const data = dotnetFS.readFile(DB_FILENAME);
153+
if (data && data.byteLength > 0) {
154+
await idbPut(IDB_KEY, data);
155+
console.log('DB persisted from .NET VFS to IndexedDB (' + data.byteLength + ' bytes).');
156+
return;
157+
}
158+
} catch (vfsErr) {
159+
console.warn('Failed to read DB from .NET VFS (might not be created yet):', vfsErr);
160+
}
161+
}
162+
163+
console.log('Saving DB: fallback to requesting export from worker...');
145164
// Ask the worker to export the DB bytes back to us
146165
const resp = await callWorker({ type: 'export', args: { filename: DB_FILENAME } });
147166
// Expect the worker to respond with an object containing result.byteArray: Uint8Array

src/Avalonia.Samples/CompleteApps/AdvancedToDoList/AdvancedToDoList/Helper/DataBaseHelper.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,6 @@ Color TEXT NULL
9494
}
9595
}
9696

97-
await UpdateIndexedDbAsync();
98-
9997
// If we have a connection, the DbService is known to be created. Thus, we can safely surpress the null warning here.
10098
// For in memory DataSource, we cannot set the _init flag to true.
10199
_initialized = App.DbService!.GetDatabasePath() != ":memory:";

0 commit comments

Comments
 (0)