142 lines
3.4 KiB
Svelte
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>
|
|
|