Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
159 changes: 13 additions & 146 deletions app/views/RoomView/List/components/InvertedScrollView.tsx
Original file line number Diff line number Diff line change
@@ -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<ViewProps>('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<NativeScrollInstance | null>;
type NativeScrollInstance = React.ComponentRef<NonNullable<typeof NativeInvertedScrollView>>;
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<ScrollViewProps>('InvertedScrollView');

const NativeInvertedScrollContentView = requireNativeComponent<ViewProps & { removeClippedSubviews?: boolean }>(
'InvertedScrollContentView'
);

const InvertedScrollView = forwardRef<InvertedScrollViewRef, ScrollViewProps>((props, externalRef) => {
const internalRef = useRef<NativeScrollInstance | null>(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<NativeScrollInstance | null>).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<ScrollViewPropsWithRef>;
const ContentView = NativeInvertedScrollContentView as React.ComponentType<ViewProps & { removeClippedSubviews?: boolean }>;
/**
* 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<ScrollView, ScrollViewProps>((props, ref) => {
const { children, ...rest } = props;

return (
<ScrollView ref={setRef} {...restWithoutStyle} style={StyleSheet.compose(baseStyle, style)} horizontal={horizontal}>
<ContentView
{...contentSizeChangeProps}
removeClippedSubviews={hasStickyHeaders ? false : removeClippedSubviews}
collapsable={false}
collapsableChildren={!preserveChildren}
style={contentContainerStyleArray as StyleProp<ViewStyle>}>
{children}
</ContentView>
<ScrollView ref={ref} {...rest}>
<NativeInvertedScrollContentView collapsable={false}>{children}</NativeInvertedScrollContentView>
</ScrollView>
);
});
Expand Down
4 changes: 2 additions & 2 deletions ios/RocketChatRN.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion ios/RocketChatRN/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>4.70.0</string>
<string>4.70.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
Expand Down
2 changes: 1 addition & 1 deletion ios/ShareRocketChatRN/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>4.70.0</string>
<string>4.70.1</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>KeychainGroup</key>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "rocket-chat-reactnative",
"version": "4.70.0",
"version": "4.70.1",
"private": true,
"packageManager": "yarn@1.22.22",
"scripts": {
Expand Down
Loading