Watch Together: Videos bleiben in Queue mit Watched-Haken
Statt Videos nach dem Abspielen aus der Warteschlange zu entfernen, bleiben sie drin und werden mit einem gruenen Haken markiert. Separate History-Section entfernt — die Queue IST die History. Videos bleiben klickbar zum erneuten Abspielen. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
22b4c6b187
commit
963bb1b775
3 changed files with 64 additions and 146 deletions
|
|
@ -20,8 +20,7 @@ interface RoomState {
|
|||
currentVideo: { url: string; title: string } | null;
|
||||
playing: boolean;
|
||||
currentTime: number;
|
||||
queue: Array<{ url: string; title: string; addedBy: string }>;
|
||||
history: Array<{ url: string; title: string; addedBy: string; playedAt: string }>;
|
||||
queue: Array<{ url: string; title: string; addedBy: string; watched: boolean }>;
|
||||
}
|
||||
|
||||
interface JoinModal {
|
||||
|
|
@ -77,7 +76,6 @@ export default function WatchTogetherTab({ data }: { data: any }) {
|
|||
const [currentTime, setCurrentTime] = useState(0);
|
||||
const [playerError, setPlayerError] = useState<string | null>(null);
|
||||
const [addingToQueue, setAddingToQueue] = useState(false);
|
||||
const [showHistory, setShowHistory] = useState(false);
|
||||
|
||||
// ── Refs ──
|
||||
const wsRef = useRef<WebSocket | null>(null);
|
||||
|
|
@ -299,7 +297,6 @@ export default function WatchTogetherTab({ data }: { data: any }) {
|
|||
playing: r.playing || false,
|
||||
currentTime: r.currentTime || 0,
|
||||
queue: r.queue || [],
|
||||
history: r.history || [],
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
|
@ -315,7 +312,6 @@ export default function WatchTogetherTab({ data }: { data: any }) {
|
|||
playing: r.playing || false,
|
||||
currentTime: r.currentTime || 0,
|
||||
queue: r.queue || [],
|
||||
history: r.history || [],
|
||||
});
|
||||
if (r.currentVideo?.url) {
|
||||
setTimeout(() => loadVideo(r.currentVideo.url), 100);
|
||||
|
|
@ -370,7 +366,6 @@ export default function WatchTogetherTab({ data }: { data: any }) {
|
|||
currentVideo: newVideo || null,
|
||||
playing: msg.playing,
|
||||
currentTime: msg.currentTime ?? prev.currentTime,
|
||||
history: msg.history ?? prev.history,
|
||||
} : prev);
|
||||
break;
|
||||
}
|
||||
|
|
@ -708,52 +703,39 @@ export default function WatchTogetherTab({ data }: { data: any }) {
|
|||
{currentRoom.queue.length === 0 ? (
|
||||
<div className="wt-queue-empty">Keine Videos in der Warteschlange</div>
|
||||
) : (
|
||||
currentRoom.queue.map((item, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className={`wt-queue-item${currentRoom.currentVideo?.url === item.url ? ' playing' : ''}${isHost ? ' clickable' : ''}`}
|
||||
onClick={() => isHost && wsSend({ type: 'play_video', index: i })}
|
||||
title={isHost ? 'Klicken zum Abspielen' : undefined}
|
||||
>
|
||||
<div className="wt-queue-item-info">
|
||||
{item.url.match(/youtu/) && (
|
||||
<img
|
||||
className="wt-queue-thumb"
|
||||
src={`https://img.youtube.com/vi/${item.url.match(/(?:youtube\.com\/(?:watch\?v=|embed\/|shorts\/)|youtu\.be\/)([a-zA-Z0-9_-]{11})/)?.[1]}/default.jpg`}
|
||||
alt=""
|
||||
/>
|
||||
)}
|
||||
<div className="wt-queue-item-text">
|
||||
<div className="wt-queue-item-title">{item.title || item.url}</div>
|
||||
<div className="wt-queue-item-by">{item.addedBy}</div>
|
||||
currentRoom.queue.map((item, i) => {
|
||||
const isCurrent = currentRoom.currentVideo?.url === item.url;
|
||||
return (
|
||||
<div
|
||||
key={i}
|
||||
className={`wt-queue-item${isCurrent ? ' playing' : ''}${item.watched && !isCurrent ? ' watched' : ''}${isHost ? ' clickable' : ''}`}
|
||||
onClick={() => isHost && wsSend({ type: 'play_video', index: i })}
|
||||
title={isHost ? 'Klicken zum Abspielen' : undefined}
|
||||
>
|
||||
<div className="wt-queue-item-info">
|
||||
{item.watched && !isCurrent && <span className="wt-queue-item-check">{'\u2713'}</span>}
|
||||
{item.url.match(/youtu/) && (
|
||||
<img
|
||||
className="wt-queue-thumb"
|
||||
src={`https://img.youtube.com/vi/${item.url.match(/(?:youtube\.com\/(?:watch\?v=|embed\/|shorts\/)|youtu\.be\/)([a-zA-Z0-9_-]{11})/)?.[1]}/default.jpg`}
|
||||
alt=""
|
||||
/>
|
||||
)}
|
||||
<div className="wt-queue-item-text">
|
||||
<div className="wt-queue-item-title">{item.title || item.url}</div>
|
||||
<div className="wt-queue-item-by">{item.addedBy}</div>
|
||||
</div>
|
||||
</div>
|
||||
{isHost && (
|
||||
<button className="wt-queue-item-remove" onClick={(e) => { e.stopPropagation(); removeFromQueue(i); }} title="Entfernen">
|
||||
{'\u00D7'}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{isHost && (
|
||||
<button className="wt-queue-item-remove" onClick={() => removeFromQueue(i)} title="Entfernen">
|
||||
{'\u00D7'}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
))
|
||||
);
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
{currentRoom.history && currentRoom.history.length > 0 && (
|
||||
<div className="wt-history-section">
|
||||
<button className="wt-history-toggle" onClick={() => setShowHistory(!showHistory)}>
|
||||
{showHistory ? '\u25BC' : '\u25B6'} Verlauf ({currentRoom.history.length})
|
||||
</button>
|
||||
{showHistory && (
|
||||
<div className="wt-history-list">
|
||||
{[...currentRoom.history].reverse().map((item, i) => (
|
||||
<div key={i} className="wt-history-item">
|
||||
<div className="wt-history-item-title">{item.title || item.url}</div>
|
||||
<div className="wt-history-item-by">{item.addedBy}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="wt-queue-add">
|
||||
<input
|
||||
className="wt-input wt-queue-input"
|
||||
|
|
|
|||
|
|
@ -780,61 +780,21 @@
|
|||
}
|
||||
}
|
||||
|
||||
/* ── History Section ── */
|
||||
.wt-history-section {
|
||||
border-top: 1px solid var(--bg-tertiary);
|
||||
margin-top: 8px;
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.wt-history-toggle {
|
||||
width: 100%;
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-muted);
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
padding: 6px 8px;
|
||||
text-align: left;
|
||||
border-radius: var(--radius);
|
||||
transition: background var(--transition), color var(--transition);
|
||||
}
|
||||
.wt-history-toggle:hover {
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.wt-history-list {
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.wt-history-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
padding: 8px 10px;
|
||||
border-radius: var(--radius);
|
||||
opacity: 0.6;
|
||||
/* ── Watched Queue Items ── */
|
||||
.wt-queue-item.watched {
|
||||
opacity: 0.55;
|
||||
transition: opacity var(--transition), background var(--transition);
|
||||
}
|
||||
.wt-history-item:hover {
|
||||
opacity: 1;
|
||||
background: var(--bg-tertiary);
|
||||
.wt-queue-item.watched:hover {
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.wt-history-item-title {
|
||||
font-size: 13px;
|
||||
color: var(--text-muted);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.wt-history-item-by {
|
||||
font-size: 11px;
|
||||
color: var(--text-faint);
|
||||
.wt-queue-item-check {
|
||||
color: #2ecc71;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
flex-shrink: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* ── Queue Thumbnail ── */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue