SHA256
Wallet-session pairing и browser plugin wallet, оплаты пока не работают
This commit is contained in:
@@ -0,0 +1,244 @@
|
||||
const els = {
|
||||
serverUrl: document.querySelector('#server-url'),
|
||||
loginInput: document.querySelector('#login-input'),
|
||||
usePassword: document.querySelector('#use-password'),
|
||||
passwordField: document.querySelector('#password-field'),
|
||||
passwordInput: document.querySelector('#password-input'),
|
||||
startBtn: document.querySelector('#start-btn'),
|
||||
pairingCard: document.querySelector('#pairing-card'),
|
||||
shortCode: document.querySelector('#short-code'),
|
||||
pairingHint: document.querySelector('#pairing-hint'),
|
||||
pairingExpire: document.querySelector('#pairing-expire'),
|
||||
cancelBtn: document.querySelector('#cancel-btn'),
|
||||
status: document.querySelector('#status'),
|
||||
sessionCard: document.querySelector('#session-card'),
|
||||
sessionLogin: document.querySelector('#session-login'),
|
||||
sessionId: document.querySelector('#session-id'),
|
||||
sessionType: document.querySelector('#session-type'),
|
||||
resumeBtn: document.querySelector('#resume-btn'),
|
||||
disconnectBtn: document.querySelector('#disconnect-btn'),
|
||||
connectionPill: document.querySelector('#connection-pill'),
|
||||
};
|
||||
|
||||
let state = {
|
||||
settings: {
|
||||
serverUrl: 'wss://shineup.me/ws',
|
||||
login: '',
|
||||
},
|
||||
pairing: {
|
||||
active: false,
|
||||
pairingId: '',
|
||||
expiresAtMs: 0,
|
||||
},
|
||||
session: null,
|
||||
connectionOnline: false,
|
||||
status: {
|
||||
text: '',
|
||||
kind: 'info',
|
||||
},
|
||||
};
|
||||
|
||||
let refreshTimer = 0;
|
||||
let saveSettingsTimer = 0;
|
||||
|
||||
function setStatus(message, kind = 'info') {
|
||||
els.status.textContent = String(message || '');
|
||||
els.status.className = `status ${kind === 'error' ? 'error' : 'info'}`;
|
||||
els.status.classList.toggle('hidden', !message);
|
||||
}
|
||||
|
||||
function setConnectedPill(connected) {
|
||||
els.connectionPill.textContent = connected ? 'online' : 'offline';
|
||||
els.connectionPill.className = connected ? 'pill pill-online' : 'pill pill-offline';
|
||||
}
|
||||
|
||||
function formatRemaining(ms) {
|
||||
const safe = Math.max(0, Math.floor(Number(ms || 0) / 1000));
|
||||
const minutes = Math.floor(safe / 60);
|
||||
const seconds = safe % 60;
|
||||
return `${minutes} мин ${seconds} сек`;
|
||||
}
|
||||
|
||||
function applyState(nextState) {
|
||||
state = nextState || state;
|
||||
const serverValue = String(state?.settings?.serverUrl || 'wss://shineup.me/ws');
|
||||
const loginValue = String(state?.settings?.login || '');
|
||||
if (document.activeElement !== els.serverUrl) {
|
||||
els.serverUrl.value = serverValue;
|
||||
}
|
||||
if (document.activeElement !== els.loginInput) {
|
||||
els.loginInput.value = loginValue;
|
||||
}
|
||||
setConnectedPill(!!state?.connectionOnline);
|
||||
setStatus(state?.status?.text || '', state?.status?.kind || 'info');
|
||||
|
||||
const session = state?.session;
|
||||
if (session) {
|
||||
els.sessionCard.classList.remove('hidden');
|
||||
els.sessionLogin.textContent = session.login || '—';
|
||||
els.sessionId.textContent = session.sessionId || '—';
|
||||
els.sessionType.textContent = String(session.sessionType || 50) === '50' ? 'wallet' : String(session.sessionType || '—');
|
||||
} else {
|
||||
els.sessionCard.classList.add('hidden');
|
||||
els.sessionLogin.textContent = '—';
|
||||
els.sessionId.textContent = '—';
|
||||
els.sessionType.textContent = 'wallet';
|
||||
}
|
||||
|
||||
const pairing = state?.pairing || {};
|
||||
if (pairing.active) {
|
||||
els.pairingCard.classList.remove('hidden');
|
||||
const shortCode = String(pairing.shortCode || els.shortCode.dataset.shortCode || els.shortCode.textContent || '0000000');
|
||||
els.shortCode.dataset.shortCode = shortCode;
|
||||
els.shortCode.textContent = shortCode;
|
||||
els.pairingHint.textContent = pairing.trustedSessionOnline
|
||||
? 'Покажите код на доверенном устройстве и подтвердите выпуск wallet-session.'
|
||||
: 'Сейчас нет онлайн доверенной сессии. Откройте другое устройство и подтвердите заявку.';
|
||||
const leftMs = Number(pairing.expiresAtMs || 0) - Date.now();
|
||||
els.pairingExpire.textContent = leftMs > 0 ? `Код действителен ещё ${formatRemaining(leftMs)}.` : 'Время ожидания истекло.';
|
||||
els.startBtn.disabled = true;
|
||||
} else {
|
||||
els.pairingCard.classList.add('hidden');
|
||||
els.shortCode.textContent = '0000000';
|
||||
delete els.shortCode.dataset.shortCode;
|
||||
els.pairingExpire.textContent = '';
|
||||
els.startBtn.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeError(response, fallback) {
|
||||
return response?.error || fallback || 'Unknown error';
|
||||
}
|
||||
|
||||
function sendMessage(type, payload = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
chrome.runtime.sendMessage({ type, payload }, (response) => {
|
||||
if (chrome.runtime.lastError) {
|
||||
reject(new Error(chrome.runtime.lastError.message || 'Runtime message failed'));
|
||||
return;
|
||||
}
|
||||
if (!response?.ok) {
|
||||
reject(new Error(normalizeError(response, 'Wallet operation failed')));
|
||||
return;
|
||||
}
|
||||
if (response?.state) applyState(response.state);
|
||||
resolve(response);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function refreshState() {
|
||||
const response = await sendMessage('wallet:getState');
|
||||
applyState(response.state);
|
||||
}
|
||||
|
||||
async function saveSettings() {
|
||||
await sendMessage('wallet:saveSettings', {
|
||||
serverUrl: String(els.serverUrl.value || '').trim(),
|
||||
login: String(els.loginInput.value || '').trim(),
|
||||
});
|
||||
}
|
||||
|
||||
function scheduleSaveSettings() {
|
||||
if (saveSettingsTimer) {
|
||||
window.clearTimeout(saveSettingsTimer);
|
||||
}
|
||||
saveSettingsTimer = window.setTimeout(() => {
|
||||
saveSettingsTimer = 0;
|
||||
void saveSettings();
|
||||
}, 250);
|
||||
}
|
||||
|
||||
async function startPairing() {
|
||||
const login = String(els.loginInput.value || '').trim();
|
||||
if (!login) {
|
||||
setStatus('Введите логин.', 'error');
|
||||
return;
|
||||
}
|
||||
setStatus('Создаём wallet-session заявку...', 'info');
|
||||
els.startBtn.disabled = true;
|
||||
try {
|
||||
const response = await sendMessage('wallet:startPairing', {
|
||||
login,
|
||||
usePassword: !!els.usePassword.checked,
|
||||
password: String(els.passwordInput.value || ''),
|
||||
serverUrl: String(els.serverUrl.value || '').trim(),
|
||||
});
|
||||
applyState(response.state);
|
||||
} catch (error) {
|
||||
els.startBtn.disabled = false;
|
||||
setStatus(error.message || 'Не удалось начать pairing.', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function cancelPairing() {
|
||||
try {
|
||||
await sendMessage('wallet:cancelPairing');
|
||||
} catch (error) {
|
||||
setStatus(error.message || 'Не удалось отменить pairing.', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function resumeSession() {
|
||||
setStatus('Проверяем сохранённую wallet-session...', 'info');
|
||||
try {
|
||||
await sendMessage('wallet:resumeSession');
|
||||
} catch (error) {
|
||||
setStatus(error.message || 'Не удалось восстановить session.', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function disconnectSession() {
|
||||
try {
|
||||
await sendMessage('wallet:disconnectSession');
|
||||
} catch (error) {
|
||||
setStatus(error.message || 'Не удалось удалить session.', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
function startUiRefreshLoop() {
|
||||
stopUiRefreshLoop();
|
||||
refreshTimer = window.setInterval(() => {
|
||||
void refreshState();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function stopUiRefreshLoop() {
|
||||
if (refreshTimer) {
|
||||
window.clearInterval(refreshTimer);
|
||||
refreshTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function bindUi() {
|
||||
els.usePassword.addEventListener('change', () => {
|
||||
els.passwordField.classList.toggle('hidden', !els.usePassword.checked);
|
||||
if (!els.usePassword.checked) {
|
||||
els.passwordInput.value = '';
|
||||
}
|
||||
});
|
||||
els.serverUrl.addEventListener('input', () => { scheduleSaveSettings(); });
|
||||
els.serverUrl.addEventListener('change', () => { void saveSettings(); });
|
||||
els.loginInput.addEventListener('input', () => { scheduleSaveSettings(); });
|
||||
els.loginInput.addEventListener('change', () => { void saveSettings(); });
|
||||
els.startBtn.addEventListener('click', () => { void startPairing(); });
|
||||
els.cancelBtn.addEventListener('click', () => { void cancelPairing(); });
|
||||
els.resumeBtn.addEventListener('click', () => { void resumeSession(); });
|
||||
els.disconnectBtn.addEventListener('click', () => { void disconnectSession(); });
|
||||
}
|
||||
|
||||
async function init() {
|
||||
bindUi();
|
||||
await refreshState();
|
||||
startUiRefreshLoop();
|
||||
}
|
||||
|
||||
window.addEventListener('beforeunload', () => {
|
||||
stopUiRefreshLoop();
|
||||
if (saveSettingsTimer) {
|
||||
window.clearTimeout(saveSettingsTimer);
|
||||
saveSettingsTimer = 0;
|
||||
}
|
||||
});
|
||||
|
||||
void init();
|
||||
Reference in New Issue
Block a user