mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 12:28:42 +00:00
fix(editor): android keyboard can not be opened (#10502)
Close [BS-2674](https://linear.app/affine-design/issue/BS-2674/[android]-%E6%96%87%E6%9C%AC%E7%BC%96%E8%BE%91%E5%8C%BA%E5%9F%9F%E7%82%B9%E5%87%BB%E5%90%8E%E6%97%A0%E6%B3%95%E6%BF%80%E6%B4%BB%E9%94%AE%E7%9B%98) [BS-2609](https://linear.app/affine-design/issue/BS-2609/[android]-%E8%BE%93%E5%85%A5%E7%9A%84-toolbar-%E6%B2%A1%E6%9C%89%E4%BA%86)
This commit is contained in:
@@ -97,15 +97,48 @@ framework.impl(ClientSchemeProvider, {
|
||||
});
|
||||
|
||||
framework.impl(VirtualKeyboardProvider, {
|
||||
addEventListener: (event, callback) => {
|
||||
Keyboard.addListener(event as any, callback as any).catch(e => {
|
||||
console.error(e);
|
||||
show: () => {
|
||||
Keyboard.show().catch(console.error);
|
||||
},
|
||||
hide: () => {
|
||||
// In some cases, the keyboard will show again. for example, it will show again
|
||||
// when this function is called in click event of button. It may be a bug of
|
||||
// android webview or capacitor.
|
||||
setTimeout(() => {
|
||||
Keyboard.hide().catch(console.error);
|
||||
});
|
||||
},
|
||||
removeAllListeners: () => {
|
||||
Keyboard.removeAllListeners().catch(e => {
|
||||
console.error(e);
|
||||
});
|
||||
onChange: callback => {
|
||||
let disposeRef = {
|
||||
dispose: () => {},
|
||||
};
|
||||
|
||||
Promise.all([
|
||||
Keyboard.addListener('keyboardWillShow', info => {
|
||||
callback({
|
||||
visible: true,
|
||||
height: info.keyboardHeight,
|
||||
});
|
||||
}),
|
||||
Keyboard.addListener('keyboardWillHide', () => {
|
||||
callback({
|
||||
visible: false,
|
||||
height: 0,
|
||||
});
|
||||
}),
|
||||
])
|
||||
.then(handlers => {
|
||||
disposeRef.dispose = () => {
|
||||
Promise.all(handlers.map(handler => handler.remove())).catch(
|
||||
console.error
|
||||
);
|
||||
};
|
||||
})
|
||||
.catch(console.error);
|
||||
|
||||
return () => {
|
||||
disposeRef.dispose();
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ const config: CapacitorConfig = {
|
||||
enabled: false,
|
||||
},
|
||||
Keyboard: {
|
||||
resize: KeyboardResize.Native,
|
||||
resize: KeyboardResize.None,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -106,15 +106,40 @@ framework.impl(ValidatorProvider, {
|
||||
},
|
||||
});
|
||||
framework.impl(VirtualKeyboardProvider, {
|
||||
addEventListener: (event, callback) => {
|
||||
Keyboard.addListener(event as any, callback as any).catch(e => {
|
||||
console.error(e);
|
||||
});
|
||||
},
|
||||
removeAllListeners: () => {
|
||||
Keyboard.removeAllListeners().catch(e => {
|
||||
console.error(e);
|
||||
});
|
||||
// We dose not provide show and hide because:
|
||||
// - Keyboard.show() is not implemented
|
||||
// - Keyboard.hide() will blur the current editor
|
||||
onChange: callback => {
|
||||
let disposeRef = {
|
||||
dispose: () => {},
|
||||
};
|
||||
|
||||
Promise.all([
|
||||
Keyboard.addListener('keyboardDidShow', info => {
|
||||
callback({
|
||||
visible: true,
|
||||
height: info.keyboardHeight,
|
||||
});
|
||||
}),
|
||||
Keyboard.addListener('keyboardWillHide', () => {
|
||||
callback({
|
||||
visible: false,
|
||||
height: 0,
|
||||
});
|
||||
}),
|
||||
])
|
||||
.then(handlers => {
|
||||
disposeRef.dispose = () => {
|
||||
Promise.all(handlers.map(handler => handler.remove())).catch(
|
||||
console.error
|
||||
);
|
||||
};
|
||||
})
|
||||
.catch(console.error);
|
||||
|
||||
return () => {
|
||||
disposeRef.dispose();
|
||||
};
|
||||
},
|
||||
});
|
||||
framework.impl(NavigationGestureProvider, {
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
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 readonly _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().catch(e => {
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this._capacitorListenerHandles.forEach(handle => {
|
||||
handle.remove().catch(e => {
|
||||
console.error(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
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().catch(e => {
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
show() {
|
||||
Keyboard.show().catch(e => {
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
@@ -2,6 +2,7 @@ import { AffineContext } from '@affine/core/components/context';
|
||||
import { AppFallback } from '@affine/core/mobile/components/app-fallback';
|
||||
import { configureMobileModules } from '@affine/core/mobile/modules';
|
||||
import { HapticProvider } from '@affine/core/mobile/modules/haptics';
|
||||
import { VirtualKeyboardProvider } from '@affine/core/mobile/modules/virtual-keyboard';
|
||||
import { router } from '@affine/core/mobile/router';
|
||||
import { configureCommonModules } from '@affine/core/modules';
|
||||
import { I18nProvider } from '@affine/core/modules/i18n';
|
||||
@@ -99,6 +100,44 @@ framework.impl(HapticProvider, {
|
||||
selectionChanged: () => Promise.reject('Not supported'),
|
||||
selectionEnd: () => Promise.reject('Not supported'),
|
||||
});
|
||||
framework.impl(VirtualKeyboardProvider, {
|
||||
onChange: callback => {
|
||||
if (!visualViewport) {
|
||||
console.warn('visualViewport is not supported');
|
||||
return () => {};
|
||||
}
|
||||
|
||||
const listener = () => {
|
||||
if (!visualViewport) return;
|
||||
const windowHeight = window.innerHeight;
|
||||
|
||||
/**
|
||||
* ┌───────────────┐ - window top
|
||||
* │ │
|
||||
* │ │
|
||||
* │ │
|
||||
* │ │
|
||||
* │ │
|
||||
* └───────────────┘ - keyboard top --
|
||||
* │ │ │ keyboard height in layout viewport
|
||||
* └───────────────┘ - page(html) bottom --
|
||||
* │ │ │ visualViewport.offsetTop
|
||||
* └───────────────┘ - window bottom --
|
||||
*/
|
||||
callback({
|
||||
visible: window.innerHeight - visualViewport.height > 0,
|
||||
height: windowHeight - visualViewport.height - visualViewport.offsetTop,
|
||||
});
|
||||
};
|
||||
|
||||
visualViewport.addEventListener('resize', listener);
|
||||
visualViewport.addEventListener('scroll', listener);
|
||||
return () => {
|
||||
visualViewport?.removeEventListener('resize', listener);
|
||||
visualViewport?.removeEventListener('scroll', listener);
|
||||
};
|
||||
},
|
||||
});
|
||||
const frameworkProvider = framework.provider();
|
||||
|
||||
// setup application lifecycle events, and emit application start event
|
||||
|
||||
@@ -151,7 +151,7 @@ const usePatchSpecs = (mode: DocMode) => {
|
||||
builder.extend([patchForAttachmentEmbedViews(reactToLit)]);
|
||||
}
|
||||
if (BUILD_CONFIG.isMobileEdition) {
|
||||
enableMobileExtension(builder);
|
||||
enableMobileExtension(builder, framework);
|
||||
}
|
||||
if (BUILD_CONFIG.isElectron) {
|
||||
builder.extend([patchForClipboardInElectron(framework)].flat());
|
||||
|
||||
@@ -1,29 +1,36 @@
|
||||
import { createKeyboardToolbarConfig } from '@affine/core/blocksuite/extensions/keyboard-toolbar-config';
|
||||
import { VirtualKeyboardProvider } from '@affine/core/mobile/modules/virtual-keyboard';
|
||||
import {
|
||||
type BlockStdScope,
|
||||
ConfigIdentifier,
|
||||
LifeCycleWatcher,
|
||||
LifeCycleWatcherIdentifier,
|
||||
} from '@blocksuite/affine/block-std';
|
||||
import type {
|
||||
CodeBlockConfig,
|
||||
ReferenceNodeConfig,
|
||||
RootBlockConfig,
|
||||
SpecBuilder,
|
||||
} from '@blocksuite/affine/blocks';
|
||||
import {
|
||||
codeToolbarWidget,
|
||||
DocModeProvider,
|
||||
embedCardToolbarWidget,
|
||||
FeatureFlagService,
|
||||
formatBarWidget,
|
||||
imageToolbarWidget,
|
||||
ParagraphBlockService,
|
||||
ReferenceNodeConfigIdentifier,
|
||||
RootBlockConfigExtension,
|
||||
slashMenuWidget,
|
||||
surfaceRefToolbarWidget,
|
||||
VirtualKeyboardProvider as BSVirtualKeyboardProvider,
|
||||
} from '@blocksuite/affine/blocks';
|
||||
import type { Container } from '@blocksuite/affine/global/di';
|
||||
import type {
|
||||
Container,
|
||||
ServiceIdentifier,
|
||||
} from '@blocksuite/affine/global/di';
|
||||
import { DisposableGroup } from '@blocksuite/affine/global/utils';
|
||||
import type { ExtensionType } from '@blocksuite/affine/store';
|
||||
import { batch, signal } from '@preact/signals-core';
|
||||
import type { FrameworkProvider } from '@toeverything/infra';
|
||||
|
||||
class MobileSpecsPatches extends LifeCycleWatcher {
|
||||
static override key = 'mobile-patches';
|
||||
@@ -86,28 +93,85 @@ class MobileSpecsPatches extends LifeCycleWatcher {
|
||||
}
|
||||
}
|
||||
|
||||
const mobileExtensions: ExtensionType[] = [
|
||||
function KeyboardToolbarExtension(framework: FrameworkProvider): ExtensionType {
|
||||
const affineVirtualKeyboardProvider = framework.get(VirtualKeyboardProvider);
|
||||
|
||||
class BSVirtualKeyboardService
|
||||
extends LifeCycleWatcher
|
||||
implements BSVirtualKeyboardProvider
|
||||
{
|
||||
setup: di => {
|
||||
const prev = di.getFactory(RootBlockConfigExtension.identifier);
|
||||
static override key = BSVirtualKeyboardProvider.identifierName;
|
||||
|
||||
di.override(RootBlockConfigExtension.identifier, provider => {
|
||||
return {
|
||||
...prev?.(provider),
|
||||
keyboardToolbar: createKeyboardToolbarConfig(),
|
||||
} satisfies RootBlockConfig;
|
||||
private readonly _disposables = new DisposableGroup();
|
||||
|
||||
private get _rootContentEditable() {
|
||||
const editorMode = this.std.get(DocModeProvider).getEditorMode();
|
||||
if (editorMode !== 'page') return null;
|
||||
|
||||
if (!this.std.host.doc.root) return;
|
||||
return this.std.view.getBlock(this.std.host.doc.root.id);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line rxjs/finnish
|
||||
readonly visible$ = signal(false);
|
||||
|
||||
// eslint-disable-next-line rxjs/finnish
|
||||
readonly height$ = signal(0);
|
||||
|
||||
show() {
|
||||
if ('show' in affineVirtualKeyboardProvider) {
|
||||
affineVirtualKeyboardProvider.show();
|
||||
} else if (this._rootContentEditable) {
|
||||
this._rootContentEditable.inputMode = '';
|
||||
}
|
||||
}
|
||||
hide() {
|
||||
if ('hide' in affineVirtualKeyboardProvider) {
|
||||
affineVirtualKeyboardProvider.hide();
|
||||
} else if (this._rootContentEditable) {
|
||||
this._rootContentEditable.inputMode = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
static override setup(di: Container) {
|
||||
super.setup(di);
|
||||
di.addImpl(BSVirtualKeyboardProvider, provider => {
|
||||
return provider.get(
|
||||
LifeCycleWatcherIdentifier(
|
||||
this.key
|
||||
) as ServiceIdentifier<BSVirtualKeyboardService>
|
||||
);
|
||||
});
|
||||
},
|
||||
},
|
||||
MobileSpecsPatches,
|
||||
];
|
||||
}
|
||||
|
||||
export function enableMobileExtension(specBuilder: SpecBuilder): void {
|
||||
override mounted() {
|
||||
this._disposables.add(
|
||||
affineVirtualKeyboardProvider.onChange(({ visible, height }) => {
|
||||
batch(() => {
|
||||
this.visible$.value = visible;
|
||||
this.height$.value = height;
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
override unmounted() {
|
||||
this._disposables.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
return BSVirtualKeyboardService;
|
||||
}
|
||||
|
||||
export function enableMobileExtension(
|
||||
specBuilder: SpecBuilder,
|
||||
framework: FrameworkProvider
|
||||
): void {
|
||||
specBuilder.omit(formatBarWidget);
|
||||
specBuilder.omit(embedCardToolbarWidget);
|
||||
specBuilder.omit(slashMenuWidget);
|
||||
specBuilder.omit(codeToolbarWidget);
|
||||
specBuilder.omit(imageToolbarWidget);
|
||||
specBuilder.omit(surfaceRefToolbarWidget);
|
||||
specBuilder.extend(mobileExtensions);
|
||||
specBuilder.extend([MobileSpecsPatches, KeyboardToolbarExtension(framework)]);
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import { type KeyboardToolbarConfig } from '@blocksuite/affine/blocks';
|
||||
|
||||
export function createKeyboardToolbarConfig(): Partial<KeyboardToolbarConfig> {
|
||||
return {
|
||||
// TODO(@L-Sun): check android following the PR
|
||||
// https://github.com/toeverything/blocksuite/pull/8645
|
||||
useScreenHeight: BUILD_CONFIG.isIOS,
|
||||
};
|
||||
}
|
||||
@@ -18,7 +18,7 @@ export const AppTabs = ({
|
||||
fixed?: boolean;
|
||||
}) => {
|
||||
const virtualKeyboardService = useService(VirtualKeyboardService);
|
||||
const virtualKeyboardVisible = useLiveData(virtualKeyboardService.show$);
|
||||
const virtualKeyboardVisible = useLiveData(virtualKeyboardService.visible$);
|
||||
|
||||
const tab = (
|
||||
<SafeArea
|
||||
|
||||
@@ -6,8 +6,5 @@ import { VirtualKeyboardService } from './services/virtual-keyboard';
|
||||
export { VirtualKeyboardProvider, VirtualKeyboardService };
|
||||
|
||||
export function configureMobileVirtualKeyboardModule(framework: Framework) {
|
||||
framework.service(
|
||||
VirtualKeyboardService,
|
||||
f => new VirtualKeyboardService(f.getOptional(VirtualKeyboardProvider))
|
||||
);
|
||||
framework.service(VirtualKeyboardService, [VirtualKeyboardProvider]);
|
||||
}
|
||||
|
||||
@@ -1,23 +1,28 @@
|
||||
import { createIdentifier } from '@toeverything/infra';
|
||||
|
||||
export type VirtualKeyboardEvent =
|
||||
| 'keyboardWillShow'
|
||||
| 'keyboardDidShow'
|
||||
| 'keyboardWillHide'
|
||||
| 'keyboardDidHide';
|
||||
|
||||
export interface VirtualKeyboardEventInfo {
|
||||
keyboardHeight: number;
|
||||
interface VirtualKeyboardInfo {
|
||||
visible: boolean;
|
||||
height: number;
|
||||
}
|
||||
type VirtualKeyboardEventListener = (info: VirtualKeyboardEventInfo) => void;
|
||||
|
||||
export interface VirtualKeyboardProvider {
|
||||
addEventListener: (
|
||||
event: VirtualKeyboardEvent,
|
||||
callback: VirtualKeyboardEventListener
|
||||
) => void;
|
||||
removeAllListeners: () => void;
|
||||
}
|
||||
type VirtualKeyboardAction = {
|
||||
/**
|
||||
* Open the virtual keyboard, the focused element should not be changed
|
||||
*/
|
||||
show: () => void;
|
||||
/**
|
||||
* Hide the virtual keyboard, the focused element should not be changed
|
||||
*/
|
||||
hide: () => void;
|
||||
};
|
||||
|
||||
type VirtualKeyboardEvent = {
|
||||
onChange: (callback: (info: VirtualKeyboardInfo) => void) => () => void;
|
||||
};
|
||||
|
||||
export type VirtualKeyboardProvider =
|
||||
| (VirtualKeyboardEvent & VirtualKeyboardAction)
|
||||
| VirtualKeyboardEvent;
|
||||
|
||||
export const VirtualKeyboardProvider =
|
||||
createIdentifier<VirtualKeyboardProvider>('VirtualKeyboardProvider');
|
||||
|
||||
@@ -3,32 +3,23 @@ import { LiveData, Service } from '@toeverything/infra';
|
||||
import type { VirtualKeyboardProvider } from '../providers/virtual-keyboard';
|
||||
|
||||
export class VirtualKeyboardService extends Service {
|
||||
show$ = new LiveData(false);
|
||||
height$ = new LiveData(0);
|
||||
readonly visible$ = new LiveData(false);
|
||||
|
||||
readonly height$ = new LiveData(0);
|
||||
|
||||
constructor(
|
||||
private readonly virtualKeyboardProvider?: VirtualKeyboardProvider
|
||||
private readonly virtualKeyboardProvider: VirtualKeyboardProvider
|
||||
) {
|
||||
super();
|
||||
this._observe();
|
||||
}
|
||||
|
||||
override dispose() {
|
||||
super.dispose();
|
||||
this.virtualKeyboardProvider?.removeAllListeners();
|
||||
}
|
||||
|
||||
private _observe() {
|
||||
this.virtualKeyboardProvider?.addEventListener(
|
||||
'keyboardWillShow',
|
||||
({ keyboardHeight }) => {
|
||||
this.show$.next(true);
|
||||
this.height$.next(keyboardHeight);
|
||||
}
|
||||
this.disposables.push(
|
||||
this.virtualKeyboardProvider.onChange(info => {
|
||||
this.visible$.next(info.visible);
|
||||
this.height$.next(info.height);
|
||||
})
|
||||
);
|
||||
this.virtualKeyboardProvider?.addEventListener('keyboardWillHide', () => {
|
||||
this.show$.next(false);
|
||||
this.height$.next(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,9 +23,6 @@ globalStyle('body', {
|
||||
globalStyle('body:has(> #app-tabs)', {
|
||||
paddingBottom: globalVars.appTabSafeArea,
|
||||
});
|
||||
globalStyle('body:has(#app-tabs) affine-keyboard-toolbar[data-shrink="true"]', {
|
||||
paddingBottom: globalVars.appTabSafeArea,
|
||||
});
|
||||
globalStyle('body:has(#app-tabs) affine-keyboard-tool-panel', {
|
||||
paddingBottom: `calc(${globalVars.appTabHeight} + env(safe-area-inset-bottom) + 8px)`,
|
||||
});
|
||||
|
||||
@@ -322,7 +322,6 @@ export class AtMenuConfigService extends Service {
|
||||
|
||||
private getMobileConfig(): Partial<LinkedWidgetConfig['mobile']> {
|
||||
return {
|
||||
useScreenHeight: BUILD_CONFIG.isIOS,
|
||||
scrollContainer: window,
|
||||
scrollTopOffset: () => {
|
||||
const header = document.querySelector('header');
|
||||
|
||||
Reference in New Issue
Block a user