Merge mobile-view-match-highlights: frame-accurate scrubbing, mobile UX, collapsed action menu
This commit is contained in:
commit
ed921a4ccf
@ -18,7 +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',
|
'start_time_seconds' => 'nullable|numeric|min:0',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Check if user owns the video
|
// Check if user owns the video
|
||||||
@ -45,7 +45,7 @@ class MatchEventController extends Controller
|
|||||||
$request->validate([
|
$request->validate([
|
||||||
'round_number' => 'sometimes|integer|min:1',
|
'round_number' => 'sometimes|integer|min:1',
|
||||||
'name' => 'required|string|max:50',
|
'name' => 'required|string|max:50',
|
||||||
'start_time_seconds' => 'nullable|integer|min:0',
|
'start_time_seconds' => 'nullable|numeric|min:0',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Check if user owns the video
|
// Check if user owns the video
|
||||||
@ -87,7 +87,7 @@ class MatchEventController extends Controller
|
|||||||
{
|
{
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'round_id' => 'required|exists:match_rounds,id',
|
'round_id' => 'required|exists:match_rounds,id',
|
||||||
'timestamp_seconds' => 'required|integer|min:0',
|
'timestamp_seconds' => 'required|numeric|min:0',
|
||||||
'action' => 'required|string|max:255',
|
'action' => 'required|string|max:255',
|
||||||
'points' => 'required|integer|min:1',
|
'points' => 'required|integer|min:1',
|
||||||
'competitor' => 'required|in:blue,red',
|
'competitor' => 'required|in:blue,red',
|
||||||
@ -150,7 +150,7 @@ class MatchEventController extends Controller
|
|||||||
public function updatePoint(Request $request, MatchPoint $point)
|
public function updatePoint(Request $request, MatchPoint $point)
|
||||||
{
|
{
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'timestamp_seconds' => 'required|integer|min:0',
|
'timestamp_seconds' => 'required|numeric|min:0',
|
||||||
'action' => 'required|string|max:255',
|
'action' => 'required|string|max:255',
|
||||||
'points' => 'required|integer|min:1',
|
'points' => 'required|integer|min:1',
|
||||||
'competitor' => 'required|in:blue,red',
|
'competitor' => 'required|in:blue,red',
|
||||||
@ -202,8 +202,8 @@ class MatchEventController extends Controller
|
|||||||
public function storeReview(Request $request, Video $video)
|
public function storeReview(Request $request, Video $video)
|
||||||
{
|
{
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'start_time_seconds' => 'required|integer|min:0',
|
'start_time_seconds' => 'required|numeric|min:0',
|
||||||
'end_time_seconds' => 'nullable|integer|min:0',
|
'end_time_seconds' => 'nullable|numeric|min:0',
|
||||||
'note' => 'required|string|max:1000',
|
'note' => 'required|string|max:1000',
|
||||||
'coach_name' => 'required|string|max:100',
|
'coach_name' => 'required|string|max:100',
|
||||||
'emoji' => 'nullable|string|max:10',
|
'emoji' => 'nullable|string|max:10',
|
||||||
@ -238,8 +238,8 @@ class MatchEventController extends Controller
|
|||||||
public function updateReview(Request $request, CoachReview $review)
|
public function updateReview(Request $request, CoachReview $review)
|
||||||
{
|
{
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'start_time_seconds' => 'required|integer|min:0',
|
'start_time_seconds' => 'required|numeric|min:0',
|
||||||
'end_time_seconds' => 'nullable|integer|min:0',
|
'end_time_seconds' => 'nullable|numeric|min:0',
|
||||||
'note' => 'required|string|max:1000',
|
'note' => 'required|string|max:1000',
|
||||||
'coach_name' => 'required|string|max:100',
|
'coach_name' => 'required|string|max:100',
|
||||||
'emoji' => 'nullable|string|max:10',
|
'emoji' => 'nullable|string|max:10',
|
||||||
|
|||||||
@ -25,6 +25,8 @@ class CoachReview extends Model
|
|||||||
protected $casts = [
|
protected $casts = [
|
||||||
'position_x' => 'float',
|
'position_x' => 'float',
|
||||||
'position_y' => 'float',
|
'position_y' => 'float',
|
||||||
|
'start_time_seconds' => 'float',
|
||||||
|
'end_time_seconds' => 'float',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function video(): BelongsTo
|
public function video(): BelongsTo
|
||||||
|
|||||||
@ -22,6 +22,10 @@ class MatchPoint extends Model
|
|||||||
'score_red',
|
'score_red',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'timestamp_seconds' => 'float',
|
||||||
|
];
|
||||||
|
|
||||||
public function video(): BelongsTo
|
public function video(): BelongsTo
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Video::class);
|
return $this->belongsTo(Video::class);
|
||||||
|
|||||||
@ -19,7 +19,7 @@ class MatchRound extends Model
|
|||||||
];
|
];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'start_time_seconds' => 'integer',
|
'start_time_seconds' => 'float',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function video(): BelongsTo
|
public function video(): BelongsTo
|
||||||
|
|||||||
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Frame-accurate timestamps: the match highlights scrubber lands on
|
||||||
|
* frame boundaries (1/fps seconds). Storing these as INTEGER seconds
|
||||||
|
* throws away sub-second precision, so we promote the columns to
|
||||||
|
* decimal(10,3) — 3 decimals cover up to 1000 fps and centuries of
|
||||||
|
* runtime, and existing integer values migrate losslessly.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
// MatchRound.start_time_seconds is used to jump to a round's opening
|
||||||
|
// frame too, so bring it along.
|
||||||
|
Schema::table('match_rounds', function (Blueprint $table) {
|
||||||
|
$table->decimal('start_time_seconds', 10, 3)->nullable()->change();
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('match_points', function (Blueprint $table) {
|
||||||
|
$table->decimal('timestamp_seconds', 10, 3)->change();
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('coach_reviews', function (Blueprint $table) {
|
||||||
|
$table->decimal('start_time_seconds', 10, 3)->change();
|
||||||
|
$table->decimal('end_time_seconds', 10, 3)->nullable()->change();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
// Round back to integer on rollback — sub-second data would be
|
||||||
|
// truncated but nothing catastrophic happens.
|
||||||
|
Schema::table('match_rounds', function (Blueprint $table) {
|
||||||
|
$table->integer('start_time_seconds')->nullable()->change();
|
||||||
|
});
|
||||||
|
Schema::table('match_points', function (Blueprint $table) {
|
||||||
|
$table->integer('timestamp_seconds')->change();
|
||||||
|
});
|
||||||
|
Schema::table('coach_reviews', function (Blueprint $table) {
|
||||||
|
$table->integer('start_time_seconds')->change();
|
||||||
|
$table->integer('end_time_seconds')->nullable()->change();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -71,10 +71,14 @@
|
|||||||
color: var(--brand-red) !important;
|
color: var(--brand-red) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Single Action dropdown for every viewport — the individual
|
||||||
|
.desktop-action buttons are collapsed into this one menu. */
|
||||||
.mobile-action-dropdown {
|
.mobile-action-dropdown {
|
||||||
display: none;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
.video-actions > .desktop-action { display: none !important; }
|
||||||
|
|
||||||
.mobile-action-dropdown .dropdown-menu {
|
.mobile-action-dropdown .dropdown-menu {
|
||||||
right: 0;
|
right: 0;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user