Skip to content

Commit 3f15762

Browse files
feat: improve foreground detection and message decryption in ThreadPanel
- Enhanced foreground detection to support re-sending state after WebSocket authentication. - Updated setupForegroundDetection to return a handle with cleanup and resend functions. - Integrated message decryption in ThreadPanel for encrypted messages, ensuring proper handling based on encryption status. - Improved logging for message loading and decryption errors.
1 parent cb899ba commit 3f15762

3 files changed

Lines changed: 42 additions & 9 deletions

File tree

packages/web/src/App.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,8 @@ export default function App() {
886886

887887
const sessionId = randomUUID();
888888
dlog.info("WS", `Connecting WebSocket (session=${sessionId.slice(0, 8)}...)`);
889+
let fgResend: (() => void) | null = null;
890+
889891
const client = new BotsChatWSClient({
890892
userId: state.user.id,
891893
sessionId,
@@ -896,6 +898,10 @@ export default function App() {
896898
onStatusChange: (connected) => {
897899
dlog.info("WS", connected ? "WebSocket connected" : "WebSocket disconnected");
898900
dispatch({ type: "SET_WS_CONNECTED", connected });
901+
// After auth succeeds, re-send foreground state so the DO knows about us.
902+
// The initial send in setupForegroundDetection fires before the socket
903+
// is open, so this is the first one that actually reaches the server.
904+
if (connected) fgResend?.();
899905
},
900906
});
901907

@@ -911,14 +917,15 @@ export default function App() {
911917
.catch((err) => {
912918
dlog.warn("Push", `Push init failed: ${err}`);
913919
});
914-
const cleanupForeground = setupForegroundDetection({
920+
const fg = setupForegroundDetection({
915921
wsClient: client,
916922
getActiveSessionKey: () => stateRef.current.selectedSessionKey,
917923
onResume: () => setForegroundResumeCount((c) => c + 1),
918924
});
925+
fgResend = fg.resend;
919926

920927
return () => {
921-
cleanupForeground();
928+
fg.cleanup();
922929
client.disconnect();
923930
wsClientRef.current = null;
924931
};

packages/web/src/components/ThreadPanel.tsx

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useAppState, useAppDispatch, type ChatMessage } from "../store";
33
import { messagesApi } from "../api";
44
import type { WSMessage } from "../ws";
55
import { MessageContent } from "./MessageContent";
6+
import { E2eService } from "../e2e";
67
import { dlog } from "../debug-log";
78
import { randomUUID } from "../utils/uuid";
89
import { formatMessageTime, formatFullDateTime } from "../utils/time";
@@ -33,10 +34,24 @@ export function ThreadPanel({ sendMessage }: ThreadPanelProps) {
3334
dlog.info("Thread", `Loading history for thread ${state.activeThreadId}`);
3435
messagesApi
3536
.list(state.user.id, threadSessionKey, state.activeThreadId)
36-
.then(({ messages }) => {
37+
.then(async ({ messages }) => {
3738
dlog.info("Thread", `Loaded ${messages.length} thread messages`);
3839
if (messages.length > 0) {
39-
dispatch({ type: "OPEN_THREAD", threadId: state.activeThreadId!, messages });
40+
const decrypted = await Promise.all(messages.map(async (m) => {
41+
if (m.encrypted && E2eService.hasKey()) {
42+
try {
43+
const plaintext = await E2eService.decrypt(m.text, m.id);
44+
return { ...m, text: plaintext, isEncryptedLocked: false };
45+
} catch (err) {
46+
dlog.warn("Thread", `Failed to decrypt message ${m.id}`, err);
47+
return { ...m, isEncryptedLocked: true };
48+
}
49+
} else if (m.encrypted) {
50+
return { ...m, isEncryptedLocked: true };
51+
}
52+
return m;
53+
}));
54+
dispatch({ type: "OPEN_THREAD", threadId: state.activeThreadId!, messages: decrypted });
4055
}
4156
})
4257
.catch((err) => {

packages/web/src/foreground.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ export interface ForegroundOptions {
1515
onResume?: () => void;
1616
}
1717

18+
export interface ForegroundHandle {
19+
cleanup: () => void;
20+
/** Re-send the current foreground/background state. Call after WS auth succeeds. */
21+
resend: () => void;
22+
}
23+
1824
/**
1925
* Send a focus.update message when the user switches channels/sessions
2026
* while already in the foreground.
@@ -27,7 +33,7 @@ export function sendFocusUpdate(
2733
dlog.info("Foreground", `Focus updated: ${sessionKey ?? "(none)"}`);
2834
}
2935

30-
export function setupForegroundDetection(opts: ForegroundOptions): () => void {
36+
export function setupForegroundDetection(opts: ForegroundOptions): ForegroundHandle {
3137
const { wsClient, getActiveSessionKey, onResume } = opts;
3238

3339
const notifyForeground = () => {
@@ -43,19 +49,23 @@ export function setupForegroundDetection(opts: ForegroundOptions): () => void {
4349

4450
if (Capacitor.isNativePlatform()) {
4551
let cleanup: (() => void) | null = null;
52+
let nativeIsActive = true;
4653

4754
import("@capacitor/app").then(({ App }) => {
4855
const handle = App.addListener("appStateChange", ({ isActive }) => {
56+
nativeIsActive = isActive;
4957
if (isActive) notifyForeground();
5058
else notifyBackground();
5159
});
5260
cleanup = () => handle.then((h) => h.remove());
5361
});
5462

55-
// Report initial foreground state once WS is connected
5663
notifyForeground();
5764

58-
return () => cleanup?.();
65+
return {
66+
cleanup: () => cleanup?.(),
67+
resend: () => { if (nativeIsActive) notifyForeground(); else notifyBackground(); },
68+
};
5969
}
6070

6171
// Web: Use Page Visibility API
@@ -68,7 +78,8 @@ export function setupForegroundDetection(opts: ForegroundOptions): () => void {
6878

6979
if (!document.hidden) notifyForeground();
7080

71-
return () => {
72-
document.removeEventListener("visibilitychange", handleVisibilityChange);
81+
return {
82+
cleanup: () => document.removeEventListener("visibilitychange", handleVisibilityChange),
83+
resend: () => { if (!document.hidden) notifyForeground(); else notifyBackground(); },
7384
};
7485
}

0 commit comments

Comments
 (0)