Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified .DS_Store
Binary file not shown.
110 changes: 29 additions & 81 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,93 +1,41 @@
# How to code a webapp with this skeleton
# PlaybackXR

## Initial setup
A browser based sandbox where players claim a plot on an isometric grid, attach a Luma capture, and walk around a shared town.
url: https://playbackxr.onrender.com/

All teammates will need (explained in weblab.is/hw0)
## What it does

- A bash console (on Mac or Linux, this is Terminal. On Windows, we recommend Git Bash)
- NodeJS version 18. If it is installed correctly, typing "node --version" should give v18.13.0 and "npm --version" should give 8.19.3, or higher.
- Visual Studio Code (or another code editor)
- the Prettier VSCode extension
1 Sign in with Google
2 Pick a map and see a grid of plots
3 Reserve an empty plot
4 Paste a Luma capture URL to fill the plot
5 Walk the town and view the placed splats
6 Select a filled plot and delete it to make the plot empty again

Also, all of you will need to go through the MongoDB Atlas setup (https://bit.ly/mongo-setup).
## Tech stack

Additionally for authentication, one of you will need to obtain a CLIENT_ID, instructions are at https://bit.ly/gauth-mit.
Frontend: React + Vite
Backend: Node.js + Express
Database: MongoDB Atlas
Auth: Google OAuth + server sessions
Splat viewing: Luma Web Library + three.js

## Downloading these files
## Demo flow for judges

First, you probably have a team repository somewhere (the link looks like: https://github.com/weblab-class/teammate1-teammate2-teammate3). You each should clone this (empty) repository by navigating to where you want your folder to be (**NOT in catbook**) and typing: git clone https://github.com/weblab-class/teammate1-teammate2-teammate3.git <-- with the correct link.
1 Open the deployed URL
2 Sign in
3 Go to Maps
4 Click an empty cell to reserve it
5 Paste a public Luma capture URL and click Attach
6 Click Walk to see the splat placed on the isometric grid
7 Click a filled cell in Maps and click Delete selected splat

Then, one of your team members will need to do the following:
## Local setup

First on GitHub, download the skeleton (this repository) as a zip file, by clicking Code -> Download as ZIP. (Do not clone it, since this will download extra files, like .git, which will lead to GitHub being confused).
Prereqs: Node 20, npm, MongoDB Atlas connection string, Google OAuth client id.

Then, drag over all of the files in this skeleton into your team's folder. **Make sure to also drag over the hidden files!** To see these hidden files, navigate to the skeleton in Finder/File Explorer and press command+shift+period (mac) or View > Show > Hidden items (windows).

The files/folders you must drag over are:

- .gitignore (hidden)
- .npmrc (hidden)
- .prettierrc (hidden)
- client (folder)
- package-lock.json
- package.json
- README.md
- server (folder)
- vite.config.js

Additionally, you must create a .env file in the root directory. See .env.example for an example of what this file should look like.

Then, in terminal, navigate to your teams folder and push all of the files to your team's GitHub repository as usual:

- git add -A
- git commit -m "Skeleton code"
- git push

Now the rest of your teammates can pull all these files with a 'git pull'!

Post on Piazza if you run into any issues

## What you need to change in the skeleton

- Change the Frontend CLIENT_ID (Skeleton.js) to your team's CLIENT_ID (obtain this at https://bit.ly/gauth-mit)
- Change the Server CLIENT_ID to the same CLIENT_ID (auth.js)
- Change the Database SRV (mongoConnectionURL) for Atlas (server.js). You got this in the MongoDB setup. remember to replace <password> and <dbname> (should be no < or > in your SRV) (From: https://bit.ly/mongo-setup)
- Change the Database Name for MongoDB to whatever you put in the SRV to replace <dbname> (server.js)
- (Optional) Add a favicon to your website at the path client/dist/favicon.ico
- (Optional) Update website title in client/dist/index.html
- (Optional) Update this README file ;)
- (Optional) Update the package.json file with your app name :) (line 2)

## How to run this skeleton

First, 'npm install'
Then open two separate terminals, and 'npm run dev' in the first, and 'npm start' in the second.
Then open http://localhost:5173

<!-- ## How to go from this skeleton to your actual app

Check out this [How to Get Started Guide](http://weblab.is/get-started) -->

## Socket stuff

Note: we'll be getting to this in lecture in week 2, so don't worry if you don't know it yet

- If you're not using realtime updating or don't need server->client communication, you can remove socket entirely! (server-socket.js, client-socket.js, and anything that imports them)
- If you are using sockets, consider what you want to do with the FIXME in server-socket.js

## Edit at your own risk

the following files students do not need to edit. feel free to read them if you would like.
1 Install

```bash
npm install
```
client/src/utilities.js
client/src/client-socket.js
server/validator.js
server/server-socket.js
.npmrc
.prettierrc
package-lock.json
vite.config.js
```

## Good luck on your project :)
Empty file removed client/dist/.gitkeep
Empty file.
Binary file added client/dist/assets/favicon-6ec0b96e.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3,751 changes: 3,751 additions & 0 deletions client/dist/assets/index-56928f4e.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions client/dist/assets/index-58bfc9a9.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions client/dist/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="shortcut icon" href="/assets/favicon-6ec0b96e.png" type="image/x-icon" />
<link rel="icon" href="/assets/favicon-6ec0b96e.png" type="image/x-icon" />
<title>PlaybackXR</title>
<script type="module" crossorigin src="/assets/index-56928f4e.js"></script>
<link rel="stylesheet" href="/assets/index-58bfc9a9.css">
</head>
<body>
<div id="root"></div>

</body>
</html>
Binary file added client/dist/placeholder.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified client/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions client/index.html
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="shortcut icon" href="favicon.png" type="image/x-icon" />
<link rel="icon" href="favicon.png" type="image/x-icon" />
<title>CHANGE ME!</title>
<title>PlaybackXR</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/index.jsx"></script>
</body>
</html>
</html>
Binary file added client/public/placeholder.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
97 changes: 97 additions & 0 deletions client/src/components/pages/Landing.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import React from "react";
import { useNavigate } from "react-router-dom";
import Login from "./Login";

export default function Landing() {
const navigate = useNavigate();
const YT_ID = "jV_am1GXbGM";

return (
<div
style={{
position: "relative",
width: "100%",
height: "100vh",
overflow: "hidden",
background: "#000",
}}
>
{/* YouTube background */}
<div style={{ position: "absolute", inset: 0, pointerEvents: "none" }}>
<iframe
title="bg"
src={
"https://www.youtube.com/embed/" +
YT_ID +
"?autoplay=1" +
"&mute=1" +
"&controls=0" +
"&rel=0" +
"&modestbranding=1" +
"&playsinline=1" +
"&loop=1" +
"&playlist=" +
YT_ID
}
allow="autoplay; encrypted-media; picture-in-picture"
style={{
position: "absolute",
top: "50%",
left: "50%",
width: "200vw",
height: "112.5vw",
minWidth: "177.78vh",
minHeight: "100vh",
transform: "translate(-50%, -50%)",
border: 0,
opacity: 0.55,
}}
/>
</div>

{/* Dark overlay */}
<div style={{ position: "absolute", inset: 0, background: "rgba(0,0,0,0.55)" }} />

{/* Center card */}
<div
style={{
position: "relative",
zIndex: 2,
height: "100%",
display: "flex",
alignItems: "center",
justifyContent: "center",
padding: 24,
}}
>
<div
style={{
width: "min(520px, 100%)",
border: "1px solid rgba(255,255,255,0.14)",
borderRadius: 18,
padding: 22,
backdropFilter: "blur(10px)",
background: "rgba(10,10,10,0.55)",
color: "#fff",
}}
>
<div style={{ fontSize: 14, opacity: 0.85, marginBottom: 6 }}>PlaybackXR</div>
<div style={{ fontSize: 26, fontWeight: 700, marginBottom: 10 }}>
Build an isometric splat town
</div>
<div style={{ fontSize: 14, lineHeight: 1.5, opacity: 0.85, marginBottom: 16 }}>
Pick a plot, attach a Luma capture link, then walk the town.
</div>

{/* Option A: render your existing Login component right here */}
<div style={{ display: "flex", justifyContent: "center", marginTop: 12 }}>
<Login onSuccess={() => navigate("/maps")} />
</div>

{/* If your Login component does not support onSuccess yet,
scroll down to Step 3 where I show how to add it. */}
</div>
</div>
</div>
);
}
41 changes: 41 additions & 0 deletions client/src/components/pages/Login.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React, { useState } from "react";
import { GoogleLogin } from "@react-oauth/google";

async function apiLoginWithGoogleCredential(credential) {
const res = await fetch("/api/login", {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ token: credential }),
});

const text = await res.text();
if (!res.ok) throw new Error(text);

return JSON.parse(text);
}

export default function Login({ onSuccess }) {
const [error, setError] = useState("");

return (
<div style={{ color: "#fff" }}>
{error ? <div style={{ color: "#ff7777", marginBottom: 10 }}>{error}</div> : null}

<GoogleLogin
onSuccess={async (cred) => {
try {
setError("");
await apiLoginWithGoogleCredential(cred.credential);
if (onSuccess) onSuccess(cred);
} catch (e) {
setError(String(e?.message || e));
}
}}
onError={() => {
setError("Google sign in failed");
}}
/>
</div>
);
}
Loading