SHA256
UI вложений и черновики форматов каналов
This commit is contained in:
@@ -309,6 +309,7 @@ export function openArweaveAttachmentManager({
|
||||
let selectedPreviewEnabled = false;
|
||||
let selectedPreviewFile = null;
|
||||
let selectedPreviewSha256 = '';
|
||||
let selectedPreviewPriceInfo = null;
|
||||
let priceInfo = null;
|
||||
let balanceInfo = null;
|
||||
let autoOpenedFileDialog = false;
|
||||
@@ -391,41 +392,48 @@ export function openArweaveAttachmentManager({
|
||||
}
|
||||
};
|
||||
|
||||
const renderTurboMeta = (metaEl) => {
|
||||
if (!metaEl) return;
|
||||
const renderTurboMeta = (mainMetaEl, previewMetaEl) => {
|
||||
if (!mainMetaEl || !previewMetaEl) return;
|
||||
const turboAddress = String(balanceInfo?.address || priceInfo?.address || '').trim();
|
||||
const turboCredits = balanceInfo ? formatTurboCredits(balanceInfo.effectiveCredits, 6) : '—';
|
||||
const solBalance = balanceInfo ? `${escapeHtml(String(balanceInfo.solBalance ?? 0))} SOL` : '—';
|
||||
const uploadPrice = priceInfo ? `${formatTurboCredits(priceInfo.credits, 6)} credits` : '—';
|
||||
const fileSize = selectedFile ? formatBytes(selectedFile.size) : '—';
|
||||
const previewSize = selectedPreviewEnabled && selectedPreviewFile ? formatBytes(selectedPreviewFile.size) : '';
|
||||
const freeHint = selectedFile
|
||||
? (combinedUploadSize() <= getFreeTurboUploadBytesLimit() ? 'Да, бесплатно через Turbo' : 'Нет, нужен Turbo balance')
|
||||
: `До ${formatBytes(getFreeTurboUploadBytesLimit())} бесплатно`;
|
||||
const freeBadge = priceInfo?.isFree
|
||||
? ' <span style="color:#15803d;">(пока файлы до 100 KB бесплатно)</span>'
|
||||
: '';
|
||||
metaEl.innerHTML = `
|
||||
mainMetaEl.innerHTML = `
|
||||
<div>Turbo-адрес: ${escapeHtml(shortAddress(turboAddress || '—'))}</div>
|
||||
<div>Turbo balance: ${escapeHtml(turboCredits)}</div>
|
||||
<div>SOL на этом ключе: ${solBalance}</div>
|
||||
<div>Размер файла: ${escapeHtml(fileSize)}</div>
|
||||
${previewSize ? `<div>Размер превью: ${escapeHtml(previewSize)}</div>` : ''}
|
||||
<div>SHA-256: ${escapeHtml(selectedSha256 || '—')}</div>
|
||||
${selectedPreviewEnabled && selectedPreviewSha256 ? `<div>SHA-256 превью: ${escapeHtml(selectedPreviewSha256)}</div>` : ''}
|
||||
<div>Цена Turbo: ${escapeHtml(uploadPrice)}${freeBadge}</div>
|
||||
<div>До ${escapeHtml(formatBytes(getFreeTurboUploadBytesLimit()))} бесплатно: ${escapeHtml(freeHint)}</div>
|
||||
`;
|
||||
previewMetaEl.innerHTML = '';
|
||||
if (selectedPreviewEnabled && selectedPreviewFile) {
|
||||
previewMetaEl.innerHTML = `
|
||||
<div><strong>Файл превью</strong></div>
|
||||
<div>Имя: ${escapeHtml(selectedPreviewFile.name)}</div>
|
||||
<div>Размер превью: ${escapeHtml(formatBytes(selectedPreviewFile.size))}</div>
|
||||
<div>SHA-256 превью: ${escapeHtml(selectedPreviewSha256 || '—')}</div>
|
||||
<div>Цена превью: ${escapeHtml(selectedPreviewPriceInfo ? `${formatTurboCredits(selectedPreviewPriceInfo.credits, 6)} credits` : '—')}</div>
|
||||
`;
|
||||
}
|
||||
};
|
||||
|
||||
const refreshTurboBalance = async (metaEl, errorEl) => {
|
||||
const refreshTurboBalance = async (mainMetaEl, previewMetaEl, errorEl) => {
|
||||
try {
|
||||
balanceInfo = await getTurboBalanceForStoredSolanaKey({
|
||||
login: cleanLogin,
|
||||
storagePwd: cleanStoragePwd,
|
||||
keySource: selectedTurboKeySource,
|
||||
});
|
||||
renderTurboMeta(metaEl);
|
||||
renderTurboMeta(mainMetaEl, previewMetaEl);
|
||||
if (errorEl && String(errorEl.textContent || '').includes('Turbo balance')) {
|
||||
setText(errorEl, '');
|
||||
}
|
||||
@@ -434,7 +442,7 @@ export function openArweaveAttachmentManager({
|
||||
}
|
||||
};
|
||||
|
||||
const promptTurboTopUp = async (metaEl, errorEl) => {
|
||||
const promptTurboTopUp = async (mainMetaEl, previewMetaEl, errorEl) => {
|
||||
const value = window.prompt('Сколько SOL перевести в Turbo?', '0.01');
|
||||
if (value == null) return;
|
||||
setText(errorEl, 'Пополняем Turbo...');
|
||||
@@ -445,7 +453,7 @@ export function openArweaveAttachmentManager({
|
||||
keySource: selectedTurboKeySource,
|
||||
amountSol: value,
|
||||
});
|
||||
await refreshTurboBalance(metaEl, errorEl);
|
||||
await refreshTurboBalance(mainMetaEl, previewMetaEl, errorEl);
|
||||
setText(errorEl, `Turbo пополнен. Tx: ${result.txId || 'отправлено'}`);
|
||||
} catch (error) {
|
||||
setText(errorEl, error?.message || 'Не удалось пополнить Turbo.');
|
||||
@@ -456,6 +464,7 @@ export function openArweaveAttachmentManager({
|
||||
selectedPreviewEnabled = false;
|
||||
selectedPreviewFile = null;
|
||||
selectedPreviewSha256 = '';
|
||||
selectedPreviewPriceInfo = null;
|
||||
};
|
||||
|
||||
const isPreviewEligibleForCurrentFile = () => (
|
||||
@@ -468,30 +477,36 @@ export function openArweaveAttachmentManager({
|
||||
return Number(selectedFile?.size || 0) + Number(selectedPreviewEnabled ? (selectedPreviewFile?.size || 0) : 0);
|
||||
};
|
||||
|
||||
const renderArweaveMeta = (metaEl) => {
|
||||
if (!metaEl) return;
|
||||
metaEl.innerHTML = `
|
||||
const renderArweaveMeta = (mainMetaEl, previewMetaEl) => {
|
||||
if (!mainMetaEl || !previewMetaEl) return;
|
||||
mainMetaEl.innerHTML = `
|
||||
${isAvatarMode ? '' : `<div>Имя: ${escapeHtml(selectedFile?.name || 'file')}</div>`}
|
||||
<div>Размер: ${escapeHtml(formatBytes(selectedFile?.size || 0))}</div>
|
||||
<div>SHA-256: ${escapeHtml(selectedSha256 || '—')}</div>
|
||||
${selectedPreviewEnabled && selectedPreviewFile
|
||||
? `<div>Превью: ${escapeHtml(selectedPreviewFile.name)} · ${escapeHtml(formatBytes(selectedPreviewFile.size))}</div>`
|
||||
: ''}
|
||||
${selectedPreviewEnabled && selectedPreviewSha256
|
||||
? `<div>SHA-256 превью: ${escapeHtml(selectedPreviewSha256)}</div>`
|
||||
: ''}
|
||||
<div>Цена: ${escapeHtml(Number(priceInfo?.ar || 0).toLocaleString('ru-RU', { maximumFractionDigits: 6 }))} AR</div>
|
||||
<div>Баланс кошелька: ${escapeHtml(Number(balanceInfo?.ar || 0).toLocaleString('ru-RU', { maximumFractionDigits: 6 }))} AR</div>
|
||||
`;
|
||||
previewMetaEl.innerHTML = '';
|
||||
if (selectedPreviewEnabled && selectedPreviewFile) {
|
||||
previewMetaEl.innerHTML = `
|
||||
<div><strong>Файл превью</strong></div>
|
||||
<div>Имя: ${escapeHtml(selectedPreviewFile.name)}</div>
|
||||
<div>Размер превью: ${escapeHtml(formatBytes(selectedPreviewFile.size))}</div>
|
||||
<div>SHA-256 превью: ${escapeHtml(selectedPreviewSha256 || '—')}</div>
|
||||
<div>Цена превью: ${escapeHtml(selectedPreviewPriceInfo ? `${Number(selectedPreviewPriceInfo.ar || 0).toLocaleString('ru-RU', { maximumFractionDigits: 6 })} AR` : '—')}</div>
|
||||
`;
|
||||
}
|
||||
};
|
||||
|
||||
const recalculateArweaveState = async (metaEl, errorEl, uploadBtn) => {
|
||||
const recalculateArweaveState = async (mainMetaEl, previewMetaEl, errorEl, uploadBtn) => {
|
||||
uploadBtn.disabled = true;
|
||||
priceInfo = null;
|
||||
balanceInfo = null;
|
||||
selectedPreviewPriceInfo = null;
|
||||
setText(errorEl, '');
|
||||
if (!selectedFile || !selectedSha256) {
|
||||
setText(metaEl, '');
|
||||
setText(mainMetaEl, '');
|
||||
setText(previewMetaEl, '');
|
||||
return;
|
||||
}
|
||||
const wallet = selectedWallet();
|
||||
@@ -500,6 +515,7 @@ export function openArweaveAttachmentManager({
|
||||
let totalWinston = BigInt(mainPrice.winston);
|
||||
if (selectedPreviewEnabled && selectedPreviewFile) {
|
||||
const previewPrice = await getArweaveUploadPrice({ gateway: cleanGateway, byteLength: selectedPreviewFile.size });
|
||||
selectedPreviewPriceInfo = previewPrice;
|
||||
totalWinston += BigInt(previewPrice.winston);
|
||||
}
|
||||
balanceInfo = await getArweaveBalance({ gateway: cleanGateway, address: wallet.address });
|
||||
@@ -507,7 +523,7 @@ export function openArweaveAttachmentManager({
|
||||
ar: Number(totalWinston) / 1e12,
|
||||
winston: totalWinston.toString(),
|
||||
};
|
||||
renderArweaveMeta(metaEl);
|
||||
renderArweaveMeta(mainMetaEl, previewMetaEl);
|
||||
const hasFunds = BigInt(balanceInfo.winston) >= totalWinston;
|
||||
if (!hasFunds) {
|
||||
setText(errorEl, 'Недостаточно AR на выбранном кошельке.');
|
||||
@@ -516,13 +532,14 @@ export function openArweaveAttachmentManager({
|
||||
uploadBtn.disabled = false;
|
||||
};
|
||||
|
||||
const refreshTurboStateForCurrentFile = async (metaEl, errorEl, uploadBtn) => {
|
||||
const refreshTurboStateForCurrentFile = async (mainMetaEl, previewMetaEl, errorEl, uploadBtn) => {
|
||||
uploadBtn.disabled = true;
|
||||
priceInfo = null;
|
||||
balanceInfo = null;
|
||||
selectedPreviewPriceInfo = null;
|
||||
setText(errorEl, '');
|
||||
if (!selectedFile || !selectedSha256) {
|
||||
renderTurboMeta(metaEl);
|
||||
renderTurboMeta(mainMetaEl, previewMetaEl);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -540,6 +557,7 @@ export function openArweaveAttachmentManager({
|
||||
keySource: selectedTurboKeySource,
|
||||
byteLength: selectedPreviewFile.size,
|
||||
});
|
||||
selectedPreviewPriceInfo = previewPrice;
|
||||
totalWinc += BigInt(previewPrice.winc);
|
||||
}
|
||||
priceInfo = {
|
||||
@@ -554,7 +572,7 @@ export function openArweaveAttachmentManager({
|
||||
storagePwd: cleanStoragePwd,
|
||||
keySource: selectedTurboKeySource,
|
||||
});
|
||||
renderTurboMeta(metaEl);
|
||||
renderTurboMeta(mainMetaEl, previewMetaEl);
|
||||
if (!priceInfo.isFree && BigInt(balanceInfo.effectiveWinc) < BigInt(priceInfo.winc)) {
|
||||
setText(errorEl, 'Turbo balance недостаточен. Нажмите «Пополнить Turbo».');
|
||||
return;
|
||||
@@ -591,11 +609,12 @@ export function openArweaveAttachmentManager({
|
||||
`}
|
||||
<label class="meta-muted" for="ar-attach-file">Файл для загрузки</label>
|
||||
<input class="input" id="ar-attach-file" type="file" ${isAvatarMode ? 'accept="image/*"' : ''} />
|
||||
<div class="ar-attachment-meta" data-meta-main="true"></div>
|
||||
<label class="meta-muted" data-preview-option="true" hidden>
|
||||
<input type="checkbox" data-preview-toggle="true" />
|
||||
Добавить превью
|
||||
</label>
|
||||
<div class="ar-attachment-meta" data-meta="true"></div>
|
||||
<div class="ar-attachment-meta" data-meta-preview="true"></div>
|
||||
<p class="meta-muted inline-error" data-error="true"></p>
|
||||
<div class="form-actions-grid">
|
||||
${turboMode ? '<button class="secondary-btn" type="button" data-action="topup">Пополнить Turbo</button>' : ''}
|
||||
@@ -609,7 +628,8 @@ export function openArweaveAttachmentManager({
|
||||
const walletEl = root.querySelector('#ar-attach-wallet');
|
||||
const turboKeyEl = root.querySelector('#turbo-key-source');
|
||||
const fileEl = root.querySelector('#ar-attach-file');
|
||||
const metaEl = root.querySelector('[data-meta="true"]');
|
||||
const mainMetaEl = root.querySelector('[data-meta-main="true"]');
|
||||
const previewMetaEl = root.querySelector('[data-meta-preview="true"]');
|
||||
const errorEl = root.querySelector('[data-error="true"]');
|
||||
const uploadBtn = root.querySelector('[data-action="upload"]');
|
||||
const previewOptionEl = root.querySelector('[data-preview-option="true"]');
|
||||
@@ -648,10 +668,10 @@ export function openArweaveAttachmentManager({
|
||||
turboKeyEl.addEventListener('change', async () => {
|
||||
selectedTurboKeySource = String(turboKeyEl.value || 'client');
|
||||
writeLastTurboKeySource(cleanLogin, selectedTurboKeySource);
|
||||
await refreshTurboStateForCurrentFile(metaEl, errorEl, uploadBtn);
|
||||
await refreshTurboStateForCurrentFile(mainMetaEl, previewMetaEl, errorEl, uploadBtn);
|
||||
});
|
||||
}
|
||||
await refreshTurboStateForCurrentFile(metaEl, errorEl, uploadBtn);
|
||||
await refreshTurboStateForCurrentFile(mainMetaEl, previewMetaEl, errorEl, uploadBtn);
|
||||
} else {
|
||||
await loadWallets(errorEl);
|
||||
if (walletEl) {
|
||||
@@ -667,16 +687,17 @@ export function openArweaveAttachmentManager({
|
||||
priceInfo = null;
|
||||
balanceInfo = null;
|
||||
if (fileEl) fileEl.value = '';
|
||||
setText(metaEl, '');
|
||||
setText(mainMetaEl, '');
|
||||
setText(previewMetaEl, '');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
root.querySelector('[data-action="cancel"]')?.addEventListener('click', () => close(resolve, null));
|
||||
root.querySelector('[data-action="topup"]')?.addEventListener('click', async () => {
|
||||
await promptTurboTopUp(metaEl, errorEl);
|
||||
await promptTurboTopUp(mainMetaEl, previewMetaEl, errorEl);
|
||||
if (turboMode && selectedFile && selectedSha256) {
|
||||
await refreshTurboStateForCurrentFile(metaEl, errorEl, uploadBtn);
|
||||
await refreshTurboStateForCurrentFile(mainMetaEl, previewMetaEl, errorEl, uploadBtn);
|
||||
}
|
||||
});
|
||||
if (!turboMode) {
|
||||
@@ -691,7 +712,8 @@ export function openArweaveAttachmentManager({
|
||||
balanceInfo = null;
|
||||
uploadBtn.disabled = true;
|
||||
setText(errorEl, '');
|
||||
setText(metaEl, '');
|
||||
setText(mainMetaEl, '');
|
||||
setText(previewMetaEl, '');
|
||||
if (previewToggleEl instanceof HTMLInputElement) previewToggleEl.checked = false;
|
||||
if (previewOptionEl) previewOptionEl.hidden = true;
|
||||
if (!selectedFile) return;
|
||||
@@ -709,9 +731,9 @@ export function openArweaveAttachmentManager({
|
||||
previewOptionEl.hidden = false;
|
||||
}
|
||||
if (turboMode) {
|
||||
await refreshTurboStateForCurrentFile(metaEl, errorEl, uploadBtn);
|
||||
await refreshTurboStateForCurrentFile(mainMetaEl, previewMetaEl, errorEl, uploadBtn);
|
||||
} else {
|
||||
await recalculateArweaveState(metaEl, errorEl, uploadBtn);
|
||||
await recalculateArweaveState(mainMetaEl, previewMetaEl, errorEl, uploadBtn);
|
||||
}
|
||||
} catch (error) {
|
||||
setText(errorEl, error?.message || 'Не удалось подготовить файл.');
|
||||
@@ -735,9 +757,9 @@ export function openArweaveAttachmentManager({
|
||||
selectedPreviewSha256 = await sha256HexFromArrayBuffer(await selectedPreviewFile.arrayBuffer());
|
||||
}
|
||||
if (turboMode) {
|
||||
await refreshTurboStateForCurrentFile(metaEl, errorEl, uploadBtn);
|
||||
await refreshTurboStateForCurrentFile(mainMetaEl, previewMetaEl, errorEl, uploadBtn);
|
||||
} else {
|
||||
await recalculateArweaveState(metaEl, errorEl, uploadBtn);
|
||||
await recalculateArweaveState(mainMetaEl, previewMetaEl, errorEl, uploadBtn);
|
||||
}
|
||||
} catch (error) {
|
||||
resetPreviewState();
|
||||
|
||||
@@ -286,11 +286,21 @@ function createMediaSlide({ item, url, kind, messageTimestampMs, slide, previewU
|
||||
img.loading = 'lazy';
|
||||
img.addEventListener('error', () => replaceWithUnavailable(slide, messageTimestampMs), { once: true });
|
||||
frame.append(img);
|
||||
} else if (previewUrl) {
|
||||
const img = document.createElement('img');
|
||||
img.className = 'message-attachment-media';
|
||||
img.src = previewUrl;
|
||||
img.alt = `${item.name} preview`;
|
||||
img.loading = 'lazy';
|
||||
img.addEventListener('error', () => replaceWithUnavailable(slide, messageTimestampMs), { once: true });
|
||||
const play = document.createElement('span');
|
||||
play.className = 'message-attachment-play';
|
||||
play.textContent = '▶';
|
||||
frame.append(img, play);
|
||||
} else {
|
||||
const video = document.createElement('video');
|
||||
video.className = 'message-attachment-media';
|
||||
video.src = url;
|
||||
if (previewUrl) video.poster = previewUrl;
|
||||
video.preload = 'metadata';
|
||||
video.muted = true;
|
||||
video.playsInline = true;
|
||||
|
||||
Reference in New Issue
Block a user