SHA256
UI: уплотнить журнал загрузок Arweave
This commit is contained in:
@@ -13,13 +13,23 @@ const HISTORY_KEY = 'shine-ui-arweave-attachment-history-v1';
|
||||
const MAX_HISTORY_ITEMS = 100;
|
||||
const RECENT_UPLOAD_MS = 20 * 60 * 1000;
|
||||
|
||||
function normalizeHistoryItem(input = {}) {
|
||||
const item = normalizeAttachment(input);
|
||||
const placedInShineAtMs = Number(input.placedInShineAtMs || 0) || 0;
|
||||
return {
|
||||
...item,
|
||||
pendingPlacement: input.pendingPlacement === true && !placedInShineAtMs,
|
||||
placedInShineAtMs,
|
||||
};
|
||||
}
|
||||
|
||||
export function readArweaveAttachmentHistory(login) {
|
||||
try {
|
||||
const raw = sessionStorage.getItem(HISTORY_KEY);
|
||||
const parsed = raw ? JSON.parse(raw) : {};
|
||||
const key = String(login || '').trim().toLowerCase();
|
||||
const rows = Array.isArray(parsed?.[key]) ? parsed[key] : [];
|
||||
return rows.map((item) => normalizeAttachment(item)).filter(Boolean);
|
||||
return rows.map((item) => normalizeHistoryItem(item)).filter(Boolean);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
@@ -30,7 +40,9 @@ export function saveArweaveAttachmentHistory(login, rows) {
|
||||
const key = String(login || '').trim().toLowerCase();
|
||||
const raw = sessionStorage.getItem(HISTORY_KEY);
|
||||
const parsed = raw ? JSON.parse(raw) : {};
|
||||
parsed[key] = (Array.isArray(rows) ? rows : []).slice(0, MAX_HISTORY_ITEMS);
|
||||
parsed[key] = (Array.isArray(rows) ? rows : [])
|
||||
.map((item) => normalizeHistoryItem(item))
|
||||
.slice(0, MAX_HISTORY_ITEMS);
|
||||
sessionStorage.setItem(HISTORY_KEY, JSON.stringify(parsed));
|
||||
} catch {
|
||||
// ignore sessionStorage errors
|
||||
@@ -41,12 +53,52 @@ export function clearArweaveAttachmentHistory(login) {
|
||||
saveArweaveAttachmentHistory(login, []);
|
||||
}
|
||||
|
||||
export function addArweaveAttachmentToHistory(login, attachment) {
|
||||
const item = normalizeAttachment(attachment);
|
||||
const rows = readArweaveAttachmentHistory(login).filter((row) => row.ar !== item.ar);
|
||||
rows.unshift(item);
|
||||
saveArweaveAttachmentHistory(login, rows);
|
||||
return item;
|
||||
export function addArweaveAttachmentToHistory(login, attachment, { pendingPlacement = undefined, markPlaced = false } = {}) {
|
||||
const item = normalizeHistoryItem(attachment);
|
||||
const rows = readArweaveAttachmentHistory(login);
|
||||
const existing = rows.find((row) => row.ar === item.ar) || {};
|
||||
const placedInShineAtMs = markPlaced
|
||||
? Date.now()
|
||||
: Number(existing.placedInShineAtMs || item.placedInShineAtMs || 0) || 0;
|
||||
const nextPendingPlacement = pendingPlacement === undefined
|
||||
? (existing.pendingPlacement === true && !placedInShineAtMs)
|
||||
: (!!pendingPlacement && !placedInShineAtMs);
|
||||
const nextItem = {
|
||||
...item,
|
||||
pendingPlacement: nextPendingPlacement,
|
||||
placedInShineAtMs,
|
||||
};
|
||||
const nextRows = rows.filter((row) => row.ar !== item.ar);
|
||||
nextRows.unshift(nextItem);
|
||||
saveArweaveAttachmentHistory(login, nextRows);
|
||||
return nextItem;
|
||||
}
|
||||
|
||||
export function markArweaveAttachmentPlaced(login, attachment) {
|
||||
const item = normalizeHistoryItem(attachment);
|
||||
const rows = readArweaveAttachmentHistory(login);
|
||||
const existing = rows.find((row) => row.ar === item.ar) || {};
|
||||
const nextItem = {
|
||||
...item,
|
||||
uploadedAtMs: item.uploadedAtMs || existing.uploadedAtMs || Date.now(),
|
||||
pendingPlacement: false,
|
||||
placedInShineAtMs: Number(existing.placedInShineAtMs || 0) || Date.now(),
|
||||
};
|
||||
const nextRows = rows.filter((row) => row.ar !== item.ar);
|
||||
nextRows.unshift(nextItem);
|
||||
saveArweaveAttachmentHistory(login, nextRows);
|
||||
return nextItem;
|
||||
}
|
||||
|
||||
export function formatArweaveHistoryTime(ms) {
|
||||
const value = Number(ms || 0);
|
||||
if (!Number.isFinite(value) || value <= 0) return '—';
|
||||
const diffMs = Date.now() - value;
|
||||
if (diffMs >= 0 && diffMs < RECENT_UPLOAD_MS) {
|
||||
const minutes = Math.max(1, Math.floor(diffMs / 60000));
|
||||
return `${minutes} мин назад`;
|
||||
}
|
||||
return new Date(value).toLocaleString('ru-RU');
|
||||
}
|
||||
|
||||
export function formatArweaveHistoryDateTime(ms) {
|
||||
@@ -87,6 +139,7 @@ export function openArweaveAttachmentManager({
|
||||
gateway,
|
||||
onSelect,
|
||||
selectedTxIds = [],
|
||||
historyOnly = false,
|
||||
} = {}) {
|
||||
const cleanLogin = String(login || '').trim();
|
||||
const cleanStoragePwd = String(storagePwd || '').trim();
|
||||
@@ -118,9 +171,12 @@ export function openArweaveAttachmentManager({
|
||||
resolve(result);
|
||||
}
|
||||
|
||||
function finish(resolve, attachment) {
|
||||
const item = addArweaveAttachmentToHistory(cleanLogin, attachment);
|
||||
if (typeof onSelect === 'function') onSelect(item);
|
||||
function finish(resolve, attachment, { pendingPlacement = undefined } = {}) {
|
||||
const item = addArweaveAttachmentToHistory(cleanLogin, attachment, {
|
||||
pendingPlacement,
|
||||
markPlaced: false,
|
||||
});
|
||||
if (!pendingPlacement && typeof onSelect === 'function') onSelect(item);
|
||||
close(resolve, item);
|
||||
}
|
||||
|
||||
@@ -151,19 +207,19 @@ export function openArweaveAttachmentManager({
|
||||
root.innerHTML = `
|
||||
<div class="modal" data-ar-attach-modal="true">
|
||||
<div class="modal-card stack ar-attachment-manager-card">
|
||||
<h3 class="modal-title">Добавить вложение</h3>
|
||||
<h3 class="modal-title">${historyOnly ? 'Загрузить файл в блокчейн' : 'Добавить вложение'}</h3>
|
||||
<label class="meta-muted" for="ar-attach-wallet">Кошелёк оплаты Arweave</label>
|
||||
<select class="input" id="ar-attach-wallet"></select>
|
||||
<button class="ghost-btn" type="button" data-action="add-wallet">Добавить кошелёк</button>
|
||||
<p class="meta-muted">Для надёжности лучше сначала загрузить файл в Arweave, дождаться доступности в журнале, а потом добавить его из истории загрузок.</p>
|
||||
${historyOnly ? '' : '<p class="meta-muted">Для надёжности лучше сначала загрузить файл в Arweave, дождаться доступности в журнале, а потом добавить его из истории загрузок.</p>'}
|
||||
<label class="meta-muted" for="ar-attach-file">Файл для загрузки</label>
|
||||
<input class="input" id="ar-attach-file" type="file" />
|
||||
<div class="ar-attachment-meta" data-meta="true"></div>
|
||||
<p class="meta-muted inline-error" data-error="true"></p>
|
||||
<div class="form-actions-grid">
|
||||
<button class="secondary-btn" type="button" data-action="existing">Ввести txId</button>
|
||||
<button class="secondary-btn" type="button" data-action="history">История загрузок</button>
|
||||
<button class="primary-btn" type="button" data-action="upload" disabled>Загрузить</button>
|
||||
${historyOnly ? '' : '<button class="secondary-btn" type="button" data-action="existing">Ввести txId</button>'}
|
||||
${historyOnly ? '' : '<button class="secondary-btn" type="button" data-action="history">История загрузок</button>'}
|
||||
<button class="primary-btn" type="button" data-action="upload" disabled>${historyOnly ? 'Загрузить в журнал' : 'Загрузить'}</button>
|
||||
</div>
|
||||
<button class="secondary-btn" type="button" data-action="cancel">Отмена</button>
|
||||
</div>
|
||||
@@ -261,7 +317,7 @@ export function openArweaveAttachmentManager({
|
||||
sha256: selectedSha256,
|
||||
ar: uploaded.id,
|
||||
uploadedAtMs: Date.now(),
|
||||
});
|
||||
}, { pendingPlacement: historyOnly });
|
||||
} catch (error) {
|
||||
uploadBtn.disabled = false;
|
||||
setText(errorEl, error?.message || 'Не удалось загрузить файл в Arweave.');
|
||||
@@ -349,11 +405,13 @@ export function openArweaveAttachmentManager({
|
||||
const isSelected = selectedTxIdSet.has(item.ar);
|
||||
return `
|
||||
<article class="ar-attachment-history-tile${isSelected ? ' is-selected' : ''}" data-history-tile="${index}">
|
||||
<div class="ar-attachment-history-tile-head">
|
||||
<strong>${escapeHtml(item.name)}</strong>
|
||||
<strong class="ar-attachment-history-name">${escapeHtml(item.name)}</strong>
|
||||
<div class="ar-attachment-history-meta-row">
|
||||
<span>${escapeHtml(formatBytes(item.size))}</span>
|
||||
<span>${escapeHtml(formatArweaveHistoryTime(item.uploadedAtMs))}</span>
|
||||
<span class="ar-attachment-status ar-attachment-status--pending" data-status="${index}">Проверяем...</span>
|
||||
${item.pendingPlacement ? '<span class="ar-attachment-placement-flag">Не добавлен в SHiNE</span>' : ''}
|
||||
</div>
|
||||
<div class="meta-muted">${escapeHtml(formatBytes(item.size))} · ${escapeHtml(formatArweaveHistoryDateTime(item.uploadedAtMs))}</div>
|
||||
<div class="mono-cell ar-attachment-history-txid">${escapeHtml(item.ar)}</div>
|
||||
<button class="${isSelected ? 'secondary-btn' : 'ghost-btn'}" type="button" data-pick="${index}" ${isSelected ? 'disabled' : ''}>${isSelected ? 'Уже добавлен ✓' : 'Выбрать'}</button>
|
||||
</article>
|
||||
|
||||
Reference in New Issue
Block a user