diff --git a/android/app/src/main/java/chat/rocket/reactnative/MainActivity.kt b/android/app/src/main/java/chat/rocket/reactnative/MainActivity.kt
index e4ab65ceba1..eac65e52db2 100644
--- a/android/app/src/main/java/chat/rocket/reactnative/MainActivity.kt
+++ b/android/app/src/main/java/chat/rocket/reactnative/MainActivity.kt
@@ -1,24 +1,26 @@
package chat.rocket.reactnative
-
+
+import android.os.Bundle
+import android.content.Intent
+import android.view.KeyEvent
+import android.view.View
import com.facebook.react.ReactActivity
import com.facebook.react.ReactActivityDelegate
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
import com.facebook.react.defaults.DefaultReactActivityDelegate
-
-import android.os.Bundle
import com.zoontek.rnbootsplash.RNBootSplash
-import android.content.Intent
-import android.content.res.Configuration
import chat.rocket.reactnative.notification.NotificationIntentHandler
-
+import chat.rocket.reactnative.a11y.KeyboardA11yModule
+import chat.rocket.reactnative.scroll.FocusUtils
+
class MainActivity : ReactActivity() {
-
+
/**
* Returns the name of the main component registered from JavaScript. This is used to schedule
* rendering of the component.
*/
override fun getMainComponentName(): String = "RocketChatRN"
-
+
/**
* Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate]
* which allows you to enable New Architecture with a single boolean flags [fabricEnabled]
@@ -29,20 +31,57 @@ class MainActivity : ReactActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
RNBootSplash.init(this, R.style.BootTheme)
super.onCreate(null)
-
+
// Handle notification intents
intent?.let { NotificationIntentHandler.handleIntent(this, it) }
}
-
+
public override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
-
+
// Handle notification intents when activity is already running
NotificationIntentHandler.handleIntent(this, intent)
}
+ override fun dispatchKeyEvent(event: KeyEvent): Boolean {
+ if (KeyboardA11yModule.isEnabled()) {
+ val current: View? = currentFocus
+ if (current != null && FocusUtils.hasInvertedParent(current)) {
+ if (event.action == KeyEvent.ACTION_DOWN) {
+ val keyCode = event.keyCode
+ val isShiftPressed = event.isShiftPressed
+ val mapped = when (keyCode) {
+ // Invert DPAD vertical arrows for inverted lists
+ KeyEvent.KEYCODE_DPAD_DOWN -> KeyEvent.KEYCODE_DPAD_UP
+ KeyEvent.KEYCODE_DPAD_UP -> KeyEvent.KEYCODE_DPAD_DOWN
+ // Map Tab / Shift+Tab to vertical navigation as well
+ KeyEvent.KEYCODE_TAB ->
+ if (isShiftPressed) KeyEvent.KEYCODE_DPAD_UP else KeyEvent.KEYCODE_DPAD_DOWN
+ else -> keyCode
+ }
+ if (mapped != keyCode) {
+ val invertedEvent = KeyEvent(
+ event.downTime,
+ event.eventTime,
+ event.action,
+ mapped,
+ event.repeatCount,
+ event.metaState,
+ event.deviceId,
+ event.scanCode,
+ event.flags,
+ event.source
+ )
+ return super.dispatchKeyEvent(invertedEvent)
+ }
+ }
+ }
+ }
+ return super.dispatchKeyEvent(event)
+ }
+
override fun invokeDefaultOnBackPressed() {
moveTaskToBack(true)
}
-}
\ No newline at end of file
+}
diff --git a/android/app/src/main/java/chat/rocket/reactnative/a11y/KeyboardA11yModule.java b/android/app/src/main/java/chat/rocket/reactnative/a11y/KeyboardA11yModule.java
new file mode 100644
index 00000000000..68b4506ba44
--- /dev/null
+++ b/android/app/src/main/java/chat/rocket/reactnative/a11y/KeyboardA11yModule.java
@@ -0,0 +1,50 @@
+package chat.rocket.reactnative.a11y;
+
+import androidx.annotation.Nullable;
+
+import com.facebook.react.bridge.Promise;
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.bridge.WritableMap;
+import com.facebook.react.module.annotations.ReactModule;
+
+@ReactModule(name = KeyboardA11ySpec.NAME)
+public class KeyboardA11yModule extends KeyboardA11ySpec {
+
+ private static volatile boolean sEnabled = false;
+ @Nullable
+ private static volatile String sScope = null;
+
+ public KeyboardA11yModule(ReactApplicationContext reactContext) {
+ super(reactContext);
+ }
+
+ public static boolean isEnabled() {
+ return sEnabled;
+ }
+
+ @Nullable
+ public static String getScope() {
+ return sScope;
+ }
+
+ @Override
+ public void enable(String scope) {
+ sEnabled = true;
+ sScope = scope;
+ }
+
+ @Override
+ public void disable() {
+ sEnabled = false;
+ sScope = null;
+ }
+
+ @Override
+ public void getState(Promise promise) {
+ WritableMap state = com.facebook.react.bridge.Arguments.createMap();
+ state.putBoolean("enabled", sEnabled);
+ state.putString("scope", sScope);
+ promise.resolve(state);
+ }
+}
+
diff --git a/android/app/src/main/java/chat/rocket/reactnative/a11y/KeyboardA11ySpec.java b/android/app/src/main/java/chat/rocket/reactnative/a11y/KeyboardA11ySpec.java
new file mode 100644
index 00000000000..eb0bc0430f2
--- /dev/null
+++ b/android/app/src/main/java/chat/rocket/reactnative/a11y/KeyboardA11ySpec.java
@@ -0,0 +1,31 @@
+package chat.rocket.reactnative.a11y;
+
+import com.facebook.react.bridge.Promise;
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.bridge.ReactContextBaseJavaModule;
+import com.facebook.react.bridge.ReactMethod;
+import com.facebook.react.turbomodule.core.interfaces.TurboModule;
+
+public abstract class KeyboardA11ySpec extends ReactContextBaseJavaModule implements TurboModule {
+
+ public static final String NAME = "KeyboardA11y";
+
+ public KeyboardA11ySpec(ReactApplicationContext reactContext) {
+ super(reactContext);
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @ReactMethod
+ public abstract void enable(String scope);
+
+ @ReactMethod
+ public abstract void disable();
+
+ @ReactMethod
+ public abstract void getState(Promise promise);
+}
+
diff --git a/android/app/src/main/java/chat/rocket/reactnative/scroll/FocusUtils.java b/android/app/src/main/java/chat/rocket/reactnative/scroll/FocusUtils.java
new file mode 100644
index 00000000000..ec17d8e7cf5
--- /dev/null
+++ b/android/app/src/main/java/chat/rocket/reactnative/scroll/FocusUtils.java
@@ -0,0 +1,30 @@
+package chat.rocket.reactnative.scroll;
+
+import android.view.View;
+import android.view.ViewParent;
+import chat.rocket.reactnative.R;
+
+/**
+ * Utilities for focus-related queries inside custom scroll views.
+ */
+public final class FocusUtils {
+
+ private FocusUtils() {}
+
+ public static boolean hasInvertedParent(View view) {
+ if (view == null) {
+ return false;
+ }
+ ViewParent parent = view.getParent();
+ while (parent instanceof View) {
+ View parentView = (View) parent;
+ Object tag = parentView.getTag(R.id.tag_inverted_list);
+ if (tag instanceof Boolean && (Boolean) tag) {
+ return true;
+ }
+ parent = parentView.getParent();
+ }
+ return false;
+ }
+}
+
diff --git a/android/app/src/main/java/chat/rocket/reactnative/scroll/InvertedScrollContentView.java b/android/app/src/main/java/chat/rocket/reactnative/scroll/InvertedScrollContentView.java
index a4acb0c1e13..02003559c6d 100644
--- a/android/app/src/main/java/chat/rocket/reactnative/scroll/InvertedScrollContentView.java
+++ b/android/app/src/main/java/chat/rocket/reactnative/scroll/InvertedScrollContentView.java
@@ -1,9 +1,11 @@
package chat.rocket.reactnative.scroll;
+import android.graphics.Rect;
import android.view.View;
import com.facebook.react.views.view.ReactViewGroup;
import java.util.ArrayList;
import java.util.Collections;
+import chat.rocket.reactnative.R;
/**
* Content view for inverted FlatLists. Reports its children to accessibility in reversed order so
@@ -11,13 +13,67 @@
*/
public class InvertedScrollContentView extends ReactViewGroup {
+ private boolean mIsInvertedContent = false;
+
public InvertedScrollContentView(android.content.Context context) {
super(context);
}
+ public void setIsInvertedContent(boolean isInverted) {
+ mIsInvertedContent = isInverted;
+ if (isInverted) {
+ setTag(R.id.tag_inverted_list, true);
+ } else {
+ setTag(R.id.tag_inverted_list, null);
+ }
+ }
+
@Override
public void addChildrenForAccessibility(ArrayList outChildren) {
super.addChildrenForAccessibility(outChildren);
- Collections.reverse(outChildren);
+ if (mIsInvertedContent) {
+ Collections.reverse(outChildren);
+ }
+ }
+
+ @Override
+ protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
+ if (mIsInvertedContent) {
+ for (int i = getChildCount() - 1; i >= 0; i--) {
+ View child = getChildAt(i);
+ if (child.getVisibility() == VISIBLE) {
+ if (child.requestFocus(direction, previouslyFocusedRect)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
+ }
+
+ @Override
+ public void addFocusables(ArrayList views, int direction, int focusableMode) {
+ super.addFocusables(views, direction, focusableMode);
+ if (mIsInvertedContent) {
+ // Find indices of focusables that are children of this view
+ ArrayList childIndices = new ArrayList<>();
+ for (int i = 0; i < views.size(); i++) {
+ View v = views.get(i);
+ if (v.getParent() == this) {
+ childIndices.add(i);
+ }
+ }
+ // Reverse only the sublist of children focusables
+ int n = childIndices.size();
+ for (int i = 0; i < n / 2; i++) {
+ int idx1 = childIndices.get(i);
+ int idx2 = childIndices.get(n - 1 - i);
+ View temp = views.get(idx1);
+ views.set(idx1, views.get(idx2));
+ views.set(idx2, temp);
+ }
+ }
}
}
+
diff --git a/android/app/src/main/java/chat/rocket/reactnative/scroll/InvertedScrollContentViewManager.java b/android/app/src/main/java/chat/rocket/reactnative/scroll/InvertedScrollContentViewManager.java
index d30f9fc84c2..095c2463424 100644
--- a/android/app/src/main/java/chat/rocket/reactnative/scroll/InvertedScrollContentViewManager.java
+++ b/android/app/src/main/java/chat/rocket/reactnative/scroll/InvertedScrollContentViewManager.java
@@ -2,6 +2,7 @@
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.ThemedReactContext;
+import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.views.view.ReactViewManager;
/**
@@ -22,4 +23,9 @@ public String getName() {
public InvertedScrollContentView createViewInstance(ThemedReactContext context) {
return new InvertedScrollContentView(context);
}
+
+ @ReactProp(name = "isInvertedContent")
+ public void setIsInvertedContent(InvertedScrollContentView view, boolean isInverted) {
+ view.setIsInvertedContent(isInverted);
+ }
}
diff --git a/android/app/src/main/java/chat/rocket/reactnative/scroll/InvertedScrollView.java b/android/app/src/main/java/chat/rocket/reactnative/scroll/InvertedScrollView.java
index def585a7511..281dc32f87f 100644
--- a/android/app/src/main/java/chat/rocket/reactnative/scroll/InvertedScrollView.java
+++ b/android/app/src/main/java/chat/rocket/reactnative/scroll/InvertedScrollView.java
@@ -1,36 +1,137 @@
package chat.rocket.reactnative.scroll;
+import android.content.Context;
+import android.view.FocusFinder;
+import android.view.KeyEvent;
import android.view.View;
-import com.facebook.react.bridge.ReactContext;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import androidx.annotation.Nullable;
+import com.facebook.react.uimanager.util.ReactFindViewUtil;
import com.facebook.react.views.scroll.ReactScrollView;
-import java.util.ArrayList;
-import java.util.Collections;
-
-// When a FlatList is inverted (inverted={true}), React Native uses scaleY: -1 transform which
-// visually inverts the list but Android still reports children in array order. This view overrides
-// addChildrenForAccessibility to reverse the order so TalkBack matches the visual order.
+/**
+ * Custom ScrollView for inverted FlatLists that corrects keyboard navigation so it follows
+ * the visual order instead of the inverted view-tree order.
+ *
+ * Both Tab/Shift+Tab and DPAD arrows navigate between FlatList cells (direct children of the
+ * content view) to avoid loops caused by inner focusable elements within a single message.
+ * Boundary exit uses ReactFindViewUtil to find a tagged exit-target view by nativeID.
+ */
public class InvertedScrollView extends ReactScrollView {
- private boolean mIsInvertedVirtualizedList = false;
+ private boolean mKeyConsumed = false;
+ private @Nullable String mExitFocusNativeId;
- public InvertedScrollView(ReactContext context) {
+ public InvertedScrollView(Context context) {
super(context);
}
-
- // Set whether this ScrollView is used for an inverted virtualized list. When true, we reverse the
- // accessibility traversal order to match the visual order.
-
- public void setIsInvertedVirtualizedList(boolean isInverted) {
- mIsInvertedVirtualizedList = isInverted;
+ public void setExitFocusNativeId(@Nullable String nativeId) {
+ mExitFocusNativeId = nativeId;
}
@Override
- public void addChildrenForAccessibility(ArrayList outChildren) {
- super.addChildrenForAccessibility(outChildren);
- if (mIsInvertedVirtualizedList) {
- Collections.reverse(outChildren);
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ int keyCode = event.getKeyCode();
+
+ if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN || keyCode == KeyEvent.KEYCODE_DPAD_UP) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ boolean isForward = (keyCode == KeyEvent.KEYCODE_DPAD_DOWN);
+ mKeyConsumed = handleCellNavigation(isForward);
+ return mKeyConsumed;
+ }
+ return mKeyConsumed;
+ }
+
+ if (keyCode == KeyEvent.KEYCODE_TAB) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ boolean isForward = !event.isShiftPressed();
+ mKeyConsumed = handleCellNavigation(isForward);
+ return mKeyConsumed;
+ }
+ return mKeyConsumed;
+ }
+
+ return super.dispatchKeyEvent(event);
+ }
+
+ /**
+ * Shared navigation logic for Tab and DPAD.
+ * @param isForward true = visual down (Tab / DPAD_DOWN), false = visual up (Shift+Tab / DPAD_UP)
+ */
+ private boolean handleCellNavigation(boolean isForward) {
+ View focused = findFocus();
+ if (focused == null || getChildCount() == 0) {
+ return false;
+ }
+
+ ViewGroup contentView = (ViewGroup) getChildAt(0);
+ int cellIndex = findContainingCellIndex(contentView, focused);
+ if (cellIndex < 0) {
+ return false;
+ }
+
+ int step = isForward ? -1 : 1;
+ int focusDir = isForward ? View.FOCUS_UP : View.FOCUS_DOWN;
+
+ for (int i = cellIndex + step; i >= 0 && i < contentView.getChildCount(); i += step) {
+ View cell = contentView.getChildAt(i);
+ if (cell != null && cell.getVisibility() == VISIBLE && cell.requestFocus(focusDir)) {
+ return true;
+ }
+ }
+
+ int exitDir = isForward ? View.FOCUS_DOWN : View.FOCUS_UP;
+ View exitTarget = findExitTarget(exitDir);
+ if (exitTarget != null) {
+ exitTarget.requestFocus();
+ return true;
+ }
+
+ return true;
+ }
+
+ private int findContainingCellIndex(ViewGroup contentView, View focused) {
+ View current = focused;
+ while (current != null && current.getParent() != contentView) {
+ ViewParent p = current.getParent();
+ if (p instanceof View) {
+ current = (View) p;
+ } else {
+ return -1;
+ }
+ }
+ return current != null ? contentView.indexOfChild(current) : -1;
+ }
+
+ private View findExitTarget(int direction) {
+ if (mExitFocusNativeId != null) {
+ View target = ReactFindViewUtil.findView(getRootView(), mExitFocusNativeId);
+ if (target != null) {
+ return target;
+ }
+ }
+ View rootView = getRootView();
+ if (!(rootView instanceof ViewGroup)) {
+ return null;
+ }
+ View target = FocusFinder.getInstance()
+ .findNextFocus((ViewGroup) rootView, this, direction);
+ if (target != null && !isDescendantOf(target, this)) {
+ return target;
+ }
+ return null;
+ }
+
+ private static boolean isDescendantOf(View view, ViewGroup ancestor) {
+ ViewParent parent = view.getParent();
+ while (parent != null) {
+ if (parent == ancestor) {
+ return true;
+ }
+ parent = parent.getParent();
}
+ return false;
}
}
diff --git a/android/app/src/main/java/chat/rocket/reactnative/scroll/InvertedScrollViewManager.java b/android/app/src/main/java/chat/rocket/reactnative/scroll/InvertedScrollViewManager.java
index 453dd009ec0..30ad13084d7 100644
--- a/android/app/src/main/java/chat/rocket/reactnative/scroll/InvertedScrollViewManager.java
+++ b/android/app/src/main/java/chat/rocket/reactnative/scroll/InvertedScrollViewManager.java
@@ -1,7 +1,9 @@
package chat.rocket.reactnative.scroll;
+import androidx.annotation.Nullable;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.ThemedReactContext;
+import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.views.scroll.ReactScrollViewManager;
/**
@@ -23,4 +25,9 @@ public String getName() {
public InvertedScrollView createViewInstance(ThemedReactContext context) {
return new InvertedScrollView(context);
}
+
+ @ReactProp(name = "exitFocusNativeId")
+ public void setExitFocusNativeId(InvertedScrollView view, @Nullable String nativeId) {
+ view.setExitFocusNativeId(nativeId);
+ }
}
diff --git a/android/app/src/main/res/values/ids.xml b/android/app/src/main/res/values/ids.xml
new file mode 100644
index 00000000000..bab9bf66bef
--- /dev/null
+++ b/android/app/src/main/res/values/ids.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/containers/Button/Button.test.tsx b/app/containers/Button/Button.test.tsx
index 1f63f7d4976..5c98db24f85 100644
--- a/app/containers/Button/Button.test.tsx
+++ b/app/containers/Button/Button.test.tsx
@@ -64,8 +64,6 @@ describe('ButtonTests', () => {
test('disabled button is in disabled state', async () => {
const { findByTestId } = render();
const button = await findByTestId(testProps.testID);
- // In the test environment, RNGH Pressable may still invoke onPress when disabled,
- // so we assert the button is in a disabled state (enabled={false}).
expect(button.props.enabled).toBe(false);
});
diff --git a/app/containers/Button/__snapshots__/Button.test.tsx.snap b/app/containers/Button/__snapshots__/Button.test.tsx.snap
index 4c0bdb49306..c514b1ae90b 100644
--- a/app/containers/Button/__snapshots__/Button.test.tsx.snap
+++ b/app/containers/Button/__snapshots__/Button.test.tsx.snap
@@ -4,43 +4,56 @@ exports[`Story Snapshots: CustomButton should match snapshot 1`] = `
+
+
+
+
+
+
+
{
title: string;
onPress: () => void;
type?: 'primary' | 'secondary';
@@ -17,6 +17,7 @@ interface IButtonProps extends PressableProps {
style?: StyleProp | StyleProp[];
styleText?: StyleProp | StyleProp[];
small?: boolean;
+ disabled?: boolean;
}
const styles = StyleSheet.create({
@@ -87,15 +88,19 @@ const Button: React.FC = ({
];
return (
-
{loading ? : {title}}
-
+
);
};
diff --git a/app/containers/LoginServices/__snapshots__/LoginServices.test.tsx.snap b/app/containers/LoginServices/__snapshots__/LoginServices.test.tsx.snap
index e36536132bb..29e20c77aef 100644
--- a/app/containers/LoginServices/__snapshots__/LoginServices.test.tsx.snap
+++ b/app/containers/LoginServices/__snapshots__/LoginServices.test.tsx.snap
@@ -5,42 +5,54 @@ exports[`Story Snapshots: Separators should match snapshot 1`] = `
+
+
void;
getText: () => string;
setInput: TSetInput;
+ focus: () => void;
}
export interface IMessageComposerContainerProps {
diff --git a/app/containers/RoomHeader/RoomHeader.tsx b/app/containers/RoomHeader/RoomHeader.tsx
index 35a44f5181f..68c319a0963 100644
--- a/app/containers/RoomHeader/RoomHeader.tsx
+++ b/app/containers/RoomHeader/RoomHeader.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { StyleSheet, Text, useWindowDimensions, View } from 'react-native';
+import { AccessibilityInfo, findNodeHandle, StyleSheet, Text, useWindowDimensions, View } from 'react-native';
import { TouchableOpacity } from 'react-native-gesture-handler';
import { useResponsiveLayout } from '../../lib/hooks/useResponsiveLayout/useResponsiveLayout';
@@ -83,6 +83,10 @@ interface IRoomHeader {
abacAttributes?: ISubscription['abacAttributes'];
}
+export interface IRoomHeaderRef {
+ focus: () => void;
+}
+
const SubTitle = React.memo(({ usersTyping, subtitle, renderFunc, scale }: TRoomHeaderSubTitle) => {
const { colors } = useTheme();
const fontSize = getSubTitleSize(scale);
@@ -131,27 +135,44 @@ const HeaderTitle = React.memo(({ title, tmid, prid, scale, testID }: TRoomHeade
return ;
});
-const Header = React.memo(
- ({
- title,
- subtitle,
- parentTitle,
- type,
- status,
- width,
- height,
- roomUserId,
- prid,
- tmid,
- onPress,
- isGroupChat,
- teamMain,
- testID,
- usersTyping = [],
- sourceType,
- disabled,
- abacAttributes
- }: IRoomHeader) => {
+const Header = React.forwardRef(
+ (
+ {
+ title,
+ subtitle,
+ parentTitle,
+ type,
+ status,
+ width,
+ height,
+ roomUserId,
+ prid,
+ tmid,
+ onPress,
+ isGroupChat,
+ teamMain,
+ testID,
+ usersTyping = [],
+ sourceType,
+ disabled,
+ abacAttributes
+ }: IRoomHeader,
+ ref
+ ) => {
+ const headerRef = React.useRef(null);
+ React.useImperativeHandle(
+ ref,
+ () => ({
+ focus: () => {
+ const nodeHandle = headerRef.current ? findNodeHandle(headerRef.current) : null;
+ if (nodeHandle) {
+ AccessibilityInfo.setAccessibilityFocus(nodeHandle);
+ }
+ }
+ }),
+ []
+ );
+
const statusAccessibilityLabel = useStatusAccessibilityLabel({
isGroupChat,
prid,
@@ -197,6 +218,7 @@ const Header = React.memo(
return (
{
- let subtitle: string | undefined;
- let statusVisitor: TUserStatus | undefined;
- let statusText: string | undefined;
- const { width, height } = useResponsiveLayout();
+ React.forwardRef(
+ (
+ {
+ isGroupChat,
+ onPress,
+ parentTitle,
+ prid,
+ roomUserId,
+ subtitle: subtitleProp,
+ teamMain,
+ testID,
+ title,
+ tmid,
+ type,
+ sourceType,
+ visitor,
+ disabled,
+ abacAttributes
+ }: IRoomHeaderContainerProps,
+ ref
+ ) => {
+ let subtitle: string | undefined;
+ let statusVisitor: TUserStatus | undefined;
+ let statusText: string | undefined;
+ const { width, height } = useResponsiveLayout();
- const connecting = useSelector((state: IApplicationState) => state.meteor.connecting || state.server.loading);
- const usersTyping = useSelector((state: IApplicationState) => state.usersTyping, shallowEqual);
- const connected = useSelector((state: IApplicationState) => state.meteor.connected);
- const activeUser = useSelector(
- (state: IApplicationState) => (roomUserId ? state.activeUsers?.[roomUserId] : undefined),
- shallowEqual
- );
+ const connecting = useSelector((state: IApplicationState) => state.meteor.connecting || state.server.loading);
+ const usersTyping = useSelector((state: IApplicationState) => state.usersTyping, shallowEqual);
+ const connected = useSelector((state: IApplicationState) => state.meteor.connected);
+ const activeUser = useSelector(
+ (state: IApplicationState) => (roomUserId ? state.activeUsers?.[roomUserId] : undefined),
+ shallowEqual
+ );
- if (connecting) {
- subtitle = I18n.t('Connecting');
- } else if (!connected) {
- subtitle = I18n.t('Waiting_for_network');
- } else {
- subtitle = subtitleProp;
- }
+ if (connecting) {
+ subtitle = I18n.t('Connecting');
+ } else if (!connected) {
+ subtitle = I18n.t('Waiting_for_network');
+ } else {
+ subtitle = subtitleProp;
+ }
- if (connected) {
- if ((type === 'd' || (tmid && roomUserId)) && activeUser) {
- const { statusText: statusTextActiveUser } = activeUser;
- statusText = statusTextActiveUser;
- } else if (type === 'l' && visitor?.status) {
- ({ status: statusVisitor } = visitor);
+ if (connected) {
+ if ((type === 'd' || (tmid && roomUserId)) && activeUser) {
+ const { statusText: statusTextActiveUser } = activeUser;
+ statusText = statusTextActiveUser;
+ } else if (type === 'l' && visitor?.status) {
+ ({ status: statusVisitor } = visitor);
+ }
}
- }
- return (
-
- );
- }
+ return (
+
+ );
+ }
+ )
);
export default RoomHeaderContainer;
+export type { IRoomHeaderRef };
diff --git a/app/containers/UIKit/__snapshots__/UiKitMessage.test.tsx.snap b/app/containers/UIKit/__snapshots__/UiKitMessage.test.tsx.snap
index 7afc636e519..5e2e329aa8b 100644
--- a/app/containers/UIKit/__snapshots__/UiKitMessage.test.tsx.snap
+++ b/app/containers/UIKit/__snapshots__/UiKitMessage.test.tsx.snap
@@ -6,42 +6,54 @@ exports[`Story Snapshots: ActionButton should match snapshot 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{
onPress={reactionInit}
key='message-add-reaction'
testID='message-add-reaction'
+ accessibilityRole='button'
+ accessibilityLabel='Add reaction'
style={[styles.reactionButton, { backgroundColor: themes[theme].surfaceRoom }]}
hitSlop={BUTTON_HIT_SLOP}>
@@ -61,6 +63,9 @@ const Reaction = React.memo(({ reaction, getCustomEmoji, theme }: IMessageReacti
onLongPress={onReactionLongPress}
key={reaction.emoji}
testID={`message-reaction-${reaction.emoji}`}
+ accessibilityRole='button'
+ accessibilityLabel={`${reaction.emoji}, ${reaction.usernames.length}`}
+ accessibilityState={{ selected: reacted }}
style={[styles.reactionButton, { backgroundColor: reacted ? themes[theme].surfaceNeutral : themes[theme].surfaceRoom }]}
hitSlop={BUTTON_HIT_SLOP}>
{
'use memo';
const { theme, colors } = useTheme();
- const { threadBadgeColor, toggleFollowThread, user, replies } = useContext(MessageContext);
+ const { threadBadgeColor, toggleFollowThread, user, replies, onThreadPress } = useContext(MessageContext);
const backgroundColor = threadBadgeColor ? colors.badgeBackgroundLevel2 : colors.buttonBackgroundSecondaryDefault;
const textColor = threadBadgeColor || theme !== 'light' ? colors.fontWhite : colors.fontPureBlack;
@@ -24,9 +25,14 @@ const Thread = React.memo(
return (
-
+
{I18n.t('View_Thread')}
-
+
void;
}
+const KeyboardPressable = withKeyboardFocus(Pressable);
+
const RCTouchable: React.FC = React.memo(({ children, ...props }) => {
'use memo';
const { onLongPress } = useContext(MessageContext);
+ const { onHoverIn, onHoverOut, ...rest } = props;
return (
-
+
{children}
-
+
);
});
diff --git a/app/containers/message/__snapshots__/Message.test.tsx.snap b/app/containers/message/__snapshots__/Message.test.tsx.snap
index eb164c03554..c498fed247d 100644
--- a/app/containers/message/__snapshots__/Message.test.tsx.snap
+++ b/app/containers/message/__snapshots__/Message.test.tsx.snap
@@ -1264,181 +1264,206 @@ exports[`Story Snapshots: AttachmentWithTextAndLink should match snapshot 1`] =
}
}
>
-
-
- Rocket.Chat
-
-
-
-
+ Rocket.Chat
+
+
+
- Rocket.Chat
-
-
- , the best open source chat
+
+ Rocket.Chat
+
+
+ , the best open source chat
+
-
+
-
+
-
-
+
-
+
-
+
-
-
-
-
+
+
+
+
+ />
+
-
+
@@ -2004,181 +2054,206 @@ exports[`Story Snapshots: AttachmentWithTextAndLinkLargeFont should match snapsh
}
}
>
-
-
- Rocket.Chat
-
-
-
-
+ Rocket.Chat
+
+
+
- Rocket.Chat
-
-
- , the best open source chat
+
+ Rocket.Chat
+
+
+ , the best open source chat
+
-
+
-
+
-
-
+
-
+
-
+
-
-
-
-
+
+
+
+
+ />
+
-
+
@@ -8658,117 +8758,142 @@ exports[`Story Snapshots: Broadcast should match snapshot 1`] = `
}
}
>
-
-
-
-
-
+
- Reply
-
+
+
+
+
+ Reply
+
+
-
+
@@ -9219,117 +9344,142 @@ exports[`Story Snapshots: BroadcastLargeFont should match snapshot 1`] = `
}
}
>
-
-
-
-
-
+
- Reply
-
+
+
+
+
+ Reply
+
+
-
+
@@ -9779,150 +9929,175 @@ exports[`Story Snapshots: CollapsedAttachments should match snapshot 1`] = `
}
}
>
-
+
+
+ Title collapsed
+
+
+
+
- Title collapsed
+
-
-
-
-
-
-
+
@@ -10307,178 +10482,132 @@ exports[`Story Snapshots: CollapsedAttachments should match snapshot 1`] = `
}
}
>
-
-
- Title collapsed
-
-
-
-
-
- Title collapsed
-
+ Title collapsed
-
-
-
+
- Field 1
+
+
+ Title collapsed
+
+
+
+
+ Field 1
+
+
- Value 1
+
+ Value 1
+
-
+
-
-
-
- Field 2
-
-
+ Field 2
+
+
- Value 2
+
+ Value 2
+
-
+
-
+
-
+
+
+ Title collapsed
+
+
+
+
- Title collapsed
+
-
-
-
-
-
-
+
@@ -11682,178 +11907,132 @@ exports[`Story Snapshots: CollapsedAttachmentsLargeFont should match snapshot 1`
}
}
>
-
-
- Title collapsed
-
-
-
-
-
- Title collapsed
-
+ Title collapsed
-
-
-
+
- Field 1
+
+
+ Title collapsed
+
+
+
+
+ Field 1
+
+
- Value 1
+
+ Value 1
+
-
+
-
-
-
- Field 2
-
-
+ Field 2
+
+
- Value 2
+
+ Value 2
+
-
+
-
+
-
+
+
+ Collapsed attachment block
+
+
+
+
- Collapsed attachment block
+
-
-
-
-
-
-
+
@@ -13059,178 +13334,132 @@ exports[`Story Snapshots: CollapsibleAttachmentWithText should match snapshot 1`
}
}
>
-
-
- Collapsed attachment block
-
-
-
-
-
- This attachment text should NOT appear as plain text above the message or duplicate before the block.
-
+ Collapsed attachment block
-
-
-
+
- Field 1
+
+
+ This attachment text should NOT appear as plain text above the message or duplicate before the block.
+
+
+
+
+ Field 1
+
+
- Value 1
+
+ Value 1
+
-
+
-
-
-
- Field 2
-
-
+ Field 2
+
+
- Value 2
+
+ Value 2
+
-
+
-
-
-
- Long field
-
-
+ Long field
+
+
- This field value could also contribute to duplicate text when expanded.
+
+ This field value could also contribute to duplicate text when expanded.
+
-
+
-
+
-
+
+
+ Collapsed attachment block
+
+
+
+
- Collapsed attachment block
+
-
-
-
-
-
-
+
@@ -14577,178 +14902,132 @@ exports[`Story Snapshots: CollapsibleAttachmentWithTextLargeFont should match sn
}
}
>
-
-
- Collapsed attachment block
-
-
-
-
-
- This attachment text should NOT appear as plain text above the message or duplicate before the block.
-
+ Collapsed attachment block
-
-
-
+
- Field 1
+
+
+ This attachment text should NOT appear as plain text above the message or duplicate before the block.
+
+
+
+
+ Field 1
+
+
- Value 1
+
+ Value 1
+
-
+
-
-
-
- Field 2
-
-
+ Field 2
+
+
- Value 2
+
+ Value 2
+
-
+
-
-
-
- Long field
-
-
+ Long field
+
+
- This field value could also contribute to duplicate text when expanded.
+
+ This field value could also contribute to duplicate text when expanded.
+
-
+
-
+
-
-
+
-
- Field 1
-
-
+ Field 1
+
+
- Value 1
+
+ Value 1
+
-
+
-
-
-
- Field 2
-
-
+ Field 2
+
+
- Value 2
+
+ Value 2
+
-
+
-
+
-
-
+
-
- Field 1
-
-
+ Field 1
+
+
- Value 1
+
+ Value 1
+
-
+
-
-
-
- Field 2
-
-
+ Field 2
+
+
- Value 2
+
+ Value 2
+
-
+
-
+
-
-
+
-
- Field 1
-
-
+ Field 1
+
+
- Value 1
+
+ Value 1
+
-
+
-
-
-
- Field 2
-
-
+ Field 2
+
+
- Value 2
+
+ Value 2
+
-
+
-
+
@@ -16716,129 +17141,128 @@ exports[`Story Snapshots: ColoredAttachmentsLargeFont should match snapshot 1`]
}
}
>
-
-
+
-
- Field 1
-
-
+ Field 1
+
+
- Value 1
+
+ Value 1
+
-
+
-
-
-
- Field 2
-
-
+ Field 2
+
+
- Value 2
+
+ Value 2
+
-
+
-
+
-
-
+
-
- Field 1
-
-
+ Field 1
+
+
- Value 1
+
+ Value 1
+
-
+
-
-
-
- Field 2
-
-
+ Field 2
+
+
- Value 2
+
+ Value 2
+
-
+
-
+
-
-
+
-
- Field 1
-
-
+ Field 1
+
+
- Value 1
+
+ Value 1
+
-
+
-
-
-
- Field 2
-
-
+ Field 2
+
+
- Value 2
+
+ Value 2
+
-
+
-
+
@@ -17919,179 +18419,133 @@ exports[`Story Snapshots: CustomFields should match snapshot 1`] = `
}
}
>
-
-
- rocket.cat
-
-
-
-
-
- Custom fields
-
+ rocket.cat
-
-
-
+
- Field 1
+
+
+ Custom fields
+
+
+
+
+ Field 1
+
+
- Value 1
+
+ Value 1
+
-
+
-
-
-
- Field 2
-
-
+ Field 2
+
+
- Value 2
+
+ Value 2
+
-
+
-
+
-
-
- rocket.cat
-
-
-
-
-
- Custom fields
-
+ rocket.cat
-
-
-
+
- Field 1
+
+
+ Custom fields
+
+
+
+
+ Field 1
+
+
- Value 1
+
+ Value 1
+
-
+
-
-
-
- Field 2
-
-
+ Field 2
+
+
- Value 2
+
+ Value 2
+
-
+
-
-
-
- Field 3
-
-
+ Field 3
+
+
- Value 3
+
+ Value 3
+
-
+
-
-
-
- Field 4
-
-
+ Field 4
+
+
- Value 4
+
+ Value 4
+
-
+
-
-
-
- Field 5
-
-
+ Field 5
+
+
- Value 5
+
+ Value 5
+
-
+
-
+
-
-
- rocket.cat
-
-
-
-
-
- Custom fields
-
+ rocket.cat
-
-
-
+
- Field 1
+
+
+ Custom fields
+
+
+
+
+ Field 1
+
+
- Value 1
+
+ Value 1
+
-
+
-
-
-
- Field 2
-
-
+ Field 2
+
+
- Value 2
+
+ Value 2
+
-
+
-
+
-
-
- rocket.cat
-
-
-
-
+ rocket.cat
+
+
+
- Custom fields
+
+ Custom fields
+
-
-
-
+
-
- Field 1
-
-
+ Field 1
+
+
- Value 1
+
+ Value 1
+
-
+
-
-
-
- Field 2
-
-
+ Field 2
+
+
- Value 2
+
+ Value 2
+
-
+
-
-
-
- Field 3
-
-
+ Field 3
+
+
- Value 3
+
+ Value 3
+
-
+
-
-
-
- Field 4
-
-
+ Field 4
+
+
- Value 4
+
+ Value 4
+
-
+
-
-
-
- Field 5
-
-
+ Field 5
+
+
- Value 5
+
+ Value 5
+
-
+
-
+
-
-
-
-
-
+
- No messages yet
-
+
+
+
+
+ No messages yet
+
+
-
+
-
-
-
-
-
+
- 1 message
-
+
+
+
+
+ 1 message
+
+
-
+
-
-
-
-
-
+
- 10 messages
-
+
+
+
+
+ 10 messages
+
+
-
+
-
-
-
-
-
+
- +999 messages
-
+
+
+
+
+ +999 messages
+
+
-
+
-
-
-
-
-
+
- No messages yet
-
+
+
+
+
+ No messages yet
+
+
-
+
-
-
-
-
-
+
- 1 message
-
+
+
+
+
+ 1 message
+
+
-
+
-
-
-
-
-
+
- 10 messages
-
+
+
+
+
+ 10 messages
+
+
-
+
-
-
-
-
-
+
- +999 messages
-
+
+
+
+
+ +999 messages
+
+
-
+
-
+
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
@@ -38700,77 +39550,102 @@ exports[`Story Snapshots: Encrypted should match snapshot 1`] = `
}
}
>
-
+
-
-
-
-
+
+
+
+
+
-
-
- 😂
-
-
- 1
-
+
+ 😂
+
+
+ 1
+
+
-
-
+
-
-
+
- 1
-
+
+
+ 1
+
+
-
-
+
-
- 🤔
-
-
- 1
-
+
+ 🤔
+
+
+ 1
+
+
-
-
+
-
-
-
+
+
+
+
-
+
@@ -40013,77 +40996,102 @@ exports[`Story Snapshots: Encrypted should match snapshot 1`] = `
}
}
>
-
+
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
@@ -43116,77 +44299,102 @@ exports[`Story Snapshots: EncryptedLargeFont should match snapshot 1`] = `
}
}
>
-
+
-
-
-
-
+
+
+
+
+
-
-
- 😂
-
-
- 1
-
+
+ 😂
+
+
+ 1
+
+
-
-
+
-
-
+
- 1
-
+
+
+ 1
+
+
-
-
+
-
- 🤔
-
-
- 1
-
+
+ 🤔
+
+
+ 1
+
+
-
-
+
-
-
-
+
+
+
+
-
+
@@ -44429,77 +45745,102 @@ exports[`Story Snapshots: EncryptedLargeFont should match snapshot 1`] = `
}
}
>
-
+
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
+
@@ -47396,77 +48912,102 @@ exports[`Story Snapshots: ErrorLargeFont should match snapshot 1`] = `
}
}
>
-
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
+
@@ -48155,129 +49721,154 @@ exports[`Story Snapshots: FileAttachmentsWithFilenames should match snapshot 1`]
}
}
>
-
+
+
+ test.py
+
+
test.py
-
- test.py
-
-
+
-
+
+
+ Component.tsx
+
+
Component.tsx
-
- Component.tsx
-
-
+
-
+
+
+ config.json
+
+
config.json
-
- config.json
-
-
+
-
-
- main.go
-
-
-
-
+ main.go
+
+
+
-
- This is the
-
- main
+ This is the
-
-
- entry point for the application
+ "backgroundColor": "transparent",
+ "fontFamily": "Inter",
+ "fontWeight": "700",
+ "textAlign": "left",
+ }
+ }
+ >
+
+ main
+
+
+
+ entry point for the application
+
-
+
-
+
-
+
+
+ document.pdf
+
+
document.pdf
-
- document.pdf
-
-
+
-
+
+
+ image.png
+
+
image.png
-
- image.png
-
-
+
@@ -50917,129 +52633,154 @@ exports[`Story Snapshots: FileAttachmentsWithFilenamesLargeFont should match sna
}
}
>
-
+
+
+ test.py
+
+
test.py
-
- test.py
-
-
+
-
+
+
+ Component.tsx
+
+
Component.tsx
-
- Component.tsx
-
-
+
-
+
+
+ config.json
+
+
config.json
-
- config.json
-
-
+
-
-
- main.go
-
-
-
-
+ main.go
+
+
+
-
- This is the
-
- main
+ This is the
-
-
- entry point for the application
+ "backgroundColor": "transparent",
+ "fontFamily": "Inter",
+ "fontWeight": "700",
+ "textAlign": "left",
+ }
+ }
+ >
+
+ main
+
+
+
+ entry point for the application
+
-
+
-
+
-
+
+
+ document.pdf
+
+
document.pdf
-
- document.pdf
-
-
+
-
+
+
+ image.png
+
+
image.png
-
- image.png
-
-
+
@@ -53666,141 +55532,166 @@ exports[`Story Snapshots: FileAttachmentsWithFilenamesLargeFont should match sna
}
}
>
-
-
-
+
- File.pdf
+
+ File.pdf
+
-
+
-
+
-
-
-
+
- File.pdf
+
+ File.pdf
+
-
+
-
+
-
+
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
+
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
+
-
+
+
+
+
+
+
+
-
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
-
-
- rocket.cat
-
-
- 10:00 AM
-
+
+ rocket.cat
+
+
+ 10:00 AM
+
+
-
+
@@ -78171,111 +80562,128 @@ exports[`Story Snapshots: MessageWithNestedReplyAndFile should match snapshot 1`
}
}
>
-
-
- rocket.cat
-
-
-
+
+ rocket.cat
+
+
-
- Nested image from forwarded message
+
+ Nested image from forwarded message
+
-
-
-
-
+
-
-
-
+
+
+
+
+
+
+
+
-
-
+ "bottom": 8,
+ "flexDirection": "row",
+ "gap": 8,
+ "position": "absolute",
+ "right": 8,
+ }
+ }
+ />
+
-
-
+
-
+
@@ -81658,162 +84099,187 @@ exports[`Story Snapshots: MessageWithReply should match snapshot 1`] = `
}
}
>
-
-
- I'm a very long long title and I'll break
-
-
-
-
+ I'm a very long long title and I'll break
+
+
+
- How are you?
+
+ How are you?
+
-
+
-
+
-
-
- rocket.cat
-
-
-
-
+ rocket.cat
+
+
+
- How are you?
-
-
-
+ How are you?
+
+
-
+ >
+
+
+
-
+
-
+
-
-
- rocket.cat
-
-
-
-
+ rocket.cat
+
+
+
- How are you?
-
-
-
+ How are you?
+
+
-
+ >
+
+
+
-
+
-
+
-
-
- rocket.cat
-
-
-
+
+ rocket.cat
+
+
-
- What you think about this one?
+
+ What you think about this one?
+
-
-
-
-
+
-
-
-
+
+
+
+
+
+
+
+
-
-
+ "bottom": 8,
+ "flexDirection": "row",
+ "gap": 8,
+ "position": "absolute",
+ "right": 8,
+ }
+ }
+ />
+
-
-
+
-
+
@@ -83999,111 +86565,128 @@ exports[`Story Snapshots: MessageWithReply should match snapshot 1`] = `
}
}
>
-
-
- rocket.cat
-
-
-
+
+ rocket.cat
+
+
-
- Are you seeing this mario
-
-
-
+ Are you seeing this mario
+
+
-
-
- ?
+ >
+
+
+
+ ?
+
-
-
-
-
+
-
-
-
+
+
+
+
+
+
+
+
-
-
+ "bottom": 8,
+ "flexDirection": "row",
+ "gap": 8,
+ "position": "absolute",
+ "right": 8,
+ }
+ }
+ />
+
-
-
+
-
+
@@ -84789,149 +87405,174 @@ exports[`Story Snapshots: MessageWithReplyAndFile should match snapshot 1`] = `
}
}
>
-
-
- rocket.cat
-
+
+ rocket.cat
+
+
+ script.sh
+
+
script.sh
-
- script.sh
-
-
+
-
-
- rocket.cat
-
-
- config.yaml
-
-
-
-
+ rocket.cat
+
+
+ config.yaml
+
+
+
-
- This is a configuration file with
-
- important
+ This is a configuration file with
-
-
- settings
+ "backgroundColor": "transparent",
+ "fontFamily": "Inter",
+ "fontWeight": "700",
+ "textAlign": "left",
+ }
+ }
+ >
+
+ important
+
+
+
+ settings
+
-
+
-
+
-
-
- rocket.cat
-
+
+ rocket.cat
+
+
+ index.ts
+
+
index.ts
-
- index.ts
-
-
+
-
-
- rocket.cat
-
+
+ rocket.cat
+
+
+ styles.css
+
+
styles.css
-
- styles.css
-
-
+
-
-
- rocket.cat
-
+
+ rocket.cat
+
+
+ script.sh
+
+
script.sh
-
- script.sh
-
-
+
-
-
- rocket.cat
-
-
- config.yaml
-
-
-
-
+ rocket.cat
+
+
+ config.yaml
+
+
+
-
- This is a configuration file with
-
- important
+ This is a configuration file with
-
-
- settings
+ "backgroundColor": "transparent",
+ "fontFamily": "Inter",
+ "fontWeight": "700",
+ "textAlign": "left",
+ }
+ }
+ >
+
+ important
+
+
+
+ settings
+
-
+
-
+
-
-
- rocket.cat
-
+
+ rocket.cat
+
+
+ index.ts
+
+
index.ts
-
- index.ts
-
-
+
-
-
- rocket.cat
-
+
+ rocket.cat
+
+
+ styles.css
+
+
styles.css
-
- styles.css
-
-
+
-
-
- I'm a very long long title and I'll break
-
-
-
-
+ I'm a very long long title and I'll break
+
+
+
- How are you?
+
+ How are you?
+
-
+
-
+
-
-
- rocket.cat
-
-
-
-
+ rocket.cat
+
+
+
- How are you?
-
-
-
+ How are you?
+
+
-
+ >
+
+
+
-
+
-
+
-
-
- rocket.cat
-
-
-
+
+ rocket.cat
+
+
-
- What you think about this one?
+
+ What you think about this one?
+
-
-
-
-
+
-
-
-
+
+
+
+
+
+
+
+
-
-
+ "bottom": 8,
+ "flexDirection": "row",
+ "gap": 8,
+ "position": "absolute",
+ "right": 8,
+ }
+ }
+ />
+
-
-
+
-
+
@@ -90871,111 +93787,128 @@ exports[`Story Snapshots: MessageWithReplyLargeFont should match snapshot 1`] =
}
}
>
-
-
- rocket.cat
-
-
-
+
+ rocket.cat
+
+
-
- Are you seeing this mario
-
-
-
+ Are you seeing this mario
+
+
-
-
- ?
+ >
+
+
+
+ ?
+
-
-
-
-
+
-
-
-
+
+
+
+
+
+
+
+
-
-
+ "bottom": 8,
+ "flexDirection": "row",
+ "gap": 8,
+ "position": "absolute",
+ "right": 8,
+ }
+ }
+ />
+
-
-
+
-
+
@@ -91714,44 +94680,100 @@ exports[`Story Snapshots: MessageWithThread should match snapshot 1`] = `
}
}
>
-
-
- View thread
-
-
+
+ View thread
+
+
+
-
-
- View thread
-
-
+
+ View thread
+
+
+
-
-
- 😂
-
-
- 1
-
+
+ 😂
+
+
+ 1
+
+
-
-
+
-
-
+
- 99
-
+
+
+ 99
+
+
-
-
+
-
- 🤔
-
-
- 999
-
+
+ 🤔
+
+
+ 999
+
+
-
-
+
-
- 🤔
-
-
- 9999
-
+
+ 🤔
+
+
+ 9999
+
+
-
-
+
-
-
-
+
+
+
+
-
+
@@ -100049,978 +103262,1221 @@ exports[`Story Snapshots: Reactions should match snapshot 1`] = `
}
}
>
-
-
-
+
- 1
-
+
+
+ 1
+
+
-
-
+
-
-
+
- 1
-
+
+
+ 1
+
+
-
-
+
-
-
+
- 1
-
+
+
+ 1
+
+
-
-
+
-
- ❤️
-
-
- 1
-
+
+ ❤️
+
+
+ 1
+
+
-
-
+
-
- 🐶
-
-
- 1
-
+
+ 🐶
+
+
+ 1
+
+
-
-
+
-
- 😀
-
-
- 1
-
+
+ 😀
+
+
+ 1
+
+
-
-
+
-
- 😬
-
-
- 1
-
+
+ 😬
+
+
+ 1
+
+
-
-
+
-
- 😁
-
-
- 1
-
+
+ 😁
+
+
+ 1
+
+
-
-
+
-
-
-
+
+
+
+
-
+
@@ -101472,530 +104928,665 @@ exports[`Story Snapshots: ReactionsLargeFont should match snapshot 1`] = `
}
}
>
-
-
- 😂
-
-
- 1
-
+
+ 😂
+
+
+ 1
+
+
-
-
+
-
-
+
- 99
-
+
+
+ 99
+
+
-
-
+
-
- 🤔
-
-
- 999
-
+
+ 🤔
+
+
+ 999
+
+
-
-
+
-
- 🤔
-
-
- 9999
-
+
+ 🤔
+
+
+ 9999
+
+
-
-
+
-
-
-
+
+
+
+
-
+
@@ -102434,978 +106025,1221 @@ exports[`Story Snapshots: ReactionsLargeFont should match snapshot 1`] = `
}
}
>
-
-
-
+
- 1
-
+
+
+ 1
+
+
-
-
+
-
-
+
- 1
-
+
+
+ 1
+
+
-
-
+
+
-
-
-
+
- 1
-
+
+
+ 1
+
+
-
-
+
-
- ❤️
-
-
- 1
-
+
+ ❤️
+
+
+ 1
+
+
-
-
+
-
- 🐶
-
-
- 1
-
+
+ 🐶
+
+
+ 1
+
+
-
-
+
-
- 😀
-
-
- 1
-
+
+ 😀
+
+
+ 1
+
+
-
-
+
-
- 😬
-
-
- 1
-
+
+ 😬
+
+
+ 1
+
+
-
-
+
-
- 😁
-
-
- 1
-
+
+ 😁
+
+
+ 1
+
+
-
-
+
-
-
-
+
+
+
+
-
+
@@ -103856,44 +107690,100 @@ exports[`Story Snapshots: SequentialThreadMessagesFollowingThreadButton should m
}
}
>
-
-
- View thread
-
-
+
+ View thread
+
+
+
-
-
- View thread
-
-
+
+ View thread
+
+
+
+
+
+
+
-
-
- Title
-
-
-
-
+ Title
+
+
+
- Image text
+
+ Image text
+
-
+
-
-
+ transition={null}
+ width={80}
+ />
+
-
+
-
-
- Title
-
-
-
-
+ Title
+
+
+
- Image text
+
+ Image text
+
-
+
-
-
+ transition={null}
+ width={80}
+ />
+
-
+
-
-
- rocket.cat
-
-
-
-
-
- Custom fields
-
+ rocket.cat
-
-
-
+
- Field 1
+
+
+ Custom fields
+
+
+
+
+ Field 1
+
+
- Value 1
+
+ Value 1
+
-
+
-
-
-
- Field 2
-
-
+ Field 2
+
+
- Value 2
+
+ Value 2
+
-
+
-
+
-
-
- rocket.cat
-
-
-
-
-
- Custom fields 2
-
+ rocket.cat
-
-
-
+
- Field 1
+
+
+ Custom fields 2
+
+
+
+
+ Field 1
+
+
- Value 1
+
+ Value 1
+
-
+
-
-
-
- Field 2
-
-
+ Field 2
+
+
- Value 2
+
+ Value 2
+
-
+
-
+
-
-
- rocket.cat
-
-
-
-
-
- Custom fields
-
+ rocket.cat
-
-
-
+
- Field 1
+
+
+ Custom fields
+
+
+
+
+ Field 1
+
+
- Value 1
+
+ Value 1
+
-
+
-
-
-
- Field 2
-
-
+ Field 2
+
+
- Value 2
+
+ Value 2
+
-
+
-
+
-
-
- rocket.cat
-
-
-
-
-
- Custom fields 2
-
+ rocket.cat
-
-
-
+
- Field 1
+
+
+ Custom fields 2
+
+
+
+
+ Field 1
+
+
- Value 1
+
+ Value 1
+
-
+
-
-
-
- Field 2
-
-
+ Field 2
+
+
- Value 2
+
+ Value 2
+
-
+
-
+
-
-
- Rocket.Chat - Free, Open Source, Enterprise Team Chat
-
-
+
- Rocket.Chat is the leading open source team chat software solution. Free, unlimited and completely customizable with on-premises and SaaS cloud hosting.
-
+
+ Rocket.Chat - Free, Open Source, Enterprise Team Chat
+
+
+ Rocket.Chat is the leading open source team chat software solution. Free, unlimited and completely customizable with on-premises and SaaS cloud hosting.
+
+
-
-
+
-
- Google
-
-
+
- Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for.
-
+
+ Google
+
+
+ Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for.
+
+
-
+
@@ -130500,7 +134702,7 @@ exports[`Story Snapshots: URL should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={451}
+ handlerTag={443}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -130627,7 +134829,7 @@ exports[`Story Snapshots: URL should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={452}
+ handlerTag={444}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -130847,104 +135049,129 @@ exports[`Story Snapshots: URL should match snapshot 1`] = `
-
-
- Google
-
-
+
- Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for.
-
+
+ Google
+
+
+ Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for.
+
+
-
+
@@ -131072,104 +135299,129 @@ exports[`Story Snapshots: URL should match snapshot 1`] = `
}
}
>
-
-
- Google
-
-
+
- Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for.
-
+
+ Google
+
+
+ Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for.
+
+
-
+
@@ -131315,7 +135567,7 @@ exports[`Story Snapshots: URLImagePreview should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={453}
+ handlerTag={445}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -131442,7 +135694,7 @@ exports[`Story Snapshots: URLImagePreview should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={454}
+ handlerTag={446}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -131559,104 +135811,129 @@ exports[`Story Snapshots: URLImagePreview should match snapshot 1`] = `
}
}
>
-
-
- Google
-
-
+
- Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for.
-
+
+ Google
+
+
+ Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for.
+
+
-
+
@@ -131782,7 +136059,7 @@ exports[`Story Snapshots: URLImagePreview should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={455}
+ handlerTag={447}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -131909,7 +136186,7 @@ exports[`Story Snapshots: URLImagePreview should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={456}
+ handlerTag={448}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -132026,47 +136303,72 @@ exports[`Story Snapshots: URLImagePreview should match snapshot 1`] = `
}
}
>
-
+ >
+
+
@@ -132205,7 +136507,7 @@ exports[`Story Snapshots: URLImagePreviewLargeFont should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={457}
+ handlerTag={449}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -132332,7 +136634,7 @@ exports[`Story Snapshots: URLImagePreviewLargeFont should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={458}
+ handlerTag={450}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -132449,104 +136751,129 @@ exports[`Story Snapshots: URLImagePreviewLargeFont should match snapshot 1`] = `
}
}
>
-
-
- Google
-
-
+
- Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for.
-
+
+ Google
+
+
+ Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for.
+
+
-
+
@@ -132672,7 +136999,7 @@ exports[`Story Snapshots: URLImagePreviewLargeFont should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={459}
+ handlerTag={451}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -132799,7 +137126,7 @@ exports[`Story Snapshots: URLImagePreviewLargeFont should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={460}
+ handlerTag={452}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -132916,47 +137243,72 @@ exports[`Story Snapshots: URLImagePreviewLargeFont should match snapshot 1`] = `
}
}
>
-
+ >
+
+
@@ -133095,7 +137447,7 @@ exports[`Story Snapshots: URLLargeFont should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={461}
+ handlerTag={453}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -133222,7 +137574,7 @@ exports[`Story Snapshots: URLLargeFont should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={462}
+ handlerTag={454}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -133339,202 +137691,252 @@ exports[`Story Snapshots: URLLargeFont should match snapshot 1`] = `
}
}
>
-
-
- Rocket.Chat - Free, Open Source, Enterprise Team Chat
-
-
+
- Rocket.Chat is the leading open source team chat software solution. Free, unlimited and completely customizable with on-premises and SaaS cloud hosting.
-
+
+ Rocket.Chat - Free, Open Source, Enterprise Team Chat
+
+
+ Rocket.Chat is the leading open source team chat software solution. Free, unlimited and completely customizable with on-premises and SaaS cloud hosting.
+
+
-
-
+
-
- Google
-
-
+
- Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for.
-
+
+ Google
+
+
+ Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for.
+
+
-
+
@@ -133660,7 +138062,7 @@ exports[`Story Snapshots: URLLargeFont should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={463}
+ handlerTag={455}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -133787,7 +138189,7 @@ exports[`Story Snapshots: URLLargeFont should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={464}
+ handlerTag={456}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -134007,104 +138409,129 @@ exports[`Story Snapshots: URLLargeFont should match snapshot 1`] = `
-
-
- Google
-
-
+
- Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for.
-
+
+ Google
+
+
+ Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for.
+
+
-
+
@@ -134232,104 +138659,129 @@ exports[`Story Snapshots: URLLargeFont should match snapshot 1`] = `
}
}
>
-
-
- Google
-
-
+
- Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for.
-
+
+ Google
+
+
+ Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for.
+
+
-
+
@@ -134475,7 +138927,7 @@ exports[`Story Snapshots: WithAlias should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={465}
+ handlerTag={457}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -134602,7 +139054,7 @@ exports[`Story Snapshots: WithAlias should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={466}
+ handlerTag={458}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -134922,7 +139374,7 @@ exports[`Story Snapshots: WithAlias should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={467}
+ handlerTag={459}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -135049,7 +139501,7 @@ exports[`Story Snapshots: WithAlias should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={468}
+ handlerTag={460}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -135382,7 +139834,7 @@ exports[`Story Snapshots: WithAliasLargeFont should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={469}
+ handlerTag={461}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -135509,7 +139961,7 @@ exports[`Story Snapshots: WithAliasLargeFont should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={470}
+ handlerTag={462}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -135829,7 +140281,7 @@ exports[`Story Snapshots: WithAliasLargeFont should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={471}
+ handlerTag={463}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -135956,7 +140408,7 @@ exports[`Story Snapshots: WithAliasLargeFont should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={472}
+ handlerTag={464}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -136289,7 +140741,7 @@ exports[`Story Snapshots: WithAudio should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={473}
+ handlerTag={465}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -136416,7 +140868,7 @@ exports[`Story Snapshots: WithAudio should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={474}
+ handlerTag={466}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -136850,7 +141302,7 @@ exports[`Story Snapshots: WithAudio should match snapshot 1`] = `
-
-
-
+
- File.pdf
+
+ File.pdf
+
-
+
-
+
-
-
-
+
- File.pdf
+
+ File.pdf
+
-
+
-
+
-
-
-
+
- File.pdf
+
+ File.pdf
+
-
+
-
+
-
-
-
+
- File.pdf
+
+ File.pdf
+
-
+
-
+
-
-
-
+
- File.pdf
+
+ File.pdf
+
-
+
-
+
-
-
+
-
+
-
+
-
-
-
-
+
+
+
+
+ />
+
-
+
@@ -144918,7 +149520,7 @@ exports[`Story Snapshots: WithImage should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={496}
+ handlerTag={488}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -145045,7 +149647,7 @@ exports[`Story Snapshots: WithImage should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={497}
+ handlerTag={489}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -145334,72 +149936,77 @@ exports[`Story Snapshots: WithImage should match snapshot 1`] = `
-
-
+
-
+
-
+
-
-
-
-
+
+
+
+
+ />
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -146045,7 +150722,7 @@ exports[`Story Snapshots: WithImage should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={498}
+ handlerTag={490}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -146172,7 +150849,7 @@ exports[`Story Snapshots: WithImage should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={499}
+ handlerTag={491}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -146355,72 +151032,77 @@ exports[`Story Snapshots: WithImage should match snapshot 1`] = `
}
}
>
-
-
+
-
+
-
+
-
-
-
-
+
+
+
+
+ />
+
-
-
+
-
+
-
+
-
+
-
-
-
-
+
+
+
+
+ />
+
-
+
@@ -146799,7 +151526,7 @@ exports[`Story Snapshots: WithImage should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={500}
+ handlerTag={492}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -146926,7 +151653,7 @@ exports[`Story Snapshots: WithImage should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={501}
+ handlerTag={493}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -147156,72 +151883,77 @@ exports[`Story Snapshots: WithImage should match snapshot 1`] = `
-
-
+
-
+
-
+
+
+
+
-
-
+
@@ -147442,7 +152194,7 @@ exports[`Story Snapshots: WithImageLargeFont should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={502}
+ handlerTag={494}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -147569,7 +152321,7 @@ exports[`Story Snapshots: WithImageLargeFont should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={503}
+ handlerTag={495}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -147755,72 +152507,77 @@ exports[`Story Snapshots: WithImageLargeFont should match snapshot 1`] = `
-
-
+
-
+
-
+
-
-
-
-
+
+
+
+
+ />
+
-
+
@@ -148112,72 +152889,77 @@ exports[`Story Snapshots: WithImageLargeFont should match snapshot 1`] = `
-
-
+
-
+
-
+
-
-
-
-
+
+
+
+
+ />
+
-
+
@@ -148405,7 +153207,7 @@ exports[`Story Snapshots: WithImageLargeFont should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={504}
+ handlerTag={496}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -148532,7 +153334,7 @@ exports[`Story Snapshots: WithImageLargeFont should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={505}
+ handlerTag={497}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -148715,72 +153517,77 @@ exports[`Story Snapshots: WithImageLargeFont should match snapshot 1`] = `
}
}
>
-
-
+
-
+
-
+
-
-
-
-
+
+
+
+
+ />
+
-
-
+
-
+
-
+
-
+
-
-
-
-
+
+
+
+
+ />
+
-
+
@@ -149159,7 +154011,7 @@ exports[`Story Snapshots: WithImageLargeFont should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={506}
+ handlerTag={498}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -149286,7 +154138,7 @@ exports[`Story Snapshots: WithImageLargeFont should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={507}
+ handlerTag={499}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -149516,72 +154368,77 @@ exports[`Story Snapshots: WithImageLargeFont should match snapshot 1`] = `
-
-
+
-
+
-
+
+
+
+
-
-
+
@@ -149802,7 +154679,7 @@ exports[`Story Snapshots: WithVideo should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={508}
+ handlerTag={500}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -149929,7 +154806,7 @@ exports[`Story Snapshots: WithVideo should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={509}
+ handlerTag={501}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -150159,125 +155036,150 @@ exports[`Story Snapshots: WithVideo should match snapshot 1`] = `
-
-
+
-
+
-
-
+
+
+
+
-
+
@@ -150476,125 +155378,150 @@ exports[`Story Snapshots: WithVideo should match snapshot 1`] = `
-
-
+
-
+
-
-
+
+
+
+
-
+
@@ -150729,7 +155656,7 @@ exports[`Story Snapshots: WithVideo should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={510}
+ handlerTag={502}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -150856,7 +155783,7 @@ exports[`Story Snapshots: WithVideo should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={511}
+ handlerTag={503}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -151046,125 +155973,150 @@ exports[`Story Snapshots: WithVideo should match snapshot 1`] = `
}
}
>
-
-
+
-
+
-
-
+
+
+
+
-
+
-
-
+
-
+
-
-
+
+
+
+
-
+
@@ -151432,7 +156409,7 @@ exports[`Story Snapshots: WithVideoLargeFont should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={512}
+ handlerTag={504}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -151559,7 +156536,7 @@ exports[`Story Snapshots: WithVideoLargeFont should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={513}
+ handlerTag={505}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -151789,125 +156766,150 @@ exports[`Story Snapshots: WithVideoLargeFont should match snapshot 1`] = `
-
-
+
-
+
-
-
+
+
+
+
-
+
@@ -152035,7 +157037,7 @@ exports[`Story Snapshots: WithVideoLargeFont should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={514}
+ handlerTag={506}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -152162,7 +157164,7 @@ exports[`Story Snapshots: WithVideoLargeFont should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={515}
+ handlerTag={507}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -152293,125 +157295,150 @@ exports[`Story Snapshots: WithVideoLargeFont should match snapshot 1`] = `
}
}
>
-
-
+
-
+
-
-
+
+
+
+
-
+
@@ -152539,7 +157566,7 @@ exports[`Story Snapshots: WithVideoLargeFont should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={516}
+ handlerTag={508}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -152666,7 +157693,7 @@ exports[`Story Snapshots: WithVideoLargeFont should match snapshot 1`] = `
collapsable={false}
delayLongPress={600}
enabled={true}
- handlerTag={517}
+ handlerTag={509}
handlerType="NativeViewGestureHandler"
innerRef={null}
onActiveStateChange={[Function]}
@@ -152856,125 +157883,150 @@ exports[`Story Snapshots: WithVideoLargeFont should match snapshot 1`] = `
}
}
>
-
-
+
-
+
-
-
+
+
+
+
-
+
-
-
+
-
+
-
-
+
+
+
+
-
+
diff --git a/app/containers/message/index.tsx b/app/containers/message/index.tsx
index 31522bd7f5c..a352240850a 100644
--- a/app/containers/message/index.tsx
+++ b/app/containers/message/index.tsx
@@ -441,6 +441,7 @@ class MessageContainer extends React.Component {
+const navigate = ({
+ item,
+ isMasterDetail,
+ ...props
+}: {
+ item: TGoRoomItem;
+ isMasterDetail: boolean;
+ focusHeaderOnOpen?: boolean;
+}) => {
const routeParams = {
rid: item.rid,
name: getRoomTitle(item),
@@ -34,7 +42,8 @@ const navigate = ({ item, isMasterDetail, ...props }: { item: TGoRoomItem; isMas
room: item,
visitor: item.visitor,
roomUserId: getUidDirectMessage(item),
- ...props
+ ...props,
+ ...(isMasterDetail && props.focusHeaderOnOpen ? { focusHeaderOnOpen: true } : {})
};
const currentRoute = Navigation.getCurrentRoute() as any;
@@ -91,6 +100,7 @@ export const goRoom = async ({
isMasterDetail: boolean;
jumpToMessageId?: string;
usedCannedResponse?: string;
+ focusHeaderOnOpen?: boolean;
}): Promise => {
if (!('id' in item) && item.t === SubscriptionType.DIRECT && item?.search) {
// if user is using the search we need first to join/create room
diff --git a/app/lib/native/KeyboardInversionA11yAndroid.ts b/app/lib/native/KeyboardInversionA11yAndroid.ts
new file mode 100644
index 00000000000..6f26387272f
--- /dev/null
+++ b/app/lib/native/KeyboardInversionA11yAndroid.ts
@@ -0,0 +1,25 @@
+import type { TurboModule } from 'react-native';
+import { TurboModuleRegistry } from 'react-native';
+
+export interface KeyboardInversionState {
+ enabled: boolean;
+ scope: 'room-view' | null;
+}
+
+interface Spec extends TurboModule {
+ enable(scope: 'room-view'): void;
+ disable(): void;
+ getState(): Promise;
+}
+
+const NativeModule = TurboModuleRegistry.getEnforcing('KeyboardA11y');
+
+export const enableRoomViewKeyboardA11y = (scope: 'room-view' = 'room-view') => {
+ NativeModule.enable(scope);
+};
+
+export const disableKeyboardA11y = () => {
+ NativeModule.disable();
+};
+
+export const getKeyboardA11yState = () => NativeModule.getState();
diff --git a/app/stacks/types.ts b/app/stacks/types.ts
index c8afff63863..fbda5ef6b66 100644
--- a/app/stacks/types.ts
+++ b/app/stacks/types.ts
@@ -41,6 +41,7 @@ export type ChatsStackParamList = {
jumpToThreadId?: string;
roomUserId?: string | null;
usedCannedResponse?: string;
+ focusHeaderOnOpen?: boolean;
status?: string;
}
| undefined; // Navigates back to RoomView already on stack
diff --git a/app/views/CannedResponsesListView/__snapshots__/CannedResponseItem.test.tsx.snap b/app/views/CannedResponsesListView/__snapshots__/CannedResponseItem.test.tsx.snap
index 660a41df1fd..267b24753aa 100644
--- a/app/views/CannedResponsesListView/__snapshots__/CannedResponseItem.test.tsx.snap
+++ b/app/views/CannedResponsesListView/__snapshots__/CannedResponseItem.test.tsx.snap
@@ -127,49 +127,57 @@ exports[`Story Snapshots: Itens should match snapshot 1`] = `
+
+
{
title='Avatars'
testID='display-pref-view-avatars'
right={() => renderAvatarSwitch(showAvatar)}
+ onPress={toggleAvatar}
additionalAccessibilityLabel={showAvatar}
accessibilityRole='switch'
/>
diff --git a/app/views/RoomView/List/components/InvertedScrollView.tsx b/app/views/RoomView/List/components/InvertedScrollView.tsx
index a441c25893c..b1a68c23896 100644
--- a/app/views/RoomView/List/components/InvertedScrollView.tsx
+++ b/app/views/RoomView/List/components/InvertedScrollView.tsx
@@ -1,24 +1,10 @@
-import React, { forwardRef } from 'react';
-import { ScrollView, requireNativeComponent, type ScrollViewProps, type ViewProps } from 'react-native';
+import type { ComponentType } from 'react';
+import { type ScrollViewProps } from 'react-native';
-const NativeInvertedScrollContentView = requireNativeComponent('InvertedScrollContentView');
+import RNLikeInvertedScrollView from './RNLikeInvertedScrollView';
-/**
- * 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;
+interface InvertedScrollViewProps extends ScrollViewProps {
+ exitFocusNativeId?: string;
+}
- return (
-
- {children}
-
- );
-});
-
-InvertedScrollView.displayName = 'InvertedScrollView';
-
-export default InvertedScrollView;
+export default RNLikeInvertedScrollView as ComponentType;
diff --git a/app/views/RoomView/List/components/List.tsx b/app/views/RoomView/List/components/List.tsx
index 7ffe0135587..df39d0f3d0c 100644
--- a/app/views/RoomView/List/components/List.tsx
+++ b/app/views/RoomView/List/components/List.tsx
@@ -44,7 +44,9 @@ const List = ({ listRef, jumpToBottom, ...props }: IListProps) => {
contentContainerStyle={styles.contentContainer}
style={styles.list}
inverted
- renderScrollComponent={isIOS ? undefined : props => }
+ renderScrollComponent={
+ isIOS ? undefined : props =>
+ }
removeClippedSubviews={isIOS}
initialNumToRender={7}
onEndReachedThreshold={0.5}
diff --git a/app/views/RoomView/List/components/RNLikeInvertedScrollView.tsx b/app/views/RoomView/List/components/RNLikeInvertedScrollView.tsx
new file mode 100644
index 00000000000..c402ac1d0b5
--- /dev/null
+++ b/app/views/RoomView/List/components/RNLikeInvertedScrollView.tsx
@@ -0,0 +1,228 @@
+import React from 'react';
+import {
+ Keyboard,
+ Platform,
+ StyleSheet,
+ TextInput,
+ type GestureResponderEvent,
+ type LayoutChangeEvent,
+ requireNativeComponent,
+ type ScrollViewProps,
+ type ViewProps
+} from 'react-native';
+
+interface InvertedScrollContentViewProps extends ViewProps {
+ isInvertedContent?: boolean;
+}
+
+interface InvertedScrollViewNativeProps extends ScrollViewProps {
+ exitFocusNativeId?: string;
+}
+
+interface Props extends ScrollViewProps {
+ exitFocusNativeId?: string;
+ scrollViewRef?: React.Ref;
+}
+
+interface State {
+ layoutHeight: number | null;
+}
+
+const NativeInvertedScrollView = requireNativeComponent('InvertedScrollView');
+const NativeInvertedScrollContentView = requireNativeComponent('InvertedScrollContentView');
+
+const IS_ANIMATING_TOUCH_START_THRESHOLD_MS = 16;
+
+class RNLikeInvertedScrollView extends React.Component {
+ private scrollRef = React.createRef();
+ private _keyboardMetrics: { height: number } | null = null;
+ private _isTouching = false;
+ private _lastMomentumScrollBeginTime = 0;
+ private _lastMomentumScrollEndTime = 0;
+ private _observedScrollSinceBecomingResponder = false;
+ private _subscriptionKeyboardDidShow?: { remove: () => void };
+ private _subscriptionKeyboardDidHide?: { remove: () => void };
+
+ state: State = {
+ layoutHeight: null
+ };
+
+ componentDidMount() {
+ this._subscriptionKeyboardDidShow = Keyboard.addListener('keyboardDidShow', this.onKeyboardDidShow);
+ this._subscriptionKeyboardDidHide = Keyboard.addListener('keyboardDidHide', this.onKeyboardDidHide);
+ }
+
+ componentWillUnmount() {
+ this._subscriptionKeyboardDidShow?.remove();
+ this._subscriptionKeyboardDidHide?.remove();
+ }
+
+ private onKeyboardDidShow = (e: any) => {
+ this._keyboardMetrics = e?.endCoordinates ?? { height: 0 };
+ };
+
+ private onKeyboardDidHide = (_e: any) => {
+ this._keyboardMetrics = null;
+ };
+
+ private isAnimating = () => {
+ const now = global.performance.now();
+ const timeSinceLastMomentumScrollEnd = now - this._lastMomentumScrollEndTime;
+ return (
+ timeSinceLastMomentumScrollEnd < IS_ANIMATING_TOUCH_START_THRESHOLD_MS ||
+ this._lastMomentumScrollEndTime < this._lastMomentumScrollBeginTime
+ );
+ };
+
+ private keyboardEventsAreUnreliable = () => Platform.OS === 'android' && Platform.Version < 30;
+
+ private keyboardIsDismissible = () => {
+ const currentlyFocusedInput = TextInput.State.currentlyFocusedInput?.();
+ const hasFocusedTextInput = currentlyFocusedInput != null;
+ const softKeyboardMayBeOpen = this._keyboardMetrics != null || this.keyboardEventsAreUnreliable();
+ return hasFocusedTextInput && softKeyboardMayBeOpen;
+ };
+
+ private handleLayout = (e: LayoutChangeEvent) => {
+ if (this.props.invertStickyHeaders === true) {
+ this.setState({ layoutHeight: e.nativeEvent.layout.height });
+ }
+ this.props.onLayout?.(e);
+ };
+
+ private handleContentOnLayout = (e: LayoutChangeEvent) => {
+ const { width, height } = e.nativeEvent.layout;
+ this.props.onContentSizeChange?.(width, height);
+ };
+
+ private handleScroll = (e: any) => {
+ this._observedScrollSinceBecomingResponder = true;
+ this.props.onScroll?.(e);
+ };
+
+ private handleMomentumScrollBegin = (e: any) => {
+ this._lastMomentumScrollBeginTime = global.performance.now();
+ this.props.onMomentumScrollBegin?.(e);
+ };
+
+ private handleMomentumScrollEnd = (e: any) => {
+ this._lastMomentumScrollEndTime = global.performance.now();
+ this.props.onMomentumScrollEnd?.(e);
+ };
+
+ private handleResponderGrant = (e: GestureResponderEvent) => {
+ this._observedScrollSinceBecomingResponder = false;
+ this.props.onResponderGrant?.(e);
+ };
+
+ private handleResponderRelease = (e: GestureResponderEvent) => {
+ this._isTouching = e.nativeEvent.touches.length !== 0;
+ this.props.onResponderRelease?.(e);
+ };
+
+ private handleResponderTerminationRequest = () => !this._observedScrollSinceBecomingResponder;
+
+ private handleScrollShouldSetResponder = () => {
+ if (this.props.disableScrollViewPanResponder === true) {
+ return false;
+ }
+ return this._isTouching;
+ };
+
+ private handleStartShouldSetResponder = (e: GestureResponderEvent) => {
+ if (this.props.disableScrollViewPanResponder === true) {
+ return false;
+ }
+ const currentlyFocusedInput = TextInput.State.currentlyFocusedInput?.();
+ if (
+ this.props.keyboardShouldPersistTaps === 'handled' &&
+ this.keyboardIsDismissible() &&
+ e.target !== currentlyFocusedInput
+ ) {
+ return true;
+ }
+ return false;
+ };
+
+ private handleStartShouldSetResponderCapture = (e: GestureResponderEvent) => {
+ if (this.isAnimating()) {
+ return true;
+ }
+ if (this.props.disableScrollViewPanResponder === true) {
+ return false;
+ }
+ const { keyboardShouldPersistTaps } = this.props;
+ const keyboardNeverPersistTaps = !keyboardShouldPersistTaps || keyboardShouldPersistTaps === 'never';
+ return keyboardNeverPersistTaps && this.keyboardIsDismissible() && e.target != null;
+ };
+
+ private setRefs = (instance: any) => {
+ this.scrollRef.current = instance;
+ const { scrollViewRef } = this.props;
+ if (!scrollViewRef) {
+ return;
+ }
+ if (typeof scrollViewRef === 'function') {
+ scrollViewRef(instance);
+ return;
+ }
+ (scrollViewRef as React.MutableRefObject).current = instance;
+ };
+
+ render() {
+ const { horizontal, children, style, contentContainerStyle, onContentSizeChange, ...rest } = this.props;
+ const contentStyle = [horizontal ? styles.contentContainerHorizontal : null, contentContainerStyle];
+ const baseStyle = horizontal ? styles.baseHorizontal : styles.baseVertical;
+ const ScrollContainer = NativeInvertedScrollView as any;
+
+ return (
+
+
+ {children}
+
+
+ );
+ }
+}
+
+const styles = StyleSheet.create({
+ baseVertical: {
+ flexGrow: 1,
+ flexShrink: 1,
+ flexDirection: 'column',
+ overflow: 'scroll'
+ },
+ baseHorizontal: {
+ flexGrow: 1,
+ flexShrink: 1,
+ flexDirection: 'row',
+ overflow: 'scroll'
+ },
+ contentContainerHorizontal: {
+ flexDirection: 'row'
+ }
+});
+
+const Wrapper = React.forwardRef((props, ref) => );
+
+Wrapper.displayName = 'RNLikeInvertedScrollView';
+
+export default Wrapper;
diff --git a/app/views/RoomView/index.tsx b/app/views/RoomView/index.tsx
index 2622040f562..e00c2514783 100644
--- a/app/views/RoomView/index.tsx
+++ b/app/views/RoomView/index.tsx
@@ -29,7 +29,7 @@ import MessageErrorActions, { type IMessageErrorActions } from '../../containers
import log, { events, logEvent } from '../../lib/methods/helpers/log';
import EventEmitter from '../../lib/methods/helpers/events';
import I18n from '../../i18n';
-import RoomHeader from '../../containers/RoomHeader';
+import RoomHeader, { type IRoomHeaderRef } from '../../containers/RoomHeader';
import ReactionsList from '../../containers/ReactionsList';
import { LISTENER } from '../../containers/Toast';
import { getBadgeColor, isBlocked, makeThreadName } from '../../lib/methods/helpers/room';
@@ -117,6 +117,7 @@ class RoomView extends React.Component {
private jumpToMessageId?: string;
private jumpToThreadId?: string;
private messageComposerRef: React.RefObject;
+ private roomHeaderRef: React.RefObject;
private joinCode: React.RefObject;
// ListContainer component
private list: React.RefObject;
@@ -201,6 +202,7 @@ class RoomView extends React.Component {
this.updateE2EEState();
this.messageComposerRef = React.createRef();
+ this.roomHeaderRef = React.createRef();
this.list = React.createRef();
this.flatList = React.createRef();
this.joinCode = React.createRef();
@@ -217,7 +219,7 @@ class RoomView extends React.Component {
}
componentDidMount() {
- const { navigation, dispatch } = this.props;
+ const { navigation, dispatch, isMasterDetail, route } = this.props;
const { selectedMessages } = this.state;
dispatch(clearInAppFeedback());
this.mounted = true;
@@ -253,6 +255,16 @@ class RoomView extends React.Component {
this.unsubscribeBlur = navigation.addListener('blur', () => {
AudioManager.pauseAudio();
});
+ this.unsubscribeFocus = navigation.addListener('focus', () => {
+ InteractionManager.runAfterInteractions(() => {
+ if (isMasterDetail && route?.params?.focusHeaderOnOpen) {
+ this.roomHeaderRef.current?.focus();
+ navigation.setParams({ focusHeaderOnOpen: undefined });
+ return;
+ }
+ this.messageComposerRef.current?.focus?.();
+ });
+ });
}
shouldComponentUpdate(nextProps: IRoomViewProps, nextState: IRoomViewState) {
@@ -534,6 +546,7 @@ class RoomView extends React.Component {
),
headerTitle: () => (
{
}
}
- return ;
+ return (
+
+
+
+ );
};
renderActions = () => {
diff --git a/app/views/RoomsListView/index.tsx b/app/views/RoomsListView/index.tsx
index 0ec091befb3..308ab92e9fe 100644
--- a/app/views/RoomsListView/index.tsx
+++ b/app/views/RoomsListView/index.tsx
@@ -73,7 +73,7 @@ const RoomsListView = memo(function RoomsListView() {
logEvent(events.RL_GO_ROOM);
stopSearch();
- goRoom({ item, isMasterDetail });
+ goRoom({ item, isMasterDetail, focusHeaderOnOpen: true });
};
const renderItem = ({ item }: { item: IRoomItem }) => {
diff --git a/app/views/SecurityPrivacyView.tsx b/app/views/SecurityPrivacyView.tsx
index d87ac03a26c..94a5a0dbc31 100644
--- a/app/views/SecurityPrivacyView.tsx
+++ b/app/views/SecurityPrivacyView.tsx
@@ -95,14 +95,18 @@ const SecurityPrivacyView = ({ navigation }: ISecurityPrivacyViewProps): JSX.Ele
title='Log_analytics_events'
testID='security-privacy-view-analytics-events'
right={() => }
+ onPress={() => toggleAnalyticsEvents(!analyticsEventsState)}
additionalAccessibilityLabel={analyticsEventsState}
+ accessibilityRole='switch'
/>
}
- additionalAccessibilityLabel={analyticsEventsState}
+ onPress={() => toggleCrashReport(!crashReportState)}
+ additionalAccessibilityLabel={crashReportState}
+ accessibilityRole='switch'
/>
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 8122f18684a..843148c6d24 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -1640,6 +1640,30 @@ PODS:
- Yoga
- react-native-cookies (6.2.1):
- React-Core
+ - react-native-external-keyboard (0.8.2):
+ - DoubleConversion
+ - glog
+ - hermes-engine
+ - RCT-Folly (= 2024.11.18.00)
+ - RCTRequired
+ - RCTTypeSafety
+ - React-Core
+ - React-debug
+ - React-Fabric
+ - React-featureflags
+ - React-graphics
+ - React-hermes
+ - React-ImageManager
+ - React-jsi
+ - React-NativeModulesApple
+ - React-RCTFabric
+ - React-renderercss
+ - React-rendererdebug
+ - React-utils
+ - ReactCodegen
+ - ReactCommon/turbomodule/bridging
+ - ReactCommon/turbomodule/core
+ - Yoga
- react-native-keyboard-controller (1.17.1):
- DoubleConversion
- glog
@@ -2722,6 +2746,7 @@ DEPENDENCIES:
- react-native-background-timer (from `../node_modules/react-native-background-timer`)
- "react-native-cameraroll (from `../node_modules/@react-native-camera-roll/camera-roll`)"
- "react-native-cookies (from `../node_modules/@react-native-cookies/cookies`)"
+ - react-native-external-keyboard (from `../node_modules/react-native-external-keyboard`)
- react-native-keyboard-controller (from `../node_modules/react-native-keyboard-controller`)
- react-native-mmkv (from `../node_modules/react-native-mmkv`)
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
@@ -2943,6 +2968,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/@react-native-camera-roll/camera-roll"
react-native-cookies:
:path: "../node_modules/@react-native-cookies/cookies"
+ react-native-external-keyboard:
+ :path: "../node_modules/react-native-external-keyboard"
react-native-keyboard-controller:
:path: "../node_modules/react-native-keyboard-controller"
react-native-mmkv:
@@ -3148,6 +3175,7 @@ SPEC CHECKSUMS:
react-native-background-timer: 4638ae3bee00320753647900b21260b10587b6f7
react-native-cameraroll: 23d28040c32ca8b20661e0c41b56ab041779244b
react-native-cookies: d648ab7025833b977c0b19e142503034f5f29411
+ react-native-external-keyboard: cfbd95f3c5623b008efe01c583f7787a86a1aee1
react-native-keyboard-controller: 9ec7ee23328c30251a399cffd8b54324a00343bf
react-native-mmkv: ec96a16cd90e0d994d486c3993abf712186f7262
react-native-netinfo: 2e3c27627db7d49ba412bfab25834e679db41e21
diff --git a/package.json b/package.json
index 4b3e53b0dd1..38e0bc2738d 100644
--- a/package.json
+++ b/package.json
@@ -94,6 +94,7 @@
"react-native-device-info": "11.1.0",
"react-native-easy-grid": "0.2.2",
"react-native-easy-toast": "2.3.0",
+ "react-native-external-keyboard": "^0.8.2",
"react-native-file-viewer": "2.1.4",
"react-native-gesture-handler": "2.24.0",
"react-native-image-crop-picker": "RocketChat/react-native-image-crop-picker#f028aac24373d05166747ef6d9e59bb037fe3224",
diff --git a/patches/react-native-gesture-handler+2.24.0.patch b/patches/react-native-gesture-handler+2.24.0.patch
new file mode 100644
index 00000000000..309449cc7c5
--- /dev/null
+++ b/patches/react-native-gesture-handler+2.24.0.patch
@@ -0,0 +1,57 @@
+diff --git a/node_modules/react-native-gesture-handler/apple/RNGestureHandlerButton.mm b/node_modules/react-native-gesture-handler/apple/RNGestureHandlerButton.mm
+index 2296c39..0a872ba 100644
+--- a/node_modules/react-native-gesture-handler/apple/RNGestureHandlerButton.mm
++++ b/node_modules/react-native-gesture-handler/apple/RNGestureHandlerButton.mm
+@@ -89,6 +89,52 @@
+ }
+
+ #if !TARGET_OS_OSX
++- (BOOL)canBecomeFocused
++{
++ return self.userEnabled && self.enabled && self.userInteractionEnabled;
++}
++
++- (BOOL)_isActivationKeyPress:(NSSet *)presses
++{
++ if (@available(iOS 13.4, *)) {
++ for (UIPress *press in presses) {
++ if (press.key != nil &&
++ (press.key.keyCode == UIKeyboardHIDUsageKeyboardSpacebar ||
++ press.key.keyCode == UIKeyboardHIDUsageKeyboardReturnOrEnter)) {
++ return YES;
++ }
++ }
++ }
++ return NO;
++}
++
++- (void)pressesBegan:(NSSet *)presses withEvent:(UIPressesEvent *)event
++{
++ if ([self _isActivationKeyPress:presses]) {
++ [self sendActionsForControlEvents:UIControlEventTouchDown];
++ } else {
++ [super pressesBegan:presses withEvent:event];
++ }
++}
++
++- (void)pressesEnded:(NSSet *)presses withEvent:(UIPressesEvent *)event
++{
++ if ([self _isActivationKeyPress:presses]) {
++ [self sendActionsForControlEvents:UIControlEventTouchUpInside];
++ } else {
++ [super pressesEnded:presses withEvent:event];
++ }
++}
++
++- (void)pressesCancelled:(NSSet *)presses withEvent:(UIPressesEvent *)event
++{
++ if ([self _isActivationKeyPress:presses]) {
++ [self sendActionsForControlEvents:UIControlEventTouchCancel];
++ } else {
++ [super pressesCancelled:presses withEvent:event];
++ }
++}
++
+ - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
+ {
+ if (UIEdgeInsetsEqualToEdgeInsets(self.hitTestEdgeInsets, UIEdgeInsetsZero)) {
diff --git a/yarn.lock b/yarn.lock
index 6d8d740fa76..a3915e4d825 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -12062,6 +12062,11 @@ react-native-edge-to-edge@1.6.0:
resolved "https://registry.yarnpkg.com/react-native-edge-to-edge/-/react-native-edge-to-edge-1.6.0.tgz#2ba63b941704a7f713e298185c26cde4d9e4b973"
integrity sha512-2WCNdE3Qd6Fwg9+4BpbATUxCLcouF6YRY7K+J36KJ4l3y+tWN6XCqAC4DuoGblAAbb2sLkhEDp4FOlbOIot2Og==
+react-native-external-keyboard@^0.8.2:
+ version "0.8.2"
+ resolved "https://registry.yarnpkg.com/react-native-external-keyboard/-/react-native-external-keyboard-0.8.2.tgz#2929460223f1ed3a1153e631ee28b062c66dac75"
+ integrity sha512-UMchkfHwC6p9hQOoMSO+OwX31VC4Pzjyul0XArH/eZLZS+GDDDpIn6q15QUwYUudrt+rXB+DtwSszTyxZbIY2g==
+
react-native-file-viewer@2.1.4:
version "2.1.4"
resolved "https://registry.yarnpkg.com/react-native-file-viewer/-/react-native-file-viewer-2.1.4.tgz#987b2902f0f0ac87b42f3ac3d3037c8ae98f17a6"