Update conversations.php
This commit is contained in:
+141
-24
@@ -665,14 +665,13 @@
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.reaction-badge {
|
||||
display: inline-block;
|
||||
background: rgba(0,0,0,0.06);
|
||||
padding: 2px 6px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.reaction-badge { background:#fff; border:1px solid rgba(0,0,0,0.06); display:inline-flex; align-items:center; justify-content:center; padding:4px 6px; border-radius:14px; font-size:13px; margin-top:6px; }
|
||||
|
||||
/* Reaction picker */
|
||||
#reaction-picker { position:fixed; display:none; z-index:2000; background:#fff; border-radius:10px; box-shadow:0 6px 24px rgba(0,0,0,0.16); padding:8px; min-width:160px; transition:transform .12s ease, opacity .12s ease; transform:scale(.96); opacity:0; }
|
||||
#reaction-picker.show { transform:scale(1); opacity:1; }
|
||||
#reaction-picker .emoji { font-size:18px; padding:6px; cursor:pointer; border-radius:6px; margin:4px; display:inline-flex; align-items:center; justify-content:center; }
|
||||
#reaction-picker .emoji:hover { background: rgba(0,0,0,0.04); }
|
||||
|
||||
.conversation-avatar img, .conversation-avatar-img { max-width: 44px; max-height:44px; border-radius:50%; }
|
||||
.conversation-avatar-wrapper { width:48px; display:flex; align-items:center; justify-content:center }
|
||||
@@ -755,7 +754,7 @@
|
||||
<button class="btn btn-sm btn-outline-secondary" id="bot-toggle">Bot: On</button>
|
||||
</div>
|
||||
<button class="btn btn-sm btn-outline-secondary" id="mark-unread-btn" title="Marcar conversación como no leída" style="display:none;">Marcar no leído</button>
|
||||
<button class="btn btn-sm btn-outline-danger" id="delete-conversation-btn" title="Eliminar conversación"><i class="fas fa-trash"></i></button>
|
||||
<button class="btn btn-sm btn-outline-danger" id="delete-conversation-btn" title="Eliminar conversación" style="display:none;"><i class="fas fa-trash"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1107,6 +1106,20 @@
|
||||
this.sendMessage();
|
||||
});
|
||||
|
||||
// Forward handler: open preview when user chooses to forward an existing message
|
||||
this.forwardMessage = function(messageId) {
|
||||
try {
|
||||
const input = document.getElementById('message-input');
|
||||
if (input) {
|
||||
input.dataset.forwardTo = messageId;
|
||||
}
|
||||
this.showReplyPreview(messageId, 'forward');
|
||||
// Optionally focus the recipient selection by showing a hint or opening the sidebar
|
||||
} catch (e) {
|
||||
console.warn('forwardMessage failed', e);
|
||||
}
|
||||
};
|
||||
|
||||
// Edit user name button
|
||||
const editUserBtn = document.getElementById('edit-user-btn');
|
||||
if (editUserBtn) editUserBtn.addEventListener('click', () => this.promptEditUser());
|
||||
@@ -1116,19 +1129,77 @@
|
||||
const input = document.getElementById('message-input');
|
||||
if (!input) return;
|
||||
input.dataset.replyTo = messageId;
|
||||
this.showReplyPreview(messageId);
|
||||
input.dataset.forwardTo = null;
|
||||
this.showReplyPreview(messageId, 'reply');
|
||||
input.focus();
|
||||
};
|
||||
|
||||
// React to message placeholder
|
||||
this.reactToMessage = function(messageId) {
|
||||
// simple emoji picker or quick reaction; for now show a prompt
|
||||
const e = prompt('Reaccionar con emoji (ej: 👍):');
|
||||
if (!e) return;
|
||||
fetch('api/react_message.php', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ message_id: messageId, emoji: e }) })
|
||||
.then(r => r.json()).then(j => { if (j && j.success) { this.loadconversations(this.currentUserId, false); } else { alert('Error reaccionando'); } }).catch(err => { console.error(err); alert('Error reaccionando'); });
|
||||
// Reaction picker: open a small emoji palette near the click
|
||||
this.openReactionPicker = function(ev, messageId) {
|
||||
if (ev && ev.stopPropagation) ev.stopPropagation();
|
||||
const picker = document.getElementById('reaction-picker');
|
||||
if (!picker) return;
|
||||
this._pendingReactionMessage = messageId;
|
||||
|
||||
// position picker near event or center
|
||||
picker.style.display = 'block';
|
||||
picker.classList.remove('show');
|
||||
// compute position
|
||||
let left = (window.innerWidth / 2) - (picker.offsetWidth / 2);
|
||||
let top = (window.innerHeight / 2) - (picker.offsetHeight / 2);
|
||||
if (ev && ev.clientX) {
|
||||
left = Math.min(Math.max(8, ev.clientX - 40), window.innerWidth - picker.offsetWidth - 8);
|
||||
top = Math.min(Math.max(8, ev.clientY - 24), window.innerHeight - picker.offsetHeight - 8);
|
||||
}
|
||||
picker.style.left = left + 'px';
|
||||
picker.style.top = top + 'px';
|
||||
|
||||
// small delay to allow CSS transition
|
||||
setTimeout(() => picker.classList.add('show'), 10);
|
||||
};
|
||||
|
||||
// Hide reaction picker on document click
|
||||
document.addEventListener('click', (ev) => {
|
||||
const picker = document.getElementById('reaction-picker');
|
||||
if (picker && picker.style.display === 'block' && !picker.contains(ev.target)) {
|
||||
picker.classList.remove('show');
|
||||
setTimeout(()=>{ picker.style.display='none'; },160);
|
||||
}
|
||||
});
|
||||
|
||||
// Attach click handler for emojis (event delegation)
|
||||
document.addEventListener('click', async (ev) => {
|
||||
const t = ev.target;
|
||||
if (t && t.dataset && t.dataset.emoji) {
|
||||
ev.stopPropagation();
|
||||
const emoji = t.dataset.emoji;
|
||||
const mid = this._pendingReactionMessage;
|
||||
if (!mid) return;
|
||||
try {
|
||||
const resp = await fetch('api/react_message.php', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ message_id: mid, emoji: emoji }) });
|
||||
const j = await resp.json();
|
||||
if (j && j.success) {
|
||||
// update local model and UI
|
||||
const m = this.conversations.find(x => x.message_id == mid || x.id == mid);
|
||||
if (m) m.reaction_emoji = emoji;
|
||||
this.updateMessageReactionInView && this.updateMessageReactionInView(mid, emoji);
|
||||
} else {
|
||||
showAlert && showAlert('Error aplicando reacción', 'danger');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Reaction error', err);
|
||||
showAlert && showAlert('Error aplicando reacción', 'danger');
|
||||
} finally {
|
||||
const picker = document.getElementById('reaction-picker');
|
||||
if (picker) {
|
||||
picker.classList.remove('show');
|
||||
setTimeout(()=>{ picker.style.display='none'; },160);
|
||||
}
|
||||
this._pendingReactionMessage = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Message type (text/template)
|
||||
const typeSelect = document.getElementById('message-type');
|
||||
if (typeSelect) {
|
||||
@@ -2047,14 +2118,15 @@
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="message ${msg.direction}">
|
||||
<div class="message ${msg.direction}" data-message-id="${escapeHtml(msg.message_id || msg.id || '')}">
|
||||
<div class="message-bubble">
|
||||
${replyHtml}
|
||||
<div class="message-content">${content}</div>
|
||||
${reactionHtml}
|
||||
<div class="message-actions mt-1">
|
||||
<button class="btn btn-sm btn-link" onclick="chat.promptReply('${msg.message_id}')" title="Responder"><i class="fas fa-reply"></i></button>
|
||||
<button class="btn btn-sm btn-link" onclick="chat.reactToMessage('${msg.message_id}')" title="Reaccionar"><i class="far fa-grin"></i></button>
|
||||
<button class="btn btn-sm btn-link" onclick="chat.forwardMessage('${msg.message_id}')" title="Reenviar"><i class="fas fa-share"></i></button>
|
||||
<button class="btn btn-sm btn-link" onclick="chat.openReactionPicker(event, '${msg.message_id}')" title="Reaccionar"><i class="far fa-grin"></i></button>
|
||||
</div>
|
||||
<div class="message-time">
|
||||
${time}
|
||||
@@ -2077,6 +2149,32 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Update reaction badge for a single message in the DOM
|
||||
updateMessageReactionInView(messageId, emoji) {
|
||||
try {
|
||||
const container = document.getElementById('chat-conversations');
|
||||
if (!container) return;
|
||||
const el = container.querySelector(`[data-message-id="${messageId}"]`);
|
||||
if (!el) return;
|
||||
let badge = el.querySelector('.reaction-badge');
|
||||
if (emoji) {
|
||||
if (badge) {
|
||||
badge.textContent = emoji;
|
||||
} else {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'reaction-badge';
|
||||
div.textContent = emoji;
|
||||
const bubble = el.querySelector('.message-bubble');
|
||||
if (bubble) bubble.insertBefore(div, bubble.querySelector('.message-actions'));
|
||||
}
|
||||
} else {
|
||||
if (badge && badge.parentNode) badge.parentNode.removeChild(badge);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('updateMessageReactionInView failed', e);
|
||||
}
|
||||
}
|
||||
|
||||
async loadQuickReplies() {
|
||||
const panel = document.getElementById('quick-replies-panel');
|
||||
if (!panel) return;
|
||||
@@ -2200,15 +2298,16 @@
|
||||
this.scrollToBottom();
|
||||
}
|
||||
|
||||
showReplyPreview(messageId) {
|
||||
showReplyPreview(messageId, type = 'reply') {
|
||||
try {
|
||||
const preview = document.getElementById('reply-preview');
|
||||
const previewText = document.getElementById('reply-preview-text');
|
||||
const cancelBtn = document.getElementById('cancel-reply-btn');
|
||||
const msg = this.conversations.find(m => m.message_id == messageId);
|
||||
const text = msg ? (msg.content || msg.message_text || '[Mensaje]') : ('Mensaje ' + messageId);
|
||||
const msg = this.conversations.find(m => m.message_id == messageId || m.id == messageId);
|
||||
const text = msg ? (msg.content || msg.message_text || (msg.caption || '[Mensaje]')) : ('Mensaje ' + messageId);
|
||||
if (preview && previewText) {
|
||||
previewText.textContent = 'En respuesta a: ' + (String(text).replace(/\n/g,' ')).substring(0,140);
|
||||
if (type === 'reply') previewText.textContent = 'En respuesta a: ' + (String(text).replace(/\n/g,' ')).substring(0,140);
|
||||
else if (type === 'forward') previewText.textContent = 'Reenviando: ' + (String(text).replace(/\n/g,' ')).substring(0,140);
|
||||
preview.style.display = 'flex';
|
||||
}
|
||||
if (cancelBtn) {
|
||||
@@ -2224,7 +2323,10 @@
|
||||
const preview = document.getElementById('reply-preview');
|
||||
if (preview) preview.style.display = 'none';
|
||||
const input = document.getElementById('message-input');
|
||||
if (input) input.dataset.replyTo = null;
|
||||
if (input) {
|
||||
input.dataset.replyTo = null;
|
||||
input.dataset.forwardTo = null;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('hideReplyPreview failed', e);
|
||||
}
|
||||
@@ -2747,5 +2849,20 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Reaction picker -->
|
||||
<div id="reaction-picker" aria-hidden="true">
|
||||
<div style="display:flex; flex-wrap:wrap; gap:6px;">
|
||||
<button class="emoji" data-emoji="👍">👍</button>
|
||||
<button class="emoji" data-emoji="❤️">❤️</button>
|
||||
<button class="emoji" data-emoji="😂">😂</button>
|
||||
<button class="emoji" data-emoji="😮">😮</button>
|
||||
<button class="emoji" data-emoji="😢">😢</button>
|
||||
<button class="emoji" data-emoji="👏">👏</button>
|
||||
<button class="emoji" data-emoji="🎉">🎉</button>
|
||||
<button class="emoji" data-emoji="🔥">🔥</button>
|
||||
<button class="emoji" data-emoji="🙏">🙏</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user