SHA256
14-04-2026
Промежуточный комит версии в которой ну хоть какието тестовые уведомления приходят. Но пока ещё вебпуш не работает
This commit is contained in:
+36
-14
@@ -8,6 +8,7 @@ import {
|
||||
authorizeSession,
|
||||
isSessionInvalidError,
|
||||
refreshSessions,
|
||||
setSessionAuthorizedHandler,
|
||||
setSessionResetHandler,
|
||||
state,
|
||||
terminateCurrentSession,
|
||||
@@ -88,6 +89,8 @@ const screenEl = document.getElementById('app-screen');
|
||||
const toolbarEl = document.getElementById('toolbar-slot');
|
||||
|
||||
let currentCleanup = null;
|
||||
let pingIntervalId = null;
|
||||
let sessionRuntimeStarted = false;
|
||||
|
||||
setClientErrorTransport((payload) => authService.reportClientError(payload));
|
||||
|
||||
@@ -273,6 +276,29 @@ async function tryAutoLogin() {
|
||||
}
|
||||
}
|
||||
|
||||
async function ensureSessionRuntimeStarted() {
|
||||
if (!state.session.isAuthorized || sessionRuntimeStarted) return;
|
||||
sessionRuntimeStarted = true;
|
||||
|
||||
await initPwaPush({
|
||||
authService,
|
||||
onLog: (entry) => addAppLogEntry(entry),
|
||||
});
|
||||
|
||||
if (pingIntervalId) {
|
||||
window.clearInterval(pingIntervalId);
|
||||
pingIntervalId = null;
|
||||
}
|
||||
pingIntervalId = window.setInterval(async () => {
|
||||
if (!state.session.isAuthorized) return;
|
||||
try {
|
||||
await authService.ws.request('Ping', { timeMs: Date.now() });
|
||||
} catch {
|
||||
// silent keep-alive
|
||||
}
|
||||
}, 60_000);
|
||||
}
|
||||
|
||||
async function init() {
|
||||
addAppLogEntry({
|
||||
level: 'info',
|
||||
@@ -281,9 +307,18 @@ async function init() {
|
||||
});
|
||||
|
||||
setSessionResetHandler(() => {
|
||||
sessionRuntimeStarted = false;
|
||||
if (pingIntervalId) {
|
||||
window.clearInterval(pingIntervalId);
|
||||
pingIntervalId = null;
|
||||
}
|
||||
navigate('start-view');
|
||||
});
|
||||
|
||||
setSessionAuthorizedHandler(() => {
|
||||
void ensureSessionRuntimeStarted();
|
||||
});
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.addEventListener('message', (event) => {
|
||||
const data = event?.data || {};
|
||||
@@ -347,20 +382,7 @@ async function init() {
|
||||
}
|
||||
});
|
||||
await tryAutoLogin();
|
||||
if (state.session.isAuthorized) {
|
||||
await initPwaPush({
|
||||
authService,
|
||||
onLog: (entry) => addAppLogEntry(entry),
|
||||
});
|
||||
window.setInterval(async () => {
|
||||
if (!state.session.isAuthorized) return;
|
||||
try {
|
||||
await authService.ws.request('Ping', { timeMs: Date.now() });
|
||||
} catch {
|
||||
// silent keep-alive
|
||||
}
|
||||
}, 60_000);
|
||||
}
|
||||
await ensureSessionRuntimeStarted();
|
||||
|
||||
if (!window.location.hash) {
|
||||
navigate(state.session.isAuthorized ? 'profile-view' : 'start-view');
|
||||
|
||||
@@ -67,6 +67,7 @@ export function render({ navigate, route }) {
|
||||
|
||||
try {
|
||||
await authService.sendDirectMessage({
|
||||
login: state.session.login,
|
||||
toLogin: chatId,
|
||||
text,
|
||||
storagePwd: state.session.storagePwdInMemory,
|
||||
|
||||
@@ -1115,14 +1115,14 @@ export class AuthService {
|
||||
return response.payload || {};
|
||||
}
|
||||
|
||||
async sendDirectMessage({ toLogin, text, storagePwd, targetSessionId = null, messageType = 1 }) {
|
||||
async sendDirectMessage({ login, toLogin, text, storagePwd, targetSessionId = null, messageType = 1 }) {
|
||||
const cleanFromLogin = String(login || '').trim();
|
||||
const cleanToLogin = String(toLogin || '').trim();
|
||||
const cleanText = String(text || '');
|
||||
if (!cleanToLogin || !cleanText) throw new Error('Не передан toLogin/text');
|
||||
if (!cleanFromLogin || !cleanToLogin || !cleanText) throw new Error('Не передан login/toLogin/text');
|
||||
if (!storagePwd) throw new Error('Не передан storagePwd для подписи');
|
||||
if (!this.ws.login) throw new Error('Нет активной авторизованной сессии');
|
||||
|
||||
const secrets = await loadEncryptedUserSecrets(this.ws.login, storagePwd);
|
||||
const secrets = await loadEncryptedUserSecrets(cleanFromLogin, storagePwd);
|
||||
const devicePriv = secrets?.deviceKey;
|
||||
if (!devicePriv) throw new Error('Не найден приватный deviceKey');
|
||||
const privateKey = await importPkcs8Ed25519(devicePriv);
|
||||
@@ -1130,7 +1130,7 @@ export class AuthService {
|
||||
const prefix = utf8Bytes('SHiNE_msg');
|
||||
const version = uint8Bytes(1);
|
||||
const toBytes = utf8Bytes(cleanToLogin);
|
||||
const fromBytes = utf8Bytes(this.ws.login);
|
||||
const fromBytes = utf8Bytes(cleanFromLogin);
|
||||
if (toBytes.length < 1 || toBytes.length > 30) throw new Error('toLogin должен быть 1..30 ASCII-символов');
|
||||
if (fromBytes.length < 1 || fromBytes.length > 30) throw new Error('fromLogin должен быть 1..30 ASCII-символов');
|
||||
if (cleanText.length > 3000) throw new Error('Слишком длинное сообщение');
|
||||
|
||||
@@ -76,15 +76,20 @@ export async function initPwaPush({ authService, onLog = null }) {
|
||||
|
||||
const serialized = JSON.stringify(sub);
|
||||
const prevSerialized = localStorage.getItem(LS_KEY);
|
||||
if (prevSerialized === serialized) {
|
||||
if (prevSerialized !== serialized) {
|
||||
localStorage.setItem(LS_KEY, serialized);
|
||||
log({
|
||||
level: 'info',
|
||||
source: 'web-push',
|
||||
message: 'Push-подписка не изменилась, отправка на сервер не требуется',
|
||||
message: 'Push-подписка изменилась относительно прошлого запуска',
|
||||
});
|
||||
} else {
|
||||
log({
|
||||
level: 'info',
|
||||
source: 'web-push',
|
||||
message: 'Push-подписка не изменилась, но в MVP включена принудительная отправка на сервер',
|
||||
});
|
||||
return;
|
||||
}
|
||||
localStorage.setItem(LS_KEY, serialized);
|
||||
|
||||
const json = sub.toJSON();
|
||||
const endpoint = json.endpoint || '';
|
||||
|
||||
+13
-1
@@ -197,6 +197,7 @@ export const state = createInitialState();
|
||||
|
||||
export const authService = new AuthService(state.entrySettings.shineServer);
|
||||
let onSessionReset = null;
|
||||
let onSessionAuthorized = null;
|
||||
|
||||
export function getChatMessages(chatId) {
|
||||
if (!state.chats[chatId]) {
|
||||
@@ -321,7 +322,11 @@ export function clearAuthMessages() {
|
||||
state.authUi.info = '';
|
||||
}
|
||||
|
||||
export function authorizeSession({ login, sessionId, storagePwd }) {
|
||||
export function authorizeSession({
|
||||
login = state.session.login,
|
||||
sessionId = state.session.sessionId,
|
||||
storagePwd = state.session.storagePwdInMemory,
|
||||
} = {}) {
|
||||
state.session.isAuthorized = true;
|
||||
state.session.login = login;
|
||||
state.session.sessionId = sessionId;
|
||||
@@ -332,12 +337,19 @@ export function authorizeSession({ login, sessionId, storagePwd }) {
|
||||
sessionId,
|
||||
});
|
||||
state.startHint = '';
|
||||
if (onSessionAuthorized) {
|
||||
onSessionAuthorized();
|
||||
}
|
||||
}
|
||||
|
||||
export function setSessionResetHandler(handler) {
|
||||
onSessionReset = typeof handler === 'function' ? handler : null;
|
||||
}
|
||||
|
||||
export function setSessionAuthorizedHandler(handler) {
|
||||
onSessionAuthorized = typeof handler === 'function' ? handler : null;
|
||||
}
|
||||
|
||||
export function isSessionInvalidError(error) {
|
||||
return INVALID_SESSION_CODES.has(error?.code);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user