Skip to content

Commit

Permalink
refactor: integrate @rocicorp/reflect and @rocicorp/reflect-server fr…
Browse files Browse the repository at this point in the history
…om npm into replidraw-do (#46)

1. Update to the new Reflect API that is independent from the Replicache API. 
2. Under the hood this is using the head of Replicache, so we are updating from 9.0.0-beta.0 to 10.0.0-alpaha.2.
3. Since Reflect does not expose an onSync callback (since it is constantly streaming), updates initShape to use a mutator that is a no-op on the client and does the init only once on the server.
  • Loading branch information
grgbkr authored Apr 9, 2022
1 parent 41d6a65 commit 852f5f7
Show file tree
Hide file tree
Showing 16 changed files with 183 additions and 1,680 deletions.
1,580 changes: 44 additions & 1,536 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 3 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
},
"dependencies": {
"@cloudflare/workers-types": "^3.3.1",
"@rocicorp/reflect": "^0.4.0",
"@rocicorp/reflect-server": "^0.10.0",
"@types/node": "^17.0.16",
"@types/react": "^17.0.39",
"@types/react-dom": "^17.0.11",
Expand All @@ -27,10 +29,7 @@
"react-dom": "^17.0.2",
"react-draggable": "^4.4.4",
"react-hotkeys": "^1.1.4",
"reflect": "file:reflect-0.8.0.tgz",
"reflect-client": "file:reflect-client-0.3.1.tgz",
"replicache": "9.0.0-beta.0",
"replicache-react": "^2.5.0",
"replicache-react": "^2.6.0",
"typescript": "^4.5.5",
"zod": "^3.11.6"
}
Expand Down
Binary file removed reflect-0.8.0.tgz
Binary file not shown.
Binary file removed reflect-client-0.3.1.tgz
Binary file not shown.
2 changes: 1 addition & 1 deletion src/datamodel/client-state.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ReadTransaction, WriteTransaction } from "replicache";
import type { ReadTransaction, WriteTransaction } from "@rocicorp/reflect";
import { randInt } from "../util/rand";

const colors = [
Expand Down
11 changes: 8 additions & 3 deletions src/datamodel/mutators.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { WriteTransaction } from "replicache";
import type { WriteTransaction } from "@rocicorp/reflect";
import {
initClientState,
setCursor,
Expand All @@ -15,9 +15,9 @@ import {
initShapes,
} from "./shape";

export type M = typeof mutators;
export type M = typeof serverMutators;

export const mutators = {
export const serverMutators = {
createShape: putShape,
deleteShape,
moveShape,
Expand All @@ -31,3 +31,8 @@ export const mutators = {
initShapes,
nop: async (_: WriteTransaction) => {},
};

export const clientMutators: M = {
...serverMutators,
initShapes: async () => {},
};
8 changes: 3 additions & 5 deletions src/datamodel/shape.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ReadTransaction, WriteTransaction } from "replicache";
import type { ReadTransaction, WriteTransaction } from "@rocicorp/reflect";
import { nanoid } from "nanoid";
import { z } from "zod";
import { randInt } from "../util/rand";
Expand Down Expand Up @@ -97,13 +97,11 @@ export async function rotateShape(
}
}

export async function initShapes(
tx: WriteTransaction,
shapes: { id: string; shape: Shape }[]
) {
export async function initShapes(tx: WriteTransaction) {
if (await tx.has("initialized")) {
return;
}
const shapes = Array.from({ length: 5 }, () => randomShape());
await Promise.all([
tx.put("initialized", true),
...shapes.map((s) => putShape(tx, s)),
Expand Down
54 changes: 27 additions & 27 deletions src/datamodel/subscriptions.ts
Original file line number Diff line number Diff line change
@@ -1,69 +1,72 @@
import type { Replicache } from "replicache";
import type { Reflect } from "@rocicorp/reflect";
import { useSubscribe } from "replicache-react";
import { getClientState, clientStatePrefix } from "./client-state";
import { getShape, shapePrefix } from "./shape";
import type { mutators } from "./mutators";
import type { M } from "./mutators";

export function useShapeIDs(rep: Replicache<typeof mutators>) {
export function useShapeIDs(reflect: Reflect<M>) {
return useSubscribe(
rep,
reflect,
async (tx) => {
const shapes = await tx.scan({ prefix: shapePrefix }).keys().toArray();
return shapes.map((k) => k.substr(shapePrefix.length));
const shapes = (await tx
.scan({ prefix: shapePrefix })
.keys()
.toArray()) as string[];

This comment has been minimized.

Copy link
@arv

arv Apr 9, 2022

Contributor

This looks worrisome. Did I accidentally break this? Let me add a unit test to Replicache to ensure we get a string array here.

This comment has been minimized.

Copy link
@arv

arv Apr 9, 2022

Contributor
return shapes.map((k) => k.substring(shapePrefix.length));
},
[]
);
}

export function useShapeByID(rep: Replicache<typeof mutators>, id: string) {
export function useShapeByID(reflect: Reflect<M>, id: string) {
return useSubscribe(
rep,
reflect,
async (tx) => {
return await getShape(tx, id);
},
null
);
}

export function useUserInfo(rep: Replicache<typeof mutators>) {
export function useUserInfo(reflect: Reflect<M>) {
return useSubscribe(
rep,
reflect,
async (tx) => {
return (await getClientState(tx, await rep.clientID)).userInfo;
return (await getClientState(tx, await reflect.clientID)).userInfo;
},
null
);
}

export function useOverShapeID(rep: Replicache<typeof mutators>) {
export function useOverShapeID(reflect: Reflect<M>) {
return useSubscribe(
rep,
reflect,
async (tx) => {
return (await getClientState(tx, await rep.clientID)).overID;
return (await getClientState(tx, await reflect.clientID)).overID;
},
""
);
}

export function useSelectedShapeID(rep: Replicache<typeof mutators>) {
export function useSelectedShapeID(reflect: Reflect<M>) {
return useSubscribe(
rep,
reflect,
async (tx) => {
return (await getClientState(tx, await rep.clientID)).selectedID;
return (await getClientState(tx, await reflect.clientID)).selectedID;
},
""
);
}

export function useCollaboratorIDs(rep: Replicache<typeof mutators>) {
export function useCollaboratorIDs(reflect: Reflect<M>) {
return useSubscribe(
rep,
reflect,
async (tx) => {
const clientIDs = await tx
const clientIDs = (await tx
.scan({ prefix: clientStatePrefix })
.keys()
.toArray();
const myClientID = await rep.clientID;
.toArray()) as string[];

This comment has been minimized.

Copy link
@arv

arv Apr 9, 2022

Contributor

remove cast

const myClientID = await reflect.clientID;
return clientIDs
.filter((k) => !k.endsWith(myClientID))
.map((k) => k.substr(clientStatePrefix.length));
Expand All @@ -72,12 +75,9 @@ export function useCollaboratorIDs(rep: Replicache<typeof mutators>) {
);
}

export function useClientInfo(
rep: Replicache<typeof mutators>,
clientID: string
) {
export function useClientInfo(reflect: Reflect<M>, clientID: string) {
return useSubscribe(
rep,
reflect,
async (tx) => {
return await getClientState(tx, clientID);
},
Expand Down
10 changes: 5 additions & 5 deletions src/frontend/collaborator.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useEffect, useState } from "react";
import styles from "./collaborator.module.css";
import type { Replicache } from "replicache";
import { Rect } from "./rect";
import type { M } from "../datamodel/mutators";
import { useClientInfo } from "../datamodel/subscriptions";
import type { Reflect } from "@rocicorp/reflect";

const hideCollaboratorDelay = 5000;

Expand All @@ -16,13 +16,13 @@ interface Position {
}

export function Collaborator({
rep,
reflect,
clientID,
}: {
rep: Replicache<M>;
reflect: Reflect<M>;
clientID: string;
}) {
const clientInfo = useClientInfo(rep, clientID);
const clientInfo = useClientInfo(reflect, clientID);
const [lastPos, setLastPos] = useState<Position | null>(null);
const [gotFirstChange, setGotFirstChange] = useState(false);
const [, setPoke] = useState({});
Expand Down Expand Up @@ -77,7 +77,7 @@ export function Collaborator({
{clientInfo.selectedID && (
<Rect
{...{
rep,
reflect,
key: `selection-${clientInfo.selectedID}`,
id: clientInfo.selectedID,
highlight: true,
Expand Down
36 changes: 19 additions & 17 deletions src/frontend/designer.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Replicache } from "replicache";
import type { Reflect } from "@rocicorp/reflect";
import React, { useRef, useState } from "react";
import { HotKeys } from "react-hotkeys";
import { DraggableCore } from "react-draggable";
Expand All @@ -15,24 +15,26 @@ import {
} from "../datamodel/subscriptions";
import type { M } from "../datamodel/mutators";

export function Designer({ rep }: { rep: Replicache<M> }) {
const ids = useShapeIDs(rep);
const overID = useOverShapeID(rep);
const selectedID = useSelectedShapeID(rep);
const collaboratorIDs = useCollaboratorIDs(rep);
export function Designer({ reflect }: { reflect: Reflect<M> }) {
const ids = useShapeIDs(reflect);
const overID = useOverShapeID(reflect);
const selectedID = useSelectedShapeID(reflect);
const collaboratorIDs = useCollaboratorIDs(reflect);

const ref = useRef<HTMLDivElement | null>(null);
const [dragging, setDragging] = useState(false);

const handlers = {
moveLeft: () => rep.mutate.moveShape({ id: selectedID, dx: -20, dy: 0 }),
moveRight: () => rep.mutate.moveShape({ id: selectedID, dx: 20, dy: 0 }),
moveUp: () => rep.mutate.moveShape({ id: selectedID, dx: 0, dy: -20 }),
moveDown: () => rep.mutate.moveShape({ id: selectedID, dx: 0, dy: 20 }),
moveLeft: () =>
reflect.mutate.moveShape({ id: selectedID, dx: -20, dy: 0 }),
moveRight: () =>
reflect.mutate.moveShape({ id: selectedID, dx: 20, dy: 0 }),
moveUp: () => reflect.mutate.moveShape({ id: selectedID, dx: 0, dy: -20 }),
moveDown: () => reflect.mutate.moveShape({ id: selectedID, dx: 0, dy: 20 }),
deleteShape: () => {
// Prevent navigating backward on some browsers.
event?.preventDefault();
rep.mutate.deleteShape(selectedID);
reflect.mutate.deleteShape(selectedID);
},
};

Expand All @@ -44,8 +46,8 @@ export function Designer({ rep }: { rep: Replicache<M> }) {
pageY: number;
}) => {
if (ref && ref.current) {
rep.mutate.setCursor({
id: await rep.clientID,
reflect.mutate.setCursor({
id: await reflect.clientID,
x: pageX,
y: pageY - ref.current.offsetTop,
});
Expand Down Expand Up @@ -82,7 +84,7 @@ export function Designer({ rep }: { rep: Replicache<M> }) {
<RectController
{...{
key: `shape-${id}`,
rep,
reflect,
id,
}}
/>
Expand All @@ -94,7 +96,7 @@ export function Designer({ rep }: { rep: Replicache<M> }) {
<Rect
{...{
key: `highlight-${overID}`,
rep,
reflect,
id: overID,
highlight: true,
}}
Expand All @@ -108,7 +110,7 @@ export function Designer({ rep }: { rep: Replicache<M> }) {
<Selection
{...{
key: `selection-${selectedID}`,
rep,
reflect,
id: selectedID,
highlight: true,
containerOffsetTop: ref.current && ref.current.offsetTop,
Expand All @@ -126,7 +128,7 @@ export function Designer({ rep }: { rep: Replicache<M> }) {
<Collaborator
{...{
key: `key-${id}`,
rep,
reflect,
clientID: id,
}}
/>
Expand Down
8 changes: 4 additions & 4 deletions src/frontend/nav.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect, useRef, useState } from "react";
import type { Replicache } from "replicache";
import type { Reflect } from "@rocicorp/reflect";
import Modal from "react-bootstrap/Modal";
import Button from "react-bootstrap/Button";
import Form from "react-bootstrap/Form";
Expand All @@ -8,11 +8,11 @@ import { randomShape } from "../datamodel/shape";
import { useUserInfo } from "../datamodel/subscriptions";
import type { M } from "../datamodel/mutators";

export function Nav({ rep }: { rep: Replicache<M> }) {
export function Nav({ reflect }: { reflect: Reflect<M> }) {
const [aboutVisible, showAbout] = useState(false);
const [shareVisible, showShare] = useState(false);
const urlBox = useRef<HTMLInputElement>(null);
const userInfo = useUserInfo(rep);
const userInfo = useUserInfo(reflect);

useEffect(() => {
if (shareVisible) {
Expand All @@ -21,7 +21,7 @@ export function Nav({ rep }: { rep: Replicache<M> }) {
});

const onRectangle = () => {
rep.mutate.createShape(randomShape());
reflect.mutate.createShape(randomShape());
};

return (
Expand Down
Loading

1 comment on commit 852f5f7

@vercel
Copy link

@vercel vercel bot commented on 852f5f7 Apr 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

replidraw-do – ./

replidraw-do.vercel.app
replidraw-do-rocicorp.vercel.app
replidraw-do-git-main-rocicorp.vercel.app

Please sign in to comment.