-
Notifications
You must be signed in to change notification settings - Fork 224
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 702255a
Showing
152 changed files
with
38,309 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
/** @type {import('eslint').Linter.Config} */ | ||
module.exports = { | ||
extends: ['@remix-run/eslint-config', '@remix-run/eslint-config/node'], | ||
rules: { | ||
'@typescript-eslint/no-extra-semi': ['off'], | ||
'@typescript-eslint/no-unused-vars': [ | ||
'warn', | ||
{ | ||
// vars: "all", | ||
varsIgnorePattern: '^_', | ||
// args: "after-used", | ||
argsIgnorePattern: '^_', | ||
}, | ||
], | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
node_modules | ||
.DS_STORE | ||
.wrangler | ||
|
||
/.cache | ||
/build | ||
/dist | ||
/public/build | ||
/.mf | ||
.env | ||
.dev.vars |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
"extends": [ | ||
"development" | ||
], | ||
"hints": { | ||
"no-inline-styles": "off", | ||
"axe/name-role-value": [ | ||
"default", | ||
{ | ||
"button-name": "off" | ||
} | ||
], | ||
"apple-touch-icons": "off" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
#!/usr/bin/env sh | ||
|
||
# This loads nvm.sh and sets the correct PATH before running hook | ||
export NVM_DIR="$HOME/.nvm" | ||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" | ||
|
||
. "$(dirname -- "$0")/_/husky.sh" | ||
|
||
npx git-format-staged -f 'prettier --ignore-unknown --stdin --stdin-filepath "{}"' . | ||
npm run typecheck |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"useTabs": true, | ||
"semi": false, | ||
"singleQuote": true, | ||
"plugins": ["prettier-plugin-organize-imports"], | ||
"trailingComma": "es5" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"recommendations": [ | ||
"dbaeumer.vscode-eslint", // https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint | ||
"esbenp.prettier-vscode" // https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode | ||
], | ||
"unwantedRecommendations": [] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"lit-plugin.disable": true, | ||
"editor.formatOnSave": true, | ||
"editor.defaultFormatter": "esbenp.prettier-vscode" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
# Welcome to Orange Meets | ||
|
||
## Variables | ||
|
||
Go to the [Cloudflare Calls dashboard](https://dash.cloudflare.com/?to=/:account/calls) and create an application. | ||
|
||
Put these variables into `.dev.vars` | ||
|
||
``` | ||
CALLS_APP_ID=<APP_ID_GOES_HERE> | ||
CALLS_APP_SECRET=<SECRET_GOES_HERE> | ||
``` | ||
|
||
## Development | ||
|
||
```sh | ||
npm run dev | ||
``` | ||
|
||
Open up [http://127.0.0.1:8787](http://127.0.0.1:8787) and you should be ready to go! | ||
|
||
## Deployment | ||
|
||
First you will need to create the feedback queue: | ||
|
||
```sh | ||
wrangler queues create orange-meets-feedback-queue | ||
``` | ||
|
||
Then you can run | ||
|
||
```sh | ||
npm run deploy | ||
``` | ||
|
||
You will also need to set the token as a secret by running: | ||
|
||
```sh | ||
wrangler secret put CALLS_APP_SECRET | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import type { AppLoadContext } from '@remix-run/cloudflare' | ||
|
||
export async function handleApiRequest( | ||
path: string[], | ||
request: Request, | ||
env: AppLoadContext | ||
) { | ||
// We've received at API request. Route the request based on the path. | ||
|
||
switch (path[0]) { | ||
case 'room': { | ||
// Request for `/api/room/...`. | ||
|
||
if (!path[1]) { | ||
// The request is for just "/api/room", with no ID. | ||
if (request.method == 'POST') { | ||
// POST to /api/room creates a private room. | ||
// | ||
// Incidentally, this code doesn't actually store anything. It just generates a valid | ||
// unique ID for this namespace. Each durable object namespace has its own ID space, but | ||
// IDs from one namespace are not valid for any other. | ||
// | ||
// The IDs returned by `newUniqueId()` are unguessable, so are a valid way to implement | ||
// "anyone with the link can access" sharing. Additionally, IDs generated this way have | ||
// a performance benefit over IDs generated from names: When a unique ID is generated, | ||
// the system knows it is unique without having to communicate with the rest of the | ||
// world -- i.e., there is no way that someone in the UK and someone in New Zealand | ||
// could coincidentally create the same ID at the same time, because unique IDs are, | ||
// well, unique! | ||
let id = env.rooms.newUniqueId() | ||
return new Response(id.toString(), { | ||
headers: { 'Access-Control-Allow-Origin': '*' }, | ||
}) | ||
} else { | ||
// If we wanted to support returning a list of public rooms, this might be a place to do | ||
// it. The list of room names might be a good thing to store in KV, though a singleton | ||
// Durable Object is also a possibility as long as the Cache API is used to cache reads. | ||
// (A caching layer would be needed because a single Durable Object is single-threaded, | ||
// so the amount of traffic it can handle is limited. Also, caching would improve latency | ||
// for users who don't happen to be located close to the singleton.) | ||
// | ||
// For this demo, though, we're not implementing a public room list, mainly because | ||
// inevitably some trolls would probably register a bunch of offensive room names. Sigh. | ||
return new Response('Method not allowed', { status: 405 }) | ||
} | ||
} | ||
|
||
// OK, the request is for `/api/room/<name>/...`. It's time to route to the Durable Object | ||
// for the specific room. | ||
let name = path[1] | ||
|
||
// Each Durable Object has a 256-bit unique ID. IDs can be derived from string names, or | ||
// chosen randomly by the system. | ||
let id | ||
if (name.match(/^[0-9a-f]{64}$/)) { | ||
// The name is 64 hex digits, so let's assume it actually just encodes an ID. We use this | ||
// for private rooms. `idFromString()` simply parses the text as a hex encoding of the raw | ||
// ID (and verifies that this is a valid ID for this namespace). | ||
id = env.rooms.idFromString(name) | ||
} else if (name.length <= 32) { | ||
// Treat as a string room name (limited to 32 characters). `idFromName()` consistently | ||
// derives an ID from a string. | ||
id = env.rooms.idFromName(name) | ||
} else { | ||
return new Response('Name too long', { status: 404 }) | ||
} | ||
|
||
// Get the Durable Object stub for this room! The stub is a client object that can be used | ||
// to send messages to the remote Durable Object instance. The stub is returned immediately; | ||
// there is no need to await it. This is important because you would not want to wait for | ||
// a network round trip before you could start sending requests. Since Durable Objects are | ||
// created on-demand when the ID is first used, there's nothing to wait for anyway; we know | ||
// an object will be available somewhere to receive our requests. | ||
let roomObject = env.rooms.get(id) | ||
|
||
// Compute a new URL with `/api/room/<name>` removed. We'll forward the rest of the path | ||
// to the Durable Object. | ||
let newUrl = new URL(request.url) | ||
newUrl.pathname = '/' + path.slice(2).join('/') | ||
|
||
// Send the request to the object. The `fetch()` method of a Durable Object stub has the | ||
// same signature as the global `fetch()` function, but the request is always sent to the | ||
// object, regardless of the request's URL. | ||
return roomObject.fetch(newUrl.toString(), request) | ||
} | ||
default: | ||
return new Response('Not found', { status: 404 }) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import * as AlertDialog from '@radix-ui/react-alert-dialog' | ||
import type { FC, ReactNode } from 'react' | ||
import { forwardRef } from 'react' | ||
import { cn } from '~/utils/style' | ||
|
||
export const Overlay = forwardRef< | ||
HTMLDivElement, | ||
AlertDialog.AlertDialogOverlayProps | ||
>(({ className, ...rest }, ref) => ( | ||
<AlertDialog.Overlay | ||
ref={ref} | ||
className={cn('fixed inset-0 bg-black opacity-40', className)} | ||
{...rest} | ||
/> | ||
)) | ||
|
||
Overlay.displayName = 'Overlay' | ||
|
||
export const Content = forwardRef< | ||
HTMLDivElement, | ||
AlertDialog.AlertDialogContentProps | ||
>(({ className, children, ...rest }, ref) => ( | ||
<AlertDialog.Content | ||
ref={ref} | ||
className={cn( | ||
'fixed', | ||
'rounded-lg', | ||
'top-1/2', | ||
'left-1/2', | ||
'-translate-x-1/2', | ||
'-translate-y-1/2', | ||
'min-w-[min(400px,95vw)]', | ||
'max-w-[95vw]', | ||
'max-h-[85vh]', | ||
'overflow-y-auto', | ||
'p-6', | ||
'bg-inherit', | ||
'shadow-xl', | ||
'dark:shadow-none' | ||
)} | ||
{...rest} | ||
> | ||
{children} | ||
</AlertDialog.Content> | ||
)) | ||
|
||
Content.displayName = 'Content' | ||
|
||
const Title = forwardRef<HTMLHeadingElement, AlertDialog.AlertDialogTitleProps>( | ||
({ className, ...rest }, ref) => ( | ||
<AlertDialog.Title | ||
ref={ref} | ||
className={cn( | ||
'text-zinc-800 dark:text-zinc-200 m-0 text-base font-medium', | ||
className | ||
)} | ||
{...rest} | ||
/> | ||
) | ||
) | ||
|
||
Title.displayName = 'Title' | ||
|
||
const Description = forwardRef< | ||
HTMLParagraphElement, | ||
AlertDialog.AlertDialogDescriptionProps | ||
>(({ className, ...rest }, ref) => ( | ||
<AlertDialog.Description | ||
ref={ref} | ||
className={cn( | ||
'text-zinc-500 dark:text-zinc-400 mt-4 mb-5 text-sm leading-normal', | ||
className | ||
)} | ||
{...rest} | ||
/> | ||
)) | ||
|
||
Description.displayName = 'Description' | ||
|
||
const Actions: FC<{ children: ReactNode; className?: string }> = ({ | ||
children, | ||
className, | ||
}) => { | ||
return ( | ||
<div className={cn('flex justify-end gap-4', className)}>{children}</div> | ||
) | ||
} | ||
|
||
export default { ...AlertDialog, Overlay, Content, Title, Description, Actions } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import type { FC, ReactNode } from 'react' | ||
import useAudioLevel from '~/hooks/useAudioLevel' | ||
import { cn } from '~/utils/style' | ||
|
||
interface AudioGlowProps { | ||
audioTrack?: MediaStreamTrack | ||
children?: ReactNode | ||
className?: string | ||
type: 'text' | 'box' | ||
} | ||
|
||
export const AudioGlow: FC<AudioGlowProps> = ({ | ||
audioTrack, | ||
children, | ||
className, | ||
type, | ||
}) => { | ||
const audioLevel = useAudioLevel(audioTrack) | ||
return ( | ||
<span | ||
className={cn( | ||
type === 'text' ? 'orange-glow-text' : 'orange-glow-box', | ||
'opacity-[--opacity] transition-opacity', | ||
className | ||
)} | ||
style={{ '--opacity': Math.min(1, audioLevel * 4) } as any} | ||
aria-hidden | ||
> | ||
{children} | ||
</span> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import type { FC } from 'react' | ||
import useAudioLevel from '~/hooks/useAudioLevel' | ||
|
||
interface AudioIndicatorProps { | ||
audioTrack: MediaStreamTrack | ||
className?: string | ||
} | ||
|
||
export const AudioIndicator: FC<AudioIndicatorProps> = ({ audioTrack }) => { | ||
const audioLevel = useAudioLevel(audioTrack) | ||
const minSize = 0.6 | ||
const scaleModifier = 0.8 | ||
return ( | ||
<div className="relative"> | ||
<div | ||
className={'h-4 w-4 rounded-full bg-orange-400 scale-[--scale]'} | ||
style={ | ||
{ | ||
'--scale': Math.max(minSize, audioLevel + scaleModifier), | ||
} as any | ||
} | ||
></div> | ||
<div | ||
className={ | ||
'h-2 w-2 absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-orange-200 scale-[--scale]' | ||
} | ||
style={ | ||
{ | ||
'--scale': Math.max(minSize, audioLevel + scaleModifier), | ||
} as any | ||
} | ||
></div> | ||
</div> | ||
) | ||
} |
Oops, something went wrong.