diff --git a/packages/uikit-react-native/src/components/ChannelInput/SendInput.tsx b/packages/uikit-react-native/src/components/ChannelInput/SendInput.tsx index 98abbe665..73ee0dc76 100644 --- a/packages/uikit-react-native/src/components/ChannelInput/SendInput.tsx +++ b/packages/uikit-react-native/src/components/ChannelInput/SendInput.tsx @@ -56,6 +56,7 @@ const SendInput = forwardRef(function SendInput( setMessageToReply, messageForThread, partialTextInputProps, + placeholder, }, ref, ) { @@ -174,6 +175,8 @@ const SendInput = forwardRef(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; diff --git a/packages/uikit-react-native/src/components/ChannelInput/index.tsx b/packages/uikit-react-native/src/components/ChannelInput/index.tsx index c92a41e37..38812b3d9 100644 --- a/packages/uikit-react-native/src/components/ChannelInput/index.tsx +++ b/packages/uikit-react-native/src/components/ChannelInput/index.tsx @@ -91,6 +91,11 @@ export type ChannelInputProps = { // TextInput props - only safe properties that don't interfere with UIKit functionality partialTextInputProps?: Partial>; + + /** 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 }); @@ -167,7 +172,7 @@ const ChannelInput = (props: ChannelInputProps) => { { void, chatDisabled: bo }; const useAutoFocusOnEditMode = ( - textInputRef: React.MutableRefObject, + textInputRef: React.RefObject, messageToEdit?: SendbirdBaseMessage, ) => { useEffect(() => { diff --git a/packages/uikit-react-native/src/domain/groupChannel/types.ts b/packages/uikit-react-native/src/domain/groupChannel/types.ts index a1ba976c7..67d1d08c5 100644 --- a/packages/uikit-react-native/src/domain/groupChannel/types.ts +++ b/packages/uikit-react-native/src/domain/groupChannel/types.ts @@ -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. @@ -114,7 +115,8 @@ export interface GroupChannelProps { | 'onPressUpdateFileMessage' | 'SuggestedMentionList' | 'AttachmentsButton' - | 'partialTextInputProps', + | 'partialTextInputProps' + | 'placeholder', 'inputDisabled' >; diff --git a/packages/uikit-react-native/src/domain/groupChannelThread/types.ts b/packages/uikit-react-native/src/domain/groupChannelThread/types.ts index 61f247d4e..50a903bd5 100644 --- a/packages/uikit-react-native/src/domain/groupChannelThread/types.ts +++ b/packages/uikit-react-native/src/domain/groupChannelThread/types.ts @@ -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; @@ -85,7 +86,8 @@ export interface GroupChannelThreadProps { | 'onPressUpdateUserMessage' | 'onPressUpdateFileMessage' | 'SuggestedMentionList' - | 'AttachmentsButton', + | 'AttachmentsButton' + | 'placeholder', 'inputDisabled' >; diff --git a/packages/uikit-react-native/src/domain/openChannel/types.ts b/packages/uikit-react-native/src/domain/openChannel/types.ts index 1a160aa41..eb00ccb02 100644 --- a/packages/uikit-react-native/src/domain/openChannel/types.ts +++ b/packages/uikit-react-native/src/domain/openChannel/types.ts @@ -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; @@ -78,7 +79,8 @@ export type OpenChannelProps = { | 'onPressSendFileMessage' | 'onPressUpdateUserMessage' | 'onPressUpdateFileMessage' - | 'AttachmentsButton', + | 'AttachmentsButton' + | 'placeholder', 'inputDisabled' >; diff --git a/packages/uikit-react-native/src/fragments/createGroupChannelFragment.tsx b/packages/uikit-react-native/src/fragments/createGroupChannelFragment.tsx index 88bdce944..8ad3ec4f7 100644 --- a/packages/uikit-react-native/src/fragments/createGroupChannelFragment.tsx +++ b/packages/uikit-react-native/src/fragments/createGroupChannelFragment.tsx @@ -71,6 +71,7 @@ const createGroupChannelFragment = (initModule?: Partial): G flatListProps, messageListQueryParams, partialTextInputProps, + placeholder, collectionCreator, }) => { const { playerService, recorderService } = usePlatformService(); @@ -343,6 +344,7 @@ const createGroupChannelFragment = (initModule?: Partial): G onPressUpdateUserMessage={onPressUpdateUserMessage} onPressUpdateFileMessage={onPressUpdateFileMessage} partialTextInputProps={partialTextInputProps} + placeholder={placeholder} /> diff --git a/packages/uikit-react-native/src/fragments/createGroupChannelThreadFragment.tsx b/packages/uikit-react-native/src/fragments/createGroupChannelThreadFragment.tsx index f091fed00..0015c2c51 100644 --- a/packages/uikit-react-native/src/fragments/createGroupChannelThreadFragment.tsx +++ b/packages/uikit-react-native/src/fragments/createGroupChannelThreadFragment.tsx @@ -53,6 +53,7 @@ const createGroupChannelThreadFragment = ( keyboardAvoidOffset, sortComparator = threadMessageComparator, flatListProps, + placeholder, }) => { const { playerService, recorderService } = usePlatformService(); const { sdk, currentUser, sbOptions, voiceMessageStatusManager, groupChannelFragmentOptions } = useSendbirdChat(); @@ -259,6 +260,7 @@ const createGroupChannelThreadFragment = ( onPressSendFileMessage={onPressSendFileMessage} onPressUpdateUserMessage={onPressUpdateUserMessage} onPressUpdateFileMessage={onPressUpdateFileMessage} + placeholder={placeholder} /> diff --git a/packages/uikit-react-native/src/fragments/createOpenChannelFragment.tsx b/packages/uikit-react-native/src/fragments/createOpenChannelFragment.tsx index 0fe012c47..6d1fdb954 100644 --- a/packages/uikit-react-native/src/fragments/createOpenChannelFragment.tsx +++ b/packages/uikit-react-native/src/fragments/createOpenChannelFragment.tsx @@ -52,6 +52,7 @@ const createOpenChannelFragment = (initModule?: Partial): Ope flatListProps, queryCreator, sortComparator = messageComparator, + placeholder, }) => { const { sdk, currentUser } = useSendbirdChat(); @@ -182,6 +183,7 @@ const createOpenChannelFragment = (initModule?: Partial): Ope onPressSendFileMessage={onPressSendFileMessage} onPressUpdateUserMessage={onPressUpdateUserMessage} onPressUpdateFileMessage={onPressUpdateFileMessage} + placeholder={placeholder} /> diff --git a/packages/uikit-react-native/src/hooks/useMentionTextInput.ts b/packages/uikit-react-native/src/hooks/useMentionTextInput.ts index 48eaf9562..9c8342b72 100644 --- a/packages/uikit-react-native/src/hooks/useMentionTextInput.ts +++ b/packages/uikit-react-native/src/hooks/useMentionTextInput.ts @@ -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'; @@ -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; + selection: { start: number; end: number }; + onSelectionChange: (e: NativeSyntheticEvent) => 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([]); - const textInputRef = useRef(undefined); + const textInputRef = useRef(null); const [text, setText] = useState(''); const [selection, setSelection] = useState({ start: 0, end: 0 }); diff --git a/packages/uikit-react-native/src/index.ts b/packages/uikit-react-native/src/index.ts index 51053f8ca..d596a3cc4 100644 --- a/packages/uikit-react-native/src/index.ts +++ b/packages/uikit-react-native/src/index.ts @@ -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'; @@ -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';