Skip to content

Commit

Permalink
Merge pull request #113 from cloudflare/quick-quality-metrics
Browse files Browse the repository at this point in the history
Add quick quality metrics stored in D1
  • Loading branch information
third774 authored Sep 18, 2024
2 parents f83cb24 + a06d7b6 commit 8b3a855
Show file tree
Hide file tree
Showing 13 changed files with 1,502 additions and 121 deletions.
10 changes: 7 additions & 3 deletions app/components/LeaveRoomButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,20 @@ import { Button } from './Button'
import { Icon } from './Icon/Icon'
import { Tooltip } from './Tooltip'

interface LeaveRoomButtonProps {}
interface LeaveRoomButtonProps {
navigateToFeedbackPage: boolean
}

export const LeaveRoomButton: FC<LeaveRoomButtonProps> = () => {
export const LeaveRoomButton: FC<LeaveRoomButtonProps> = ({
navigateToFeedbackPage,
}) => {
const navigate = useNavigate()
return (
<Tooltip content="Leave">
<Button
displayType="danger"
onClick={() => {
navigate('/')
navigate(navigateToFeedbackPage ? '/call-quality-feedback' : '/')
}}
>
<VisuallyHidden>Leave</VisuallyHidden>
Expand Down
24 changes: 22 additions & 2 deletions app/routes/_room.$roomName._index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,29 @@ import { useRoomContext } from '~/hooks/useRoomContext'
import { errorMessageMap } from '~/hooks/useUserMedia'
import getUsername from '~/utils/getUsername.server'

export const loader = async ({ request }: LoaderFunctionArgs) => {
export const loader = async ({ request, context }: LoaderFunctionArgs) => {
const username = await getUsername(request)
invariant(username)
return json({ username })
return json({ username, callsAppId: context.env.CALLS_APP_ID })
}

let refreshCheckDone = false
function trackRefreshes() {
if (refreshCheckDone) return
if (typeof document === 'undefined') return

const key = `previously loaded`
const initialValue = sessionStorage.getItem(key)
const refreshed = initialValue !== null
sessionStorage.setItem(key, Date.now().toString())

if (refreshed) {
fetch(`/api/reportRefresh`, {
method: 'POST',
})
}

refreshCheckDone = true
}

export default function Lobby() {
Expand All @@ -33,6 +52,7 @@ export default function Lobby() {
const { videoStreamTrack, audioStreamTrack, audioEnabled } = userMedia
const session = useSubscribedState(peer.session$)
const sessionError = useSubscribedState(peer.sessionError$)
trackRefreshes()

const joinedUsers = new Set(
room.otherUsers.filter((u) => u.tracks.audio).map((u) => u.name)
Expand Down
4 changes: 3 additions & 1 deletion app/routes/_room.$roomName.room.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export const loader = async ({ request, context }: LoaderFunctionArgs) => {
context.env.FEEDBACK_STORAGE
),
mode: context.mode,
hasDb: Boolean(context.env.DB),
})
}

Expand Down Expand Up @@ -123,6 +124,7 @@ export default function Room() {
}

function JoinedRoom({ bugReportsEnabled }: { bugReportsEnabled: boolean }) {
const { hasDb } = useLoaderData<typeof loader>()
const {
userMedia,
peer,
Expand Down Expand Up @@ -324,7 +326,7 @@ function JoinedRoom({ bugReportsEnabled }: { bugReportsEnabled: boolean }) {
className="hidden md:block"
></ParticipantsButton>
<OverflowMenu bugReportsEnabled={bugReportsEnabled} />
<LeaveRoomButton />
<LeaveRoomButton navigateToFeedbackPage={hasDb} />
</div>
</div>
<HighPacketLossWarningsToast />
Expand Down
16 changes: 16 additions & 0 deletions app/routes/api.reportRefresh.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { ActionFunctionArgs } from '@remix-run/cloudflare'
import { AnalyticsRefreshes, getDb } from 'schema'
import { RELEASE } from '~/utils/constants'
import { mode } from '~/utils/mode'

export const action = async ({ context }: ActionFunctionArgs) => {
const db = getDb(context)
if (db === null || mode === 'development')
return new Response(null, { status: 200 })

await db.insert(AnalyticsRefreshes).values({
version: RELEASE ?? 'dev',
})

return new Response(null, { status: 201 })
}
42 changes: 42 additions & 0 deletions app/routes/call-quality-feedback.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { type ActionFunctionArgs } from '@remix-run/cloudflare'
import { Form } from '@remix-run/react'
import { AnalyticsSimpleCallFeedback, getDb } from 'schema'
import { Button } from '~/components/Button'
import { RELEASE } from '~/utils/constants'

const redirectToHome = new Response(null, {
status: 302,
headers: {
Location: '/',
},
})

export const action = async ({ request, context }: ActionFunctionArgs) => {
const db = getDb(context)
if (!db) return redirectToHome

const formData = await request.formData()
const experiencedIssues = formData.get('experiencedIssues') === 'true'
await db.insert(AnalyticsSimpleCallFeedback).values({
experiencedIssues: Number(experiencedIssues),
version: RELEASE ?? 'dev',
})

return redirectToHome
}

export default function SetUsername() {
return (
<div className="grid h-full gap-4 place-content-center">
<h1 className="text-3xl font-bold">Experience any issues?</h1>
<Form className="flex items-end gap-4" method="post">
<Button displayType="secondary" value="true" name="experiencedIssues">
Yes
</Button>
<Button displayType="secondary" value="false" name="experiencedIssues">
No
</Button>
</Form>
</div>
)
}
1 change: 1 addition & 0 deletions app/types/Env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ export type Env = {
MAX_WEBCAM_BITRATE?: string
MAX_WEBCAM_QUALITY_LEVEL?: string
MAX_API_HISTORY?: string
DB?: D1Database
}
24 changes: 24 additions & 0 deletions drizzle.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { Config } from 'drizzle-kit'

const { LOCAL_DB_PATH, DB_ID, D1_TOKEN, CF_ACCOUNT_ID } = process.env

// Use better-sqlite driver for local development
export default LOCAL_DB_PATH
? ({
schema: './schema.ts',
dialect: 'sqlite',
dbCredentials: {
url: LOCAL_DB_PATH,
},
} satisfies Config)
: ({
schema: './schema.ts',
out: './migrations',
dialect: 'sqlite',
driver: 'd1-http',
dbCredentials: {
databaseId: DB_ID!,
token: D1_TOKEN!,
accountId: CF_ACCOUNT_ID!,
},
} satisfies Config)
16 changes: 16 additions & 0 deletions migrations/0000_warm_siren.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
CREATE TABLE `AnalyticsRefreshes` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`created` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
`modified` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
`deleted` text,
`version` text NOT NULL
);
--> statement-breakpoint
CREATE TABLE `AnalyticsSimpleCallFeedback` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`created` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
`modified` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
`deleted` text,
`version` text NOT NULL,
`experiencedIssues` integer NOT NULL
);
116 changes: 116 additions & 0 deletions migrations/meta/0000_snapshot.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
{
"version": "6",
"dialect": "sqlite",
"id": "fe0342f9-5cae-44a5-86d3-368c43bf7869",
"prevId": "00000000-0000-0000-0000-000000000000",
"tables": {
"AnalyticsRefreshes": {
"name": "AnalyticsRefreshes",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"created": {
"name": "created",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP"
},
"modified": {
"name": "modified",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP"
},
"deleted": {
"name": "deleted",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"version": {
"name": "version",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"AnalyticsSimpleCallFeedback": {
"name": "AnalyticsSimpleCallFeedback",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"created": {
"name": "created",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP"
},
"modified": {
"name": "modified",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP"
},
"deleted": {
"name": "deleted",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"version": {
"name": "version",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"experiencedIssues": {
"name": "experiencedIssues",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"indexes": {}
}
}
13 changes: 13 additions & 0 deletions migrations/meta/_journal.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"version": "7",
"dialect": "sqlite",
"entries": [
{
"idx": 0,
"version": "6",
"when": 1726697068248,
"tag": "0000_warm_siren",
"breakpoints": true
}
]
}
Loading

0 comments on commit 8b3a855

Please sign in to comment.