Track ring loop completions with +N badge and show mod badge for shop owners
When a viewer watches every item in the ring, the loop counter increments, history resets (un-dims all items), and a +1/+2/+3 badge appears next to the progress display. Shop owners/editors get a green "mod" badge. Ring state: ringLoops persisted in localStorage alongside existing ring state. Server: watch_json now returns is_mod flag. Templates pass data-watch-is-mod.
This commit is contained in:
parent
740001f09b
commit
98efbfe566
5 changed files with 76 additions and 4 deletions
|
|
@ -2767,6 +2767,24 @@ textarea {
|
|||
color: var(--text-muted, #999);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.ring-loops-badge {
|
||||
font-size: 0.75em;
|
||||
font-weight: bold;
|
||||
padding: 1px 6px;
|
||||
border-radius: 10px;
|
||||
background-color: var(--blue-color, #5871ad);
|
||||
color: white;
|
||||
}
|
||||
.ring-mod-badge {
|
||||
font-size: 0.65em;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
padding: 1px 5px;
|
||||
border-radius: 10px;
|
||||
background-color: #48bb78;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.watch-direction-label {
|
||||
display: grid;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
var ringPosition = 0;
|
||||
var ringDirection = 1; // 1 = forward, -1 = reverse
|
||||
var ringHistory = {}; // { productId: true } — set of watched IDs
|
||||
var ringLoops = 0; // completed full circuits of the ring
|
||||
|
||||
// Load ring state from localStorage
|
||||
try {
|
||||
|
|
@ -42,6 +43,10 @@
|
|||
var savedHistory = localStorage.getItem('watchRingHistory');
|
||||
if (savedHistory) ringHistory = JSON.parse(savedHistory);
|
||||
} catch (e) {}
|
||||
try {
|
||||
var savedLoops = localStorage.getItem('watchRingLoops');
|
||||
if (savedLoops !== null) ringLoops = parseInt(savedLoops, 10) || 0;
|
||||
} catch (e) {}
|
||||
|
||||
function saveRingState() {
|
||||
try {
|
||||
|
|
@ -49,12 +54,31 @@
|
|||
localStorage.setItem('watchRingPosition', String(ringPosition));
|
||||
localStorage.setItem('watchRingDirection', String(ringDirection));
|
||||
localStorage.setItem('watchRingHistory', JSON.stringify(ringHistory));
|
||||
localStorage.setItem('watchRingLoops', String(ringLoops));
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
function markWatched(productId) {
|
||||
if (!productId) return;
|
||||
ringHistory[productId] = true;
|
||||
|
||||
// Check for complete loop — all ring items watched
|
||||
if (ringProductIds.length > 0) {
|
||||
var allWatched = true;
|
||||
for (var i = 0; i < ringProductIds.length; i++) {
|
||||
if (!ringHistory[ringProductIds[i]]) {
|
||||
allWatched = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allWatched) {
|
||||
ringLoops++;
|
||||
ringHistory = {};
|
||||
// Current item starts the new loop as already watched
|
||||
ringHistory[productId] = true;
|
||||
}
|
||||
}
|
||||
|
||||
saveRingState();
|
||||
updateProgressDisplay();
|
||||
}
|
||||
|
|
@ -73,7 +97,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Update progress display ("23 / 87")
|
||||
// Update progress display ("23 / 87") and loop badge
|
||||
function updateProgressDisplay() {
|
||||
var el = document.getElementById('ring-progress');
|
||||
if (!el) return;
|
||||
|
|
@ -86,6 +110,17 @@
|
|||
if (ringProductIds.indexOf(id) !== -1) watched++;
|
||||
}
|
||||
el.textContent = watched + ' / ' + ringProductIds.length;
|
||||
|
||||
// Loop badge
|
||||
var badge = document.getElementById('ring-loops-badge');
|
||||
if (badge) {
|
||||
if (ringLoops > 0) {
|
||||
badge.textContent = '+' + ringLoops;
|
||||
badge.style.display = '';
|
||||
} else {
|
||||
badge.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Autoplay preference (persisted in localStorage)
|
||||
|
|
@ -119,12 +154,18 @@
|
|||
if (savedQueue) queue = JSON.parse(savedQueue);
|
||||
} catch (e) {}
|
||||
|
||||
// Mod status (shop owner/editor)
|
||||
var isMod = false;
|
||||
|
||||
// Mark current item as watched and sync ring position
|
||||
var initialProductEl = document.querySelector('[data-watch-product-id]');
|
||||
if (initialProductEl) {
|
||||
var initialId = initialProductEl.getAttribute('data-watch-product-id');
|
||||
markWatched(initialId);
|
||||
syncRingPosition(initialId);
|
||||
if (initialProductEl.getAttribute('data-watch-is-mod') === '1') {
|
||||
isMod = true;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Autoplay with sound (for video/audio) ---
|
||||
|
|
@ -448,10 +489,11 @@
|
|||
|
||||
// --- Update page content (title, description, related, URL) ---
|
||||
function updatePageContent(data) {
|
||||
// Update ring from server response
|
||||
// Update ring and mod status from server response
|
||||
if (data.ring && data.ring.length) {
|
||||
ringProductIds = data.ring;
|
||||
}
|
||||
if (data.is_mod) isMod = true;
|
||||
markWatched(data.product_id);
|
||||
syncRingPosition(data.product_id);
|
||||
|
||||
|
|
@ -492,9 +534,15 @@
|
|||
|
||||
var autoplayChecked = autoplayEnabled ? ' checked' : '';
|
||||
var directionChecked = ringDirection === -1 ? ' checked' : '';
|
||||
var modBadge = isMod ? '<span class="ring-mod-badge">mod</span>' : '';
|
||||
var loopsBadge = ringLoops > 0
|
||||
? '<span id="ring-loops-badge" class="ring-loops-badge">+' + ringLoops + '</span>'
|
||||
: '<span id="ring-loops-badge" class="ring-loops-badge" style="display:none"></span>';
|
||||
var headerHtml = '<div class="related-content-header">'
|
||||
+ '<h3>Up Next</h3>'
|
||||
+ modBadge
|
||||
+ '<span id="ring-progress" class="ring-progress"></span>'
|
||||
+ loopsBadge
|
||||
+ '<label class="watch-autoplay-label js-only">'
|
||||
+ '<span class="autoplay-label-text">Autoplay</span>'
|
||||
+ '<input type="checkbox" id="watch-autoplay-toggle"' + autoplayChecked + ' />'
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
|
||||
<section class="two-column{% if request.shop.watch_mode_enabled %} watch-mode{% endif %}">
|
||||
|
||||
{% if request.shop.watch_mode_enabled %}<div class="watch-left" data-watch-product-id="{{ product.id }}">{% endif %}
|
||||
{% if request.shop.watch_mode_enabled %}<div class="watch-left" data-watch-product-id="{{ product.id }}"{% if request.user.can_edit_shop(request.shop) %} data-watch-is-mod="1"{% endif %}>{% endif %}
|
||||
|
||||
<div class="product-images">
|
||||
{% set video_extensions = ["mp4", "avi", "mov", "wmv", "flv", "mkv", "webm", "m4v"] %}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
<section class="two-column{% if request.shop.watch_mode_enabled %} watch-mode{% endif %}">
|
||||
|
||||
{% if request.shop.watch_mode_enabled %}<div class="watch-left" data-watch-product-id="{{ product.id }}">{% endif %}
|
||||
{% if request.shop.watch_mode_enabled %}<div class="watch-left" data-watch-product-id="{{ product.id }}"{% if request.user.can_edit_shop(request.shop) %} data-watch-is-mod="1"{% endif %}>{% endif %}
|
||||
|
||||
<div class="product-images">
|
||||
<h1>{{ product.title }}<br/><span class="subtitle-text">sold by <a href="{{ product.shop.absolute_about_url(request) }}" rel="nofollow" class="shop-theme-link-color">{{ product.shop.name }}</a></span></h1>
|
||||
|
|
|
|||
|
|
@ -109,6 +109,11 @@ def watch_json(request):
|
|||
"media_type": r_media,
|
||||
})
|
||||
|
||||
# Check if current user is a shop editor (mod)
|
||||
is_mod = False
|
||||
if request.user and request.user.authenticated:
|
||||
is_mod = request.user.can_edit_shop(request.shop)
|
||||
|
||||
return {
|
||||
"product_id": str(product.id),
|
||||
"title": product.title,
|
||||
|
|
@ -120,6 +125,7 @@ def watch_json(request):
|
|||
"is_sellable": product.is_sellable,
|
||||
"related": related_data,
|
||||
"ring": ring,
|
||||
"is_mod": is_mod,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue