Add sports-match type, device tracking, profile visits, and share refactor
- New SportsMatch model/controller and sports UI components/modal - Move share-modal to a reusable x-share-modal/x-share-button component - Add VideoSharedWithUser notification and share-to-members flow - Device/user-agent tracking on views, downloads, share accesses - ProfileVisit model + migration; subscription source tracking - Email thumbnail support; remove stale TODO files
This commit is contained in:
1 parent
6aae6f86b6
commit
73527f3781
61 files changed
+4014
-636
No files matched your search
@@ -791,6 +791,31 @@
|
||||
box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2);
|
||||
}
|
||||
|
||||
.delete-otp-input {
|
||||
width: 100%;
|
||||
background: #1a1a1a;
|
||||
border: 1px solid rgba(239, 68, 68, 0.5);
|
||||
border-radius: 8px;
|
||||
color: #fff;
|
||||
padding: 12px 16px;
|
||||
font-size: 22px;
|
||||
letter-spacing: 0.4em;
|
||||
text-align: center;
|
||||
text-indent: 0.4em;
|
||||
font-family: 'SFMono-Regular', Menlo, Monaco, Consolas, monospace;
|
||||
box-sizing: border-box;
|
||||
transition: border-color .15s ease, box-shadow .15s ease;
|
||||
}
|
||||
.delete-otp-input::placeholder {
|
||||
color: rgba(255, 255, 255, 0.35);
|
||||
letter-spacing: 0.4em;
|
||||
}
|
||||
.delete-otp-input:focus {
|
||||
outline: none;
|
||||
border-color: #ef4444;
|
||||
box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.18);
|
||||
}
|
||||
|
||||
/* ── Playlist controls bar (sidebar, all video types) ── */
|
||||
.pl-controls-bar {
|
||||
display: flex;
|
||||
@@ -1021,6 +1046,11 @@
|
||||
</style>
|
||||
|
||||
@yield('extra_styles')
|
||||
|
||||
{{-- Device fingerprint: computes a stable per-device hash on first visit,
|
||||
caches it in localStorage + the `_fp` cookie so the server can dedupe guests
|
||||
across IP/VPN/country changes. Loaded async — never blocks paint. --}}
|
||||
<script src="{{ asset('fp.js') }}" async></script>
|
||||
</head>
|
||||
<body class="{{ $bodyClass ?? '' }} {{ session('impersonator_id') ? 'has-impersonate-bar' : '' }}">
|
||||
<!-- Header -->
|
||||
@@ -1065,13 +1095,14 @@
|
||||
@auth
|
||||
@include('layouts.partials.upload-modal')
|
||||
@include('layouts.partials.edit-video-modal')
|
||||
@include('layouts.partials.sports-match-modal')
|
||||
@endauth
|
||||
|
||||
<!-- Add to Playlist Modal - Available for all users (shows login prompt if not authenticated) -->
|
||||
@include('layouts.partials.add-to-playlist-modal')
|
||||
|
||||
<!-- Share Modal - Available on all pages -->
|
||||
@include('layouts.partials.share-modal')
|
||||
<x-share-modal />
|
||||
|
||||
<!-- Delete Video Modal -->
|
||||
@auth
|
||||
@@ -1118,8 +1149,7 @@
|
||||
</label>
|
||||
<input type="text" id="deleteOtpInput" inputmode="numeric" pattern="[0-9]*"
|
||||
maxlength="6" autocomplete="one-time-code"
|
||||
class="form-input"
|
||||
style="letter-spacing: 0.3em; font-size: 22px; text-align: center;"
|
||||
class="delete-otp-input"
|
||||
placeholder="000000">
|
||||
</div>
|
||||
@endif
|
||||
@@ -1426,6 +1456,7 @@
|
||||
case 'new_user': return '🎉 ' + actor + ' just joined TAKEONE!';
|
||||
case 'new_subscriber': return '🔔 ' + actor + ' subscribed to your channel';
|
||||
case 'video_like': return '❤️ ' + actor + ' liked your video ' + title;
|
||||
case 'video_shared': return '📤 ' + actor + ' shared a video with you: ' + title + (d.message ? ' <em class="yt-notif-preview">"' + escHtml(d.message) + '"</em>' : '');
|
||||
case 'new_post': return '📝 ' + actor + ' posted something new';
|
||||
default: return actor + ' uploaded a new video: ' + title;
|
||||
}
|
||||
@@ -2110,5 +2141,34 @@
|
||||
})();
|
||||
</script>
|
||||
|
||||
{{-- Profile-visit tracking: any link with data-profile-visit-url fires a beacon on click --}}
|
||||
<script>
|
||||
(function () {
|
||||
var _csrf = document.querySelector('meta[name="csrf-token"]')?.content || '';
|
||||
document.addEventListener('click', function (e) {
|
||||
var a = e.target.closest('[data-profile-visit-url]');
|
||||
if (!a) return;
|
||||
var url = a.getAttribute('data-profile-visit-url');
|
||||
var src = a.getAttribute('data-source-video-id') || '';
|
||||
if (!url) return;
|
||||
try {
|
||||
var body = new URLSearchParams({ source_video_id: src }).toString();
|
||||
if (navigator.sendBeacon) {
|
||||
var blob = new Blob([body + '&_token=' + encodeURIComponent(_csrf)], { type: 'application/x-www-form-urlencoded' });
|
||||
navigator.sendBeacon(url, blob);
|
||||
} else {
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'X-CSRF-TOKEN': _csrf, 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' },
|
||||
body: body,
|
||||
credentials: 'same-origin',
|
||||
keepalive: true,
|
||||
}).catch(function(){});
|
||||
}
|
||||
} catch (err) {}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user