mirror of
https://github.com/mattermost/mattermost-mobile.git
synced 2026-11-11 21:50:13 +00:00
* Expo-Router partial migration * Full migration to ExpoRouter * i18n * add tests * Merge branch 'main' into expo-router * align package-lock.json * i18n * feedback review * feedback review * android 16kb patch * Refactored emoji picker keyboard using a state machine * set android launch_screen in drawables instead of layout * no conditional hooks * remove duplicate call in test * address feedback * fix tests and address more feedback * small corrections * small corrections * small corrections * fix draft test * fix draft test 2 * remove navigateToScreen in draff test * fix nit in draft test * remove doc file * Feedback review * fix snapshots * improve test coverage * fix i18n * Upgrade t o RN 0.83, other dependencies and new architecture (#9737) * library hardware-keyboard android new arch support fix * library rnshare android new arch support fix * library rnutils android new arch support fix * library secure-pdf-viewer add new arch support * Enable new arch on iOS and Android * update emm and network client with new-arch * update intune to use new-arch * performance improvements * fix tests * fixes post input on new-arch * fix android emoji picker * remove doc * run tests in parallel * upgrade to React Native 0.83.9, Expo 55, React 19, and New Architecture Bumps core dependencies to their latest versions: - React Native 0.77.3 → 0.83.9 (New Architecture enabled) - React 18.3.1 → 19.2.5 - Expo SDK 52 → 55 - TypeScript 5.8.3 → 6.0.3 - Node.js 22.16.0 → 24.15.0 - react-native-reanimated 3.17.3 → 4.3.0 - Various other dependency upgrades Migrates iOS AppDelegate from Objective-C to Swift, updates Android build tooling and Gradle wrapper, refreshes all patches and font assets, and adapts app code, tests, and CI workflows throughout for API changes introduced by these upgrades. * remove unecessary dismissKeyboard in notifications * feedback review * update intune to use new-arch * update @mattermost deps * missing i18n string * additional fixes * update fastlane
124 lines
5.1 KiB
TypeScript
124 lines
5.1 KiB
TypeScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
import {Platform} from 'react-native';
|
|
|
|
import {isEdgeToEdge} from '@constants/device';
|
|
import {DEFAULT_INPUT_ACCESSORY_HEIGHT, MINIMAL_EMOJI_LIST_HEIGHT, SEARCH_BAR_HEIGHT, SEARCH_CONTAINER_PADDING, SEARCH_VISIBILITY_OFFSET} from '@keyboard/constants';
|
|
import {InputContainerStateType, type ActionUpdates, type StateEvent, type StateSnapshot} from '@keyboard/state_machine/types';
|
|
|
|
/**
|
|
* Guard function to check if keyboard height indicates a software keyboard
|
|
* Heights > 75px are considered meaningful software keyboard heights
|
|
*/
|
|
export const isSoftwareKeyboard = (snapshot: StateSnapshot, event: StateEvent): boolean => {
|
|
'worklet';
|
|
const height = event.height ?? snapshot.keyboardEventHeight;
|
|
return height > 75;
|
|
};
|
|
|
|
/**
|
|
* Guard function to check if keyboard height is near zero
|
|
* Heights < 75px are considered zero (keyboard fully dismissed or external keyboard).
|
|
* hasZeroKeyboardHeight extends this to catch hardware keyboard events that arrive with
|
|
* a small but non-zero height — but only when the event height is itself below the
|
|
* software keyboard threshold. A clear software keyboard height (> 75) always wins.
|
|
*/
|
|
export const isZeroHeight = (snapshot: StateSnapshot, event: StateEvent): boolean => {
|
|
'worklet';
|
|
const height = event.height ?? snapshot.keyboardEventHeight;
|
|
if (height > 75) {
|
|
return false;
|
|
}
|
|
return height < 75 || snapshot.hasZeroKeyboardHeight;
|
|
};
|
|
|
|
/**
|
|
* Calculate adjusted keyboard height accounting for tab bar and safe area
|
|
* Uses progressive calculation based on keyboard progress
|
|
*/
|
|
export function calculateAdjustedHeight(
|
|
lastKeyboardHeight: number,
|
|
tabBarHeight: number,
|
|
safeAreaBottom: number,
|
|
rawHeight: number,
|
|
): number {
|
|
'worklet';
|
|
|
|
const targetHeight = lastKeyboardHeight > 0 ? lastKeyboardHeight : rawHeight;
|
|
const keyboardProgress = rawHeight > 0 ? Math.min(1, rawHeight / Math.max(targetHeight, DEFAULT_INPUT_ACCESSORY_HEIGHT)) : 0;
|
|
const tabBarAdjustment = tabBarHeight + (safeAreaBottom * keyboardProgress);
|
|
return Math.max(0, rawHeight - tabBarAdjustment);
|
|
}
|
|
|
|
/**
|
|
* Calculate keyboard-related updates (postInputTranslateY, scrollOffset)
|
|
* This drives the scroll padding and scroll compensation
|
|
*/
|
|
export function calculateKeyboardUpdates(snapshot: StateSnapshot, adjustedHeight: number, rawKeyboardHeight: number): ActionUpdates {
|
|
'worklet';
|
|
|
|
const currentState = snapshot.currentState;
|
|
const isOpening = currentState === InputContainerStateType.KEYBOARD_OPENING;
|
|
const isOpen = currentState === InputContainerStateType.KEYBOARD_OPEN;
|
|
const needsConstantSubtraction = isOpening || isOpen;
|
|
|
|
const tabBarAdjustment = snapshot.tabBarHeight + snapshot.safeAreaBottom;
|
|
|
|
// Check if this is an adjustment event (adjustedHeight !== rawKeyboardHeight)
|
|
// When adjustment event fires, adjustedHeight is set to lastHeight by processEvent
|
|
// We should use that value directly instead of recalculating from rawKeyboardHeight
|
|
const needsAdjustment = Math.abs(adjustedHeight - rawKeyboardHeight) > 50;
|
|
|
|
let effectiveHeight: number;
|
|
if (needsAdjustment) {
|
|
effectiveHeight = adjustedHeight;
|
|
} else if (needsConstantSubtraction) {
|
|
// During opening/open/closing: constant subtraction to eliminate gap
|
|
if (rawKeyboardHeight <= tabBarAdjustment) {
|
|
effectiveHeight = 0;
|
|
} else {
|
|
effectiveHeight = Math.max(0, rawKeyboardHeight - tabBarAdjustment);
|
|
}
|
|
} else {
|
|
effectiveHeight = adjustedHeight;
|
|
}
|
|
|
|
const updates: ActionUpdates = {
|
|
postInputTranslateY: {value: effectiveHeight, animated: false},
|
|
};
|
|
|
|
if (Platform.OS === 'ios') {
|
|
updates.scrollOffset = {value: effectiveHeight, animated: false};
|
|
}
|
|
|
|
return updates;
|
|
}
|
|
|
|
/**
|
|
* Calculate the total height of search UI components (search bar + padding + visibility offset)
|
|
* This is the additional height added on top of keyboard height when emoji search is active
|
|
* @param tabBarHeight - Tab bar height (for tablets in split view)
|
|
* @param safeAreaBottom - Bottom safe area height (for devices with notch or home indicator)
|
|
* @returns The total height of search components
|
|
*/
|
|
export function getEmojiSearchActiveHeight(tabBarHeight: number, safeAreaBottom: number): number {
|
|
'worklet';
|
|
|
|
const offset = isEdgeToEdge ? SEARCH_VISIBILITY_OFFSET : SEARCH_CONTAINER_PADDING;
|
|
return (SEARCH_BAR_HEIGHT + SEARCH_CONTAINER_PADDING + offset + MINIMAL_EMOJI_LIST_HEIGHT + tabBarHeight) - safeAreaBottom;
|
|
}
|
|
|
|
/**
|
|
* Calculate emoji search height based on keyboard height
|
|
* @param keyboardHeight - Current keyboard height (0 if no keyboard or hardware keyboard)
|
|
* @param tabBarHeight - Tab bar height (for tablets in split view)
|
|
* @param safeAreaBottom - Bottom safe area height
|
|
* @returns The calculated search height
|
|
*/
|
|
export function calculateSearchHeight(keyboardHeight: number, tabBarHeight: number, safeAreaBottom: number): number {
|
|
'worklet';
|
|
|
|
return keyboardHeight + getEmojiSearchActiveHeight(tabBarHeight, safeAreaBottom);
|
|
}
|