gfsd
This commit is contained in:
+54
-8
@@ -27,6 +27,8 @@ class CommentSystem {
|
||||
if (this.displayMode === 1) this.sort = 'old';
|
||||
|
||||
this.customEmojis = CommentSystem.emojiCache || {};
|
||||
this._selfPostedCommentIds = new Set();
|
||||
this._pendingSelfCommentTexts = new Set();
|
||||
|
||||
this.icons = {
|
||||
reply: `<i class="fa-solid fa-reply"></i>`,
|
||||
@@ -472,6 +474,34 @@ class CommentSystem {
|
||||
}
|
||||
}
|
||||
|
||||
_isSelfComment(data) {
|
||||
if (!data) return false;
|
||||
if (data.id && this._selfPostedCommentIds?.has(data.id)) return true;
|
||||
if (data.body && this._pendingSelfCommentTexts?.has(data.body)) return true;
|
||||
|
||||
const session = window.f0ckSession || {};
|
||||
const currentUserId = session.id || session.user_id;
|
||||
if (currentUserId && data.user_id && parseInt(data.user_id, 10) === parseInt(currentUserId, 10)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const currentUsername = session.user || this.user;
|
||||
if (currentUsername && data.username && currentUsername.toLowerCase() === data.username.toLowerCase()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (session.is_anon && data.is_anon) {
|
||||
if (data.anon_fingerprint && session.fingerprint && data.anon_fingerprint === session.fingerprint) {
|
||||
return true;
|
||||
}
|
||||
if (data.anon_short_fingerprint && session.fingerprint && session.fingerprint.includes(data.anon_short_fingerprint)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
handleLiveComment(data) {
|
||||
if (!this.container || !this.itemId) return;
|
||||
// 1. Check if comment belongs to this item
|
||||
@@ -537,14 +567,20 @@ class CommentSystem {
|
||||
|
||||
// Danmaku: fire one-shot for other users' comments.
|
||||
// Own comment is handled by the optimistic submit path (fire + addItem).
|
||||
// The _loadDanmaku re-render will add this comment to the items rotation.
|
||||
const currentUser = window.f0ckSession?.user;
|
||||
if (window.danmakuInstance && data.username !== currentUser) {
|
||||
const isSelf = this._isSelfComment(data);
|
||||
if (window.danmakuInstance && !isSelf) {
|
||||
window.danmakuInstance.fire(
|
||||
data.body,
|
||||
data.display_name || data.username || '?',
|
||||
data.username_color || null
|
||||
);
|
||||
window.danmakuInstance.addItem({
|
||||
id: data.id,
|
||||
content: data.body,
|
||||
video_time: data.video_time ?? null,
|
||||
display_name: data.display_name || data.username || '?',
|
||||
username_color: data.username_color || null
|
||||
});
|
||||
}
|
||||
|
||||
// Update backlinks for live comment
|
||||
@@ -3448,6 +3484,7 @@ class CommentSystem {
|
||||
} else {
|
||||
this.isMainSubmitting = true;
|
||||
}
|
||||
this._pendingSelfCommentTexts.add(text);
|
||||
|
||||
let retryCount = 0;
|
||||
const maxRetries = 20; // Allow several minutes of retrying during restart
|
||||
@@ -3534,7 +3571,7 @@ class CommentSystem {
|
||||
// For 4xx errors, we stop and show the error to user (likely validation or auth)
|
||||
const json = await res.json().catch(() => ({}));
|
||||
alert('Error: ' + (json.message || `Status ${res.status}`));
|
||||
this._finishSubmit(submitBtn, originalBtnHtml, parentId);
|
||||
this._finishSubmit(submitBtn, originalBtnHtml, parentId, text);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3692,6 +3729,11 @@ class CommentSystem {
|
||||
poll: null
|
||||
};
|
||||
|
||||
if (json.comment?.id) {
|
||||
this._selfPostedCommentIds.add(json.comment.id);
|
||||
}
|
||||
this._pendingSelfCommentTexts.delete(text);
|
||||
|
||||
// Danmaku: fire immediately (one-shot) + add to future rotation
|
||||
if (window.danmakuInstance) {
|
||||
window.danmakuInstance.fire(
|
||||
@@ -3700,6 +3742,7 @@ class CommentSystem {
|
||||
session.username_color || null
|
||||
);
|
||||
window.danmakuInstance.addItem({
|
||||
id: newComment.id,
|
||||
content: text,
|
||||
video_time: newComment.video_time ?? null,
|
||||
display_name: session.display_name || currentUsername || '?',
|
||||
@@ -3810,10 +3853,10 @@ class CommentSystem {
|
||||
}
|
||||
|
||||
this._silentSync();
|
||||
this._finishSubmit(submitBtn, originalBtnHtml, parentId);
|
||||
this._finishSubmit(submitBtn, originalBtnHtml, parentId, text);
|
||||
} else {
|
||||
alert('Error: ' + json.message);
|
||||
this._finishSubmit(submitBtn, originalBtnHtml, parentId);
|
||||
this._finishSubmit(submitBtn, originalBtnHtml, parentId, text);
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(`[CommentSystem] Submit attempt ${retryCount + 1} failed:`, err);
|
||||
@@ -3825,7 +3868,7 @@ class CommentSystem {
|
||||
setTimeout(attemptSubmit, delay);
|
||||
} else {
|
||||
alert('Failed to send comment after multiple attempts. Please check your connection.');
|
||||
this._finishSubmit(submitBtn, originalBtnHtml, parentId);
|
||||
this._finishSubmit(submitBtn, originalBtnHtml, parentId, text);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -3857,7 +3900,10 @@ class CommentSystem {
|
||||
console.log('[ensureBtn] done, button in DOM:', !!contentEl.querySelector('.load-full-comment-btn'));
|
||||
}
|
||||
|
||||
_finishSubmit(btn, originalHtml, parentId) {
|
||||
_finishSubmit(btn, originalHtml, parentId, submittedText = null) {
|
||||
if (submittedText) {
|
||||
this._pendingSelfCommentTexts?.delete(submittedText);
|
||||
}
|
||||
if (parentId) {
|
||||
this.pendingSubmissions.delete(parentId);
|
||||
} else {
|
||||
|
||||
@@ -195,6 +195,7 @@ class Danmaku {
|
||||
const mapped = comments
|
||||
.filter(c => !c.is_deleted && c.content)
|
||||
.map(c => ({
|
||||
id: c.id || null,
|
||||
text: this._prepareText(c.content),
|
||||
username: c.display_name || c.username || '?',
|
||||
color: c.username_color || null,
|
||||
@@ -288,6 +289,7 @@ class Danmaku {
|
||||
*/
|
||||
addItem(comment) {
|
||||
if (!comment || !comment.content) return;
|
||||
if (comment.id && this.items && this.items.some(i => i.id === comment.id)) return;
|
||||
|
||||
const duration = this.media.duration;
|
||||
const hasDuration = isFinite(duration) && duration > 0;
|
||||
@@ -307,6 +309,7 @@ class Danmaku {
|
||||
}
|
||||
|
||||
const item = {
|
||||
id: comment.id || null,
|
||||
video_time: t,
|
||||
text: this._prepareText(comment.content),
|
||||
username: comment.display_name || comment.username || '?',
|
||||
@@ -314,6 +317,13 @@ class Danmaku {
|
||||
fired: true // mark as already fired — caller handles any immediate one-shot
|
||||
};
|
||||
|
||||
if (this._synthClock && this._flashPool) {
|
||||
if (!this._flashPool.some(i => (comment.id && i.id === comment.id) || (i.text === item.text && i.username === item.username))) {
|
||||
this._flashPool.push(item);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Insert in sorted order
|
||||
const idx = this.items.findIndex(i => i.video_time > t);
|
||||
if (idx === -1) this.items.push(item);
|
||||
|
||||
+2255
-150
File diff suppressed because it is too large
Load Diff
@@ -532,14 +532,32 @@
|
||||
else if (rClass === 'sfw' && blurSfw) isBlurred = true;
|
||||
else if (rClass === 'untagged' && blurUntagged) isBlurred = true;
|
||||
|
||||
let thumbUrl = `/t/${c.item_id}.webp`;
|
||||
const mime = c.mime || c.item_mime || '';
|
||||
const activeDest = c.item_dest || c.dest || '';
|
||||
const isAudio = mime.startsWith('audio/') || (!mime && activeDest.match(/\.(mp3|wav|ogg|flac|m4a|aac|opus)$/i));
|
||||
|
||||
let thumbUrl = c.thumb || `/t/${c.item_id}.webp`;
|
||||
if (isBlurred) {
|
||||
thumbUrl = `/t/${c.item_id}_blur.webp`;
|
||||
const baseThumb = thumbUrl.replace(/\.webp$/, '');
|
||||
thumbUrl = `${baseThumb}_blur.webp`;
|
||||
}
|
||||
|
||||
if (window.applyThumbCacheBust) thumbUrl = window.applyThumbCacheBust(thumbUrl);
|
||||
|
||||
mediaHtml = `<img src="${thumbUrl}" style="width: 32px; height: 32px; object-fit: cover; border-radius: 2px;" loading="lazy" onerror="this.style.display='none'" />`;
|
||||
if (isAudio && !c.has_coverart) {
|
||||
mediaHtml = `
|
||||
<div class="sidebar-media-placeholder audio">
|
||||
<i class="fa-solid fa-music"></i>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
mediaHtml = `
|
||||
<img src="${thumbUrl}" style="width: 32px; height: 32px; object-fit: cover; border-radius: 2px;" loading="lazy" onerror="this.style.display='none'; if(this.nextElementSibling) this.nextElementSibling.classList.remove('hidden');" />
|
||||
<div class="sidebar-media-placeholder ${isAudio ? 'audio' : ''} hidden">
|
||||
<i class="${isAudio ? 'fa-solid fa-music' : 'fa-solid fa-image'}"></i>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
itemPreview = `
|
||||
<div class="item-preview">
|
||||
@@ -1869,6 +1887,7 @@
|
||||
}
|
||||
}
|
||||
};
|
||||
window.switchSidebarTab = switchSidebarTab;
|
||||
|
||||
const initSidebarTabs = () => {
|
||||
const tabsContainer = document.querySelector('.sidebar-tabs');
|
||||
|
||||
+39
-8
@@ -88,6 +88,8 @@ const updateHoverStates = () => {
|
||||
const isOver = mouseX >= rect.left && mouseX <= rect.right &&
|
||||
mouseY >= rect.top && mouseY <= rect.bottom;
|
||||
p.classList.toggle("v0ck_hover", isOver);
|
||||
const gal = p.closest('.album-gallery-container');
|
||||
if (gal) gal.classList.toggle("v0ck_hover", isOver);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -117,6 +119,8 @@ class v0ck {
|
||||
if (mouseX >= rect.left && mouseX <= rect.right &&
|
||||
mouseY >= rect.top && mouseY <= rect.bottom) {
|
||||
parent.classList.add("v0ck_hover", "v0ck_no_transition");
|
||||
const gal = parent.closest('.album-gallery-container');
|
||||
if (gal) gal.classList.add("v0ck_hover");
|
||||
// Remove no-transition after a frame
|
||||
setTimeout(() => parent.classList.remove("v0ck_no_transition"), 50);
|
||||
}
|
||||
@@ -142,14 +146,24 @@ class v0ck {
|
||||
if (!ph) {
|
||||
ph = document.createElement('div');
|
||||
ph.className = 'sidebar-media-placeholder audio';
|
||||
ph.innerHTML = '<div class="audio-cover-circle"></div><i class="fa-solid fa-music"></i>';
|
||||
ph.innerHTML = '<div class="audio-cover-circle"><i class="fa-solid fa-music"></i></div>';
|
||||
player.prepend(ph);
|
||||
}
|
||||
let coverCircle = ph.querySelector('.audio-cover-circle');
|
||||
if (!coverCircle) {
|
||||
coverCircle = document.createElement('div');
|
||||
coverCircle.className = 'audio-cover-circle';
|
||||
coverCircle.innerHTML = '<i class="fa-solid fa-music"></i>';
|
||||
ph.insertBefore(coverCircle, ph.firstChild);
|
||||
} else {
|
||||
if (!coverCircle.querySelector('i')) {
|
||||
const existingI = ph.querySelector(':scope > i');
|
||||
if (existingI) {
|
||||
coverCircle.appendChild(existingI);
|
||||
} else {
|
||||
coverCircle.insertAdjacentHTML('beforeend', '<i class="fa-solid fa-music"></i>');
|
||||
}
|
||||
}
|
||||
}
|
||||
player.style.backgroundImage = 'none';
|
||||
player.style.backgroundColor = 'transparent';
|
||||
@@ -157,7 +171,7 @@ class v0ck {
|
||||
coverCircle.style.backgroundImage = `url('${poster}')`;
|
||||
ph.classList.add('has-cover');
|
||||
} else {
|
||||
coverCircle.style.backgroundImage = 'none';
|
||||
coverCircle.style.backgroundImage = '';
|
||||
ph.classList.remove('has-cover');
|
||||
}
|
||||
}
|
||||
@@ -203,6 +217,23 @@ class v0ck {
|
||||
let wasPausedWhenStarted = false;
|
||||
// Mobile tap-to-show-controls: true when this touch revealed the controls bar
|
||||
let controlsJustShown = false;
|
||||
|
||||
const setHover = (active) => {
|
||||
if (active) {
|
||||
player.classList.add('v0ck_hover');
|
||||
const gal = player.closest('.album-gallery-container');
|
||||
if (gal) gal.classList.add('v0ck_hover');
|
||||
} else {
|
||||
player.classList.remove('v0ck_hover');
|
||||
const gal = player.closest('.album-gallery-container');
|
||||
if (gal) {
|
||||
gal.classList.remove('v0ck_hover');
|
||||
gal.classList.remove('strip-peek');
|
||||
const strip = gal.querySelector('.album-thumbnails-strip');
|
||||
if (strip) strip.classList.remove('strip-peek');
|
||||
}
|
||||
}
|
||||
};
|
||||
const speedIndicator = player.querySelector('.v0ck_speed_indicator');
|
||||
|
||||
// (mouse position is now tracked via docMouseX/docMouseY in resetControlsTimer block)
|
||||
@@ -343,7 +374,7 @@ class v0ck {
|
||||
if (isMobile && controlsJustShown) {
|
||||
// First tap: controls were just revealed by this touch — don't toggle play
|
||||
controlsJustShown = false;
|
||||
player.classList.add('v0ck_hover');
|
||||
setHover(true);
|
||||
return;
|
||||
}
|
||||
controlsJustShown = false;
|
||||
@@ -353,7 +384,7 @@ class v0ck {
|
||||
toggle.addEventListener('click', togglePlay);
|
||||
overlay.addEventListener('click', e => {
|
||||
e.stopPropagation();
|
||||
player.classList.add('v0ck_hover');
|
||||
setHover(true);
|
||||
togglePlay();
|
||||
});
|
||||
video.addEventListener('play', updatePlayIcon);
|
||||
@@ -831,7 +862,7 @@ class v0ck {
|
||||
}
|
||||
|
||||
if (isMobile && !isInsidePlayer && !isFlashYankUI) {
|
||||
player.classList.remove('v0ck_hover');
|
||||
setHover(false);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -928,7 +959,7 @@ class v0ck {
|
||||
const isFullscreen = player.classList.contains('v0ck_fullscreen');
|
||||
if (!video.paused || isFullscreen) {
|
||||
controlsTimer = setTimeout(() => {
|
||||
player.classList.remove('v0ck_hover');
|
||||
setHover(false);
|
||||
if (settingsMenu && !settingsMenu.classList.contains('v0ck_hidden')) {
|
||||
settingsMenu.classList.add('v0ck_hidden');
|
||||
document.dispatchEvent(new CustomEvent('v0ck_settings_closed'));
|
||||
@@ -950,7 +981,7 @@ class v0ck {
|
||||
docMouseX = e.clientX;
|
||||
docMouseY = e.clientY;
|
||||
}
|
||||
player.classList.add('v0ck_hover');
|
||||
setHover(true);
|
||||
resetControlsTimer();
|
||||
}
|
||||
|
||||
@@ -998,7 +1029,7 @@ class v0ck {
|
||||
}
|
||||
docMouseX = -1;
|
||||
docMouseY = -1;
|
||||
player.classList.remove('v0ck_hover');
|
||||
setHover(false);
|
||||
clearTimeout(controlsTimer);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user