Files
SHiNE-server/shine-UI/js/pages/devnet-topup-view.js
T
qandClaude Opus 5.5 f8900e531a UI: новый общий стиль, тёмная/светлая тема, настраиваемая палитра
- styles/main.css: роли цветов (по умолчанию «Индиго»), шкала отступов/скруглений/шрифтов,
  цвета отношений для обеих тем; жёсткие цвета в стилях заменены на роли.
- Палитра: пресеты и личные правки, «Оформление» Авто/День/Ночь, долгое нажатие —
  редактор цветов с экспортом/импортом.
- Каналы: пузыри постов автора, плашки дней, строка «Написать в канал…», «О канале»,
  создание канала с адресом из названия; лента открывается на свежих постах.
- Чаты: плоский список, чипы-фильтры, пузыри, плашки дней; нижняя панель скрыта в переписке.
- Корневые разделы — единая шапка; профиль и чужой профиль — общая карточка;
  настройки, кошелёк, сеансы — меню-списки.
- Нижняя панель: иконки без подписей, бейджи на иконках.
- confirmDialog вместо window.confirm/alert; на телефоне диалоги — шторки снизу.
- docs/UI-Design/ISSUES-for-dev.md — найденные ошибки сервера/UI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 10:05:26 +03:00

198 lines
7.4 KiB
JavaScript

import { createTopBar } from '../components/topbar.js';
import { formatSol, getBalanceSol, transferSol, createSolanaWalletFromPrivateBase58 } from '../services/solana-wallet-service.js';
export const pageMeta = { id: 'devnet-topup-view', title: 'Пополнение DEVNET', showAppChrome: false };
const DEVNET_ENDPOINT = 'https://api.devnet.solana.com';
const SENDER_PRIVATE_32_BASE58 = '6xqAuKYvA8qrCdAkcw7Y8aMgvBnYk8JLxWLma5BzbAvu';
const TRANSFER_AMOUNT_SOL = 0.02;
function readWalletFromUrl() {
try {
const url = new URL(window.location.href);
return String(url.searchParams.get('wallet') || '').trim();
} catch {
return '';
}
}
export function render() {
const screen = document.createElement('section');
screen.className = 'stack';
screen.style.width = '100%';
screen.style.justifyItems = 'center';
const targetWallet = readWalletFromUrl();
const senderBox = document.createElement('div');
senderBox.className = 'card stack';
senderBox.style.width = 'min(100%, 320px)';
senderBox.innerHTML = `
<strong>Тестовый DEVNET-кошелёк</strong>
<p class="meta-muted" id="devnet-topup-sender-address">Адрес: ...</p>
<p class="meta-muted" id="devnet-topup-sender-balance">Баланс: ...</p>
`;
const targetBox = document.createElement('div');
targetBox.className = 'card stack';
targetBox.style.width = 'min(100%, 320px)';
targetBox.innerHTML = `
<strong>Кошелёк получателя</strong>
<p class="meta-muted" style="word-break:break-all;">${targetWallet || 'Не передан параметр wallet'}</p>
<p class="meta-muted">Сумма перевода: ${TRANSFER_AMOUNT_SOL} SOL</p>
<p class="meta-muted" id="devnet-topup-target-balance">Баланс: ...</p>
`;
const status = document.createElement('p');
status.className = 'meta-muted';
status.style.width = 'min(100%, 320px)';
status.style.overflowWrap = 'anywhere';
status.style.wordBreak = 'break-word';
status.style.whiteSpace = 'pre-wrap';
status.textContent = `Готово к пополнению.\nСеть: DEVNET (${DEVNET_ENDPOINT})`;
const amountHint = document.createElement('p');
amountHint.style.width = 'min(100%, 320px)';
amountHint.style.display = 'none';
amountHint.style.margin = '0';
amountHint.style.fontSize = '22px';
amountHint.style.fontWeight = '800';
amountHint.style.lineHeight = '1.25';
amountHint.style.color = 'var(--success)';
amountHint.style.textAlign = 'center';
amountHint.textContent = '';
const balanceHint = document.createElement('p');
balanceHint.className = 'meta-muted';
balanceHint.style.width = 'min(100%, 320px)';
balanceHint.style.display = 'none';
balanceHint.style.margin = '0';
balanceHint.style.textAlign = 'center';
balanceHint.style.fontSize = '14px';
balanceHint.style.color = 'var(--text-primary)';
balanceHint.textContent = '';
const closeHint = document.createElement('p');
closeHint.style.width = 'min(100%, 320px)';
closeHint.style.display = 'none';
closeHint.style.margin = '0';
closeHint.style.fontSize = '26px';
closeHint.style.fontWeight = '800';
closeHint.style.lineHeight = '1.25';
closeHint.style.color = 'var(--success)';
closeHint.style.textAlign = 'center';
closeHint.textContent = 'Можете закрыть эту страницу и продолжить регистрацию.';
const fillBtn = document.createElement('button');
fillBtn.className = 'primary-btn';
fillBtn.type = 'button';
fillBtn.textContent = `Пополнить на ${TRANSFER_AMOUNT_SOL} SOL`;
const actions = document.createElement('div');
actions.className = 'auth-footer-actions';
actions.style.width = 'min(100%, 320px)';
actions.style.justifySelf = 'center';
actions.append(fillBtn);
let senderAddress = '';
let senderKeypair = null;
const updateSenderBalance = async () => {
if (!senderAddress) return;
const balance = await getBalanceSol({ endpoint: DEVNET_ENDPOINT, address: senderAddress });
const senderBalanceEl = senderBox.querySelector('#devnet-topup-sender-balance');
if (senderBalanceEl) senderBalanceEl.textContent = `Баланс: ${formatSol(balance.sol, 6)} SOL`;
};
const updateTargetBalance = async () => {
if (!targetWallet) return null;
const balance = await getBalanceSol({ endpoint: DEVNET_ENDPOINT, address: targetWallet });
const targetBalanceEl = targetBox.querySelector('#devnet-topup-target-balance');
if (targetBalanceEl) targetBalanceEl.textContent = `Баланс: ${formatSol(balance.sol, 6)} SOL`;
return balance.sol;
};
fillBtn.addEventListener('click', async () => {
if (!targetWallet) {
status.textContent = 'Ошибка: в URL не передан параметр wallet.';
return;
}
if (!senderKeypair) {
status.textContent = 'Ошибка: кошелёк отправителя не инициализирован.';
return;
}
fillBtn.disabled = true;
status.textContent = 'Отправляем перевод...';
amountHint.style.display = 'none';
balanceHint.style.display = 'none';
closeHint.style.display = 'none';
try {
const tx = await transferSol({
endpoint: DEVNET_ENDPOINT,
fromKeypair: senderKeypair,
toAddress: targetWallet,
amountSol: TRANSFER_AMOUNT_SOL,
});
await updateSenderBalance();
const targetBalanceSol = await updateTargetBalance();
status.textContent = `Транзакция прошла.\nSignature: ${tx.signature}`;
status.style.fontSize = '13px';
status.style.fontWeight = '500';
status.style.color = 'var(--text-secondary)';
fillBtn.style.display = 'none';
actions.style.display = 'none';
amountHint.style.display = '';
amountHint.textContent = `Кошелёк пополнен на ${TRANSFER_AMOUNT_SOL} SOL`;
balanceHint.style.display = '';
balanceHint.textContent = `Новый баланс: ${formatSol(targetBalanceSol || 0, 6)} SOL`;
closeHint.style.display = '';
} catch (error) {
status.textContent = `Ошибка перевода: ${error?.message || 'unknown'}`;
status.style.fontSize = '';
status.style.fontWeight = '';
status.style.color = '';
amountHint.style.display = 'none';
balanceHint.style.display = 'none';
closeHint.style.display = 'none';
} finally {
fillBtn.disabled = false;
}
});
(async () => {
try {
const sender = await createSolanaWalletFromPrivateBase58(SENDER_PRIVATE_32_BASE58);
senderAddress = sender.address;
senderKeypair = sender.keypair;
const senderAddressEl = senderBox.querySelector('#devnet-topup-sender-address');
if (senderAddressEl) senderAddressEl.textContent = `Адрес: ${senderAddress}`;
await updateSenderBalance();
await updateTargetBalance();
if (!targetWallet) {
fillBtn.disabled = true;
status.textContent = 'Передайте адрес получателя в параметре wallet.';
}
} catch (error) {
fillBtn.disabled = true;
status.textContent = `Ошибка инициализации отправителя: ${error?.message || 'unknown'}`;
}
})();
screen.append(
createTopBar({
title: 'DEVNET пополнение',
}),
senderBox,
targetBox,
status,
amountHint,
balanceHint,
closeHint,
actions,
);
return screen;
}