Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
226 changes: 226 additions & 0 deletions client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
# Chat E2EE Client - React.js Implementation

This is the React.js implementation of the Chat E2EE client, migrated from vanilla TypeScript while maintaining full functional parity with the original.

## 📋 Project Structure

```
src/
├── components/ # All React components
│ ├── common/ # Reusable UI components (Button, Input, Icons)
│ ├── SetupOverlay/ # Channel creation/joining flow
│ ├── ChatContainer/ # Main chat interface
│ └── CallOverlay/ # Audio call UI
├── context/ # React Context for global state
│ └── ChatContext.tsx # Chat service wrapper and state management
├── hooks/ # Custom React hooks
│ ├── useCallTimer.ts
│ ├── useUrlHash.ts
│ └── useAudioNotification.ts
├── types/ # TypeScript type definitions
├── utils/ # Utility functions
│ ├── audioNotification.ts
│ ├── messageHandling.ts
│ └── callTimer.ts
├── styles/ # Global styles
│ └── global.css
├── App.tsx # Main application component
└── main.tsx # React application entry point
```

## 🚀 Getting Started

### Prerequisites
- Node.js (v16+)
- npm or yarn

### Installation

```bash
cd client
npm install
```

### Development Server

```bash
npm run dev
```

The application will be available at `http://localhost:5173`

### Building for Production

```bash
npm run build
```

The compiled output will be in the `dist/` folder.

### Preview Production Build

```bash
npm run preview
```

## 🏗️ Architecture

### State Management
- **ChatContext**: Wraps the `@chat-e2ee/service` package without any modifications
- Global state includes: connection status, messages, call status, user ID, and channel hash
- All service initialization and event handling is managed through React hooks

### Component Hierarchy

```
App
├── ChatProvider (Context)
│ ├── SetupOverlay
│ │ ├── InitialActions
│ │ ├── CreateHashView
│ │ └── JoinHashView
│ └── ChatContainer
│ ├── ChatHeader
│ ├── MessagesArea
│ │ └── MessageBubble (repeated)
│ ├── ChatFooter
│ └── CallOverlay
```

### Key Features
- ✅ Full end-to-end message encryption
- ✅ Audio call support
- ✅ Real-time peer detection
- ✅ Glass-morphism UI design
- ✅ Mobile-responsive layout
- ✅ Native share API integration
- ✅ URL hash auto-population for channel joining

## 🔒 Security & Backend Integration

**Important:** All backend communication and encryption logic is handled by the `@chat-e2ee/service` package. This client implementation:
- ✅ Does NOT modify any service logic
- ✅ Does NOT change API contracts
- ✅ Only wraps the service with React state management
- ✅ Preserves all encryption/decryption calls

Backend environment variable:
```bash
CHATE2EE_API_URL=http://localhost:3001 # or your backend URL
```

## 📱 Responsive Design

The UI is fully responsive and tested on:
- Desktop browsers (1920x1080+)
- Tablets (768px+)
- Mobile phones (320px+)
- iOS Safe Area support for notches

## 🔄 Migration Notes

This is a pure UI layer refactoring from vanilla TypeScript to React.js:

**What Changed:**
- DOM manipulation replaced with React components
- Event listeners replaced with React hooks and Context
- Global variables replaced with React state
- CSS organization improved with component-scoped styling

**What Stayed the Same:**
- All service integration unchanged
- All encryption logic unchanged
- All API calls unchanged
- All user-facing functionality identical
- All UI/UX identical

## 🧪 Testing

### Manual Testing Checklist

- [ ] Create new channel flow
- [ ] Join existing channel flow
- [ ] Send/receive messages in real-time
- [ ] Audio call initiation and termination
- [ ] Copy hash functionality
- [ ] URL hash auto-population
- [ ] Peer detection and status indicators
- [ ] Mobile responsiveness
- [ ] Message animations
- [ ] Call duration timer

### Browser Support

- Chrome/Chromium 90+
- Firefox 88+
- Safari 14+
- Edge 90+

## 🛠️ Development

### Adding a New Component

1. Create component directory in `src/components/`
2. Add component file (`.tsx`) and styles file (`.css`)
3. Export from component file using named export
4. Import and use in parent component

### Adding a New Hook

1. Create hook file in `src/hooks/`
2. Use React hooks (useState, useEffect, useCallback, etc.)
3. Export custom hook with `use` prefix
4. Import in components that need it

### Styling Guidelines

- Use CSS classes for styling (not inline styles)
- Follow BEM-like naming: `component-name`, `component-name__element`
- Import CSS in component file for scoped styling
- Use CSS variables from `styles/global.css` for consistency
- Maintain mobile-first responsive design

## 📦 Dependencies

- `react@^18.2.0` - React library
- `react-dom@^18.2.0` - React DOM rendering
- `@chat-e2ee/service@*` - E2EE messaging service (unmodified)
- `typescript@^5.6.2` - TypeScript compiler
- `vite@^5.4.1` - Build tool

## 🐛 Troubleshooting

### Chat not initializing
- Check `CHATE2EE_API_URL` environment variable
- Verify backend server is running
- Check browser console for detailed errors

### Messages not sending
- Ensure peer has joined the channel
- Check network connection
- Verify encryption keys are initialized

### Build errors
- Run `npm install` to ensure all dependencies are installed
- Check Node.js version (requires v16+)
- Clear `node_modules` and reinstall if needed

## 📄 License

Same as the main Chat E2EE project.

## 🤝 Contributing

This is part of the open-source Chat E2EE project. When contributing:

1. **Only modify files within the `client/` folder**
2. **Do not change `@chat-e2ee/service` imports or behavior**
3. **Maintain full backward compatibility with the service**
4. **Test all features before submitting PR**
5. **Follow the existing code style and component patterns**

For specific contribution guidelines, see the main project README.

---

Made with ❤️ for secure, private communication
129 changes: 3 additions & 126 deletions client/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,138 +5,15 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>Chat E2EE - Minimal & Secure</title>
<link rel="stylesheet" href="./style.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600&display=swap" rel="stylesheet">
</head>

<body>
<div id="app">
<!-- Setup Overlay -->
<div id="setup-overlay" class="overlay">
<div class="overlay-content glass">
<h1>Secure Messenger</h1>
<p>Simple. End-to-End Encrypted. Private.</p>

<div id="initial-actions" class="button-group-vertical">
<button id="show-create-hash" class="primary large">Create New Channel</button>
<button id="show-join-hash" class="secondary large">Already have a Hash?</button>
</div>

<div id="create-hash-view" class="hidden">
<div class="input-group">
<label>Your Channel Hash</label>
<div class="copy-input">
<input type="text" id="generated-hash-display" readonly placeholder="Generating...">
<button id="copy-hash-btn" class="icon-btn small">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
</svg>
</button>
</div>
</div>
</div>

<div id="join-hash-view" class="hidden">
<div class="input-group">
<label for="channel-hash">Channel Hash</label>
<input type="text" id="channel-hash" placeholder="Enter hash to join...">
</div>
</div>

<div id="final-actions" class="hidden">
<div class="button-group">
<button id="back-btn" class="secondary">Back</button>
<button id="join-btn" class="primary">Connect Securely</button>
</div>
</div>

<div id="setup-status" class="status-text"></div>
</div>
</div>

<!-- Main Chat UI -->
<div id="chat-container" class="hidden">
<header class="glass">
<div class="header-info">
<div class="title-row">
<span class="dot" id="status-dot"></span>
<h2 id="channel-title">Secure Channel</h2>
<span id="encryption-badge" class="badge">E2EE</span>
</div>
<div id="channel-hash-display" class="hash-badge-container hidden">
<span id="header-hash" class="hash-text"></span>
<button id="copy-header-hash" class="icon-btn tiny" title="Copy Link">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
</svg>
</button>
</div>
<p id="participant-info">Waiting for someone to join...</p>
</div>
<div class="header-actions">
<button id="share-btn" title="Share Link" class="icon-btn hidden">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round">
<circle cx="18" cy="5" r="3"></circle>
<circle cx="6" cy="12" r="3"></circle>
<circle cx="18" cy="19" r="3"></circle>
<line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line>
<line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line>
</svg>
</button>
<button id="start-call-btn" title="Start Audio Call" class="icon-btn">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round">
<path
d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z">
</path>
</svg>
</button>
</div>
</header>

<main id="messages-area">
<!-- Messages will appear here -->
</main>

<footer class="glass">
<div class="input-container">
<input type="text" id="msg-input" placeholder="Type a secure message...">
<button id="send-btn" class="primary circle">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round">
<line x1="22" y1="2" x2="11" y2="13"></line>
<polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
</svg>
</button>
</div>
</footer>
</div>

<!-- Call Status Overlay -->
<div id="call-overlay" class="hidden glass blur-overlay">
<div class="call-info">
<div class="call-avatar shimmer"></div>
<h3 id="call-status">Calling...</h3>
<p id="call-duration">00:00</p>
<button id="end-call-btn" class="danger circle large">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round">
<path
d="M10.68 13.31a16 16 0 0 0 3.41 2.6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7 2 2 0 0 1 1.72 2v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 3.41 2.6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z">
</path>
</svg>
</button>
</div>
</div>
</div>
<script type="module" src="./app.ts"></script>
<!-- React renders everything here -->
<div id="app"></div>
<script type="module" src="./src/main.tsx"></script>
</body>

</html>
9 changes: 7 additions & 2 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,14 @@
},
"devDependencies": {
"typescript": "^5.6.2",
"vite": "^5.4.1"
"vite": "^5.4.1",
"@vitejs/plugin-react": "^4.2.1",
"@types/react": "^18.2.37",
"@types/react-dom": "^18.2.15"
},
"dependencies": {
"@chat-e2ee/service": "*"
"@chat-e2ee/service": "*",
"react": "^18.2.0",
"react-dom": "^18.2.0"
}
}
Loading
Loading