Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
afc818f
fix: attachment picker performance issues
isekovanic Feb 5, 2026
a85a3ef
fix: bottom sheet animation concurrency
isekovanic Feb 6, 2026
0134258
feat: introduce separate state store for picker state
isekovanic Feb 6, 2026
6587b04
fix: failing tests
isekovanic Feb 6, 2026
38fc6fd
Merge branch 'develop' into feat/attachment-picker-rework
isekovanic Feb 6, 2026
079abb5
fix: resolve conflicts
isekovanic Feb 6, 2026
2a9b89e
chore: move picker down to message input
isekovanic Feb 6, 2026
6f6ba6e
fix: bottom sheet issues on android and tests
isekovanic Feb 6, 2026
d802299
fix: android scrollable flatlist
isekovanic Feb 6, 2026
932427b
feat: implement ios limited view
isekovanic Feb 7, 2026
063ce6f
fix: unmounting issues
isekovanic Feb 7, 2026
40733a3
fix: move selection bar to handle component
isekovanic Feb 9, 2026
ca2d8d6
chore: animate attach button
isekovanic Feb 9, 2026
cac2074
feat: implement the new attachment selection picker
isekovanic Feb 10, 2026
c2d570d
chore: add translations
isekovanic Feb 10, 2026
4888027
feat: button closing functionality
isekovanic Feb 10, 2026
361286a
fix: tests
isekovanic Feb 10, 2026
75b4555
feat: implement attachment picker content
isekovanic Feb 10, 2026
2849b73
feat: add file picker content
isekovanic Feb 10, 2026
5948e77
feat: introduce camera picker
isekovanic Feb 10, 2026
74709ab
fix: icons
isekovanic Feb 11, 2026
45a5796
feat: implement remaining pickers
isekovanic Feb 11, 2026
1b2cb76
feat: make content overridable
isekovanic Feb 11, 2026
5972086
fix: abstract away icons
isekovanic Feb 11, 2026
c8dddf0
fix: add missing translations
isekovanic Feb 11, 2026
e5c3c0f
fix: tests
isekovanic Feb 11, 2026
2c51cdd
feat: implement media picker items
isekovanic Feb 11, 2026
49da96e
feat: implement command picker designs
isekovanic Feb 11, 2026
17eafa9
fix: update icons
isekovanic Feb 11, 2026
a1929af
chore: remove redundant components
isekovanic Feb 11, 2026
d45228b
chore: remove remaining redundant props
isekovanic Feb 11, 2026
e3e82f2
Merge branch 'develop' into feat/attachment-picker-rework
isekovanic Feb 11, 2026
fc9afe6
fix: failing tests
isekovanic Feb 11, 2026
2528d97
fix: sheet opening unintentionally on safe area drag
isekovanic Feb 11, 2026
357c461
chore: remove unused cb
isekovanic Feb 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 23 additions & 11 deletions examples/SampleApp/ios/SampleApp.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

53 changes: 53 additions & 0 deletions examples/SampleApp/src/components/AttachmentPickerContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React, { useCallback, useState } from 'react';
import {
useAttachmentPickerState,
AttachmentPickerContentProps,
AttachmentPickerContent,
AttachmentPickerGenericContent,
useStableCallback,
useTheme,
useTranslationContext,
} from 'stream-chat-react-native';
import { ShareLocationIcon } from '../icons/ShareLocationIcon.tsx';
import { LiveLocationCreateModal } from './LocationSharing/CreateLocationModal.tsx';

export const CustomAttachmentPickerContent = (props: AttachmentPickerContentProps) => {
const [modalVisible, setModalVisible] = useState(false);
const { selectedPicker } = useAttachmentPickerState();
const { t } = useTranslationContext();
const {
theme: { semantics },
} = useTheme();

const Icon = useCallback(
() => <ShareLocationIcon stroke={semantics.textTertiary} />,
[semantics.textTertiary],
);

const onRequestClose = () => {
setModalVisible(false);
};

const onOpenModal = useStableCallback(() => {
setModalVisible(true);
});

if (selectedPicker === 'location') {
return (
<>
<AttachmentPickerGenericContent
Icon={Icon}
onPress={onOpenModal}
height={props.height}
buttonText={t('Share Location')}
description={t('Share your location with everyone')}
/>
{modalVisible ? (
<LiveLocationCreateModal visible={modalVisible} onRequestClose={onRequestClose} />
) : null}
</>
);
}

return <AttachmentPickerContent {...props} />;
};
48 changes: 32 additions & 16 deletions examples/SampleApp/src/components/AttachmentPickerSelectionBar.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,52 @@
import { useState } from 'react';
import { Pressable, StyleSheet, View } from 'react-native';
import { AttachmentPickerSelectionBar, useMessageInputContext } from 'stream-chat-react-native';
import React, { useState } from 'react';
import { StyleSheet, View } from 'react-native';
import {
AttachmentTypePickerButton,
useAttachmentPickerState,
CameraPickerButton,
CommandsPickerButton,
FilePickerButton,
MediaPickerButton,
PollPickerButton,
useAttachmentPickerContext,
useStableCallback,
} from 'stream-chat-react-native';
import { ShareLocationIcon } from '../icons/ShareLocationIcon';
import { LiveLocationCreateModal } from './LocationSharing/CreateLocationModal';

export const CustomAttachmentPickerSelectionBar = () => {
const [modalVisible, setModalVisible] = useState(false);
const { closeAttachmentPicker } = useMessageInputContext();
const { attachmentPickerStore } = useAttachmentPickerContext();
const { selectedPicker } = useAttachmentPickerState();

const onRequestClose = () => {
setModalVisible(false);
closeAttachmentPicker();
};

const onOpenModal = () => {
const onOpenModal = useStableCallback(() => {
attachmentPickerStore.setSelectedPicker('location');
setModalVisible(true);
};
});

return (
<View style={styles.selectionBar}>
<AttachmentPickerSelectionBar />
<Pressable style={styles.liveLocationButton} onPress={onOpenModal}>
<ShareLocationIcon />
</Pressable>
<LiveLocationCreateModal visible={modalVisible} onRequestClose={onRequestClose} />
<MediaPickerButton />
<CameraPickerButton />
<FilePickerButton />
<PollPickerButton />
<CommandsPickerButton />
<AttachmentTypePickerButton
Icon={ShareLocationIcon}
onPress={onOpenModal}
selected={selectedPicker === 'location'}
/>
{modalVisible ? (
<LiveLocationCreateModal visible={modalVisible} onRequestClose={onRequestClose} />
) : null}
</View>
);
};

const styles = StyleSheet.create({
selectionBar: { flexDirection: 'row', alignItems: 'center' },
liveLocationButton: {
paddingLeft: 4,
},
selectionBar: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 16, paddingBottom: 12, },
});
15 changes: 5 additions & 10 deletions examples/SampleApp/src/icons/ShareLocationIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
import Svg, { Path } from 'react-native-svg';
import { useTheme } from 'stream-chat-react-native';
import { ColorValue } from 'react-native';

// Icon for "Share Location" button, next to input box.
export const ShareLocationIcon = () => {
const {
theme: {
colors: { grey },
},
} = useTheme();
export const ShareLocationIcon = ({ stroke }: { stroke: ColorValue }) => {
return (
<Svg width={24} height={24} viewBox='0 0 24 24' fill='none'>
<Svg width={20} height={20} viewBox='0 0 24 24' fill='none'>
<Path
d='M12 12c-1.654 0-3-1.345-3-3 0-1.654 1.346-3 3-3s3 1.346 3 3c0 1.655-1.346 3-3 3zm0-4a1.001 1.001 0 101 1c0-.551-.449-1-1-1z'
fill={grey}
fill={stroke}
/>
<Path
fillRule='evenodd'
clipRule='evenodd'
d='M12 22s7-5.455 7-12.727C19 5.636 16.667 2 12 2S5 5.636 5 9.273C5 16.545 12 22 12 22zm1.915-4.857C15.541 15.032 17 12.277 17 9.273c0-1.412-.456-2.75-1.27-3.7C14.953 4.664 13.763 4 12 4s-2.953.664-3.73 1.573C7.456 6.523 7 7.86 7 9.273c0 3.004 1.459 5.759 3.085 7.87.678.88 1.358 1.614 1.915 2.166a21.689 21.689 0 001.915-2.166zm-.683 3.281s0 .001 0 0z'
fill={grey}
fill={stroke}
/>
</Svg>
);
Expand Down
2 changes: 2 additions & 0 deletions examples/SampleApp/src/screens/ChannelScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { MessageLocation } from '../components/LocationSharing/MessageLocation.t
import { useStreamChatContext } from '../context/StreamChatContext.tsx';
import { CustomAttachmentPickerSelectionBar } from '../components/AttachmentPickerSelectionBar.tsx';
import { MessageInfoBottomSheet } from '../components/MessageInfoBottomSheet.tsx';
import { CustomAttachmentPickerContent } from '../components/AttachmentPickerContent.tsx';

export type ChannelScreenNavigationProp = NativeStackNavigationProp<
StackNavigatorParamList,
Expand Down Expand Up @@ -223,6 +224,7 @@ export const ChannelScreen: React.FC<ChannelScreenProps> = ({
<Channel
audioRecordingEnabled={true}
AttachmentPickerSelectionBar={CustomAttachmentPickerSelectionBar}
AttachmentPickerContent={CustomAttachmentPickerContent}
channel={channel}
messageInputFloating={messageInputFloating}
onPressMessage={onPressMessage}
Expand Down
10 changes: 9 additions & 1 deletion package/jest-setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ jest.mock('react-native-reanimated', () => {
});

jest.mock('@react-native-community/netinfo', () => mockRNCNetInfo);

const BottomSheetMock = ({ handleComponent, children }) => (
<View>
{handleComponent()}
{children}
</View>
);
jest.mock('@gorhom/bottom-sheet', () => {
const react = require('react-native');
return {
Expand All @@ -48,8 +55,9 @@ jest.mock('@gorhom/bottom-sheet', () => {
BottomSheetModal: react.View,
BottomSheetModalProvider: react.View,
BottomSheetScrollView: react.ScrollView,
default: react.View,
default: BottomSheetMock,
TouchableOpacity: react.View,
useBottomSheetSpringConfigs: jest.fn(() => ({})),
};
});
jest.mock('@op-engineering/op-sqlite', () => {
Expand Down
Loading
Loading