mast/js/src/App.svelte
2024-08-05 23:19:11 -07:00

142 lines
3.4 KiB
Svelte

<script lang="ts">
import { onMount } from 'svelte';
import initWasm from '@vlcn.io/crsqlite-wasm';
import wasmUrl from '@vlcn.io/crsqlite-wasm/crsqlite.wasm?url';
import { start } from "@vlcn.io/ws-client/worker.js";
import { createDbProvider } from "@vlcn.io/ws-browserdb";
let db;
let ws;
let todos = [];
let newTodo = '';
onMount(async () => {
const sqlite = await initWasm(() => wasmUrl);
db = await sqlite.open('todo.db');
await db.exec(`
DROP TABLE todos;
`);
await db.exec(`
CREATE TABLE IF NOT EXISTS todos (
id BLOB PRIMARY KEY NOT NULL,
content TEXT,
completed INTEGER NOT NULL DEFAULT 0
);
SELECT crsql_as_crr('todos');
`);
const config = {
dbProvider: createDbProvider(),
transportProvider: (dbName) => new WebSocket(`ws://localhost:8080/sync/${dbName}`),
};
start(config);
ws = new WebSocket('ws://localhost:8080/sync');
db.onUpdate(async () => {
const changes = await db.execA(`SELECT * FROM crsql_changes WHERE db_version > 0 AND site_id = crsql_site_id()`);
sendChangesToServer(changes);
});
ws.onmessage = async function(event) {
const { type, data } = JSON.parse(event.data);
if (type === 'changes') {
await applyChanges(data);
}
};
loadTodos();
});
function sendChangesToServer(changes) {
const formattedChanges = changes.map(change => ({
TableName: change[0],
PK: Array.isArray(change[1]) ? btoa(change[1]) : btoa(change[1]),
ColumnName: change[2],
Value: Array.isArray(change[3]) ? btoa(String.fromCharCode.apply(null, change[3])) : btoa(change[3]),
ColVersion: Number(change[4]),
DBVersion: Number(change[5]),
SiteID: Array.isArray(change[6]) ? btoa(new TextDecoder('utf8').decode(change[6])) : btoa(change[6]),
CL: Number(change[7]),
Seq: Number(change[8])
}));
console.log(formattedChanges)
const message = {
type: "changes",
data: formattedChanges
};
ws.send(JSON.stringify(message));
}
async function applyChanges(changes) {
await db.tx(async (tx) => {
for (const change of changes) {
await tx.exec(
`INSERT INTO crsql_changes VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
change
);
}
});
}
async function loadTodos() {
const result = await db.execO("SELECT * FROM todos ORDER BY id");
todos = result;
}
async function addTodo() {
if (newTodo.trim()) {
await db.exec(`
INSERT INTO todos (id, content, completed)
VALUES (lower(hex(randomblob(16))), ?, 0)
`, [newTodo.trim()]);
newTodo = '';
loadTodos();
}
}
async function toggleTodo(id) {
await db.exec(`
UPDATE todos
SET completed = NOT completed
WHERE id = ?
`, [id]);
loadTodos();
}
</script>
<main>
<h1>Svelte Todo with cr-sqlite</h1>
<input
bind:value={newTodo}
on:keypress={(e) => e.key === 'Enter' && addTodo()}
placeholder="Add a new todo"
/>
<button on:click={addTodo}>Add Todo</button>
<ul>
{#each todos as todo}
<li>
<input
type="checkbox"
checked={todo.completed}
on:change={() => toggleTodo(todo.id)}
/>
<span class:completed={todo.completed}>{todo.content}</span>
</li>
{/each}
</ul>
</main>
<style>
.completed {
text-decoration: line-through;
}
</style>