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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ const SendInput = forwardRef<RNTextInput, SendInputProps>(function SendInput(
setMessageToReply,
messageForThread,
partialTextInputProps,
placeholder,
},
ref,
) {
Expand Down Expand Up @@ -174,6 +175,8 @@ const SendInput = forwardRef<RNTextInput, SendInputProps>(function SendInput(
const sheetItems = useChannelInputItems(channel, sendFileMessage);

const getPlaceholder = () => {
if (placeholder != null) return placeholder;

if (inputMuted) return STRINGS.LABELS.CHANNEL_INPUT_PLACEHOLDER_MUTED;
if (inputFrozen) return STRINGS.LABELS.CHANNEL_INPUT_PLACEHOLDER_DISABLED;
if (inputDisabled) return STRINGS.LABELS.CHANNEL_INPUT_PLACEHOLDER_DISABLED;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ export type ChannelInputProps = {

// TextInput props - only safe properties that don't interfere with UIKit functionality
partialTextInputProps?: Partial<Pick<TextInputProps, 'autoCorrect'>>;

/** Custom placeholder text for the send input.
* Overrides all default localization strings (including muted/frozen/disabled states) for send mode only.
* Edit mode continues to use the default localized placeholder. */
placeholder?: string;
};

const AUTO_FOCUS = Platform.select({ ios: false, android: true, default: false });
Expand Down Expand Up @@ -167,7 +172,7 @@ const ChannelInput = (props: ChannelInputProps) => {
<SendInput
{...props}
key={inputKeyToRemount}
ref={textInputRef as never}
ref={textInputRef}
text={text}
onChangeText={onChangeText}
onSelectionChange={onSelectionChange}
Expand All @@ -182,7 +187,7 @@ const ChannelInput = (props: ChannelInputProps) => {
<EditInput
{...props}
key={inputKeyToRemount}
ref={textInputRef as never}
ref={textInputRef}
text={text}
onChangeText={onChangeText}
autoFocus={AUTO_FOCUS}
Expand Down Expand Up @@ -237,7 +242,7 @@ const useTextClearOnDisabled = (setText: (val: string) => void, chatDisabled: bo
};

const useAutoFocusOnEditMode = (
textInputRef: React.MutableRefObject<TextInput | undefined>,
textInputRef: React.RefObject<TextInput | null>,
messageToEdit?: SendbirdBaseMessage,
) => {
useEffect(() => {
Expand Down
4 changes: 3 additions & 1 deletion packages/uikit-react-native/src/domain/groupChannel/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export interface GroupChannelProps {
searchItem?: GroupChannelProps['MessageList']['searchItem'];

partialTextInputProps?: GroupChannelProps['Input']['partialTextInputProps'];
placeholder?: GroupChannelProps['Input']['placeholder'];

/**
* @description You can specify the query parameters for the message list.
Expand Down Expand Up @@ -114,7 +115,8 @@ export interface GroupChannelProps {
| 'onPressUpdateFileMessage'
| 'SuggestedMentionList'
| 'AttachmentsButton'
| 'partialTextInputProps',
| 'partialTextInputProps'
| 'placeholder',
'inputDisabled'
>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export interface GroupChannelThreadProps {
keyboardAvoidOffset?: GroupChannelThreadProps['Provider']['keyboardAvoidOffset'];
flatListProps?: GroupChannelThreadProps['MessageList']['flatListProps'];
sortComparator?: UseGroupChannelMessagesOptions['sortComparator'];
placeholder?: GroupChannelThreadProps['Input']['placeholder'];
};
Header: {
onPressLeft: () => void;
Expand Down Expand Up @@ -85,7 +86,8 @@ export interface GroupChannelThreadProps {
| 'onPressUpdateUserMessage'
| 'onPressUpdateFileMessage'
| 'SuggestedMentionList'
| 'AttachmentsButton',
| 'AttachmentsButton'
| 'placeholder',
'inputDisabled'
>;

Expand Down
4 changes: 3 additions & 1 deletion packages/uikit-react-native/src/domain/openChannel/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export type OpenChannelProps = {
flatListProps?: OpenChannelProps['MessageList']['flatListProps'];
sortComparator?: UseOpenChannelMessagesOptions['sortComparator'];
queryCreator?: UseOpenChannelMessagesOptions['queryCreator'];
placeholder?: OpenChannelProps['Input']['placeholder'];
};
Header: {
rightIconName: keyof typeof Icon.Assets;
Expand Down Expand Up @@ -78,7 +79,8 @@ export type OpenChannelProps = {
| 'onPressSendFileMessage'
| 'onPressUpdateUserMessage'
| 'onPressUpdateFileMessage'
| 'AttachmentsButton',
| 'AttachmentsButton'
| 'placeholder',
'inputDisabled'
>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ const createGroupChannelFragment = (initModule?: Partial<GroupChannelModule>): G
flatListProps,
messageListQueryParams,
partialTextInputProps,
placeholder,
collectionCreator,
}) => {
const { playerService, recorderService } = usePlatformService();
Expand Down Expand Up @@ -343,6 +344,7 @@ const createGroupChannelFragment = (initModule?: Partial<GroupChannelModule>): G
onPressUpdateUserMessage={onPressUpdateUserMessage}
onPressUpdateFileMessage={onPressUpdateFileMessage}
partialTextInputProps={partialTextInputProps}
placeholder={placeholder}
/>
</StatusComposition>
</GroupChannelModule.Provider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ const createGroupChannelThreadFragment = (
keyboardAvoidOffset,
sortComparator = threadMessageComparator,
flatListProps,
placeholder,
}) => {
const { playerService, recorderService } = usePlatformService();
const { sdk, currentUser, sbOptions, voiceMessageStatusManager, groupChannelFragmentOptions } = useSendbirdChat();
Expand Down Expand Up @@ -259,6 +260,7 @@ const createGroupChannelThreadFragment = (
onPressSendFileMessage={onPressSendFileMessage}
onPressUpdateUserMessage={onPressUpdateUserMessage}
onPressUpdateFileMessage={onPressUpdateFileMessage}
placeholder={placeholder}
/>
</StatusComposition>
</GroupChannelThreadModule.Provider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const createOpenChannelFragment = (initModule?: Partial<OpenChannelModule>): Ope
flatListProps,
queryCreator,
sortComparator = messageComparator,
placeholder,
}) => {
const { sdk, currentUser } = useSendbirdChat();

Expand Down Expand Up @@ -182,6 +183,7 @@ const createOpenChannelFragment = (initModule?: Partial<OpenChannelModule>): Ope
onPressSendFileMessage={onPressSendFileMessage}
onPressUpdateUserMessage={onPressUpdateUserMessage}
onPressUpdateFileMessage={onPressUpdateFileMessage}
placeholder={placeholder}
/>
</StatusComposition>
</OpenChannelModule.Provider>
Expand Down
19 changes: 16 additions & 3 deletions packages/uikit-react-native/src/hooks/useMentionTextInput.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from 'react';
import { type RefObject, useEffect, useRef, useState } from 'react';
import type { NativeSyntheticEvent, TextInput, TextInputSelectionChangeEventData } from 'react-native';
import { Platform } from 'react-native';

Expand All @@ -7,12 +7,25 @@ import { SendbirdFileMessage, SendbirdUserMessage, replace, useFreshCallback } f
import type { MentionedUser } from '../types';
import { useSendbirdChat } from './useContext';

export interface UseMentionTextInputParams {
messageToEdit?: SendbirdUserMessage | SendbirdFileMessage;
}

export interface UseMentionTextInputReturn {
textInputRef: RefObject<TextInput | null>;
selection: { start: number; end: number };
onSelectionChange: (e: NativeSyntheticEvent<TextInputSelectionChangeEventData>) => void;
text: string;
onChangeText: (text: string, addedMentionedUser?: MentionedUser) => void;
mentionedUsers: MentionedUser[];
}

// Note: The selection change with the keyboard cursor might not work properly with RTL languages
const useMentionTextInput = (params: { messageToEdit?: SendbirdUserMessage | SendbirdFileMessage }) => {
const useMentionTextInput = (params: UseMentionTextInputParams): UseMentionTextInputReturn => {
const { mentionManager, sbOptions } = useSendbirdChat();

const mentionedUsersRef = useRef<MentionedUser[]>([]);
const textInputRef = useRef<TextInput | undefined>(undefined);
const textInputRef = useRef<TextInput | null>(null);

const [text, setText] = useState('');
const [selection, setSelection] = useState({ start: 0, end: 0 });
Expand Down
4 changes: 4 additions & 0 deletions packages/uikit-react-native/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { PromisePolyfill } from './utils/promise';

/** Components **/
export { default as ChannelInput } from './components/ChannelInput';
export type { ChannelInputProps } from './components/ChannelInput';
export { default as EditInput } from './components/ChannelInput/EditInput';
export { default as ChannelMessageList } from './components/ChannelMessageList';
export { default as GroupChannelMessageRenderer } from './components/GroupChannelMessageRenderer';
export { default as OpenChannelMessageRenderer } from './components/OpenChannelMessageRenderer';
Expand Down Expand Up @@ -61,6 +63,8 @@ export { LocalizationContext, LocalizationProvider } from './contexts/Localizati
export { default as useConnection } from './hooks/useConnection';
export { default as usePushTokenRegistration } from './hooks/usePushTokenRegistration';
export * from './hooks/useContext';
export { default as useMentionTextInput } from './hooks/useMentionTextInput';
export type { UseMentionTextInputParams, UseMentionTextInputReturn } from './hooks/useMentionTextInput';

/** Localization **/
export { createBaseStringSet } from './localization/createBaseStringSet';
Expand Down
Loading