SHA256
Закомитил промежуточную почти работающую версию ...
This commit is contained in:
+4
-4
@@ -5,9 +5,9 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
||||
<link rel="manifest" href="./manifest.webmanifest" />
|
||||
<title>Shine UI Demo</title>
|
||||
<link rel="stylesheet" href="./styles/main.css?v=20260405171816" />
|
||||
<link rel="stylesheet" href="./styles/layout.css?v=20260405171816" />
|
||||
<link rel="stylesheet" href="./styles/components.css?v=20260405171816" />
|
||||
<link rel="stylesheet" href="./styles/main.css?v=20260407105357" />
|
||||
<link rel="stylesheet" href="./styles/layout.css?v=20260407105357" />
|
||||
<link rel="stylesheet" href="./styles/components.css?v=20260407105357" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="app-shell">
|
||||
@@ -27,6 +27,6 @@
|
||||
};
|
||||
window.__SHINE_FIREBASE_VAPID_KEY__ = '';
|
||||
</script>
|
||||
<script type="module" src="./js/app.js?v=20260405171816"></script>
|
||||
<script type="module" src="./js/app.js?v=20260407105357"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+66
-36
@@ -1,7 +1,7 @@
|
||||
import { navigate, getRoute, PRE_AUTH_PAGES } from './router.js?v=20260405171816';
|
||||
import { renderToolbar } from './components/toolbar.js?v=20260405171816';
|
||||
import { captureClientError, setClientErrorTransport } from './services/client-error-reporter.js?v=20260405171816';
|
||||
import { initPwaPush } from './services/pwa-push-service.js?v=20260405171816';
|
||||
import { navigate, getRoute, PRE_AUTH_PAGES } from './router.js?v=20260407105357';
|
||||
import { renderToolbar } from './components/toolbar.js?v=20260407105357';
|
||||
import { captureClientError, setClientErrorTransport } from './services/client-error-reporter.js?v=20260407105357';
|
||||
import { initPwaPush } from './services/pwa-push-service.js?v=20260407105357';
|
||||
import {
|
||||
authService,
|
||||
authorizeSession,
|
||||
@@ -12,38 +12,38 @@ import {
|
||||
terminateCurrentSession,
|
||||
addIncomingMessage,
|
||||
setContacts,
|
||||
} from './state.js?v=20260405171816';
|
||||
} from './state.js?v=20260407105357';
|
||||
|
||||
import * as startView from './pages/start-view.js?v=20260405171816';
|
||||
import * as entrySettingsView from './pages/entry-settings-view.js?v=20260405171816';
|
||||
import * as registerView from './pages/register-view.js?v=20260405171816';
|
||||
import * as registrationPaymentView from './pages/registration-payment-view.js?v=20260405171816';
|
||||
import * as registrationKeysView from './pages/registration-keys-view.js?v=20260405171816';
|
||||
import * as topupView from './pages/topup-view.js?v=20260405171816';
|
||||
import * as loginView from './pages/login-view.js?v=20260405171816';
|
||||
import * as loginCameraView from './pages/login-camera-view.js?v=20260405171816';
|
||||
import * as loginPasswordView from './pages/login-password-view.js?v=20260405171816';
|
||||
import * as keyStorageView from './pages/key-storage-view.js?v=20260405171816';
|
||||
import * as startView from './pages/start-view.js?v=20260407105357';
|
||||
import * as entrySettingsView from './pages/entry-settings-view.js?v=20260407105357';
|
||||
import * as registerView from './pages/register-view.js?v=20260407105357';
|
||||
import * as registrationPaymentView from './pages/registration-payment-view.js?v=20260407105357';
|
||||
import * as registrationKeysView from './pages/registration-keys-view.js?v=20260407105357';
|
||||
import * as topupView from './pages/topup-view.js?v=20260407105357';
|
||||
import * as loginView from './pages/login-view.js?v=20260407105357';
|
||||
import * as loginCameraView from './pages/login-camera-view.js?v=20260407105357';
|
||||
import * as loginPasswordView from './pages/login-password-view.js?v=20260407105357';
|
||||
import * as keyStorageView from './pages/key-storage-view.js?v=20260407105357';
|
||||
|
||||
import * as profileView from './pages/profile-view.js?v=20260405171816';
|
||||
import * as walletView from './pages/wallet-view.js?v=20260405171816';
|
||||
import * as settingsView from './pages/settings-view.js?v=20260405171816';
|
||||
import * as serverSettingsView from './pages/server-settings-view.js?v=20260405171816';
|
||||
import * as deviceView from './pages/device-view.js?v=20260405171816';
|
||||
import * as connectDeviceView from './pages/connect-device-view.js?v=20260405171816';
|
||||
import * as deviceQrView from './pages/device-qr-view.js?v=20260405171816';
|
||||
import * as deviceCameraView from './pages/device-camera-view.js?v=20260405171816';
|
||||
import * as showKeysView from './pages/show-keys-view.js?v=20260405171816';
|
||||
import * as deviceSessionView from './pages/device-session-view.js?v=20260405171816';
|
||||
import * as languageView from './pages/language-view.js?v=20260405171816';
|
||||
import * as messagesList from './pages/messages-list.js?v=20260405171816';
|
||||
import * as contactSearchView from './pages/contact-search-view.js?v=20260405171816';
|
||||
import * as chatView from './pages/chat-view.js?v=20260405171816';
|
||||
import * as channelsList from './pages/channels-list.js?v=20260405171816';
|
||||
import * as channelView from './pages/channel-view.js?v=20260405171816';
|
||||
import * as addChannelView from './pages/add-channel-view.js?v=20260405171816';
|
||||
import * as networkView from './pages/network-view.js?v=20260405171816';
|
||||
import * as notificationsView from './pages/notifications-view.js?v=20260405171816';
|
||||
import * as profileView from './pages/profile-view.js?v=20260407105357';
|
||||
import * as walletView from './pages/wallet-view.js?v=20260407105357';
|
||||
import * as settingsView from './pages/settings-view.js?v=20260407105357';
|
||||
import * as serverSettingsView from './pages/server-settings-view.js?v=20260407105357';
|
||||
import * as deviceView from './pages/device-view.js?v=20260407105357';
|
||||
import * as connectDeviceView from './pages/connect-device-view.js?v=20260407105357';
|
||||
import * as deviceQrView from './pages/device-qr-view.js?v=20260407105357';
|
||||
import * as deviceCameraView from './pages/device-camera-view.js?v=20260407105357';
|
||||
import * as showKeysView from './pages/show-keys-view.js?v=20260407105357';
|
||||
import * as deviceSessionView from './pages/device-session-view.js?v=20260407105357';
|
||||
import * as languageView from './pages/language-view.js?v=20260407105357';
|
||||
import * as messagesList from './pages/messages-list.js?v=20260407105357';
|
||||
import * as contactSearchView from './pages/contact-search-view.js?v=20260407105357';
|
||||
import * as chatView from './pages/chat-view.js?v=20260407105357';
|
||||
import * as channelsList from './pages/channels-list.js?v=20260407105357';
|
||||
import * as channelView from './pages/channel-view.js?v=20260407105357';
|
||||
import * as addChannelView from './pages/add-channel-view.js?v=20260407105357';
|
||||
import * as networkView from './pages/network-view.js?v=20260407105357';
|
||||
import * as notificationsView from './pages/notifications-view.js?v=20260407105357';
|
||||
|
||||
const routes = {
|
||||
'start-view': startView,
|
||||
@@ -84,7 +84,20 @@ let currentCleanup = null;
|
||||
|
||||
setClientErrorTransport((payload) => authService.reportClientError(payload));
|
||||
|
||||
function showGlobalErrorAlert(title, details = {}) {
|
||||
const lines = [title];
|
||||
if (details.message) lines.push(`Сообщение: ${details.message}`);
|
||||
if (details.pageId) lines.push(`Экран: ${details.pageId}`);
|
||||
if (details.sourceUrl) lines.push(`Источник: ${details.sourceUrl}`);
|
||||
if (Number.isFinite(details.lineNumber)) lines.push(`Строка: ${details.lineNumber}`);
|
||||
if (Number.isFinite(details.columnNumber)) lines.push(`Колонка: ${details.columnNumber}`);
|
||||
if (details.reasonType) lines.push(`Тип: ${details.reasonType}`);
|
||||
if (details.stack) lines.push(`Stack:\n${details.stack}`);
|
||||
window.alert(lines.join('\n'));
|
||||
}
|
||||
|
||||
window.addEventListener('error', (event) => {
|
||||
const pageId = getRoute().pageId || '';
|
||||
captureClientError({
|
||||
kind: 'global_error',
|
||||
message: event.message || 'Global JS error',
|
||||
@@ -93,22 +106,39 @@ window.addEventListener('error', (event) => {
|
||||
lineNumber: event.lineno,
|
||||
columnNumber: event.colno,
|
||||
context: {
|
||||
pageId: getRoute().pageId || '',
|
||||
pageId,
|
||||
},
|
||||
});
|
||||
|
||||
showGlobalErrorAlert('Поймана глобальная ошибка UI', {
|
||||
message: event.message || 'Global JS error',
|
||||
stack: event.error?.stack || '',
|
||||
sourceUrl: event.filename || '',
|
||||
lineNumber: event.lineno,
|
||||
columnNumber: event.colno,
|
||||
pageId,
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener('unhandledrejection', (event) => {
|
||||
const reason = event.reason;
|
||||
const pageId = getRoute().pageId || '';
|
||||
captureClientError({
|
||||
kind: 'unhandled_rejection',
|
||||
message: reason?.message || String(reason || 'Unhandled promise rejection'),
|
||||
stack: reason?.stack || '',
|
||||
context: {
|
||||
pageId: getRoute().pageId || '',
|
||||
pageId,
|
||||
reasonType: reason?.constructor?.name || typeof reason,
|
||||
},
|
||||
});
|
||||
|
||||
showGlobalErrorAlert('Пойман необработанный Promise reject', {
|
||||
message: reason?.message || String(reason || 'Unhandled promise rejection'),
|
||||
stack: reason?.stack || '',
|
||||
pageId,
|
||||
reasonType: reason?.constructor?.name || typeof reason,
|
||||
});
|
||||
});
|
||||
|
||||
function renderApp() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { resolveToolbarActive } from '../router.js?v=20260405171816';
|
||||
import { resolveToolbarActive } from '../router.js?v=20260407105357';
|
||||
|
||||
const ITEMS = [
|
||||
{ pageId: 'messages-list', label: 'Личные сообщения', icon: '💬' },
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export const profile = {
|
||||
login: '@shine.alex',
|
||||
name: 'Алексей сияющий',
|
||||
name: '',
|
||||
avatarInitials: 'АС',
|
||||
phone: '+7 (916) 221-45-88',
|
||||
address: 'Москва, Пресненская наб., 12',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { renderHeader } from '../components/header.js?v=20260405171816';
|
||||
import { renderHeader } from '../components/header.js?v=20260407105357';
|
||||
|
||||
export const pageMeta = { id: 'add-channel-view', title: 'Добавить канал' };
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { renderHeader } from '../components/header.js?v=20260405171816';
|
||||
import { channelPosts, channels } from '../mock-data.js?v=20260405171816';
|
||||
import { addLocalChannelPost, authService, getLocalChannelPosts, state } from '../state.js?v=20260405171816';
|
||||
import { renderHeader } from '../components/header.js?v=20260407105357';
|
||||
import { channelPosts, channels } from '../mock-data.js?v=20260407105357';
|
||||
import { addLocalChannelPost, authService, getLocalChannelPosts, state } from '../state.js?v=20260407105357';
|
||||
|
||||
export const pageMeta = { id: 'channel-view', title: 'Канал' };
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { renderHeader } from '../components/header.js?v=20260405171816';
|
||||
import { directMessages } from '../mock-data.js?v=20260405171816';
|
||||
import { addChatMessage, getChatMessages, authService, state } from '../state.js?v=20260405171816';
|
||||
import { renderHeader } from '../components/header.js?v=20260407105357';
|
||||
import { directMessages } from '../mock-data.js?v=20260407105357';
|
||||
import { addChatMessage, getChatMessages, authService, state } from '../state.js?v=20260407105357';
|
||||
|
||||
export const pageMeta = { id: 'chat-view', title: 'Чат' };
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { renderHeader } from '../components/header.js?v=20260405171816';
|
||||
import { state } from '../state.js?v=20260405171816';
|
||||
import { renderHeader } from '../components/header.js?v=20260407105357';
|
||||
import { state } from '../state.js?v=20260407105357';
|
||||
|
||||
export const pageMeta = { id: 'connect-device-view', title: 'Подключить устройство' };
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { renderHeader } from '../components/header.js?v=20260405171816';
|
||||
import { directMessages } from '../mock-data.js?v=20260405171816';
|
||||
import { authService, ensureChat, setContacts, state } from '../state.js?v=20260405171816';
|
||||
import { renderHeader } from '../components/header.js?v=20260407105357';
|
||||
import { directMessages } from '../mock-data.js?v=20260407105357';
|
||||
import { authService, ensureChat, setContacts, state } from '../state.js?v=20260407105357';
|
||||
|
||||
export const pageMeta = { id: 'contact-search-view', title: 'Поиск контактов' };
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { renderHeader } from '../components/header.js?v=20260405171816';
|
||||
import { renderHeader } from '../components/header.js?v=20260407105357';
|
||||
|
||||
export const pageMeta = { id: 'device-camera-view', title: 'Подключить через камеру' };
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { renderHeader } from '../components/header.js?v=20260405171816';
|
||||
import { profile } from '../mock-data.js?v=20260405171816';
|
||||
import { state } from '../state.js?v=20260405171816';
|
||||
import { renderHeader } from '../components/header.js?v=20260407105357';
|
||||
import { profile } from '../mock-data.js?v=20260407105357';
|
||||
import { state } from '../state.js?v=20260407105357';
|
||||
|
||||
export const pageMeta = { id: 'device-qr-view', title: 'Показать QR-код' };
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { renderHeader } from '../components/header.js?v=20260405171816';
|
||||
import { renderHeader } from '../components/header.js?v=20260407105357';
|
||||
import {
|
||||
authService,
|
||||
isSessionInvalidError,
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
setAuthError,
|
||||
state,
|
||||
terminateCurrentSession,
|
||||
} from '../state.js?v=20260405171816';
|
||||
} from '../state.js?v=20260407105357';
|
||||
|
||||
export const pageMeta = { id: 'device-session-view', title: 'Сеанс устройства' };
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { renderHeader } from '../components/header.js?v=20260405171816';
|
||||
import { renderHeader } from '../components/header.js?v=20260407105357';
|
||||
import {
|
||||
authService,
|
||||
isSessionInvalidError,
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
setAuthInfo,
|
||||
state,
|
||||
terminateCurrentSession,
|
||||
} from '../state.js?v=20260405171816';
|
||||
} from '../state.js?v=20260407105357';
|
||||
|
||||
export const pageMeta = { id: 'device-view', title: 'Устройства' };
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { renderHeader } from '../components/header.js?v=20260405171816';
|
||||
import { checkServerAvailability, saveEntrySettings, state } from '../state.js?v=20260405171816';
|
||||
import { renderHeader } from '../components/header.js?v=20260407105357';
|
||||
import { checkServerAvailability, saveEntrySettings, state } from '../state.js?v=20260407105357';
|
||||
|
||||
export const pageMeta = { id: 'entry-settings-view', title: 'Настройки входа', showAppChrome: false };
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { renderHeader } from '../components/header.js?v=20260405171816';
|
||||
import { authorizeSession, state } from '../state.js?v=20260405171816';
|
||||
import { renderHeader } from '../components/header.js?v=20260407105357';
|
||||
import { authorizeSession, state } from '../state.js?v=20260407105357';
|
||||
|
||||
export const pageMeta = { id: 'key-storage-view', title: 'Какие ключи сохранить', showAppChrome: false };
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { renderHeader } from '../components/header.js?v=20260405171816';
|
||||
import { state } from '../state.js?v=20260405171816';
|
||||
import { renderHeader } from '../components/header.js?v=20260407105357';
|
||||
import { state } from '../state.js?v=20260407105357';
|
||||
|
||||
export const pageMeta = { id: 'language-view', title: 'Язык' };
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { renderHeader } from '../components/header.js?v=20260405171816';
|
||||
import { renderHeader } from '../components/header.js?v=20260407105357';
|
||||
|
||||
export const pageMeta = { id: 'login-camera-view', title: 'Войти по камере', showAppChrome: false };
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { renderHeader } from '../components/header.js?v=20260405171816';
|
||||
import { renderHeader } from '../components/header.js?v=20260407105357';
|
||||
import {
|
||||
authService,
|
||||
clearAuthMessages,
|
||||
setAuthBusy,
|
||||
setAuthError,
|
||||
state,
|
||||
} from '../state.js?v=20260405171816';
|
||||
} from '../state.js?v=20260407105357';
|
||||
|
||||
export const pageMeta = { id: 'login-password-view', title: 'Войти по логину', showAppChrome: false };
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { renderHeader } from '../components/header.js?v=20260405171816';
|
||||
import { renderHeader } from '../components/header.js?v=20260407105357';
|
||||
|
||||
export const pageMeta = { id: 'login-view', title: 'Войти', showAppChrome: false };
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { renderHeader } from '../components/header.js?v=20260405171816';
|
||||
import { directMessages } from '../mock-data.js?v=20260405171816';
|
||||
import { renderHeader } from '../components/header.js?v=20260407105357';
|
||||
import { directMessages } from '../mock-data.js?v=20260407105357';
|
||||
|
||||
export const pageMeta = { id: 'messages-list', title: 'Личные сообщения' };
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { renderHeader } from '../components/header.js?v=20260405171816';
|
||||
import { authService, state } from '../state.js?v=20260405171816';
|
||||
import { renderHeader } from '../components/header.js?v=20260407105357';
|
||||
import { authService, state } from '../state.js?v=20260407105357';
|
||||
|
||||
export const pageMeta = { id: 'network-view', title: 'Связи' };
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { renderHeader } from '../components/header.js?v=20260405171816';
|
||||
import { notifications } from '../mock-data.js?v=20260405171816';
|
||||
import { state } from '../state.js?v=20260405171816';
|
||||
import { renderHeader } from '../components/header.js?v=20260407105357';
|
||||
import { notifications } from '../mock-data.js?v=20260407105357';
|
||||
import { state } from '../state.js?v=20260407105357';
|
||||
|
||||
export const pageMeta = { id: 'notifications-view', title: 'Уведомления' };
|
||||
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
import { renderHeader } from '../components/header.js?v=20260405171816';
|
||||
import { profile } from '../mock-data.js?v=20260405171816';
|
||||
import { state } from '../state.js?v=20260405171816';
|
||||
import { renderHeader } from '../components/header.js?v=20260407105357';
|
||||
import { profile } from '../mock-data.js?v=20260407105357';
|
||||
import { state } from '../state.js?v=20260407105357';
|
||||
import {
|
||||
loadProfileSnapshot,
|
||||
saveProfileParamBlock,
|
||||
saveProfileToggle,
|
||||
} from '../services/user-profile-params.js?v=20260405171816';
|
||||
} from '../services/user-profile-params.js?v=20260407105357';
|
||||
|
||||
export const pageMeta = { id: 'profile-view', title: 'Профиль' };
|
||||
|
||||
function getDisplayName(fields) {
|
||||
const firstName = fields.find((field) => field.key === 'first_name')?.value?.trim() || '';
|
||||
const lastName = fields.find((field) => field.key === 'last_name')?.value?.trim() || '';
|
||||
const fullName = `${firstName} ${lastName}`.trim();
|
||||
return fullName || profile.name;
|
||||
}
|
||||
|
||||
function toggleText(enabled) {
|
||||
return enabled ? 'Yes' : 'No';
|
||||
}
|
||||
|
||||
function showLocalErrorAlert(prefix, error) {
|
||||
const message = error?.message || 'Неизвестная ошибка';
|
||||
const stack = error?.stack ? `\n\nStack:\n${error.stack}` : '';
|
||||
window.alert(`${prefix}: ${message}${stack}`);
|
||||
}
|
||||
|
||||
export function render({ navigate }) {
|
||||
const login = state.session.login || profile.login;
|
||||
const displayLogin = String(login || '').toUpperCase();
|
||||
|
||||
const screen = document.createElement('section');
|
||||
screen.className = 'stack';
|
||||
@@ -45,8 +45,7 @@ export function render({ navigate }) {
|
||||
<div class="row" style="gap:12px; align-items:center;">
|
||||
<div class="avatar large">${profile.avatarInitials}</div>
|
||||
<div>
|
||||
<h2 style="font-size:22px; margin-bottom:2px;" data-profile-name="true">${profile.name}</h2>
|
||||
<p class="meta-muted">${login}</p>
|
||||
<h2 style="font-size:22px; margin-bottom:2px;" data-profile-login="true">${displayLogin}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<button class="primary-btn" type="button" data-reload="true">Обновить</button>
|
||||
@@ -59,13 +58,6 @@ export function render({ navigate }) {
|
||||
<button class="badge profile-toggle-btn is-no" type="button" data-toggle="shine">Сияющий: No</button>
|
||||
`;
|
||||
|
||||
const hint = document.createElement('div');
|
||||
hint.className = 'card profile-data-help';
|
||||
hint.innerHTML = `
|
||||
<div class="meta-muted">Личные данные пользователя</div>
|
||||
<p>Параметры читаются через API GetUserParam. Изменения записываются в блокчейн и после этого список сразу обновляется.</p>
|
||||
`;
|
||||
|
||||
const status = document.createElement('div');
|
||||
status.className = 'status-line';
|
||||
status.textContent = 'Загрузка параметров...';
|
||||
@@ -73,7 +65,6 @@ export function render({ navigate }) {
|
||||
const listWrap = document.createElement('div');
|
||||
listWrap.className = 'stack profile-param-list';
|
||||
|
||||
const profileNameEl = topRow.querySelector('[data-profile-name="true"]');
|
||||
const reloadBtn = topRow.querySelector('[data-reload="true"]');
|
||||
const officialBtn = badgesRow.querySelector('[data-toggle="official"]');
|
||||
const shineBtn = badgesRow.querySelector('[data-toggle="shine"]');
|
||||
@@ -105,15 +96,15 @@ export function render({ navigate }) {
|
||||
}
|
||||
|
||||
function renderFields(fields) {
|
||||
profileNameEl.textContent = getDisplayName(fields);
|
||||
|
||||
listWrap.innerHTML = '';
|
||||
fields.forEach((field) => {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'card profile-param-item row';
|
||||
const value = String(field.value || '').trim() || 'не заполнено';
|
||||
const isNameField = field.key === 'first_name' || field.key === 'last_name';
|
||||
const valueClass = isNameField ? 'profile-param-value profile-param-value-small' : 'profile-param-value';
|
||||
row.innerHTML = `
|
||||
<div class="profile-param-value"><b>${field.label}</b>: ${value}</div>
|
||||
<div class="${valueClass}"><b>${field.label}</b>: ${value}</div>
|
||||
<button class="ghost-btn" type="button" data-edit-field="${field.key}">Изменить</button>
|
||||
`;
|
||||
listWrap.append(row);
|
||||
@@ -140,6 +131,7 @@ export function render({ navigate }) {
|
||||
} catch (error) {
|
||||
status.className = 'status-line is-unavailable';
|
||||
status.textContent = `Не удалось загрузить параметры: ${error.message || 'ошибка сети'}`;
|
||||
showLocalErrorAlert('Ошибка загрузки параметров профиля', error);
|
||||
} finally {
|
||||
reloadBtn.disabled = false;
|
||||
officialBtn.disabled = false;
|
||||
@@ -167,6 +159,7 @@ export function render({ navigate }) {
|
||||
} catch (error) {
|
||||
status.className = 'status-line is-unavailable';
|
||||
status.textContent = `Не удалось изменить ${toggleKey}: ${error.message || 'ошибка сети'}`;
|
||||
showLocalErrorAlert(`Ошибка изменения ${toggleKey}`, error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,6 +184,7 @@ export function render({ navigate }) {
|
||||
} catch (error) {
|
||||
status.className = 'status-line is-unavailable';
|
||||
status.textContent = `Не удалось изменить ${field.key}: ${error.message || 'ошибка сети'}`;
|
||||
showLocalErrorAlert(`Ошибка изменения ${field.key}`, error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,7 +200,7 @@ export function render({ navigate }) {
|
||||
officialBtn.addEventListener('click', () => onToggleClick('official'));
|
||||
shineBtn.addEventListener('click', () => onToggleClick('shine'));
|
||||
|
||||
card.append(topRow, badgesRow, hint, status, listWrap);
|
||||
card.append(topRow, badgesRow, status, listWrap);
|
||||
screen.append(card);
|
||||
|
||||
refreshProfileSnapshot();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { renderHeader } from '../components/header.js?v=20260405171816';
|
||||
import { authService, clearAuthMessages, state } from '../state.js?v=20260405171816';
|
||||
import { renderHeader } from '../components/header.js?v=20260407105357';
|
||||
import { authService, clearAuthMessages, state } from '../state.js?v=20260407105357';
|
||||
|
||||
export const pageMeta = { id: 'register-view', title: 'Зарегистрироваться', showAppChrome: false };
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { renderHeader } from '../components/header.js?v=20260405171816';
|
||||
import { renderHeader } from '../components/header.js?v=20260407105357';
|
||||
import {
|
||||
authService,
|
||||
authorizeSession,
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
setAuthError,
|
||||
setAuthInfo,
|
||||
state,
|
||||
} from '../state.js?v=20260405171816';
|
||||
} from '../state.js?v=20260407105357';
|
||||
|
||||
export const pageMeta = { id: 'registration-keys-view', title: 'Сохранение ключей', showAppChrome: false };
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { renderHeader } from '../components/header.js?v=20260405171816';
|
||||
import { renderHeader } from '../components/header.js?v=20260407105357';
|
||||
import {
|
||||
authService,
|
||||
refreshRegistrationBalance,
|
||||
setAuthError,
|
||||
setAuthInfo,
|
||||
state,
|
||||
} from '../state.js?v=20260405171816';
|
||||
} from '../state.js?v=20260407105357';
|
||||
|
||||
export const pageMeta = { id: 'registration-payment-view', title: 'Оплата регистрации', showAppChrome: false };
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { renderHeader } from '../components/header.js?v=20260405171816';
|
||||
import { checkServerAvailability, saveEntrySettings, state } from '../state.js?v=20260405171816';
|
||||
import { renderHeader } from '../components/header.js?v=20260407105357';
|
||||
import { checkServerAvailability, saveEntrySettings, state } from '../state.js?v=20260407105357';
|
||||
|
||||
export const pageMeta = { id: 'server-settings-view', title: 'Настройки серверов' };
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { renderHeader } from '../components/header.js?v=20260405171816';
|
||||
import { renderHeader } from '../components/header.js?v=20260407105357';
|
||||
|
||||
export const pageMeta = { id: 'settings-view', title: 'Настройки' };
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { renderHeader } from '../components/header.js?v=20260405171816';
|
||||
import { state } from '../state.js?v=20260405171816';
|
||||
import { loadEncryptedUserSecrets } from '../services/key-vault.js?v=20260405171816';
|
||||
import { renderHeader } from '../components/header.js?v=20260407105357';
|
||||
import { state } from '../state.js?v=20260407105357';
|
||||
import { loadEncryptedUserSecrets } from '../services/key-vault.js?v=20260407105357';
|
||||
|
||||
export const pageMeta = { id: 'show-keys-view', title: 'Показать ключи' };
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { clearStartHint, state } from '../state.js?v=20260405171816';
|
||||
import { clearStartHint, state } from '../state.js?v=20260407105357';
|
||||
|
||||
export const pageMeta = { id: 'start-view', title: 'Старт', showAppChrome: false };
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { renderHeader } from '../components/header.js?v=20260405171816';
|
||||
import { state } from '../state.js?v=20260405171816';
|
||||
import { renderHeader } from '../components/header.js?v=20260407105357';
|
||||
import { state } from '../state.js?v=20260407105357';
|
||||
|
||||
export const pageMeta = { id: 'topup-view', title: 'Пополнение счета', showAppChrome: false };
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { renderHeader } from '../components/header.js?v=20260405171816';
|
||||
import { wallet } from '../mock-data.js?v=20260405171816';
|
||||
import { renderHeader } from '../components/header.js?v=20260407105357';
|
||||
import { wallet } from '../mock-data.js?v=20260407105357';
|
||||
|
||||
export const pageMeta = { id: 'wallet-view', title: 'Кошелёк' };
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { WsJsonClient } from './ws-client.js?v=20260405171816';
|
||||
import { WsJsonClient } from './ws-client.js?v=20260407105357';
|
||||
import {
|
||||
bytesToBase64,
|
||||
deriveEd25519FromPassword,
|
||||
@@ -11,13 +11,13 @@ import {
|
||||
signBytes,
|
||||
signBase64,
|
||||
utf8Bytes,
|
||||
} from './crypto-utils.js?v=20260405171816';
|
||||
} from './crypto-utils.js?v=20260407105357';
|
||||
import {
|
||||
loadEncryptedUserSecrets,
|
||||
loadSessionMaterial,
|
||||
saveEncryptedUserSecrets,
|
||||
saveSessionMaterial,
|
||||
} from './key-vault.js?v=20260405171816';
|
||||
} from './key-vault.js?v=20260407105357';
|
||||
|
||||
const BCH_SUFFIX = '001';
|
||||
|
||||
@@ -378,6 +378,12 @@ export class AuthService {
|
||||
|
||||
const user = await this.getUser(cleanLogin);
|
||||
const blockchainName = String(user?.blockchainName || `${cleanLogin}-${BCH_SUFFIX}`).trim();
|
||||
const freshNum = Number(user?.serverLastGlobalNumber);
|
||||
const freshHash = String(user?.serverLastGlobalHash || '').trim().toLowerCase();
|
||||
const freshCursor = {
|
||||
serverLastGlobalNumber: Number.isFinite(freshNum) ? freshNum : -1,
|
||||
serverLastGlobalHash: freshHash.length === 64 ? freshHash : '0'.repeat(64),
|
||||
};
|
||||
|
||||
const savedKeys = await loadEncryptedUserSecrets(cleanLogin, storagePwd);
|
||||
const blockchainPrivatePkcs8 = savedKeys?.blockchainKey;
|
||||
@@ -391,11 +397,14 @@ export class AuthService {
|
||||
const blockNumber = Number(cursor?.serverLastGlobalNumber ?? -1) + 1;
|
||||
const prevBlockHash = String(cursor?.serverLastGlobalHash || '0'.repeat(64));
|
||||
|
||||
// Для USER_PARAM отправляем старт новой line-цепочки:
|
||||
// prevLineNumber=-1, prevLineHash=0x00..00, thisLineNumber=-1.
|
||||
// Этот формат соответствует BodyHasLine правилам на сервере.
|
||||
const bodyBytes = makeUserParamBodyBytes({
|
||||
lineCode: Number(cursor?.serverLastGlobalNumber ?? 0),
|
||||
prevLineNumber: Number(cursor?.serverLastGlobalNumber ?? 0),
|
||||
prevLineHashHex: prevBlockHash,
|
||||
thisLineNumber: 1,
|
||||
lineCode: 0,
|
||||
prevLineNumber: -1,
|
||||
prevLineHashHex: '0'.repeat(64),
|
||||
thisLineNumber: -1,
|
||||
key: cleanParam,
|
||||
value: cleanValue,
|
||||
});
|
||||
@@ -426,7 +435,7 @@ export class AuthService {
|
||||
return response;
|
||||
};
|
||||
|
||||
let cursor = { serverLastGlobalNumber: -1, serverLastGlobalHash: '0'.repeat(64) };
|
||||
let cursor = freshCursor;
|
||||
let response = await tryAdd(cursor);
|
||||
if (response.status !== 200) {
|
||||
const knownNum = Number(response?.payload?.serverLastGlobalNumber);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
decryptJsonWithStoragePwd,
|
||||
encryptJsonWithStoragePwd,
|
||||
} from './crypto-utils.js?v=20260405171816';
|
||||
} from './crypto-utils.js?v=20260407105357';
|
||||
|
||||
const DB_NAME = 'shine-ui-auth';
|
||||
const DB_VERSION = 1;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { authService, state } from '../state.js?v=20260403081123';
|
||||
import { authService, state } from '../state.js?v=20260407105357';
|
||||
|
||||
export const profileFieldDefs = [
|
||||
{ key: 'first_name', readKeys: ['first_name'], label: 'Имя', placeholder: 'Введите имя' },
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { captureClientError } from './client-error-reporter.js?v=20260405171816';
|
||||
import { captureClientError } from './client-error-reporter.js?v=20260407105357';
|
||||
|
||||
const DEFAULT_TIMEOUT_MS = 12000;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { chatMessages, wallet } from './mock-data.js?v=20260405171816';
|
||||
import { AuthService } from './services/auth-service.js?v=20260405171816';
|
||||
import { clearClientAuthData } from './services/key-vault.js?v=20260405171816';
|
||||
import { chatMessages, wallet } from './mock-data.js?v=20260407105357';
|
||||
import { AuthService } from './services/auth-service.js?v=20260407105357';
|
||||
import { clearClientAuthData } from './services/key-vault.js?v=20260407105357';
|
||||
|
||||
const clone = (value) => JSON.parse(JSON.stringify(value));
|
||||
const SESSION_STORAGE_KEY = 'shine-ui-current-session-v1';
|
||||
|
||||
@@ -177,6 +177,10 @@
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.profile-param-value-small {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.profile-param-time {
|
||||
font-size: 12px;
|
||||
}
|
||||
@@ -408,6 +412,10 @@
|
||||
.avatar {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
min-width: 44px;
|
||||
min-height: 44px;
|
||||
flex: 0 0 auto;
|
||||
aspect-ratio: 1 / 1;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(130deg, #3c4f73, #243352);
|
||||
display: grid;
|
||||
|
||||
Reference in New Issue
Block a user