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:
ghassan committed 2026-05-29 01:50:28 +03:00
1 parent 6aae6f86b6
commit 73527f3781
61 files changed
+4014 -636

No files matched your search

+63 -3
View File
@@ -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 '&#x1F389; ' + actor + ' just joined TAKEONE!';
case 'new_subscriber': return '&#x1F514; ' + actor + ' subscribed to your channel';
case 'video_like': return '&#x2764;&#xFE0F; ' + actor + ' liked your video ' + title;
case 'video_shared': return '&#x1F4E4; ' + actor + ' shared a video with you: ' + title + (d.message ? ' <em class="yt-notif-preview">"' + escHtml(d.message) + '"</em>' : '');
case 'new_post': return '&#x1F4DD; ' + 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>