Build chat in minutes with Sendbird UIKit open source code.
MIT License
Published by HoonBaek 10 months ago
--sendbird-vh
CSS Variable Logic in InviteUsers Component
InviteUsers
component.window
in SSR Environments
window
object. (Original Author: Aaron James King)NO_CHANNEL
Placeholder
NO_CHANNEL
placeholder after leaving all channels. (Original Author: Alden Quimby)Published by AhyoungRyu 10 months ago
animatedMessage
instead of highlightedMessage
in the smart appPropsWithChildren
.PropsWithChildren
.
PropsWithChildren<unknown>
instead of PropsWithChildren
.Published by AhyoungRyu 11 months ago
IntersectionObserver
uikitUploadSizeLimit
to the Open Channel Message Input
<Avatar />
component to fix ruined alignlatest_last_message
is the default order.<EditUserProfileUI />
into Modal and View parts<ReactionItem />
component<EmojiReactions />
Published by liamcho 11 months ago
TypingIndicatorBubble
is a new typing indicator UI that can be turned on through typingIndicatorTypes
option. When turned on, it will be displayed in MessageList
upon receiving typing event in real time.
typingIndicatorTypes
global optionTypingIndicatorType
enum
<App
appId={appId}
userId={userId}
uikitOptions={{
groupChannel: {
// Below turns on both bubble and text typing indicators. Default is Text only.
typingIndicatorTypes: new Set([TypingIndicatorType.Bubble, TypingIndicatorType.Text]),
}
}}
/>
TypingIndicatorBubble
const moveScroll = (): void => {
const current = scrollRef?.current;
if (current) {
const bottom = current.scrollHeight - current.scrollTop - current.offsetHeight;
if (scrollBottom < bottom && scrollBottom < SCROLL_BUFFER) {
// Move the scroll as much as the height of the message has changed
current.scrollTop += bottom - scrollBottom;
}
}
};
return (
<TypingIndicatorBubble
typingMembers={typingMembers}
handleScroll={moveScroll} // Scroll to the rendered typing indicator message IFF current scroll is bottom.
/>
);
eventHandlers.connection.onFailed
callback in setupConnection
. This callback will be called on connection failure
<Sendbird
appId={appId}
userId={undefined} // this will cause an error
eventHandlers={{
connection: {
onFailed: (error) => {
alert(error?.message); // display a browser alert and print the error message inside
}
}
}}
>
MessageContent
component: renderMessageMenu
, renderEmojiMenu
, and renderEmojiReactions
<Channel
renderMessageContent={(props) => {
return <MessageContent
{...props}
renderMessageMenu={(props) => {
return <MessageMenu {...props} />
}}
renderEmojiMenu={(props) => {
return <MessageEmojiMenu {...props} />
}}
renderEmojiReactions={(props) => {
return <EmojiReactions {...props} />
}}
/>
}}
/>
onProfileEditSuccess
prop to App
and ChannelList
componentsrenderFrozenNotification
in ChannelUIProps
<Channel
channelUrl={channelUrl}
renderFrozenNotification={() => {
return (
<div
className="sendbird-notification sendbird-notification--frozen sendbird-conversation__messages__notification"
>My custom Frozen Notification</div>
);
}}
/>
VoiceMessageInputWrapper
and useHandleUploadFiles
import { useHandleUploadFiles } from '@sendbird/uikit-react/Channel/hooks/useHandleUploadFiles'
import { VoiceMessageInputWrapper, VoiceMessageInputWrapperProps } from '@sendbird/uikit-react/Channel/components/MessageInput'
startingPoint
scrolls to the middle of the target message when it should be at the top of the messageOGMessage
being displayed as not fitting the containerThread
results in displaying resulting message in Channel
Published by liamcho 12 months ago
MessageContent
is not customizable with three new optional properties:
renderSenderProfile
, renderMessageBody
, and renderMessageHeader
import Channel from '@sendbird/uikit-react/Channel'
import { useSendbirdStateContext } from '@sendbird/uikit-react/useSendbirdStateContext'
import { useChannelContext } from '@sendbird/uikit-react/Channel/context'
import MessageContent from '@sendbird/uikit-react/ui/MessageContent'
const CustomChannel = () => {
const { config } = useSendbirdStateContext();
const { userId } = config;
const { currentGroupChannel } = useChannelContext();
return (
<Channel
...
renderMessage={({ message }) => {
return (
<MessageContent
userId={userId}
channel={currentGroupChannel}
message={message}
...
renderSenderProfile={(props: MessageProfileProps) => (
<MessageProfile {...props}/>
)}
renderMessageBody={(props: MessageBodyProps) => (
<MessageBody {...props}/>
)}
renderMessageHeader={(props: MessageHeaderProps) => (
<MessageHeader {...props}/>
)}
/>
)
}}
/>
)
}
UnreadCount
banner since stringresizingWidth
and resizingHeight
ImageCompression
to the Thread
moduleImageCompression
for sending file message and multiple files messagechannel.members
instead of fetching for non-super group channels in the SuggestedMentionList
Published by liamcho 12 months ago
MessageContent
is not customizable with three new optional properties:
renderSenderProfile
, renderMessageBody
, and renderMessageHeader
import Channel from '@sendbird/uikit-react/Channel'
import { useSendbirdStateContext } from '@sendbird/uikit-react/useSendbirdStateContext'
import { useChannelContext } from '@sendbird/uikit-react/Channel/context'
import MessageContent from '@sendbird/uikit-react/ui/MessageContent'
const CustomChannel = () => {
const { config } = useSendbirdStateContext();
const { userId } = config;
const { currentGroupChannel } = useChannelContext();
return (
<Channel
...
renderMessage={({ message }) => {
return (
<MessageContent
userId={userId}
channel={currentGroupChannel}
message={message}
...
renderSenderProfile={(props: MessageProfileProps) => (
<MessageProfile {...props}/>
)}
renderMessageBody={(props: MessageBodyProps) => (
<MessageBody {...props}/>
)}
renderMessageHeader={(props: MessageHeaderProps) => (
<MessageHeader {...props}/>
)}
/>
)
}}
/>
)
}
UnreadCount
banner since stringresizingWidth
and resizingHeight
ImageCompression
to the Thread
moduleImageCompression
for sending file message and multiple files messagechannel.members
instead of fetching for non-super group channels in the SuggestedMentionList
Published by AhyoungRyu 12 months ago
DateSeparators
and UnreadCount
.initialMessagesFetch
callback from the hook to provide more flexibility in UIKit customization.UserProfileProvider
in `OpenChannelSettings``.package.json
. If you were using the package in a Native CJS environment, this might have an impact.- const ChannelList = require('@sendbird/uikit-react/cjs/ChannelList');
+ const ChannelList = require('@sendbird/uikit-react/ChannelList');
Published by HoonBaek about 1 year ago
Now we are supporting Multiple Files Message feature!
You can select some multiple files in the message inputs, and send multiple images in one message.
If you select several types of files, only images will be combined in the message and the other files will be sent separately.
Also we have resolved many issues found during QA.
You can turn it on in four places.
import App from '@sendbird/uikit-react/App'
<App
...
isMultipleFilesMessageEnabled
/>
import { SendbirdProvider } from '@sendbird/uikit-react/SendbirdProvider'
<SendbirdProvider
...
isMultipleFilesMessageEnabled
>
{...}
</SendbirdProvider>
import Channel from '@sendbird/uikit-react/Channel';
import { ChannelProvider } from '@sendbird/uikit-react/Channel/context';
<Channel
...
isMultipleFilesMessageEnabled
/>
<ChannelProvider
...
isMultipleFilesMessageEnabled
>
{...}
</ChannelProvider>
import Thread from '@sendbird/uikit-react/Thread';
import { ThreadProvider } from '@sendbird/uikit-react/Thread/context';
<Thread
...
isMultipleFilesMessageEnabled
/>
<ThreadProvider
...
isMultipleFilesMessageEnabled
>
{...}
</ThreadProvider>
ChannelContext
and ThreadContext
has been changed little bit.
allMessages
of the ChannelContext has been divided into allMessages
and localMessages
allThreadMessages
of the ThreadContext has been divided into allThreadMessages
and localThreadMessages
pending
and failed
messages, and the all messages will contain only succeeded
messageslocal messages
to draw your custom message components.publishingModules
has been added to the payload of pubSub.publishimport { useCallback } from 'react'
import { SendbirdProvider, useSendbirdStateContext } from '@sendbird/uikit-react/SendbirdProvider'
import { PUBSUB_TOPICS as topics, PublishingModuleTypes } from '@sendbird/uikit-react/pubSub/topics'
const CustomApp = () => {
const globalState = useSendbirdStateContext();
const { stores, config } = globalState;
const { sdk, initialized } = stores.sdkStore;
const { pubSub } = config;
const onSendFileMessageOnlyInChannel = useCallback((channel, params) => {
channel.sendFileMessage(params)
.onPending((pendingMessage) => {
pubSub.publish(topics.SEND_MESSAGE_START, {
channel,
message: pendingMessage,
publishingModules: [PublishingModuleTypes.CHANNEL],
});
})
.onFailed((failedMessage) => {
pubSub.publish(topics.SEND_MESSAGE_FAILED, {
channel,
message: failedMessage,
publishingModules: [PublishingModuleTypes.CHANNEL],
});
})
.onSucceeded((succeededMessage) => {
pubSub.publish(topics.SEND_FILE_MESSAGE, {
channel,
message: succeededMessage,
publishingModules: [PublishingModuleTypes.CHANNEL],
});
})
}, []);
return (<>...</>)
};
const App = () => (
<SendbirdProvider>
<CustomApp />
</SendbirdProvider>
);
quoteMessage
of ChannelProviderInterfaceuseEditUserProfileProviderContext
has been renamed to useEditUserProfileContext
import { useEditUserProfileProviderContext } from '@sendbird/uikit-react/EditUserProfile/context'
// to
import { useEditUserProfileContext } from '@sendbird/uikit-react/EditUserProfile/context'
Published by AhyoungRyu about 1 year ago
Published by HoonBaek about 1 year ago
ui/MessageInput
component
interface MessageInputProps {
...
onFileUpload?: (fileList: FileList) => void;
onSendMessage?: (props: { message: string, mentionTemplate: string }) => void;
onUpdateMessage?: (props: { messageId: string, message: string, mentionTemplate: string }) => void;
}
interface ChannelContextProps {
onBeforeSendVoiceMessage?: (file: File, quotedMessage?: SendableMessageType) => FileMessageCreateParams;
}
import { ChannelProvider } from '@sendbird/uikit-react/Channel/context'
<ChannelProvider
onBeforeSendVoiceMessage={() => {}}
/>
interface ThreadProviderProps {
onBeforeSendUserMessage?: (message: string, quotedMessage?: SendableMessageType) => UserMessageCreateParams;
onBeforeSendFileMessage?: (file: File, quotedMessage?: SendableMessageType) => FileMessageCreateParams;
onBeforeSendVoiceMessage?: (file: File, quotedMessage?: SendableMessageType) => FileMessageCreateParams;
}
import { ThreadProvider } from '@sendbird/uikit-react/Thread/context'
<ThreadProvider
onBeforeSendUserMessage={() => {}}
onBeforeSendFileMessage={() => {}}
onBeforeSendVoiceMessage={() => {}}
/>
enum ButtonTypes {
PRIMARY = 'PRIMARY',
SECONDARY = 'SECONDARY',
DANGER = 'DANGER',
DISABLED = 'DISABLED',
}
enum ButtonSizes {
BIG = 'BIG',
SMALL = 'SMALL',
}
import Button, { ButtonTypes, ButtonSizes } from '@sendbird/uikit-react/ui/Button'
<Button
type={ButtonTypes.PRIMARY}
size={ButtonSizes.BIG}
/>
export enum IconTypes {
ADD = 'ADD',
ARROW_LEFT = 'ARROW_LEFT',
ATTACH = 'ATTACH',
AUDIO_ON_LINED = 'AUDIO_ON_LINED',
BAN = 'BAN',
BROADCAST = 'BROADCAST',
CAMERA = 'CAMERA',
CHANNELS = 'CHANNELS',
CHAT = 'CHAT',
CHAT_FILLED = 'CHAT_FILLED',
CHEVRON_DOWN = 'CHEVRON_DOWN',
CHEVRON_RIGHT = 'CHEVRON_RIGHT',
CLOSE = 'CLOSE',
COLLAPSE = 'COLLAPSE',
COPY = 'COPY',
CREATE = 'CREATE',
DELETE = 'DELETE',
DISCONNECTED = 'DISCONNECTED',
DOCUMENT = 'DOCUMENT',
DONE = 'DONE',
DONE_ALL = 'DONE_ALL',
DOWNLOAD = 'DOWNLOAD',
EDIT = 'EDIT',
EMOJI_MORE = 'EMOJI_MORE',
ERROR = 'ERROR',
EXPAND = 'EXPAND',
FILE_AUDIO = 'FILE_AUDIO',
FILE_DOCUMENT = 'FILE_DOCUMENT',
FREEZE = 'FREEZE',
GIF = 'GIF',
INFO = 'INFO',
LEAVE = 'LEAVE',
MEMBERS = 'MEMBERS',
MESSAGE = 'MESSAGE',
MODERATIONS = 'MODERATIONS',
MORE = 'MORE',
MUTE = 'MUTE',
NOTIFICATIONS = 'NOTIFICATIONS',
NOTIFICATIONS_OFF_FILLED = 'NOTIFICATIONS_OFF_FILLED',
OPERATOR = 'OPERATOR',
PHOTO = 'PHOTO',
PLAY = 'PLAY',
PLUS = 'PLUS',
QUESTION = 'QUESTION',
REFRESH = 'REFRESH',
REPLY = 'REPLY',
REMOVE = 'REMOVE',
SEARCH = 'SEARCH',
SEND = 'SEND',
SETTINGS_FILLED = 'SETTINGS_FILLED',
SLIDE_LEFT = 'SLIDE_LEFT',
SPINNER = 'SPINNER',
SUPERGROUP = 'SUPERGROUP',
THREAD = 'THREAD',
THUMBNAIL_NONE = 'THUMBNAIL_NONE',
TOGGLE_OFF = 'TOGGLE_OFF',
TOGGLE_ON = 'TOGGLE_ON',
USER = 'USER',
}
export enum IconColors {
DEFAULT = 'DEFAULT',
PRIMARY = 'PRIMARY',
PRIMARY_2 = 'PRIMARY_2',
SECONDARY = 'SECONDARY',
CONTENT = 'CONTENT',
CONTENT_INVERSE = 'CONTENT_INVERSE',
WHITE = 'WHITE',
GRAY = 'GRAY',
THUMBNAIL_ICON = 'THUMBNAIL_ICON',
SENT = 'SENT',
READ = 'READ',
ON_BACKGROUND_1 = 'ON_BACKGROUND_1',
ON_BACKGROUND_2 = 'ON_BACKGROUND_2',
ON_BACKGROUND_3 = 'ON_BACKGROUND_3',
ON_BACKGROUND_4 = 'ON_BACKGROUND_4',
BACKGROUND_3 = 'BACKGROUND_3',
ERROR = 'ERROR',
}
import Icon, { IconTypes, IconColors } from '@sendbird/uikit-react/ui/Icon'
<Icon
type={IconTypes.INFO}
fillColor={IconColors.PRIMARY}
/>
Published by HoonBaek about 1 year ago
[v3.6.8] (Sep 1 2023)
ui/FileViewer
to support multiple images
export enum ViewerTypes {
SINGLE = 'SINGLE',
MULTI = 'MULTI',
}
interface SenderInfo {
profileUrl: string;
nickname: string;
}
interface FileInfo {
name: string;
type: string;
url: string;
}
interface BaseViewer {
onClose: (e: React.MouseEvent) => void;
}
interface SingleFileViewer extends SenderInfo, FileInfo, BaseViewer {
viewerType?: typeof ViewerTypes.SINGLE;
isByMe?: boolean;
disableDelete?: boolean;
onDelete: (e: React.MouseEvent) => void;
}
interface MultiFilesViewer extends SenderInfo, BaseViewer {
viewerType: typeof ViewerTypes.MULTI;
fileInfoList: FileInfo[];
currentIndex: number;
onClickLeft: () => void;
onClickRight: () => void;
}
export type FileViewerComponentProps = SingleFileViewer | MultiFilesViewer;
Channel/utils/getMessagePartsInfo
Channel/utils/compareMessagesForGrouping
Message/hooks/useDirtyGetMentions
ui/MessageInput/hooks/usePaste
metadata
to the ChannelListQuery
<Channel or ChannelProvider
queries={{
channelListQuery: {
metadataKey: 'isMatching',
metadataValues: ['true'],
}
}}
/>
ui/FileViewer
and Channel/component/FileViewer
<ImageRenderer />
not converting number to pixel stringuseChannelContext.setQuoteMessage
should accept UserMessage | FileMessage
useThreadContext.sendMessage
should be string
Published by AhyoungRyu about 1 year ago
fetchChannelList
to the ChannelListContext
.
useFetchChannelList
.ChannelListUI
component.ChannelListContext
: fetchChannelList
.import SendbirdProvider from '@sendbird/uikit-react/SendbirdProvider'
import useSendbirdStateContext from '@sendbird/uikit-react/useSendbirdStateContext'
import { ChannelListProvider, useChannelListContext } from '@sendbird/uikit-react/ChannelList/context'
const isAboutSame = (a, b, px) => (Math.abs(a - b) <= px);
const CustomChannelList = () => {
const {
allChannels,
fetchChannelList,
} = useChannelListContext();
return (
<div
className="custom-channel-list"
onScroll={(e) => {
const target = e.target;
if (isAboutSame(target.clientHeight + target.scrollTop, target.scrollHeight, 10)) {
fetchChannelList();
}
}}
>
{allChannels.map((channel) => {
return // custom channel list item
})}
</div>
);
};
const CustomApp = () => {
return (
<div className="custom-app">
<SendbirdProvider ... >
<ChannelListProvider ... >
<CustomChannelList />
</ChannelListProvider>
</SendbirdProvider>
</div>
);
};
useGetChannel
hook (#705).SEND_MESSAGE_FAILED
event publishing (#704):
sendbirdSelectors.getSendUserMessage
and published the SEND_MESSAGE_FAILED
event.SEND_MESSAGEGE_FAILURE
.Published by AhyoungRyu about 1 year ago
customExtensionParams
for sdk.addSendbirdExtensions
(#698)sdk.addSendbirdExtension
function, allowing it to be delivered from outside of UIKit React.<SendbirdProvider
customExtensionParams={{
a: 'a', // the key-value set will be passed when sdk.addSendbirdExtensions is called
}}
/>
sdk.addSendbirdExtensions
during the connection process (#682)isUserMessage
and isFileMessage
Published by AhyoungRyu over 1 year ago
sdkInitParams
that allows passing custom parameters when sdk.init(params)
is called from outside of UIKit.e.g.
<SendbirdProvider
sdkInitParams={{
appStateToggleEnabled: false,
debugMode: true,
// more options can be found here https://sendbird.com/docs/chat/v4/javascript/ref/interfaces/_sendbird_chat.SendbirdChatParams.html
}}
/>
Published by AhyoungRyu over 1 year ago
isUserIdUsedForNickname
to the public interface. This prop allows using the userId as the nickname. (#683)reconnectOnIdle
(default: true), which prevents data refresh in the background. (#690)Published by AhyoungRyu over 1 year ago
UIKitConfig
to type definition (#677)Published by sravan-s over 1 year ago
Published by sravan-s over 1 year ago
Contributors: @tylerhammer
Published by AhyoungRyu over 1 year ago
Feat:
uikitOptions
prop of <SendbirdProvider />
or <App />
component. You can also find the detailed sample usage from SAMPLE.md#UIKit-Configuration-Samples
@sendbird/chat
version has been increased to 4.9.2. <SendbirdProvider
uikitOptions={{
common: {
enableUsingDefaultUserProfile: true,
},
groupChannel: {
enableMention: false,
enableOgtag: true,
enableReaction: true,
enableTypingIndicator: true,
input: {
camera: {
enablePhoto: true,
enableVideo: true,
},
gallery: {
enablePhoto: true,
enableVideo: true,
},
enableDocument: true,
},
},
groupChannelList: {
enableTypingIndicator: true,
enableMessageReceiptStatus: true,
},
groupChannelSettings: {
enableMessageSearch: true,
},
openChannel: {
enableOgtag: true,
input: {
camera: {
enablePhoto: true,
enableVideo: true,
},
gallery: {
enablePhoto: true,
enableVideo: true,
},
enableDocument: true,
},
},
}}
/>
Published by HoonBaek over 1 year ago
Fixes: