refactor: extract ChatListPanel component and add conditional auto-scroll behavior

This commit is contained in:
Yusuf İpek
2025-10-05 04:42:47 +03:00
parent aef59fb291
commit 0f01640050
7 changed files with 125 additions and 42 deletions
-5
View File
@@ -1,5 +0,0 @@
YOUTUBE_LIVE_ID=""
INNERTUBE_API_KEY="" # optional override
INNERTUBE_CLIENT_NAME="" # optional override
INNERTUBE_CLIENT_VERSION="" # optional override
SESSION_SECRET="change-me"
+59
View File
@@ -0,0 +1,59 @@
# YouTube Live Chat Client (WIP)
Build a desktop-operated, open-source YouTube Live chat client that delivers a fast, reliable stream of messages and allows a streamer to spotlight a selected chat message on an OBS-ready overlay.
Currently work in progress bugs might be there.
## Key Features
- **High-Performance Chat**: A sleek, minimal dashboard for monitoring YouTube Live chat in real-time.
- **OBS Integration**: Select any message to instantly display it on an OBS-ready overlay.
- **Rich Message Support**: Full support for Super Chats, gifted memberships, and user badges.
- **Compact and Efficient**: A dark, minimalist UI designed to be space-efficient and easy on the eyes.
- **Intelligent Auto-Scroll**: The chat automatically scrolls to new messages but stops when you scroll up to read previous messages.
## Getting Started
Follow these instructions to set up the project for local development.
### Prerequisites
- [Node.js](https://nodejs.org/) (v20.x or later)
- [pnpm](https://pnpm.io/)
### Installation
1. **Clone the repository:**
```bash
git clone https://github.com/your-username/youtube-client.git
cd youtube-client
```
2. **Install dependencies:**
```bash
pnpm install
```
### Running the Application
To start the development server for both the backend and frontend, run:
```bash
pnpm dev
```
- The dashboard will be available at `http://localhost:3000/dashboard`.
- The OBS overlay will be available at `http://localhost:3000/overlay`.
## Usage
Once the application is running, open the dashboard. It will automatically connect to the YouTube Live stream specified in your `.env` file. If you need to connect to a different stream, you can use the connection prompt on the dashboard.
## Technology Stack
- **Frontend**: [Next.js](https://nextjs.org/) (React)
- **Backend**: [Node.js](https://nodejs.org/) with [Fastify](https://www.fastify.io/) and [tsx](https://github.com/esbuild-kit/tsx)
- **YouTube Integration**: [youtubei.js](https://github.com/LuanRT/YouTube.js)
- **Styling**: Handcrafted CSS
+48 -18
View File
@@ -105,21 +105,22 @@ export default function DashboardPage() {
<div className="panel__header">
<h2>💬 CHAT MESSAGES</h2>
</div>
<div className="chatList">
{regularMessages.map((message) => (
<ChatListPanel
messages={regularMessages}
renderItem={(message) => (
<ChatItem
key={message.id}
message={message}
isSelected={selection?.id === message.id}
onSelect={() => handleSelect(message)}
/>
))}
{regularMessages.length === 0 && (
)}
emptyState={
<div className="chatList__empty">
<p> Waiting for chat messages...</p>
</div>
)}
</div>
}
/>
</div>
<div className="dashboard__grid-right">
@@ -127,42 +128,44 @@ export default function DashboardPage() {
<div className="panel__header">
<h2>🔥 SUPER CHATS</h2>
</div>
<div className="chatList">
{superChats.map((message) => (
<ChatListPanel
messages={superChats}
renderItem={(message) => (
<ChatItem
key={message.id}
message={message}
isSelected={selection?.id === message.id}
onSelect={() => handleSelect(message)}
/>
))}
{superChats.length === 0 && (
)}
emptyState={
<div className="chatList__empty">
<p>No super chats yet</p>
</div>
)}
</div>
}
/>
</div>
<div className="panel panel--members">
<div className="panel__header">
<h2> MEMBERSHIPS & MILESTONES</h2>
</div>
<div className="chatList">
{newMembers.map((message) => (
<ChatListPanel
messages={newMembers}
renderItem={(message) => (
<MemberItem
key={message.id}
message={message}
isSelected={selection?.id === message.id}
onSelect={() => handleSelect(message)}
/>
))}
{newMembers.length === 0 && (
)}
emptyState={
<div className="chatList__empty">
<p>No new members yet</p>
</div>
)}
</div>
}
/>
</div>
</div>
</section>
@@ -380,6 +383,33 @@ type ChatItemProps = {
onSelect: () => void;
};
type ChatListPanelProps = {
messages: ChatMessage[];
renderItem: (message: ChatMessage) => React.ReactNode;
emptyState: React.ReactNode;
};
function ChatListPanel({ messages, renderItem, emptyState }: ChatListPanelProps) {
const scrollRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const node = scrollRef.current;
if (node) {
// Check if user is near the bottom before scrolling
const isAtBottom = node.scrollHeight - node.scrollTop - node.clientHeight <= 100;
if (isAtBottom) {
node.scrollTop = node.scrollHeight;
}
}
}, [messages]);
return (
<div className="chatList" ref={scrollRef}>
{messages.length > 0 ? messages.map(renderItem) : emptyState}
</div>
);
}
function ChatItem({ message, isSelected, onSelect }: ChatItemProps) {
return (
<button
+5 -3
View File
@@ -86,15 +86,17 @@ export default function OverlayPage() {
</div>
{message.superChat && (
<div className="overlay__superchat" style={{ backgroundColor: message.superChat.color }}>
💰 {message.superChat.amount} {message.superChat.currency}
💰 {message.superChat.currency}{message.superChat.amount}
</div>
)}
{message.membershipGift && (
<div className="overlay__membership">
🎁 {message.membershipLevel || 'New Member'}
🎁 New Member!
</div>
)}
<p className="overlay__text">{message.text}</p>
{(!message.superChat && !message.membershipGift) && message.text && (
<p className="overlay__text">{message.text}</p>
)}
</div>
) : (
<div className="overlay__placeholder">
+5 -11
View File
@@ -13,17 +13,11 @@
- Redesigned dashboard with gradient backgrounds, centered layout, and modern glass-morphism effects
- Separated overlay preview into its own highlighted section that only appears when a message is selected
- Centered overlay message card content so live chat, superchat, and membership boxes align for OBS
- Reworked dashboard grid so live chat runs full-height on the left and super chat/new member panels stack in a centered right column with tuned spacing and clear separation between the stacked cards
- Moved the connection control into the header (inline, centered between logo/title and status) with a compact input width
- Fixed panel heights to fit within the viewport (no page scroll); grid rows split 1fr/1fr with internal lists scrolling so bottom edges are always visible
- Improved parsing so superchat amounts/colors show reliably; overlay and dashboard both display the amount
- Strengthened superchat amount extraction with nested field handling and regex-based fallback; UI now shows amount + currency on overlay and dashboard
- Overlay membership banner now shows the membership level text
- Membership gifts are recognized and included in the New Members list
- Completely redesigned UI to match provided screenshot: minimal dark theme (#0a0a0a bg), compact tabs with counts, tight spacing throughout
- Added connection prompt screen that shows initially and disappears after successful YouTube Live stream connection
- Reduced all padding and gaps to create a more compact, space-efficient layout
- Fixed CLEAR button to appear in bottom-right corner only when a message is selected
- **UI Layout Fixed**: Adjusted the dashboard grid to eliminate unnecessary space and properly align content. The Super Chat card is now left-aligned for better visual consistency.
- **Currency Parsing**: Improved the backend logic to correctly parse Super Chat amounts and currencies, including for non-standard formats like TRY. The hardcoded 'USD' fallback has been removed.
- **Conditional Auto-Scroll**: Implemented intelligent auto-scrolling that only activates when the user is at the bottom of the chat, preventing interruptions when reading older messages.
- **Overlay Redesign**: The overlay has been completely restyled to match the dashboard's compact, dark theme. It now displays new member announcements.
- **Gifted Memberships**: The 'Memberships & Milestones' panel now correctly displays the user who purchased the gift, not the recipient.
## Immediate Next Steps
1. Test with live YouTube stream to verify badge parsing and superchat detection
+5 -2
View File
@@ -18,6 +18,7 @@
- [x] Provide message selection controls with visual feedback.
- [x] Centered layout with gradient backgrounds and glass-morphism effects.
- [x] Message count display and connection status.
- [x] Conditional auto-scrolling for chat panels.
- [ ] Add filters/search functionality.
- [ ] Handle error states (rate limits, disconnects) gracefully in UI.
@@ -25,11 +26,11 @@
- [x] Create overlay page that consumes SSE stream at `/overlay`.
- [x] Style overlay with modern design including avatars, badges, and superchat displays.
- [x] Ensure transparent background for OBS browser source.
- [x] Display new member announcements on overlay.
- [ ] Add entrance/exit animations for message transitions.
- [ ] Add theme controls and customization options.
## Phase 4 Reliability & Polish
- [ ] Add optional persistence (SQLite) and crash recovery.
- [ ] Expand logging/metrics for long-stream observability.
- [ ] Write tests (unit/integration) and contributor documentation.
@@ -37,5 +38,7 @@
- **✅ YouTube Integration Live**: Backend connects to real YouTube Live chat and parses all message types
- **✅ Modern UI Complete**: Dashboard features centered layout, gradient backgrounds, badges, and superchat displays
- **✅ CORS Fixed**: Overlay SSE stream works cross-origin for OBS integration
- **✅ Rich Parsing**: Moderators, members, verified users, superchats, and membership gifts all detected and displayed
- **✅ Rich Parsing**: Moderators, members, verified users, superchats, and membership gifts all detected and displayed. Currency parsing is now more robust.
- **✅ UI Polished**: The dashboard and overlay layouts have been refined for better spacing and alignment. The overlay now matches the dashboard's aesthetic.
- **✅ Auto-Scroll Implemented**: Chat panels now auto-scroll intelligently.
- **Next**: Add search/filter, error recovery, and polish UX details
+3 -3
View File
@@ -16,6 +16,6 @@ Build a desktop-operated, open-source YouTube Live chat client that delivers a f
- Must tolerate YouTube API rate limits and intermittent network issues without crashing.
## Success Criteria
- Operator dashboard stays responsive during long streams (>4 hours) without memory leaks.
- Overlay updates within ~1 second of operator selection.
- Project documentation enables others to reproduce setup from scratch.
- **Responsive Dashboard**: The operator dashboard remains responsive during long streams (>4 hours) without memory leaks.
- **Instant Overlay Updates**: The OBS overlay updates in real-time when a message is selected.
- **Comprehensive Documentation**: The project includes a detailed README and a well-maintained Memory Bank to facilitate easy setup and contribution.