Откат мультисессии: возвращен один активный сеанс
This commit is contained in:
parent
94263a46bd
commit
0fdb5b245c
@ -1,2 +1,2 @@
|
||||
client.version=1.2.54
|
||||
server.version=1.2.48
|
||||
client.version=1.2.55
|
||||
server.version=1.2.49
|
||||
|
||||
@ -30,7 +30,6 @@ import {
|
||||
markIncomingReadByBaseKey,
|
||||
markOutgoingReadByBaseKey,
|
||||
setContacts,
|
||||
cancelAddAccountFlow,
|
||||
} from './state.js';
|
||||
|
||||
import * as startView from './pages/start-view.js';
|
||||
@ -45,7 +44,6 @@ import * as loginPasswordView from './pages/login-password-view.js';
|
||||
import * as keyStorageView from './pages/key-storage-view.js';
|
||||
|
||||
import * as profileView from './pages/profile-view.js';
|
||||
import * as accountSwitcherView from './pages/account-switcher-view.js';
|
||||
import * as profileEditView from './pages/profile-edit-view.js';
|
||||
import * as walletView from './pages/wallet-view.js';
|
||||
import * as settingsView from './pages/settings-view.js';
|
||||
@ -85,7 +83,6 @@ const routes = {
|
||||
'login-password-view': loginPasswordView,
|
||||
'key-storage-view': keyStorageView,
|
||||
'profile-view': profileView,
|
||||
'account-switcher-view': accountSwitcherView,
|
||||
'profile-edit-view': profileEditView,
|
||||
'wallet-view': walletView,
|
||||
'settings-view': settingsView,
|
||||
@ -679,13 +676,10 @@ function renderApp() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.session.isAuthorized && PRE_AUTH_PAGES.includes(pageId) && !state.accountAddingMode) {
|
||||
if (state.session.isAuthorized && PRE_AUTH_PAGES.includes(pageId)) {
|
||||
navigate('messages-list');
|
||||
return;
|
||||
}
|
||||
if (state.session.isAuthorized && !PRE_AUTH_PAGES.includes(pageId) && state.accountAddingMode) {
|
||||
cancelAddAccountFlow();
|
||||
}
|
||||
|
||||
const page = routes[pageId] || routes['start-view'];
|
||||
|
||||
|
||||
@ -9,7 +9,6 @@ const ITEMS = [
|
||||
{ pageId: 'profile-view', label: 'Профиль', icon: '👤' },
|
||||
];
|
||||
const CHANNEL_HOLD_MS = 260;
|
||||
const PROFILE_HOLD_MS = 320;
|
||||
const CHANNEL_MODES = Object.freeze([
|
||||
{ key: 'feed', label: 'Каналы' },
|
||||
{ key: 'dialogs', label: 'Чаты' },
|
||||
@ -63,8 +62,6 @@ export function renderToolbar(currentPageId, navigate) {
|
||||
}
|
||||
if (item.pageId === 'channels-list') {
|
||||
installChannelsHoldSwitcher(btn, navigate);
|
||||
} else if (item.pageId === 'profile-view') {
|
||||
installProfileHoldMenu(btn, navigate);
|
||||
} else {
|
||||
btn.addEventListener('click', () => navigate(item.pageId));
|
||||
}
|
||||
@ -74,73 +71,6 @@ export function renderToolbar(currentPageId, navigate) {
|
||||
return root;
|
||||
}
|
||||
|
||||
function installProfileHoldMenu(button, navigate) {
|
||||
let holdTimer = 0;
|
||||
let pressed = false;
|
||||
let holdActive = false;
|
||||
let overlay = null;
|
||||
|
||||
const clearTimer = () => {
|
||||
if (holdTimer) {
|
||||
window.clearTimeout(holdTimer);
|
||||
holdTimer = 0;
|
||||
}
|
||||
};
|
||||
|
||||
const closeOverlay = () => {
|
||||
if (overlay) overlay.remove();
|
||||
overlay = null;
|
||||
holdActive = false;
|
||||
};
|
||||
|
||||
const openOverlay = () => {
|
||||
const rect = button.getBoundingClientRect();
|
||||
overlay = document.createElement('div');
|
||||
overlay.className = 'toolbar-channels-hold-overlay';
|
||||
overlay.style.minWidth = '190px';
|
||||
overlay.style.left = `${Math.round(rect.left + rect.width / 2)}px`;
|
||||
overlay.style.top = `${Math.round(rect.top - 12)}px`;
|
||||
overlay.innerHTML = `<button type="button" class="toolbar-channels-hold-item is-active" data-action="switch-profile">Сменить профиль</button>`;
|
||||
overlay.querySelector('[data-action="switch-profile"]')?.addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
closeOverlay();
|
||||
navigate('account-switcher-view');
|
||||
});
|
||||
document.body.append(overlay);
|
||||
holdActive = true;
|
||||
};
|
||||
|
||||
button.addEventListener('pointerdown', () => {
|
||||
pressed = true;
|
||||
holdActive = false;
|
||||
clearTimer();
|
||||
holdTimer = window.setTimeout(() => {
|
||||
if (!pressed) return;
|
||||
openOverlay();
|
||||
}, PROFILE_HOLD_MS);
|
||||
});
|
||||
|
||||
button.addEventListener('pointerup', () => {
|
||||
clearTimer();
|
||||
const wasHold = holdActive;
|
||||
pressed = false;
|
||||
if (wasHold) {
|
||||
window.setTimeout(closeOverlay, 80);
|
||||
return;
|
||||
}
|
||||
navigate('profile-view');
|
||||
});
|
||||
|
||||
button.addEventListener('pointercancel', () => {
|
||||
clearTimer();
|
||||
pressed = false;
|
||||
closeOverlay();
|
||||
});
|
||||
|
||||
button.addEventListener('contextmenu', (event) => event.preventDefault());
|
||||
}
|
||||
|
||||
function installChannelsHoldSwitcher(button, navigate) {
|
||||
let holdTimer = 0;
|
||||
let pressed = false;
|
||||
|
||||
@ -1,76 +0,0 @@
|
||||
import { renderHeader } from '../components/header.js';
|
||||
import { beginAddAccountFlow, state, switchToAccount } from '../state.js';
|
||||
import { toUserMessage } from '../services/ui-error-texts.js';
|
||||
|
||||
export const pageMeta = { id: 'account-switcher-view', title: 'Сменить профиль' };
|
||||
|
||||
export function render({ navigate }) {
|
||||
const screen = document.createElement('section');
|
||||
screen.className = 'stack';
|
||||
|
||||
screen.append(
|
||||
renderHeader({
|
||||
title: 'Сменить профиль',
|
||||
leftAction: { label: '<', onClick: () => navigate('profile-view') },
|
||||
}),
|
||||
);
|
||||
|
||||
const list = document.createElement('div');
|
||||
list.className = 'stack';
|
||||
const status = document.createElement('div');
|
||||
status.className = 'meta-muted';
|
||||
|
||||
const accounts = Array.isArray(state.accounts) ? state.accounts : [];
|
||||
const activeLogin = String(state.session.login || '').trim().toLowerCase();
|
||||
|
||||
if (!accounts.length) {
|
||||
const empty = document.createElement('div');
|
||||
empty.className = 'card meta-muted';
|
||||
empty.textContent = 'Сохранённых аккаунтов пока нет.';
|
||||
list.append(empty);
|
||||
} else {
|
||||
accounts.forEach((account) => {
|
||||
const login = String(account?.login || '').trim();
|
||||
if (!login) return;
|
||||
const card = document.createElement('button');
|
||||
card.type = 'button';
|
||||
card.className = 'card row';
|
||||
card.style.justifyContent = 'space-between';
|
||||
card.style.alignItems = 'center';
|
||||
card.innerHTML = `
|
||||
<strong>${login}</strong>
|
||||
<span class="meta-muted">${login.toLowerCase() === activeLogin ? 'Активный' : 'Переключить'}</span>
|
||||
`;
|
||||
card.addEventListener('click', async () => {
|
||||
if (login.toLowerCase() === activeLogin) return;
|
||||
status.textContent = 'Переключаем аккаунт...';
|
||||
try {
|
||||
await switchToAccount(login);
|
||||
status.textContent = '';
|
||||
navigate('profile-view');
|
||||
} catch (error) {
|
||||
status.textContent = toUserMessage(error, 'Не удалось переключить аккаунт.');
|
||||
}
|
||||
});
|
||||
list.append(card);
|
||||
});
|
||||
}
|
||||
|
||||
const actions = document.createElement('div');
|
||||
actions.className = 'form-actions-grid';
|
||||
actions.innerHTML = `
|
||||
<button class="secondary-btn" id="account-switcher-add-login" type="button">Добавить аккаунт (Войти)</button>
|
||||
<button class="secondary-btn" id="account-switcher-add-register" type="button">Добавить аккаунт (Регистрация)</button>
|
||||
`;
|
||||
actions.querySelector('#account-switcher-add-login')?.addEventListener('click', () => {
|
||||
beginAddAccountFlow();
|
||||
navigate('login-view');
|
||||
});
|
||||
actions.querySelector('#account-switcher-add-register')?.addEventListener('click', () => {
|
||||
beginAddAccountFlow();
|
||||
navigate('register-view');
|
||||
});
|
||||
|
||||
screen.append(list, actions, status);
|
||||
return screen;
|
||||
}
|
||||
@ -142,7 +142,6 @@ export function resolveToolbarActive(pageId) {
|
||||
if (ROOT_PAGES.includes(pageId)) return pageId;
|
||||
if (
|
||||
pageId === 'profile-edit-view' ||
|
||||
pageId === 'account-switcher-view' ||
|
||||
pageId === 'wallet-view' ||
|
||||
pageId === 'settings-view' ||
|
||||
pageId === 'developer-settings-view' ||
|
||||
|
||||
@ -3,7 +3,6 @@ import { listStoredMessages, putStoredMessage } from './services/message-store.j
|
||||
|
||||
const clone = (value) => JSON.parse(JSON.stringify(value));
|
||||
const SESSION_STORAGE_KEY = 'shine-ui-current-session-v1';
|
||||
const ACCOUNTS_STORAGE_KEY = 'shine-ui-accounts-v1';
|
||||
const REACTIONS_STORAGE_KEY = 'shine-ui-message-reactions-v2';
|
||||
const WEB_PUSH_SUBSCRIPTION_KEY = 'shine-ui-webpush-subscription-v1';
|
||||
const ENTRY_SETTINGS_STORAGE_KEY = 'shine-ui-entry-settings-v1';
|
||||
@ -117,33 +116,6 @@ function loadStoredSession() {
|
||||
}
|
||||
}
|
||||
|
||||
function loadStoredAccounts() {
|
||||
try {
|
||||
const raw = localStorage.getItem(ACCOUNTS_STORAGE_KEY);
|
||||
if (!raw) return [];
|
||||
const parsed = JSON.parse(raw);
|
||||
if (!Array.isArray(parsed)) return [];
|
||||
return parsed
|
||||
.map((item) => ({
|
||||
login: String(item?.login || '').trim(),
|
||||
sessionId: String(item?.sessionId || '').trim(),
|
||||
updatedAtMs: Number(item?.updatedAtMs || Date.now()),
|
||||
}))
|
||||
.filter((item) => item.login && item.sessionId);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function persistStoredAccounts(accounts) {
|
||||
try {
|
||||
const payload = Array.isArray(accounts) ? accounts : [];
|
||||
localStorage.setItem(ACCOUNTS_STORAGE_KEY, JSON.stringify(payload));
|
||||
} catch {
|
||||
// ignore storage errors
|
||||
}
|
||||
}
|
||||
|
||||
function loadStoredReactions() {
|
||||
try {
|
||||
const raw = localStorage.getItem(REACTIONS_STORAGE_KEY);
|
||||
@ -216,7 +188,6 @@ function persistEntrySettings(settings) {
|
||||
function clearBrowserClientData() {
|
||||
const localKeys = [
|
||||
SESSION_STORAGE_KEY,
|
||||
ACCOUNTS_STORAGE_KEY,
|
||||
REACTIONS_STORAGE_KEY,
|
||||
WEB_PUSH_SUBSCRIPTION_KEY,
|
||||
CHANNEL_NOTIFY_KEY,
|
||||
@ -239,7 +210,6 @@ function clearBrowserClientData() {
|
||||
|
||||
function createInitialState({ withStoredSession = true } = {}) {
|
||||
const storedSession = withStoredSession ? loadStoredSession() : null;
|
||||
const storedAccounts = loadStoredAccounts();
|
||||
const storedReactions = loadStoredReactions();
|
||||
const storedEntrySettings = loadStoredEntrySettings();
|
||||
const initialShineServer = LOCAL_WS_OVERRIDE_URL || DEFAULT_SHINE_SERVER;
|
||||
@ -259,9 +229,6 @@ function createInitialState({ withStoredSession = true } = {}) {
|
||||
sessionId: storedSession?.sessionId || '',
|
||||
storagePwdInMemory: '',
|
||||
},
|
||||
accounts: storedAccounts,
|
||||
activeAccountLogin: String(storedSession?.login || ''),
|
||||
accountAddingMode: false,
|
||||
startHint: '',
|
||||
entrySettings: {
|
||||
language: String(storedEntrySettings?.language || 'ru'),
|
||||
@ -731,19 +698,6 @@ export function authorizeSession({
|
||||
login,
|
||||
sessionId,
|
||||
});
|
||||
const loginKey = String(login || '').trim().toLowerCase();
|
||||
const nextAccounts = [
|
||||
{
|
||||
login: String(login || '').trim(),
|
||||
sessionId: String(sessionId || '').trim(),
|
||||
updatedAtMs: Date.now(),
|
||||
},
|
||||
...state.accounts.filter((item) => String(item?.login || '').trim().toLowerCase() !== loginKey),
|
||||
];
|
||||
state.accounts = nextAccounts;
|
||||
state.activeAccountLogin = String(login || '').trim();
|
||||
state.accountAddingMode = false;
|
||||
persistStoredAccounts(nextAccounts);
|
||||
state.startHint = '';
|
||||
if (onSessionAuthorized) {
|
||||
onSessionAuthorized();
|
||||
@ -767,20 +721,6 @@ export async function refreshSessions() {
|
||||
return state.sessions;
|
||||
}
|
||||
|
||||
export async function switchToAccount(login) {
|
||||
const targetLogin = String(login || '').trim();
|
||||
if (!targetLogin) throw new Error('Не передан логин аккаунта.');
|
||||
const account = (state.accounts || []).find((item) => String(item?.login || '').trim().toLowerCase() === targetLogin.toLowerCase());
|
||||
if (!account?.sessionId) throw new Error('Сессия аккаунта не найдена.');
|
||||
const resumed = await authService.resumeSession(account.login, account.sessionId);
|
||||
authorizeSession({
|
||||
login: resumed.login || account.login,
|
||||
sessionId: resumed.sessionId || account.sessionId,
|
||||
storagePwd: resumed.storagePwd || state.session.storagePwdInMemory,
|
||||
});
|
||||
return resumed;
|
||||
}
|
||||
|
||||
function resetStateForSignedOut() {
|
||||
const next = createInitialState({ withStoredSession: false });
|
||||
state.chats = next.chats;
|
||||
@ -792,9 +732,6 @@ function resetStateForSignedOut() {
|
||||
state.notificationsTab = next.notificationsTab;
|
||||
state.pageLabelCollapsed = next.pageLabelCollapsed;
|
||||
state.session = next.session;
|
||||
state.accounts = next.accounts;
|
||||
state.activeAccountLogin = next.activeAccountLogin;
|
||||
state.accountAddingMode = next.accountAddingMode;
|
||||
state.startHint = next.startHint;
|
||||
state.entrySettings = next.entrySettings;
|
||||
state.registrationDraft = next.registrationDraft;
|
||||
@ -811,10 +748,6 @@ function resetStateForSignedOut() {
|
||||
}
|
||||
|
||||
export async function terminateCurrentSession({ infoMessage = '' } = {}) {
|
||||
const currentLogin = String(state.session.login || '').trim().toLowerCase();
|
||||
const nextAccounts = (state.accounts || []).filter((item) => String(item?.login || '').trim().toLowerCase() !== currentLogin);
|
||||
state.accounts = nextAccounts;
|
||||
persistStoredAccounts(nextAccounts);
|
||||
clearStoredSession();
|
||||
resetStateForSignedOut();
|
||||
authService.close();
|
||||
@ -849,14 +782,6 @@ export async function closeCurrentSessionAndSignOut({ infoMessage = '' } = {}) {
|
||||
await terminateCurrentSession({ infoMessage });
|
||||
}
|
||||
|
||||
export function beginAddAccountFlow() {
|
||||
state.accountAddingMode = true;
|
||||
}
|
||||
|
||||
export function cancelAddAccountFlow() {
|
||||
state.accountAddingMode = false;
|
||||
}
|
||||
|
||||
export function refreshRegistrationBalance() {
|
||||
const next = (0.005 + Math.random() * 0.03).toFixed(4);
|
||||
state.registrationPayment.balanceSOL = next;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user