fix: memory leaks — cleanup timers on unmount, tighten speech queue, fix isOwner reactivity
- FightViewer: track deferred speechSynthesis.cancel() timer, clear on unmount - BotProfilePage: change isOwner from ref to computed for reactive login state - sounds.ts: tighten speech queue depth cap from 3 to 2 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
c98007e95c
commit
b2d1a2da02
@@ -46,6 +46,7 @@ const canvasContainer = ref<HTMLElement>()
|
|||||||
const logEl = ref<HTMLElement>()
|
const logEl = ref<HTMLElement>()
|
||||||
let scene: FightSceneController | null = null
|
let scene: FightSceneController | null = null
|
||||||
const sceneReady = ref(false)
|
const sceneReady = ref(false)
|
||||||
|
let cleanupTimerHandle: ReturnType<typeof setTimeout> | null = null
|
||||||
|
|
||||||
const isReplaying = ref(false)
|
const isReplaying = ref(false)
|
||||||
const displayHpA = ref(100)
|
const displayHpA = ref(100)
|
||||||
@@ -98,6 +99,8 @@ onUnmounted(() => {
|
|||||||
stopAllAudio()
|
stopAllAudio()
|
||||||
sceneReady.value = false
|
sceneReady.value = false
|
||||||
if (scene) { scene.destroy(); scene = null }
|
if (scene) { scene.destroy(); scene = null }
|
||||||
|
if (cleanupTimerHandle) { clearTimeout(cleanupTimerHandle); cleanupTimerHandle = null }
|
||||||
|
if (typeof speechSynthesis !== 'undefined') speechSynthesis.cancel()
|
||||||
})
|
})
|
||||||
|
|
||||||
async function initScene() {
|
async function initScene() {
|
||||||
@@ -398,8 +401,10 @@ async function replay() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Kill any remaining queued speech — fight is over
|
// Kill any remaining queued speech — fight is over
|
||||||
setTimeout(() => {
|
if (cleanupTimerHandle) clearTimeout(cleanupTimerHandle)
|
||||||
|
cleanupTimerHandle = setTimeout(() => {
|
||||||
if (typeof speechSynthesis !== 'undefined') speechSynthesis.cancel()
|
if (typeof speechSynthesis !== 'undefined') speechSynthesis.cancel()
|
||||||
|
cleanupTimerHandle = null
|
||||||
}, 3000)
|
}, 3000)
|
||||||
|
|
||||||
isReplaying.value = false
|
isReplaying.value = false
|
||||||
|
|||||||
@@ -290,9 +290,9 @@ function speak(text: string, profileName: string, cancelPrevious: boolean = fals
|
|||||||
if (!voicesLoaded) loadVoices()
|
if (!voicesLoaded) loadVoices()
|
||||||
// Chrome bug: speechSynthesis can get stuck. Nudge it.
|
// Chrome bug: speechSynthesis can get stuck. Nudge it.
|
||||||
if (speechSynthesis.paused) speechSynthesis.resume()
|
if (speechSynthesis.paused) speechSynthesis.resume()
|
||||||
// Only flush if queue is getting deep — allows voices to overlap naturally
|
// Flush if queue is getting deep — max 2 queued to prevent buildup
|
||||||
if (cancelPrevious || (speechSynthesis.pending && speechSynthesis.speaking)) {
|
if (cancelPrevious || (speechSynthesis.pending && speechSynthesis.speaking)) {
|
||||||
if (_speechQueueDepth > 3) {
|
if (_speechQueueDepth > 2) {
|
||||||
speechSynthesis.cancel()
|
speechSynthesis.cancel()
|
||||||
_speechQueueDepth = 0
|
_speechQueueDepth = 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ const showChoose = ref(false)
|
|||||||
const waitingFighters = ref<QueueEntry[]>([])
|
const waitingFighters = ref<QueueEntry[]>([])
|
||||||
let pollHandle: ReturnType<typeof setInterval> | null = null
|
let pollHandle: ReturnType<typeof setInterval> | null = null
|
||||||
|
|
||||||
const isOwner = ref(false)
|
|
||||||
const loadError = ref('')
|
const loadError = ref('')
|
||||||
const fightError = ref('')
|
const fightError = ref('')
|
||||||
const showCustomize = ref(false)
|
const showCustomize = ref(false)
|
||||||
@@ -102,6 +101,8 @@ const ARCHETYPES = [
|
|||||||
'broom_man',
|
'broom_man',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const isOwner = computed(() => isLoggedIn.value && nostrBot.value?.name === botName)
|
||||||
|
|
||||||
const custForm = reactive({
|
const custForm = reactive({
|
||||||
archetype: '',
|
archetype: '',
|
||||||
primaryColor: '#3388cc',
|
primaryColor: '#3388cc',
|
||||||
@@ -216,9 +217,6 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
isLoading.value = false
|
isLoading.value = false
|
||||||
|
|
||||||
// Check ownership
|
|
||||||
isOwner.value = isLoggedIn.value && nostrBot.value?.name === botName
|
|
||||||
|
|
||||||
// Poll queue for "choose your fight"
|
// Poll queue for "choose your fight"
|
||||||
pollQueue()
|
pollQueue()
|
||||||
pollHandle = setInterval(pollQueue, 4000)
|
pollHandle = setInterval(pollQueue, 4000)
|
||||||
|
|||||||
Reference in New Issue
Block a user