adding emoji picker to the shitpost upload form
This commit is contained in:
@@ -1166,8 +1166,53 @@
|
|||||||
user-select: text !important;
|
user-select: text !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.item-comment-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
.item-comment-input {
|
.item-comment-input {
|
||||||
min-height: 60px;
|
min-height: 60px;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Actions bar sits below the textarea, left-aligned */
|
||||||
|
.item-comment-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-emoji-trigger {
|
||||||
|
background: none;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||||
|
color: rgba(255, 255, 255, 0.5);
|
||||||
|
font-size: 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 2px 7px;
|
||||||
|
line-height: 1.4;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: color 0.15s, background 0.15s, border-color 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-emoji-trigger:hover {
|
||||||
|
color: var(--accent);
|
||||||
|
border-color: var(--accent);
|
||||||
|
background: rgba(255, 255, 255, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Picker pops up above the actions row */
|
||||||
|
.item-comment-container .item-emoji-picker,
|
||||||
|
.item-comment-container .emoji-picker {
|
||||||
|
position: relative;
|
||||||
|
bottom: calc(100% + 4px);
|
||||||
|
left: 0;
|
||||||
|
z-index: 10000;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Media column wrapper for URL items in shitpost mode:
|
/* Media column wrapper for URL items in shitpost mode:
|
||||||
|
|||||||
@@ -76,6 +76,63 @@ window.initUploadForm = (selector) => {
|
|||||||
let autoTags = []; // Track tags suggested from metadata
|
let autoTags = []; // Track tags suggested from metadata
|
||||||
let selectedFiles = []; // Array of files for shitpost_mode
|
let selectedFiles = []; // Array of files for shitpost_mode
|
||||||
let activeMode = 'file'; // 'file' or 'url'
|
let activeMode = 'file'; // 'file' or 'url'
|
||||||
|
// Shared emoji cache for per-item pickers (fetched once, reused by all items)
|
||||||
|
let _emojiCache = null;
|
||||||
|
let _emojiCachePromise = null;
|
||||||
|
const getEmojis = () => {
|
||||||
|
if (_emojiCache) return Promise.resolve(_emojiCache);
|
||||||
|
if (_emojiCachePromise) return _emojiCachePromise;
|
||||||
|
_emojiCachePromise = fetch('/api/v2/emojis')
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success && data.emojis) {
|
||||||
|
_emojiCache = {};
|
||||||
|
data.emojis.forEach(e => { _emojiCache[e.name] = e.url; });
|
||||||
|
}
|
||||||
|
return _emojiCache || {};
|
||||||
|
})
|
||||||
|
.catch(() => ({}));
|
||||||
|
return _emojiCachePromise;
|
||||||
|
};
|
||||||
|
const setupItemEmojiPicker = (textarea, triggerBtn) => {
|
||||||
|
// Always use standalone picker for per-item comments
|
||||||
|
let picker = null;
|
||||||
|
triggerBtn.addEventListener('click', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
if (picker) {
|
||||||
|
picker.style.display = picker.style.display === 'none' ? '' : 'none';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const emojis = await getEmojis();
|
||||||
|
if (!emojis || !Object.keys(emojis).length) return;
|
||||||
|
picker = document.createElement('div');
|
||||||
|
picker.className = 'emoji-picker item-emoji-picker';
|
||||||
|
Object.keys(emojis).forEach(name => {
|
||||||
|
const img = document.createElement('img');
|
||||||
|
img.src = emojis[name];
|
||||||
|
img.title = `:${name}:`;
|
||||||
|
img.loading = 'lazy';
|
||||||
|
img.onerror = () => { img.style.display = 'none'; };
|
||||||
|
img.onclick = (ev) => {
|
||||||
|
ev.stopPropagation();
|
||||||
|
textarea.value += ` :${name}: `;
|
||||||
|
textarea.dispatchEvent(new Event('input'));
|
||||||
|
textarea.focus();
|
||||||
|
};
|
||||||
|
picker.appendChild(img);
|
||||||
|
});
|
||||||
|
const container = triggerBtn.closest('.item-comment-container');
|
||||||
|
container.appendChild(picker);
|
||||||
|
const closeHandler = (ev) => {
|
||||||
|
if (!picker.contains(ev.target) && ev.target !== triggerBtn) {
|
||||||
|
picker.style.display = 'none';
|
||||||
|
document.removeEventListener('click', closeHandler);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
setTimeout(() => document.addEventListener('click', closeHandler), 0);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// If shitpost mode is active, the global rating is hidden and per-item ratings are used.
|
// If shitpost mode is active, the global rating is hidden and per-item ratings are used.
|
||||||
// We must remove the 'required' attribute from global radios to prevent browser from blocking submission.
|
// We must remove the 'required' attribute from global radios to prevent browser from blocking submission.
|
||||||
@@ -812,7 +869,12 @@ window.initUploadForm = (selector) => {
|
|||||||
|
|
||||||
const commentPlaceholder = window.f0ckI18n?.upload_comment_placeholder || 'Comment (optional)...';
|
const commentPlaceholder = window.f0ckI18n?.upload_comment_placeholder || 'Comment (optional)...';
|
||||||
commentUI = `
|
commentUI = `
|
||||||
<textarea class="item-comment-input" placeholder="${window.escapeHtmlUpload(commentPlaceholder)}">${window.escapeHtmlUpload(item.comment || '')}</textarea>
|
<div class="item-comment-container">
|
||||||
|
<textarea class="item-comment-input" placeholder="${window.escapeHtmlUpload(commentPlaceholder)}">${window.escapeHtmlUpload(item.comment || '')}</textarea>
|
||||||
|
<div class="item-comment-actions">
|
||||||
|
<button type="button" class="item-emoji-trigger" title="Emoji">☺</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -839,8 +901,10 @@ window.initUploadForm = (selector) => {
|
|||||||
|
|
||||||
// Handle Comment
|
// Handle Comment
|
||||||
const commentInput = infoRow.querySelector('.item-comment-input');
|
const commentInput = infoRow.querySelector('.item-comment-input');
|
||||||
|
const emojiTrigger = infoRow.querySelector('.item-emoji-trigger');
|
||||||
if (commentInput) {
|
if (commentInput) {
|
||||||
commentInput.oninput = () => { item.comment = commentInput.value; };
|
commentInput.oninput = () => { item.comment = commentInput.value; };
|
||||||
|
if (emojiTrigger) setupItemEmojiPicker(commentInput, emojiTrigger);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle Tags
|
// Handle Tags
|
||||||
|
|||||||
Reference in New Issue
Block a user