fdsafs
This commit is contained in:
+229
-13
@@ -7486,6 +7486,112 @@ a.removetag i {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* Tag Badge & Hover-to-Exclude Action */
|
||||
.tag-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
transition: background-color 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease, opacity 0.2s ease;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.tag-badge a.tag-name {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
|
||||
.tag-badge .tag-exclude-btn {
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.8em;
|
||||
line-height: 1;
|
||||
vertical-align: middle;
|
||||
border-radius: 50%;
|
||||
outline: none;
|
||||
transition: all 0.22s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
.tag-badge:not(.tag-is-excluded) .tag-exclude-btn {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
max-width: 0;
|
||||
margin-left: 0;
|
||||
overflow: hidden;
|
||||
transform: scale(0.5);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.tag-badge:hover .tag-exclude-btn,
|
||||
.tag-badge:focus-within .tag-exclude-btn {
|
||||
opacity: 0.85;
|
||||
width: 14px;
|
||||
max-width: 14px;
|
||||
margin-left: 5px;
|
||||
overflow: visible;
|
||||
transform: scale(1);
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (hover: none) {
|
||||
.tag-badge .tag-exclude-btn {
|
||||
opacity: 0.7;
|
||||
width: 14px;
|
||||
max-width: 14px;
|
||||
margin-left: 4px;
|
||||
overflow: visible;
|
||||
pointer-events: auto;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.tag-badge .tag-exclude-btn:hover,
|
||||
.tag-badge .tag-exclude-btn:focus-visible {
|
||||
opacity: 1;
|
||||
color: #ff4757 !important;
|
||||
transform: scale(1.28);
|
||||
filter: drop-shadow(0 0 6px rgba(255, 71, 87, 0.75));
|
||||
}
|
||||
|
||||
/* Excluded Tag State */
|
||||
.tag-badge.tag-is-excluded {
|
||||
opacity: 0.75;
|
||||
box-shadow: inset 0 0 0 1px rgba(255, 71, 87, 0.55), 0 0 8px rgba(255, 71, 87, 0.2);
|
||||
background-color: rgba(255, 71, 87, 0.15) !important;
|
||||
}
|
||||
|
||||
.tag-badge.tag-is-excluded a.tag-name {
|
||||
text-decoration: line-through;
|
||||
text-decoration-color: rgba(255, 71, 87, 0.8);
|
||||
text-decoration-thickness: 1.5px;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.tag-badge.tag-is-excluded .tag-exclude-btn {
|
||||
opacity: 0.95;
|
||||
width: 14px;
|
||||
max-width: 14px;
|
||||
margin-left: 5px;
|
||||
overflow: visible;
|
||||
pointer-events: auto;
|
||||
transform: scale(1);
|
||||
color: #ff4757;
|
||||
}
|
||||
|
||||
.tag-badge.tag-is-excluded .tag-exclude-btn:hover {
|
||||
color: #2ed573 !important;
|
||||
filter: drop-shadow(0 0 6px rgba(46, 213, 115, 0.75));
|
||||
}
|
||||
|
||||
.badge-greentext {
|
||||
color: #789922;
|
||||
text-shadow: inherit !important;
|
||||
@@ -10106,6 +10212,26 @@ input#s_avatar {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.nav-mime-item.locked {
|
||||
cursor: not-allowed !important;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.nav-mime-item.locked:hover {
|
||||
background: transparent !important;
|
||||
color: #ccc !important;
|
||||
}
|
||||
|
||||
.nav-mime-item.locked input[type="checkbox"] {
|
||||
cursor: not-allowed !important;
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
.filter-pill.locked {
|
||||
cursor: not-allowed !important;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.nav-mime-divider {
|
||||
height: 1px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
@@ -11715,23 +11841,19 @@ html[theme="f0ck95d"] .badge-dark {
|
||||
vertical-align: middle;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: color 0.15s ease, transform 0.12s cubic-bezier(0.2, 0, 0, 1), opacity 0.15s ease;
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
|
||||
.steuerung a:hover,
|
||||
.steuerung button:hover {
|
||||
color: var(--accent);
|
||||
opacity: 0.85;
|
||||
transform: translateY(-1px);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.steuerung a:active,
|
||||
.steuerung button:active,
|
||||
.steuerung a.is-clicked,
|
||||
.steuerung button.is-clicked {
|
||||
transform: scale(0.92) translateY(1px);
|
||||
opacity: 0.65;
|
||||
transition-duration: 0.05s;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.steuerung.steuerung-icon {
|
||||
@@ -11745,7 +11867,7 @@ html[theme="f0ck95d"] .badge-dark {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 1.6em;
|
||||
transition: color 0.15s ease, transform 0.12s cubic-bezier(0.2, 0, 0, 1), opacity 0.15s ease;
|
||||
transition: opacity 0.15s ease;
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
@@ -11755,17 +11877,14 @@ html[theme="f0ck95d"] .badge-dark {
|
||||
|
||||
.steuerung.steuerung-icon a:hover,
|
||||
.steuerung.steuerung-icon button:hover {
|
||||
color: var(--accent);
|
||||
transform: translateY(-1px);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.steuerung.steuerung-icon a:active,
|
||||
.steuerung.steuerung-icon button:active,
|
||||
.steuerung.steuerung-icon a.is-clicked,
|
||||
.steuerung.steuerung-icon button.is-clicked {
|
||||
transform: scale(0.88) translateY(1px);
|
||||
opacity: 0.65;
|
||||
transition-duration: 0.05s;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
html[theme='light'] .steuerung a,
|
||||
@@ -12503,6 +12622,11 @@ input:checked+.slider:before {
|
||||
grid-column: 1;
|
||||
}
|
||||
|
||||
.rating-selector.locked {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.rating-toggle-btn {
|
||||
display: inline-block;
|
||||
background: transparent;
|
||||
@@ -12528,6 +12652,12 @@ input:checked+.slider:before {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.rating-toggle-btn.locked {
|
||||
cursor: not-allowed !important;
|
||||
pointer-events: none !important;
|
||||
opacity: 0.85 !important;
|
||||
}
|
||||
|
||||
/* Per-rating active colours */
|
||||
.rating-toggle-btn.active[data-rating="sfw"] {
|
||||
background: #2e7d32;
|
||||
@@ -15670,6 +15800,41 @@ span.gchat-post-card--loading {
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
/* Excluded Tags Filter Modal Section */
|
||||
.excluded-tags-section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.excluded-tags-section-title {
|
||||
font-size: 1.05em;
|
||||
font-weight: 600;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
letter-spacing: 0.5px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
}
|
||||
|
||||
.excluded-tags-section-title i {
|
||||
color: #ff4757;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.excluded-tags-count-badge {
|
||||
background: #ff4757;
|
||||
color: #fff;
|
||||
font-size: 0.72em;
|
||||
font-weight: 700;
|
||||
padding: 2px 7px;
|
||||
border-radius: 12px;
|
||||
line-height: 1.2;
|
||||
box-shadow: 0 2px 6px rgba(255, 71, 87, 0.4);
|
||||
}
|
||||
|
||||
#nav_excluded_tags_list {
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
@@ -15677,6 +15842,57 @@ span.gchat-post-card--loading {
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
max-width: 100%;
|
||||
min-height: 28px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.excluded-tag-chip {
|
||||
padding: 5px 12px;
|
||||
border-radius: 20px;
|
||||
background: rgba(255, 71, 87, 0.12);
|
||||
border: 1px solid rgba(255, 71, 87, 0.35);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 0.9em;
|
||||
color: #f1f2f6;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
|
||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.excluded-tag-chip:hover {
|
||||
background: rgba(255, 71, 87, 0.2);
|
||||
border-color: rgba(255, 71, 87, 0.55);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 10px rgba(255, 71, 87, 0.25);
|
||||
}
|
||||
|
||||
.excluded-tag-chip .remove-excluded-tag {
|
||||
color: #ff4757;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
font-size: 1.15em;
|
||||
line-height: 1;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 50%;
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.excluded-tag-chip .remove-excluded-tag:hover {
|
||||
color: #fff;
|
||||
background: #ff4757;
|
||||
transform: scale(1.18);
|
||||
}
|
||||
|
||||
.no-excluded-tags-hint {
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
font-size: 0.9em;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.nav-exclude {
|
||||
|
||||
+53
-35
@@ -99,7 +99,7 @@
|
||||
if (canManage) {
|
||||
span.classList.add('can-cycle');
|
||||
}
|
||||
} else if (tag.display_name || tag.user) {
|
||||
} else if (!window.f0ckSession?.is_anonymized && window.f0ckSession?.logged_in && !window.f0ckSession?.is_anon && (tag.display_name || tag.user)) {
|
||||
span.setAttribute('tooltip', tag.display_name || tag.user);
|
||||
}
|
||||
|
||||
@@ -226,6 +226,13 @@
|
||||
|
||||
|
||||
const toggleFavEvent = async (e) => {
|
||||
if (e && typeof e.preventDefault === 'function') e.preventDefault();
|
||||
if (window.f0ckSession?.is_anon && window.f0ckSession?.anon_permissions && window.f0ckSession.anon_permissions.favorite === false) {
|
||||
if (typeof window.flashMessage === 'function') {
|
||||
window.flashMessage('Anonymous favoriting is disabled.', 3000, 'error');
|
||||
}
|
||||
return;
|
||||
}
|
||||
const ctx = getContext();
|
||||
if (!ctx) return;
|
||||
const { postid } = ctx;
|
||||
@@ -234,46 +241,57 @@
|
||||
const favoBtn = document.querySelector("#a_favo");
|
||||
const wasAlreadyFav = favoBtn && favoBtn.classList.contains('fa-solid');
|
||||
|
||||
const res = await post('/api/v2/togglefav', {
|
||||
postid: postid
|
||||
});
|
||||
if (res.success) {
|
||||
if (window.invalidateItemCache) {
|
||||
window.invalidateItemCache(postid);
|
||||
}
|
||||
// New state is the logical opposite of what it was before the API call
|
||||
const isNowFav = !wasAlreadyFav;
|
||||
try {
|
||||
const res = await post('/api/v2/togglefav', {
|
||||
postid: postid
|
||||
});
|
||||
if (res && res.success) {
|
||||
if (window.invalidateItemCache) {
|
||||
window.invalidateItemCache(postid);
|
||||
}
|
||||
// New state is the logical opposite of what it was before the API call
|
||||
const isNowFav = !wasAlreadyFav;
|
||||
|
||||
if (favoBtn) {
|
||||
favoBtn.classList.toggle('fa-solid', isNowFav);
|
||||
favoBtn.classList.toggle('fa-regular', !isNowFav);
|
||||
}
|
||||
if (favoBtn) {
|
||||
favoBtn.classList.toggle('fa-solid', isNowFav);
|
||||
favoBtn.classList.toggle('fa-regular', !isNowFav);
|
||||
}
|
||||
|
||||
const favcontainer = document.querySelector('#favs');
|
||||
favcontainer.innerHTML = "";
|
||||
if (res.favs.length > 0) {
|
||||
res.favs.forEach(f => {
|
||||
const a = document.createElement('a');
|
||||
a.href = `/user/${f.user}`;
|
||||
a.setAttribute('tooltip', f.display_name || f.user);
|
||||
a.setAttribute('flow', 'up');
|
||||
const favcontainer = document.querySelector('#favs');
|
||||
favcontainer.innerHTML = "";
|
||||
if (res.favs && res.favs.length > 0) {
|
||||
res.favs.forEach(f => {
|
||||
const a = document.createElement('a');
|
||||
a.href = `/user/${f.user}`;
|
||||
a.setAttribute('tooltip', f.display_name || f.user);
|
||||
a.setAttribute('flow', 'up');
|
||||
|
||||
const img = document.createElement('img');
|
||||
img.src = f.avatar_file ? `/a/${f.avatar_file}` : (f.avatar ? `/t/${f.avatar}.webp` : '/a/default.png');
|
||||
img.style.height = "32px";
|
||||
img.style.width = "32px";
|
||||
if (f.username_color) img.style.borderColor = f.username_color;
|
||||
const img = document.createElement('img');
|
||||
img.src = f.avatar_file ? `/a/${f.avatar_file}` : (f.avatar ? `/t/${f.avatar}.webp` : '/a/default.png');
|
||||
img.style.height = "32px";
|
||||
img.style.width = "32px";
|
||||
if (f.username_color) img.style.borderColor = f.username_color;
|
||||
|
||||
a.appendChild(img);
|
||||
favcontainer.appendChild(a);
|
||||
});
|
||||
favcontainer.hidden = false;
|
||||
a.appendChild(img);
|
||||
favcontainer.appendChild(a);
|
||||
});
|
||||
favcontainer.hidden = false;
|
||||
} else {
|
||||
favcontainer.hidden = true;
|
||||
}
|
||||
|
||||
window.flashMessage((window.f0ckI18n && (isNowFav ? window.f0ckI18n.fav_added : window.f0ckI18n.fav_removed)) || (isNowFav ? 'ADDED TO FAVORITES' : 'REMOVED FROM FAVORITES'));
|
||||
if (navigator.vibrate) navigator.vibrate(50);
|
||||
} else {
|
||||
favcontainer.hidden = true;
|
||||
const errMsg = (res && (res.msg || res.error)) || 'Anonymous favoriting is disabled.';
|
||||
if (typeof window.flashMessage === 'function') {
|
||||
window.flashMessage(errMsg, 3000, 'error');
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (typeof window.flashMessage === 'function') {
|
||||
window.flashMessage('Failed to update favorite.', 3000, 'error');
|
||||
}
|
||||
|
||||
window.flashMessage((window.f0ckI18n && (isNowFav ? window.f0ckI18n.fav_added : window.f0ckI18n.fav_removed)) || (isNowFav ? 'ADDED TO FAVORITES' : 'REMOVED FROM FAVORITES'));
|
||||
if (navigator.vibrate) navigator.vibrate(50);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+141
-17
@@ -84,6 +84,9 @@
|
||||
this.rawSeed = null;
|
||||
this.isSessionReady = false;
|
||||
this.hwFingerprint = null;
|
||||
try {
|
||||
this.hwFingerprint = localStorage.getItem('f0ck_anon_hw_fp') || null;
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,6 +95,13 @@
|
||||
*/
|
||||
async getHardwareFingerprint() {
|
||||
if (this.hwFingerprint) return this.hwFingerprint;
|
||||
try {
|
||||
const cached = localStorage.getItem('f0ck_anon_hw_fp');
|
||||
if (cached) {
|
||||
this.hwFingerprint = cached;
|
||||
return this.hwFingerprint;
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
try {
|
||||
let glVendor = '';
|
||||
@@ -117,16 +127,67 @@
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
// WebGPU adapter architecture (modern GPUs)
|
||||
let gpuArch = '';
|
||||
try {
|
||||
if (navigator.gpu) {
|
||||
const adapter = await navigator.gpu.requestAdapter();
|
||||
if (adapter && adapter.info) {
|
||||
gpuArch = [adapter.info.architecture, adapter.info.vendor, adapter.info.description].filter(Boolean).join(':');
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
// CPU & Memory
|
||||
const concurrency = navigator.hardwareConcurrency || 0;
|
||||
const memory = navigator.deviceMemory || 0;
|
||||
const platform = navigator.platform || '';
|
||||
|
||||
// Display, Gamut & Dynamic Range
|
||||
const screenInfo = [
|
||||
window.screen ? window.screen.width : 0,
|
||||
window.screen ? window.screen.height : 0,
|
||||
window.screen ? window.screen.availWidth : 0,
|
||||
window.screen ? window.screen.availHeight : 0,
|
||||
window.screen ? window.screen.colorDepth : 0,
|
||||
window.screen ? window.screen.pixelDepth : 0,
|
||||
window.devicePixelRatio || 1
|
||||
window.devicePixelRatio || 1,
|
||||
window.matchMedia && window.matchMedia('(color-gamut: p3)').matches ? 'p3' : (window.matchMedia && window.matchMedia('(color-gamut: rec2020)').matches ? 'rec2020' : 'srgb'),
|
||||
window.matchMedia && window.matchMedia('(dynamic-range: high)').matches ? 'hdr' : 'sdr'
|
||||
].join('x');
|
||||
|
||||
// Input & Peripheral Hardware (touch, mouse, stylus)
|
||||
const touchPoints = (typeof navigator !== 'undefined' && 'maxTouchPoints' in navigator) ? navigator.maxTouchPoints : 0;
|
||||
const pointerType = (window.matchMedia && window.matchMedia('(pointer: fine)').matches) ? 'fine' : ((window.matchMedia && window.matchMedia('(pointer: coarse)').matches) ? 'coarse' : 'none');
|
||||
const hoverType = (window.matchMedia && window.matchMedia('(hover: hover)').matches) ? 'hover' : 'none';
|
||||
const inputHardware = `${touchPoints}:${pointerType}:${hoverType}`;
|
||||
|
||||
// 2D Canvas Font Rasterizer & Subpixel Geometry
|
||||
let canvasFp = '';
|
||||
try {
|
||||
const c2d = document.createElement('canvas');
|
||||
c2d.width = 240;
|
||||
c2d.height = 60;
|
||||
const ctx = c2d.getContext('2d');
|
||||
if (ctx) {
|
||||
ctx.textBaseline = 'alphabetic';
|
||||
ctx.fillStyle = '#f60';
|
||||
ctx.fillRect(10, 5, 60, 20);
|
||||
ctx.fillStyle = '#069';
|
||||
ctx.font = '14pt Arial, sans-serif';
|
||||
ctx.fillText('f0ck.dev <canvas> 😃', 4, 35);
|
||||
ctx.fillStyle = 'rgba(102, 204, 0, 0.7)';
|
||||
ctx.font = '16pt Times, serif';
|
||||
ctx.fillText('f0ck.dev <canvas> 😃', 20, 48);
|
||||
const imgData = ctx.getImageData(0, 0, 240, 60).data;
|
||||
let sum = 0;
|
||||
for (let i = 0; i < imgData.length; i += 4) {
|
||||
sum = (sum * 31 + imgData[i] + imgData[i+1] + imgData[i+2] + imgData[i+3]) >>> 0;
|
||||
}
|
||||
canvasFp = sum.toString(16);
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
// Web Audio DSP Floating-Point Math
|
||||
let audioFp = '';
|
||||
try {
|
||||
const AudioContext = window.OfflineAudioContext || window.webkitOfflineAudioContext;
|
||||
@@ -159,14 +220,26 @@
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
// FPU Math Microarchitecture Precision
|
||||
const mathPrecision = [
|
||||
Math.tan(-1e300).toString().substring(0, 10),
|
||||
Math.sinh(1).toString().substring(0, 10),
|
||||
Math.acos(0.123456789).toString().substring(0, 10)
|
||||
].join(';');
|
||||
|
||||
const rawHardware = [
|
||||
glVendor,
|
||||
glRenderer,
|
||||
glLimits,
|
||||
gpuArch,
|
||||
concurrency,
|
||||
memory,
|
||||
platform,
|
||||
screenInfo,
|
||||
audioFp
|
||||
inputHardware,
|
||||
canvasFp,
|
||||
audioFp,
|
||||
mathPrecision
|
||||
].join('~~~');
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
@@ -174,6 +247,9 @@
|
||||
const hashBuf = await window.crypto.subtle.digest('SHA-256', data);
|
||||
const hashHex = bytesToHex(new Uint8Array(hashBuf));
|
||||
this.hwFingerprint = `HW:${hashHex}`;
|
||||
try {
|
||||
localStorage.setItem('f0ck_anon_hw_fp', this.hwFingerprint);
|
||||
} catch (e) {}
|
||||
return this.hwFingerprint;
|
||||
} catch (err) {
|
||||
console.warn('[ANON_SSH] Failed to compute hardware fingerprint:', err);
|
||||
@@ -483,7 +559,7 @@
|
||||
localStorage.setItem(STORAGE_KEY_PUB, this.pubkey);
|
||||
localStorage.setItem(STORAGE_KEY_FP, this.fingerprint);
|
||||
|
||||
await this.ensureSession(true);
|
||||
await this.ensureSession(true, isExplicitLogin);
|
||||
return this.getIdentity();
|
||||
}
|
||||
|
||||
@@ -514,7 +590,11 @@
|
||||
/**
|
||||
* Authenticate to backend and establish/refresh anonymous session
|
||||
*/
|
||||
async ensureSession(force = false) {
|
||||
async ensureSession(force = false, isExplicitLogin = false) {
|
||||
if (window.location.pathname === '/banned') {
|
||||
return;
|
||||
}
|
||||
|
||||
// If user is already logged in as a real registered user, don't overwrite session!
|
||||
if (window.f0ckSession && window.f0ckSession.user && !window.f0ckSession.is_anon) {
|
||||
return;
|
||||
@@ -550,8 +630,15 @@
|
||||
reason: data.reason,
|
||||
expires: data.expires
|
||||
});
|
||||
if (window.location.pathname !== '/banned') {
|
||||
window.location.href = data.redirect || '/banned';
|
||||
this.clearStoredIdentity();
|
||||
this.updateNavUI(false);
|
||||
// ONLY redirect to /banned if user was explicitly trying to login!
|
||||
if (isExplicitLogin) {
|
||||
const anonModal = document.getElementById('anon-ssh-modal');
|
||||
if (anonModal) anonModal.style.display = 'none';
|
||||
if (window.location.pathname !== '/banned') {
|
||||
window.location.href = data.redirect || '/banned';
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -594,6 +681,9 @@
|
||||
setTombstone(tombstone) {
|
||||
try {
|
||||
localStorage.setItem('f0ck_anon_tombstone', JSON.stringify(tombstone));
|
||||
if (tombstone && tombstone.banned) {
|
||||
document.cookie = `f0ck_banned=${encodeURIComponent(JSON.stringify(tombstone))}; Path=/; Max-Age=31536000; SameSite=Lax`;
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
@@ -653,6 +743,7 @@
|
||||
localStorage.removeItem(STORAGE_KEY_PRIV);
|
||||
localStorage.removeItem(STORAGE_KEY_PUB);
|
||||
localStorage.removeItem(STORAGE_KEY_FP);
|
||||
document.cookie = 'f0ck_banned=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax';
|
||||
this.cryptoKey = null;
|
||||
this.rawSeed = null;
|
||||
this.rawPub = null;
|
||||
@@ -669,11 +760,27 @@
|
||||
if (window.f0ckSession && window.f0ckSession.enable_anonymous_access === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If client has a ban tombstone, redirect to /banned directly on login attempt
|
||||
const tombstone = this.getTombstone();
|
||||
if (tombstone && tombstone.banned) {
|
||||
const anonModal = document.getElementById('anon-ssh-modal');
|
||||
if (anonModal) anonModal.style.display = 'none';
|
||||
if (window.location.pathname !== '/banned') {
|
||||
window.location.href = '/banned';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!this.pubkey) {
|
||||
await this.generateIdentity();
|
||||
await this.generateIdentity(true);
|
||||
} else {
|
||||
await this.ensureSession(true, true);
|
||||
}
|
||||
await this.ensureSession(true);
|
||||
|
||||
if (!this.isSessionReady) return;
|
||||
|
||||
// Sync any guest favorites saved in localStorage to this anon account
|
||||
if (window.f0ckGuestFavs && typeof window.f0ckGuestFavs.importToAccount === 'function') {
|
||||
await window.f0ckGuestFavs.importToAccount();
|
||||
@@ -698,12 +805,16 @@
|
||||
this.clearStoredIdentity();
|
||||
await fetch('/api/v2/anon/logout', { method: 'POST', credentials: 'same-origin' }).catch(() => {});
|
||||
} finally {
|
||||
window.location.href = '/';
|
||||
this.updateNavUI(false);
|
||||
if (typeof window.showToastNotification === 'function') {
|
||||
window.showToastNotification('Logged out from anonymous session');
|
||||
}
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update visitor navbar UI elements
|
||||
* Update navigation bar elements based on anonymous authentication state
|
||||
*/
|
||||
updateNavUI(isAnon) {
|
||||
const icon = document.getElementById('nav-visitor-icon');
|
||||
@@ -765,8 +876,12 @@
|
||||
* Initialize on page startup
|
||||
*/
|
||||
async init() {
|
||||
if (window.location.pathname === '/banned') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (window.f0ckSession && window.f0ckSession.enable_anonymous_access === false) {
|
||||
if (window.f0ckSession.is_anon) {
|
||||
if (window.f0ckSession.is_anon && window.f0ckSession.logged_in) {
|
||||
await fetch('/api/v2/anon/logout', { method: 'POST', credentials: 'same-origin' }).catch(() => {});
|
||||
window.location.reload();
|
||||
return;
|
||||
@@ -780,6 +895,15 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// If client has a ban tombstone, do NOT attempt background session handshake on page load/reload!
|
||||
const tombstone = this.getTombstone();
|
||||
if (tombstone && tombstone.banned) {
|
||||
this.clearStoredIdentity();
|
||||
this.updateNavUI(false);
|
||||
this.attachUIListeners();
|
||||
return;
|
||||
}
|
||||
|
||||
const storedPriv = localStorage.getItem(STORAGE_KEY_PRIV);
|
||||
const storedPub = localStorage.getItem(STORAGE_KEY_PUB);
|
||||
const storedFp = localStorage.getItem(STORAGE_KEY_FP);
|
||||
@@ -808,12 +932,12 @@
|
||||
|
||||
// User previously logged in as anonymous with this key
|
||||
// If backend session is already active, avoid redundant session churn
|
||||
if (window.f0ckSession && window.f0ckSession.is_anon) {
|
||||
if (window.f0ckSession && window.f0ckSession.is_anon && window.f0ckSession.logged_in) {
|
||||
this.isSessionReady = true;
|
||||
this.updateNavUI(true);
|
||||
} else {
|
||||
await this.ensureSession();
|
||||
this.updateNavUI(true);
|
||||
// Unauthenticated guest state: do NOT auto-login as anonymous on page load!
|
||||
this.updateNavUI(false);
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('[ANON_SSH] Failed to restore stored key:', err);
|
||||
@@ -822,7 +946,7 @@
|
||||
}
|
||||
} else {
|
||||
// Clean guest state! Do NOT auto-generate or establish session!
|
||||
if (window.f0ckSession && window.f0ckSession.is_anon) {
|
||||
if (window.f0ckSession && window.f0ckSession.is_anon && window.f0ckSession.logged_in) {
|
||||
// Stale backend anon session cookie without corresponding local key: clear cookie
|
||||
await fetch('/api/v2/anon/logout', { method: 'POST', credentials: 'same-origin' }).catch(() => {});
|
||||
window.location.reload();
|
||||
@@ -853,7 +977,7 @@
|
||||
}
|
||||
|
||||
const logoutTarget = e.target.closest('#nav-anon-logout-btn, a[href="/logout"]');
|
||||
if (logoutTarget && ((window.f0ckSession && window.f0ckSession.is_anon) || this.pubkey)) {
|
||||
if (logoutTarget && ((window.f0ckSession && window.f0ckSession.is_anon && window.f0ckSession.logged_in) || this.pubkey)) {
|
||||
e.preventDefault();
|
||||
this.logoutAnonymous();
|
||||
return;
|
||||
|
||||
@@ -2230,7 +2230,7 @@ class CommentSystem {
|
||||
const authorUserId = comment.user_id ?? (comment.username && window.f0ckSession && comment.username.toLowerCase() === (window.f0ckSession.user || '').toLowerCase() ? (window.f0ckSession.id || window.f0ckSession.user_id) : null);
|
||||
const authorUsernameColor = comment.username_color || (comment.username && window.f0ckSession && comment.username.toLowerCase() === (window.f0ckSession.user || '').toLowerCase() ? window.f0ckSession.username_color : null);
|
||||
|
||||
const isAnonGuest = window.f0ckSession?.guest_anonymize && !window.f0ckSession?.logged_in;
|
||||
const isAnonGuest = window.f0ckSession?.is_anonymized ?? (window.f0ckSession?.guest_anonymize && !window.f0ckSession?.logged_in);
|
||||
if (isAnonGuest) bannerStyle = '';
|
||||
|
||||
const avatarHtml = isAnonGuest
|
||||
@@ -2366,7 +2366,8 @@ class CommentSystem {
|
||||
const counter = (maxLen !== null && maxLen !== undefined)
|
||||
? `<span class="char-counter" data-max="${maxLen}">0 / ${maxLen}</span>`
|
||||
: '';
|
||||
const fileUploadEnabled = session.logged_in && session.allow_fileupload_comments;
|
||||
const anonAttachmentsDisabled = session.is_anon && session.anon_permissions?.comment_attachments === false;
|
||||
const fileUploadEnabled = session.logged_in && session.allow_fileupload_comments && !anonAttachmentsDisabled;
|
||||
const multiFile = session.fileupload_comments_multifile;
|
||||
const attachBtn = fileUploadEnabled
|
||||
? `<button class="comment-attach-btn" title="${attachLabel}" type="button"><i class="fa-solid fa-paperclip"></i></button><input type="file" class="comment-file-input" accept="image/*,video/*,audio/*" ${multiFile ? 'multiple' : ''} style="display:none;">`
|
||||
@@ -3350,6 +3351,11 @@ class CommentSystem {
|
||||
return;
|
||||
}
|
||||
if (submitBtn.classList.contains('loading') || submitBtn.disabled) return;
|
||||
if (window.f0ckSession?.is_anon && window.f0ckSession?.anon_permissions?.comment === false) {
|
||||
if (window.flashMessage) window.flashMessage('Anonymous commenting is disabled.', 3000, 'error');
|
||||
else alert('Anonymous commenting is disabled.');
|
||||
return;
|
||||
}
|
||||
|
||||
// ── Upload all staged files now (at submit time) ───────────────────────
|
||||
const fileIds = [];
|
||||
|
||||
+680
-100
File diff suppressed because it is too large
Load Diff
+76
-15
@@ -97,7 +97,11 @@
|
||||
let autoNextLoops = Math.max(0, parseInt(prefs.autoNextLoops ?? 1, 10));
|
||||
let leftHandEnabled = prefs.leftHand === true;
|
||||
|
||||
let applied = { mode: defaultMode, mime: '', order: 'random', tags: [], externalUrl: null };
|
||||
const isScrollerAnon = !(window.f0ckSession && window.f0ckSession.user && !window.f0ckSession.is_anon);
|
||||
const scrollerAllowedMimes = window.f0ckSession?.anon_permissions?.allowed_mimes;
|
||||
const scrollerSingleMime = (isScrollerAnon && Array.isArray(scrollerAllowedMimes) && scrollerAllowedMimes.length === 1) ? scrollerAllowedMimes[0] : null;
|
||||
|
||||
let applied = { mode: defaultMode, mime: scrollerSingleMime || '', order: 'random', tags: [], externalUrl: null };
|
||||
let pending = { ...applied, tags: [] };
|
||||
|
||||
// Volume / mute
|
||||
@@ -192,7 +196,8 @@
|
||||
if (hid && !cache.items.some(item => String(item.id) === hid)) return false;
|
||||
|
||||
if (cache.filters) {
|
||||
applied = { mode: defaultMode, mime: '', order: 'random', tags: [], ...cache.filters };
|
||||
applied = { mode: defaultMode, mime: scrollerSingleMime || '', order: 'random', tags: [], ...cache.filters };
|
||||
if (scrollerSingleMime) applied.mime = scrollerSingleMime;
|
||||
applied.tags = Array.isArray(cache.filters.tags) ? [...cache.filters.tags] : [];
|
||||
pending = { ...applied, tags: [...applied.tags] };
|
||||
}
|
||||
@@ -1141,7 +1146,23 @@
|
||||
}
|
||||
|
||||
async function toggleFav(slide) {
|
||||
if (!window.scrollerLoggedIn) return;
|
||||
if (window.f0ckSession?.is_anon && window.f0ckSession?.anon_permissions && window.f0ckSession.anon_permissions.favorite === false) {
|
||||
const errMsg = 'Anonymous favoriting is disabled.';
|
||||
if (typeof window.flashMessage === 'function') {
|
||||
window.flashMessage(errMsg, 3000, 'error');
|
||||
} else if (typeof showShareToast === 'function') {
|
||||
showShareToast(errMsg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!window.scrollerLoggedIn) {
|
||||
if (typeof showShareToast === 'function') {
|
||||
showShareToast('Login to favorite posts');
|
||||
} else if (typeof window.flashMessage === 'function') {
|
||||
window.flashMessage('Login to favorite posts', 3000, 'warning');
|
||||
}
|
||||
return;
|
||||
}
|
||||
const id = slide.dataset.localId || slide.dataset.id;
|
||||
// External items have non-numeric IDs (e.g. "gif/123") — can't fav until rehosted
|
||||
if (!/^\d+$/.test(id)) { showShareToast('Can\u2019t fav external items'); return; }
|
||||
@@ -1157,10 +1178,6 @@
|
||||
if (countEl) countEl.textContent = Math.max(0, (parseInt(countEl.textContent || '0', 10)) + (nowFaved ? 1 : -1));
|
||||
if (nowFaved) flashFav(slide);
|
||||
}
|
||||
if (!window.scrollerLoggedIn && (!window.f0ckSession || !window.f0ckSession.user)) {
|
||||
showShareToast('Login to favorite posts');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const csrfToken = window.f0ckSession?.csrf_token || window.scrollerCsrf || '';
|
||||
const resp = await fetch('/api/v2/togglefav', {
|
||||
@@ -1171,7 +1188,24 @@
|
||||
},
|
||||
body: `postid=${id}${csrfToken ? `&csrf_token=${encodeURIComponent(csrfToken)}` : ''}`
|
||||
});
|
||||
const data = await resp.json();
|
||||
const data = await resp.json().catch(() => ({}));
|
||||
if (!resp.ok || !data.success) {
|
||||
// Rollback optimistic update
|
||||
if (favBtn) {
|
||||
favBtn.classList.toggle('faved', wasFaved);
|
||||
const icon = favBtn.querySelector('i');
|
||||
if (icon) icon.className = (wasFaved ? 'fa-solid' : 'fa-regular') + ' fa-heart';
|
||||
const countEl = favBtn.querySelector('.scroll-btn-count');
|
||||
if (countEl) countEl.textContent = Math.max(0, (parseInt(countEl.textContent || '0', 10)) + (wasFaved ? 0 : -1));
|
||||
}
|
||||
const errMsg = (data && (data.msg || data.error)) || 'Anonymous favoriting is disabled.';
|
||||
if (typeof window.flashMessage === 'function') {
|
||||
window.flashMessage(errMsg, 3000, 'error');
|
||||
} else if (typeof showShareToast === 'function') {
|
||||
showShareToast(errMsg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Sync count to server truth (handles race conditions)
|
||||
if (data.success && favBtn && data.favs) {
|
||||
const countEl = favBtn.querySelector('.scroll-btn-count');
|
||||
@@ -1183,6 +1217,8 @@
|
||||
favBtn.classList.toggle('faved', wasFaved);
|
||||
const icon = favBtn.querySelector('i');
|
||||
if (icon) icon.className = (wasFaved ? 'fa-solid' : 'fa-regular') + ' fa-heart';
|
||||
const countEl = favBtn.querySelector('.scroll-btn-count');
|
||||
if (countEl) countEl.textContent = Math.max(0, (parseInt(countEl.textContent || '0', 10)) + (wasFaved ? 0 : -1));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1349,7 +1385,7 @@
|
||||
const meta = document.createElement('div'); meta.className = 'scroll-meta';
|
||||
// esc() the color value: a raw '"' in username_color would break out of the
|
||||
// style attribute and allow arbitrary HTML injection (XSS).
|
||||
const isAnonGuest = window.f0ckSession?.guest_anonymize && !window.f0ckSession?.logged_in;
|
||||
const isAnonGuest = window.f0ckSession?.is_anonymized ?? (window.f0ckSession?.guest_anonymize && !window.f0ckSession?.logged_in);
|
||||
const colorStyle = (!isAnonGuest && item.username_color) ? `color:${esc(item.username_color)}` : '';
|
||||
const ratingHtml = `<span class="scroll-rating ${esc(item.rating_class)}" data-item-id="${item.id}" data-rating="${esc(item.rating_class)}">${esc(item.rating_label)}</span>`;
|
||||
const ocHtml = item.is_oc ? `<span class="scroll-oc"><i class="fa-solid fa-star" style="font-size:.6rem"></i> OC</span>` : '';
|
||||
@@ -2176,7 +2212,7 @@
|
||||
function renderCommentEl(c, canReply) {
|
||||
const el = document.createElement('div'); el.className = 'comment-item';
|
||||
el.dataset.commentId = c.id || '';
|
||||
const isAnonGuest = window.f0ckSession?.guest_anonymize && !window.f0ckSession?.logged_in;
|
||||
const isAnonGuest = window.f0ckSession?.is_anonymized ?? (window.f0ckSession?.guest_anonymize && !window.f0ckSession?.logged_in);
|
||||
const av = isAnonGuest ? '/a/default.png' : (c.avatar_file ? `/a/${c.avatar_file}` : (c.avatar ? `/t/${c.avatar}.webp` : '/a/default.png'));
|
||||
const nc = (!isAnonGuest && c.username_color) ? `color:${esc(c.username_color)}` : '';
|
||||
const _i = window.f0ckI18n || {};
|
||||
@@ -3003,6 +3039,10 @@
|
||||
});
|
||||
|
||||
function syncPanelUI() {
|
||||
if (scrollerSingleMime) {
|
||||
pending.mime = scrollerSingleMime;
|
||||
applied.mime = scrollerSingleMime;
|
||||
}
|
||||
document.querySelectorAll('#mode-pills .filter-pill').forEach(p => p.classList.toggle('active', +p.dataset.mode === pending.mode));
|
||||
document.querySelectorAll('#mime-pills .filter-pill').forEach(p => p.classList.toggle('active', p.dataset.mime === pending.mime));
|
||||
document.querySelectorAll('#order-pills .filter-pill').forEach(p => p.classList.toggle('active', p.dataset.order === pending.order));
|
||||
@@ -3011,9 +3051,15 @@
|
||||
}
|
||||
|
||||
function makePillListener(groupId, key, transform) {
|
||||
document.getElementById(groupId).querySelectorAll('.filter-pill').forEach(pill => {
|
||||
pill.addEventListener('click', () => {
|
||||
document.getElementById(groupId).querySelectorAll('.filter-pill').forEach(p => p.classList.remove('active'));
|
||||
const el = document.getElementById(groupId);
|
||||
if (!el) return;
|
||||
el.querySelectorAll('.filter-pill').forEach(pill => {
|
||||
pill.addEventListener('click', (e) => {
|
||||
if (groupId === 'mime-pills' && scrollerSingleMime) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
el.querySelectorAll('.filter-pill').forEach(p => p.classList.remove('active'));
|
||||
pill.classList.add('active'); pending[key] = transform ? transform(pill.dataset[key]) : pill.dataset[key];
|
||||
});
|
||||
});
|
||||
@@ -3078,7 +3124,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
filterResetBtn.addEventListener('click', () => { pending = { mode: defaultMode, mime: '', order: 'random', tags: [] }; syncPanelUI(); tagInput.value = ''; tagClear.classList.remove('show'); tagSuggestEl.innerHTML = ''; lastSugg = []; renderActiveTags(); });
|
||||
filterResetBtn.addEventListener('click', () => { pending = { mode: defaultMode, mime: scrollerSingleMime || '', order: 'random', tags: [] }; syncPanelUI(); tagInput.value = ''; tagClear.classList.remove('show'); tagSuggestEl.innerHTML = ''; lastSugg = []; renderActiveTags(); });
|
||||
|
||||
function updateFilterSummary() {
|
||||
const is4chan = !!applied.externalUrl;
|
||||
@@ -3500,11 +3546,26 @@
|
||||
|
||||
const initScrollerSSE = () => {
|
||||
if (sseEs) sseEs.close();
|
||||
sseEs = new EventSource(`/api/notifications/stream?tabId=${tabId}`);
|
||||
let sseUrl = `/api/notifications/stream?tabId=${tabId}`;
|
||||
try {
|
||||
const fp = localStorage.getItem('f0ck_anon_ssh_fp');
|
||||
if (fp) sseUrl += `&fp=${encodeURIComponent(fp)}`;
|
||||
const hw = localStorage.getItem('f0ck_anon_hw_fp');
|
||||
if (hw) sseUrl += `&hw=${encodeURIComponent(hw)}`;
|
||||
} catch (e) {}
|
||||
sseEs = new EventSource(sseUrl);
|
||||
sseEs.onopen = () => { sseRetryCount = 0; };
|
||||
sseEs.onmessage = (e) => {
|
||||
try {
|
||||
const data = JSON.parse(e.data);
|
||||
if (data.type === 'banned') {
|
||||
try {
|
||||
const tombstoneData = { banned: true, reason: data.data?.reason, expires: data.data?.expires };
|
||||
document.cookie = `f0ck_banned=${encodeURIComponent(JSON.stringify(tombstoneData))}; Path=/; Max-Age=31536000; SameSite=Lax`;
|
||||
} catch (err) {}
|
||||
window.location.href = data.data?.redirect || '/banned';
|
||||
return;
|
||||
}
|
||||
if (data.type === 'notify') {
|
||||
pollNotifCount(); // instant re-fetch on SSE push
|
||||
if (navigator.vibrate) navigator.vibrate([200, 80, 200]);
|
||||
|
||||
@@ -562,7 +562,7 @@
|
||||
? `style="--author-banner: url('/a/${bannerFile}'); --author-banner-position: ${bannerPos === 'center' ? 'center top' : (bannerPos || 'center top')}; --author-banner-size: ${(bannerSz && bannerSz !== 'cover') ? bannerSz : '100% auto'}; --author-banner-repeat: no-repeat;"`
|
||||
: '';
|
||||
|
||||
const isAnonGuest = window.f0ckSession?.guest_anonymize && !window.f0ckSession?.logged_in;
|
||||
const isAnonGuest = window.f0ckSession?.is_anonymized ?? (window.f0ckSession?.guest_anonymize && !window.f0ckSession?.logged_in);
|
||||
const effectiveBannerStyle = isAnonGuest ? '' : bannerStyle;
|
||||
const authorAvatarHtml = isAnonGuest
|
||||
? `<span class="sidebar-avatar-link"><img src="/a/default.png" class="sidebar-avatar" loading="eager" /></span>`
|
||||
@@ -1031,13 +1031,31 @@
|
||||
let lastBoundMode = typeof window.activeMode !== 'undefined' ? window.activeMode : null;
|
||||
|
||||
const getCurrentMimeFilter = () => {
|
||||
const allowed = window.f0ckSession?.is_anon && Array.isArray(window.f0ckSession?.anon_permissions?.allowed_mimes) ? window.f0ckSession.anon_permissions.allowed_mimes : null;
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const qMime = urlParams.get('mime');
|
||||
if (qMime !== null) return qMime.trim();
|
||||
let qMime = urlParams.get('mime');
|
||||
if (qMime !== null) {
|
||||
qMime = qMime.trim();
|
||||
if (allowed) {
|
||||
const parts = qMime.split(',').filter(m => allowed.includes(m));
|
||||
return parts.length > 0 ? parts.join(',') : (allowed.length < 5 ? allowed.join(',') : '');
|
||||
}
|
||||
return qMime;
|
||||
}
|
||||
const cookieMime = document.cookie.split('; ').find(row => row.startsWith('mime='));
|
||||
if (cookieMime) {
|
||||
const val = cookieMime.split('=')[1];
|
||||
if (typeof val === 'string') return decodeURIComponent(val).trim();
|
||||
if (typeof val === 'string') {
|
||||
const cMime = decodeURIComponent(val).trim();
|
||||
if (allowed) {
|
||||
const parts = cMime.split(',').filter(m => allowed.includes(m));
|
||||
return parts.length > 0 ? parts.join(',') : (allowed.length < 5 ? allowed.join(',') : '');
|
||||
}
|
||||
return cMime;
|
||||
}
|
||||
}
|
||||
if (allowed && allowed.length < 5) {
|
||||
return allowed.join(',');
|
||||
}
|
||||
return '';
|
||||
};
|
||||
@@ -1176,7 +1194,7 @@
|
||||
displayTitle = `${videoKey}`;
|
||||
}
|
||||
|
||||
const isAnonGuest = window.f0ckSession?.guest_anonymize && !window.f0ckSession?.logged_in;
|
||||
const isAnonGuest = window.f0ckSession?.is_anonymized ?? (window.f0ckSession?.guest_anonymize && !window.f0ckSession?.logged_in);
|
||||
const authorName = isAnonGuest ? 'anonymous' : (video.display_name || video.username);
|
||||
const userColorStyle = (!isAnonGuest && video.username_color) ? `style="color: ${escapeHtml(video.username_color)}"` : '';
|
||||
|
||||
|
||||
+58
-42
@@ -95,7 +95,7 @@
|
||||
if (canManage) {
|
||||
span.classList.add('can-cycle');
|
||||
}
|
||||
} else if (tag.display_name || tag.user) {
|
||||
} else if (!window.f0ckSession?.is_anonymized && window.f0ckSession?.logged_in && !window.f0ckSession?.is_anon && (tag.display_name || tag.user)) {
|
||||
span.setAttribute('tooltip', tag.display_name || tag.user);
|
||||
}
|
||||
|
||||
@@ -196,6 +196,13 @@
|
||||
|
||||
const toggleFavEvent = async (e) => {
|
||||
// e is the click event or undefined
|
||||
if (e && typeof e.preventDefault === 'function') e.preventDefault();
|
||||
if (window.f0ckSession?.is_anon && window.f0ckSession?.anon_permissions && window.f0ckSession.anon_permissions.favorite === false) {
|
||||
if (typeof window.flashMessage === 'function') {
|
||||
window.flashMessage('Anonymous favoriting is disabled.', 3000, 'error');
|
||||
}
|
||||
return;
|
||||
}
|
||||
const ctx = getContext();
|
||||
if (!ctx) return;
|
||||
const { postid } = ctx;
|
||||
@@ -204,50 +211,59 @@
|
||||
const favoBtn = document.querySelector("#a_favo");
|
||||
const wasAlreadyFav = favoBtn && favoBtn.classList.contains('fa-solid');
|
||||
|
||||
const res = await post('/api/v2/togglefav', {
|
||||
postid: postid
|
||||
});
|
||||
if (res.success) {
|
||||
if (window.invalidateItemCache) {
|
||||
window.invalidateItemCache(postid);
|
||||
try {
|
||||
const res = await post('/api/v2/togglefav', {
|
||||
postid: postid
|
||||
});
|
||||
if (res && res.success) {
|
||||
if (window.invalidateItemCache) {
|
||||
window.invalidateItemCache(postid);
|
||||
}
|
||||
// New state is the logical opposite of what it was before the API call
|
||||
const isNowFav = !wasAlreadyFav;
|
||||
|
||||
if (favoBtn) {
|
||||
favoBtn.classList.toggle('fa-solid', isNowFav);
|
||||
favoBtn.classList.toggle('fa-regular', !isNowFav);
|
||||
}
|
||||
|
||||
// span#favs
|
||||
const favcontainer = document.querySelector('#favs');
|
||||
favcontainer.innerHTML = "";
|
||||
if (res.favs && res.favs.length > 0) {
|
||||
res.favs.forEach(f => {
|
||||
const a = document.createElement('a');
|
||||
a.href = `/user/${f.user}`;
|
||||
a.setAttribute('tooltip', f.display_name || f.user);
|
||||
a.setAttribute('flow', 'up');
|
||||
|
||||
const img = document.createElement('img');
|
||||
img.src = f.avatar_file ? `/a/${f.avatar_file}` : (f.avatar ? `/t/${f.avatar}.webp` : '/a/default.png');
|
||||
img.style.height = "32px";
|
||||
img.style.width = "32px";
|
||||
if (f.username_color) img.style.borderColor = f.username_color;
|
||||
|
||||
a.appendChild(img);
|
||||
favcontainer.appendChild(a);
|
||||
});
|
||||
favcontainer.hidden = false;
|
||||
} else {
|
||||
favcontainer.hidden = true;
|
||||
}
|
||||
|
||||
window.flashMessage((window.f0ckI18n && (isNowFav ? window.f0ckI18n.fav_added : window.f0ckI18n.fav_removed)) || (isNowFav ? 'ADDED TO FAVORITES' : 'REMOVED FROM FAVORITES'));
|
||||
if (navigator.vibrate) navigator.vibrate(50);
|
||||
}
|
||||
// New state is the logical opposite of what it was before the API call
|
||||
const isNowFav = !wasAlreadyFav;
|
||||
|
||||
if (favoBtn) {
|
||||
favoBtn.classList.toggle('fa-solid', isNowFav);
|
||||
favoBtn.classList.toggle('fa-regular', !isNowFav);
|
||||
else {
|
||||
const errMsg = (res && (res.msg || res.error)) || 'Anonymous favoriting is disabled.';
|
||||
if (typeof window.flashMessage === 'function') {
|
||||
window.flashMessage(errMsg, 3000, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// span#favs
|
||||
const favcontainer = document.querySelector('#favs');
|
||||
favcontainer.innerHTML = "";
|
||||
if (res.favs.length > 0) {
|
||||
res.favs.forEach(f => {
|
||||
const a = document.createElement('a');
|
||||
a.href = `/user/${f.user}`;
|
||||
a.setAttribute('tooltip', f.display_name || f.user);
|
||||
a.setAttribute('flow', 'up');
|
||||
|
||||
const img = document.createElement('img');
|
||||
img.src = f.avatar_file ? `/a/${f.avatar_file}` : (f.avatar ? `/t/${f.avatar}.webp` : '/a/default.png');
|
||||
img.style.height = "32px";
|
||||
img.style.width = "32px";
|
||||
if (f.username_color) img.style.borderColor = f.username_color;
|
||||
|
||||
a.appendChild(img);
|
||||
favcontainer.appendChild(a);
|
||||
});
|
||||
favcontainer.hidden = false;
|
||||
} else {
|
||||
favcontainer.hidden = true;
|
||||
} catch (err) {
|
||||
if (typeof window.flashMessage === 'function') {
|
||||
window.flashMessage('Failed to update favorite.', 3000, 'error');
|
||||
}
|
||||
|
||||
window.flashMessage((window.f0ckI18n && (isNowFav ? window.f0ckI18n.fav_added : window.f0ckI18n.fav_removed)) || (isNowFav ? 'ADDED TO FAVORITES' : 'REMOVED FROM FAVORITES'));
|
||||
if (navigator.vibrate) navigator.vibrate(50);
|
||||
}
|
||||
else {
|
||||
// lul
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user