fix(mobile): incorrect height of keyboard toolbar in ios (#8653)

### What Changes:
- Fix keyboard height calculation with the `KeyboardToolBarConfig.useScreenHeight = true`. More detials in: https://github.com/toeverything/blocksuite/pull/8645
- Add safe bottom padding for shrinked keyboard toolbar
- Add `VirtualKeyboard` polyfill. Close [AF-1573](https://linear.app/affine-design/issue/AF-1573/virtualkeyboard-polyfill-with-capacitor)
  - Since the `@capacitor/keyboard` dose not implement the `Keyboard.show()` method, this polyfill is not enabled now.
This commit is contained in:
L-Sun
2024-11-01 05:48:30 +00:00
parent 10b1f233d9
commit 1973cea035
9 changed files with 198 additions and 5 deletions

View File

@@ -13,6 +13,7 @@ def capacitor_pods
pod 'CapacitorCordova', :path => '../../../../../node_modules/@capacitor/ios'
pod 'CapacitorApp', :path => '../../../../../node_modules/@capacitor/app'
pod 'CapacitorBrowser', :path => '../../../../../node_modules/@capacitor/browser'
pod 'CapacitorKeyboard', :path => '../../../../../node_modules/@capacitor/keyboard'
end
target 'App' do

View File

@@ -6,6 +6,8 @@ PODS:
- CapacitorBrowser (6.0.3):
- Capacitor
- CapacitorCordova (6.1.2)
- CapacitorKeyboard (6.0.2):
- Capacitor
- CryptoSwift (1.8.3)
DEPENDENCIES:
@@ -13,6 +15,7 @@ DEPENDENCIES:
- "CapacitorApp (from `../../../../../node_modules/@capacitor/app`)"
- "CapacitorBrowser (from `../../../../../node_modules/@capacitor/browser`)"
- "CapacitorCordova (from `../../../../../node_modules/@capacitor/ios`)"
- "CapacitorKeyboard (from `../../../../../node_modules/@capacitor/keyboard`)"
- CryptoSwift (~> 1.8.3)
SPEC REPOS:
@@ -28,14 +31,17 @@ EXTERNAL SOURCES:
:path: "../../../../../node_modules/@capacitor/browser"
CapacitorCordova:
:path: "../../../../../node_modules/@capacitor/ios"
CapacitorKeyboard:
:path: "../../../../../node_modules/@capacitor/keyboard"
SPEC CHECKSUMS:
Capacitor: 679f9673fdf30597493a6362a5d5bf233d46abc2
CapacitorApp: 0bc633b4eae40a1f32cd2834788fad3bc42da6a1
CapacitorBrowser: aab1ed943b01c0365c4810538a8b3477e2d9f72e
CapacitorCordova: f48c89f96c319101cd2f0ce8a2b7449b5fb8b3dd
CapacitorKeyboard: 2700f9b18687be021e28b5a09b59eb151a46d5e0
CryptoSwift: 967f37cea5a3294d9cce358f78861652155be483
PODFILE CHECKSUM: 763e3dac392c17bcf42dab97a9225ea234e8416a
PODFILE CHECKSUM: 1b0d3fe81862c0e9ce712ddd0c5a0accd0097698
COCOAPODS: 1.15.2
COCOAPODS: 1.16.1

View File

@@ -1,4 +1,5 @@
import type { CapacitorConfig } from '@capacitor/cli';
import { KeyboardResize } from '@capacitor/keyboard';
const config: CapacitorConfig = {
appId: 'app.affine.pro',
@@ -17,6 +18,9 @@ const config: CapacitorConfig = {
CapacitorHttp: {
enabled: true,
},
Keyboard: {
resize: KeyboardResize.Native,
},
},
};

View File

@@ -19,6 +19,7 @@
"@capacitor/browser": "^6.0.3",
"@capacitor/core": "^6.1.2",
"@capacitor/ios": "^6.1.2",
"@capacitor/keyboard": "^6.0.2",
"@sentry/react": "^8.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",

View File

@@ -1,3 +1,6 @@
import '@affine/core/bootstrap/browser';
import '@affine/component/theme';
import '@affine/core/mobile/styles/mobile.css';
// TODO(@L-Sun) Uncomment this when the `show` method implement by `@capacitor/keyboard` in ios
// import './virtual-keyboard';

View File

@@ -0,0 +1,152 @@
import type { PluginListenerHandle } from '@capacitor/core/types/definitions';
import { Keyboard } from '@capacitor/keyboard';
type VirtualKeyboardCallback =
| (<K extends keyof VirtualKeyboardEventMap>(
this: VirtualKeyboard,
ev: VirtualKeyboardEventMap[K]
) => any)
| EventListenerOrEventListenerObject;
class NavigatorVirtualKeyboard implements VirtualKeyboard {
private readonly _boundingRect = new DOMRect();
private readonly _overlaysContent = false;
private _listeners = new Map<
string,
Set<{
cb: VirtualKeyboardCallback;
options?: boolean | AddEventListenerOptions;
}>
>();
private _capacitorListenerHandles: PluginListenerHandle[] = [];
private async _bindListener() {
const updateBoundingRect = (info?: { keyboardHeight: number }) => {
this.boundingRect.x = 0;
this.boundingRect.y = info ? window.innerHeight - info.keyboardHeight : 0;
this.boundingRect.width = window.innerWidth;
this.boundingRect.height = info ? info.keyboardHeight : 0;
this.dispatchEvent(new Event('geometrychange'));
};
this._capacitorListenerHandles = [
await Keyboard.addListener('keyboardDidShow', updateBoundingRect),
await Keyboard.addListener('keyboardDidHide', updateBoundingRect),
];
}
dispatchEvent = (event: Event) => {
const listeners = this._listeners.get(event.type);
if (listeners) {
for (const l of listeners) {
if (typeof l.cb === 'function') {
l.cb.call(this, event);
} else {
l.cb.handleEvent(event);
}
}
}
return !(event.cancelable && event.defaultPrevented);
};
constructor() {
this._bindListener();
}
destroy() {
this._capacitorListenerHandles.forEach(handle => handle.remove());
}
get boundingRect(): DOMRect {
return this._boundingRect;
}
get overlaysContent(): boolean {
return this._overlaysContent;
}
set overlaysContent(_: boolean) {
console.warn(
'overlaysContent is read-only in polyfill based on @capacitor/keyboard'
);
}
hide() {
Keyboard.hide();
}
show() {
Keyboard.show();
}
ongeometrychange: ((this: VirtualKeyboard, ev: Event) => any) | null = null;
addEventListener<K extends keyof VirtualKeyboardEventMap>(
type: K,
listener: VirtualKeyboardCallback,
options?: boolean | AddEventListenerOptions
) {
if (!this._listeners.has(type)) {
this._listeners.set(type, new Set());
}
const listeners = this._listeners.get(type);
if (!listeners) return;
listeners.add({ cb: listener, options });
}
removeEventListener<K extends keyof VirtualKeyboardEventMap>(
type: K,
listener: VirtualKeyboardCallback,
options?: boolean | EventListenerOptions
) {
const listeners = this._listeners.get(type);
if (!listeners) return;
const sameCapture = (
a?: boolean | AddEventListenerOptions,
b?: boolean | EventListenerOptions
) => {
if (a === undefined && b === undefined) {
return true;
}
if (typeof a === 'boolean' && typeof b === 'boolean') {
return a === b;
}
if (typeof a === 'object' && typeof b === 'object') {
return a.capture === b.capture;
}
if (typeof a === 'object' && typeof b === 'boolean') {
return a.capture === b;
}
if (typeof a === 'boolean' && typeof b === 'object') {
return a === b.capture;
}
return false;
};
let target = null;
for (const l of listeners) {
if (l.cb === listener && sameCapture(l.options, options)) {
target = l;
break;
}
}
if (target) {
listeners.delete(target);
}
}
}
// @ts-expect-error polyfill
navigator.virtualKeyboard = new NavigatorVirtualKeyboard();