Skip to content

Commit

Permalink
Move code into new repository
Browse files Browse the repository at this point in the history
  • Loading branch information
third774 committed Mar 25, 2024
0 parents commit 702255a
Show file tree
Hide file tree
Showing 152 changed files with 38,309 additions and 0 deletions.
16 changes: 16 additions & 0 deletions .eslintrc.js
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: '^_',
},
],
},
}
11 changes: 11 additions & 0 deletions .gitignore
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
15 changes: 15 additions & 0 deletions .hintrc
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"
}
}
10 changes: 10 additions & 0 deletions .husky/pre-commit
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
7 changes: 7 additions & 0 deletions .prettierrc
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"
}
7 changes: 7 additions & 0 deletions .vscode/extensions.json
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": []
}
5 changes: 5 additions & 0 deletions .vscode/settings.json
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"
}
40 changes: 40 additions & 0 deletions README.md
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
```
89 changes: 89 additions & 0 deletions app/api/roomsApi.server.ts
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 })
}
}
89 changes: 89 additions & 0 deletions app/components/AlertDialog.tsx
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 }
32 changes: 32 additions & 0 deletions app/components/AudioGlow.tsx
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>
)
}
35 changes: 35 additions & 0 deletions app/components/AudioIndicator.tsx
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>
)
}
Loading

0 comments on commit 702255a

Please sign in to comment.