merge: MEM-1/2/3 event listener leak cleanup (resolved session.store.ts conflict)

This commit is contained in:
Vantz Stockwell 2026-03-29 16:41:08 -04:00
commit 625a4500bc
3 changed files with 39 additions and 16 deletions

View File

@ -88,7 +88,7 @@
</template>
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { ref, onMounted, onBeforeUnmount } from "vue";
import { invoke } from "@tauri-apps/api/core";
import { useSessionStore, type Session } from "@/stores/session.store";
import { useConnectionStore } from "@/stores/connection.store";
@ -151,16 +151,10 @@ function closeMenuTab(): void {
if (session) sessionStore.closeSession(session.id);
}
// Listen for reattach events from detached windows
import { listen } from "@tauri-apps/api/event";
listen<{ sessionId: string; name: string; protocol: string }>("session:reattach", (event) => {
const { sessionId } = event.payload;
const session = sessionStore.sessions.find(s => s.id === sessionId);
if (session) {
session.active = true;
sessionStore.activateSession(sessionId);
}
});
import type { UnlistenFn } from "@tauri-apps/api/event";
let unlistenReattach: UnlistenFn | null = null;
onMounted(async () => {
try {
@ -168,6 +162,19 @@ onMounted(async () => {
} catch {
availableShells.value = [];
}
unlistenReattach = await listen<{ sessionId: string; name: string; protocol: string }>("session:reattach", (event) => {
const { sessionId } = event.payload;
const session = sessionStore.sessions.find(s => s.id === sessionId);
if (session) {
session.active = true;
sessionStore.activateSession(sessionId);
}
});
});
onBeforeUnmount(() => {
unlistenReattach?.();
});
// Drag-and-drop tab reordering

View File

@ -195,6 +195,7 @@ export function useRdp(): UseRdpReturn {
const clipboardSync = ref(false);
let animFrameId: number | null = null;
let unlistenFrame: (() => void) | null = null;
/**
* Fetch the current frame from the Rust RDP backend.
@ -315,8 +316,7 @@ export function useRdp(): UseRdpReturn {
listen(`rdp:frame:${sessionId}`, () => {
onFrameReady();
}).then((unlisten) => {
// Store unlisten so we can clean up
(canvas as any).__wraith_unlisten = unlisten;
unlistenFrame = unlisten;
});
});
@ -332,6 +332,10 @@ export function useRdp(): UseRdpReturn {
cancelAnimationFrame(animFrameId);
animFrameId = null;
}
if (unlistenFrame !== null) {
unlistenFrame();
unlistenFrame = null;
}
connected.value = false;
}

View File

@ -2,6 +2,7 @@ import { defineStore } from "pinia";
import { ref, computed } from "vue";
import { invoke } from "@tauri-apps/api/core";
import { listen } from "@tauri-apps/api/event";
import type { UnlistenFn } from "@tauri-apps/api/event";
import { useConnectionStore } from "@/stores/connection.store";
import type { ThemeDefinition } from "@/components/common/ThemePicker.vue";
@ -39,10 +40,14 @@ export const useSessionStore = defineStore("session", () => {
const sessionCount = computed(() => sessions.value.length);
const sessionUnlisteners = new Map<string, Array<UnlistenFn>>();
// Listen for backend close/exit events to update session status
function setupStatusListeners(sessionId: string): void {
listen(`ssh:close:${sessionId}`, () => markDisconnected(sessionId));
listen(`ssh:exit:${sessionId}`, () => markDisconnected(sessionId));
async function setupStatusListeners(sessionId: string): Promise<void> {
const unlisteners: UnlistenFn[] = [];
unlisteners.push(await listen(`ssh:close:${sessionId}`, () => markDisconnected(sessionId)));
unlisteners.push(await listen(`ssh:exit:${sessionId}`, () => markDisconnected(sessionId)));
sessionUnlisteners.set(sessionId, unlisteners);
}
function markDisconnected(sessionId: string): void {
@ -92,6 +97,12 @@ export const useSessionStore = defineStore("session", () => {
console.error("Failed to disconnect session:", err);
}
const unlisteners = sessionUnlisteners.get(id);
if (unlisteners) {
unlisteners.forEach((fn) => fn());
sessionUnlisteners.delete(id);
}
sessions.value.splice(idx, 1);
if (activeSessionId.value === id) {
@ -325,7 +336,8 @@ export const useSessionStore = defineStore("session", () => {
});
// Listen for PTY close
listen(`pty:close:${sessionId}`, () => markDisconnected(sessionId));
const unlistenPty = await listen(`pty:close:${sessionId}`, () => markDisconnected(sessionId));
sessionUnlisteners.set(sessionId, [unlistenPty]);
activeSessionId.value = sessionId;
} catch (err: unknown) {