mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-16 13:03:31 +08:00
feat(desktop): subscribe preview bridge events into browser store
Routes preview://event (navigated/ready) from Rust into the browser store via a new subscribePreviewEvents subscriber; adds title field and setNavigated/setReady reducers to BrowserSessionState; BrowserSurface subscribes on mount to replace M1 optimistic nav-state. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
36a3ffff34
commit
5d02f4c4e7
@ -10,6 +10,7 @@ const { bridge } = vi.hoisted(() => ({
|
||||
bridge: { open: vi.fn(), navigate: vi.fn(), setBounds: vi.fn(), setVisible: vi.fn(), close: vi.fn() },
|
||||
}))
|
||||
vi.mock('../../lib/previewBridge', () => ({ previewBridge: bridge }))
|
||||
vi.mock('@tauri-apps/api/event', () => ({ listen: () => Promise.resolve(() => {}) }))
|
||||
|
||||
import { BrowserSurface } from './BrowserSurface'
|
||||
import { useBrowserPanelStore } from '../../stores/browserPanelStore'
|
||||
|
||||
@ -2,6 +2,7 @@ import { useEffect, useLayoutEffect, useRef } from 'react'
|
||||
import { BrowserAddressBar } from './BrowserAddressBar'
|
||||
import { computeWebviewBounds } from './computeWebviewBounds'
|
||||
import { previewBridge } from '../../lib/previewBridge'
|
||||
import { subscribePreviewEvents } from '../../lib/previewEvents'
|
||||
import { useBrowserPanelStore } from '../../stores/browserPanelStore'
|
||||
|
||||
export function BrowserSurface({ sessionId }: { sessionId: string }) {
|
||||
@ -33,6 +34,13 @@ export function BrowserSurface({ sessionId }: { sessionId: string }) {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [sessionId])
|
||||
|
||||
useEffect(() => {
|
||||
let unsub: (() => void) | undefined
|
||||
void subscribePreviewEvents(sessionId).then((u) => { unsub = u })
|
||||
return () => { unsub?.() }
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [sessionId])
|
||||
|
||||
useEffect(() => () => { previewBridge.setVisible(false) }, [])
|
||||
|
||||
if (!session) return null
|
||||
|
||||
18
desktop/src/lib/previewEvents.test.ts
Normal file
18
desktop/src/lib/previewEvents.test.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
const listeners: Record<string, (e: { payload: string }) => void> = {}
|
||||
vi.mock('@tauri-apps/api/event', () => ({
|
||||
listen: (name: string, cb: (e: { payload: string }) => void) => { listeners[name] = cb; return Promise.resolve(() => {}) },
|
||||
}))
|
||||
|
||||
import { subscribePreviewEvents } from './previewEvents'
|
||||
import { useBrowserPanelStore } from '../stores/browserPanelStore'
|
||||
|
||||
describe('subscribePreviewEvents', () => {
|
||||
it('routes navigated event to the store', async () => {
|
||||
useBrowserPanelStore.getState().open('s1', 'http://x/a')
|
||||
await subscribePreviewEvents('s1')
|
||||
listeners['preview://event']!({ payload: JSON.stringify({ v: 1, type: 'navigated', url: 'http://x/c', title: 'C' }) })
|
||||
expect(useBrowserPanelStore.getState().bySession['s1']!.url).toBe('http://x/c')
|
||||
})
|
||||
})
|
||||
13
desktop/src/lib/previewEvents.ts
Normal file
13
desktop/src/lib/previewEvents.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { listen } from '@tauri-apps/api/event'
|
||||
import { useBrowserPanelStore } from '../stores/browserPanelStore'
|
||||
|
||||
export async function subscribePreviewEvents(sessionId: string): Promise<() => void> {
|
||||
return listen<string>('preview://event', (e) => {
|
||||
let msg: { type?: string; url?: string; title?: string }
|
||||
try { msg = JSON.parse(e.payload) } catch { return }
|
||||
const store = useBrowserPanelStore.getState()
|
||||
if (msg.type === 'navigated' && msg.url) store.setNavigated(sessionId, msg.url, msg.title ?? '')
|
||||
else if (msg.type === 'ready') store.setReady(sessionId)
|
||||
// selection / screenshot 在 M4/M5 接入
|
||||
})
|
||||
}
|
||||
@ -48,4 +48,13 @@ describe('browserPanelStore', () => {
|
||||
st.setPicker('s1', true)
|
||||
expect(useBrowserPanelStore.getState().bySession['s1']!.pickerActive).toBe(true)
|
||||
})
|
||||
|
||||
it('setNavigated updates url/title without growing history', () => {
|
||||
const st = useBrowserPanelStore.getState()
|
||||
st.open('s1', 'http://x/a')
|
||||
st.setNavigated('s1', 'http://x/b', 'B')
|
||||
const s = useBrowserPanelStore.getState().bySession['s1']!
|
||||
expect(s.url).toBe('http://x/b')
|
||||
expect(s.title).toBe('B')
|
||||
})
|
||||
})
|
||||
|
||||
@ -3,6 +3,7 @@ import { create } from 'zustand'
|
||||
export type BrowserSessionState = {
|
||||
isOpen: boolean
|
||||
url: string
|
||||
title: string
|
||||
history: string[]
|
||||
historyIndex: number
|
||||
loading: boolean
|
||||
@ -20,10 +21,12 @@ type BrowserPanelState = {
|
||||
setLoading: (sessionId: string, loading: boolean) => void
|
||||
setPicker: (sessionId: string, active: boolean) => void
|
||||
close: (sessionId: string) => void
|
||||
setNavigated: (sessionId: string, url: string, title: string) => void
|
||||
setReady: (sessionId: string) => void
|
||||
}
|
||||
|
||||
const empty = (url: string): BrowserSessionState => ({
|
||||
isOpen: true, url, history: [url], historyIndex: 0,
|
||||
isOpen: true, url, title: '', history: [url], historyIndex: 0,
|
||||
loading: false, pickerActive: false, canGoBack: false, canGoForward: false,
|
||||
})
|
||||
|
||||
@ -64,4 +67,12 @@ export const useBrowserPanelStore = create<BrowserPanelState>((set) => ({
|
||||
const cur = st.bySession[sessionId]; if (!cur) return st
|
||||
return { bySession: { ...st.bySession, [sessionId]: { ...cur, isOpen: false, pickerActive: false } } }
|
||||
}),
|
||||
setNavigated: (sessionId, url, title) => set((st) => {
|
||||
const cur = st.bySession[sessionId]; if (!cur) return st
|
||||
return { bySession: { ...st.bySession, [sessionId]: { ...cur, url, title, loading: false } } }
|
||||
}),
|
||||
setReady: (sessionId) => set((st) => {
|
||||
const cur = st.bySession[sessionId]; if (!cur) return st
|
||||
return { bySession: { ...st.bySession, [sessionId]: { ...cur, loading: false } } }
|
||||
}),
|
||||
}))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user