81 lines
2.7 KiB
TypeScript
81 lines
2.7 KiB
TypeScript
import { useEffect, useRef } from 'react'
|
|
|
|
import { useI18n } from '@/i18n'
|
|
import { setPetActivity } from '@/store/pet'
|
|
import { setPetScale } from '@/store/pet-gallery'
|
|
import {
|
|
setPetOverlayLocale,
|
|
setPetOverlayOpenAppHandler,
|
|
setPetOverlayScaleHandler,
|
|
setPetOverlaySubmitHandler
|
|
} from '@/store/pet-overlay'
|
|
import { $sessions } from '@/store/session'
|
|
import { $attentionSessionIds } from '@/store/session-states'
|
|
import { isAuxiliaryWindow } from '@/store/windows'
|
|
|
|
import type { GatewayRequester } from '../types'
|
|
|
|
interface PetBridgeParams {
|
|
requestGateway: GatewayRequester
|
|
resumeSession: (sessionId: string) => Promise<unknown> | unknown
|
|
submitText: (text: string) => Promise<unknown> | unknown
|
|
}
|
|
|
|
/**
|
|
* Wires the popped-out pet overlay back into the app: submit a prompt, resize,
|
|
* and open the most-recent thread, plus mirroring "a session is awaiting the
|
|
* user" into the pet's pose. Handlers register ONCE through refs tracking the
|
|
* latest callbacks — re-registering on identity churn leaves a nulled-handler
|
|
* window that can drop a submit. Primary window only.
|
|
*/
|
|
export function usePetBridge({ requestGateway, resumeSession, submitText }: PetBridgeParams): void {
|
|
const { locale } = useI18n()
|
|
useEffect(() => {
|
|
if (!isAuxiliaryWindow()) {
|
|
setPetOverlayLocale(locale)
|
|
}
|
|
}, [locale])
|
|
const submitTextRef = useRef(submitText)
|
|
submitTextRef.current = submitText
|
|
const resumeSessionRef = useRef(resumeSession)
|
|
resumeSessionRef.current = resumeSession
|
|
const requestGatewayRef = useRef(requestGateway)
|
|
requestGatewayRef.current = requestGateway
|
|
|
|
useEffect(() => {
|
|
if (isAuxiliaryWindow()) {
|
|
return
|
|
}
|
|
|
|
setPetOverlaySubmitHandler(text => void submitTextRef.current(text))
|
|
// Alt+wheel resize from the popped-out pet — persist through this window's
|
|
// gateway (the overlay has none) so it survives restart.
|
|
setPetOverlayScaleHandler(scale => setPetScale(requestGatewayRef.current, scale))
|
|
// Mail icon: $sessions is most-recent-first; the pet is global, so "most
|
|
// recent" is the right target.
|
|
setPetOverlayOpenAppHandler(() => {
|
|
const recent = $sessions.get()[0]
|
|
|
|
if (recent?.id) {
|
|
void resumeSessionRef.current(recent.id)
|
|
}
|
|
})
|
|
|
|
return () => {
|
|
setPetOverlaySubmitHandler(null)
|
|
setPetOverlayOpenAppHandler(null)
|
|
setPetOverlayScaleHandler(null)
|
|
}
|
|
}, [])
|
|
|
|
// Mirror "a session is blocked on the user" (clarify/approval) into the pet's
|
|
// awaitingInput flag so it shows the `waiting` pose.
|
|
useEffect(() => {
|
|
const sync = () => setPetActivity({ awaitingInput: $attentionSessionIds.get().length > 0 })
|
|
|
|
sync()
|
|
|
|
return $attentionSessionIds.listen(sync)
|
|
}, [])
|
|
}
|