Skip to content

Commit 13e74d9

Browse files
authored
feat: Thread (#357)
Features: * Provide new module `Thread`. See the specific informations of this module on the [Docs page](https://sendbird.com/docs/uikit) * You can use a combined component `Thread`. Import it with ```typescript import Thread from "@sendbird/uikit-react/Thread" ``` * Also you can use `ThreadProvider` and `useThreadContext` for customization. Import it with ```typescript import { ThreadProvider, useThreadContext } from "@sendbird/uikit-react/Thread/context" ``` * And the other UI components are provided under the Thread. `ThreadUI`, `ThreadHeader`, `ParentMessageInfo`, `ParentMessageInfoItem`, `ThreadList`, `ThreadListItem`, and `ThreadMessageInput` are it * Add `ui/ThreadReplies` component ```typescript interface ThreadRepliesProps { className?: string; threadInfo: ThreadInfo; onClick?: (e: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>) => void; } ``` * Add channel props `threadReplySelectType` * Type of the value should be ```typescript enum ThreadReplySelectType { PARENT, THREAD } ``` You can see how to use it below ```typescript import { ThreadReplySelectType } from "@sendbird/uikit-react/Channel/context"; <Channel ... threadReplySelectType={ThreadReplySelectType.PARENT} /> ``` Fixes: * Do not allow operator to unregister itself on the OperatorList of GroupChannel * Create new group channel when user open 1:1 channel on the UserProfile * Register the channel creator as an operator in 1:1 channel
1 parent f18cb4d commit 13e74d9

File tree

123 files changed

+5549
-277
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

123 files changed

+5549
-277
lines changed

exports.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,18 @@ export default {
9191
'MessageSearch/context': 'src/smart-components/MessageSearch/context/MessageSearchProvider.tsx',
9292
'MessageSearch/components/MessageSearchUI': 'src/smart-components/MessageSearch/components/MessageSearchUI/index.tsx',
9393

94+
// Thread
95+
Thread: 'src/smart-components/Thread/index.tsx',
96+
'Thread/context': 'src/smart-components/Thread/context/ThreadProvider.tsx',
97+
'Thread/context/types': 'src/smart-components/Thread/types.tsx',
98+
'Thread/components/ThreadUI': 'src/smart-components/Thread/components/ThreadUI/index.tsx',
99+
'Thread/components/ThreadHeader': 'src/smart-components/Thread/components/ThreadHeader/index.tsx',
100+
'Thread/components/ParentMessageInfo': 'src/smart-components/Thread/components/ParentMessageInfo/index.tsx',
101+
'Thread/components/ParentMessageInfoItem': 'src/smart-components/Thread/components/ParentMessageInfo/ParentMessageInfoItem.tsx',
102+
'Thread/components/ThreadList': 'src/smart-components/Thread/components/ThreadList/index.tsx',
103+
'Thread/components/ThreadListItem': 'src/smart-components/Thread/components/ThreadList/ThreadListItem.tsx',
104+
'Thread/components/ThreadMessageInput': 'src/smart-components/Thread/components/ThreadMessageInput/index.tsx',
105+
94106
// CreateChannel
95107
CreateChannel: 'src/smart-components/CreateChannel/index.tsx',
96108
'CreateChannel/context': 'src/smart-components/CreateChannel/context/CreateChannelProvider.tsx',
@@ -154,6 +166,7 @@ export default {
154166
'ui/SortByRow': 'src/ui/SortByRow/index.tsx',
155167
'ui/TextButton': 'src/ui/TextButton/index.tsx',
156168
'ui/TextMessageItemBody': 'src/ui/TextMessageItemBody/index.tsx',
169+
'ui/ThreadReplies': 'src/ui/ThreadReplies/index.tsx',
157170
'ui/ThumbnailMessageItemBody': 'src/ui/ThumbnailMessageItemBody/index.tsx',
158171
'ui/Tooltip': 'src/ui/Tooltip/index.tsx',
159172
'ui/TooltipWrapper': 'src/ui/TooltipWrapper/index.tsx',

scripts/index_d_ts

Lines changed: 223 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ declare module "SendbirdUIKitGlobal" {
1212
SessionHandler,
1313
User,
1414
ApplicationUserListQueryParams,
15+
EmojiContainer,
1516
} from '@sendbird/chat';
1617
import type {
1718
GroupChannel,
@@ -24,6 +25,7 @@ declare module "SendbirdUIKitGlobal" {
2425
} from '@sendbird/chat/groupChannel';
2526
import type {
2627
AdminMessage,
28+
BaseMessage,
2729
FailedMessageHandler,
2830
FileMessage,
2931
FileMessageCreateParams,
@@ -268,6 +270,9 @@ declare module "SendbirdUIKitGlobal" {
268270
resizingWidth?: number | string,
269271
resizingHeight?: number | string,
270272
};
273+
isTypingIndicatorEnabledOnChannelList?: boolean;
274+
isMessageReceiptStatusEnabledOnChannelList?: boolean;
275+
replyType: ReplyType;
271276
}
272277
export interface SdkStore {
273278
error: boolean;
@@ -453,6 +458,11 @@ declare module "SendbirdUIKitGlobal" {
453458
messageListParams?: MessageListParams;
454459
};
455460

461+
export enum ThreadReplySelectType {
462+
PARENT = 'PARENT',
463+
THREAD = 'THREAD',
464+
}
465+
456466
export type ChannelContextProps = {
457467
children?: React.ReactElement;
458468
channelUrl: string;
@@ -468,6 +478,7 @@ declare module "SendbirdUIKitGlobal" {
468478
onSearchClick?(): void;
469479
onBackClick?(): void;
470480
replyType?: ReplyType;
481+
threadReplySelectType?: ThreadReplySelectType;
471482
queries?: ChannelQueries;
472483
renderUserProfile?: (props: RenderUserProfileProps) => React.ReactNode | React.ReactElement;
473484
disableUserProfile?: boolean;
@@ -868,6 +879,131 @@ declare module "SendbirdUIKitGlobal" {
868879
onCloseClick?: () => void;
869880
}
870881

882+
/**
883+
* Thread
884+
*/
885+
export enum ChannelStateTypes {
886+
NIL = 'NIL',
887+
LOADING = 'LOADING',
888+
INVALID = 'INVALID',
889+
INITIALIZED = 'INITIALIZED',
890+
}
891+
export enum ParentMessageInfoStateTypes {
892+
NIL = 'NIL',
893+
LOADING = 'LOADING',
894+
INVALID = 'INVALID',
895+
INITIALIZED = 'INITIALIZED',
896+
}
897+
export enum ThreadListStateTypes {
898+
NIL = 'NIL',
899+
LOADING = 'LOADING',
900+
INVALID = 'INVALID',
901+
INITIALIZED = 'INITIALIZED',
902+
}
903+
904+
export interface ThreadProps extends ThreadProviderProps, ThreadContextInitialState {
905+
className?: string;
906+
}
907+
908+
export interface ThreadProviderInterface extends ThreadProviderProps, ThreadContextInitialState {
909+
fetchPrevThreads: (callback?: (messages?: Array<BaseMessage>) => void) => void;
910+
fetchNextThreads: (callback?: (messages?: Array<BaseMessage>) => void) => void;
911+
toggleReaction: (message, key, isReacted) => void;
912+
sendMessage: (props: {
913+
message: UserMessage,
914+
quoteMessage?: UserMessage | FileMessage,
915+
mentionTemplate?: string,
916+
mentionedUsers?: Array<User>,
917+
}) => void;
918+
sendFileMessage: (file: File, quoteMessage: UserMessage | FileMessage) => void;
919+
resendMessage: (failedMessage: UserMessage | FileMessage) => void;
920+
updateMessage: (props, callback?: () => void) => void;
921+
deleteMessage: (message: UserMessage | FileMessage) => Promise<UserMessage | FileMessage>;
922+
nicknamesMap: Map<string, string>;
923+
}
924+
925+
export type ThreadProviderProps = {
926+
children?: React.ReactElement;
927+
channelUrl: string;
928+
message: UserMessage | FileMessage;
929+
onHeaderActionClick?: () => void;
930+
onMoveToParentMessage?: (props: { message: UserMessage | FileMessage, channel: GroupChannel }) => void;
931+
disableUserProfile?: boolean;
932+
renderUserProfile?: (props: { user: User, close: () => void }) => React.ReactElement;
933+
onUserProfileMessage?: (channel: GroupChannel) => void;
934+
}
935+
936+
export interface ThreadContextInitialState {
937+
currentChannel: GroupChannel;
938+
allThreadMessages: Array<BaseMessage>;
939+
parentMessage: UserMessage | FileMessage;
940+
channelStatus: ChannelStateTypes;
941+
parentMessageInfoStatus: ParentMessageInfoStateTypes;
942+
threadListStatus: ThreadListStateTypes;
943+
hasMorePrev: boolean;
944+
hasMoreNext: boolean;
945+
emojiContainer: EmojiContainer;
946+
isMuted: boolean;
947+
isChannelFrozen: boolean;
948+
currentUserId: string;
949+
}
950+
951+
export interface ThreadUIProps {
952+
renderHeader?: () => React.ReactElement;
953+
renderParentMessageInfo?: () => React.ReactElement;
954+
renderMessage?: (props: { message: UserMessage | FileMessage }) => React.ReactElement;
955+
renderMessageInput?: () => React.ReactElement;
956+
renderCustomSeparator?: () => React.ReactElement;
957+
renderParentMessageInfoPlaceholder?: (type: ParentMessageInfoStateTypes) => React.ReactElement;
958+
renderThreadListPlaceHolder?: (type: ThreadListStateTypes) => React.ReactElement;
959+
}
960+
961+
type EventType = React.MouseEvent<HTMLDivElement | HTMLButtonElement> | React.KeyboardEvent<HTMLDivElement>;
962+
export interface ThreadHeaderProps {
963+
className?: string;
964+
channelName: string;
965+
renderActionIcon?: (props: { onActionIconClick: (e: EventType) => void }) => React.ReactElement;
966+
onActionIconClick?: (e: EventType) => void;
967+
onChannelNameClick?: (e: EventType) => void;
968+
}
969+
970+
export interface ParentMessageInfoProps {
971+
className?: string;
972+
}
973+
974+
export interface ParentMessageInfoItemProps {
975+
className?: string;
976+
message: UserMessage | FileMessage;
977+
showFileViewer?: (bool: boolean) => void;
978+
}
979+
980+
export interface ThreadListProps {
981+
className?: string;
982+
allThreadMessages: Array<UserMessage | FileMessage | BaseMessage>;
983+
renderMessage?: (props: {
984+
message: UserMessage | FileMessage,
985+
chainTop: boolean,
986+
chainBottom: boolean,
987+
hasSeparator: boolean,
988+
}) => React.ReactElement;
989+
renderCustomSeparator?: (props: { message: UserMessage | FileMessage }) => React.ReactElement;
990+
scrollRef?: React.RefObject<HTMLDivElement>;
991+
scrollBottom?: number;
992+
}
993+
export interface ThreadListItemProps {
994+
className?: string;
995+
message: UserMessage | FileMessage;
996+
chainTop?: boolean;
997+
chainBottom?: boolean;
998+
hasSeparator?: boolean;
999+
renderCustomSeparator?: (props: { message: UserMessage | FileMessage }) => React.ReactElement;
1000+
handleScroll?: () => void;
1001+
}
1002+
1003+
export interface ThreadMessageInputProps {
1004+
className?: string;
1005+
}
1006+
8711007
/**
8721008
* CreateChannel
8731009
*/
@@ -1122,6 +1258,7 @@ declare module '@sendbird/uikit-react/Channel/context' {
11221258
import SendbirdUIKitGlobal from 'SendbirdUIKitGlobal';
11231259
export const ChannelProvider: React.FunctionComponent<SendbirdUIKitGlobal.ChannelContextProps>;
11241260
export function useChannelContext(): SendbirdUIKitGlobal.ChannelProviderInterface;
1261+
export const ThreadReplySelectType: SendbirdUIKitGlobal.ThreadReplySelectType;
11251262
}
11261263

11271264
declare module '@sendbird/uikit-react/Channel/components/ChannelHeader' {
@@ -1343,6 +1480,64 @@ declare module '@sendbird/uikit-react/MessageSearch/components/MessageSearchUI'
13431480
export default MessageSearchUI;
13441481
}
13451482

1483+
/**
1484+
* Thread
1485+
*/
1486+
declare module '@sendbird/uikit-react/Thread' {
1487+
import SendbirdUIKitGlobal from 'SendbirdUIKitGlobal';
1488+
const Thread: React.FC<SendbirdUIKitGlobal.ThreadProviderProps>;
1489+
export default Thread;
1490+
}
1491+
1492+
declare module '@sendbird/uikit-react/Thread/context' {
1493+
import SendbirdUIKitGlobal from 'SendbirdUIKitGlobal';
1494+
export const useThreadContext: () => SendbirdUIKitGlobal.ThreadProviderInterface;
1495+
export const ThreadProvider: React.FC<SendbirdUIKitGlobal.ThreadProviderProps>;
1496+
}
1497+
1498+
declare module '@sendbird/uikit-react/Thread/context/types' {
1499+
import SendbirdUIKitGlobal from 'SendbirdUIKitGlobal';
1500+
export const ChannelStateTypes: SendbirdUIKitGlobal.ChannelStateTypes;
1501+
export const ParentMessageInfoStateTypes: SendbirdUIKitGlobal.ParentMessageInfoStateTypes;
1502+
export const ThreadListStateTypes: SendbirdUIKitGlobal.ThreadListStateTypes;
1503+
}
1504+
1505+
declare module '@sendbird/uikit-react/Thread/components/ThreadUI' {
1506+
import SendbirdUIKitGlobal from 'SendbirdUIKitGlobal';
1507+
const ThreadUI: React.FC<SendbirdUIKitGlobal.ThreadUIProps>;
1508+
export default ThreadUI;
1509+
}
1510+
declare module '@sendbird/uikit-react/Thread/components/ThreadHeader' {
1511+
import SendbirdUIKitGlobal from 'SendbirdUIKitGlobal';
1512+
const ThreadHeader: React.FC<SendbirdUIKitGlobal.ThreadHeaderProps>;
1513+
export default ThreadHeader;
1514+
}
1515+
declare module '@sendbird/uikit-react/Thread/components/ParentMessageInfo' {
1516+
import SendbirdUIKitGlobal from 'SendbirdUIKitGlobal';
1517+
const ParentMessageInfo: React.FC<SendbirdUIKitGlobal.ParentMessageInfoProps>;
1518+
export default ParentMessageInfo;
1519+
}
1520+
declare module '@sendbird/uikit-react/Thread/components/ParentMessageInfoItem' {
1521+
import SendbirdUIKitGlobal from 'SendbirdUIKitGlobal';
1522+
const ParentMessageInfoItem: React.FC<SendbirdUIKitGlobal.ParentMessageInfoItemProps>;
1523+
export default ParentMessageInfoItem;
1524+
}
1525+
declare module '@sendbird/uikit-react/Thread/components/ThreadList' {
1526+
import SendbirdUIKitGlobal from 'SendbirdUIKitGlobal';
1527+
const ThreadList: React.FC<SendbirdUIKitGlobal.ThreadListProps>;
1528+
export default ThreadList;
1529+
}
1530+
declare module '@sendbird/uikit-react/Thread/components/ThreadListItem' {
1531+
import SendbirdUIKitGlobal from 'SendbirdUIKitGlobal';
1532+
const ThreadListItem: React.FC<SendbirdUIKitGlobal.ThreadListItemProps>;
1533+
export default ThreadListItem;
1534+
}
1535+
declare module '@sendbird/uikit-react/Thread/components/ThreadMessageInput' {
1536+
import SendbirdUIKitGlobal from 'SendbirdUIKitGlobal';
1537+
const ThreadMessageInput: React.FC<SendbirdUIKitGlobal.ThreadMessageInputProps>;
1538+
export default ThreadMessageInput;
1539+
}
1540+
13461541
/**
13471542
* CreateChannel
13481543
*/
@@ -1633,16 +1828,17 @@ declare module '@sendbird/uikit-react/ui/IconButton' {
16331828

16341829
declare module '@sendbird/uikit-react/ui/ImageRenderer' {
16351830
interface ImageRendererProps {
1636-
className?: string | Array<string>,
1637-
defaultComponent?: () => React.ReactElement,
1638-
placeHolder?: () => React.ReactElement,
1639-
alt?: string,
1640-
width?: number,
1641-
height?: number,
1642-
fixedSize?: boolean,
1643-
circle?: boolean,
1644-
onLoad?: () => void,
1645-
onError?: () => void,
1831+
className?: string | Array<string>;
1832+
url: string;
1833+
alt?: string;
1834+
width?: string | number;
1835+
height?: string | number;
1836+
circle?: boolean;
1837+
fixedSize?: boolean;
1838+
placeHolder?: ((props: { style: { [key: string]: string | number } }) => React.ReactElement) | React.ReactElement;
1839+
defaultComponent?: (() => React.ReactElement) | React.ReactElement;
1840+
onLoad?: () => void;
1841+
onError?: () => void;
16461842
}
16471843
const ImageRenderer: React.FC<ImageRendererProps>;
16481844
export default ImageRenderer;
@@ -1778,20 +1974,23 @@ declare module '@sendbird/uikit-react/ui/MessageItemMenu' {
17781974
import type { GroupChannel } from '@sendbird/chat/groupChannel';
17791975
import type { FileMessage, UserMessage } from '@sendbird/chat/message';
17801976
import type { OpenChannel } from '@sendbird/chat/openChannel';
1781-
import type SenbirdUIKitGlobal from 'SendbirdUIKitGlobal';
1977+
type ReplyType = "NONE" | "QUOTE_REPLY" | "THREAD";
17821978

17831979
interface MessageItemMenuProps {
17841980
className?: string | Array<string>;
17851981
message: UserMessage | FileMessage;
17861982
channel: GroupChannel | OpenChannel;
17871983
isByMe?: boolean;
17881984
disabled?: boolean;
1789-
replyType?: SenbirdUIKitGlobal.ReplyType;
1985+
replyType?: ReplyType;
1986+
disableDeleteMessage?: boolean;
17901987
showEdit?: (bool: boolean) => void;
17911988
showRemove?: (bool: boolean) => void;
17921989
resendMessage?: (message: UserMessage | FileMessage) => void;
17931990
setQuoteMessage?: (message: UserMessage | FileMessage) => void;
17941991
setSupposedHover?: (bool: boolean) => void;
1992+
onReplyInThread?: (props: { message: UserMessage | FileMessage }) => void;
1993+
onMoveToParentMessage?: () => void;
17951994
}
17961995
const MessageItemMenu: React.FC<MessageItemMenuProps>;
17971996
export default MessageItemMenu;
@@ -2087,6 +2286,17 @@ declare module '@sendbird/uikit-react/ui/TextMessageItemBody' {
20872286
export default TextMessageItemBody;
20882287
}
20892288

2289+
declare module '@sendbird/uikit-react/ui/ThreadReplies' {
2290+
import type { ThreadInfo } from '@sendbird/chat/message';
2291+
interface ThreadRepliesProps {
2292+
className?: string;
2293+
threadInfo: ThreadInfo;
2294+
onClick?: (e: React.MouseEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>) => void;
2295+
}
2296+
const ThreadReplies: React.FC<ThreadRepliesProps>;
2297+
export default ThreadReplies;
2298+
}
2299+
20902300
declare module '@sendbird/uikit-react/ui/ThumbnailMessageItemBody' {
20912301
import type { FileMessage } from '@sendbird/chat/message';
20922302
interface ThumbnailMessageItemBodyProps {
@@ -2096,6 +2306,7 @@ declare module '@sendbird/uikit-react/ui/ThumbnailMessageItemBody' {
20962306
mouseHover?: boolean;
20972307
isReactionEnabled?: boolean;
20982308
showFileViewer?: (bool: boolean) => void;
2309+
style?: Record<string, any>;
20992310
}
21002311
const ThumbnailMessageItemBody: React.FC<ThumbnailMessageItemBodyProps>;
21012312
export default ThumbnailMessageItemBody;

src/lib/Sendbird.jsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export default function Sendbird(props) {
5252
isMentionEnabled,
5353
isTypingIndicatorEnabledOnChannelList,
5454
isMessageReceiptStatusEnabledOnChannelList,
55+
replyType,
5556
} = props;
5657

5758
const mediaQueryBreakPoint = false;
@@ -202,6 +203,7 @@ export default function Sendbird(props) {
202203
},
203204
isTypingIndicatorEnabledOnChannelList,
204205
isMessageReceiptStatusEnabledOnChannelList,
206+
replyType,
205207
},
206208
}}
207209
>
@@ -270,6 +272,7 @@ Sendbird.propTypes = {
270272
}),
271273
isTypingIndicatorEnabledOnChannelList: PropTypes.bool,
272274
isMessageReceiptStatusEnabledOnChannelList: PropTypes.bool,
275+
replyType: PropTypes.oneOf(['NONE', 'QUOTE_REPLY', 'THREAD']),
273276
};
274277

275278
Sendbird.defaultProps = {
@@ -296,4 +299,5 @@ Sendbird.defaultProps = {
296299
isMentionEnabled: false,
297300
isTypingIndicatorEnabledOnChannelList: false,
298301
isMessageReceiptStatusEnabledOnChannelList: false,
302+
replyType: 'NONE',
299303
};

0 commit comments

Comments
 (0)