feat(workspace): live synchronisation, verbindungsindikator, leere ordner persistenz und sperrkonfliktschutz
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m0s

This commit is contained in:
sandrodach committed 2026-10-26 10:18:02 +01:00
1 parent 35fd682fcd
commit 2f92e1f49a
4 files changed
+234 -14

No files matched your search

+48
View File
@@ -1019,6 +1019,8 @@ export function getOfflineQueue(): Thought[] {
let activeGlobalWS: WebSocket | null = null;
let wsReconnectTimer: any = null;
let wsListeners: ((msg: WSMessage) => void)[] = [];
let wsStatusListeners: ((connected: boolean) => void)[] = [];
let isWSConnected = false;
let manualDisconnect = false;
export function buildWebSocketURL(listId?: string): string {
@@ -1030,6 +1032,30 @@ export function buildWebSocketURL(listId?: string): string {
return `${protocol}//${window.location.host}/api/v1/ws?token=${encodeURIComponent(token)}`;
}
export function isWebSocketConnected(): boolean {
return isWSConnected && activeGlobalWS !== null && activeGlobalWS.readyState === WebSocket.OPEN;
}
export function onWebSocketStatus(callback: (connected: boolean) => void): () => void {
wsStatusListeners.push(callback);
callback(isWebSocketConnected());
return () => {
wsStatusListeners = wsStatusListeners.filter((fn) => fn !== callback);
};
}
function notifyStatusChange(connected: boolean) {
isWSConnected = connected;
wsStatusListeners.forEach((fn) => {
try {
fn(connected);
} catch (e) {
console.error('WS status listener error', e);
}
});
window.dispatchEvent(new CustomEvent('mindsift:ws:status', { detail: { connected } }));
}
export function connectWebSocket(): void {
const token = getToken();
if (!token) return;
@@ -1044,6 +1070,10 @@ export function connectWebSocket(): void {
const ws = new WebSocket(url);
activeGlobalWS = ws;
ws.onopen = () => {
notifyStatusChange(true);
};
ws.onmessage = (event) => {
try {
const msg = JSON.parse(event.data) as WSMessage;
@@ -1062,6 +1092,7 @@ export function connectWebSocket(): void {
ws.onclose = () => {
activeGlobalWS = null;
notifyStatusChange(false);
if (!manualDisconnect && getToken()) {
clearTimeout(wsReconnectTimer);
wsReconnectTimer = setTimeout(() => {
@@ -1071,15 +1102,31 @@ export function connectWebSocket(): void {
};
ws.onerror = () => {
notifyStatusChange(false);
try {
ws.close();
} catch {}
};
} catch (e) {
console.error('Failed to connect WebSocket', e);
notifyStatusChange(false);
}
}
// Ensure reconnect when browser recovers from sleep or tab visibility
if (typeof window !== 'undefined') {
window.addEventListener('online', () => {
if (getToken() && !isWebSocketConnected()) {
connectWebSocket();
}
});
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible' && getToken() && !isWebSocketConnected()) {
connectWebSocket();
}
});
}
export function disconnectWebSocket(): void {
manualDisconnect = true;
clearTimeout(wsReconnectTimer);
@@ -1089,6 +1136,7 @@ export function disconnectWebSocket(): void {
} catch {}
activeGlobalWS = null;
}
notifyStatusChange(false);
}
export function sendWebSocketMessage(msg: any): void {