Compare commits
No commits in common. "1a8ebaf88d30014f323b36d89077d0ca48f0f2e4" and "6ac181fca7aca4ed0c4ab708ace43425fb710178" have entirely different histories.
1a8ebaf88d
...
6ac181fca7
|
@ -214,7 +214,7 @@ function App({ ctx }) {
|
||||||
<>
|
<>
|
||||||
<section className="pt-8 container">
|
<section className="pt-8 container">
|
||||||
<Input type="text"
|
<Input type="text"
|
||||||
className="bg-background"
|
className="bg-background p-4"
|
||||||
placeholder="..."
|
placeholder="..."
|
||||||
value={newText}
|
value={newText}
|
||||||
onKeyUp={parseTodos}
|
onKeyUp={parseTodos}
|
||||||
|
|
|
@ -7,53 +7,16 @@ export interface InputProps
|
||||||
|
|
||||||
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||||
({ className, type, ...props }, ref) => {
|
({ className, type, ...props }, ref) => {
|
||||||
const [selectedIndex, setSelectedIndex] = React.useState(0)
|
|
||||||
const options = ["add", "done", "delete"]
|
|
||||||
|
|
||||||
const handleWheel = (event: React.WheelEvent) => {
|
|
||||||
event.preventDefault()
|
|
||||||
if (event.deltaY > 0) {
|
|
||||||
// Scroll down
|
|
||||||
setSelectedIndex((prev) => (prev + 1) % options.length)
|
|
||||||
} else {
|
|
||||||
// Scroll up
|
|
||||||
setSelectedIndex((prev) => (prev - 1 + options.length) % options.length)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative"
|
|
||||||
onWheel={handleWheel}
|
|
||||||
>
|
|
||||||
<select
|
|
||||||
className={cn(
|
|
||||||
"absolute top-0 left-0 h-9 rounded-l-md border-r border-input bg-transparent px-2 text-sm cursor-default",
|
|
||||||
"shadow-sm transition-all duration-200 text-center appearance-none",
|
|
||||||
)}
|
|
||||||
value={options[selectedIndex]}
|
|
||||||
onChange={(e) => {
|
|
||||||
setSelectedIndex(options.indexOf(e.target.value))
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<option value="" disabled selected>
|
|
||||||
Actions
|
|
||||||
</option>
|
|
||||||
{options.map((option) => (
|
|
||||||
<option key={option} value={option}>
|
|
||||||
{option}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
<input
|
<input
|
||||||
type={type}
|
type={type}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-9 w-full rounded-md border border-input bg-background px-[90px] text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,15 +1,19 @@
|
||||||
|
import { StrictMode } from 'react'
|
||||||
import { createRoot } from 'react-dom/client'
|
import { createRoot } from 'react-dom/client'
|
||||||
import './index.css'
|
import './index.css'
|
||||||
import App from "./App.tsx";
|
import App from "./App.tsx";
|
||||||
|
import Root from "./Root.tsx";
|
||||||
|
import { start } from "@vlcn.io/ws-client/worker.js";
|
||||||
|
import { createDbProvider } from "@vlcn.io/ws-browserdb";
|
||||||
|
|
||||||
import initWasm from '@vlcn.io/crsqlite-wasm';
|
import initWasm from '@vlcn.io/crsqlite-wasm';
|
||||||
import wasmUrl from '@vlcn.io/crsqlite-wasm/crsqlite.wasm?url';
|
import wasmUrl from '@vlcn.io/crsqlite-wasm/crsqlite.wasm?url';
|
||||||
import tblrx from '@vlcn.io/rx-tbl';
|
import tblrx from '@vlcn.io/rx-tbl';
|
||||||
|
|
||||||
const sqlite = await initWasm(() => wasmUrl);
|
async function initializeDatabase() {
|
||||||
|
const sqlite = await initWasm(() => wasmUrl);
|
||||||
const db = await sqlite.open("todo.db");
|
const db = await sqlite.open("todo.db");
|
||||||
await db.exec(`
|
await db.exec(`
|
||||||
CREATE TABLE IF NOT EXISTS todos (
|
CREATE TABLE IF NOT EXISTS todos (
|
||||||
id BLOB PRIMARY KEY NOT NULL,
|
id BLOB PRIMARY KEY NOT NULL,
|
||||||
description TEXT,
|
description TEXT,
|
||||||
|
@ -22,10 +26,65 @@ await db.exec(`
|
||||||
completed INTEGER NOT NULL DEFAULT 0
|
completed INTEGER NOT NULL DEFAULT 0
|
||||||
);
|
);
|
||||||
SELECT crsql_as_crr('todos');
|
SELECT crsql_as_crr('todos');
|
||||||
`);
|
`);
|
||||||
|
db.onUpdate(async () => {
|
||||||
|
const changes = await db.execA(`SELECT * FROM crsql_changes WHERE db_version > 0 AND site_id = crsql_site_id()`);
|
||||||
|
|
||||||
const rx = tblrx(db);
|
sendChangesToServer(changes);
|
||||||
const ctx = { db, rx };
|
});
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
dbProvider: createDbProvider(),
|
||||||
|
transportProvider: (dbName) => new WebSocket(`ws://localhost:8080/sync/${dbName}`),
|
||||||
|
};
|
||||||
|
|
||||||
|
start(config);
|
||||||
|
const ws = new WebSocket('ws://localhost:8080/sync');
|
||||||
|
|
||||||
|
ws.onmessage = async function(event) {
|
||||||
|
const { type, data } = JSON.parse(event.data);
|
||||||
|
if (type === 'changes') {
|
||||||
|
await applyChanges(data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function sendChangesToServer(changes) {
|
||||||
|
const formattedChanges = changes.map(change => ({
|
||||||
|
TableName: change[0],
|
||||||
|
PK: Array.isArray(change[1]) ? btoa(String.fromCharCode.apply(null, change[1])) : btoa(change[1]),
|
||||||
|
ColumnName: change[2],
|
||||||
|
Value: Array.isArray(change[3]) ? btoa(String.fromCharCode.apply(null, change[3])) : 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) {
|
||||||
|
const db = initializeDatabase();
|
||||||
|
const rx = tblrx(db);
|
||||||
|
const ctx = { db, rx };
|
||||||
|
await db.tx(async (tx) => {
|
||||||
|
for (const change of changes) {
|
||||||
|
await tx.exec(
|
||||||
|
`INSERT INTO crsql_changes VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
|
change
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById("root")!.classList.add("dark");
|
document.getElementById("root")!.classList.add("dark");
|
||||||
|
|
||||||
|
@ -34,4 +93,3 @@ createRoot(document.getElementById("root") as HTMLElement).render(
|
||||||
<App ctx={ctx} />
|
<App ctx={ctx} />
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue