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
44 changes: 14 additions & 30 deletions src/modules/Channel/components/ChannelUI/index.tsx
Original file line number Diff line number Diff line change
@@ -1,53 +1,37 @@
import React from 'react';

import type { MessageContentProps } from '../../../../ui/MessageContent';
import type { GroupChannelHeaderProps } from '../../../GroupChannel/components/GroupChannelHeader';
import type { RenderCustomSeparatorProps } from '../../../../types';
import { RenderMessageParamsType } from '../../../../types';
import { useChannelContext } from '../../context/ChannelProvider';
import { GroupChannelUIView } from '../../../GroupChannel/components/GroupChannelUI/GroupChannelUIView';
import { GroupChannelUIBasicProps, GroupChannelUIView } from '../../../GroupChannel/components/GroupChannelUI/GroupChannelUIView';

import ChannelHeader from '../ChannelHeader';
import MessageList from '../MessageList';
import MessageInputWrapper from '../MessageInputWrapper';

export interface ChannelUIProps {
export interface ChannelUIProps extends GroupChannelUIBasicProps {
isLoading?: boolean;
renderPlaceholderLoader?: () => React.ReactElement;
renderPlaceholderInvalid?: () => React.ReactElement;
renderPlaceholderEmpty?: () => React.ReactElement;
renderChannelHeader?: (props: GroupChannelHeaderProps) => React.ReactElement;
renderMessage?: (props: RenderMessageParamsType) => React.ReactElement;
renderMessageContent?: (props: MessageContentProps) => React.ReactElement;
renderMessageInput?: () => React.ReactElement;
renderFileUploadIcon?: () => React.ReactElement;
renderVoiceMessageIcon?: () => React.ReactElement;
renderSendMessageIcon?: () => React.ReactElement;
renderTypingIndicator?: () => React.ReactElement;
renderCustomSeparator?: (props: RenderCustomSeparatorProps) => React.ReactElement;
renderFrozenNotification?: () => React.ReactElement;
}

const ChannelUI = (props: ChannelUIProps) => {
const context = useChannelContext();
const { channelUrl, isInvalid, loading } = context;

// Inject components to presentation layer
const {
channelUrl,
isInvalid,
loading,
} = context;
renderChannelHeader = (p) => <ChannelHeader {...p} />,
renderMessageList = (p) => <MessageList {...p} className="sendbird-conversation__message-list" />,
renderMessageInput = () => <MessageInputWrapper {...props} />,
} = props;

return (
<GroupChannelUIView
{...props}
{...context}
requestedChannelUrl={channelUrl}
loading={props?.isLoading ?? loading}
isInvalid={isInvalid}
renderChannelHeader={(props) => (<ChannelHeader {...props} />)}
renderMessageList={(props) => (<MessageList {...props} />)}
renderMessageInput={() => (
props.renderMessageInput?.()
?? <MessageInputWrapper {...props} />
)}
channelUrl={channelUrl}
renderChannelHeader={renderChannelHeader}
renderMessageList={renderMessageList}
renderMessageInput={renderMessageInput}
/>
);
};
Expand Down
18 changes: 5 additions & 13 deletions src/modules/Channel/components/MessageList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@ import '../../../GroupChannel/components/MessageList/index.scss';
import React, { useState } from 'react';
import type { UserMessage } from '@sendbird/chat/message';

import type { MessageContentProps } from '../../../../ui/MessageContent';
import { useChannelContext } from '../../context/ChannelProvider';
import PlaceHolder, { PlaceHolderTypes } from '../../../../ui/PlaceHolder';
import Icon, { IconColors, IconTypes } from '../../../../ui/Icon';
import Message from '../Message';
import { EveryMessage, RenderCustomSeparatorProps, RenderMessageParamsType, TypingIndicatorType } from '../../../../types';
import { EveryMessage, TypingIndicatorType } from '../../../../types';
import { isAboutSame } from '../../context/utils';
import { getMessagePartsInfo } from '../../../GroupChannel/components/MessageList/getMessagePartsInfo';
import UnreadCount from '../UnreadCount';
import FrozenNotification from '../FrozenNotification';
import { SCROLL_BUFFER } from '../../../../utils/consts';
Expand All @@ -23,18 +21,12 @@ import { useScrollBehavior } from './hooks/useScrollBehavior';
import TypingIndicatorBubble from '../../../../ui/TypingIndicatorBubble';
import { useOnScrollPositionChangeDetector } from '../../../../hooks/useOnScrollReachedEndDetector';

const SCROLL_BOTTOM_PADDING = 50;
import { getMessagePartsInfo } from '../../../GroupChannel/components/MessageList/getMessagePartsInfo';
import { GroupChannelMessageListProps } from '../../../GroupChannel/components/MessageList';

export interface MessageListProps {
className?: string;
renderMessage?: (props: RenderMessageParamsType) => React.ReactElement;
renderMessageContent?: (props: MessageContentProps) => React.ReactElement;
renderPlaceholderEmpty?: () => React.ReactElement;
renderCustomSeparator?: (props: RenderCustomSeparatorProps) => React.ReactElement;
renderPlaceholderLoader?: () => React.ReactElement;
renderFrozenNotification?: () => React.ReactElement;
}
const SCROLL_BOTTOM_PADDING = 50;

export type MessageListProps = GroupChannelMessageListProps;
export const MessageList = ({
className = '',
renderMessage,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,42 +1,63 @@
import './index.scss';
import React from 'react';

import type { GroupChannelUIProps } from '.';
import useSendbirdStateContext from '../../../../hooks/useSendbirdStateContext';

import GroupChannelHeader, { type GroupChannelHeaderProps } from '../GroupChannelHeader';
import MessageList from '../MessageList';
import TypingIndicator from '../TypingIndicator';
import { TypingIndicatorType } from '../../../../types';
import ConnectionStatus from '../../../../ui/ConnectionStatus';
import PlaceHolder, { PlaceHolderTypes } from '../../../../ui/PlaceHolder';
import MessageInputWrapper from '../MessageInputWrapper';

export interface GroupChannelUIViewProps extends GroupChannelUIProps, GroupChannelHeaderProps {
requestedChannelUrl: string;
import type { RenderCustomSeparatorProps, RenderMessageParamsType } from '../../../../types';
import type { GroupChannelHeaderProps } from '../GroupChannelHeader';
import type { GroupChannelMessageListProps } from '../MessageList';
import type { MessageContentProps } from '../../../../ui/MessageContent';

export interface GroupChannelUIBasicProps {
// Components
renderPlaceholderLoader?: () => React.ReactElement;
renderPlaceholderInvalid?: () => React.ReactElement;
renderPlaceholderEmpty?: () => React.ReactElement;
renderChannelHeader?: (props: GroupChannelHeaderProps) => React.ReactElement;
renderMessageList?: (props: GroupChannelMessageListProps) => React.ReactElement;
renderMessageInput?: () => React.ReactElement;

// Sub components
// MessageList
renderMessage?: (props: RenderMessageParamsType) => React.ReactElement;
renderMessageContent?: (props: MessageContentProps) => React.ReactElement;
renderCustomSeparator?: (props: RenderCustomSeparatorProps) => React.ReactElement;
renderFrozenNotification?: () => React.ReactElement;

// MessageInput
renderFileUploadIcon?: () => React.ReactElement;
renderVoiceMessageIcon?: () => React.ReactElement;
renderSendMessageIcon?: () => React.ReactElement;
renderTypingIndicator?: () => React.ReactElement;
}

export interface GroupChannelUIViewProps extends GroupChannelUIBasicProps {
loading: boolean;
isInvalid: boolean;
channelUrl: string;
renderChannelHeader: GroupChannelUIBasicProps['renderChannelHeader'];
renderMessageList: GroupChannelUIBasicProps['renderMessageList'];
renderMessageInput: GroupChannelUIBasicProps['renderMessageInput'];
}

export const GroupChannelUIView = (props: GroupChannelUIViewProps) => {
const {
requestedChannelUrl,
loading,
isInvalid,
renderChannelHeader = (props) => (
<GroupChannelHeader {...props} />
),
renderMessageList = (props) => (
<MessageList
{...props}
className="sendbird-conversation__message-list"
/>
),
renderMessageInput = () => <MessageInputWrapper />,
channelUrl,
renderChannelHeader,
renderMessageList,
renderMessageInput,
renderTypingIndicator,
renderPlaceholderLoader,
renderPlaceholderInvalid,
} = props;

const { stores, config } = useSendbirdStateContext();
const sdkError = stores?.sdkStore?.error;
const { logger, isOnline } = config;
Expand All @@ -45,9 +66,9 @@ export const GroupChannelUIView = (props: GroupChannelUIViewProps) => {
return <div className="sendbird-conversation">{renderPlaceholderLoader?.() || <PlaceHolder type={PlaceHolderTypes.LOADING} />}</div>;
}

if (!requestedChannelUrl) {
if (!channelUrl) {
return (
<div className="sendbird-conversation">{renderPlaceholderInvalid?.() || <PlaceHolder type={PlaceHolderTypes.NO_CHANNELS} />}</div>
<div className="sendbird-conversation">{renderPlaceholderInvalid?.() || <PlaceHolder type={PlaceHolderTypes.NO_CHANNELS} />}</div>
);
}

Expand All @@ -72,21 +93,16 @@ export const GroupChannelUIView = (props: GroupChannelUIViewProps) => {
}

return (
<div className='sendbird-conversation'>
<div className="sendbird-conversation">
{renderChannelHeader?.({ className: 'sendbird-conversation__channel-header' })}
{renderMessageList?.(props)}
<div className="sendbird-conversation__footer">
{renderMessageInput?.()}
<div className="sendbird-conversation__footer__typing-indicator">
{renderTypingIndicator?.()
|| (
config?.groupChannel?.enableTypingIndicator
&& config?.groupChannel?.typingIndicatorTypes?.has(TypingIndicatorType.Text)
&& (
<TypingIndicator channelUrl={requestedChannelUrl} />
)
)
}
|| (config?.groupChannel?.enableTypingIndicator && config?.groupChannel?.typingIndicatorTypes?.has(TypingIndicatorType.Text) && (
<TypingIndicator channelUrl={channelUrl} />
))}
{!isOnline && <ConnectionStatus />}
</div>
</div>
Expand Down
47 changes: 16 additions & 31 deletions src/modules/GroupChannel/components/GroupChannelUI/index.tsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,35 @@
import React from 'react';

import type { GroupChannelHeaderProps } from '../GroupChannelHeader';
import type { MessageListProps } from '../../../Channel/components/MessageList';
import type { GroupChannelMessageListProps } from '../MessageList';
import { RenderCustomSeparatorProps, RenderMessageParamsType } from '../../../../types';
import { useGroupChannelContext } from '../../context/GroupChannelProvider';
import { GroupChannelUIView } from './GroupChannelUIView';
import type { MessageContentProps } from '../../../../ui/MessageContent';
import { GroupChannelUIBasicProps, GroupChannelUIView } from './GroupChannelUIView';

import GroupChannelHeader from '../GroupChannelHeader';
import MessageList from '../MessageList';
import MessageInputWrapper from '../MessageInputWrapper';

export interface GroupChannelUIProps {
renderPlaceholderLoader?: () => React.ReactElement;
renderPlaceholderInvalid?: () => React.ReactElement;
renderPlaceholderEmpty?: () => React.ReactElement;
renderChannelHeader?: (props: GroupChannelHeaderProps) => React.ReactElement;
renderMessage?: (props: RenderMessageParamsType) => React.ReactElement;
renderMessageContent?: (props: MessageContentProps) => React.ReactElement;
renderMessageList?: (props: MessageListProps | GroupChannelMessageListProps) => React.ReactElement;
renderMessageInput?: () => React.ReactElement;
renderFileUploadIcon?: () => React.ReactElement;
renderVoiceMessageIcon?: () => React.ReactElement;
renderSendMessageIcon?: () => React.ReactElement;
renderTypingIndicator?: () => React.ReactElement;
renderCustomSeparator?: (props: RenderCustomSeparatorProps) => React.ReactElement;
renderFrozenNotification?: () => React.ReactElement;
}
export interface GroupChannelUIProps extends GroupChannelUIBasicProps {}

export const GroupChannelUI = (props: GroupChannelUIProps) => {
const context = useGroupChannelContext();
const { currentChannel, channelUrl, loading } = context;

// Inject components to presentation layer
const {
currentChannel,
channelUrl,
loading,
} = context;
renderChannelHeader = (props) => <GroupChannelHeader {...props} />,
renderMessageList = (props) => <MessageList {...props} className="sendbird-conversation__message-list" />,
renderMessageInput = () => <MessageInputWrapper {...props} />,
} = props;

return (
<GroupChannelUIView
{...props}
{...context}
requestedChannelUrl={channelUrl}
loading={loading}
isInvalid={channelUrl && !currentChannel}
renderMessageInput={() => (
props.renderMessageInput?.()
?? <MessageInputWrapper {...props} />
)}
channelUrl={channelUrl}
renderChannelHeader={renderChannelHeader}
renderMessageList={renderMessageList}
renderMessageInput={renderMessageInput}
/>
);
};
Expand Down