feat: finalize channels fixes and runtime stability

This commit is contained in:
DrygMira
2026-04-13 23:00:36 +03:00
parent 0c7d8fac02
commit a9c69e5947
46 changed files with 4654 additions and 356 deletions
+103 -3
View File
@@ -4,6 +4,7 @@ import { clearClientAuthData } from './services/key-vault.js';
const clone = (value) => JSON.parse(JSON.stringify(value));
const SESSION_STORAGE_KEY = 'shine-ui-current-session-v1';
const REACTIONS_STORAGE_KEY = 'shine-ui-message-reactions-v2';
const INVALID_SESSION_CODES = new Set([
'NOT_AUTHENTICATED',
'SESSION_NOT_FOUND',
@@ -13,18 +14,63 @@ const INVALID_SESSION_CODES = new Set([
function readLocalWsOverrideUrl() {
try {
const value = new URLSearchParams(window.location.search).get('localWsPort');
const params = new URLSearchParams(window.location.search);
const explicitWsUrl = String(params.get('wsUrl') || '').trim();
if (explicitWsUrl) {
if (explicitWsUrl.startsWith('ws://') || explicitWsUrl.startsWith('wss://')) {
try {
const parsed = new URL(explicitWsUrl);
if (!parsed.pathname || parsed.pathname === '/') parsed.pathname = '/ws';
return parsed.toString();
} catch {
return explicitWsUrl;
}
}
if (explicitWsUrl.startsWith('http://') || explicitWsUrl.startsWith('https://')) {
try {
const parsed = new URL(explicitWsUrl);
parsed.protocol = parsed.protocol === 'https:' ? 'wss:' : 'ws:';
if (!parsed.pathname || parsed.pathname === '/') parsed.pathname = '/ws';
return parsed.toString();
} catch {
return `${explicitWsUrl.replace(/^http/, 'ws').replace(/\/$/, '')}/ws`;
}
}
return '';
}
const value = params.get('localWsPort');
const asNum = Number(value);
if (!Number.isFinite(asNum)) return '';
const port = Math.trunc(asNum);
if (port <= 0 || port > 65535) return '';
return `ws://localhost:${port}/ws`;
const isHttpsPage = window.location.protocol === 'https:';
const forceInsecureLocal = params.get('allowInsecureLocalWs') === '1';
const scheme = (isHttpsPage && !forceInsecureLocal) ? 'wss' : 'ws';
return `${scheme}://localhost:${port}/ws`;
} catch {
return '';
}
}
const LOCAL_WS_OVERRIDE_URL = readLocalWsOverrideUrl();
function inferTunnelWsUrl() {
try {
const host = String(window.location.host || '').toLowerCase();
const isTunnelHost = (
host.endsWith('.ngrok-free.dev') ||
host.endsWith('.ngrok.io') ||
host.endsWith('.trycloudflare.com')
);
if (!isTunnelHost) return '';
const scheme = window.location.protocol === 'https:' ? 'wss' : 'ws';
return `${scheme}://${window.location.host}/ws`;
} catch {
return '';
}
}
const LOCAL_WS_OVERRIDE_URL = readLocalWsOverrideUrl() || inferTunnelWsUrl();
const DEFAULT_SHINE_SERVER = 'wss://shineup.me/ws';
function loadStoredSession() {
@@ -37,6 +83,26 @@ function loadStoredSession() {
}
}
function loadStoredReactions() {
try {
const raw = localStorage.getItem(REACTIONS_STORAGE_KEY);
if (!raw) return {};
const parsed = JSON.parse(raw);
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return {};
return parsed;
} catch {
return {};
}
}
function persistStoredReactions(reactions) {
try {
localStorage.setItem(REACTIONS_STORAGE_KEY, JSON.stringify(reactions || {}));
} catch {
// ignore storage errors
}
}
function persistSession(session) {
try {
localStorage.setItem(SESSION_STORAGE_KEY, JSON.stringify(session));
@@ -55,6 +121,7 @@ function clearStoredSession() {
function createInitialState({ withStoredSession = true } = {}) {
const storedSession = withStoredSession ? loadStoredSession() : null;
const storedReactions = loadStoredReactions();
const initialShineServer = LOCAL_WS_OVERRIDE_URL || DEFAULT_SHINE_SERVER;
return {
@@ -120,6 +187,7 @@ function createInitialState({ withStoredSession = true } = {}) {
channelsFeed: null,
channelsIndex: {},
localChannelPosts: {},
messageReactions: storedReactions,
};
}
@@ -247,6 +315,10 @@ function resetStateForSignedOut() {
state.deviceConnect = next.deviceConnect;
state.authUi = next.authUi;
state.sessions = next.sessions;
state.channelsFeed = next.channelsFeed;
state.channelsIndex = next.channelsIndex;
state.localChannelPosts = next.localChannelPosts;
state.messageReactions = next.messageReactions;
}
export async function terminateCurrentSession({ infoMessage = '' } = {}) {
@@ -295,3 +367,31 @@ export function addLocalChannelPost(channelId, post) {
body: text,
});
}
function makeMessageReactionKey(messageRef, login = state.session.login) {
const bch = String(messageRef?.blockchainName || '').trim();
const blockNumber = Number(messageRef?.blockNumber);
const blockHash = String(messageRef?.blockHash || '').trim().toLowerCase();
const cleanLogin = String(login || '').trim().toLowerCase();
if (!cleanLogin || !bch || !Number.isFinite(blockNumber) || blockNumber < 0 || !blockHash) return '';
return `${cleanLogin}|${bch}|${blockNumber}|${blockHash}`;
}
export function getMessageReactionState(messageRef) {
const key = makeMessageReactionKey(messageRef);
if (!key) return '';
return state.messageReactions[key] || '';
}
export function setMessageReactionState(messageRef, nextState) {
const key = makeMessageReactionKey(messageRef);
if (!key) return;
const normalized = String(nextState || '').trim().toLowerCase();
if (normalized === 'liked' || normalized === 'unliked') {
state.messageReactions[key] = normalized;
persistStoredReactions(state.messageReactions);
return;
}
delete state.messageReactions[key];
persistStoredReactions(state.messageReactions);
}