gfsdgsd
This commit is contained in:
+448
-30
@@ -500,6 +500,34 @@ window.initUploadForm = (selector) => {
|
||||
let autoTags = []; // Track tags suggested from metadata
|
||||
let selectedFiles = []; // Array of files for shitpost_mode
|
||||
let activeMode = 'file'; // 'file' or 'url'
|
||||
|
||||
// Album mode state and helpers
|
||||
const albumChoiceContainer = form.querySelector('#album-choice-container');
|
||||
let albumChoiceMode = 'album'; // 'album' | 'batch'
|
||||
|
||||
const isAlbumModeActive = () => {
|
||||
if (activeMode === 'album') {
|
||||
return selectedFiles.length > 0;
|
||||
}
|
||||
return activeMode === 'file' && selectedFiles.length > 1 && (albumChoiceMode === 'album' || !isShitpost);
|
||||
};
|
||||
|
||||
if (albumChoiceContainer) {
|
||||
const btns = albumChoiceContainer.querySelectorAll('.album-choice-btn');
|
||||
btns.forEach(btn => {
|
||||
btn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
const choice = btn.getAttribute('data-choice');
|
||||
if (choice === albumChoiceMode) return;
|
||||
albumChoiceMode = choice;
|
||||
btns.forEach(b => b.classList.toggle('active', b === btn));
|
||||
selectedFiles.forEach(item => { delete item._rendered; });
|
||||
if (filePreview) filePreview.innerHTML = '';
|
||||
handleFile();
|
||||
updateSubmitButton();
|
||||
});
|
||||
});
|
||||
}
|
||||
// Shared emoji cache for per-item pickers (fetched once, reused by all items)
|
||||
let _emojiCache = null;
|
||||
let _emojiCachePromise = null;
|
||||
@@ -649,14 +677,47 @@ window.initUploadForm = (selector) => {
|
||||
tab.addEventListener('click', () => {
|
||||
const mode = tab.dataset.mode;
|
||||
if (mode === activeMode) return;
|
||||
const prevMode = activeMode;
|
||||
activeMode = mode;
|
||||
|
||||
modeTabs.forEach(t => t.classList.remove('active'));
|
||||
tab.classList.add('active');
|
||||
|
||||
if (modeFile) modeFile.style.display = mode === 'file' ? '' : 'none';
|
||||
if (modeFile) modeFile.style.display = (mode === 'file' || mode === 'album') ? '' : 'none';
|
||||
if (modeUrl) modeUrl.style.display = mode === 'url' ? '' : 'none';
|
||||
|
||||
if (mode === 'album') {
|
||||
albumChoiceMode = 'album';
|
||||
form.classList.add('album-mode-active');
|
||||
if (albumChoiceContainer) albumChoiceContainer.style.display = 'none';
|
||||
if (fileInput) {
|
||||
try {
|
||||
const mimesObj = JSON.parse(form.getAttribute('data-mimes') || '{}');
|
||||
fileInput.accept = Object.keys(mimesObj).join(',');
|
||||
} catch {}
|
||||
}
|
||||
if (selectedFiles.length > 0) {
|
||||
renderAlbumStaging();
|
||||
}
|
||||
} else if (mode === 'file') {
|
||||
if (fileInput) {
|
||||
try {
|
||||
const mimesObj = JSON.parse(form.getAttribute('data-mimes') || '{}');
|
||||
fileInput.accept = Object.keys(mimesObj).join(',');
|
||||
} catch {}
|
||||
}
|
||||
if (!isAlbumModeActive()) {
|
||||
form.classList.remove('album-mode-active');
|
||||
if (prevMode === 'album' && isShitpost && selectedFiles.length > 0) {
|
||||
selectedFiles.forEach(item => { delete item._rendered; });
|
||||
if (filePreview) filePreview.innerHTML = '';
|
||||
handleFile();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
form.classList.remove('album-mode-active');
|
||||
}
|
||||
|
||||
// Reset status
|
||||
if (statusDiv) {
|
||||
statusDiv.textContent = '';
|
||||
@@ -1013,11 +1074,17 @@ window.initUploadForm = (selector) => {
|
||||
}
|
||||
|
||||
const isShitpost = !!window.f0ckShitpostMode;
|
||||
const isAlbum = isAlbumModeActive();
|
||||
const isAlbumTab = activeMode === 'album';
|
||||
const isAlbumActive = isAlbum || isAlbumTab;
|
||||
|
||||
form.classList.toggle('album-mode-active', isAlbumActive);
|
||||
|
||||
const rating = form.querySelector('input[name="rating"]:checked');
|
||||
|
||||
// In Shitpost Mode, ratings are per-item. If require rating is true, every item must be rated.
|
||||
// In Shitpost Mode, ratings are per-item unless album mode is active
|
||||
let hasRating = true;
|
||||
if (isShitpost && activeMode === 'file') {
|
||||
if (isShitpost && !isAlbumActive && activeMode === 'file') {
|
||||
if (shitpostRequireRating) {
|
||||
hasRating = selectedFiles.length > 0 && selectedFiles.every(item => ['sfw', 'nsfw', 'nsfl'].includes(item.rating));
|
||||
}
|
||||
@@ -1026,7 +1093,7 @@ window.initUploadForm = (selector) => {
|
||||
}
|
||||
|
||||
let hasTags = true;
|
||||
if (!isShitpost) {
|
||||
if (!isShitpost || isAlbumActive) {
|
||||
hasTags = tags.length >= minTags;
|
||||
} else if (shitpostMinTags > 0 && activeMode === 'file') {
|
||||
// In shitpost file mode with min-tags enforced: every queued item must meet the threshold.
|
||||
@@ -1038,17 +1105,21 @@ window.initUploadForm = (selector) => {
|
||||
const commentSec = form.querySelector('.global-comment-section');
|
||||
const tagsSec = form.querySelector('.global-tag-section');
|
||||
const ocSec = form.querySelector('.global-oc-section');
|
||||
const titleSec = form.querySelector('.global-title-section');
|
||||
const formActions = form.querySelector('.form-actions');
|
||||
if (isShitpost) {
|
||||
if (formActions) {
|
||||
formActions.style.display = activeMode === 'url' ? 'none' : 'block';
|
||||
formActions.style.display = (activeMode === 'url' && !isAlbumActive) ? 'none' : 'block';
|
||||
}
|
||||
const hide = activeMode === 'file';
|
||||
const hide = activeMode === 'file' && !isAlbumActive;
|
||||
const disp = hide ? 'none' : 'block';
|
||||
|
||||
if (ratingSec) {
|
||||
ratingSec.style.display = disp;
|
||||
ratingSec.querySelectorAll('input').forEach(i => i.disabled = hide);
|
||||
ratingSec.querySelectorAll('input').forEach(i => {
|
||||
i.disabled = hide;
|
||||
i.required = !hide;
|
||||
});
|
||||
}
|
||||
if (commentSec) {
|
||||
commentSec.style.display = disp;
|
||||
@@ -1059,13 +1130,19 @@ window.initUploadForm = (selector) => {
|
||||
tagsSec.querySelectorAll('input').forEach(i => i.disabled = hide);
|
||||
}
|
||||
if (ocSec) {
|
||||
ocSec.style.display = 'none';
|
||||
ocSec.querySelectorAll('input').forEach(i => i.disabled = true);
|
||||
ocSec.style.display = isAlbumActive ? 'block' : 'none';
|
||||
ocSec.querySelectorAll('input').forEach(i => i.disabled = !isAlbumActive);
|
||||
}
|
||||
if (titleSec) {
|
||||
titleSec.style.display = isAlbumActive ? 'block' : 'none';
|
||||
titleSec.querySelectorAll('input').forEach(i => i.disabled = !isAlbumActive);
|
||||
}
|
||||
}
|
||||
|
||||
let hasContent = false;
|
||||
if (activeMode === 'file') {
|
||||
if (activeMode === 'album') {
|
||||
hasContent = selectedFiles.length >= 2;
|
||||
} else if (activeMode === 'file') {
|
||||
hasContent = selectedFiles.length > 0;
|
||||
} else {
|
||||
hasContent = urlInput && urlInput.value.trim().length > 0;
|
||||
@@ -1077,13 +1154,19 @@ window.initUploadForm = (selector) => {
|
||||
const btnText = submitBtn.querySelector('.btn-text');
|
||||
if (btnText) {
|
||||
const i18n = window.f0ckI18n || {};
|
||||
if (!hasContent) {
|
||||
if (activeMode === 'album' && selectedFiles.length === 0) {
|
||||
btnText.textContent = i18n.album_select_pictures || 'Select files for album';
|
||||
submitBtn.disabled = true;
|
||||
} else if (activeMode === 'album' && selectedFiles.length === 1) {
|
||||
btnText.textContent = i18n.album_min_pictures || 'Add at least 2 items for an album';
|
||||
submitBtn.disabled = true;
|
||||
} else if (!hasContent) {
|
||||
btnText.textContent = activeMode === 'file'
|
||||
? (ssrSelectFileText || i18n.select_file || 'Select a file')
|
||||
: (i18n.enter_url || 'Enter a URL');
|
||||
} else if (!hasTags) {
|
||||
// non-shitpost or shitpost with min-tags
|
||||
if (isShitpost && shitpostMinTags > 0) {
|
||||
if (isShitpost && !isAlbumActive && shitpostMinTags > 0) {
|
||||
const remaining = shitpostMinTags - Math.min(...selectedFiles.map(item => (item.tags || []).length));
|
||||
btnText.textContent = `${remaining} more tag${remaining !== 1 ? 's' : ''} required per item`;
|
||||
} else {
|
||||
@@ -1095,7 +1178,7 @@ window.initUploadForm = (selector) => {
|
||||
}
|
||||
} else if (!hasRating) {
|
||||
const nsflEnabled = !!form.querySelector('input[name="rating"][value="nsfl"]');
|
||||
if (isShitpost && shitpostRequireRating) {
|
||||
if (isShitpost && !isAlbumActive && shitpostRequireRating) {
|
||||
btnText.textContent = 'Select a rating for each item';
|
||||
} else {
|
||||
if (nsflEnabled) {
|
||||
@@ -1105,7 +1188,10 @@ window.initUploadForm = (selector) => {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (activeMode === 'url' && urlInput && ytRegex.test(urlInput.value.trim()) && window.f0ckEnableYoutubeUpload !== false) {
|
||||
if (isAlbumActive) {
|
||||
const tpl = i18n.upload_album || 'Upload Album (%s subf0cks)';
|
||||
btnText.textContent = tpl.replace('%s', selectedFiles.length);
|
||||
} else if (activeMode === 'url' && urlInput && ytRegex.test(urlInput.value.trim()) && window.f0ckEnableYoutubeUpload !== false) {
|
||||
btnText.textContent = i18n.embed_youtube || 'Embed YouTube Video';
|
||||
} else if (activeMode === 'url') {
|
||||
btnText.textContent = i18n.upload_from_url || 'Upload from URL';
|
||||
@@ -1126,15 +1212,194 @@ window.initUploadForm = (selector) => {
|
||||
}
|
||||
};
|
||||
|
||||
const renderAlbumStaging = () => {
|
||||
if (!filePreview) return;
|
||||
filePreview.style.display = 'block';
|
||||
filePreview.innerHTML = '';
|
||||
|
||||
const stagingCont = document.createElement('div');
|
||||
stagingCont.className = 'album-staging-container';
|
||||
|
||||
const stagingHeader = document.createElement('div');
|
||||
stagingHeader.className = 'album-staging-header';
|
||||
stagingHeader.innerHTML = `
|
||||
<div class="album-staging-title">
|
||||
<i class="fa-solid fa-layer-group"></i>
|
||||
<span>${(window.f0ckI18n && window.f0ckI18n.album_title) || 'Album'} (${selectedFiles.length} subf0cks)</span>
|
||||
</div>
|
||||
<button type="button" class="btn-add-album-pics">
|
||||
<i class="fa-solid fa-plus"></i> ${(window.f0ckI18n && window.f0ckI18n.album_add_more) || 'Add subf0cks'}
|
||||
</button>
|
||||
`;
|
||||
const addBtn = stagingHeader.querySelector('.btn-add-album-pics');
|
||||
if (addBtn) {
|
||||
addBtn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
if (fileInput) fileInput.click();
|
||||
});
|
||||
}
|
||||
stagingCont.appendChild(stagingHeader);
|
||||
|
||||
const grid = document.createElement('div');
|
||||
grid.className = 'album-staging-grid';
|
||||
|
||||
selectedFiles.forEach((item, index) => {
|
||||
const file = item.file || item;
|
||||
const card = document.createElement('div');
|
||||
card.className = 'album-stage-card' + (index === 0 ? ' is-cover' : '');
|
||||
|
||||
const badge = document.createElement('div');
|
||||
badge.className = 'album-stage-badge';
|
||||
badge.innerHTML = index === 0 ? '<i class="fa-solid fa-star"></i> Cover' : `#${index + 1}`;
|
||||
card.appendChild(badge);
|
||||
|
||||
const isVideo = (file.type && file.type.startsWith('video/')) || /\.(mp4|webm|mov|mkv)$/i.test(file.name || '');
|
||||
const isAudio = (file.type && file.type.startsWith('audio/')) || /\.(mp3|ogg|wav|flac|m4a|aac)$/i.test(file.name || '');
|
||||
|
||||
if (isVideo) {
|
||||
const video = document.createElement('video');
|
||||
video.src = URL.createObjectURL(file);
|
||||
video.muted = true;
|
||||
video.playsInline = true;
|
||||
video.autoplay = false;
|
||||
video.preload = 'metadata';
|
||||
card.appendChild(video);
|
||||
|
||||
const mimeBadge = document.createElement('span');
|
||||
mimeBadge.className = 'album-stage-mime-badge';
|
||||
mimeBadge.innerHTML = '<i class="fa-solid fa-play"></i>';
|
||||
card.appendChild(mimeBadge);
|
||||
} else if (isAudio) {
|
||||
const audioPreview = document.createElement('div');
|
||||
audioPreview.className = 'album-stage-audio-preview';
|
||||
audioPreview.innerHTML = `
|
||||
<i class="fa-solid fa-music"></i>
|
||||
<span class="album-stage-audio-name" title="${file.name || 'Audio'}">${file.name || 'Audio'}</span>
|
||||
`;
|
||||
card.appendChild(audioPreview);
|
||||
|
||||
const mimeBadge = document.createElement('span');
|
||||
mimeBadge.className = 'album-stage-mime-badge';
|
||||
mimeBadge.innerHTML = '<i class="fa-solid fa-music"></i>';
|
||||
card.appendChild(mimeBadge);
|
||||
} else {
|
||||
const img = document.createElement('img');
|
||||
img.src = URL.createObjectURL(file);
|
||||
img.alt = file.name || `Subf0ck ${index + 1}`;
|
||||
card.appendChild(img);
|
||||
}
|
||||
|
||||
const actions = document.createElement('div');
|
||||
actions.className = 'album-stage-actions';
|
||||
|
||||
// Move Left
|
||||
const btnLeft = document.createElement('button');
|
||||
btnLeft.type = 'button';
|
||||
btnLeft.className = 'album-action-btn btn-album-move-left';
|
||||
btnLeft.title = window.f0ckI18n?.album_move_left || 'Move left';
|
||||
btnLeft.innerHTML = '<i class="fa-solid fa-arrow-left"></i>';
|
||||
if (index === 0) {
|
||||
btnLeft.disabled = true;
|
||||
btnLeft.style.opacity = '0.3';
|
||||
btnLeft.style.cursor = 'not-allowed';
|
||||
} else {
|
||||
btnLeft.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const temp = selectedFiles[index];
|
||||
selectedFiles[index] = selectedFiles[index - 1];
|
||||
selectedFiles[index - 1] = temp;
|
||||
renderAlbumStaging();
|
||||
updateSubmitButton();
|
||||
});
|
||||
}
|
||||
actions.appendChild(btnLeft);
|
||||
|
||||
// Move Right
|
||||
const btnRight = document.createElement('button');
|
||||
btnRight.type = 'button';
|
||||
btnRight.className = 'album-action-btn btn-album-move-right';
|
||||
btnRight.title = window.f0ckI18n?.album_move_right || 'Move right';
|
||||
btnRight.innerHTML = '<i class="fa-solid fa-arrow-right"></i>';
|
||||
if (index === selectedFiles.length - 1) {
|
||||
btnRight.disabled = true;
|
||||
btnRight.style.opacity = '0.3';
|
||||
btnRight.style.cursor = 'not-allowed';
|
||||
} else {
|
||||
btnRight.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const temp = selectedFiles[index];
|
||||
selectedFiles[index] = selectedFiles[index + 1];
|
||||
selectedFiles[index + 1] = temp;
|
||||
renderAlbumStaging();
|
||||
updateSubmitButton();
|
||||
});
|
||||
}
|
||||
actions.appendChild(btnRight);
|
||||
|
||||
// Remove
|
||||
const btnRemove = document.createElement('button');
|
||||
btnRemove.type = 'button';
|
||||
btnRemove.className = 'album-action-btn btn-album-remove';
|
||||
btnRemove.title = window.f0ckI18n?.album_remove || 'Remove subf0ck';
|
||||
btnRemove.innerHTML = '<i class="fa-solid fa-xmark"></i>';
|
||||
btnRemove.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
selectedFiles.splice(index, 1);
|
||||
if (selectedFiles.length === 0) {
|
||||
if (form._f0ckUploader && typeof form._f0ckUploader.reset === 'function') {
|
||||
form._f0ckUploader.reset();
|
||||
}
|
||||
} else {
|
||||
handleFile();
|
||||
updateSubmitButton();
|
||||
}
|
||||
});
|
||||
actions.appendChild(btnRemove);
|
||||
|
||||
card.appendChild(actions);
|
||||
|
||||
const caption = document.createElement('div');
|
||||
caption.className = 'album-stage-caption';
|
||||
caption.title = file.name;
|
||||
caption.textContent = `${file.name} (${formatSize(file.size)})`;
|
||||
card.appendChild(caption);
|
||||
|
||||
grid.appendChild(card);
|
||||
});
|
||||
|
||||
// Add More Card in Grid if under 100 subf0cks
|
||||
const maxAlbumItems = 100;
|
||||
if (selectedFiles.length < maxAlbumItems) {
|
||||
const addMoreCard = document.createElement('div');
|
||||
addMoreCard.className = 'album-stage-card album-stage-add-more';
|
||||
addMoreCard.innerHTML = `
|
||||
<div class="album-add-icon"><i class="fa-solid fa-plus"></i></div>
|
||||
<div class="album-add-text">${(window.f0ckI18n && window.f0ckI18n.album_add_more) || 'Add subf0cks'}</div>
|
||||
<div class="album-add-sub">(${selectedFiles.length}/${maxAlbumItems})</div>
|
||||
`;
|
||||
addMoreCard.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
if (fileInput) fileInput.click();
|
||||
});
|
||||
grid.appendChild(addMoreCard);
|
||||
}
|
||||
|
||||
stagingCont.appendChild(grid);
|
||||
filePreview.appendChild(stagingCont);
|
||||
};
|
||||
|
||||
const handleFile = (files) => {
|
||||
const isShitpost = !!window.f0ckShitpostMode;
|
||||
|
||||
// If files were provided, process them (append or replace)
|
||||
if (files && files.length > 0) {
|
||||
const filesToProcess = isShitpost ? Array.from(files) : [files[0]];
|
||||
if (!isShitpost) {
|
||||
selectedFiles = []; // Reset for normal mode — replace, not append
|
||||
// Also wipe the preview DOM so the old card doesn't linger
|
||||
const isMultiAllowed = isShitpost || activeMode === 'album' || files.length > 1 || selectedFiles.length > 0;
|
||||
const filesToProcess = isMultiAllowed ? Array.from(files) : [files[0]];
|
||||
if (!isMultiAllowed && selectedFiles.length === 0) {
|
||||
selectedFiles = []; // Reset for normal mode single non-image file
|
||||
if (filePreview) filePreview.innerHTML = '';
|
||||
}
|
||||
|
||||
@@ -1219,18 +1484,32 @@ window.initUploadForm = (selector) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (activeMode === 'album' && selectedFiles.length >= 100) {
|
||||
const errorMsg = 'Album limit reached (maximum 100 subf0cks).';
|
||||
if (typeof window.flashMessage === 'function') window.flashMessage('✕ ' + errorMsg, 4000, 'error');
|
||||
else if (window.showFlash) window.showFlash(errorMsg, 'error');
|
||||
else if (statusDiv) { statusDiv.textContent = errorMsg; statusDiv.className = 'upload-status error'; }
|
||||
break;
|
||||
}
|
||||
|
||||
if (!selectedFiles.some(f => (f.file || f).name === file.name && (f.file || f).size === file.size)) {
|
||||
if (isShitpost) {
|
||||
selectedFiles.push({ type: 'file', file: file, rating: '', visibility: '', tags: [], comment: '', title: '', is_oc: false });
|
||||
} else {
|
||||
selectedFiles.push(file); // Legacy single file mode uses raw File
|
||||
}
|
||||
selectedFiles.push({ type: 'file', file: file, rating: '', visibility: '', tags: [], comment: '', title: '', is_oc: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle album choice container
|
||||
if (activeMode === 'album') {
|
||||
albumChoiceMode = 'album';
|
||||
}
|
||||
const isAlbumCandidate = activeMode === 'file' && selectedFiles.length > 1;
|
||||
if (albumChoiceContainer) {
|
||||
albumChoiceContainer.style.display = isAlbumCandidate ? 'flex' : 'none';
|
||||
}
|
||||
|
||||
// Rebuild UI state
|
||||
if (selectedFiles.length === 0) {
|
||||
if (albumChoiceContainer) albumChoiceContainer.style.display = 'none';
|
||||
if (filePreview) {
|
||||
filePreview.style.display = 'none';
|
||||
filePreview.innerHTML = '';
|
||||
@@ -1257,14 +1536,23 @@ window.initUploadForm = (selector) => {
|
||||
statusDiv.className = 'upload-status';
|
||||
}
|
||||
|
||||
// Force 'file' mode tab UI
|
||||
if (activeMode !== 'file' && modeTabs.length > 0) {
|
||||
// Force 'file' or 'album' mode tab UI if coming from URL mode
|
||||
if (activeMode !== 'file' && activeMode !== 'album' && modeTabs.length > 0) {
|
||||
modeTabs.forEach(t => t.classList.remove('active'));
|
||||
const fileTab = form.querySelector('.upload-mode-tab[data-mode="file"]');
|
||||
if (fileTab) fileTab.classList.add('active');
|
||||
const targetMode = isAlbumModeActive() ? 'album' : 'file';
|
||||
const targetTab = form.querySelector(`.upload-mode-tab[data-mode="${targetMode}"]`);
|
||||
if (targetTab) targetTab.classList.add('active');
|
||||
if (modeFile) modeFile.style.display = '';
|
||||
if (modeUrl) modeUrl.style.display = 'none';
|
||||
activeMode = 'file';
|
||||
activeMode = targetMode;
|
||||
}
|
||||
|
||||
// If Album Mode is active, render Album Staging
|
||||
if (isAlbumModeActive()) {
|
||||
renderAlbumStaging();
|
||||
updateSubmitButton();
|
||||
form.dispatchEvent(new CustomEvent('fileReady', { detail: { files: selectedFiles } }));
|
||||
return true;
|
||||
}
|
||||
|
||||
let lastNewPreviewItem = null;
|
||||
@@ -1962,7 +2250,7 @@ window.initUploadForm = (selector) => {
|
||||
|
||||
// Legacy Global Meta Sync (Non-Shitpost Mode)
|
||||
if (!isShitpost && selectedFiles.length > 0 && files && files.length > 0) {
|
||||
const primaryFile = selectedFiles[0];
|
||||
const primaryFile = selectedFiles[0].file || selectedFiles[0];
|
||||
autoTags = [];
|
||||
const metaCont = form.querySelector('.meta-suggestions-container');
|
||||
const metaList = form.querySelector('.meta-suggestions-list');
|
||||
@@ -2084,6 +2372,7 @@ window.initUploadForm = (selector) => {
|
||||
if (el._swfObjectUrl) { URL.revokeObjectURL(el._swfObjectUrl); el._swfObjectUrl = null; }
|
||||
});
|
||||
selectedFiles = [];
|
||||
if (albumChoiceContainer) albumChoiceContainer.style.display = 'none';
|
||||
form.querySelector('.gps-privacy-warning')?.remove();
|
||||
if (fileInput) fileInput.value = '';
|
||||
if (dropZonePrompt) dropZonePrompt.style.display = 'block';
|
||||
@@ -2475,17 +2764,22 @@ window.initUploadForm = (selector) => {
|
||||
}
|
||||
|
||||
const isFileMode = activeMode === 'file';
|
||||
const isAlbum = isAlbumModeActive() || activeMode === 'album';
|
||||
|
||||
const globalRatingEl = form.querySelector('input[name="rating"]:checked');
|
||||
|
||||
// Validation
|
||||
if (isShitpost && isFileMode) {
|
||||
if (isShitpost && isFileMode && !isAlbum) {
|
||||
if (selectedFiles.length === 0) {
|
||||
if (window.showFlash) window.showFlash('No files selected', 'error');
|
||||
return;
|
||||
}
|
||||
// No tag or rating requirement in shitpost mode — untagged items are allowed
|
||||
} else {
|
||||
if (isAlbum && selectedFiles.length < 2) {
|
||||
if (window.showFlash) window.showFlash('Add at least 2 pictures for an album', 'error');
|
||||
return;
|
||||
}
|
||||
if (!globalRatingEl) {
|
||||
if (window.showFlash) window.showFlash('Please select a rating', 'error');
|
||||
return;
|
||||
@@ -2661,6 +2955,122 @@ window.initUploadForm = (selector) => {
|
||||
// --- File Upload ---
|
||||
if (selectedFiles.length === 0) return;
|
||||
|
||||
const isAlbum = isAlbumModeActive() || activeMode === 'album';
|
||||
if (isAlbum) {
|
||||
const statusMsg = window.f0ckI18n?.uploading_album || `Uploading album (${selectedFiles.length} pictures)...`;
|
||||
setBtnLoading(statusMsg);
|
||||
if (progressContainer) progressContainer.style.display = 'flex';
|
||||
if (statusDiv) {
|
||||
statusDiv.textContent = '';
|
||||
statusDiv.className = 'upload-status';
|
||||
}
|
||||
|
||||
const globalRatingEl = form.querySelector('input[name="rating"]:checked');
|
||||
const globalVisEl = form.querySelector('input[name="visibility"]:checked');
|
||||
const globalExpiryEl = form.querySelector('select[name="expiry"], input[name="expiry"]');
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('is_album', 'true');
|
||||
formData.append('rating', globalRatingEl ? globalRatingEl.value : 'sfw');
|
||||
formData.append('visibility', globalVisEl ? globalVisEl.value : '0');
|
||||
formData.append('expiry', globalExpiryEl ? globalExpiryEl.value : 'permanent');
|
||||
formData.append('tags', tags.join(','));
|
||||
formData.append('is_oc', isOc ? 'true' : 'false');
|
||||
if (titleVal) formData.append('title', titleVal);
|
||||
if (comment) formData.append('comment', comment);
|
||||
|
||||
for (let i = 0; i < selectedFiles.length; i++) {
|
||||
const f = selectedFiles[i].file || selectedFiles[i];
|
||||
formData.append('files', f);
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await new Promise((resolve, reject) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.upload.addEventListener('progress', (e) => {
|
||||
if (e.lengthComputable) {
|
||||
const percent = Math.round((e.loaded / e.total) * 100);
|
||||
if (progressFill) progressFill.style.width = percent + '%';
|
||||
if (progressText) progressText.textContent = percent + '%';
|
||||
}
|
||||
});
|
||||
|
||||
xhr.onload = () => {
|
||||
try {
|
||||
const data = JSON.parse(xhr.responseText);
|
||||
resolve(data);
|
||||
} catch(e) {
|
||||
let msg = 'Server error';
|
||||
if (xhr.status === 413) msg = 'File too large';
|
||||
try {
|
||||
const errData = JSON.parse(xhr.responseText);
|
||||
if (errData.msg) msg = errData.msg;
|
||||
} catch(e2) {}
|
||||
reject(new Error(msg));
|
||||
}
|
||||
};
|
||||
xhr.onerror = () => reject(new Error('Connection error'));
|
||||
xhr.open('POST', '/api/v2/upload');
|
||||
const csrf = window.f0ckSession?.csrf_token || document.querySelector('input[name="csrf_token"]')?.value || '';
|
||||
xhr.setRequestHeader('X-CSRF-Token', csrf);
|
||||
xhr.send(formData);
|
||||
});
|
||||
|
||||
if (res.success) {
|
||||
if (dragModal) dragModal.classList.remove('show');
|
||||
const dropModal = document.getElementById('upload-drag-modal');
|
||||
if (dropModal) dropModal.classList.remove('show');
|
||||
form._f0ckUploader.reset();
|
||||
|
||||
const successMsg = res.msg || `Album (${res.album_count || selectedFiles.length} subf0cks) uploaded successfully!`;
|
||||
if (typeof window.flashMessage === 'function') {
|
||||
window.flashMessage(successMsg, 3000, 'success');
|
||||
} else if (!dragModal && statusDiv) {
|
||||
statusDiv.innerHTML = '✓ ' + successMsg;
|
||||
statusDiv.className = 'upload-status success';
|
||||
}
|
||||
|
||||
if (res.itemid && window.NotificationSystemInstance && typeof window.NotificationSystemInstance.handleNewItem === 'function') {
|
||||
window.NotificationSystemInstance.handleNewItem({
|
||||
id: res.itemid,
|
||||
dest: res.dest,
|
||||
mime: res.mime,
|
||||
username: res.username || window.f0ckSession?.user || '',
|
||||
display_name: res.display_name || window.f0ckSession?.display_name || null,
|
||||
tag_id: res.tag_id ?? 0,
|
||||
is_oc: !!res.is_oc,
|
||||
slug: res.slug,
|
||||
visibility: res.visibility || 0
|
||||
});
|
||||
}
|
||||
|
||||
const targetUrl = res.slug ? `/${res.slug}` : (res.itemid ? `/${res.itemid}` : '/');
|
||||
if (typeof window.loadPageAjax === 'function') {
|
||||
window.loadPageAjax(targetUrl, true, { bypassCache: true });
|
||||
} else {
|
||||
window.location.href = targetUrl;
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
const errMsg = res.msg || 'Upload failed';
|
||||
const err = new Error(errMsg);
|
||||
if (res.repost) err.repost = res.repost;
|
||||
throw err;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[ALBUM UPLOAD ERROR]', err);
|
||||
if (err.repost) {
|
||||
statusDiv.innerHTML = '✕ ' + window.escapeHtmlUpload(err.message) + ` (<a href="/${err.repost}" class="repost-link">view existing</a>)`;
|
||||
} else {
|
||||
statusDiv.textContent = '✕ ' + err.message;
|
||||
}
|
||||
statusDiv.className = 'upload-status error';
|
||||
if (progressContainer) progressContainer.style.display = 'none';
|
||||
restoreBtn();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setBtnLoading(isShitpost ? `Uploading 1/${selectedFiles.length}...` : 'Uploading...');
|
||||
if (progressContainer) progressContainer.style.display = 'flex';
|
||||
if (statusDiv) {
|
||||
@@ -2909,6 +3319,13 @@ window.initUploadForm = (selector) => {
|
||||
form.reset();
|
||||
tags = [];
|
||||
selectedFiles = [];
|
||||
if (albumChoiceContainer) albumChoiceContainer.style.display = 'none';
|
||||
albumChoiceMode = 'album';
|
||||
if (albumChoiceContainer) {
|
||||
albumChoiceContainer.querySelectorAll('.album-choice-btn').forEach(b => {
|
||||
b.classList.toggle('active', b.getAttribute('data-choice') === 'album');
|
||||
});
|
||||
}
|
||||
if (tagsList) tagsList.innerHTML = '';
|
||||
if (tagsHidden) tagsHidden.value = '';
|
||||
if (fileInput) fileInput.style.display = 'inline-block';
|
||||
@@ -2937,6 +3354,7 @@ window.initUploadForm = (selector) => {
|
||||
|
||||
// Reset mode to 'file'
|
||||
activeMode = 'file';
|
||||
form.classList.remove('album-mode-active');
|
||||
if (modeTabs.length > 0) {
|
||||
modeTabs.forEach(t => {
|
||||
if (t.dataset.mode === 'file') t.classList.add('active');
|
||||
|
||||
Reference in New Issue
Block a user