fix(desktop): window lifecycle for instance switch (#5381)

This resolves window accumulation during instance switching by
implementing proper window lifecycle management using Tauri's
WebviewWindow APIs.

Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com>
This commit is contained in:
Shreyas 2025-09-22 14:55:53 +05:30 committed by GitHub
parent a23a2c657d
commit ba700886b5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 56 additions and 19 deletions

View file

@ -40,7 +40,7 @@
"@hoppscotch/httpsnippet": "3.0.9", "@hoppscotch/httpsnippet": "3.0.9",
"@hoppscotch/js-sandbox": "workspace:^", "@hoppscotch/js-sandbox": "workspace:^",
"@hoppscotch/kernel": "workspace:^", "@hoppscotch/kernel": "workspace:^",
"@hoppscotch/plugin-appload": "github:CuriousCorrelation/tauri-plugin-appload#e8dbe06eabf947e5efaf07d2e573238ceb11a7b1", "@hoppscotch/plugin-appload": "github:CuriousCorrelation/tauri-plugin-appload#e05861959938b57479a1a81fa796735ebbd08c7c",
"@hoppscotch/ui": "0.2.5", "@hoppscotch/ui": "0.2.5",
"@hoppscotch/vue-toasted": "0.1.0", "@hoppscotch/vue-toasted": "0.1.0",
"@lezer/highlight": "1.2.1", "@lezer/highlight": "1.2.1",

View file

@ -2,7 +2,17 @@ import { Service } from "dioc"
import { BehaviorSubject, Observable } from "rxjs" import { BehaviorSubject, Observable } from "rxjs"
import { computed } from "vue" import { computed } from "vue"
import { LazyStore } from "@tauri-apps/plugin-store" import { LazyStore } from "@tauri-apps/plugin-store"
import { download, load, clear, remove } from "@hoppscotch/plugin-appload" import {
getCurrentWebviewWindow,
getAllWebviewWindows,
} from "@tauri-apps/api/webviewWindow"
import {
download,
load,
clear,
remove,
close,
} from "@hoppscotch/plugin-appload"
import { useToast } from "~/composables/toast" import { useToast } from "~/composables/toast"
import { platform } from "~/platform" import { platform } from "~/platform"
@ -156,6 +166,11 @@ export class InstanceSwitcherService extends Service<ConnectionState> {
this.getVendoredInstance().displayName this.getVendoredInstance().displayName
) )
) )
// Close current window AFTER successful load
// NOTE: No need to await it.
this.closeCurrentWindow()
return true return true
} catch (error) { } catch (error) {
const errorMessage = const errorMessage =
@ -234,6 +249,10 @@ export class InstanceSwitcherService extends Service<ConnectionState> {
throw new Error("Failed to load bundle") throw new Error("Failed to load bundle")
} }
// Close current window AFTER successful load
// NOTE: No need to await it.
this.closeCurrentWindow()
this.toast.success(`Connected to ${displayName}`) this.toast.success(`Connected to ${displayName}`)
return true return true
} catch (error) { } catch (error) {
@ -251,6 +270,33 @@ export class InstanceSwitcherService extends Service<ConnectionState> {
} }
} }
/**
* Closes the current window using Tauri's window management
*/
private async closeCurrentWindow(): Promise<void> {
try {
const currentWindow = getCurrentWebviewWindow()
const currentLabel = currentWindow.label
// Don't close if we're the main window or if there are no other windows
if (currentLabel === "main") {
const allWindows = await getAllWebviewWindows()
if (allWindows.length <= 1) {
// Don't close the last window
return
}
}
const closeResponse = await close({ windowLabel: currentLabel })
if (!closeResponse.success) {
console.warn(`Failed to close window ${currentLabel}`)
}
} catch (error) {
console.warn("Failed to close current window:", error)
// Don't throw - window closing shouldn't block the operation
}
}
public async removeInstance(serverUrl: string): Promise<boolean> { public async removeInstance(serverUrl: string): Promise<boolean> {
try { try {
const normalizedUrl = this.normalizeUrl(serverUrl) const normalizedUrl = this.normalizeUrl(serverUrl)
@ -285,8 +331,9 @@ export class InstanceSwitcherService extends Service<ConnectionState> {
const displayName = this.getDisplayNameFromUrl(serverUrl) const displayName = this.getDisplayNameFromUrl(serverUrl)
this.toast.success(`Removed ${displayName}`) this.toast.success(`Removed ${displayName}`)
// If we're currently connected to this instance, go back to idle state // If we're currently connected to this instance, close the window and go to idle state
if (this.isCurrentlyConnectedTo(serverUrl)) { if (this.isCurrentlyConnectedTo(serverUrl)) {
await this.closeCurrentWindow()
this.state$.next({ status: "idle" }) this.state$.next({ status: "idle" })
this.emit(this.state$.value) this.emit(this.state$.value)
await this.saveCurrentState() await this.saveCurrentState()
@ -301,6 +348,7 @@ export class InstanceSwitcherService extends Service<ConnectionState> {
public async clearCache(): Promise<boolean> { public async clearCache(): Promise<boolean> {
try { try {
await this.closeCurrentWindow()
await clear() await clear()
this.toast.success("Cache cleared successfully") this.toast.success("Cache cleared successfully")
return true return true
@ -367,19 +415,16 @@ export class InstanceSwitcherService extends Service<ConnectionState> {
// If it fails, fall back to the original URL with default port // If it fails, fall back to the original URL with default port
try { try {
const re = withSubpath.toString().replace(/\/$/, "") return withSubpath.toString().replace(/\/$/, "")
return re
} catch { } catch {
if (!urlObj.port) { if (!urlObj.port) {
urlObj.port = "3200" urlObj.port = "3200"
} }
const re = urlObj.toString().replace(/\/$/, "") return urlObj.toString().replace(/\/$/, "")
return re
} }
} }
const re = urlObj.toString().replace(/\/$/, "") return urlObj.toString().replace(/\/$/, "")
return re
} catch (error) { } catch (error) {
return url return url
} }

View file

@ -508,8 +508,8 @@ importers:
specifier: workspace:^ specifier: workspace:^
version: link:../hoppscotch-kernel version: link:../hoppscotch-kernel
'@hoppscotch/plugin-appload': '@hoppscotch/plugin-appload':
specifier: github:CuriousCorrelation/tauri-plugin-appload#e8dbe06eabf947e5efaf07d2e573238ceb11a7b1 specifier: github:CuriousCorrelation/tauri-plugin-appload#e05861959938b57479a1a81fa796735ebbd08c7c
version: '@CuriousCorrelation/plugin-appload@https://codeload.github.com/CuriousCorrelation/tauri-plugin-appload/tar.gz/e8dbe06eabf947e5efaf07d2e573238ceb11a7b1' version: '@CuriousCorrelation/plugin-appload@https://codeload.github.com/CuriousCorrelation/tauri-plugin-appload/tar.gz/e05861959938b57479a1a81fa796735ebbd08c7c'
'@hoppscotch/ui': '@hoppscotch/ui':
specifier: 0.2.5 specifier: 0.2.5
version: 0.2.5(eslint@8.57.0)(terser@5.39.2)(typescript@5.9.2)(vite@6.3.5(@types/node@24.3.0)(jiti@2.5.1)(sass@1.91.0)(terser@5.39.2)(yaml@2.8.1))(vue@3.5.20(typescript@5.9.2)) version: 0.2.5(eslint@8.57.0)(terser@5.39.2)(typescript@5.9.2)(vite@6.3.5(@types/node@24.3.0)(jiti@2.5.1)(sass@1.91.0)(terser@5.39.2)(yaml@2.8.1))(vue@3.5.20(typescript@5.9.2))
@ -1853,10 +1853,6 @@ packages:
resolution: {tarball: https://codeload.github.com/CuriousCorrelation/tauri-plugin-appload/tar.gz/e05861959938b57479a1a81fa796735ebbd08c7c} resolution: {tarball: https://codeload.github.com/CuriousCorrelation/tauri-plugin-appload/tar.gz/e05861959938b57479a1a81fa796735ebbd08c7c}
version: 0.1.0 version: 0.1.0
'@CuriousCorrelation/plugin-appload@https://codeload.github.com/CuriousCorrelation/tauri-plugin-appload/tar.gz/e8dbe06eabf947e5efaf07d2e573238ceb11a7b1':
resolution: {tarball: https://codeload.github.com/CuriousCorrelation/tauri-plugin-appload/tar.gz/e8dbe06eabf947e5efaf07d2e573238ceb11a7b1}
version: 0.1.0
'@CuriousCorrelation/plugin-relay@https://codeload.github.com/CuriousCorrelation/tauri-plugin-relay/tar.gz/ff18f776ddeb53dbbdeaf97e1fabc30bdc57c158': '@CuriousCorrelation/plugin-relay@https://codeload.github.com/CuriousCorrelation/tauri-plugin-relay/tar.gz/ff18f776ddeb53dbbdeaf97e1fabc30bdc57c158':
resolution: {tarball: https://codeload.github.com/CuriousCorrelation/tauri-plugin-relay/tar.gz/ff18f776ddeb53dbbdeaf97e1fabc30bdc57c158} resolution: {tarball: https://codeload.github.com/CuriousCorrelation/tauri-plugin-relay/tar.gz/ff18f776ddeb53dbbdeaf97e1fabc30bdc57c158}
version: 0.1.0 version: 0.1.0
@ -15542,10 +15538,6 @@ snapshots:
dependencies: dependencies:
'@tauri-apps/api': 2.1.1 '@tauri-apps/api': 2.1.1
'@CuriousCorrelation/plugin-appload@https://codeload.github.com/CuriousCorrelation/tauri-plugin-appload/tar.gz/e8dbe06eabf947e5efaf07d2e573238ceb11a7b1':
dependencies:
'@tauri-apps/api': 2.1.1
'@CuriousCorrelation/plugin-relay@https://codeload.github.com/CuriousCorrelation/tauri-plugin-relay/tar.gz/ff18f776ddeb53dbbdeaf97e1fabc30bdc57c158': '@CuriousCorrelation/plugin-relay@https://codeload.github.com/CuriousCorrelation/tauri-plugin-relay/tar.gz/ff18f776ddeb53dbbdeaf97e1fabc30bdc57c158':
dependencies: dependencies:
'@tauri-apps/api': 2.1.1 '@tauri-apps/api': 2.1.1