Skip to content

Commit

Permalink
* Move to Typescript
Browse files Browse the repository at this point in the history
* Introduce a little data abstraction layer
  • Loading branch information
aboodman committed Feb 15, 2021
1 parent 58d5470 commit 6f49b51
Show file tree
Hide file tree
Showing 10 changed files with 196 additions and 81 deletions.
2 changes: 2 additions & 0 deletions next-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/// <reference types="next" />
/// <reference types="next/types/global" />
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@
"@babel/core": "^7.4.4",
"@babel/plugin-proposal-class-properties": "^7.4.4",
"@babel/plugin-proposal-export-default-from": "^7.2.0",
"babel-plugin-css-modules-transform": "^1.6.2"
"@types/react": "^17.0.2",
"babel-plugin-css-modules-transform": "^1.6.2",
"typescript": "^4.1.5"
},
"resolutions": {
"webpack": "^5.0.0-beta.30"
Expand Down
21 changes: 15 additions & 6 deletions pages/index.js → pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,34 @@
import { useEffect, useState } from 'react';
import Replicache from 'replicache';
import {Data} from '../src/data';
import {Designer2} from '../src/Designer2';

export default function Home() {
const [rep, setRep] = useState(null);
const [data, setData] = useState(null);

// TODO: Think through React under SSR.
// TODO: Think through Replicache + SSR.
useEffect(() => {
if (rep) {
if (data) {
return;
}
// TODO: Use clientID from Replicache:
// https://github.com/rocicorp/replicache-sdk-js/issues/275
let clientID = localStorage.clientID;
if (!clientID) {
clientID = localStorage.clientID = Math.random().toString(36).substring(2);
}

const isProd = location.host.indexOf('.vercel.app') > -1;
setRep(new Replicache({
const rep = new Replicache({
clientViewURL: new URL('/api/replicache-client-view', location.href).toString(),
diffServerURL: isProd ? 'https://serve.replicache.dev/pull' : 'http://localhost:7001/pull',
diffServerAuth: isProd ? '1000000' : 'sandbox',
wasmModule: '/replicache/replicache.dev.wasm',
syncInterval: 5000,
}));
});

setData(new Data(rep));
});

return rep && <Designer2 rep={rep}/>;
return data && <Designer2 {...{data}}/>;
}
11 changes: 4 additions & 7 deletions src/Designer2.js → src/Designer2.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import React from 'react';
import {useSubscribe} from 'replicache-react-util';
import {Rect2} from './objects/Rect2';
import {Data} from './data';

export function Designer2({rep}) {
const keys = useSubscribe(rep,
async tx => (await tx.scanAll({prefix: '/object/'})).map(([k]) => k), []);

export function Designer2({data}: {data: Data}) {
const ids = data.useShapeIDs();
return <svg style={styles} width={350} height={400}>
{keys.map(rk => <Rect2 rep={rep} key={rk} rk={rk}/>)}
{ids.map(id => <Rect2 key={id} {...{data, id}}/>)}
</svg>;
}

Expand All @@ -18,5 +16,4 @@ const styles = {
+ 'PSIyMCIgZmlsbD0iI2ZmZiI+PC9yZWN0Pgo8cmVjdCB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIGZpbGw9I'
+ 'iNGN0Y3RjciPjwvcmVjdD4KPHJlY3QgeD0iMTAiIHk9IjEwIiB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIG'
+ 'ZpbGw9IiNGN0Y3RjciPjwvcmVjdD4KPC9zdmc+)',
backgroundSize: "auto"
};
47 changes: 47 additions & 0 deletions src/data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import Replicache, {JSONObject, ReadTransaction, WriteTransaction} from 'replicache';
import {useSubscribe} from 'replicache-react-util';

export interface Shape {
x: number,
y: number,
}

export class Data {
private rep: Replicache;

constructor(rep: Replicache) {
this.rep = rep;

// TODO: Is there a way to consolidate the type information and re-use what is declared below?
this.moveShape = rep.register('moveShape', async (tx: WriteTransaction, args: {id: string, dx: number, dy: number}) => {
const {id, dx, dy} = args;
const shape = await this.getShape(tx, id);
shape.x += dx;
shape.y += dy;
await this.putShape(tx, id, shape);
});
}

readonly moveShape: (args: {id: string, dx: number, dy: number}) => Promise<void>;

useShapeIDs(): Array<string> {
return useSubscribe(this.rep, async (tx: ReadTransaction) => {
const shapes = await tx.scanAll({prefix:'/object/'});
return shapes.map(([k, _]) => k.split('/')[2]);
}, []);
}

useShapeByID(id: string): Shape|null {
return useSubscribe(this.rep, (tx: ReadTransaction) => {
return this.getShape(tx, id);
}, null);
}

private async getShape(tx: ReadTransaction, id: string): Promise<Shape> {
// TODO: Is there an automated way to check that the returned value implements Shape?
return await tx.get(`/object/${id}`) as unknown as Shape;
}
private async putShape(tx: WriteTransaction, id: string, shape: Shape) {
return await tx.put(`/object/${id}`, shape as unknown as JSONObject) as unknown as Shape;
}
}
3 changes: 3 additions & 0 deletions src/declarations.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// TODO: Why isn't Typescript picking up the delcaration in
// replicache-react-util?
declare module 'replicache-react-util';
8 changes: 0 additions & 8 deletions src/objects/Rect2.js

This file was deleted.

11 changes: 11 additions & 0 deletions src/objects/Rect2.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';
import {Data} from '../data';
import {getObjectAttributes} from './attribs';

export function Rect2({data, id}: {data: Data, id: string}) {
const shape = data.useShapeByID(id);
if (!shape) {
return null;
}
return <rect {...getObjectAttributes(shape)} />;
}
29 changes: 29 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve"
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx"
],
"exclude": [
"node_modules"
]
}
Loading

0 comments on commit 6f49b51

Please sign in to comment.