Resolve mobile browser address sync before commit (#3228)
This commit is contained in:
parent
73e17b947c
commit
3dbc38f4f3
|
|
@ -47,6 +47,7 @@ import {
|
|||
type BrowserZoomState
|
||||
} from './browser-touch-geometry'
|
||||
import { displayBrowserUrl, normalizeBrowserUrl } from './browser-url'
|
||||
import { resolveMobileBrowserAddressSync } from './mobile-browser-address-sync'
|
||||
|
||||
export type MobileBrowserTab = {
|
||||
type: 'browser'
|
||||
|
|
@ -135,6 +136,10 @@ export function MobileBrowserPane({
|
|||
const cachedInitialFrame = peekCachedBrowserFrame(cacheKey)
|
||||
const [addressValue, setAddressValue] = useState(displayBrowserUrl(tab.url))
|
||||
const [addressFocused, setAddressFocused] = useState(false)
|
||||
const [addressSyncState, setAddressSyncState] = useState({
|
||||
focused: false,
|
||||
url: tab.url
|
||||
})
|
||||
const [keyboardValue, setKeyboardValue] = useState('')
|
||||
const [frameUri, setFrameUri] = useState<string | null>(cachedInitialFrame?.uri ?? null)
|
||||
const [frameMetadata, setFrameMetadata] = useState<BrowserScreencastFrameMetadata | null>(
|
||||
|
|
@ -222,11 +227,18 @@ export function MobileBrowserPane({
|
|||
}
|
||||
}, [worktreeId])
|
||||
|
||||
useEffect(() => {
|
||||
if (!addressFocused) {
|
||||
const addressSync = resolveMobileBrowserAddressSync(addressSyncState, {
|
||||
focused: addressFocused,
|
||||
url: tab.url
|
||||
})
|
||||
if (addressSync.nextState !== addressSyncState) {
|
||||
setAddressSyncState(addressSync.nextState)
|
||||
if (addressSync.shouldSyncValue) {
|
||||
// Why: keep browser stream/goto address updates intact, but avoid a
|
||||
// stale post-blur paint when the tab URL is the source of truth.
|
||||
setAddressValue(displayBrowserUrl(tab.url))
|
||||
}
|
||||
}, [addressFocused, tab.url])
|
||||
}
|
||||
|
||||
useLayoutEffect(() => {
|
||||
// Why: gesture and stream handlers need committed values before passive
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { resolveMobileBrowserAddressSync } from './mobile-browser-address-sync'
|
||||
|
||||
describe('resolveMobileBrowserAddressSync', () => {
|
||||
it('syncs the tab URL when the input is not focused', () => {
|
||||
const result = resolveMobileBrowserAddressSync(
|
||||
{ focused: false, url: 'https://old.example/' },
|
||||
{ focused: false, url: 'https://new.example/' }
|
||||
)
|
||||
|
||||
expect(result).toEqual({
|
||||
nextState: { focused: false, url: 'https://new.example/' },
|
||||
shouldSyncValue: true
|
||||
})
|
||||
})
|
||||
|
||||
it('defers tab URL sync while the user is editing', () => {
|
||||
const result = resolveMobileBrowserAddressSync(
|
||||
{ focused: false, url: 'https://old.example/' },
|
||||
{ focused: true, url: 'https://new.example/' }
|
||||
)
|
||||
|
||||
expect(result).toEqual({
|
||||
nextState: { focused: true, url: 'https://new.example/' },
|
||||
shouldSyncValue: false
|
||||
})
|
||||
})
|
||||
|
||||
it('syncs the latest tab URL when editing ends', () => {
|
||||
const result = resolveMobileBrowserAddressSync(
|
||||
{ focused: true, url: 'https://new.example/' },
|
||||
{ focused: false, url: 'https://new.example/' }
|
||||
)
|
||||
|
||||
expect(result).toEqual({
|
||||
nextState: { focused: false, url: 'https://new.example/' },
|
||||
shouldSyncValue: true
|
||||
})
|
||||
})
|
||||
|
||||
it('preserves externally updated address text when focus and tab URL are unchanged', () => {
|
||||
const previous = { focused: false, url: 'https://new.example/' }
|
||||
const result = resolveMobileBrowserAddressSync(previous, {
|
||||
focused: false,
|
||||
url: 'https://new.example/'
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
nextState: previous,
|
||||
shouldSyncValue: false
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
export type MobileBrowserAddressSyncState = {
|
||||
focused: boolean
|
||||
url: string
|
||||
}
|
||||
|
||||
export type MobileBrowserAddressSyncResult = {
|
||||
nextState: MobileBrowserAddressSyncState
|
||||
shouldSyncValue: boolean
|
||||
}
|
||||
|
||||
export function resolveMobileBrowserAddressSync(
|
||||
previous: MobileBrowserAddressSyncState,
|
||||
next: MobileBrowserAddressSyncState
|
||||
): MobileBrowserAddressSyncResult {
|
||||
const changed = previous.focused !== next.focused || previous.url !== next.url
|
||||
return {
|
||||
nextState: changed ? next : previous,
|
||||
shouldSyncValue: changed && !next.focused
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue