SHA256
Исправить edit/delete сообщений, упростить вкладки каналов и улучшить автоскролл DM
This commit is contained in:
@@ -246,12 +246,11 @@ function firstNonEmptyText(...candidates) {
|
||||
}
|
||||
|
||||
function latestVersionText(versions) {
|
||||
if (!Array.isArray(versions)) return '';
|
||||
for (let i = versions.length - 1; i >= 0; i -= 1) {
|
||||
const version = versions[i];
|
||||
const value = firstNonEmptyText(version?.text, version?.message, version?.body);
|
||||
if (value) return value;
|
||||
}
|
||||
if (!Array.isArray(versions) || !versions.length) return '';
|
||||
const version = versions[versions.length - 1];
|
||||
if (typeof version?.text === 'string') return version.text;
|
||||
if (typeof version?.message === 'string') return version.message;
|
||||
if (typeof version?.body === 'string') return version.body;
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -333,15 +332,104 @@ function openReplyModal({ onSubmit, navigate }) {
|
||||
if (textEl) textEl.focus();
|
||||
}
|
||||
|
||||
function openMessageHistoryModal({ versions = [], title = 'История изменений' }) {
|
||||
const root = document.getElementById('modal-root');
|
||||
const rows = Array.isArray(versions) ? versions : [];
|
||||
root.innerHTML = `
|
||||
<div class="modal" id="thread-history-modal">
|
||||
<div class="modal-card stack">
|
||||
<h3 class="modal-title">${title}</h3>
|
||||
<div class="stack" id="thread-history-list"></div>
|
||||
<div class="form-actions-grid">
|
||||
<button class="secondary-btn" id="thread-history-close" type="button">Закрыть</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const list = root.querySelector('#thread-history-list');
|
||||
if (list) {
|
||||
rows.forEach((item, index) => {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'card stack';
|
||||
const ts = Number(item?.createdAtMs || 0);
|
||||
const text = String(item?.text || '').trim() || 'удалено';
|
||||
row.innerHTML = `
|
||||
<strong>Версия ${index + 1}</strong>
|
||||
<div class="meta-muted">${ts > 0 ? new Date(ts).toLocaleString('ru-RU') : '—'}</div>
|
||||
<p class="channel-message-body">${text}</p>
|
||||
`;
|
||||
list.append(row);
|
||||
});
|
||||
}
|
||||
|
||||
root.querySelector('#thread-history-close')?.addEventListener('click', () => {
|
||||
root.innerHTML = '';
|
||||
});
|
||||
}
|
||||
|
||||
function openEditMessageModal({ initialText = '', onSave, onDelete }) {
|
||||
const root = document.getElementById('modal-root');
|
||||
root.innerHTML = `
|
||||
<div class="modal" id="thread-edit-modal">
|
||||
<div class="modal-card stack">
|
||||
<h3 class="modal-title">Редактировать сообщение</h3>
|
||||
<textarea id="thread-edit-text" class="input" rows="6" maxlength="2000"></textarea>
|
||||
<div class="meta-muted inline-error" id="thread-edit-error"></div>
|
||||
<div class="form-actions-grid">
|
||||
<button class="secondary-btn" id="thread-edit-cancel" type="button">Отмена</button>
|
||||
<button class="primary-btn" id="thread-edit-save" type="button">ОК</button>
|
||||
</div>
|
||||
<button class="destructive-btn modal-danger-action" id="thread-edit-delete" type="button">Удалить</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
const textEl = root.querySelector('#thread-edit-text');
|
||||
const errorEl = root.querySelector('#thread-edit-error');
|
||||
if (textEl) textEl.value = String(initialText || '');
|
||||
|
||||
const close = () => {
|
||||
root.innerHTML = '';
|
||||
};
|
||||
|
||||
root.querySelector('#thread-edit-cancel')?.addEventListener('click', close);
|
||||
root.querySelector('#thread-edit-save')?.addEventListener('click', async () => {
|
||||
const value = String(textEl?.value || '').trim();
|
||||
if (!value) {
|
||||
errorEl.textContent = 'Введите текст сообщения.';
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await onSave(value);
|
||||
close();
|
||||
} catch (error) {
|
||||
errorEl.textContent = toUserMessage(error, 'Не удалось изменить сообщение.');
|
||||
}
|
||||
});
|
||||
root.querySelector('#thread-edit-delete')?.addEventListener('click', async () => {
|
||||
try {
|
||||
await onDelete();
|
||||
close();
|
||||
} catch (error) {
|
||||
errorEl.textContent = toUserMessage(error, 'Не удалось удалить сообщение.');
|
||||
}
|
||||
});
|
||||
if (textEl) textEl.focus();
|
||||
}
|
||||
|
||||
function renderNodeCard(node, heading, handlers, localNumber) {
|
||||
const card = document.createElement('article');
|
||||
card.className = 'card stack thread-node-card channel-message-card';
|
||||
card.classList.add('is-counters-visible');
|
||||
|
||||
const author = node?.authorLogin || 'автор';
|
||||
const text = resolveNodeText(node) || '(пусто)';
|
||||
const versions = Array.isArray(node?.versions) ? node.versions : [];
|
||||
const versionsTotal = Number(node?.versionsTotal || versions.length || 1);
|
||||
const text = resolveNodeText(node) || (versionsTotal > 1 ? 'удалено' : '(пусто)');
|
||||
const likes = Number(node?.likesCount || 0);
|
||||
const replies = Number(node?.repliesCount || 0);
|
||||
const isOwnMessage = String(node?.authorLogin || '').trim().toLowerCase() === String(state.session.login || '').trim().toLowerCase();
|
||||
const isChannelPost = Number(node?.channelInfo?.channelRoot?.blockNumber) >= 0;
|
||||
|
||||
const headingText = String(heading || '').trim();
|
||||
if (headingText) {
|
||||
@@ -369,16 +457,33 @@ function renderNodeCard(node, heading, handlers, localNumber) {
|
||||
const numberEl = document.createElement('span');
|
||||
numberEl.className = 'author-line-num';
|
||||
numberEl.textContent = `· #${localNumber}`;
|
||||
title.append(loginEl, numberEl);
|
||||
if (versionsTotal > 1) {
|
||||
const editedMarker = document.createElement('button');
|
||||
editedMarker.type = 'button';
|
||||
editedMarker.className = 'message-edited-marker';
|
||||
editedMarker.textContent = `изменено ${Math.max(1, versionsTotal - 1)}`;
|
||||
editedMarker.title = 'Открыть историю редактирования';
|
||||
editedMarker.addEventListener('click', (event) => {
|
||||
event.stopPropagation();
|
||||
animatePress(event.currentTarget);
|
||||
openMessageHistoryModal({
|
||||
title: `История #${localNumber}`,
|
||||
versions,
|
||||
});
|
||||
});
|
||||
title.append(editedMarker);
|
||||
}
|
||||
const timestamp = document.createElement('div');
|
||||
timestamp.className = 'channel-message-time';
|
||||
timestamp.textContent = node?.createdAtMs ? new Date(node.createdAtMs).toLocaleString() : '—';
|
||||
title.append(loginEl, numberEl);
|
||||
authorBlock.append(title, timestamp);
|
||||
authorTile.append(avatar, authorBlock);
|
||||
|
||||
const isDeletedMessage = String(text || '').trim().toLowerCase() === 'удалено';
|
||||
const body = document.createElement('p');
|
||||
body.className = 'channel-message-body';
|
||||
body.textContent = text;
|
||||
body.className = `channel-message-body${isDeletedMessage ? ' channel-message-body--deleted' : ''}`;
|
||||
body.textContent = isDeletedMessage ? 'Сообщение удалено' : text;
|
||||
|
||||
card.append(authorTile, body);
|
||||
|
||||
@@ -460,20 +565,27 @@ function renderNodeCard(node, heading, handlers, localNumber) {
|
||||
await handlers.onShare(target);
|
||||
});
|
||||
|
||||
const openThreadButton = document.createElement('button');
|
||||
openThreadButton.type = 'button';
|
||||
openThreadButton.className = 'channel-action-item thread-open-btn';
|
||||
openThreadButton.innerHTML = `
|
||||
<span class="channel-action-icon" aria-hidden="true">#</span>
|
||||
<span class="channel-action-label">Тред</span>
|
||||
`;
|
||||
openThreadButton.addEventListener('click', (event) => {
|
||||
event.stopPropagation();
|
||||
animatePress(event.currentTarget);
|
||||
handlers.onOpenThread(target);
|
||||
});
|
||||
|
||||
actions.append(likeButton, replyButton, openThreadButton, shareButton);
|
||||
actions.append(likeButton, replyButton, shareButton);
|
||||
if (isOwnMessage) {
|
||||
const editButton = document.createElement('button');
|
||||
editButton.type = 'button';
|
||||
editButton.className = 'channel-action-item';
|
||||
editButton.setAttribute('aria-label', 'Редактировать');
|
||||
editButton.title = 'Редактировать';
|
||||
editButton.innerHTML = `
|
||||
<span class="channel-action-icon" aria-hidden="true">✏️</span>
|
||||
`;
|
||||
editButton.addEventListener('click', (event) => {
|
||||
event.stopPropagation();
|
||||
animatePress(event.currentTarget);
|
||||
openEditMessageModal({
|
||||
initialText: String(text || '').trim() === 'удалено' ? '' : text,
|
||||
onSave: async (nextText) => handlers.onEdit(target, nextText, { isChannelPost }),
|
||||
onDelete: async () => handlers.onEdit(target, '', { isChannelPost, isDelete: true }),
|
||||
});
|
||||
});
|
||||
actions.append(editButton);
|
||||
}
|
||||
card.append(actions);
|
||||
authorTile.addEventListener('click', (event) => {
|
||||
event.stopPropagation();
|
||||
@@ -670,6 +782,21 @@ export function render({ navigate, route }) {
|
||||
: 'Не удалось поставить лайк.';
|
||||
showStatus(toUserMessage(error, fallback));
|
||||
},
|
||||
onEdit: async (target, textValue, meta = {}) => {
|
||||
const { login, storagePwd } = requireSigningSession();
|
||||
await authService.addBlockEditMessage({
|
||||
login,
|
||||
storagePwd,
|
||||
message: target,
|
||||
text: textValue,
|
||||
isChannelPost: meta?.isChannelPost === true,
|
||||
channel: selector?.channel || null,
|
||||
});
|
||||
softHaptic(12);
|
||||
showToast('Сообщение обновлено');
|
||||
showStatus('');
|
||||
rerender();
|
||||
},
|
||||
};
|
||||
|
||||
screen.append(header, statusBox);
|
||||
|
||||
Reference in New Issue
Block a user