diff --git a/.github/workflows/deploy-reflect.yml b/.github/workflows/deploy-reflect.yml new file mode 100644 index 0000000..58cfe39 --- /dev/null +++ b/.github/workflows/deploy-reflect.yml @@ -0,0 +1,35 @@ +name: Deploy Reflect to Production + +on: + push: + branches: + - main + pull_request: + branches: [main] + +env: + NODE_VERSION: "18.x" + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + cache: "npm" + + - name: npm install + run: | + npm install + + - name: publish + env: + REFLECT_AUTH_KEY: ${{ secrets.REFLECT_AUTH_KEY }} + run: | + npx reflect publish --server-path="./reflect/orchestrator/server.ts" --reflect-channel=canary --app=loop-orchestrator-${GITHUB_HEAD_REF//[^a-zA-Z0-9]/-} --auth-key-from-env=REFLECT_AUTH_KEY + npx reflect publish --server-path="./reflect/share/server.ts" --reflect-channel=canary --app=loop-share-${GITHUB_HEAD_REF//[^a-zA-Z0-9]/-} --auth-key-from-env=REFLECT_AUTH_KEY + npx reflect publish --server-path="./reflect/play/server.ts" --reflect-channel=canary --app=loop-play-${GITHUB_HEAD_REF//[^a-zA-Z0-9]/-} --auth-key-from-env=REFLECT_AUTH_KEY diff --git a/README.md b/README.md index 6c1b6eb..1edcedf 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,5 @@ We will improve Reflect's APIs over time to not require separate apps for this t # Publish -1. Remove the `apps` key from each of the `reflect.config.json` files -2. Publish each app to reflect with `npx reflect publish` -3. Publish the frontend to some host, i.e., Vercel and set `NEXT_PUBLIC_ORCHESTRATOR_SERVER`, `NEXT_PUBLIC_PLAY_SERVER`, and `NEXT_PUBLIC_SHARE_SERVER` accordingly. +1. Publish each app to reflect with `npx reflect publish` +2. Publish the frontend to some host, i.e., Vercel and set `NEXT_PUBLIC_ORCHESTRATOR_SERVER`, `NEXT_PUBLIC_PLAY_SERVER`, and `NEXT_PUBLIC_SHARE_SERVER` accordingly. diff --git a/frontend/App.tsx b/frontend/App.tsx index 7fb19d6..f69e1b1 100644 --- a/frontend/App.tsx +++ b/frontend/App.tsx @@ -19,13 +19,13 @@ import ShareModal from "./ShareModal"; import { useElementSize, useWindowSize } from "./sizeHooks"; import { event } from "nextjs-google-analytics"; import styles from "./App.module.css"; +import { getReflectServer } from "./host"; -const orchestratorServer = - process.env.NEXT_PUBLIC_ORCHESTRATOR_SERVER ?? "http://127.0.0.1:8080/"; -const playServer = - process.env.NEXT_PUBLIC_PLAY_SERVER ?? "http://127.0.0.1:8080/"; -const shareServer = - process.env.NEXT_PUBLIC_SHARE_SERVER ?? "http://127.0.0.1:8080/"; +const orchestratorServer = getReflectServer( + process.env.NEXT_PUBLIC_ORCHESTRATOR_SERVER +); +const playServer = getReflectServer(process.env.NEXT_PUBLIC_PLAY_SERVER); +const shareServer = getReflectServer(process.env.NEXT_PUBLIC_SHARE_SERVER); type RoomAssignment = { roomID: string; color: string }; @@ -206,11 +206,11 @@ const animateMessage = (messageDiv: HTMLDivElement | null) => { const animateButton = (elementId: string) => { const element = document.getElementById(elementId); if (element) { - element.classList.add('animated-button'); + element.classList.add("animated-button"); - setTimeout(() => { - element.classList.remove('animated-button'); - }, 600); + setTimeout(() => { + element.classList.remove("animated-button"); + }, 600); } }; diff --git a/frontend/CursorField.tsx b/frontend/CursorField.tsx index 3816a9c..1e136a5 100644 --- a/frontend/CursorField.tsx +++ b/frontend/CursorField.tsx @@ -60,7 +60,7 @@ export default function CursorField({ @@ -88,7 +88,7 @@ function Cursor({ return (
) : ( - + ) )}
diff --git a/frontend/host.ts b/frontend/host.ts new file mode 100644 index 0000000..55b6f37 --- /dev/null +++ b/frontend/host.ts @@ -0,0 +1,15 @@ +export function getReflectServer(template: string | undefined) { + if (!template) { + throw new Error("Environment variable is required"); + } + return applyTemplate(template); +} + +function applyTemplate(template: string) { + const f = new Function( + "NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF", + `return \`${template}\`` + ); + const branchName = process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF ?? ""; + return f(branchName.replace(/[^a-zA-Z0-9]/g, "-")); +} diff --git a/package-lock.json b/package-lock.json index 0c70ee4..5d4dd08 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,8 @@ "version": "0.0.0", "dependencies": { "@badrap/valita": "^0.3.0", - "@rocicorp/rails": "^0.7.1", - "@rocicorp/reflect": "^0.38.202312080119", + "@rocicorp/rails": "^0.10.0", + "@rocicorp/reflect": "^0.39.202402011004", "@vercel/og": "^0.5.20", "classnames": "^2.3.2", "nanoid": "^5.0.3", @@ -474,31 +474,21 @@ "integrity": "sha512-/co4DJq3opjULOHe7hMAho/E2WJuv6rMOJ1QDlCKsPHZ09XkkKHDM8dPu+odmDHWBb5aMnnzC92WQLd05CTxnA==" }, "node_modules/@rocicorp/rails": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@rocicorp/rails/-/rails-0.7.1.tgz", - "integrity": "sha512-mCSx80zR4vRU3abh4ZDEDN8Nk2pjGo9Sa0z3NA45YBCq+xd+h4hr2kP1scAlATaHJW59WrzKOBq36odYS10D+Q==", - "peerDependencies": { - "react": ">=16.0 <19.0", - "react-dom": ">=16.0 <19.0", - "replicache": ">=10.0 <14.0 || >12.0.0-beta <12.0.0 || >13.0.0-beta <13.0.0" - }, - "peerDependenciesMeta": { - "replicache": { - "optional": true - } - } + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@rocicorp/rails/-/rails-0.10.0.tgz", + "integrity": "sha512-PfMf4z6H8lqQlvCWg8b+HCZKOQKrSQNXoU4br2LFQzBQILiV0cWdBQP7WgTt/LYbM+9EzfWtN581SiSY5sNI3w==" }, "node_modules/@rocicorp/reflect": { - "version": "0.38.202312080119", - "resolved": "https://registry.npmjs.org/@rocicorp/reflect/-/reflect-0.38.202312080119.tgz", - "integrity": "sha512-A/TBJx1d21bGJDGRxZkhUDQr7BQAEGDNMAULtD0+IAWOodF+zdijm8tu/NzhAuLpIoR+0rw8pS3bjcP59p410Q==", + "version": "0.39.202402011004", + "resolved": "https://registry.npmjs.org/@rocicorp/reflect/-/reflect-0.39.202402011004.tgz", + "integrity": "sha512-/wEx4WCDQqOxwseSYdr1AemQlGclw76Y3sZQlqHAqdDa1rU4+/InBtZ6CzSBQuCmPwsmkjecyzd6K7ktFJhmLQ==", "dependencies": { "@badrap/valita": "^0.3.0", "@rocicorp/lock": "^1.0.3", "@rocicorp/logger": "^5.2.1", "@rocicorp/resolver": "^1.0.1", "esbuild": "^0.19.4", - "miniflare": "^3.20231030.2" + "miniflare": "^3.20231030.4" }, "bin": { "reflect": "cli.js" diff --git a/package.json b/package.json index a4b2755..086c871 100644 --- a/package.json +++ b/package.json @@ -13,8 +13,8 @@ }, "dependencies": { "@badrap/valita": "^0.3.0", - "@rocicorp/rails": "^0.7.1", - "@rocicorp/reflect": "^0.38.202312080119", + "@rocicorp/rails": "^0.10.0", + "@rocicorp/reflect": "^0.39.202402011004", "@vercel/og": "^0.5.20", "classnames": "^2.3.2", "nanoid": "^5.0.3", diff --git a/reflect/model/cell.ts b/reflect/model/cell.ts index 1687d7d..e715f4d 100644 --- a/reflect/model/cell.ts +++ b/reflect/model/cell.ts @@ -59,7 +59,7 @@ async function setCellEnabled( await cellGenerated.delete(tx, id); } } - const client = await getClient(tx, tx.clientID); + const client = await getClient(tx); await cellGenerated.put(tx, { id, color: client?.color ?? colorIDFromID(tx.clientID), diff --git a/reflect/model/client.ts b/reflect/model/client.ts index bfc23df..cd4319d 100644 --- a/reflect/model/client.ts +++ b/reflect/model/client.ts @@ -1,5 +1,5 @@ import * as v from "@badrap/valita"; -import { Update, generate } from "@rocicorp/rails"; +import { Update, generatePresence } from "@rocicorp/rails"; import { WriteTransaction } from "@rocicorp/reflect"; const cursorSchema = v.object({ x: v.number(), y: v.number() }); @@ -8,7 +8,7 @@ const locationSchema = v.object({ country: v.string(), }); const clientModelSchema = v.object({ - id: v.string(), + clientID: v.string(), color: v.string(), cursor: cursorSchema.optional(), location: locationSchema.optional(), @@ -19,7 +19,7 @@ export type Cursor = v.Infer; export type Location = v.Infer; export type Client = v.Infer; export type ClientUpdate = Update; -const clientGenerated = generate( +const clientGenerated = generatePresence( "client", clientModelSchema.parse.bind(clientModelSchema) ); @@ -30,21 +30,17 @@ const initClient = async ( tx: WriteTransaction, { color }: { color: string } ) => { - const id = tx.clientID; const client = { - id, color, }; - await clientGenerated.put(tx, client); + await clientGenerated.set(tx, client); }; const updateLocation = async (tx: WriteTransaction, location: Location) => { if (!allowLocation(location)) { return; } - const id = tx.clientID; const client = { - id, location, }; await clientGenerated.update(tx, client); @@ -60,14 +56,12 @@ function allowLocation(location: Location): boolean { const updateCursor = async (tx: WriteTransaction, cursor: Cursor) => { await clientGenerated.update(tx, { - id: tx.clientID, cursor, }); }; const markAsTouchClient = async (tx: WriteTransaction) => { await clientGenerated.update(tx, { - id: tx.clientID, isTouch: true, }); }; diff --git a/reflect/orchestrator/reflect.config.json b/reflect/orchestrator/reflect.config.json deleted file mode 100644 index bc08d4e..0000000 --- a/reflect/orchestrator/reflect.config.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "server": "server.ts", - "apps": { - "default": { - "appID": "lqhif59k" - } - } -} \ No newline at end of file diff --git a/reflect/play/reflect.config.json b/reflect/play/reflect.config.json deleted file mode 100644 index dbba421..0000000 --- a/reflect/play/reflect.config.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "server": "server.ts", - "apps": { - "default": { - "appID": "loxvdsq5" - } - } -} \ No newline at end of file diff --git a/reflect/share/reflect.config.json b/reflect/share/reflect.config.json deleted file mode 100644 index 68e0576..0000000 --- a/reflect/share/reflect.config.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "server": "server.ts", - "apps": { - "default": { - "appID": "loxv5ldt" - } - } -} \ No newline at end of file diff --git a/reflect/subscriptions.ts b/reflect/subscriptions.ts index bff52d7..cdad279 100644 --- a/reflect/subscriptions.ts +++ b/reflect/subscriptions.ts @@ -5,11 +5,7 @@ import { PLAY_M } from "./play/mutators"; import { SHARE_M } from "./share/mutators"; export function useSelfColor(r: Reflect | undefined) { - return useSubscribe( - r, - async (tx) => (await getClient(tx, tx.clientID))?.color, - null - ); + return useSubscribe(r, async (tx) => (await getClient(tx))?.color, null); } export function usePresentClients( @@ -21,7 +17,7 @@ export function usePresentClients( async (tx) => { const presentClients = []; for (const clientID of presentClientIDs) { - const client = await getClient(tx, clientID); + const client = await getClient(tx, { clientID }); if (client) { presentClients.push(client); }