SHA256
UI: добавить вложения Arweave в каналы
This commit is contained in:
@@ -12,6 +12,12 @@ import {
|
||||
} from '../services/channels-ux.js';
|
||||
import { navigateBack } from '../router.js';
|
||||
import { renderUserAvatar } from '../components/avatar-image.js';
|
||||
import { openArweaveAttachmentManager } from '../components/arweave-attachment-manager.js';
|
||||
import {
|
||||
composeMessageWithAttachments,
|
||||
createAttachmentListElement,
|
||||
parseMessageAttachments,
|
||||
} from '../services/attachment-format.js';
|
||||
import { loadProfileSnapshot } from '../services/user-profile-params.js';
|
||||
import { extractLoginFromBlockchainName, makeProfileRoute, makeShineChannelRoute, makeShineMessageRoute } from '../services/shine-routes.js';
|
||||
|
||||
@@ -331,6 +337,25 @@ function resolveNodeText(node) {
|
||||
);
|
||||
}
|
||||
|
||||
function renderDraftAttachments(container, attachments) {
|
||||
if (!container) return;
|
||||
container.innerHTML = '';
|
||||
(Array.isArray(attachments) ? attachments : []).forEach((item, index) => {
|
||||
const button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.className = 'draft-attachment-chip';
|
||||
button.textContent = `${item.name} · ${item.ar}`;
|
||||
button.title = 'Нажмите, чтобы убрать вложение';
|
||||
button.addEventListener('click', () => {
|
||||
const ok = window.confirm('Отменить вложение?');
|
||||
if (!ok) return;
|
||||
attachments.splice(index, 1);
|
||||
renderDraftAttachments(container, attachments);
|
||||
});
|
||||
container.append(button);
|
||||
});
|
||||
}
|
||||
|
||||
function openReplyModal({ onSubmit, navigate }) {
|
||||
const root = document.getElementById('modal-root');
|
||||
root.innerHTML = `
|
||||
@@ -338,6 +363,8 @@ function openReplyModal({ onSubmit, navigate }) {
|
||||
<div class="modal-card stack">
|
||||
<h3 class="modal-title">Ответ</h3>
|
||||
<textarea id="thread-reply-text" class="input" rows="5" maxlength="2000" placeholder="Текст ответа"></textarea>
|
||||
<div class="draft-attachments" id="thread-reply-attachments"></div>
|
||||
<button class="secondary-btn attachment-trigger-btn" id="thread-reply-attach" type="button" aria-label="Добавить вложение" title="Добавить вложение">▣ 📎</button>
|
||||
<div class="meta-muted inline-error" id="thread-reply-error"></div>
|
||||
<div class="form-actions-grid">
|
||||
<button class="secondary-btn" id="thread-reply-cancel" type="button">Отмена</button>
|
||||
@@ -348,14 +375,17 @@ function openReplyModal({ onSubmit, navigate }) {
|
||||
`;
|
||||
|
||||
const textEl = root.querySelector('#thread-reply-text');
|
||||
const attachmentsEl = root.querySelector('#thread-reply-attachments');
|
||||
const errorEl = root.querySelector('#thread-reply-error');
|
||||
const submitEl = root.querySelector('#thread-reply-submit');
|
||||
const attachments = [];
|
||||
let inFlight = false;
|
||||
|
||||
const setBusy = (busy) => {
|
||||
inFlight = !!busy;
|
||||
submitEl.disabled = inFlight;
|
||||
if (textEl) textEl.disabled = inFlight;
|
||||
root.querySelector('#thread-reply-attach')?.toggleAttribute('disabled', inFlight);
|
||||
submitEl.textContent = inFlight ? 'Отправляем...' : 'Отправить';
|
||||
};
|
||||
|
||||
@@ -368,8 +398,8 @@ function openReplyModal({ onSubmit, navigate }) {
|
||||
if (inFlight) return;
|
||||
|
||||
const text = String(textEl?.value || '').trim();
|
||||
if (!text) {
|
||||
errorEl.textContent = 'Введите текст ответа.';
|
||||
if (!text && attachments.length === 0) {
|
||||
errorEl.textContent = 'Введите текст ответа или добавьте вложение.';
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -377,7 +407,7 @@ function openReplyModal({ onSubmit, navigate }) {
|
||||
errorEl.textContent = '';
|
||||
|
||||
try {
|
||||
await onSubmit(text);
|
||||
await onSubmit(composeMessageWithAttachments(text, attachments));
|
||||
close();
|
||||
} catch (error) {
|
||||
setBusy(false);
|
||||
@@ -385,6 +415,21 @@ function openReplyModal({ onSubmit, navigate }) {
|
||||
}
|
||||
});
|
||||
|
||||
root.querySelector('#thread-reply-attach')?.addEventListener('click', async () => {
|
||||
try {
|
||||
const item = await openArweaveAttachmentManager({
|
||||
login: state.session.login,
|
||||
storagePwd: state.session.storagePwdInMemory,
|
||||
gateway: state.entrySettings.arweaveServer,
|
||||
});
|
||||
if (!item) return;
|
||||
attachments.push(item);
|
||||
renderDraftAttachments(attachmentsEl, attachments);
|
||||
} catch (error) {
|
||||
errorEl.textContent = toUserMessage(error, 'Не удалось добавить вложение.');
|
||||
}
|
||||
});
|
||||
|
||||
bindSubmitOnPlainEnter(textEl, () => root.querySelector('#thread-reply-submit')?.click());
|
||||
|
||||
if (textEl) textEl.focus();
|
||||
@@ -504,7 +549,7 @@ function openMessageHistoryModal({ versions = [], title = 'История изм
|
||||
});
|
||||
}
|
||||
|
||||
function openEditMessageModal({ initialText = '', onSave, onDelete }) {
|
||||
function openEditMessageModal({ initialText = '', allowEmptyText = false, onSave, onDelete }) {
|
||||
const root = document.getElementById('modal-root');
|
||||
root.innerHTML = `
|
||||
<div class="modal" id="thread-edit-modal">
|
||||
@@ -531,7 +576,7 @@ function openEditMessageModal({ initialText = '', onSave, onDelete }) {
|
||||
root.querySelector('#thread-edit-cancel')?.addEventListener('click', close);
|
||||
root.querySelector('#thread-edit-save')?.addEventListener('click', async () => {
|
||||
const value = String(textEl?.value || '').trim();
|
||||
if (!value) {
|
||||
if (!value && !allowEmptyText) {
|
||||
errorEl.textContent = 'Введите текст сообщения.';
|
||||
return;
|
||||
}
|
||||
@@ -569,6 +614,7 @@ function renderNodeCard(node, heading, handlers, localNumber) {
|
||||
const isChannelPost = Number(node?.channelInfo?.channelRoot?.blockNumber) >= 0;
|
||||
const msgSubType = Number(node?.msgSubType || 0);
|
||||
const repostTarget = msgSubType === 30 ? buildRepostTargetFromNode(node) : null;
|
||||
const parsedText = parseMessageAttachments(text);
|
||||
|
||||
const headingText = String(heading || '').trim();
|
||||
if (headingText) {
|
||||
@@ -620,9 +666,12 @@ function renderNodeCard(node, heading, handlers, localNumber) {
|
||||
const isDeletedMessage = String(text || '').trim().toLowerCase() === 'удалено';
|
||||
const body = document.createElement('p');
|
||||
body.className = `channel-message-body${isDeletedMessage ? ' channel-message-body--deleted' : ''}`;
|
||||
body.textContent = isDeletedMessage ? 'Сообщение удалено' : text;
|
||||
body.textContent = isDeletedMessage ? 'Сообщение удалено' : parsedText.text;
|
||||
|
||||
card.append(authorTile, body);
|
||||
if (!isDeletedMessage && parsedText.attachments.length > 0) {
|
||||
card.append(createAttachmentListElement(parsedText.attachments, { gateway: state.entrySettings.arweaveServer }));
|
||||
}
|
||||
|
||||
const target = buildTargetFromNode(node);
|
||||
const refKey = messageRefKey(target);
|
||||
@@ -740,8 +789,9 @@ function renderNodeCard(node, heading, handlers, localNumber) {
|
||||
event.stopPropagation();
|
||||
animatePress(event.currentTarget);
|
||||
openEditMessageModal({
|
||||
initialText: String(text || '').trim() === 'удалено' ? '' : text,
|
||||
onSave: async (nextText) => handlers.onEdit(target, nextText, { isChannelPost }),
|
||||
initialText: String(text || '').trim() === 'удалено' ? '' : parsedText.text,
|
||||
allowEmptyText: parsedText.attachments.length > 0,
|
||||
onSave: async (nextText) => handlers.onEdit(target, composeMessageWithAttachments(nextText, parsedText.attachments), { isChannelPost }),
|
||||
onDelete: async () => handlers.onEdit(target, '', { isChannelPost, isDelete: true }),
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user