Merged Client from Vanilla HTML JS to React 18 Components#443
Merged Client from Vanilla HTML JS to React 18 Components#443VishnuGadekar7 wants to merge 2 commits intomuke1908:masterfrom
Conversation
|
Hi! Could you please review this PR and approve the pending workflows so the checks can complete? Thanks! |
There was a problem hiding this comment.
Pull request overview
Migrates the client/ UI from a vanilla TypeScript/imperative DOM implementation to a React 18 + Vite component architecture while preserving the existing @chat-e2ee/service integration.
Changes:
- Adds React 18 + Vite React plugin, and switches the client entrypoint to
src/main.tsx. - Introduces React Context (
ChatContext) for wrapping the service SDK and managing chat/call/message state. - Adds componentized UI (Setup overlay, chat container, call overlay) with new global + scoped CSS.
Reviewed changes
Copilot reviewed 41 out of 42 changed files in this pull request and generated 15 comments.
Show a summary per file
| File | Description |
|---|---|
| package-lock.json | Adds React/ReactDOM and React tooling to the workspace lockfile. |
| client/vite.config.ts | Enables @vitejs/plugin-react for JSX/React Fast Refresh support. |
| client/tsconfig.json | Enables react-jsx and includes TSX sources for the new React client. |
| client/src/utils/messageHandling.ts | Adds message creation + timestamp formatting helpers. |
| client/src/utils/callTimer.ts | Adds URL hash helper utilities (despite the filename). |
| client/src/utils/audioNotification.ts | Adds a small WebAudio beep utility. |
| client/src/types/index.ts | Introduces app/service/component type definitions. |
| client/src/styles/global.css | Adds global CSS variables and base layout styles for the React app. |
| client/src/main.tsx | New React entrypoint rendering ChatProvider + App. |
| client/src/hooks/useUrlHash.ts | Adds URL hash management hook for join flow auto-population. |
| client/src/hooks/useCallTimer.ts | Adds call-duration timer hook. |
| client/src/hooks/useAudioNotification.ts | Adds hook wrapper around the beep utility. |
| client/src/context/ChatContext.tsx | Adds Context-based wrapper around @chat-e2ee/service for app state + events. |
| client/src/components/SetupOverlay/SetupOverlay.tsx | Implements the setup flow container (initial/create/join). |
| client/src/components/SetupOverlay/SetupOverlay.css | Styles for the setup overlay. |
| client/src/components/SetupOverlay/JoinHashView.tsx | Join-by-hash view with URL hash auto-fill. |
| client/src/components/SetupOverlay/JoinHashView.css | Styles for join view. |
| client/src/components/SetupOverlay/InitialActions.tsx | Initial setup actions (create/join). |
| client/src/components/SetupOverlay/InitialActions.css | Styles for initial actions. |
| client/src/components/SetupOverlay/CreateHashView.tsx | Create-channel view with copy UI. |
| client/src/components/SetupOverlay/CreateHashView.css | Styles for create view. |
| client/src/components/common/Input.tsx | Reusable Input component. |
| client/src/components/common/Input.css | Styles for Input. |
| client/src/components/common/icons.tsx | SVG icon components. |
| client/src/components/common/Button.tsx | Reusable Button component. |
| client/src/components/common/Button.css | Styles for Button variants/sizes. |
| client/src/components/ChatContainer/MessagesArea.tsx | Renders messages list with auto-scroll behavior. |
| client/src/components/ChatContainer/MessagesArea.css | Styles for messages area. |
| client/src/components/ChatContainer/MessageBubble.tsx | Individual message bubble rendering. |
| client/src/components/ChatContainer/MessageBubble.css | Styles for message bubble variants. |
| client/src/components/ChatContainer/ChatHeader.tsx | Header UI with copy/share/call actions. |
| client/src/components/ChatContainer/ChatHeader.css | Styles for chat header. |
| client/src/components/ChatContainer/ChatFooter.tsx | Footer input + send action UI. |
| client/src/components/ChatContainer/ChatFooter.css | Styles for chat footer. |
| client/src/components/ChatContainer/ChatContainer.tsx | Composes header/messages/footer + call overlay. |
| client/src/components/ChatContainer/ChatContainer.css | Styles for overall chat container. |
| client/src/components/CallOverlay/CallOverlay.tsx | Call overlay UI with timer + end-call button. |
| client/src/components/CallOverlay/CallOverlay.css | Styles for call overlay. |
| client/src/App.tsx | App composition: setup overlay vs. chat container, error toast. |
| client/README.md | Documents the new React client structure and workflows. |
| client/package.json | Adds React dependencies and React/Vite dev tooling. |
| client/index.html | Replaces old static DOM markup with a React mount point + main.tsx. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
client/src/types/index.ts
Outdated
| export interface ButtonProps { | ||
| variant?: 'primary' | 'secondary' | 'danger'; | ||
| size?: 'small' | 'large'; | ||
| onClick: () => void; | ||
| disabled?: boolean; |
There was a problem hiding this comment.
ButtonProps.size only allows 'small' | 'large', but Button defaults size = 'medium' and various call sites pass class names like btn--tiny. With noUnusedLocals/strict, this will fail type-checking and also produces CSS classes that aren't defined. Align the union with the supported size classes (e.g., include 'medium'/'tiny' or remove the default and map sizes consistently).
client/src/types/index.ts
Outdated
| onClick: () => void; | ||
| disabled?: boolean; | ||
| icon?: boolean; | ||
| circle?: boolean; | ||
| children: React.ReactNode; | ||
| className?: string; | ||
| title?: string; | ||
| } |
There was a problem hiding this comment.
ButtonProps is a custom interface and doesn’t include standard button attributes (e.g., id, aria-*, type, etc.), but components in this PR pass id/title to Button. This will cause TypeScript errors under strict. Consider extending React.ButtonHTMLAttributes<HTMLButtonElement> (and forwarding remaining props in Button) so common attributes are supported safely.
client/src/types/index.ts
Outdated
| export interface ChatInstance { | ||
| init: () => Promise<void>; | ||
| getKeyPair: () => { privateKey: string; publicKey: string }; | ||
| getLink: () => Promise<{ hash: string }>; | ||
| setChannel: (hash: string, userId: string) => Promise<void>; | ||
| getUsersInChannel: () => Promise<any[]>; | ||
| startCall: () => Promise<Call>; | ||
| on: (event: string, callback: Function) => void; | ||
| encrypt: (data: { text: string }) => { send: () => Promise<void> }; | ||
| } |
There was a problem hiding this comment.
ChatInstance doesn’t match the actual IChatE2EE type from @chat-e2ee/service (e.g., setChannel is typed as Promise<void> here but is void in the SDK; encrypt().send() returns a message result, not Promise<void>). Because createChatInstance() is typed, setChat(chatInstance) will fail structural typing in strict mode. Prefer importing IChatE2EE from the SDK (or aligning this interface to it) instead of redefining it.
| "include": [ | ||
| "src/**/*.ts", | ||
| "src/**/*.tsx", | ||
| "app.ts" | ||
| ] |
There was a problem hiding this comment.
tsconfig.json still includes app.ts, which is the legacy vanilla client entrypoint and is no longer referenced by index.html. With noUnusedLocals enabled, keeping it in the compilation set can reintroduce build failures and makes it easy for dead code to drift. Remove app.ts from include (and ideally delete the file) now that src/main.tsx is the entrypoint.
| /** | ||
| * URL hash handling utilities | ||
| */ | ||
|
|
||
| /** | ||
| * Extract hash from current URL | ||
| */ | ||
| export function getUrlHash(): string { | ||
| return window.location.hash.replace('#', ''); | ||
| } |
There was a problem hiding this comment.
This file is named callTimer.ts but it only contains URL hash helpers (getUrlHash, updateUrlHash, hasValidHash) and is imported by useUrlHash. The name is misleading (and collides conceptually with useCallTimer). Rename it to something like urlHash.ts and update imports to avoid future confusion.
client/src/context/ChatContext.tsx
Outdated
| * No modifications to the service itself | ||
| */ | ||
|
|
||
| import React, { createContext, useContext, ReactNode, useState, useEffect, useCallback } from 'react'; |
There was a problem hiding this comment.
useEffect is imported but never used. With noUnusedLocals: true, this will fail the TypeScript build; remove the unused import (or add the missing effect if something was intended here).
| import React, { createContext, useContext, ReactNode, useState, useEffect, useCallback } from 'react'; | |
| import React, { createContext, useContext, ReactNode, useState, useCallback } from 'react'; |
| const startCall = useCallback(async () => { | ||
| if (!chat) throw new Error('Chat not initialized'); | ||
| try { | ||
| const call = await chat.startCall(); | ||
| setCallActive(true); | ||
| setupCallListeners(call); | ||
| } catch (err) { |
There was a problem hiding this comment.
endCall() only updates local state; it never terminates the underlying WebRTC call in @chat-e2ee/service. startCall() also discards the returned call instance, so the UI “End Call” action won’t actually hang up. Store the active call (or use chat.activeCall / chat.endCall() from the SDK) and invoke the real end call before clearing state.
| const { duration, formatDuration, startTimer } = useCallTimer(); | ||
|
|
||
| useEffect(() => { | ||
| if (callActive && callStatus === 'Connected') { | ||
| startTimer(); | ||
| } | ||
| }, [callActive, callStatus, startTimer]); |
There was a problem hiding this comment.
useCallTimer() is started when the call connects, but it is never stopped/reset when the call ends. Since CallOverlay stays mounted (it just return null when callActive is false), isRunning can remain true and the interval can keep incrementing in the background, causing the next call to start with a non-zero duration. Call stopTimer() when callActive becomes false (or when status transitions away from connected).
| const { duration, formatDuration, startTimer } = useCallTimer(); | |
| useEffect(() => { | |
| if (callActive && callStatus === 'Connected') { | |
| startTimer(); | |
| } | |
| }, [callActive, callStatus, startTimer]); | |
| const { duration, formatDuration, startTimer, stopTimer } = useCallTimer(); | |
| useEffect(() => { | |
| if (callActive && callStatus === 'Connected') { | |
| startTimer(); | |
| } else { | |
| stopTimer(); | |
| } | |
| }, [callActive, callStatus, startTimer, stopTimer]); |
| const handleKeyPress = (e: React.KeyboardEvent) => { | ||
| if (e.key === 'Enter' && !e.shiftKey) { | ||
| e.preventDefault(); | ||
| handleSend(); | ||
| } |
There was a problem hiding this comment.
React’s onKeyPress is deprecated and can behave inconsistently across browsers/IME input. Prefer onKeyDown (or onKeyUp) for handling Enter-to-send logic, especially since this is a single-line <input type="text">.
client/src/context/ChatContext.tsx
Outdated
| }, []); | ||
|
|
||
| // Setup chat listeners | ||
| const setupChatListeners = (chatInstance: ChatInstance, currentUserId: string) => { |
There was a problem hiding this comment.
setupChatListeners declares currentUserId but never uses it. With noUnusedParameters: true, this will fail TypeScript compilation. Remove the unused parameter (or use it if it was meant for filtering self-events).
| const setupChatListeners = (chatInstance: ChatInstance, currentUserId: string) => { | |
| const setupChatListeners = (chatInstance: ChatInstance, currentUserId: string) => { | |
| void currentUserId; |
|



Description
Migrates the Chat E2EE client UI from vanilla TypeScript with imperative DOM manipulation to a modern React 18 component-based architecture.
Changes
✅ Complete React 18 Migration
✅ Component Structure
✅ Features Preserved
Backend Impact
✅ ZERO - This is UI-only refactoring
Testing Done
Files Modified
Only in
client/folder:Checklist