diff --git a/android/app/build.gradle b/android/app/build.gradle index 951a4c11312..a74f2cb7fe0 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -90,7 +90,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode VERSIONCODE as Integer - versionName "4.70.0" + versionName "4.70.1" vectorDrawables.useSupportLibrary = true manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String] resValue "string", "rn_config_reader_custom_package", "chat.rocket.reactnative" diff --git a/app/views/RoomView/List/components/InvertedScrollView.tsx b/app/views/RoomView/List/components/InvertedScrollView.tsx index c160fbcc42a..a441c25893c 100644 --- a/app/views/RoomView/List/components/InvertedScrollView.tsx +++ b/app/views/RoomView/List/components/InvertedScrollView.tsx @@ -1,153 +1,20 @@ -import React, { forwardRef, useRef, useLayoutEffect } from 'react'; -import { - findNodeHandle, - requireNativeComponent, - StyleSheet, - UIManager, - type StyleProp, - type ViewStyle, - type LayoutChangeEvent, - type ScrollViewProps, - type ViewProps -} from 'react-native'; +import React, { forwardRef } from 'react'; +import { ScrollView, requireNativeComponent, type ScrollViewProps, type ViewProps } from 'react-native'; -const COMMAND_SCROLL_TO = 1; -const COMMAND_SCROLL_TO_END = 2; -const COMMAND_FLASH_SCROLL_INDICATORS = 3; +const NativeInvertedScrollContentView = requireNativeComponent('InvertedScrollContentView'); -const styles = StyleSheet.create({ - baseVertical: { - flexGrow: 1, - flexShrink: 1, - flexDirection: 'column', - overflow: 'scroll' - }, - baseHorizontal: { - flexGrow: 1, - flexShrink: 1, - flexDirection: 'row', - overflow: 'scroll' - } -}); - -type ScrollViewPropsWithRef = ScrollViewProps & React.RefAttributes; -type NativeScrollInstance = React.ComponentRef>; -interface IScrollableMethods { - scrollTo(options?: { x?: number; y?: number; animated?: boolean }): void; - scrollToEnd(options?: { animated?: boolean }): void; - flashScrollIndicators(): void; - getScrollRef(): NativeScrollInstance | null; - setNativeProps(props: object): void; -} - -export type InvertedScrollViewRef = NativeScrollInstance & IScrollableMethods; - -const NativeInvertedScrollView = requireNativeComponent('InvertedScrollView'); - -const NativeInvertedScrollContentView = requireNativeComponent( - 'InvertedScrollContentView' -); - -const InvertedScrollView = forwardRef((props, externalRef) => { - const internalRef = useRef(null); - - useLayoutEffect(() => { - const node = internalRef.current as any; - - if (node) { - // 1. Implementation of scrollTo - node.scrollTo = (options?: { x?: number; y?: number; animated?: boolean }) => { - const tag = findNodeHandle(node); - if (tag != null) { - const x = options?.x || 0; - const y = options?.y || 0; - const animated = options?.animated !== false; - UIManager.dispatchViewManagerCommand(tag, COMMAND_SCROLL_TO, [x, y, animated]); - } - }; - - // 2. Implementation of scrollToEnd - node.scrollToEnd = (options?: { animated?: boolean }) => { - const tag = findNodeHandle(node); - if (tag != null) { - const animated = options?.animated !== false; - UIManager.dispatchViewManagerCommand(tag, COMMAND_SCROLL_TO_END, [animated]); - } - }; - - // 3. Implementation of flashScrollIndicators - node.flashScrollIndicators = () => { - const tag = findNodeHandle(node as any); - if (tag !== null) { - UIManager.dispatchViewManagerCommand(tag, COMMAND_FLASH_SCROLL_INDICATORS, []); - } - }; - - node.getScrollRef = () => node; - const originalSetNativeProps = (node as any).setNativeProps; - if (typeof originalSetNativeProps !== 'function') { - node.setNativeProps = (_nativeProps: object) => {}; - } - } - }, []); - - // Callback Ref to handle merging internal and external refs - const setRef = (node: NativeScrollInstance | null) => { - internalRef.current = node; - - if (typeof externalRef === 'function') { - externalRef(node as InvertedScrollViewRef); - } else if (externalRef) { - (externalRef as React.MutableRefObject).current = node; - } - }; - - const { - children, - contentContainerStyle, - onContentSizeChange, - removeClippedSubviews, - maintainVisibleContentPosition, - snapToAlignment, - stickyHeaderIndices, - ...rest - } = props; - - const preserveChildren = maintainVisibleContentPosition != null || snapToAlignment != null; - const hasStickyHeaders = Array.isArray(stickyHeaderIndices) && stickyHeaderIndices.length > 0; - - const contentContainerStyleArray = [props.horizontal ? { flexDirection: 'row' as const } : null, contentContainerStyle]; - - const contentSizeChangeProps = - onContentSizeChange == null - ? undefined - : { - onLayout: (e: LayoutChangeEvent) => { - const { width, height } = e.nativeEvent.layout; - onContentSizeChange(width, height); - } - }; - - const horizontal = !!props.horizontal; - const baseStyle = horizontal ? styles.baseHorizontal : styles.baseVertical; - const { style, ...restWithoutStyle } = rest; - - if (!NativeInvertedScrollView || !NativeInvertedScrollContentView) { - return null; - } - const ScrollView = NativeInvertedScrollView as React.ComponentType; - const ContentView = NativeInvertedScrollContentView as React.ComponentType; +/** + * Android-only scroll component that wraps the standard ScrollView but uses a native content view + * that reverses accessibility traversal order. This fixes TalkBack reading inverted FlatList items + * in the wrong order, while preserving all ScrollView JS-side behavior (responder handling, + * momentum events, touch coordination). + */ +const InvertedScrollView = forwardRef((props, ref) => { + const { children, ...rest } = props; return ( - - }> - {children} - + + {children} ); }); diff --git a/ios/RocketChatRN.xcodeproj/project.pbxproj b/ios/RocketChatRN.xcodeproj/project.pbxproj index b3d4cea22b2..afb3173c42c 100644 --- a/ios/RocketChatRN.xcodeproj/project.pbxproj +++ b/ios/RocketChatRN.xcodeproj/project.pbxproj @@ -2935,7 +2935,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 4.70.0; + MARKETING_VERSION = 4.70.1; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; @@ -2988,7 +2988,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 4.70.0; + MARKETING_VERSION = 4.70.1; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.NotificationService; diff --git a/ios/RocketChatRN/Info.plist b/ios/RocketChatRN/Info.plist index 0f6998375c0..2126fa04336 100644 --- a/ios/RocketChatRN/Info.plist +++ b/ios/RocketChatRN/Info.plist @@ -28,7 +28,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 4.70.0 + 4.70.1 CFBundleSignature ???? CFBundleURLTypes diff --git a/ios/ShareRocketChatRN/Info.plist b/ios/ShareRocketChatRN/Info.plist index 6d272ca7aae..4461781f02d 100644 --- a/ios/ShareRocketChatRN/Info.plist +++ b/ios/ShareRocketChatRN/Info.plist @@ -26,7 +26,7 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 4.70.0 + 4.70.1 CFBundleVersion 1 KeychainGroup diff --git a/package.json b/package.json index d146ad9f360..cd1a74d048a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rocket-chat-reactnative", - "version": "4.70.0", + "version": "4.70.1", "private": true, "packageManager": "yarn@1.22.22", "scripts": {