diff --git a/.gitignore b/.gitignore
index 3895765f2f..ca66e76e98 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,3 +24,5 @@ index.android.bundle
*.DSYM.zip
**/metrics/
package/shared-native/.sync-state/
+
+.claude/worktrees
diff --git a/README.md b/README.md
index 5622f90803..a010d8eb45 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@
[](https://www.npmjs.com/package/stream-chat-react-native)
[](https://github.com/GetStream/stream-chat-react-native/actions)
[](https://getstream.io/chat/docs/sdk/reactnative)
-
+
diff --git a/examples/ExpoMessaging/app/channel/[cid]/index.tsx b/examples/ExpoMessaging/app/channel/[cid]/index.tsx
index 0410bec2f6..fa8be06f86 100644
--- a/examples/ExpoMessaging/app/channel/[cid]/index.tsx
+++ b/examples/ExpoMessaging/app/channel/[cid]/index.tsx
@@ -73,6 +73,7 @@ export default function ChannelScreen() {
channel={channel}
onPressMessage={onPressMessage}
keyboardVerticalOffset={headerHeight}
+ topInset={headerHeight}
thread={thread}
>
diff --git a/examples/ExpoMessaging/package.json b/examples/ExpoMessaging/package.json
index 1664d6fd02..b57ba6ed16 100644
--- a/examples/ExpoMessaging/package.json
+++ b/examples/ExpoMessaging/package.json
@@ -47,13 +47,14 @@
"react-dom": "19.2.0",
"react-native": "0.83.2",
"react-native-gesture-handler": "~2.30.0",
+ "react-native-keyboard-controller": "1.20.7",
"react-native-maps": "1.26.20",
"react-native-reanimated": "4.2.1",
"react-native-safe-area-context": "~5.6.2",
"react-native-screens": "~4.23.0",
"react-native-svg": "15.15.3",
- "react-native-web": "^0.21.0",
"react-native-teleport": "^1.0.2",
+ "react-native-web": "^0.21.0",
"react-native-worklets": "0.7.2",
"stream-chat-expo": "link:../../package/expo-package",
"stream-chat-react-native-core": "link:../../package"
diff --git a/examples/ExpoMessaging/yarn.lock b/examples/ExpoMessaging/yarn.lock
index edfa2b6f3b..d307548819 100644
--- a/examples/ExpoMessaging/yarn.lock
+++ b/examples/ExpoMessaging/yarn.lock
@@ -5526,6 +5526,13 @@ react-native-is-edge-to-edge@1.2.1, react-native-is-edge-to-edge@^1.2.1:
resolved "https://registry.yarnpkg.com/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.2.1.tgz#64e10851abd9d176cbf2b40562f751622bde3358"
integrity sha512-FLbPWl/MyYQWz+KwqOZsSyj2JmLKglHatd3xLZWskXOpRaio4LfEDEz8E/A6uD8QoTHW6Aobw1jbEwK7KMgR7Q==
+react-native-keyboard-controller@1.20.7:
+ version "1.20.7"
+ resolved "https://registry.yarnpkg.com/react-native-keyboard-controller/-/react-native-keyboard-controller-1.20.7.tgz#e1be1c15a5eb10b96a40a0812d8472e6e4bd8f29"
+ integrity sha512-G8S5jz1FufPrcL1vPtReATx+jJhT/j+sTqxMIb30b1z7cYEfMlkIzOCyaHgf6IMB2KA9uBmnA5M6ve2A9Ou4kw==
+ dependencies:
+ react-native-is-edge-to-edge "^1.2.1"
+
react-native-lightbox@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/react-native-lightbox/-/react-native-lightbox-0.7.0.tgz#e52b4d7fcc141f59d7b23f0180de535e35b20ec9"
@@ -6073,10 +6080,10 @@ stream-chat-react-native-core@8.1.0:
version "0.0.0"
uid ""
-stream-chat@^9.42.1:
- version "9.42.1"
- resolved "https://registry.yarnpkg.com/stream-chat/-/stream-chat-9.42.1.tgz#8b6aa4e3e73a39ed07bb2a4f2a6829ba9354567a"
- integrity sha512-o+9wQO4Ruu1A48T0IrX9ZH8+9F5xPgGLPvflaswaPeLyIZXcy8bsQdcT/HSrPmT7gs0WGD3qcbXaAJU5lMQezQ==
+stream-chat@^9.44.2:
+ version "9.44.2"
+ resolved "https://registry.yarnpkg.com/stream-chat/-/stream-chat-9.44.2.tgz#97d23ae4ac356b352bb0f20a31a29dc63d3ea6f5"
+ integrity sha512-TXALWeHyWnSn1KlGYEF0sltEHB26vFd26l5m1qlE9Q1XHo9RPPSyLb5mfXqTEY8b2FAv57Ei3hrT8nSXVWacDQ==
dependencies:
"@types/jsonwebtoken" "^9.0.8"
"@types/ws" "^8.5.14"
diff --git a/package/package.json b/package/package.json
index 4cc0500837..96eee94d36 100644
--- a/package/package.json
+++ b/package/package.json
@@ -94,7 +94,6 @@
"emoji-mart": ">=5.4.0",
"react-native": ">=0.73.0",
"react-native-gesture-handler": ">=2.18.0",
- "react-native-keyboard-controller": ">=1.20.2",
"react-native-reanimated": ">=3.16.0",
"react-native-safe-area-context": ">=5.4.1",
"react-native-svg": ">=15.8.0",
@@ -112,9 +111,6 @@
},
"@emoji-mart/data": {
"optional": true
- },
- "react-native-keyboard-controller": {
- "optional": true
}
},
"devDependencies": {
@@ -163,7 +159,6 @@
"react-native": "0.80.2",
"react-native-builder-bob": "0.40.11",
"react-native-gesture-handler": "^2.26.0",
- "react-native-keyboard-controller": "^1.20.2",
"react-native-reanimated": "3.18.0",
"react-native-safe-area-context": "^5.6.1",
"react-native-svg": "15.12.0",
diff --git a/package/src/components/AttachmentPicker/AttachmentPicker.tsx b/package/src/components/AttachmentPicker/AttachmentPicker.tsx
index 3b8586e82f..cd4d786930 100644
--- a/package/src/components/AttachmentPicker/AttachmentPicker.tsx
+++ b/package/src/components/AttachmentPicker/AttachmentPicker.tsx
@@ -1,7 +1,6 @@
import React, { useEffect, useMemo, useRef, useState } from 'react';
import {
BackHandler,
- EmitterSubscription,
Keyboard,
Platform,
View,
@@ -21,7 +20,6 @@ import { useComponentsContext } from '../../contexts/componentsContext/Component
import { useTheme } from '../../contexts/themeContext/ThemeContext';
import { useStableCallback } from '../../hooks';
import { BottomSheet } from '../BottomSheetCompatibility/BottomSheet';
-import { KeyboardControllerPackage } from '../KeyboardCompatibleView/KeyboardControllerAvoidingView';
dayjs.extend(duration);
@@ -41,6 +39,7 @@ export const AttachmentPicker = () => {
attachmentPickerBottomSheetHeight,
bottomSheetRef: ref,
bottomInset,
+ topInset,
disableAttachmentPicker,
} = useAttachmentPickerContext();
const { AttachmentPickerContent, AttachmentPickerSelectionBar } = useComponentsContext();
@@ -78,18 +77,10 @@ export const AttachmentPicker = () => {
}
closePicker();
};
- let keyboardSubscription: EmitterSubscription | null = null;
- if (KeyboardControllerPackage?.KeyboardEvents) {
- keyboardSubscription = KeyboardControllerPackage.KeyboardEvents.addListener(
- 'keyboardWillShow',
- onKeyboardOpenHandler,
- );
- } else {
- const keyboardShowEvent = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';
- keyboardSubscription = Keyboard.addListener(keyboardShowEvent, onKeyboardOpenHandler);
- }
+ const keyboardShowEvent = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';
+ const keyboardSubscription = Keyboard.addListener(keyboardShowEvent, onKeyboardOpenHandler);
return () => {
- keyboardSubscription?.remove();
+ keyboardSubscription.remove();
};
}, [attachmentPickerStore, closePicker]);
@@ -104,7 +95,7 @@ export const AttachmentPicker = () => {
const initialSnapPoint = attachmentPickerBottomSheetHeight;
const pickerTopInset = Math.max(
0,
- windowHeight - attachmentPickerBottomSheetHeight - bottomInset,
+ windowHeight - topInset - attachmentPickerBottomSheetHeight - bottomInset,
);
/**
diff --git a/package/src/components/Channel/Channel.tsx b/package/src/components/Channel/Channel.tsx
index 3645e6dc8f..52070c39b8 100644
--- a/package/src/components/Channel/Channel.tsx
+++ b/package/src/components/Channel/Channel.tsx
@@ -114,7 +114,7 @@ import {
} from '../../utils/utils';
import { NotificationAnnouncer } from '../Accessibility/NotificationAnnouncer';
import { AttachmentPicker } from '../AttachmentPicker/AttachmentPicker';
-import type { KeyboardCompatibleViewProps } from '../KeyboardCompatibleView/KeyboardControllerAvoidingView';
+import type { KeyboardCompatibleViewProps } from '../KeyboardCompatibleView/KeyboardCompatibleView';
import { Emoji } from '../MessageMenu/EmojiPickerList';
import { emojis } from '../MessageMenu/emojis';
import { toUnicodeScalarString } from '../MessageMenu/utils/toUnicodeScalarString';
@@ -496,7 +496,7 @@ const ChannelWithContext = (props: PropsWithChildren) =
thread: threadFromProps,
threadList,
threadMessages,
- topInset,
+ topInset = 0,
isOnline,
maximumMessageLimit,
initializeOnMount = true,
@@ -567,6 +567,18 @@ const ChannelWithContext = (props: PropsWithChildren) =
channel,
});
+ const shouldLoadInitialChannelAtFirstUnreadMessage = useStableCallback((unreadCount?: number) => {
+ if (messageId || !initialScrollToFirstUnreadMessage || !client.user) {
+ return false;
+ }
+
+ return (unreadCount ?? channel.countUnread()) > scrollToFirstUnreadThreshold;
+ });
+
+ const hasPendingInitialTargetLoad = useStableCallback(() => {
+ return !!messageId || shouldLoadInitialChannelAtFirstUnreadMessage();
+ });
+
const { setMessages: copyMessagesStateFromChannel, viewabilityChangedCallback } =
usePrunableMessageList({ maximumMessageLimit, setMessages: rawCopyMessagesStateFromChannel });
@@ -693,6 +705,7 @@ const ChannelWithContext = (props: PropsWithChildren) =
const initChannel = async () => {
setLastRead(new Date());
const unreadCount = channel.countUnread();
+ const shouldLoadAtFirstUnread = shouldLoadInitialChannelAtFirstUnreadMessage(unreadCount);
if (!channel || !shouldSyncChannel) {
return;
}
@@ -722,13 +735,14 @@ const ChannelWithContext = (props: PropsWithChildren) =
if (messageId) {
await loadChannelAroundMessage({ messageId, setTargetedMessage });
- } else if (
- initialScrollToFirstUnreadMessage &&
- client.user &&
- unreadCount > scrollToFirstUnreadThreshold
- ) {
+ } else if (shouldLoadAtFirstUnread) {
+ const clientUserId = client.user?.id;
+ if (!clientUserId) {
+ return;
+ }
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
- const { user, ...ownReadState } = channel.state.read[client.user.id];
+ const { user, ...ownReadState } = channel.state.read[clientUserId];
await loadChannelAtFirstUnreadMessage({
channelUnreadState: ownReadState,
@@ -1578,6 +1592,7 @@ const ChannelWithContext = (props: PropsWithChildren) =
setChannelUnreadState,
setLastRead,
setTargetedMessage,
+ hasPendingInitialTargetLoad,
targetedMessage,
threadList,
uploadAbortControllerRef,
diff --git a/package/src/components/Channel/hooks/useCreateChannelContext.ts b/package/src/components/Channel/hooks/useCreateChannelContext.ts
index 5c7c9e33ca..8e8870707c 100644
--- a/package/src/components/Channel/hooks/useCreateChannelContext.ts
+++ b/package/src/components/Channel/hooks/useCreateChannelContext.ts
@@ -27,6 +27,7 @@ export const useCreateChannelContext = ({
setChannelUnreadState,
setLastRead,
setTargetedMessage,
+ hasPendingInitialTargetLoad,
targetedMessage,
threadList,
uploadAbortControllerRef,
@@ -69,6 +70,7 @@ export const useCreateChannelContext = ({
setChannelUnreadState,
setLastRead,
setTargetedMessage,
+ hasPendingInitialTargetLoad,
targetedMessage,
threadList,
uploadAbortControllerRef,
diff --git a/package/src/components/ImageGallery/ImageGallery.tsx b/package/src/components/ImageGallery/ImageGallery.tsx
index d3af58ec2d..a07076bc59 100644
--- a/package/src/components/ImageGallery/ImageGallery.tsx
+++ b/package/src/components/ImageGallery/ImageGallery.tsx
@@ -36,7 +36,7 @@ import { useViewport } from '../../hooks/useViewport';
import { IconProps } from '../../icons/utils/base';
import { ImageGalleryState } from '../../state-store/image-gallery-state-store';
import { FileTypes } from '../../types/types';
-import { dismissKeyboard } from '../KeyboardCompatibleView/KeyboardControllerAvoidingView';
+import { dismissKeyboard } from '../KeyboardCompatibleView/KeyboardCompatibleView';
import { BottomSheetModal } from '../UIComponents';
export type ImageGalleryActionHandler = () => Promise | void;
diff --git a/package/src/components/KeyboardCompatibleView/KeyboardCompatibleView.tsx b/package/src/components/KeyboardCompatibleView/KeyboardCompatibleView.tsx
index 52f599ea16..2371c6e364 100644
--- a/package/src/components/KeyboardCompatibleView/KeyboardCompatibleView.tsx
+++ b/package/src/components/KeyboardCompatibleView/KeyboardCompatibleView.tsx
@@ -22,6 +22,12 @@ import {
import { KeyboardProvider } from '../../contexts/keyboardContext/KeyboardContext';
+export type KeyboardCompatibleViewProps = KeyboardAvoidingViewProps;
+
+export const dismissKeyboard = () => {
+ Keyboard.dismiss();
+};
+
type State = {
bottom: number;
};
diff --git a/package/src/components/KeyboardCompatibleView/KeyboardControllerAvoidingView.tsx b/package/src/components/KeyboardCompatibleView/KeyboardControllerAvoidingView.tsx
deleted file mode 100644
index fd31080d70..0000000000
--- a/package/src/components/KeyboardCompatibleView/KeyboardControllerAvoidingView.tsx
+++ /dev/null
@@ -1,69 +0,0 @@
-import React, { useEffect } from 'react';
-
-import {
- Keyboard,
- KeyboardAvoidingViewProps as ReactNativeKeyboardAvoidingViewProps,
-} from 'react-native';
-
-import { KeyboardCompatibleView as KeyboardCompatibleViewDefault } from './KeyboardCompatibleView';
-
-type ExtraKeyboardControllerProps = {
- behavior?: 'translate-with-padding';
-};
-
-type KeyboardControllerModule = typeof import('react-native-keyboard-controller');
-
-const optionalRequire = (): T | undefined => {
- try {
- return require('react-native-keyboard-controller') as T;
- } catch {
- return undefined;
- }
-};
-
-export type KeyboardCompatibleViewProps = ReactNativeKeyboardAvoidingViewProps &
- ExtraKeyboardControllerProps;
-
-const KeyboardControllerPackage = optionalRequire();
-
-const { AndroidSoftInputModes, KeyboardController, KeyboardProvider, KeyboardAvoidingView } =
- KeyboardControllerPackage ?? {};
-
-export const KeyboardCompatibleView = (props: KeyboardCompatibleViewProps) => {
- const { behavior = 'translate-with-padding', children, ...rest } = props;
-
- useEffect(() => {
- if (AndroidSoftInputModes) {
- KeyboardController?.setInputMode(AndroidSoftInputModes.SOFT_INPUT_ADJUST_RESIZE);
- }
-
- return () => KeyboardController?.setDefaultMode();
- }, []);
-
- if (KeyboardProvider && KeyboardAvoidingView) {
- return (
-
- {/* @ts-expect-error - The reason is that react-native-keyboard-controller's KeyboardAvoidingViewProps is a discriminated union, not a simple behavior union so it complains about the `position` value passed. */}
-
- {children}
-
-
- );
- }
- const compatibleBehavior = behavior === 'translate-with-padding' ? 'padding' : behavior;
-
- return (
-
- {children}
-
- );
-};
-
-export const dismissKeyboard = () => {
- if (KeyboardControllerPackage?.KeyboardController) {
- KeyboardControllerPackage?.KeyboardController.dismiss();
- }
- Keyboard.dismiss();
-};
-
-export { KeyboardControllerPackage };
diff --git a/package/src/components/Message/Message.tsx b/package/src/components/Message/Message.tsx
index 316979be3f..57768e813e 100644
--- a/package/src/components/Message/Message.tsx
+++ b/package/src/components/Message/Message.tsx
@@ -77,7 +77,7 @@ import {
MessageStatusTypes,
} from '../../utils/utils';
import type { Thumbnail } from '../Attachment/utils/buildGallery/types';
-import { dismissKeyboard } from '../KeyboardCompatibleView/KeyboardControllerAvoidingView';
+import { dismissKeyboard } from '../KeyboardCompatibleView/KeyboardCompatibleView';
import { BottomSheetModal } from '../UIComponents';
const createMessageOverlayId = (messageId?: string) =>
diff --git a/package/src/components/Message/MessageItemView/MessageContent.tsx b/package/src/components/Message/MessageItemView/MessageContent.tsx
index 4f94ad2383..fc1f92bc18 100644
--- a/package/src/components/Message/MessageItemView/MessageContent.tsx
+++ b/package/src/components/Message/MessageItemView/MessageContent.tsx
@@ -656,7 +656,6 @@ const styles = StyleSheet.create({
containerInner: {
borderTopLeftRadius: components.messageBubbleRadiusGroupBottom,
borderTopRightRadius: components.messageBubbleRadiusGroupBottom,
- overflow: 'hidden',
},
contentBody: {
flexShrink: 1,
diff --git a/package/src/components/MessageList/MessageFlashList.tsx b/package/src/components/MessageList/MessageFlashList.tsx
index 05a033b949..e9f8fb7bd7 100644
--- a/package/src/components/MessageList/MessageFlashList.tsx
+++ b/package/src/components/MessageList/MessageFlashList.tsx
@@ -132,6 +132,7 @@ type MessageFlashListPropsWithContext = Pick<
| 'scrollToFirstUnreadThreshold'
| 'setChannelUnreadState'
| 'setTargetedMessage'
+ | 'hasPendingInitialTargetLoad'
| 'targetedMessage'
| 'threadList'
| 'maximumMessageLimit'
@@ -289,6 +290,7 @@ const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) =>
setChannelUnreadState,
setFlatListRef,
setTargetedMessage,
+ hasPendingInitialTargetLoad,
targetedMessage,
thread,
threadInstance,
@@ -388,11 +390,15 @@ const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) =>
useEffect(() => {
if (autoscrollToRecent && flashListRef.current) {
+ if (hasPendingInitialTargetLoad?.()) {
+ return;
+ }
+
flashListRef.current.scrollToEnd({
animated: true,
});
}
- }, [autoscrollToRecent]);
+ }, [autoscrollToRecent, hasPendingInitialTargetLoad]);
const maintainVisibleContentPosition = useMemo(() => {
return {
@@ -408,18 +414,6 @@ const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) =>
}
}, [disabled]);
- const indexToScrollToRef = useRef(undefined);
-
- const initialIndexToScrollTo = useMemo(() => {
- return targetedMessage
- ? processedMessageList.findIndex((message) => message?.id === targetedMessage)
- : -1;
- }, [processedMessageList, targetedMessage]);
-
- useEffect(() => {
- indexToScrollToRef.current = initialIndexToScrollTo;
- }, [initialIndexToScrollTo]);
-
/**
* Check if a messageId needs to be scrolled to after list loads, and scroll to it
* Note: This effect fires on every list change with a small debounce so that scrolling isnt abrupted by an immediate rerender
@@ -440,13 +434,29 @@ const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) =>
scrollToDebounceTimeoutRef.current = setTimeout(() => {
clearTimeout(scrollToDebounceTimeoutRef.current);
- // now scroll to it
- flashListRef.current?.scrollToIndex({
- animated: true,
- index: indexOfParentInMessageList,
- viewPosition: 0.5,
+ const scrollToIndex = async () => {
+ const list = flashListRef.current;
+
+ if (!list) {
+ return false;
+ }
+
+ await list.scrollToIndex({
+ index: indexOfParentInMessageList,
+ animated: true,
+ viewPosition: 0.5,
+ });
+
+ return true;
+ };
+
+ requestAnimationFrame(async () => {
+ await scrollToIndex();
+ requestAnimationFrame(async () => {
+ await scrollToIndex();
+ setTargetedMessage(undefined);
+ });
});
- setTargetedMessage(undefined);
}, WAIT_FOR_SCROLL_TIMEOUT);
}
}, [loadChannelAroundMessage, processedMessageList, setTargetedMessage, targetedMessage]);
@@ -456,8 +466,6 @@ const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) =>
(message) => message?.id === messageId,
);
- indexToScrollToRef.current = indexOfParentInMessageList;
-
try {
if (indexOfParentInMessageList === -1) {
clearTimeout(scrollToDebounceTimeoutRef.current);
@@ -529,7 +537,6 @@ const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) =>
setScrollToBottomButtonVisible(true);
return;
} else {
- indexToScrollToRef.current = undefined;
setAutoscrollToRecent(true);
}
const latestNonCurrentMessageBeforeUpdate = latestNonCurrentMessageBeforeUpdateRef.current;
@@ -1064,9 +1071,6 @@ const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) =>
data={processedMessageList}
drawDistance={800}
getItemType={getItemTypeInternal}
- initialScrollIndex={
- indexToScrollToRef.current === -1 ? undefined : indexToScrollToRef.current
- }
keyboardShouldPersistTaps='handled'
keyExtractor={keyExtractor}
ListFooterComponent={ListFooterComponent}
@@ -1203,6 +1207,7 @@ export const MessageFlashList = (props: MessageFlashListProps) => {
scrollToFirstUnreadThreshold,
setChannelUnreadState,
setTargetedMessage,
+ hasPendingInitialTargetLoad,
targetedMessage,
threadList,
} = useChannelContext();
@@ -1246,6 +1251,7 @@ export const MessageFlashList = (props: MessageFlashListProps) => {
scrollToFirstUnreadThreshold,
setChannelUnreadState,
setTargetedMessage,
+ hasPendingInitialTargetLoad,
shouldShowUnreadUnderlay,
targetedMessage,
thread,
diff --git a/package/src/components/Thread/__tests__/__snapshots__/Thread.test.tsx.snap b/package/src/components/Thread/__tests__/__snapshots__/Thread.test.tsx.snap
index a40eb0ce5e..f41411a83b 100644
--- a/package/src/components/Thread/__tests__/__snapshots__/Thread.test.tsx.snap
+++ b/package/src/components/Thread/__tests__/__snapshots__/Thread.test.tsx.snap
@@ -506,7 +506,6 @@ exports[`Thread should match thread snapshot 1`] = `
{
"borderTopLeftRadius": 20,
"borderTopRightRadius": 20,
- "overflow": "hidden",
},
{
"backgroundColor": "#ebeef1",
@@ -849,7 +848,6 @@ exports[`Thread should match thread snapshot 1`] = `
{
"borderTopLeftRadius": 20,
"borderTopRightRadius": 20,
- "overflow": "hidden",
},
{
"backgroundColor": "#ebeef1",
@@ -1225,7 +1223,6 @@ exports[`Thread should match thread snapshot 1`] = `
{
"borderTopLeftRadius": 20,
"borderTopRightRadius": 20,
- "overflow": "hidden",
},
{
"backgroundColor": "#ebeef1",
@@ -1559,7 +1556,6 @@ exports[`Thread should match thread snapshot 1`] = `
{
"borderTopLeftRadius": 20,
"borderTopRightRadius": 20,
- "overflow": "hidden",
},
{
"backgroundColor": "#ebeef1",
diff --git a/package/src/components/UIComponents/BottomSheetModal.tsx b/package/src/components/UIComponents/BottomSheetModal.tsx
index 843aec89b8..337f4d3f2a 100644
--- a/package/src/components/UIComponents/BottomSheetModal.tsx
+++ b/package/src/components/UIComponents/BottomSheetModal.tsx
@@ -19,7 +19,6 @@ import {
View,
} from 'react-native';
import { Gesture, GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler';
-import type { KeyboardEventData } from 'react-native-keyboard-controller';
import Animated, {
Easing,
FadeIn,
@@ -43,7 +42,6 @@ import { BottomSheetProvider } from '../../contexts/bottomSheetContext/BottomShe
import { useTheme } from '../../contexts/themeContext/ThemeContext';
import { useStableCallback } from '../../hooks';
import { primitives } from '../../theme';
-import { KeyboardControllerPackage } from '../KeyboardCompatibleView/KeyboardControllerAvoidingView';
export type BottomSheetModalProps = {
/**
@@ -377,18 +375,11 @@ const BottomSheetModalInner = (props: PropsWithChildren)
const listeners: EventSubscription[] = [];
- if (KeyboardControllerPackage?.KeyboardEvents) {
- const keyboardDidShowKC = (event: KeyboardEventData) => {
- animateKeyboardOffset(event.height);
- };
-
+ if (Platform.OS === 'ios') {
listeners.push(
- KeyboardControllerPackage.KeyboardEvents.addListener('keyboardDidShow', keyboardDidShowKC),
- KeyboardControllerPackage.KeyboardEvents.addListener('keyboardDidHide', keyboardDidHide),
+ Keyboard.addListener('keyboardWillShow', keyboardDidShowRN),
+ Keyboard.addListener('keyboardWillHide', keyboardDidHide),
);
- } else if (Platform.OS === 'ios') {
- listeners.push(Keyboard.addListener('keyboardWillShow', keyboardDidShowRN));
- listeners.push(Keyboard.addListener('keyboardWillHide', keyboardDidHide));
}
return () => listeners.forEach((l) => l.remove());
diff --git a/package/src/contexts/channelContext/ChannelContext.tsx b/package/src/contexts/channelContext/ChannelContext.tsx
index 6167626f5d..a14119ac0e 100644
--- a/package/src/contexts/channelContext/ChannelContext.tsx
+++ b/package/src/contexts/channelContext/ChannelContext.tsx
@@ -112,6 +112,12 @@ export type ChannelContextValue = {
setChannelUnreadState: (data: ChannelUnreadStateStoreType['channelUnreadState']) => void;
setLastRead: React.Dispatch>;
setTargetedMessage: (messageId?: string) => void;
+ /**
+ * Returns true when Channel is about to load an initial targeted message.
+ *
+ * @internal
+ */
+ hasPendingInitialTargetLoad?: () => boolean;
/**
* Abort controller for cancelling async requests made for uploading images/files
* Its a map of filename and AbortController
diff --git a/package/src/contexts/componentsContext/defaultComponents.ts b/package/src/contexts/componentsContext/defaultComponents.ts
index 6735fba586..4aac65dee5 100644
--- a/package/src/contexts/componentsContext/defaultComponents.ts
+++ b/package/src/contexts/componentsContext/defaultComponents.ts
@@ -50,7 +50,7 @@ import { ImageGalleryGrid } from '../../components/ImageGallery/components/Image
import { EmptyStateIndicator } from '../../components/Indicators/EmptyStateIndicator';
import { LoadingErrorIndicator } from '../../components/Indicators/LoadingErrorIndicator';
import { LoadingIndicator } from '../../components/Indicators/LoadingIndicator';
-import { KeyboardCompatibleView } from '../../components/KeyboardCompatibleView/KeyboardControllerAvoidingView';
+import { KeyboardCompatibleView } from '../../components/KeyboardCompatibleView/KeyboardCompatibleView';
import { Message } from '../../components/Message/Message';
import { MessagePinnedHeader } from '../../components/Message/MessageItemView/Headers/MessagePinnedHeader';
import { MessageReminderHeader } from '../../components/Message/MessageItemView/Headers/MessageReminderHeader';
diff --git a/package/src/contexts/messageInputContext/MessageInputContext.tsx b/package/src/contexts/messageInputContext/MessageInputContext.tsx
index af240756a0..dd6ad459ac 100644
--- a/package/src/contexts/messageInputContext/MessageInputContext.tsx
+++ b/package/src/contexts/messageInputContext/MessageInputContext.tsx
@@ -23,7 +23,7 @@ import {
import { useCreateMessageInputContext } from './hooks/useCreateMessageInputContext';
import { useMessageComposer } from './hooks/useMessageComposer';
-import { dismissKeyboard } from '../../components/KeyboardCompatibleView/KeyboardControllerAvoidingView';
+import { dismissKeyboard } from '../../components/KeyboardCompatibleView/KeyboardCompatibleView';
import { parseLinksFromText } from '../../components/Message/MessageItemView/utils/parseLinks';
import { useAudioRecorder } from '../../components/MessageInput/hooks/useAudioRecorder';
import { useNotificationApi } from '../../components/Notifications';
diff --git a/package/src/hooks/useAfterKeyboardOpenCallback.ts b/package/src/hooks/useAfterKeyboardOpenCallback.ts
index 199bd92cc4..9ac6a78a8e 100644
--- a/package/src/hooks/useAfterKeyboardOpenCallback.ts
+++ b/package/src/hooks/useAfterKeyboardOpenCallback.ts
@@ -3,7 +3,6 @@ import { EventSubscription, Keyboard, Platform } from 'react-native';
import { useStableCallback } from './useStableCallback';
-import { KeyboardControllerPackage } from '../components/KeyboardCompatibleView/KeyboardControllerAvoidingView';
import { useMessageInputContext } from '../contexts/messageInputContext/MessageInputContext';
/**
@@ -50,9 +49,7 @@ export const useAfterKeyboardOpenCallback = (
const keyboardEvent = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';
- keyboardSubscriptionRef.current = KeyboardControllerPackage?.KeyboardEvents
- ? KeyboardControllerPackage.KeyboardEvents.addListener(keyboardEvent, runCallback)
- : Keyboard.addListener(keyboardEvent, runCallback);
+ keyboardSubscriptionRef.current = Keyboard.addListener(keyboardEvent, runCallback);
inputBoxRef.current.focus();
});
diff --git a/package/src/hooks/useKeyboardVisibility.ts b/package/src/hooks/useKeyboardVisibility.ts
index cffd977b31..2218f6321a 100644
--- a/package/src/hooks/useKeyboardVisibility.ts
+++ b/package/src/hooks/useKeyboardVisibility.ts
@@ -1,7 +1,5 @@
import { useEffect, useState } from 'react';
-import { EventSubscription, Keyboard, Platform } from 'react-native';
-
-import { KeyboardControllerPackage } from '../components/KeyboardCompatibleView/KeyboardControllerAvoidingView';
+import { Keyboard, Platform } from 'react-native';
/**
* A custom hook that provides a boolean value indicating whether the keyboard is visible.
@@ -11,30 +9,14 @@ export const useKeyboardVisibility = () => {
const [isKeyboardVisible, setIsKeyboardVisible] = useState(false);
useEffect(() => {
- const listeners: EventSubscription[] = [];
- if (KeyboardControllerPackage?.KeyboardEvents) {
- listeners.push(
- KeyboardControllerPackage.KeyboardEvents.addListener('keyboardWillShow', () =>
- setIsKeyboardVisible(true),
- ),
- );
- listeners.push(
- KeyboardControllerPackage.KeyboardEvents.addListener('keyboardWillHide', () =>
- setIsKeyboardVisible(false),
- ),
- );
- } else {
- listeners.push(
- Keyboard.addListener(Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow', () =>
- setIsKeyboardVisible(true),
- ),
- );
- listeners.push(
- Keyboard.addListener(Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide', () =>
- setIsKeyboardVisible(false),
- ),
- );
- }
+ const listeners = [
+ Keyboard.addListener(Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow', () =>
+ setIsKeyboardVisible(true),
+ ),
+ Keyboard.addListener(Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide', () =>
+ setIsKeyboardVisible(false),
+ ),
+ ];
return () => listeners.forEach((listener) => listener.remove());
}, []);
diff --git a/package/src/state-store/audio-player.ts b/package/src/state-store/audio-player.ts
index 8eb2a4394b..bd0a021937 100644
--- a/package/src/state-store/audio-player.ts
+++ b/package/src/state-store/audio-player.ts
@@ -425,17 +425,10 @@ export class AudioPlayer {
}
this.position = positionInMillis;
if (this.isExpoCLI) {
- if (positionInMillis === 0) {
- // If currentTime is 0, we should replay the video from 0th position.
- if (this.playerRef?.replayAsync) {
- await this.playerRef.replayAsync({});
- }
+ if (this.playerRef?.setPositionAsync) {
+ await this.playerRef.setPositionAsync(positionInMillis);
} else {
- if (this.playerRef?.setPositionAsync) {
- await this.playerRef.setPositionAsync(positionInMillis);
- } else {
- this.notifyError('seek-not-supported');
- }
+ this.notifyError('seek-not-supported');
}
} else {
if (this.playerRef?.seek) {