Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 54 additions & 1 deletion src/lib/dweb-keyboard-overlay.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { describe, expect, it, vi } from 'vitest'
import { applyDwebKeyboardOverlay, type DwebPluginsModule } from './dweb-keyboard-overlay'
import {
applyDwebKeyboardOverlay,
startDwebKeyboardOverlay,
type DwebPluginsModule,
} from './dweb-keyboard-overlay'

describe('dweb keyboard overlay', () => {
it('skips when current environment is not dweb', async () => {
Expand Down Expand Up @@ -56,4 +60,53 @@ describe('dweb keyboard overlay', () => {

warnSpy.mockRestore()
})

it('retries until dweb environment is ready', async () => {
vi.useFakeTimers()

const setOverlay = vi.fn<(overlay: boolean) => Promise<void>>().mockResolvedValue()
const loadPlugins = vi.fn<() => Promise<DwebPluginsModule>>().mockResolvedValue({
virtualKeyboardPlugin: { setOverlay },
})

let ready = false
const stop = startDwebKeyboardOverlay({
isDweb: () => ready,
loadPlugins,
maxAttempts: 4,
retryDelayMs: 100,
})

expect(loadPlugins).not.toHaveBeenCalled()

ready = true
await vi.advanceTimersByTimeAsync(120)

expect(loadPlugins).toHaveBeenCalledTimes(1)
expect(setOverlay).toHaveBeenCalledWith(true)

stop()
vi.useRealTimers()
})

it('stops retrying after cleanup', async () => {
vi.useFakeTimers()

const loadPlugins = vi.fn<() => Promise<DwebPluginsModule>>().mockResolvedValue({})
const stop = startDwebKeyboardOverlay({
isDweb: () => true,
loadPlugins,
maxAttempts: 10,
retryDelayMs: 100,
})

await vi.advanceTimersByTimeAsync(120)
expect(loadPlugins).toHaveBeenCalledTimes(2)

stop()
await vi.advanceTimersByTimeAsync(500)
expect(loadPlugins).toHaveBeenCalledTimes(2)

vi.useRealTimers()
})
})
45 changes: 45 additions & 0 deletions src/lib/dweb-keyboard-overlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ export interface ApplyDwebKeyboardOverlayOptions {
loadPlugins?: () => Promise<DwebPluginsModule>
}

export interface StartDwebKeyboardOverlayOptions extends ApplyDwebKeyboardOverlayOptions {
maxAttempts?: number
retryDelayMs?: number
}

async function defaultLoadPlugins(): Promise<DwebPluginsModule> {
const moduleName = '@plaoc/plugins'
const module = await import(/* @vite-ignore */ moduleName)
Expand Down Expand Up @@ -46,3 +51,43 @@ export async function applyDwebKeyboardOverlay(
return false
}
}

/**
* 启动键盘 overlay 应用流程(包含重试),用于规避运行时初始化时机过早导致的失效。
*/
export function startDwebKeyboardOverlay(
options: StartDwebKeyboardOverlayOptions = {},
): () => void {
const maxAttempts = Math.max(1, options.maxAttempts ?? 10)
const retryDelayMs = Math.max(50, options.retryDelayMs ?? 300)
let stopped = false
let timer: ReturnType<typeof setTimeout> | null = null
let attempts = 0

const run = async (): Promise<void> => {
if (stopped) {
return
}

const applied = await applyDwebKeyboardOverlay(options)
attempts += 1

if (applied || stopped || attempts >= maxAttempts) {
return
}

timer = setTimeout(() => {
void run()
}, retryDelayMs)
}

void run()

return () => {
stopped = true
if (timer !== null) {
clearTimeout(timer)
timer = null
}
}
}
5 changes: 3 additions & 2 deletions src/service-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
installLegacyAuthorizeHashRewriter,
rewriteLegacyAuthorizeHashInPlace,
} from '@/services/authorize/deep-link'
import { applyDwebKeyboardOverlay } from '@/lib/dweb-keyboard-overlay'
import { startDwebKeyboardOverlay } from '@/lib/dweb-keyboard-overlay'

export type ServiceMainCleanup = () => void

Expand All @@ -19,7 +19,7 @@ export function startServiceMain(): ServiceMainCleanup {
rewriteLegacyAuthorizeHashInPlace()

// DWEB: keep viewport stable when soft keyboard appears.
void applyDwebKeyboardOverlay()
const cleanupKeyboardOverlay = startDwebKeyboardOverlay()

// Initialize preference side effects (i18n + RTL) as early as possible.
preferencesActions.initialize()
Expand All @@ -37,5 +37,6 @@ export function startServiceMain(): ServiceMainCleanup {

return () => {
cleanupDeepLink()
cleanupKeyboardOverlay()
}
}