292 lines
11 KiB
PHP
292 lines
11 KiB
PHP
{{-- Video Type Icon (using Bootstrap Icons like video-card.blade.php) --}}
|
|
@php
|
|
$typeIcon = match($video->type) {
|
|
'music' => 'bi-music-note',
|
|
'match' => 'bi-trophy',
|
|
default => 'bi-film',
|
|
};
|
|
@endphp
|
|
|
|
{{-- Video Title with Type Icon Inline --}}
|
|
<h1 class="video-title" style="display: flex; align-items: center; gap: 10px; margin: 8px 0 6px;">
|
|
<i class="bi {{ $typeIcon }}" style="color: #ef4444; margin-right: 6px;"></i>
|
|
<span>{{ $video->title }}</span>
|
|
</h1>
|
|
|
|
{{-- Channel Row with Actions Inline --}}
|
|
<div class="channel-row" style="display: flex; align-items: center; justify-content: space-between; padding: 6px 0; flex-wrap: wrap; gap: 12px;">
|
|
{{-- Left: Channel Info --}}
|
|
<div style="display: flex; align-items: center; gap: 12px;">
|
|
<a href="{{ route('channel', $video->user_id) }}" class="channel-info text-decoration-none" style="color: inherit; display: flex; align-items: center; gap: 12px;">
|
|
@if($video->user)
|
|
<img src="{{ $video->user->avatar_url }}" class="channel-avatar" style="width: 36px; height: 36px;" alt="{{ $video->user->name }}">
|
|
@else
|
|
<div class="channel-avatar" style="width: 36px; height: 36px;"></div>
|
|
@endif
|
|
<div>
|
|
<div class="channel-name">{{ $video->user->name ?? 'Unknown' }}</div>
|
|
<div class="channel-subs">{{ number_format($video->user->subscriber_count ?? 0) }} subscribers</div>
|
|
</div>
|
|
</a>
|
|
|
|
{{-- Subscribe Button --}}
|
|
@auth
|
|
@if(Auth::id() !== $video->user_id)
|
|
<button class="subscribe-btn" style="background: white; color: black; border: none; padding: 8px 16px; border-radius: 18px; font-weight: 600; font-size: 14px; cursor: pointer; white-space: nowrap;">
|
|
Subscribe
|
|
</button>
|
|
@endif
|
|
@else
|
|
<a href="{{ route('login') }}" class="subscribe-btn" style="background: white; color: black; border: none; padding: 8px 16px; border-radius: 18px; font-weight: 600; font-size: 14px; text-decoration: none; white-space: nowrap; display: inline-block;">
|
|
Subscribe
|
|
</a>
|
|
@endauth
|
|
</div>
|
|
|
|
{{-- Right: Action Buttons (Like, Edit, Share) --}}
|
|
<div class="video-actions">
|
|
@auth
|
|
{{-- Like Button with Icon and Count --}}
|
|
<form method="POST" action="{{ $video->isLikedBy(Auth::user()) ? route('videos.unlike', $video->id) : route('videos.like', $video->id) }}" class="d-inline">
|
|
@csrf
|
|
<button type="submit" class="yt-action-btn {{ $video->isLikedBy(Auth::user()) ? 'liked' : '' }}">
|
|
<i class="bi {{ $video->isLikedBy(Auth::user()) ? 'bi-hand-thumbs-up-fill' : 'bi-hand-thumbs-up' }}"></i>
|
|
{{ $video->like_count > 0 ? number_format($video->like_count) : 'Like' }}
|
|
</button>
|
|
</form>
|
|
|
|
{{-- Edit Button - Only for video owner --}}
|
|
@if(Auth::id() === $video->user_id)
|
|
<button class="yt-action-btn" onclick="openEditVideoModal({{ $video->id }})">
|
|
<i class="bi bi-pencil"></i> Edit
|
|
</button>
|
|
@endif
|
|
@else
|
|
<a href="{{ route('login') }}" class="yt-action-btn">
|
|
<i class="bi bi-hand-thumbs-up"></i> Like
|
|
</a>
|
|
@endauth
|
|
|
|
{{-- Share Button --}}
|
|
@if($video->isShareable())
|
|
<button class="yt-action-btn" onclick="openShareModal('{{ $video->share_url }}', '{{ addslashes($video->title) }}')">
|
|
<i class="bi bi-share"></i> Share
|
|
</button>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Description Box (Expandable) --}}
|
|
@if($video->description)
|
|
@php
|
|
// Parse markdown description
|
|
$fullDescription = $video->description;
|
|
$shortDescription = Str::limit($fullDescription, 200);
|
|
$needsExpand = strlen($fullDescription) > 200;
|
|
@endphp
|
|
|
|
<div class="video-description-box" style="background: var(--bg-secondary); border-radius: 12px; padding: 12px; margin-bottom: 16px;">
|
|
{{-- Fixed Stats Row (views + date - cannot be manipulated) --}}
|
|
<div class="description-stats" style="font-size: 14px; font-weight: 500; margin-bottom: 8px;">
|
|
<span>{{ number_format($video->view_count) }} views</span>
|
|
<span>•</span>
|
|
<span>{{ $video->created_at->format('M d, Y') }}</span>
|
|
</div>
|
|
|
|
{{-- Separator Line --}}
|
|
<div style="border-bottom: 1px solid var(--border-color); margin: 8px 0;"></div>
|
|
|
|
{{-- Description Content --}}
|
|
<div class="description-content" id="descriptionContent">
|
|
@if($needsExpand)
|
|
<div class="description-short" id="descShort">
|
|
<span class="description-text">{!! Str::markdown($shortDescription) !!}</span>
|
|
<span class="description-ellipsis" style="color: var(--text-secondary);">... </span>
|
|
</div>
|
|
<div class="description-full" id="descFull" style="display: none;">
|
|
<span class="description-text">{!! Str::markdown($fullDescription) !!}</span>
|
|
</div>
|
|
<button onclick="toggleDescription()" id="descToggleBtn" style="background: none; border: none; color: var(--text-primary); font-weight: 600; font-size: 14px; cursor: pointer; padding: 0; margin-top: 4px;">
|
|
Show more
|
|
</button>
|
|
@else
|
|
<span class="description-text">{!! Str::markdown($fullDescription) !!}</span>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function toggleDescription() {
|
|
const descShort = document.getElementById('descShort');
|
|
const descFull = document.getElementById('descFull');
|
|
const toggleBtn = document.getElementById('descToggleBtn');
|
|
|
|
if (descShort.style.display !== 'none') {
|
|
descShort.style.display = 'none';
|
|
descFull.style.display = 'block';
|
|
toggleBtn.textContent = 'Show less';
|
|
} else {
|
|
descShort.style.display = 'block';
|
|
descFull.style.display = 'none';
|
|
toggleBtn.textContent = 'Show more';
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.video-description-box .description-text {
|
|
font-size: 14px;
|
|
line-height: 1.5;
|
|
color: var(--text-primary);
|
|
}
|
|
.video-description-box .description-text p {
|
|
margin-bottom: 8px;
|
|
}
|
|
.video-description-box .description-text p:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
.video-description-box .description-text a {
|
|
color: #3ea6ff;
|
|
}
|
|
.video-description-box .description-text code {
|
|
background: rgba(255,255,255,0.1);
|
|
padding: 2px 6px;
|
|
border-radius: 4px;
|
|
font-family: monospace;
|
|
}
|
|
.video-description-box .description-text pre {
|
|
background: rgba(255,255,255,0.1);
|
|
padding: 12px;
|
|
border-radius: 8px;
|
|
overflow-x: auto;
|
|
}
|
|
.video-description-box .description-text ul,
|
|
.video-description-box .description-text ol {
|
|
padding-left: 20px;
|
|
margin-bottom: 8px;
|
|
}
|
|
.video-description-box .description-text strong {
|
|
font-weight: 600;
|
|
}
|
|
</style>
|
|
@endif
|
|
|
|
{{-- Comment Section --}}
|
|
<div class="comments-section" style="margin-top: 24px; padding-top: 16px; border-top: 1px solid var(--border-color);">
|
|
<h3 style="font-size: 18px; font-weight: 600; margin-bottom: 16px;">
|
|
Comments <span style="color: var(--text-secondary); font-weight: 400;">({{ $video->comment_count }})</span>
|
|
</h3>
|
|
|
|
{{-- Comment Form --}}
|
|
@auth
|
|
<div class="comment-form" style="display: flex; gap: 12px; margin-bottom: 24px;">
|
|
<img src="{{ Auth::user()->avatar_url }}" class="channel-avatar" style="width: 40px; height: 40px;" alt="{{ Auth::user()->name }}">
|
|
<div style="flex: 1;">
|
|
<textarea
|
|
id="commentBody"
|
|
class="form-control"
|
|
placeholder="Add a comment... Use @ to mention someone"
|
|
rows="3"
|
|
style="background: var(--bg-secondary); border: 1px solid var(--border-color); color: var(--text-primary); border-radius: 8px; padding: 12px; width: 100%; resize: none;"
|
|
></textarea>
|
|
<div style="display: flex; gap: 8px; margin-top: 8px; justify-content: flex-end;">
|
|
<button type="button" class="yt-action-btn" onclick="document.getElementById('commentBody').value = ''">Cancel</button>
|
|
<button type="button" class="yt-action-btn" style="background: var(--brand-red); color: white;" onclick="submitComment({{ $video->id }})">Comment</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@else
|
|
<div style="margin-bottom: 24px; padding: 16px; background: var(--bg-secondary); border-radius: 8px; text-align: center;">
|
|
<a href="{{ route('login') }}" style="color: var(--brand-red);">Sign in</a> to comment
|
|
</div>
|
|
@endauth
|
|
|
|
{{-- Comments List --}}
|
|
<div id="commentsList">
|
|
@forelse($video->comments()->whereNull('parent_id')->with('user', 'replies.user')->latest()->get() as $comment)
|
|
@include('videos.partials.comment', ['comment' => $comment])
|
|
@empty
|
|
<p style="color: var(--text-secondary); text-align: center; padding: 20px;">No comments yet. Be the first to comment!</p>
|
|
@endforelse
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function submitComment(videoId) {
|
|
const body = document.getElementById('commentBody').value.trim();
|
|
if (!body) return;
|
|
|
|
fetch(`/videos/${videoId}/comments`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
|
},
|
|
body: JSON.stringify({ body: body })
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
document.getElementById('commentBody').value = '';
|
|
loadComments(videoId);
|
|
}
|
|
});
|
|
}
|
|
|
|
function loadComments(videoId) {
|
|
fetch(`/videos/${videoId}/comments`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
// Reload page to show new comments
|
|
location.reload();
|
|
});
|
|
}
|
|
|
|
function deleteComment(commentId) {
|
|
if (!confirm('Are you sure you want to delete this comment?')) return;
|
|
|
|
fetch(`/comments/${commentId}`, {
|
|
method: 'DELETE',
|
|
headers: {
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
location.reload();
|
|
}
|
|
});
|
|
}
|
|
|
|
// Highlight @mentions in comments
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const commentTexts = document.querySelectorAll('.comment-body');
|
|
commentTexts.forEach(text => {
|
|
const html = text.innerHTML.replace(/@(\w+)/g, '<span style="color: #3ea6ff; font-weight: 500;">@$1</span>');
|
|
text.innerHTML = html;
|
|
});
|
|
});
|
|
|
|
function submitReply(videoId, parentId) {
|
|
const textarea = document.querySelector(`#replyForm${parentId} textarea`);
|
|
const body = textarea.value.trim();
|
|
if (!body) return;
|
|
|
|
fetch(`/videos/${videoId}/comments`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
|
},
|
|
body: JSON.stringify({ body: body, parent_id: parentId })
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
location.reload();
|
|
}
|
|
});
|
|
}
|
|
</script>
|