mirror of
https://github.com/yusufipk/YTChatHub.git
synced 2026-09-11 10:56:17 +00:00
refactor: extract ChatListPanel component and add conditional auto-scroll behavior
This commit is contained in:
@@ -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"
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
{(!message.superChat && !message.membershipGift) && message.text && (
|
||||
<p className="overlay__text">{message.text}</p>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="overlay__placeholder">
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user