diff --git a/Templates/GlobalChat/widget.tpl b/Templates/GlobalChat/widget.tpl index 2338a984..0eebc1fd 100644 --- a/Templates/GlobalChat/widget.tpl +++ b/Templates/GlobalChat/widget.tpl @@ -14,9 +14,6 @@ ## - MH si Admin evidentiati fata de restul jucatorilor ## ## - format afisare: [TAG] Nume, sau doar Nume daca nu are alianta ## ## ## -## Faza 2 (09.09.2026): stergere mesaj (mod), editare mesaj propriu (user), ## -## sondaje, emoji, si fix la badge-ul de mesaje necitite (vezi loadInitial). ## -## ## ## Nu depinde de MooTools/jQuery (evita coliziuni cu $ / $$ din unx.js / ## ## mt-full.js) - JS vanilla, prefixat "gchat_" peste tot. ## ## ## @@ -39,20 +36,7 @@
- - - - @@ -163,11 +147,8 @@ .gchat_badge_role.admin { background: #b8290a; } .gchat_badge_role.mh { background: #1a5aa8; } - /* Faza 2: actiuni (edit propriu + moderare) - o singura clasa flat, - ca sa nu se dubleze margin-left cand cele doua se combina pe acelasi rand. - Inlocuieste .gchat_mod_actions din Faza 1 (nu mai e generata de JS). */ - .gchat_actions { margin-left: 4px; } - .gchat_actions a { + .gchat_mod_actions { margin-left: 4px; } + .gchat_mod_actions a { font-size: 10px; color: #888; text-decoration: none; @@ -175,59 +156,7 @@ margin-right: 4px; cursor: pointer; } - .gchat_actions a:hover { color: #b8290a; } - - .gchat_text_deleted { color: #999; font-style: italic; } - .gchat_edited_tag { color: #999; font-size: 10px; } - - .gchat_edit_bar { display: flex; gap: 4px; margin-top: 3px; } - .gchat_edit_bar input { - flex: 1; - font-size: 11px; - padding: 2px 4px; - border: 1px solid #ccc; - border-radius: 3px; - } - .gchat_edit_bar a { - font-size: 10px; - color: #6b8f47; - cursor: pointer; - align-self: center; - text-decoration: none; - } - - /* sondaje */ - .gchat_poll_block { - margin-top: 3px; - padding: 5px 6px; - background: #f2f0e6; - border: 1px solid #ddd6bd; - border-radius: 4px; - } - .gchat_poll_question { font-weight: bold; margin-bottom: 4px; } - .gchat_poll_option { - position: relative; - display: block; - width: 100%; - text-align: left; - margin-bottom: 3px; - padding: 3px 6px; - border: 1px solid #ccc; - border-radius: 3px; - background: #fff; - cursor: pointer; - overflow: hidden; - font-size: 11px; - } - .gchat_poll_bar { - position: absolute; - left: 0; top: 0; bottom: 0; - background: #cfe0bb; - z-index: 0; - } - .gchat_poll_option_label { position: relative; z-index: 1; } - .gchat_poll_option_mine { border-color: #6b8f47; } - .gchat_poll_total { font-size: 10px; color: #888; margin-top: 2px; } + .gchat_mod_actions a:hover { color: #b8290a; } #gchat_notice { padding: 4px 8px; @@ -261,80 +190,6 @@ #gchat_input:disabled { background: #eee; color: #999; } - .gchat_toolbar_btn { - border: none; - background: none; - font-size: 16px; - line-height: 1; - cursor: pointer; - padding: 0 4px; - flex-shrink: 0; - } - - #gchat_emoji_panel { - display: none; - grid-template-columns: repeat(8, 1fr); - gap: 2px; - padding: 6px; - max-height: 110px; - overflow-y: auto; - border-top: 1px solid #ddd; - flex-shrink: 0; - } - .gchat_emoji_item { - border: none; - background: none; - font-size: 16px; - cursor: pointer; - padding: 2px; - border-radius: 3px; - } - .gchat_emoji_item:hover { background: #eee; } - - #gchat_poll_form { - display: none; - padding: 6px 8px; - border-top: 1px solid #ddd; - flex-shrink: 0; - background: #f7f5ef; - } - #gchat_poll_form input[type="text"] { - width: 100%; - box-sizing: border-box; - margin-bottom: 4px; - padding: 4px 6px; - font-size: 12px; - border: 1px solid #ccc; - border-radius: 3px; - } - .gchat_poll_option_row { - display: flex; - align-items: center; - gap: 4px; - margin-bottom: 4px; - } - .gchat_poll_option_row input { flex: 1; } - .gchat_poll_option_remove { - border: none; - background: #ddd; - border-radius: 3px; - width: 20px; - height: 20px; - cursor: pointer; - flex-shrink: 0; - } - .gchat_poll_form_actions { display: flex; justify-content: space-between; gap: 6px; } - .gchat_poll_form_actions button { - flex: 1; - padding: 4px; - font-size: 11px; - border: none; - border-radius: 3px; - cursor: pointer; - } - #gchat_poll_add_option { background: #ddd; } - #gchat_poll_submit { background: #6b8f47; color: #fff; } - @media (max-width: 480px) { #gchat_panel { right: 8px; bottom: 66px; } #gchat_bubble { right: 8px; bottom: 8px; } @@ -357,17 +212,7 @@ block: , unmute: , adminBadge: , - mhBadge: , - edit: , - save: , - cancel: , - editedTag: , - deleteMsg: , - confirmDelete: , - deletedPlaceholder: , - pollOptionPlaceholder: , - pollVotesWord: , - errorGeneric: + mhBadge: }; var ACCESS_ADMIN = 9, ACCESS_MH = 8; @@ -384,12 +229,6 @@ var notice = document.getElementById('gchat_notice'); var form = document.getElementById('gchat_form'); var input = document.getElementById('gchat_input'); - var emojiBtn = document.getElementById('gchat_emoji_btn'); - var emojiPanel = document.getElementById('gchat_emoji_panel'); - var pollBtn = document.getElementById('gchat_poll_btn'); - var pollForm = document.getElementById('gchat_poll_form'); - var pollQuestionInput = document.getElementById('gchat_poll_question'); - var pollOptionsBox = document.getElementById('gchat_poll_options'); var myUid = parseInt(root.getAttribute('data-uid'), 10) || 0; var lastId = 0; @@ -398,14 +237,6 @@ var viewerIsMod = false; var viewerAccess = 0; var pollTimer = null; - // Faza 2: watermark de timp pentru editari/stergeri/voturi pe mesaje deja - // afisate (separat de 'lastId', care e watermark pe mesaje NOI) - initializat - // la "acum", nu la 0, ca sa nu tragem tot istoricul de mutatii de la - // pornirea serverului (irelevant - contam doar ce s-a schimbat de cand - // pagina curenta a fost incarcata) - var lastMutationTs = Math.floor(Date.now() / 1000); - var MAX_POLL_OPTIONS = 6; - var EMOJI_LIST = ['😀','😂','😅','😊','😍','😎','🤔','😴','😭','😡','👍','👎','👏','🙏','💪','🔥','⭐','❤️','💯','🎉','⚔️','🛡️','🏰','🌾','🪵','⛏️','🧱','⏳','🐎','🏆']; function fmtTime(unixTs) { var d = new Date(unixTs * 1000); @@ -420,177 +251,41 @@ function escapeForLog(s) { return s; } // (folosim textContent peste tot mai jos - nu innerHTML pe input de utilizator) - function appendModActionLinks(parentSpan, row) { - if (!viewerIsMod) { return; } - + function renderModActions(row) { var targetAccess = parseInt(row.access, 10) || 0; var targetUid = parseInt(row.id_user, 10) || 0; + if (!viewerIsMod || targetUid === myUid || targetAccess >= viewerAccess) { + return null; + } + + var span = document.createElement('span'); + span.className = 'gchat_mod_actions'; + function actionLink(label, handler) { var a = document.createElement('a'); a.textContent = label; a.addEventListener('click', handler); - parentSpan.appendChild(a); + span.appendChild(a); } - // mute/block: doar pe alt user, cu rang strict mai mic (neschimbat) - if (targetUid !== myUid && targetAccess < viewerAccess) { - actionLink(GCHAT_TXT.mute5, function () { doModerate('gchat_mute', row.id_user, 5); }); - actionLink(GCHAT_TXT.mute30, function () { doModerate('gchat_mute', row.id_user, 30); }); - actionLink(GCHAT_TXT.mute60, function () { doModerate('gchat_mute', row.id_user, 60); }); - actionLink(GCHAT_TXT.mute1440, function () { doModerate('gchat_mute', row.id_user, 1440); }); - actionLink(GCHAT_TXT.block, function () { - if (window.confirm(GCHAT_TXT.confirmBlock)) { - doModerate('gchat_block', row.id_user, 0); - } - }); - actionLink(GCHAT_TXT.unmute, function () { doModerate('gchat_unmute', row.id_user, 0); }); - } - - // Faza 2: stergere mesaj - NU e limitata de rang (vezi comentariul din - // Database::deleteGlobalChatMessage) - orice MH/Admin poate sterge orice - // mesaj, inclusiv al altui MH sau al lui insusi, ca sa poata curata - // rapid continut vulgar - actionLink(GCHAT_TXT.deleteMsg, function () { - if (window.confirm(GCHAT_TXT.confirmDelete)) { - deleteMessage(row.id); + actionLink(GCHAT_TXT.mute5, function () { doModerate('gchat_mute', row.id_user, 5); }); + actionLink(GCHAT_TXT.mute30, function () { doModerate('gchat_mute', row.id_user, 30); }); + actionLink(GCHAT_TXT.mute60, function () { doModerate('gchat_mute', row.id_user, 60); }); + actionLink(GCHAT_TXT.mute1440, function () { doModerate('gchat_mute', row.id_user, 1440); }); + actionLink(GCHAT_TXT.block, function () { + if (window.confirm(GCHAT_TXT.confirmBlock)) { + doModerate('gchat_block', row.id_user, 0); } }); - } + actionLink(GCHAT_TXT.unmute, function () { doModerate('gchat_unmute', row.id_user, 0); }); - function deleteMessage(id) { - fetch(AJAX_URL + '?f=gchat_delete', { - method: 'POST', - credentials: 'same-origin', - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - body: 'id=' + encodeURIComponent(id) - }).then(function (r) { return r.json(); }) - .then(function (data) { - if (data && data.ok) { - fetchUpdatesNow(); - } else { - showNotice(GCHAT_TXT.errorGeneric); - } - }); - } - - function startEdit(line, row, textSpan) { - var actions = line.querySelector('.gchat_actions'); - var original = textSpan.textContent; - - var editInput = document.createElement('input'); - editInput.type = 'text'; - editInput.className = 'gchat_edit_input'; - editInput.maxLength = 250; - editInput.value = original; - - var saveBtn = document.createElement('a'); - saveBtn.textContent = GCHAT_TXT.save; - var cancelBtn = document.createElement('a'); - cancelBtn.textContent = GCHAT_TXT.cancel; - - var editBar = document.createElement('div'); - editBar.className = 'gchat_edit_bar'; - editBar.appendChild(editInput); - editBar.appendChild(saveBtn); - editBar.appendChild(cancelBtn); - - textSpan.style.display = 'none'; - if (actions) { actions.style.display = 'none'; } - line.appendChild(editBar); - editInput.focus(); - - function cleanup() { - editBar.remove(); - textSpan.style.display = ''; - if (actions) { actions.style.display = ''; } - } - - cancelBtn.addEventListener('click', cleanup); - - saveBtn.addEventListener('click', function () { - var newMsg = editInput.value.trim(); - if (!newMsg) { return; } - - fetch(AJAX_URL + '?f=gchat_edit', { - method: 'POST', - credentials: 'same-origin', - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - body: 'id=' + encodeURIComponent(row.id) + '&msg=' + encodeURIComponent(newMsg) - }).then(function (r) { return r.json(); }) - .then(function (data) { - cleanup(); - if (data && data.ok) { - fetchUpdatesNow(); - } else { - showNotice(GCHAT_TXT.errorGeneric); - } - }); - }); - } - - function renderPollBlock(pollData, msgId) { - var wrap = document.createElement('div'); - wrap.className = 'gchat_poll_block'; - if (!pollData) { return wrap; } - - var q = document.createElement('div'); - q.className = 'gchat_poll_question'; - q.textContent = pollData.question; - wrap.appendChild(q); - - var total = pollData.total || 0; - (pollData.options || []).forEach(function (optText, idx) { - var count = (pollData.counts && pollData.counts[idx]) || 0; - var pct = total > 0 ? Math.round((count / total) * 100) : 0; - var isMine = pollData.myVote !== null && pollData.myVote !== undefined - && parseInt(pollData.myVote, 10) === idx; - - var optBtn = document.createElement('button'); - optBtn.type = 'button'; - optBtn.className = 'gchat_poll_option' + (isMine ? ' gchat_poll_option_mine' : ''); - - var bar = document.createElement('span'); - bar.className = 'gchat_poll_bar'; - bar.style.width = pct + '%'; - optBtn.appendChild(bar); - - var label = document.createElement('span'); - label.className = 'gchat_poll_option_label'; - label.textContent = optText + ' \u2014 ' + pct + '% (' + count + ')'; - optBtn.appendChild(label); - - optBtn.addEventListener('click', function () { - fetch(AJAX_URL + '?f=gchat_poll_vote', { - method: 'POST', - credentials: 'same-origin', - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - body: 'pollId=' + encodeURIComponent(pollData.id) + '&option=' + encodeURIComponent(idx) - }).then(function (r) { return r.json(); }) - .then(function (data) { - if (data && data.ok) { - fetchUpdatesNow(); - } else { - showNotice(GCHAT_TXT.errorGeneric); - } - }); - }); - - wrap.appendChild(optBtn); - }); - - var totalLine = document.createElement('div'); - totalLine.className = 'gchat_poll_total'; - totalLine.textContent = total + ' ' + GCHAT_TXT.pollVotesWord; - wrap.appendChild(totalLine); - - return wrap; + return span; } function renderMessage(row) { var line = document.createElement('div'); line.className = 'gchat_msg'; - line.id = 'gchat_msg_' + row.id; var time = document.createElement('span'); time.className = 'gchat_time'; @@ -624,113 +319,18 @@ line.appendChild(document.createTextNode(': ')); - // Faza 2: mesaj sters de moderator - text golit server-side, aici doar - // afisam un placeholder; nicio actiune (nimic de moderat pe un mesaj deja sters) - if (parseInt(row.deleted, 10) === 1) { - var delText = document.createElement('span'); - delText.className = 'gchat_text gchat_text_deleted'; - delText.textContent = GCHAT_TXT.deletedPlaceholder; - line.appendChild(delText); - return line; - } - - // Faza 2: sondaj - randare separata, doar cu actiunea de stergere (mod) - if (row.type === 'poll') { - line.appendChild(renderPollBlock(row.poll, row.id)); - - var pollActions = document.createElement('span'); - pollActions.className = 'gchat_actions'; - appendModActionLinks(pollActions, row); - if (pollActions.childNodes.length) { line.appendChild(pollActions); } - - return line; - } - var text = document.createElement('span'); text.className = 'gchat_text'; text.textContent = row.msg; line.appendChild(text); - var editedTag = document.createElement('span'); - editedTag.className = 'gchat_edited_tag'; - editedTag.textContent = ' ' + GCHAT_TXT.editedTag; - editedTag.style.display = parseInt(row.edited, 10) === 1 ? 'inline' : 'none'; - line.appendChild(editedTag); - - var actions = document.createElement('span'); - actions.className = 'gchat_actions'; - - var targetUid = parseInt(row.id_user, 10) || 0; - if (targetUid === myUid) { - var editLink = document.createElement('a'); - editLink.textContent = GCHAT_TXT.edit; - editLink.addEventListener('click', function () { startEdit(line, row, text); }); - actions.appendChild(editLink); - } - - appendModActionLinks(actions, row); - - if (actions.childNodes.length) { line.appendChild(actions); } + var mod = renderModActions(row); + if (mod) { line.appendChild(mod); } return line; } - function applyMutation(row) { - var line = document.getElementById('gchat_msg_' + row.id); - if (!line) { return; } - - if (parseInt(row.deleted, 10) === 1) { - var textEl = line.querySelector('.gchat_text'); - var pollEl = line.querySelector('.gchat_poll_block'); - var actionsEl = line.querySelector('.gchat_actions'); - var editedTagEl = line.querySelector('.gchat_edited_tag'); - if (pollEl) { pollEl.remove(); } - if (actionsEl) { actionsEl.remove(); } - if (editedTagEl) { editedTagEl.remove(); } - if (textEl) { - textEl.textContent = GCHAT_TXT.deletedPlaceholder; - textEl.className = 'gchat_text gchat_text_deleted'; - } else { - var span = document.createElement('span'); - span.className = 'gchat_text gchat_text_deleted'; - span.textContent = GCHAT_TXT.deletedPlaceholder; - line.appendChild(span); - } - return; - } - - if (row.type === 'poll' && row.poll) { - var existingPoll = line.querySelector('.gchat_poll_block'); - var freshPoll = renderPollBlock(row.poll, row.id); - if (existingPoll) { - existingPoll.replaceWith(freshPoll); - } else { - line.appendChild(freshPoll); - } - return; - } - - var textEl2 = line.querySelector('.gchat_text'); - var editedTagEl2 = line.querySelector('.gchat_edited_tag'); - if (textEl2) { textEl2.textContent = row.msg; } - if (editedTagEl2 && parseInt(row.edited, 10) === 1) { - editedTagEl2.style.display = 'inline'; - } - } - - function fetchUpdatesNow() { - fetch(AJAX_URL + '?f=gchat_updates&sinceTs=' + lastMutationTs, { credentials: 'same-origin' }) - .then(function (r) { return r.json(); }) - .then(function (data) { - if (!data || !data.ok) { return; } - (data.rows || []).forEach(applyMutation); - lastMutationTs = data.now; - }) - .catch(function () { /* reincercam la urmatorul tick */ }); - } - - function appendMessages(rows, countUnread) { - if (countUnread === undefined) { countUnread = true; } + function appendMessages(rows) { var wasAtBottom = messagesBox.scrollTop + messagesBox.clientHeight >= messagesBox.scrollHeight - 4; rows.forEach(function (row) { @@ -742,7 +342,7 @@ messagesBox.scrollTop = messagesBox.scrollHeight; } - if (countUnread && rows.length && !isOpen) { + if (rows.length && !isOpen) { unread += rows.length; badge.textContent = unread > 99 ? '99+' : String(unread); badge.style.display = 'inline-block'; @@ -765,10 +365,6 @@ } } - // NOTA: numele functiei 'poll()' vine de la mecanismul de long-polling - // pentru mesaje NOI (dupa id) - nu are legatura cu sondajele (chat_global_polls, - // "gchat_poll_create/vote" mai jos); denumire mostenita din Faza 1, pastrata - // ca sa nu umblam degeaba prin tot fisierul. function poll() { fetch(AJAX_URL + '?f=gchat_poll&sinceId=' + lastId, { credentials: 'same-origin' }) .then(function (r) { return r.json(); }) @@ -778,8 +374,6 @@ appendMessages(data.messages || []); }) .catch(function () { /* hiccup de retea - reincercam la urmatorul tick */ }); - - fetchUpdatesNow(); } function loadInitial() { @@ -795,13 +389,7 @@ messagesBox.appendChild(empty); } applyViewerState(data.viewer || {}); - // FIX (Faza 2, 09.09.2026): istoricul incarcat la deschiderea/ - // reincarcarea paginii NU e "necitit" - inainte se aduna la - // contorul de mesaje noi de fiecare data cand pagina se - // (re)incarca, motiv pentru care badge-ul arata numarul total - // de mesaje din istoric (ex. 17), nu doar cele aparute cat timp - // panoul a stat efectiv inchis. - appendMessages(data.messages || [], false); + appendMessages(data.messages || []); }); } @@ -869,108 +457,6 @@ }); }); - function insertAtCursor(field, text) { - var start = field.selectionStart != null ? field.selectionStart : field.value.length; - var end = field.selectionEnd != null ? field.selectionEnd : field.value.length; - field.value = field.value.slice(0, start) + text + field.value.slice(end); - var pos = start + text.length; - field.focus(); - if (field.setSelectionRange) { field.setSelectionRange(pos, pos); } - } - - function addPollOptionRow(value) { - if (pollOptionsBox.children.length >= MAX_POLL_OPTIONS) { return; } - - var row = document.createElement('div'); - row.className = 'gchat_poll_option_row'; - - var optInput = document.createElement('input'); - optInput.type = 'text'; - optInput.maxLength = 60; - optInput.placeholder = GCHAT_TXT.pollOptionPlaceholder; - optInput.value = value || ''; - row.appendChild(optInput); - - // primele 2 optiuni sunt obligatorii (minim necesar la un sondaj) - - // fara buton de stergere pe ele - if (pollOptionsBox.children.length >= 2) { - var removeBtn = document.createElement('button'); - removeBtn.type = 'button'; - removeBtn.className = 'gchat_poll_option_remove'; - removeBtn.textContent = '\u00d7'; - removeBtn.addEventListener('click', function () { row.remove(); }); - row.appendChild(removeBtn); - } - - pollOptionsBox.appendChild(row); - } - - function resetPollForm() { - pollQuestionInput.value = ''; - pollOptionsBox.innerHTML = ''; - addPollOptionRow(''); - addPollOptionRow(''); - } - - function closePollForm() { - pollForm.style.display = 'none'; - } - - EMOJI_LIST.forEach(function (em) { - var b = document.createElement('button'); - b.type = 'button'; - b.className = 'gchat_emoji_item'; - b.textContent = em; - b.addEventListener('click', function () { insertAtCursor(input, em); }); - emojiPanel.appendChild(b); - }); - - emojiBtn.addEventListener('click', function () { - pollForm.style.display = 'none'; - emojiPanel.style.display = emojiPanel.style.display === 'none' ? 'grid' : 'none'; - }); - - pollBtn.addEventListener('click', function () { - emojiPanel.style.display = 'none'; - var opening = pollForm.style.display === 'none'; - pollForm.style.display = opening ? 'block' : 'none'; - if (opening) { resetPollForm(); } - }); - - document.getElementById('gchat_poll_add_option').addEventListener('click', function () { - addPollOptionRow(''); - }); - - document.getElementById('gchat_poll_submit').addEventListener('click', function () { - var question = pollQuestionInput.value.trim(); - var options = Array.prototype.map.call( - pollOptionsBox.querySelectorAll('input'), - function (inp) { return inp.value.trim(); } - ).filter(function (v) { return v !== ''; }); - - if (!question || options.length < 2) { return; } - - var body = 'question=' + encodeURIComponent(question); - options.forEach(function (opt) { body += '&options[]=' + encodeURIComponent(opt); }); - - fetch(AJAX_URL + '?f=gchat_poll_create', { - method: 'POST', - credentials: 'same-origin', - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - body: body - }).then(function (r) { return r.json(); }) - .then(function (data) { - if (data && data.ok) { - closePollForm(); - poll(); - } else if (data && data.reason === 'muted') { - applyViewerState({ access: viewerAccess, mutedUntil: data.mutedUntil }); - } else { - showNotice(GCHAT_TXT.errorGeneric); - } - }); - }); - loadInitial(); pollTimer = window.setInterval(poll, 2500);