latest update
This commit is contained in:
parent
3b09f4baed
commit
84fcbd84dc
File diff suppressed because it is too large
Load Diff
@ -18,6 +18,7 @@ class MatchEventController extends Controller
|
|||||||
$request->validate([
|
$request->validate([
|
||||||
'round_number' => 'required|integer|min:1',
|
'round_number' => 'required|integer|min:1',
|
||||||
'name' => 'nullable|string|max:50',
|
'name' => 'nullable|string|max:50',
|
||||||
|
'start_time_seconds' => 'nullable|integer|min:0',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Check if user owns the video
|
// Check if user owns the video
|
||||||
@ -29,6 +30,7 @@ class MatchEventController extends Controller
|
|||||||
'video_id' => $video->id,
|
'video_id' => $video->id,
|
||||||
'round_number' => $request->round_number,
|
'round_number' => $request->round_number,
|
||||||
'name' => $request->name ?? 'ROUND '.$request->round_number,
|
'name' => $request->name ?? 'ROUND '.$request->round_number,
|
||||||
|
'start_time_seconds' => $request->start_time_seconds,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
@ -41,7 +43,9 @@ class MatchEventController extends Controller
|
|||||||
public function updateRound(Request $request, MatchRound $round)
|
public function updateRound(Request $request, MatchRound $round)
|
||||||
{
|
{
|
||||||
$request->validate([
|
$request->validate([
|
||||||
|
'round_number' => 'sometimes|integer|min:1',
|
||||||
'name' => 'required|string|max:50',
|
'name' => 'required|string|max:50',
|
||||||
|
'start_time_seconds' => 'nullable|integer|min:0',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Check if user owns the video
|
// Check if user owns the video
|
||||||
@ -50,7 +54,9 @@ class MatchEventController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
$round->update([
|
$round->update([
|
||||||
|
'round_number' => $request->round_number ?? $round->round_number,
|
||||||
'name' => $request->name,
|
'name' => $request->name,
|
||||||
|
'start_time_seconds' => $request->start_time_seconds,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
@ -93,16 +99,25 @@ class MatchEventController extends Controller
|
|||||||
return response()->json(['success' => false, 'message' => 'Unauthorized'], 403);
|
return response()->json(['success' => false, 'message' => 'Unauthorized'], 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate scores
|
// Get ALL previous points in this round (ordered by timestamp)
|
||||||
$previousPoints = MatchPoint::where('video_id', $video->id)
|
$previousPoints = MatchPoint::where('match_round_id', $request->round_id)
|
||||||
->where('match_round_id', $request->round_id)
|
|
||||||
->where('timestamp_seconds', '<', $request->timestamp_seconds)
|
->where('timestamp_seconds', '<', $request->timestamp_seconds)
|
||||||
->orderBy('timestamp_seconds', 'desc')
|
->orderBy('timestamp_seconds', 'asc')
|
||||||
->first();
|
->pluck('points', 'competitor')
|
||||||
|
->toArray();
|
||||||
|
|
||||||
$scoreBlue = $previousPoints ? $previousPoints->score_blue : 0;
|
// Calculate cumulative scores by summing each point value
|
||||||
$scoreRed = $previousPoints ? $previousPoints->score_red : 0;
|
$scoreBlue = 0;
|
||||||
|
$scoreRed = 0;
|
||||||
|
|
||||||
|
if (isset($previousPoints['blue'])) {
|
||||||
|
$scoreBlue += $previousPoints['blue'];
|
||||||
|
}
|
||||||
|
if (isset($previousPoints['red'])) {
|
||||||
|
$scoreRed += $previousPoints['red'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add current point
|
||||||
if ($request->competitor === 'blue') {
|
if ($request->competitor === 'blue') {
|
||||||
$scoreBlue += $request->points;
|
$scoreBlue += $request->points;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -18,7 +18,7 @@ class VideoController extends Controller
|
|||||||
{
|
{
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->middleware('auth')->except(['index', 'show', 'search', 'stream']);
|
$this->middleware('auth')->except(['index', 'show', 'search', 'stream', 'trending', 'shorts']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function index()
|
public function index()
|
||||||
|
|||||||
@ -15,6 +15,11 @@ class MatchRound extends Model
|
|||||||
'video_id',
|
'video_id',
|
||||||
'round_number',
|
'round_number',
|
||||||
'name',
|
'name',
|
||||||
|
'start_time_seconds',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'start_time_seconds' => 'integer',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function video(): BelongsTo
|
public function video(): BelongsTo
|
||||||
|
|||||||
@ -57,7 +57,7 @@ return [
|
|||||||
|
|
||||||
'url' => env('APP_URL', 'http://localhost'),
|
'url' => env('APP_URL', 'http://localhost'),
|
||||||
|
|
||||||
'asset_url' => env('ASSET_URL'),
|
'asset_url' => env('APP_URL'),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
@ -70,7 +70,7 @@ return [
|
|||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'timezone' => 'UTC',
|
'timezone' => 'Asia/Bahrain',
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|||||||
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('match_rounds', function (Blueprint $table) {
|
||||||
|
if (! Schema::hasColumn('match_rounds', 'start_time_seconds')) {
|
||||||
|
$table->integer('start_time_seconds')->nullable()->after('name');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('match_rounds', function (Blueprint $table) {
|
||||||
|
if (Schema::hasColumn('match_rounds', 'start_time_seconds')) {
|
||||||
|
$table->dropColumn('start_time_seconds');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
66
resources/views/components/channel-row.blade.php
Normal file
66
resources/views/components/channel-row.blade.php
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<div class="channel-row">
|
||||||
|
<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" alt="{{ $video->user->name }}">
|
||||||
|
@else
|
||||||
|
<div class="channel-avatar"></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>
|
||||||
|
</div>
|
||||||
|
<x-video-actions :video="$video" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Channel Row */
|
||||||
|
.channel-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 16px 0;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-avatar {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-name {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-subs {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive */
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.channel-row {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start !important;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-info {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
@props(['video'])
|
||||||
200
resources/views/components/video-actions.blade.php
Normal file
200
resources/views/components/video-actions.blade.php
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
@props(['video'])
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.action-btn {
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 14px;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
color: var(--text-primary);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn:hover {
|
||||||
|
background: var(--border-color);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn svg,
|
||||||
|
.action-btn i {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn.comment-btn {
|
||||||
|
background: var(--brand-red);
|
||||||
|
color: white;
|
||||||
|
border-color: var(--brand-red);
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn.liked {
|
||||||
|
color: var(--brand-red) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-action-dropdown .dropdown-item.liked {
|
||||||
|
color: var(--brand-red) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-action-dropdown {
|
||||||
|
display: none;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-action-dropdown .dropdown-menu {
|
||||||
|
right: 0;
|
||||||
|
left: auto;
|
||||||
|
min-width: 200px;
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 6px 0;
|
||||||
|
z-index: 1200;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-action-dropdown .dropdown-item {
|
||||||
|
color: var(--text-primary);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-action-dropdown .dropdown-item:hover {
|
||||||
|
background: var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.video-actions>.desktop-action {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-action-dropdown {
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="video-actions" style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap; overflow: visible;">
|
||||||
|
@auth
|
||||||
|
@if (Auth::id() === $video->user_id)
|
||||||
|
<button class="action-btn desktop-action" onclick="openEditVideoModal({{ $video->id }})">
|
||||||
|
<i class="bi bi-pencil"></i>
|
||||||
|
<span>Edit</span>
|
||||||
|
</button>
|
||||||
|
@elseif (Auth::id() !== $video->user_id)
|
||||||
|
<button class="action-btn desktop-action"><i class="bi bi-bell"></i><span>Subscribe</span></button>
|
||||||
|
@endif
|
||||||
|
@else
|
||||||
|
<button onclick="window.location.href='{{ route('login') }}'" class="action-btn desktop-action"><i
|
||||||
|
class="bi bi-bell"></i><span>Subscribe</span></button>
|
||||||
|
@endauth
|
||||||
|
|
||||||
|
@auth
|
||||||
|
<form method="POST"
|
||||||
|
action="{{ $video->isLikedBy(Auth::user()) ? route('videos.unlike', $video->id) : route('videos.like', $video->id) }}"
|
||||||
|
class="d-inline desktop-action">
|
||||||
|
@csrf
|
||||||
|
<button type="submit" class="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>
|
||||||
|
@else
|
||||||
|
<button onclick="window.location.href='{{ route('login') }}'" class="action-btn desktop-action">
|
||||||
|
<i class="bi bi-hand-thumbs-up"></i>
|
||||||
|
{{ $video->like_count > 0 ? number_format($video->like_count) : 'Like' }}
|
||||||
|
</button>
|
||||||
|
@endauth
|
||||||
|
|
||||||
|
@if ($video->isShareable())
|
||||||
|
<button class="action-btn desktop-action"
|
||||||
|
onclick="openShareModal('{{ $video->share_url }}', '{{ addslashes($video->title) }}')">
|
||||||
|
<i class="bi bi-share"></i> Share
|
||||||
|
</button>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<!-- Save to Playlist Button -->
|
||||||
|
<button class="action-btn desktop-action" onclick="openAddToPlaylistModal({{ $video->id }})">
|
||||||
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
||||||
|
stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"></path>
|
||||||
|
</svg>
|
||||||
|
<span>Save</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="dropdown mobile-action-dropdown">
|
||||||
|
<button class="action-btn dropdown-toggle" type="button" id="dropdownMenuLinkMusic{{ $video->id }}"
|
||||||
|
data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
<i class="bi bi-lightning-charge-fill"></i>
|
||||||
|
<span>Action</span>
|
||||||
|
</button>
|
||||||
|
<div class="dropdown-menu" aria-labelledby="dropdownMenuLinkMusic{{ $video->id }}">
|
||||||
|
@auth
|
||||||
|
@if (Auth::id() !== $video->user_id)
|
||||||
|
<button type="button" class="dropdown-item">
|
||||||
|
<i class="bi bi-bell"></i> Subscribe
|
||||||
|
</button>
|
||||||
|
@else
|
||||||
|
<button type="button" class="dropdown-item" onclick="openEditVideoModal({{ $video->id }})">
|
||||||
|
<i class="bi bi-pencil"></i> Edit
|
||||||
|
</button>
|
||||||
|
@endif
|
||||||
|
@else
|
||||||
|
<button class="dropdown-item" onclick="window.location.href='{{ route('login') }}'">
|
||||||
|
<i class="bi bi-bell"></i> Subscribe
|
||||||
|
</button>
|
||||||
|
@endauth
|
||||||
|
|
||||||
|
@auth
|
||||||
|
<form method="POST"
|
||||||
|
action="{{ $video->isLikedBy(Auth::user()) ? route('videos.unlike', $video->id) : route('videos.like', $video->id) }}"
|
||||||
|
class="d-inline w-100">
|
||||||
|
@csrf
|
||||||
|
<button type="submit" class="dropdown-item {{ $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>
|
||||||
|
@else
|
||||||
|
<button class="dropdown-item" onclick="window.location.href='{{ route('login') }}'">
|
||||||
|
<i class="bi bi-hand-thumbs-up"></i>
|
||||||
|
{{ $video->like_count > 0 ? number_format($video->like_count) : 'Like' }}
|
||||||
|
</button>
|
||||||
|
@endauth
|
||||||
|
|
||||||
|
@if ($video->isShareable())
|
||||||
|
<button class="dropdown-item"
|
||||||
|
onclick="openShareModal('{{ $video->share_url }}', '{{ addslashes($video->title) }}')">
|
||||||
|
<i class="bi bi-share"></i> Share
|
||||||
|
</button>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<button class="dropdown-item" onclick="openAddToPlaylistModal({{ $video->id }})">
|
||||||
|
<i class="bi bi-bookmark"></i> Save
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
150
resources/views/components/video-comments.blade.php
Normal file
150
resources/views/components/video-comments.blade.php
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
<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>
|
||||||
|
|
||||||
|
@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; display: flex; align-items: center; gap: 8px;">
|
||||||
|
<textarea id="commentBody{{ $video->id }}" class="form-control"
|
||||||
|
placeholder="Add a comment... Use @ to mention someone" rows="1"
|
||||||
|
style="background: transparent; border: none; border-bottom: 2px solid var(--border-color); color: var(--text-primary); border-radius: 0; padding: 12px 0 8px 0; flex: 1; margin: 0; height: 40px; font-size: 14px; outline: none; overflow: hidden;"></textarea>
|
||||||
|
<button type="button" class="action-btn comment-btn" onclick="submitComment({{ $video->id }})"
|
||||||
|
style="flex-shrink: 0;">
|
||||||
|
<span>Send</span>
|
||||||
|
</button>
|
||||||
|
</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
|
||||||
|
|
||||||
|
<div id="commentsList{{ $video->id }}">
|
||||||
|
@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' + videoId).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())
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error:', error);
|
||||||
|
alert('Failed to post comment: ' + error);
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
if (data & amp; & amp; data.success) {
|
||||||
|
document.getElementById('commentBody' + videoId).value = '';
|
||||||
|
addCommentToList(data.comment, videoId);
|
||||||
|
} else {
|
||||||
|
alert('Failed to post comment');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addCommentToList(comment, videoId) {
|
||||||
|
const commentsList = document.getElementById('commentsList' + videoId);
|
||||||
|
const commentHtml = `
|
||||||
|
<div class="comment-item" style="display: flex; gap: 12px; margin-bottom: 16px;" id="comment-${comment.id}">
|
||||||
|
<img src="${comment.user.avatar_url}" class="channel-avatar" style="width: 36px; height: 36px; flex-shrink: 0;" alt="${comment.user.name}">
|
||||||
|
<div style="flex: 1;">
|
||||||
|
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 4px;">
|
||||||
|
<span style="font-weight: 600; font-size: 14px;">${comment.user.name}</span>
|
||||||
|
<span style="color: var(--text-secondary); font-size: 12px;">just now</span>
|
||||||
|
</div>
|
||||||
|
<div class="comment-body" style="font-size: 14px; line-height: 1.5; word-wrap: break-word;">
|
||||||
|
${comment.body.replace(/@(\\\\w+)/g, '<span style="color: #3ea6ff; font-weight: 500;">@$1</span>')}
|
||||||
|
</div>
|
||||||
|
<div style="display: flex; gap: 12px; margin-top: 8px;">
|
||||||
|
<button onclick="toggleReplyForm(${comment.id})" style="background: none; border: none; color: var(--text-secondary); font-size: 12px; font-weight: 600; cursor: pointer; padding: 0;">
|
||||||
|
Reply
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
commentsList.insertAdjacentHTML('afterbegin', commentHtml);
|
||||||
|
const commentCount = commentsList.closest('.comments-section').querySelector('h3 span');
|
||||||
|
if (commentCount) {
|
||||||
|
const count = parseInt(commentCount.textContent.match(/\\((\\d+)\\)/)?.[2] || 0) + 1;
|
||||||
|
commentCount.textContent = `(${count})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteComment(commentId) {
|
||||||
|
if (confirm('Are you sure you want to delete this comment?')) {
|
||||||
|
fetch(`/comments/${commentId}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
document.getElementById('comment-' + commentId).remove();
|
||||||
|
// Update comment count for all comment sections (simplified)
|
||||||
|
document.querySelectorAll('.comments-section h3 span').forEach(span => {
|
||||||
|
const count = parseInt(span.textContent.match(/\\((\\d+)\\)/)?.[2] || 0) - 1;
|
||||||
|
span.textContent = `(${Math.max(0, count)})`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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>
|
||||||
@ -1,5 +1,6 @@
|
|||||||
<!-- Edit Video Modal - Cute Staged Pop-up -->
|
<!-- Edit Video Modal - Cute Staged Pop-up -->
|
||||||
<div class="modal fade" id="editVideoModal" tabindex="-1" aria-labelledby="editVideoModalLabel" aria-hidden="true" data-bs-backdrop="static">
|
<div class="modal fade" id="editVideoModal" tabindex="-1" aria-labelledby="editVideoModalLabel" aria-hidden="true"
|
||||||
|
data-bs-backdrop="static">
|
||||||
<div class="modal-dialog modal-dialog-centered modal-lg">
|
<div class="modal-dialog modal-dialog-centered modal-lg">
|
||||||
<div class="modal-content edit-modal-content">
|
<div class="modal-content edit-modal-content">
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
@ -13,7 +14,8 @@
|
|||||||
<span class="edit-subtitle">Update your video</span>
|
<span class="edit-subtitle">Update your video</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn-close btn-close-white" onclick="closeEditVideoModal()" aria-label="Close"></button>
|
<button type="button" class="btn-close btn-close-white" onclick="closeEditVideoModal()"
|
||||||
|
aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Body -->
|
<!-- Body -->
|
||||||
@ -58,7 +60,8 @@
|
|||||||
<i class="bi bi-image"></i> Current Thumbnail
|
<i class="bi bi-image"></i> Current Thumbnail
|
||||||
</label>
|
</label>
|
||||||
<div class="current-thumbnail-wrapper" id="current-thumbnail-wrapper">
|
<div class="current-thumbnail-wrapper" id="current-thumbnail-wrapper">
|
||||||
<img id="current-thumbnail-preview" src="" alt="Current thumbnail" class="current-thumbnail-img">
|
<img id="current-thumbnail-preview" src="" alt="Current thumbnail"
|
||||||
|
class="current-thumbnail-img">
|
||||||
<div class="thumbnail-placeholder" id="thumbnail-placeholder">
|
<div class="thumbnail-placeholder" id="thumbnail-placeholder">
|
||||||
<i class="bi bi-card-image"></i>
|
<i class="bi bi-card-image"></i>
|
||||||
<span>No thumbnail</span>
|
<span>No thumbnail</span>
|
||||||
@ -70,8 +73,7 @@
|
|||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
<i class="bi bi-card-heading"></i> Title *
|
<i class="bi bi-card-heading"></i> Title *
|
||||||
</label>
|
</label>
|
||||||
<input type="text" name="title" id="edit-video-title" required
|
<input type="text" name="title" id="edit-video-title" required class="form-input"
|
||||||
class="form-input"
|
|
||||||
placeholder="Give your video a catchy title">
|
placeholder="Give your video a catchy title">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -79,8 +81,7 @@
|
|||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
<i class="bi bi-text-paragraph"></i> Description
|
<i class="bi bi-text-paragraph"></i> Description
|
||||||
</label>
|
</label>
|
||||||
<textarea name="description" id="edit-video-description" rows="3"
|
<textarea name="description" id="edit-video-description" rows="3" class="form-textarea"
|
||||||
class="form-textarea"
|
|
||||||
placeholder="Tell viewers about your video"></textarea>
|
placeholder="Tell viewers about your video"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -144,7 +145,8 @@
|
|||||||
<p class="filename" id="edit-thumbnail-filename"></p>
|
<p class="filename" id="edit-thumbnail-filename"></p>
|
||||||
<p id="edit-thumbnail-filesize"></p>
|
<p id="edit-thumbnail-filesize"></p>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn-remove-file" onclick="removeEditThumbnail(event)">
|
<button type="button" class="btn-remove-file"
|
||||||
|
onclick="removeEditThumbnail(event)">
|
||||||
<i class="bi bi-x-lg"></i>
|
<i class="bi bi-x-lg"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -251,9 +253,11 @@
|
|||||||
transform: scale(0.9) translateY(10px);
|
transform: scale(0.9) translateY(10px);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
60% {
|
60% {
|
||||||
transform: scale(1.02) translateY(-5px);
|
transform: scale(1.02) translateY(-5px);
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
transform: scale(1) translateY(0);
|
transform: scale(1) translateY(0);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@ -413,6 +417,7 @@
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateX(20px);
|
transform: translateX(20px);
|
||||||
}
|
}
|
||||||
|
|
||||||
to {
|
to {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: translateX(0);
|
transform: translateX(0);
|
||||||
@ -494,7 +499,8 @@
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-input, .form-textarea {
|
.form-input,
|
||||||
|
.form-textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: #121212;
|
background: #121212;
|
||||||
border: 1px solid #333;
|
border: 1px solid #333;
|
||||||
@ -505,13 +511,15 @@
|
|||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-input:focus, .form-textarea:focus {
|
.form-input:focus,
|
||||||
|
.form-textarea:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: #e61e1e;
|
border-color: #e61e1e;
|
||||||
box-shadow: 0 0 0 3px rgba(230, 30, 30, 0.15);
|
box-shadow: 0 0 0 3px rgba(230, 30, 30, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-input::placeholder, .form-textarea::placeholder {
|
.form-input::placeholder,
|
||||||
|
.form-textarea::placeholder {
|
||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -613,8 +621,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes float {
|
@keyframes float {
|
||||||
0%, 100% { transform: translateY(0); }
|
|
||||||
50% { transform: translateY(-8px); }
|
0%,
|
||||||
|
100% {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: translateY(-8px);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropzone-title {
|
.dropzone-title {
|
||||||
@ -780,7 +795,9 @@
|
|||||||
border-top: 1px solid #2a2a2a;
|
border-top: 1px solid #2a2a2a;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-prev, .btn-next, .btn-save-changes {
|
.btn-prev,
|
||||||
|
.btn-next,
|
||||||
|
.btn-save-changes {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
@ -804,18 +821,21 @@
|
|||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-next, .btn-save-changes {
|
.btn-next,
|
||||||
|
.btn-save-changes {
|
||||||
background: linear-gradient(135deg, #e61e1e 0%, #ff4757 100%);
|
background: linear-gradient(135deg, #e61e1e 0%, #ff4757 100%);
|
||||||
color: white;
|
color: white;
|
||||||
box-shadow: 0 4px 15px rgba(230, 30, 30, 0.3);
|
box-shadow: 0 4px 15px rgba(230, 30, 30, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-next:hover, .btn-save-changes:hover {
|
.btn-next:hover,
|
||||||
|
.btn-save-changes:hover {
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
box-shadow: 0 6px 20px rgba(230, 30, 30, 0.4);
|
box-shadow: 0 6px 20px rgba(230, 30, 30, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-next:disabled, .btn-save-changes:disabled {
|
.btn-next:disabled,
|
||||||
|
.btn-save-changes:disabled {
|
||||||
background: #444;
|
background: #444;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
transform: none;
|
transform: none;
|
||||||
@ -872,7 +892,9 @@
|
|||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-prev, .btn-next, .btn-save-changes {
|
.btn-prev,
|
||||||
|
.btn-next,
|
||||||
|
.btn-save-changes {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
@ -880,13 +902,13 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
let currentEditStep = 1;
|
window.currentEditStep = window.currentEditStep ?? 1;
|
||||||
const totalEditSteps = 3;
|
window.totalEditSteps = window.totalEditSteps ?? 3;
|
||||||
let currentVideoId = null;
|
window.currentVideoId = window.currentVideoId ?? null;
|
||||||
|
|
||||||
// Open modal and load video data
|
// Open modal and load video data
|
||||||
function openEditVideoModal(videoId) {
|
function openEditVideoModal(videoId) {
|
||||||
currentVideoId = videoId;
|
window.currentVideoId = videoId;
|
||||||
|
|
||||||
// Fetch video data
|
// Fetch video data
|
||||||
fetch(`/videos/${videoId}/edit`, {
|
fetch(`/videos/${videoId}/edit`, {
|
||||||
@ -944,7 +966,7 @@ function openEditVideoModal(videoId) {
|
|||||||
document.getElementById('edit-status-message').className = 'status-message-modal';
|
document.getElementById('edit-status-message').className = 'status-message-modal';
|
||||||
|
|
||||||
// Reset step to 1
|
// Reset step to 1
|
||||||
currentEditStep = 1;
|
window.currentEditStep = 1;
|
||||||
updateEditStepDisplay();
|
updateEditStepDisplay();
|
||||||
|
|
||||||
// Update form action
|
// Update form action
|
||||||
@ -977,8 +999,10 @@ function closeEditVideoModal() {
|
|||||||
|
|
||||||
modalEl.addEventListener('hidden.bs.modal', function() {
|
modalEl.addEventListener('hidden.bs.modal', function() {
|
||||||
modalEl.classList.remove('show');
|
modalEl.classList.remove('show');
|
||||||
currentVideoId = null;
|
window.currentVideoId = null;
|
||||||
}, { once: true });
|
}, {
|
||||||
|
once: true
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step Navigation
|
// Step Navigation
|
||||||
@ -992,12 +1016,12 @@ function editNextStep(step) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
currentEditStep = step;
|
window.currentEditStep = step;
|
||||||
updateEditStepDisplay();
|
updateEditStepDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
function editPrevStep(step) {
|
function editPrevStep(step) {
|
||||||
currentEditStep = step;
|
window.currentEditStep = step;
|
||||||
updateEditStepDisplay();
|
updateEditStepDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1007,16 +1031,16 @@ function updateEditStepDisplay() {
|
|||||||
const stepNum = index + 1;
|
const stepNum = index + 1;
|
||||||
el.classList.remove('active', 'completed');
|
el.classList.remove('active', 'completed');
|
||||||
|
|
||||||
if (stepNum === currentEditStep) {
|
if (stepNum === window.currentEditStep) {
|
||||||
el.classList.add('active');
|
el.classList.add('active');
|
||||||
} else if (stepNum < currentEditStep) {
|
} else if (stepNum < window.currentEditStep) {
|
||||||
el.classList.add('completed');
|
el.classList.add('completed');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update step lines
|
// Update step lines
|
||||||
document.querySelectorAll('.step-line').forEach((el, index) => {
|
document.querySelectorAll('.step-line').forEach((el, index) => {
|
||||||
if (index < currentEditStep - 1) {
|
if (index < window.currentEditStep - 1) {
|
||||||
el.classList.add('active');
|
el.classList.add('active');
|
||||||
} else {
|
} else {
|
||||||
el.classList.remove('active');
|
el.classList.remove('active');
|
||||||
@ -1026,7 +1050,7 @@ function updateEditStepDisplay() {
|
|||||||
// Update step content
|
// Update step content
|
||||||
document.querySelectorAll('.edit-step-content').forEach(el => {
|
document.querySelectorAll('.edit-step-content').forEach(el => {
|
||||||
el.classList.remove('active');
|
el.classList.remove('active');
|
||||||
if (parseInt(el.dataset.step) === currentEditStep) {
|
if (parseInt(el.dataset.step) === window.currentEditStep) {
|
||||||
el.classList.add('active');
|
el.classList.add('active');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -1066,7 +1090,8 @@ function handleEditThumbnailSelect(input) {
|
|||||||
if (input.files && input.files[0]) {
|
if (input.files && input.files[0]) {
|
||||||
const file = input.files[0];
|
const file = input.files[0];
|
||||||
document.getElementById('edit-thumbnail-filename').textContent = file.name;
|
document.getElementById('edit-thumbnail-filename').textContent = file.name;
|
||||||
document.getElementById('edit-thumbnail-filesize').textContent = (file.size / 1024 / 1024).toFixed(2) + ' MB';
|
document.getElementById('edit-thumbnail-filesize').textContent = (file.size / 1024 / 1024).toFixed(2) +
|
||||||
|
' MB';
|
||||||
|
|
||||||
// Show thumbnail preview
|
// Show thumbnail preview
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
@ -1120,7 +1145,7 @@ document.getElementById('edit-video-form-modal').addEventListener('submit', func
|
|||||||
submitBtn.innerHTML = '<i class="bi bi-arrow-repeat"></i> Saving...';
|
submitBtn.innerHTML = '<i class="bi bi-arrow-repeat"></i> Saving...';
|
||||||
document.getElementById('edit-status-message').className = 'status-message-modal';
|
document.getElementById('edit-status-message').className = 'status-message-modal';
|
||||||
|
|
||||||
fetch(`/videos/${currentVideoId}`, {
|
fetch(`/videos/${window.currentVideoId}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: formData,
|
body: formData,
|
||||||
headers: {
|
headers: {
|
||||||
@ -1137,8 +1162,10 @@ document.getElementById('edit-video-form-modal').addEventListener('submit', func
|
|||||||
})
|
})
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
document.getElementById('edit-status-message').innerHTML = '<i class="bi bi-check-circle-fill"></i> ' + data.message;
|
document.getElementById('edit-status-message').innerHTML =
|
||||||
document.getElementById('edit-status-message').className = 'status-message-modal success';
|
'<i class="bi bi-check-circle-fill"></i> ' + data.message;
|
||||||
|
document.getElementById('edit-status-message').className =
|
||||||
|
'status-message-modal success';
|
||||||
|
|
||||||
// Close modal and reload page after short delay
|
// Close modal and reload page after short delay
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -1151,7 +1178,8 @@ document.getElementById('edit-video-form-modal').addEventListener('submit', func
|
|||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('Error:', error);
|
console.error('Error:', error);
|
||||||
document.getElementById('edit-status-message').innerHTML = '<i class="bi bi-exclamation-circle-fill"></i> ' + error.message;
|
document.getElementById('edit-status-message').innerHTML =
|
||||||
|
'<i class="bi bi-exclamation-circle-fill"></i> ' + error.message;
|
||||||
document.getElementById('edit-status-message').className = 'status-message-modal error';
|
document.getElementById('edit-status-message').className = 'status-message-modal error';
|
||||||
|
|
||||||
submitBtn.disabled = false;
|
submitBtn.disabled = false;
|
||||||
|
|||||||
@ -1,20 +1,43 @@
|
|||||||
<div class="comment-item" style="display: flex; gap: 12px; margin-bottom: 16px;" id="comment-{{ $comment->id }}">
|
<div class="comment-item" style="display: flex; gap: 12px; margin-bottom: 16px;" id="comment-{{ $comment->id }}">
|
||||||
<img src="{{ $comment->user->avatar_url }}" class="channel-avatar" style="width: 36px; height: 36px; flex-shrink: 0;" alt="{{ $comment->user->name }}">
|
<img src="{{ $comment->user->avatar_url }}" class="channel-avatar" style="width: 36px; height: 36px; flex-shrink: 0;"
|
||||||
|
alt="{{ $comment->user->name }}">
|
||||||
<div style="flex: 1;">
|
<div style="flex: 1;">
|
||||||
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 4px;">
|
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 4px;">
|
||||||
<span style="font-weight: 600; font-size: 14px;">{{ $comment->user->name }}</span>
|
<span style="font-weight: 600; font-size: 14px;">{{ $comment->user->name }}</span>
|
||||||
<span style="color: var(--text-secondary); font-size: 12px;">{{ $comment->created_at->diffForHumans() }}</span>
|
<span
|
||||||
|
style="color: var(--text-secondary); font-size: 12px;">{{ $comment->created_at->diffForHumans() }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="comment-body" style="font-size: 14px; line-height: 1.5; word-wrap: break-word;">
|
<div class="comment-body" style="font-size: 14px; line-height: 1.5; word-wrap: break-word;">
|
||||||
{{ $comment->body }}
|
{{ $comment->body }}
|
||||||
</div>
|
</div>
|
||||||
|
@auth
|
||||||
|
@if (Auth::id() === $comment->user_id)
|
||||||
|
<div id="commentEditWrap{{ $comment->id }}" style="display:none; margin-top:8px;">
|
||||||
|
<textarea id="commentEditInput{{ $comment->id }}" class="form-control" rows="3"
|
||||||
|
style="background: var(--bg-secondary); border: 1px solid var(--border-color); color: var(--text-primary); border-radius: 8px; padding: 8px; width: 100%; resize: vertical; font-size: 14px;">{{ $comment->body }}</textarea>
|
||||||
|
<div style="display:flex; gap:8px; justify-content:flex-end; margin-top:8px;">
|
||||||
|
<button type="button" class="yt-action-btn" style="font-size: 12px; padding: 6px 12px;"
|
||||||
|
onclick="cancelEditComment({{ $comment->id }})">Cancel</button>
|
||||||
|
<button type="button" class="yt-action-btn"
|
||||||
|
style="background: var(--brand-red); color: white; font-size: 12px; padding: 6px 12px;"
|
||||||
|
onclick="saveEditComment({{ $comment->id }})">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
@endauth
|
||||||
<div style="display: flex; gap: 12px; margin-top: 8px;">
|
<div style="display: flex; gap: 12px; margin-top: 8px;">
|
||||||
@auth
|
@auth
|
||||||
<button onclick="toggleReplyForm({{ $comment->id }})" style="background: none; border: none; color: var(--text-secondary); font-size: 12px; font-weight: 600; cursor: pointer; padding: 0;">
|
<button onclick="toggleReplyForm({{ $comment->id }})"
|
||||||
|
style="background: none; border: none; color: var(--text-secondary); font-size: 12px; font-weight: 600; cursor: pointer; padding: 0;">
|
||||||
Reply
|
Reply
|
||||||
</button>
|
</button>
|
||||||
@if (Auth::id() === $comment->user_id)
|
@if (Auth::id() === $comment->user_id)
|
||||||
<button onclick="deleteComment({{ $comment->id }})" style="background: none; border: none; color: var(--text-secondary); font-size: 12px; font-weight: 600; cursor: pointer; padding: 0;">
|
<button onclick="startEditComment({{ $comment->id }})"
|
||||||
|
style="background: none; border: none; color: var(--text-secondary); font-size: 12px; font-weight: 600; cursor: pointer; padding: 0;">
|
||||||
|
Edit
|
||||||
|
</button>
|
||||||
|
<button onclick="deleteComment({{ $comment->id }})"
|
||||||
|
style="background: none; border: none; color: var(--text-secondary); font-size: 12px; font-weight: 600; cursor: pointer; padding: 0;">
|
||||||
Delete
|
Delete
|
||||||
</button>
|
</button>
|
||||||
@endif
|
@endif
|
||||||
@ -24,22 +47,22 @@
|
|||||||
<!-- Reply Form -->
|
<!-- Reply Form -->
|
||||||
<div id="replyForm{{ $comment->id }}" style="display: none; margin-top: 12px;">
|
<div id="replyForm{{ $comment->id }}" style="display: none; margin-top: 12px;">
|
||||||
<div style="display: flex; gap: 8px;">
|
<div style="display: flex; gap: 8px;">
|
||||||
<textarea
|
<textarea class="form-control" placeholder="Write a reply..." rows="2"
|
||||||
class="form-control"
|
style="background: var(--bg-secondary); border: 1px solid var(--border-color); color: var(--text-primary); border-radius: 8px; padding: 8px; width: 100%; resize: none; font-size: 14px;"></textarea>
|
||||||
placeholder="Write a reply..."
|
|
||||||
rows="2"
|
|
||||||
style="background: var(--bg-secondary); border: 1px solid var(--border-color); color: var(--text-primary); border-radius: 8px; padding: 8px; width: 100%; resize: none; font-size: 14px;"
|
|
||||||
></textarea>
|
|
||||||
</div>
|
</div>
|
||||||
<div style="display: flex; gap: 8px; margin-top: 8px; justify-content: flex-end;">
|
<div style="display: flex; gap: 8px; margin-top: 8px; justify-content: flex-end;">
|
||||||
<button type="button" class="yt-action-btn" style="font-size: 12px; padding: 6px 12px;" onclick="toggleReplyForm({{ $comment->id }})">Cancel</button>
|
<button type="button" class="yt-action-btn" style="font-size: 12px; padding: 6px 12px;"
|
||||||
<button type="button" class="yt-action-btn" style="background: var(--brand-red); color: white; font-size: 12px; padding: 6px 12px;" onclick="submitReply({{ $video->id ?? $comment->video_id }}, {{ $comment->id }})">Reply</button>
|
onclick="toggleReplyForm({{ $comment->id }})">Cancel</button>
|
||||||
|
<button type="button" class="yt-action-btn"
|
||||||
|
style="background: var(--brand-red); color: white; font-size: 12px; padding: 6px 12px;"
|
||||||
|
onclick="submitReply({{ $video->id ?? $comment->video_id }}, {{ $comment->id }})">Reply</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Replies -->
|
<!-- Replies -->
|
||||||
@if ($comment->replies && $comment->replies->count() > 0)
|
@if ($comment->replies && $comment->replies->count() > 0)
|
||||||
<div style="margin-left: 24px; margin-top: 12px; border-left: 2px solid var(--border-color); padding-left: 12px;">
|
<div
|
||||||
|
style="margin-left: 24px; margin-top: 12px; border-left: 2px solid var(--border-color); padding-left: 12px;">
|
||||||
@foreach ($comment->replies as $reply)
|
@foreach ($comment->replies as $reply)
|
||||||
@include('videos.partials.comment', ['comment' => $reply, 'video' => $video ?? null])
|
@include('videos.partials.comment', ['comment' => $reply, 'video' => $video ?? null])
|
||||||
@endforeach
|
@endforeach
|
||||||
@ -53,4 +76,60 @@ function toggleReplyForm(commentId) {
|
|||||||
const form = document.getElementById('replyForm' + commentId);
|
const form = document.getElementById('replyForm' + commentId);
|
||||||
form.style.display = form.style.display === 'none' ? 'block' : 'none';
|
form.style.display = form.style.display === 'none' ? 'block' : 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function startEditComment(commentId) {
|
||||||
|
const wrap = document.getElementById('commentEditWrap' + commentId);
|
||||||
|
const body = document.querySelector('#comment-' + commentId + ' .comment-body');
|
||||||
|
const input = document.getElementById('commentEditInput' + commentId);
|
||||||
|
if (!wrap || !body || !input) return;
|
||||||
|
|
||||||
|
input.value = body.textContent.trim();
|
||||||
|
wrap.style.display = 'block';
|
||||||
|
body.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelEditComment(commentId) {
|
||||||
|
const wrap = document.getElementById('commentEditWrap' + commentId);
|
||||||
|
const body = document.querySelector('#comment-' + commentId + ' .comment-body');
|
||||||
|
if (wrap) wrap.style.display = 'none';
|
||||||
|
if (body) body.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveEditComment(commentId) {
|
||||||
|
const input = document.getElementById('commentEditInput' + commentId);
|
||||||
|
const bodyEl = document.querySelector('#comment-' + commentId + ' .comment-body');
|
||||||
|
const wrap = document.getElementById('commentEditWrap' + commentId);
|
||||||
|
if (!input || !bodyEl || !wrap) return;
|
||||||
|
|
||||||
|
const body = input.value.trim();
|
||||||
|
if (!body) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(`/comments/${commentId}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
body
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
if (res.ok) {
|
||||||
|
bodyEl.textContent = data.body || body;
|
||||||
|
bodyEl.dataset.timeEnhanced = '';
|
||||||
|
if (typeof enhanceCommentBodyWithTimeBadges === 'function') {
|
||||||
|
enhanceCommentBodyWithTimeBadges(document.getElementById('comment-' + commentId));
|
||||||
|
}
|
||||||
|
wrap.style.display = 'none';
|
||||||
|
bodyEl.style.display = 'block';
|
||||||
|
} else {
|
||||||
|
alert(data.error || 'Failed to update comment');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
alert('Failed to update comment: ' + e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
1116
resources/views/videos/types/backup/generic.blade.php
Normal file
1116
resources/views/videos/types/backup/generic.blade.php
Normal file
File diff suppressed because it is too large
Load Diff
4184
resources/views/videos/types/backup/match.blade.php
Normal file
4184
resources/views/videos/types/backup/match.blade.php
Normal file
File diff suppressed because it is too large
Load Diff
1069
resources/views/videos/types/backup/music.blade.php
Normal file
1069
resources/views/videos/types/backup/music.blade.php
Normal file
File diff suppressed because it is too large
Load Diff
@ -210,75 +210,6 @@
|
|||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-actions {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.yt-action-btn {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
padding: 8px 16px;
|
|
||||||
border-radius: 20px;
|
|
||||||
border: none;
|
|
||||||
background: var(--bg-secondary);
|
|
||||||
color: var(--text-primary);
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.yt-action-btn:hover {
|
|
||||||
background: var(--border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn,
|
|
||||||
.comments-section .action-btn {
|
|
||||||
border: none;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 8px 14px;
|
|
||||||
font-size: 0.82rem;
|
|
||||||
font-weight: 500;
|
|
||||||
cursor: pointer;
|
|
||||||
background: var(--bg-secondary);
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
color: var(--text-primary);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn:hover,
|
|
||||||
.comments-section .action-btn:hover {
|
|
||||||
background: var(--border-color);
|
|
||||||
transform: translateY(-1px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn:active,
|
|
||||||
.comments-section .action-btn:active {
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn svg,
|
|
||||||
.action-btn i,
|
|
||||||
.comments-section .action-btn svg,
|
|
||||||
.comments-section .action-btn i {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn.comment-btn {
|
|
||||||
background: var(--brand-red);
|
|
||||||
color: white;
|
|
||||||
border-color: var(--brand-red);
|
|
||||||
}
|
|
||||||
|
|
||||||
.yt-action-btn.liked {
|
|
||||||
color: var(--brand-red);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Channel Row */
|
/* Channel Row */
|
||||||
.channel-row {
|
.channel-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -310,18 +241,6 @@
|
|||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.subscribe-btn {
|
|
||||||
background: white;
|
|
||||||
color: black;
|
|
||||||
border: none;
|
|
||||||
padding: 8px 16px;
|
|
||||||
border-radius: 18px;
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 14px;
|
|
||||||
cursor: pointer;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Description */
|
/* Description */
|
||||||
.video-description {
|
.video-description {
|
||||||
background: var(--bg-secondary);
|
background: var(--bg-secondary);
|
||||||
@ -546,76 +465,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Channel Row - All in one line -->
|
<x-channel-row :video="$video" />
|
||||||
<div class="channel-row"
|
|
||||||
style="display: flex; align-items: center; justify-content: space-between; padding: 12px 0; flex-wrap: wrap; gap: 16px;">
|
|
||||||
<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: 40px; height: 40px; border-radius: 50%;" alt="{{ $video->user->name }}">
|
|
||||||
@else
|
|
||||||
<div class="channel-avatar" style="width: 40px; height: 40px; border-radius: 50%;"></div>
|
|
||||||
@endif
|
|
||||||
<div>
|
|
||||||
<div class="channel-name" style="font-weight: 600;">{{ $video->user->name ?? 'Unknown' }}</div>
|
|
||||||
<div class="channel-subs" style="font-size: 12px; color: var(--text-secondary);">
|
|
||||||
{{ number_format($video->user->subscriber_count ?? 0) }} subscribers</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="video-actions" style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap;">
|
|
||||||
@auth
|
|
||||||
@if (Auth::id() !== $video->user_id)
|
|
||||||
<button class="subscribe-btn">Subscribe</button>
|
|
||||||
@else
|
|
||||||
<button class="action-btn" onclick="openEditVideoModal({{ $video->id }})">
|
|
||||||
<i class="bi bi-pencil"></i>
|
|
||||||
<span>Edit</span>
|
|
||||||
</button>
|
|
||||||
@endif
|
|
||||||
@else
|
|
||||||
<a href="{{ route('login') }}" class="subscribe-btn">Subscribe</a>
|
|
||||||
@endauth
|
|
||||||
|
|
||||||
@auth
|
|
||||||
<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="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>
|
|
||||||
@else
|
|
||||||
<a href="{{ route('login') }}" class="yt-action-btn">
|
|
||||||
<i class="bi bi-hand-thumbs-up"></i> Like
|
|
||||||
</a>
|
|
||||||
@endauth
|
|
||||||
|
|
||||||
@if ($video->isShareable())
|
|
||||||
<button class="action-btn"
|
|
||||||
onclick="openShareModal('{{ $video->share_url }}', '{{ addslashes($video->title) }}')">
|
|
||||||
<i class="bi bi-share"></i> Share
|
|
||||||
</button>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
<!-- Save to Playlist Button -->
|
|
||||||
<button class="action-btn" onclick="openAddToPlaylistModal({{ $video->id }})">
|
|
||||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
||||||
<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"></path>
|
|
||||||
</svg>
|
|
||||||
<span>Save</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Description Box -->
|
<!-- Description Box -->
|
||||||
@if ($video->description)
|
@if ($video->description)
|
||||||
@ -700,16 +550,15 @@
|
|||||||
<img src="{{ Auth::user()->avatar_url }}" class="channel-avatar" style="width: 40px; height: 40px;"
|
<img src="{{ Auth::user()->avatar_url }}" class="channel-avatar" style="width: 40px; height: 40px;"
|
||||||
alt="{{ Auth::user()->name }}">
|
alt="{{ Auth::user()->name }}">
|
||||||
<div style="flex: 1; display: flex; align-items: center; gap: 8px;">
|
<div style="flex: 1; display: flex; align-items: center; gap: 8px;">
|
||||||
<textarea id="commentBody" class="form-control" placeholder="Add a comment... Use @ to mention someone"
|
<textarea id="commentBody" class="form-control" placeholder="Add a comment... Use @ to mention someone" rows="1"
|
||||||
rows="1"
|
|
||||||
style="background: transparent; border: none; border-bottom: 2px solid var(--border-color); color: var(--text-primary); border-radius: 0; padding: 12px 0 8px 0; flex: 1; margin: 0; height: 40px; font-size: 14px; outline: none; overflow: hidden;"></textarea>
|
style="background: transparent; border: none; border-bottom: 2px solid var(--border-color); color: var(--text-primary); border-radius: 0; padding: 12px 0 8px 0; flex: 1; margin: 0; height: 40px; font-size: 14px; outline: none; overflow: hidden;"></textarea>
|
||||||
<button type="button" class="action-btn"
|
<button type="button" class="action-btn"
|
||||||
onclick="document.getElementById('commentBody').value = ''" style="flex-shrink: 0;">
|
onclick="document.getElementById('commentBody').value = ''" style="flex-shrink: 0;">
|
||||||
<i class="bi bi-x-lg"></i>
|
<i class="bi bi-x-lg"></i>
|
||||||
<span>Cancel</span>
|
<span>Cancel</span>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="action-btn comment-btn"
|
<button type="button" class="action-btn comment-btn" onclick="submitComment({{ $video->id }})"
|
||||||
onclick="submitComment({{ $video->id }})" style="flex-shrink: 0;">
|
style="flex-shrink: 0;">
|
||||||
<i class="bi bi-chat-dots"></i>
|
<i class="bi bi-chat-dots"></i>
|
||||||
<span>Comment</span>
|
<span>Comment</span>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
3515
resources/views/videos/types/match.blade copy.php
Normal file
3515
resources/views/videos/types/match.blade copy.php
Normal file
File diff suppressed because it is too large
Load Diff
9
resources/views/videos/types/match.blade.backup.php
Normal file
9
resources/views/videos/types/match.blade.backup.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
@extends('layouts.app')
|
||||||
|
@section('title', ($video->title ?? 'Match Video') . ' | ' . config('app.name'))
|
||||||
|
|
||||||
|
@section('extra_styles')
|
||||||
|
<style>
|
||||||
|
/* ===== Video Section ===== */
|
||||||
|
.yt-video-section {
|
||||||
|
flex: 1;
|
||||||
|
min
|
||||||
File diff suppressed because it is too large
Load Diff
3615
resources/views/videos/types/match.blade.php.bak_20260316_025638
Normal file
3615
resources/views/videos/types/match.blade.php.bak_20260316_025638
Normal file
File diff suppressed because it is too large
Load Diff
3682
resources/views/videos/types/match.blade.pre_coach_review_backup.php
Normal file
3682
resources/views/videos/types/match.blade.pre_coach_review_backup.php
Normal file
File diff suppressed because it is too large
Load Diff
@ -210,81 +210,13 @@
|
|||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-actions {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.yt-action-btn {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
padding: 8px 16px;
|
|
||||||
border-radius: 20px;
|
|
||||||
border: none;
|
|
||||||
background: var(--bg-secondary);
|
|
||||||
color: var(--text-primary);
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.yt-action-btn:hover {
|
|
||||||
background: var(--border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn,
|
|
||||||
.comments-section .action-btn {
|
|
||||||
border: none;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 8px 14px;
|
|
||||||
font-size: 0.82rem;
|
|
||||||
font-weight: 500;
|
|
||||||
cursor: pointer;
|
|
||||||
background: var(--bg-secondary);
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
color: var(--text-primary);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn:hover,
|
|
||||||
.comments-section .action-btn:hover {
|
|
||||||
background: var(--border-color);
|
|
||||||
transform: translateY(-1px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn:active,
|
|
||||||
.comments-section .action-btn:active {
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn svg,
|
|
||||||
.action-btn i,
|
|
||||||
.comments-section .action-btn svg,
|
|
||||||
.comments-section .action-btn i {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn.comment-btn {
|
|
||||||
background: var(--brand-red);
|
|
||||||
color: white;
|
|
||||||
border-color: var(--brand-red);
|
|
||||||
}
|
|
||||||
|
|
||||||
.yt-action-btn.liked {
|
|
||||||
color: var(--brand-red);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Channel Row */
|
/* Channel Row */
|
||||||
.channel-row {
|
.channel-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 16px 0;
|
padding: 16px 0;
|
||||||
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.channel-info {
|
.channel-info {
|
||||||
@ -310,18 +242,6 @@
|
|||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.subscribe-btn {
|
|
||||||
background: white;
|
|
||||||
color: black;
|
|
||||||
border: none;
|
|
||||||
padding: 8px 16px;
|
|
||||||
border-radius: 18px;
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 14px;
|
|
||||||
cursor: pointer;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Description */
|
/* Description */
|
||||||
.video-description {
|
.video-description {
|
||||||
background: var(--bg-secondary);
|
background: var(--bg-secondary);
|
||||||
@ -432,8 +352,16 @@
|
|||||||
|
|
||||||
.video-actions {
|
.video-actions {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow-x: auto;
|
justify-content: flex-end;
|
||||||
justify-content: flex-start;
|
}
|
||||||
|
|
||||||
|
.video-actions>.desktop-action {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-action-dropdown {
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yt-main {
|
.yt-main {
|
||||||
@ -546,76 +474,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Channel Row - All in one line -->
|
<x-channel-row :video="$video" />
|
||||||
<div class="channel-row"
|
|
||||||
style="display: flex; align-items: center; justify-content: space-between; padding: 12px 0; flex-wrap: wrap; gap: 16px;">
|
|
||||||
<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: 40px; height: 40px; border-radius: 50%;" alt="{{ $video->user->name }}">
|
|
||||||
@else
|
|
||||||
<div class="channel-avatar" style="width: 40px; height: 40px; border-radius: 50%;"></div>
|
|
||||||
@endif
|
|
||||||
<div>
|
|
||||||
<div class="channel-name" style="font-weight: 600;">{{ $video->user->name ?? 'Unknown' }}</div>
|
|
||||||
<div class="channel-subs" style="font-size: 12px; color: var(--text-secondary);">
|
|
||||||
{{ number_format($video->user->subscriber_count ?? 0) }} subscribers</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="video-actions" style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap;">
|
|
||||||
@auth
|
|
||||||
@if (Auth::id() !== $video->user_id)
|
|
||||||
<button class="subscribe-btn">Subscribe</button>
|
|
||||||
@else
|
|
||||||
<button class="action-btn" onclick="openEditVideoModal({{ $video->id }})">
|
|
||||||
<i class="bi bi-pencil"></i>
|
|
||||||
<span>Edit</span>
|
|
||||||
</button>
|
|
||||||
@endif
|
|
||||||
@else
|
|
||||||
<a href="{{ route('login') }}" class="subscribe-btn">Subscribe</a>
|
|
||||||
@endauth
|
|
||||||
|
|
||||||
@auth
|
|
||||||
<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="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>
|
|
||||||
@else
|
|
||||||
<a href="{{ route('login') }}" class="yt-action-btn">
|
|
||||||
<i class="bi bi-hand-thumbs-up"></i> Like
|
|
||||||
</a>
|
|
||||||
@endauth
|
|
||||||
|
|
||||||
@if ($video->isShareable())
|
|
||||||
<button class="action-btn"
|
|
||||||
onclick="openShareModal('{{ $video->share_url }}', '{{ addslashes($video->title) }}')">
|
|
||||||
<i class="bi bi-share"></i> Share
|
|
||||||
</button>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
<!-- Save to Playlist Button -->
|
|
||||||
<button class="action-btn" onclick="openAddToPlaylistModal({{ $video->id }})">
|
|
||||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
||||||
<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"></path>
|
|
||||||
</svg>
|
|
||||||
<span>Save</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Description Box -->
|
<!-- Description Box -->
|
||||||
@if ($video->description)
|
@if ($video->description)
|
||||||
@ -687,49 +546,10 @@
|
|||||||
</style>
|
</style>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<!-- Comment Section -->
|
<x-video-comments :video="$video" />
|
||||||
<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>
|
|
||||||
|
|
||||||
@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; display: flex; align-items: center; gap: 8px;">
|
|
||||||
<textarea id="commentBody" class="form-control" placeholder="Add a comment... Use @ to mention someone"
|
|
||||||
rows="1"
|
|
||||||
style="background: transparent; border: none; border-bottom: 2px solid var(--border-color); color: var(--text-primary); border-radius: 0; padding: 12px 0 8px 0; flex: 1; margin: 0; height: 40px; font-size: 14px; outline: none; overflow: hidden;"></textarea>
|
|
||||||
<button type="button" class="action-btn"
|
|
||||||
onclick="document.getElementById('commentBody').value = ''" style="flex-shrink: 0;">
|
|
||||||
<i class="bi bi-x-lg"></i>
|
|
||||||
<span>Cancel</span>
|
|
||||||
</button>
|
|
||||||
<button type="button" class="action-btn comment-btn"
|
|
||||||
onclick="submitComment({{ $video->id }})" style="flex-shrink: 0;">
|
|
||||||
<i class="bi bi-chat-dots"></i>
|
|
||||||
<span>Comment</span>
|
|
||||||
</button>
|
|
||||||
</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
|
|
||||||
|
|
||||||
<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>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -872,8 +692,7 @@
|
|||||||
alt="{{ $playlistVideo->title }}">
|
alt="{{ $playlistVideo->title }}">
|
||||||
@endif
|
@endif
|
||||||
@if ($playlistVideo->duration)
|
@if ($playlistVideo->duration)
|
||||||
<span
|
<span class="yt-video-duration">{{ gmdate('i:s', $playlistVideo->duration) }}</span>
|
||||||
class="yt-video-duration">{{ gmdate('i:s', $playlistVideo->duration) }}</span>
|
|
||||||
@endif
|
@endif
|
||||||
@if ($playlistVideo->is_shorts)
|
@if ($playlistVideo->is_shorts)
|
||||||
<span class="yt-shorts-badge"
|
<span class="yt-shorts-badge"
|
||||||
|
|||||||
@ -133,5 +133,3 @@ Route::delete('/points/{point}', [MatchEventController::class, 'destroyPoint'])-
|
|||||||
Route::post('/videos/{video}/reviews', [MatchEventController::class, 'storeReview'])->name('match.storeReview');
|
Route::post('/videos/{video}/reviews', [MatchEventController::class, 'storeReview'])->name('match.storeReview');
|
||||||
Route::put('/reviews/{review}', [MatchEventController::class, 'updateReview'])->name('match.updateReview');
|
Route::put('/reviews/{review}', [MatchEventController::class, 'updateReview'])->name('match.updateReview');
|
||||||
Route::delete('/reviews/{review}', [MatchEventController::class, 'destroyReview'])->name('match.destroyReview');
|
Route::delete('/reviews/{review}', [MatchEventController::class, 'destroyReview'])->name('match.destroyReview');
|
||||||
|
|
||||||
// });
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user