- ${escapeHtml(item.name)}
+ ${escapeHtml(item.name)}
+
+ ${escapeHtml(formatBytes(item.size))}
+ ${escapeHtml(formatArweaveHistoryTime(item.uploadedAtMs))}
Проверяем...
+ ${item.pendingPlacement ? 'Не добавлен в SHiNE' : ''}
- ${escapeHtml(formatBytes(item.size))} · ${escapeHtml(formatArweaveHistoryDateTime(item.uploadedAtMs))}
${escapeHtml(item.ar)}
diff --git a/shine-UI/js/pages/arweave-uploads-view.js b/shine-UI/js/pages/arweave-uploads-view.js
index dbb51c22..449a7dbe 100644
--- a/shine-UI/js/pages/arweave-uploads-view.js
+++ b/shine-UI/js/pages/arweave-uploads-view.js
@@ -1,7 +1,6 @@
-import { renderHeader } from '../components/header.js';
import {
clearArweaveAttachmentHistory,
- formatArweaveHistoryDateTime,
+ formatArweaveHistoryTime,
getArweaveAttachmentAvailability,
openArweaveAttachmentManager,
readArweaveAttachmentHistory,
@@ -10,7 +9,7 @@ import { formatBytes } from '../services/attachment-format.js';
import { toUserMessage } from '../services/ui-error-texts.js';
import { state } from '../state.js';
-export const pageMeta = { id: 'arweave-uploads-view', title: 'Загрузки в Arweave' };
+export const pageMeta = { id: 'arweave-uploads-view', title: 'Загрузить файлы в блокчейн' };
function setStatusBadge(node, status, label) {
if (!node) return;
@@ -22,10 +21,8 @@ function renderTile(item, index) {
const tile = document.createElement('article');
tile.className = 'ar-attachment-history-tile ar-attachment-history-tile--page';
- const head = document.createElement('div');
- head.className = 'ar-attachment-history-tile-head';
-
const name = document.createElement('strong');
+ name.className = 'ar-attachment-history-name';
name.textContent = item.name;
const status = document.createElement('span');
@@ -34,15 +31,24 @@ function renderTile(item, index) {
status.textContent = 'Проверяем...';
const meta = document.createElement('div');
- meta.className = 'meta-muted';
- meta.textContent = `${formatBytes(item.size)} · ${formatArweaveHistoryDateTime(item.uploadedAtMs)}`;
+ meta.className = 'ar-attachment-history-meta-row';
+ const size = document.createElement('span');
+ size.textContent = formatBytes(item.size);
+ const time = document.createElement('span');
+ time.textContent = formatArweaveHistoryTime(item.uploadedAtMs);
+ meta.append(size, time, status);
+ if (item.pendingPlacement) {
+ const flag = document.createElement('span');
+ flag.className = 'ar-attachment-placement-flag';
+ flag.textContent = 'Не добавлен в SHiNE';
+ meta.append(flag);
+ }
const txId = document.createElement('div');
txId.className = 'mono-cell ar-attachment-history-txid';
txId.textContent = item.ar;
- head.append(name, status);
- tile.append(head, meta, txId);
+ tile.append(name, meta, txId);
return tile;
}
@@ -50,25 +56,16 @@ export function render({ navigate }) {
const screen = document.createElement('section');
screen.className = 'stack arweave-uploads-screen';
- screen.append(
- renderHeader({
- title: 'Загрузки в Arweave',
- leftAction: { label: '←', onClick: () => navigate('settings-view') },
- }),
- );
-
- const intro = document.createElement('div');
- intro.className = 'card stack';
- intro.innerHTML = `
- Журнал файлов этой сессии
-Здесь можно заранее загрузить файл в Arweave. Он попадёт только в журнал, а в сообщение его потом можно добавить из истории загрузок.
- `; - const controls = document.createElement('div'); - controls.className = 'card arweave-uploads-toolbar'; + controls.className = 'arweave-uploads-toolbar'; controls.innerHTML = ` + - + +
+
+
+
`;
const statusLine = document.createElement('p');
@@ -80,7 +77,7 @@ export function render({ navigate }) {
function renderHistory() {
const rows = readArweaveAttachmentHistory(state.session.login).slice().reverse();
list.innerHTML = '';
- statusLine.textContent = rows.length ? `Файлов в журнале: ${rows.length}` : 'Журнал пока пуст.';
+ statusLine.textContent = '';
if (!rows.length) {
const empty = document.createElement('div');
@@ -113,6 +110,7 @@ export function render({ navigate }) {
login: state.session.login,
storagePwd: state.session.storagePwdInMemory,
gateway: state.entrySettings.arweaveServer,
+ historyOnly: true,
});
renderHistory();
} catch (error) {
@@ -120,14 +118,30 @@ export function render({ navigate }) {
}
});
+ controls.querySelector('[data-action="back"]')?.addEventListener('click', () => navigate('settings-view'));
+ controls.querySelector('[data-action="menu"]')?.addEventListener('click', () => {
+ const menu = controls.querySelector('[data-menu="true"]');
+ if (menu) menu.hidden = !menu.hidden;
+ });
controls.querySelector('[data-action="clear"]')?.addEventListener('click', () => {
+ const menu = controls.querySelector('[data-menu="true"]');
+ if (menu) menu.hidden = true;
const confirmed = window.confirm('Очистить журнал загруженных файлов этой сессии?');
if (!confirmed) return;
clearArweaveAttachmentHistory(state.session.login);
renderHistory();
});
+ controls.querySelector('[data-action="help"]')?.addEventListener('click', () => {
+ const menu = controls.querySelector('[data-menu="true"]');
+ if (menu) menu.hidden = true;
+ window.alert(
+ 'Здесь вы можете заранее добавить файл в Arweave. Файл загрузится в блокчейн-хранилище и сохранится в журнале только этой браузерной сессии.\n\n'
+ + 'Потом при создании сообщения можно открыть историю загрузок и выбрать уже загруженный файл по txId. '
+ + 'Если файл только что загружен, gateway может несколько минут его не отдавать, поэтому статус может быть “ещё обновляется”.'
+ );
+ });
- screen.append(intro, controls, statusLine, list);
+ screen.append(controls, statusLine, list);
renderHistory();
return screen;
}
diff --git a/shine-UI/js/pages/channel-thread-view.js b/shine-UI/js/pages/channel-thread-view.js
index bba2ba35..61bd58f0 100644
--- a/shine-UI/js/pages/channel-thread-view.js
+++ b/shine-UI/js/pages/channel-thread-view.js
@@ -12,7 +12,7 @@ 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 { markArweaveAttachmentPlaced, openArweaveAttachmentManager } from '../components/arweave-attachment-manager.js';
import {
composeMessageWithAttachments,
createAttachmentCarouselElement,
@@ -409,6 +409,7 @@ function openReplyModal({ onSubmit, navigate }) {
try {
await onSubmit(composeMessageWithAttachments(text, attachments));
+ attachments.forEach((attachment) => markArweaveAttachmentPlaced(state.session.login, attachment));
close();
} catch (error) {
setBusy(false);
diff --git a/shine-UI/js/pages/channel-view.js b/shine-UI/js/pages/channel-view.js
index e0dc360a..1829ca30 100644
--- a/shine-UI/js/pages/channel-view.js
+++ b/shine-UI/js/pages/channel-view.js
@@ -18,7 +18,7 @@ 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 { markArweaveAttachmentPlaced, openArweaveAttachmentManager } from '../components/arweave-attachment-manager.js';
import {
composeMessageWithAttachments,
createAttachmentCarouselElement,
@@ -384,6 +384,7 @@ function openReplyModal({ onSubmit, navigate }) {
try {
await onSubmit(composeMessageWithAttachments(text, attachments));
+ attachments.forEach((attachment) => markArweaveAttachmentPlaced(state.session.login, attachment));
close();
} catch (error) {
setBusy(false);
@@ -543,6 +544,7 @@ function openAddMessageModal({ channelName, onSubmit, navigate }) {
try {
await onSubmit(composeMessageWithAttachments(body, attachments));
+ attachments.forEach((attachment) => markArweaveAttachmentPlaced(state.session.login, attachment));
close();
} catch (error) {
setBusy(false);
diff --git a/shine-UI/js/pages/settings-view.js b/shine-UI/js/pages/settings-view.js
index 48566ff5..afcd046b 100644
--- a/shine-UI/js/pages/settings-view.js
+++ b/shine-UI/js/pages/settings-view.js
@@ -56,7 +56,7 @@ export function render({ navigate }) {
diff --git a/shine-UI/styles/components.css b/shine-UI/styles/components.css
index 66743672..c097410b 100644
--- a/shine-UI/styles/components.css
+++ b/shine-UI/styles/components.css
@@ -2500,20 +2500,27 @@ textarea.input {
.ar-attachment-history-tiles {
display: grid;
- gap: 0.75rem;
+ gap: 0.5rem;
max-height: 58vh;
- overflow: auto;
+ overflow-x: hidden;
+ overflow-y: auto;
padding-right: 0.1rem;
+ width: 100%;
}
.ar-attachment-history-tile {
background: linear-gradient(135deg, rgba(248, 250, 252, 0.96), rgba(236, 253, 245, 0.96));
border: 1px solid rgba(20, 184, 166, 0.22);
- border-radius: 1rem;
+ border-radius: 0.8rem;
+ box-sizing: border-box;
color: #0f172a;
display: grid;
- gap: 0.45rem;
- padding: 0.9rem;
+ gap: 0.32rem;
+ max-width: 100%;
+ min-width: 0;
+ overflow: hidden;
+ padding: 0.62rem 0.7rem;
+ width: 100%;
}
.ar-attachment-history-tile.is-selected {
@@ -2532,6 +2539,16 @@ textarea.input {
justify-content: space-between;
}
+.ar-attachment-history-name {
+ display: block;
+ font-size: 0.92rem;
+ line-height: 1.2;
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
.ar-attachment-history-tile-head strong {
min-width: 0;
overflow: hidden;
@@ -2539,19 +2556,34 @@ textarea.input {
white-space: nowrap;
}
+.ar-attachment-history-meta-row {
+ align-items: center;
+ color: #64748b;
+ display: flex;
+ flex-wrap: wrap;
+ font-size: 0.76rem;
+ gap: 0.35rem;
+ min-width: 0;
+}
+
.ar-attachment-history-txid {
background: rgba(15, 23, 42, 0.06);
- border-radius: 0.65rem;
- padding: 0.45rem 0.55rem;
+ border-radius: 0.5rem;
+ font-size: 0.62rem;
+ line-height: 1.25;
+ overflow: hidden;
+ padding: 0.32rem 0.42rem;
+ text-overflow: ellipsis;
+ white-space: nowrap;
}
.ar-attachment-status {
border-radius: 999px;
flex: 0 0 auto;
- font-size: 0.74rem;
+ font-size: 0.68rem;
font-weight: 800;
line-height: 1;
- padding: 0.35rem 0.55rem;
+ padding: 0.28rem 0.42rem;
}
.ar-attachment-status--available {
@@ -2569,20 +2601,59 @@ textarea.input {
color: #991b1b;
}
+.ar-attachment-placement-flag {
+ background: rgba(219, 234, 254, 0.96);
+ border-radius: 999px;
+ color: #1e3a8a;
+ flex: 0 0 auto;
+ font-size: 0.68rem;
+ font-weight: 800;
+ line-height: 1;
+ padding: 0.28rem 0.42rem;
+}
+
.arweave-uploads-screen {
- min-height: calc(100vh - 2rem);
+ min-height: calc(100vh - 1rem);
}
.arweave-uploads-toolbar {
align-items: center;
+ background: rgba(248, 250, 252, 0.96);
+ border: 1px solid rgba(15, 23, 42, 0.08);
+ border-radius: 0.9rem;
+ box-shadow: 0 10px 30px rgba(15, 23, 42, 0.08);
display: flex;
- flex-wrap: wrap;
- gap: 0.7rem;
+ gap: 0.5rem;
+ justify-content: space-between;
+ padding: 0.45rem;
+ position: sticky;
+ top: 0.5rem;
+ z-index: 15;
+}
+
+.arweave-uploads-back,
+.arweave-uploads-menu-btn {
+ min-width: 2.6rem;
+ padding-inline: 0.7rem;
+}
+
+.arweave-uploads-menu {
+ background: #fff;
+ border: 1px solid rgba(15, 23, 42, 0.12);
+ border-radius: 0.85rem;
+ box-shadow: 0 18px 50px rgba(15, 23, 42, 0.18);
+ display: grid;
+ min-width: 13rem;
+ padding: 0.35rem;
+ position: absolute;
+ right: 0.45rem;
+ top: calc(100% + 0.35rem);
+ z-index: 20;
}
.arweave-uploads-list {
- max-height: calc(100vh - 18rem);
- min-height: 18rem;
+ max-height: calc(100vh - 7.5rem);
+ min-height: calc(100vh - 7.5rem);
}
.mono-cell {