From aa12bd7e60edb84682a42eaf08f314cb8059f831 Mon Sep 17 00:00:00 2001 From: Aman Harwara Date: Fri, 2 May 2025 14:49:20 +0530 Subject: [PATCH] fix: Android keyboard covering content --- packages/mobile/src/MobileWebAppContainer.tsx | 53 +++++++++++-------- .../services/src/Domain/Event/WebAppEvent.ts | 1 - .../snjs/lib/Client/ReactNativeToWebEvent.ts | 4 +- .../WebApplication/WebApplicationInterface.ts | 2 +- .../javascripts/Application/WebApplication.ts | 4 +- .../NativeMobileWeb/MobileWebReceiver.ts | 8 ++- 6 files changed, 40 insertions(+), 32 deletions(-) diff --git a/packages/mobile/src/MobileWebAppContainer.tsx b/packages/mobile/src/MobileWebAppContainer.tsx index 588016000d7..0343a26cfdc 100644 --- a/packages/mobile/src/MobileWebAppContainer.tsx +++ b/packages/mobile/src/MobileWebAppContainer.tsx @@ -2,7 +2,7 @@ import { ApplicationEvent, ReactNativeToWebEvent } from '@standardnotes/snjs' import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { Button, Dimensions, Keyboard, Platform, Text, View } from 'react-native' +import { Button, Dimensions, Keyboard, KeyboardEvent, Platform, Text, View } from 'react-native' import VersionInfo from 'react-native-version-info' import { WebView, WebViewMessageEvent } from 'react-native-webview' import { OnShouldStartLoadWithRequest, WebViewNativeConfig } from 'react-native-webview/lib/WebViewTypes' @@ -58,7 +58,7 @@ const MobileWebAppContents = ({ destroyAndReload }: { destroyAndReload: () => vo webViewRef.current?.postMessage(JSON.stringify({ reactNativeEvent: event, messageType: 'event' })) }) - const keyboardShowListener = Keyboard.addListener('keyboardWillShow', () => { + const keyboardWillShowListener = Keyboard.addListener('keyboardWillShow', () => { device.reloadStatusBarStyle(false) webViewRef.current?.postMessage( JSON.stringify({ @@ -78,43 +78,54 @@ const MobileWebAppContents = ({ destroyAndReload }: { destroyAndReload: () => vo ) }) - const keyboardHideListener = Keyboard.addListener('keyboardDidHide', () => { - device.reloadStatusBarStyle(false) - }) - - const keyboardWillChangeFrame = Keyboard.addListener('keyboardWillChangeFrame', (e) => { + const fireKeyboardSizeChangeEvent = (e: KeyboardEvent) => { webViewRef.current?.postMessage( JSON.stringify({ - reactNativeEvent: ReactNativeToWebEvent.KeyboardFrameWillChange, + reactNativeEvent: ReactNativeToWebEvent.KeyboardSizeChanged, messageType: 'event', messageData: { height: e.endCoordinates.height, contentHeight: e.endCoordinates.screenY, - isFloatingKeyboard: e.endCoordinates.width !== Dimensions.get('window').width, + isFloatingKeyboard: Math.floor(e.endCoordinates.width) !== Math.floor(Dimensions.get('window').width), }, }), ) + } + + const keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', (e) => { + // iOS handles this using the `willChangeFrame` event instead + if (Platform.OS === 'android') { + fireKeyboardSizeChangeEvent(e) + } + device.reloadStatusBarStyle(false) }) - const keyboardDidChangeFrame = Keyboard.addListener('keyboardDidChangeFrame', (e) => { - webViewRef.current?.postMessage( - JSON.stringify({ - reactNativeEvent: ReactNativeToWebEvent.KeyboardFrameDidChange, - messageType: 'event', - messageData: { height: e.endCoordinates.height, contentHeight: e.endCoordinates.screenY }, - }), - ) + const keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', () => { + // iOS handles this using the `willChangeFrame` event instead + if (Platform.OS === 'android') { + webViewRef.current?.postMessage( + JSON.stringify({ + reactNativeEvent: ReactNativeToWebEvent.KeyboardDidHide, + messageType: 'event', + }), + ) + } + device.reloadStatusBarStyle(false) + }) + + const keyboardWillChangeFrame = Keyboard.addListener('keyboardWillChangeFrame', (e) => { + fireKeyboardSizeChangeEvent(e) }) return () => { removeStateServiceListener() removeBackHandlerServiceListener() removeColorSchemeServiceListener() - keyboardShowListener.remove() - keyboardHideListener.remove() - keyboardWillChangeFrame.remove() - keyboardDidChangeFrame.remove() + keyboardWillShowListener.remove() keyboardWillHideListener.remove() + keyboardDidShowListener.remove() + keyboardDidHideListener.remove() + keyboardWillChangeFrame.remove() } }, [webViewRef, stateService, device, androidBackHandlerService, colorSchemeService]) diff --git a/packages/services/src/Domain/Event/WebAppEvent.ts b/packages/services/src/Domain/Event/WebAppEvent.ts index 265f4ee35c7..90a575232e0 100644 --- a/packages/services/src/Domain/Event/WebAppEvent.ts +++ b/packages/services/src/Domain/Event/WebAppEvent.ts @@ -6,6 +6,5 @@ export enum WebAppEvent { PanelResized = 'PanelResized', WindowDidFocus = 'WindowDidFocus', WindowDidBlur = 'WindowDidBlur', - MobileKeyboardDidChangeFrame = 'MobileKeyboardDidChangeFrame', MobileKeyboardWillChangeFrame = 'MobileKeyboardWillChangeFrame', } diff --git a/packages/snjs/lib/Client/ReactNativeToWebEvent.ts b/packages/snjs/lib/Client/ReactNativeToWebEvent.ts index 776c4bc3f30..a4032f6f085 100644 --- a/packages/snjs/lib/Client/ReactNativeToWebEvent.ts +++ b/packages/snjs/lib/Client/ReactNativeToWebEvent.ts @@ -5,10 +5,10 @@ export enum ReactNativeToWebEvent { LosingFocus = 'LosingFocus', AndroidBackButtonPressed = 'AndroidBackButtonPressed', ColorSchemeChanged = 'ColorSchemeChanged', - KeyboardFrameWillChange = 'KeyboardFrameWillChange', - KeyboardFrameDidChange = 'KeyboardFrameDidChange', + KeyboardSizeChanged = 'KeyboardSizeChanged', KeyboardWillShow = 'KeyboardWillShow', KeyboardWillHide = 'KeyboardWillHide', + KeyboardDidHide = 'KeyboardDidHide', ReceivedFile = 'ReceivedFile', ReceivedLink = 'ReceivedLink', ReceivedText = 'ReceivedText', diff --git a/packages/ui-services/src/WebApplication/WebApplicationInterface.ts b/packages/ui-services/src/WebApplication/WebApplicationInterface.ts index f232d13a59f..f2d2bac3da3 100644 --- a/packages/ui-services/src/WebApplication/WebApplicationInterface.ts +++ b/packages/ui-services/src/WebApplication/WebApplicationInterface.ts @@ -20,7 +20,7 @@ export interface WebApplicationInterface extends ApplicationInterface { contentHeight: number isFloatingKeyboard: boolean }): void - handleMobileKeyboardDidChangeFrameEvent(frame: { height: number; contentHeight: number }): void + handleMobileKeyboardDidHideEvent(): void handleReceivedFileEvent(file: { name: string; mimeType: string; data: string }): void handleReceivedTextEvent(item: { text: string; title?: string }): Promise handleReceivedLinkEvent(item: { link: string; title: string }): Promise diff --git a/packages/web/src/javascripts/Application/WebApplication.ts b/packages/web/src/javascripts/Application/WebApplication.ts index 58d94b571f6..46c91062b57 100644 --- a/packages/web/src/javascripts/Application/WebApplication.ts +++ b/packages/web/src/javascripts/Application/WebApplication.ts @@ -357,8 +357,8 @@ export class WebApplication extends SNApplication implements WebApplicationInter this.notifyWebEvent(WebAppEvent.MobileKeyboardWillChangeFrame, frame) } - handleMobileKeyboardDidChangeFrameEvent(frame: { height: number; contentHeight: number }): void { - this.notifyWebEvent(WebAppEvent.MobileKeyboardDidChangeFrame, frame) + handleMobileKeyboardDidHideEvent(): void { + setCustomViewportHeight(100, 'vh', true) } handleOpenFilePreviewEvent({ id }: { id: string }): void { diff --git a/packages/web/src/javascripts/NativeMobileWeb/MobileWebReceiver.ts b/packages/web/src/javascripts/NativeMobileWeb/MobileWebReceiver.ts index 14b6a8fb2b6..959d9ed8b15 100644 --- a/packages/web/src/javascripts/NativeMobileWeb/MobileWebReceiver.ts +++ b/packages/web/src/javascripts/NativeMobileWeb/MobileWebReceiver.ts @@ -73,15 +73,13 @@ export class MobileWebReceiver { case ReactNativeToWebEvent.ColorSchemeChanged: void this.application.handleMobileColorSchemeChangeEvent() break - case ReactNativeToWebEvent.KeyboardFrameWillChange: + case ReactNativeToWebEvent.KeyboardSizeChanged: void this.application.handleMobileKeyboardWillChangeFrameEvent( messageData as { height: number; contentHeight: number; isFloatingKeyboard: boolean }, ) break - case ReactNativeToWebEvent.KeyboardFrameDidChange: - void this.application.handleMobileKeyboardDidChangeFrameEvent( - messageData as { height: number; contentHeight: number }, - ) + case ReactNativeToWebEvent.KeyboardDidHide: + void this.application.handleMobileKeyboardDidHideEvent() break case ReactNativeToWebEvent.ReceivedFile: void this.application.handleReceivedFileEvent(