diff --git a/app/components/CopyButton.tsx b/app/components/CopyButton.tsx index ce98f26e..cb4c967f 100644 --- a/app/components/CopyButton.tsx +++ b/app/components/CopyButton.tsx @@ -1,42 +1,58 @@ import { VisuallyHidden } from '@radix-ui/react-visually-hidden' -import type { FC } from 'react' -import { useState } from 'react' +import { + type ComponentProps, + type ElementRef, + forwardRef, + type ReactNode, + useState, +} from 'react' import { useTimeoutFn } from 'react-use' -import { useRoomUrl } from '~/hooks/useRoomUrl' import { Button } from './Button' import { Icon } from './Icon/Icon' -import { Tooltip } from './Tooltip' -interface CopyButtonProps {} - -export const CopyButton: FC = () => { - const [copied, setCopied] = useState(false) +interface CopyButtonProps extends ComponentProps<'button'> { + contentValue: string + copiedMessage?: ReactNode +} - const roomUrl = useRoomUrl() +export const CopyButton = forwardRef, CopyButtonProps>( + ( + { + children = Copy, + copiedMessage = Copied!, + contentValue, + onClick, + ...rest + }, + ref + ) => { + const [copied, setCopied] = useState(false) - const [_isReady, _cancel, reset] = useTimeoutFn(() => { - setCopied(false) - }, 2000) + const [_isReady, _cancel, reset] = useTimeoutFn(() => { + setCopied(false) + }, 2000) - return ( - + return ( - - ) -} + ) + } +) + +CopyButton.displayName = 'CopyButton' diff --git a/app/components/LeaveRoomButton.tsx b/app/components/LeaveRoomButton.tsx index 86b0934d..01da2d46 100644 --- a/app/components/LeaveRoomButton.tsx +++ b/app/components/LeaveRoomButton.tsx @@ -7,10 +7,12 @@ import { Tooltip } from './Tooltip' interface LeaveRoomButtonProps { navigateToFeedbackPage: boolean + meetingId?: string } export const LeaveRoomButton: FC = ({ navigateToFeedbackPage, + meetingId, }) => { const navigate = useNavigate() return ( @@ -18,7 +20,11 @@ export const LeaveRoomButton: FC = ({ - - + {meetingId ? ( + <> +

Experience any issues?

+
+ + + +
+ + ) : ( +

Missing meetingId

+ )} ) } diff --git a/app/types/Messages.ts b/app/types/Messages.ts index f08bd898..b262f0ea 100644 --- a/app/types/Messages.ts +++ b/app/types/Messages.ts @@ -16,6 +16,7 @@ export type User = { } export type RoomState = { + meetingId?: string users: User[] } diff --git a/app/utils/logging.ts b/app/utils/logging.ts new file mode 100644 index 00000000..c11094cc --- /dev/null +++ b/app/utils/logging.ts @@ -0,0 +1,57 @@ +export type LogEvent = + | { + eventName: 'onStart' + meetingId?: string + } + | { + eventName: 'alarm' + meetingId?: string + } + | { + eventName: 'onConnect' + meetingId?: string + foundInStorage: boolean + connectionId: string + } + | { + eventName: 'userLeft' + meetingId?: string + connectionId: string + } + | { + eventName: 'userTimedOut' + meetingId?: string + connectionId: string + } + | { + eventName: 'startingMeeting' + meetingId?: string + } + | { + eventName: 'endingMeeting' + meetingId?: string + } + | { + eventName: 'meetingIdNotFoundInCleanup' + } + | { + eventName: 'errorBroadcastingToUser' + meetingId?: string + connectionId: string + } + | { + eventName: 'onErrorHandler' + meetingId?: string + connectionId: string + error: unknown + } + | { + eventName: 'errorHandlingMessage' + meetingId?: string + connectionId: string + error: unknown + } + +export function log(event: LogEvent) { + console.log(event) +} diff --git a/migrations/0001_calm_cloak.sql b/migrations/0001_calm_cloak.sql new file mode 100644 index 00000000..8e09e76a --- /dev/null +++ b/migrations/0001_calm_cloak.sql @@ -0,0 +1,17 @@ +CREATE TABLE `Meetings` ( + `id` text PRIMARY KEY NOT NULL, + `created` text DEFAULT CURRENT_TIMESTAMP NOT NULL, + `modified` text DEFAULT CURRENT_TIMESTAMP NOT NULL, + `deleted` text, + `userCount` integer NOT NULL, + `ended` text +); +--> statement-breakpoint +ALTER TABLE `AnalyticsSimpleCallFeedback` ADD `meetingId` text REFERENCES Meetings(id);--> statement-breakpoint +/* + SQLite does not support "Creating foreign key on existing column" out of the box, we do not generate automatic migration for that, so it has to be done manually + Please refer to: https://www.techonthenet.com/sqlite/tables/alter_table.php + https://www.sqlite.org/lang_altertable.html + + Due to that we don't generate migration automatically and it has to be done manually +*/ \ No newline at end of file diff --git a/migrations/meta/0001_snapshot.json b/migrations/meta/0001_snapshot.json new file mode 100644 index 00000000..8a49cc0a --- /dev/null +++ b/migrations/meta/0001_snapshot.json @@ -0,0 +1,190 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "09e3ebdf-2b4c-466e-9e47-679c8f22b42d", + "prevId": "fe0342f9-5cae-44a5-86d3-368c43bf7869", + "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 + }, + "meetingId": { + "name": "meetingId", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "AnalyticsSimpleCallFeedback_meetingId_Meetings_id_fk": { + "name": "AnalyticsSimpleCallFeedback_meetingId_Meetings_id_fk", + "tableFrom": "AnalyticsSimpleCallFeedback", + "tableTo": "Meetings", + "columnsFrom": [ + "meetingId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Meetings": { + "name": "Meetings", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "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 + }, + "userCount": { + "name": "userCount", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ended": { + "name": "ended", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/migrations/meta/_journal.json b/migrations/meta/_journal.json index 9c25e5d4..35b6822a 100644 --- a/migrations/meta/_journal.json +++ b/migrations/meta/_journal.json @@ -8,6 +8,13 @@ "when": 1726697068248, "tag": "0000_warm_siren", "breakpoints": true + }, + { + "idx": 1, + "version": "6", + "when": 1727550987318, + "tag": "0001_calm_cloak", + "breakpoints": true } ] } \ No newline at end of file diff --git a/schema.ts b/schema.ts index f8cf2784..0298187f 100644 --- a/schema.ts +++ b/schema.ts @@ -1,7 +1,7 @@ -import type { AppLoadContext } from '@remix-run/cloudflare' import { sql } from 'drizzle-orm' import { drizzle } from 'drizzle-orm/d1' import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core' +import type { Env } from '~/types/Env' const metadataColumns = { id: integer('id').primaryKey({ autoIncrement: true }), @@ -25,10 +25,18 @@ export const AnalyticsSimpleCallFeedback = sqliteTable( ...metadataColumns, version: text('version').notNull(), experiencedIssues: integer('experiencedIssues').notNull(), + meetingId: text('meetingId').references(() => Meetings.id), } ) -export function getDb(context: AppLoadContext) { +export const Meetings = sqliteTable('Meetings', { + ...metadataColumns, + id: text('id').primaryKey(), + peakUserCount: integer('userCount').notNull(), + ended: text('ended'), +}) + +export function getDb(context: { env: Env }) { if (!context.env.DB) { return null } diff --git a/wrangler.development.toml b/wrangler.development.toml index 84775937..92ce8e36 100644 --- a/wrangler.development.toml +++ b/wrangler.development.toml @@ -51,6 +51,9 @@ new_classes = ["ChatRoom", "RateLimiter"] tag = "v2" deleted_classes = ["RateLimiter"] +[observability] +enabled = true + # The necessary secrets are: # - CALLS_APP_SECRET # Run `echo | wrangler secret put ` for each of these diff --git a/wrangler.production.toml b/wrangler.production.toml index 41e18e08..ad9ac40f 100644 --- a/wrangler.production.toml +++ b/wrangler.production.toml @@ -51,6 +51,9 @@ new_classes = ["ChatRoom", "RateLimiter"] tag = "v2" deleted_classes = ["RateLimiter"] +[observability] +enabled = true + # The necessary secrets are: # - CALLS_APP_SECRET # Run `echo | wrangler secret put ` for each of these diff --git a/wrangler.staging.toml b/wrangler.staging.toml index 957e4f94..0e661581 100644 --- a/wrangler.staging.toml +++ b/wrangler.staging.toml @@ -51,6 +51,9 @@ new_classes = ["ChatRoom", "RateLimiter"] tag = "v2" deleted_classes = ["RateLimiter"] +[observability] +enabled = true + # The necessary secrets are: # - CALLS_APP_SECRET # Run `echo | wrangler secret put ` for each of these