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);
}