diff --git a/src/hooks/VoicePlayer/useVoicePlayer.tsx b/src/hooks/VoicePlayer/useVoicePlayer.tsx index 77b9151d5..5f1f3df38 100644 --- a/src/hooks/VoicePlayer/useVoicePlayer.tsx +++ b/src/hooks/VoicePlayer/useVoicePlayer.tsx @@ -25,7 +25,7 @@ export interface UseVoicePlayerContext { export const useVoicePlayer = ({ key = '', channelUrl = '', - audioFile = null, + audioFile, audioFileUrl = '', }: UseVoicePlayerProps): UseVoicePlayerContext => { const [groupKey] = useState(generateGroupKey(channelUrl, key)); diff --git a/src/hooks/VoiceRecorder/WebAudioUtils.ts b/src/hooks/VoiceRecorder/WebAudioUtils.ts index 8f23c4f26..abea9d77e 100644 --- a/src/hooks/VoiceRecorder/WebAudioUtils.ts +++ b/src/hooks/VoiceRecorder/WebAudioUtils.ts @@ -14,11 +14,13 @@ function encodeMp3(arrayBuffer: ArrayBuffer): WavHeader { if (wav.channels > 1) { for (let j = 0; j < samplesLeft.length; j++) { samplesLeft[j] = dataView[j * 2]; - samplesRight[j] = dataView[j * 2 + 1]; + if (samplesRight) { + samplesRight[j] = dataView[j * 2 + 1]; + } } } - const dataBuffer = []; + const dataBuffer: Int8Array[] = []; let remaining = samplesLeft.length; for (let i = 0; remaining >= maxSamples; i += maxSamples) { const left = samplesLeft.subarray(i, i + maxSamples); @@ -44,7 +46,7 @@ function downsampleToWav(file: File, callback: (buffer: ArrayBuffer) => void): v const fileReader = new FileReader(); fileReader.onload = function (ev) { // Decode audio - audioCtx.decodeAudioData(ev.target.result as ArrayBuffer, (buffer) => { + audioCtx.decodeAudioData(ev.target?.result as ArrayBuffer, (buffer) => { // this is where you down sample the audio, usually is 44100 samples per second const usingWebkit = !window.OfflineAudioContext; const offlineAudioCtx = new OfflineAudioContext(1, 16000 * buffer.duration, 16000); diff --git a/src/hooks/VoiceRecorder/index.tsx b/src/hooks/VoiceRecorder/index.tsx index 50cde679b..5376e4367 100644 --- a/src/hooks/VoiceRecorder/index.tsx +++ b/src/hooks/VoiceRecorder/index.tsx @@ -107,7 +107,7 @@ export const VoiceRecorderProvider = (props: VoiceRecorderProps): React.ReactEle lastModified: new Date().getTime(), type: VOICE_MESSAGE_MIME_TYPE, }); - eventHandler?.onRecordingEnded(convertedAudioFile); + eventHandler?.onRecordingEnded?.(convertedAudioFile); logger.info('VoiceRecorder: Succeeded converting audio file.', convertedAudioFile); }); stream?.getAudioTracks?.().forEach?.(track => track?.stop()); diff --git a/src/hooks/VoiceRecorder/useVoiceRecorder.tsx b/src/hooks/VoiceRecorder/useVoiceRecorder.tsx index 730739deb..13e87d434 100644 --- a/src/hooks/VoiceRecorder/useVoiceRecorder.tsx +++ b/src/hooks/VoiceRecorder/useVoiceRecorder.tsx @@ -1,6 +1,7 @@ import { useCallback, useEffect, useState } from 'react'; import { VoiceRecorderEventHandler, useVoiceRecorderContext } from '.'; import useSendbirdStateContext from '../useSendbirdStateContext'; +import { VOICE_RECORDER_DEFAULT_MAX } from '../../utils/consts'; // export interface UseVoiceRecorderProps extends VoiceRecorderEventHandler { // /** @@ -34,11 +35,11 @@ export const useVoiceRecorder = ({ }: VoiceRecorderEventHandler): UseVoiceRecorderContext => { const { config } = useSendbirdStateContext(); const { voiceRecord } = config; - const { maxRecordingTime } = voiceRecord; + const maxRecordingTime = voiceRecord?.maxRecordingTime ?? VOICE_RECORDER_DEFAULT_MAX; const voiceRecorder = useVoiceRecorderContext(); const { isRecordable } = voiceRecorder; - const [recordedFile, setRecordedFile] = useState(null); + const [recordedFile, setRecordedFile] = useState(null); const [recordingStatus, setRecordingStatus] = useState(VoiceRecorderStatus.PREPARING); useEffect(() => { if (isRecordable && recordingStatus === VoiceRecorderStatus.PREPARING) { diff --git a/src/hooks/useThrottleCallback.ts b/src/hooks/useThrottleCallback.ts index b9fed48a1..afdc1ac3c 100644 --- a/src/hooks/useThrottleCallback.ts +++ b/src/hooks/useThrottleCallback.ts @@ -12,8 +12,8 @@ export function useThrottleCallback void>( trailing: false, }, ) { - const timer = useRef(null); - const trailingArgs = useRef(null); + const timer = useRef | null>(null); + const trailingArgs = useRef(null); useEffect(() => { return () => { diff --git a/src/lib/Sendbird.tsx b/src/lib/Sendbird.tsx index 13afc7629..2422a650d 100644 --- a/src/lib/Sendbird.tsx +++ b/src/lib/Sendbird.tsx @@ -271,9 +271,9 @@ const SendbirdSDK = ({ useEffect(() => { const body = document.querySelector('body'); - body.classList.remove('sendbird-experimental__rem__units'); + body?.classList.remove('sendbird-experimental__rem__units'); if (isREMUnitEnabled) { - body.classList.add('sendbird-experimental__rem__units'); + body?.classList.add('sendbird-experimental__rem__units'); } }, [isREMUnitEnabled]); // add-remove theme from body @@ -281,9 +281,9 @@ const SendbirdSDK = ({ logger.info('Setup theme', `Theme: ${currentTheme}`); try { const body = document.querySelector('body'); - body.classList.remove('sendbird-theme--light'); - body.classList.remove('sendbird-theme--dark'); - body.classList.add(`sendbird-theme--${currentTheme || 'light'}`); + body?.classList.remove('sendbird-theme--light'); + body?.classList.remove('sendbird-theme--dark'); + body?.classList.add(`sendbird-theme--${currentTheme || 'light'}`); logger.info('Finish setup theme'); // eslint-disable-next-line no-empty } catch (e) { @@ -292,8 +292,8 @@ const SendbirdSDK = ({ return () => { try { const body = document.querySelector('body'); - body.classList.remove('sendbird-theme--light'); - body.classList.remove('sendbird-theme--dark'); + body?.classList.remove('sendbird-theme--light'); + body?.classList.remove('sendbird-theme--dark'); // eslint-disable-next-line no-empty } catch { } }; diff --git a/src/lib/emojiManager.tsx b/src/lib/emojiManager.tsx index a974c0a83..6a2a92572 100644 --- a/src/lib/emojiManager.tsx +++ b/src/lib/emojiManager.tsx @@ -58,7 +58,7 @@ export class EmojiManager { } public getEmojiUrl(reactionKey: Reaction['key']) { - return this.AllEmojisAsArray.find((emoji) => emoji.key === reactionKey).url ?? ''; + return this.AllEmojisAsArray.find((emoji) => emoji.key === reactionKey)?.url ?? ''; } public get emojiContainer() { diff --git a/src/lib/hooks/useOnlineStatus.ts b/src/lib/hooks/useOnlineStatus.ts index 0976e22a0..e0f08bb2d 100644 --- a/src/lib/hooks/useOnlineStatus.ts +++ b/src/lib/hooks/useOnlineStatus.ts @@ -78,14 +78,14 @@ function useOnlineStatus(sdk: SendbirdChat, logger: LoggerInterface) { const body = document.querySelector('body'); if (!isOnline && !sdk?.isCacheEnabled) { try { - body.classList.add('sendbird__offline'); + body?.classList.add('sendbird__offline'); logger.info('Added class sendbird__offline to body'); } catch (e) { // } } else { try { - body.classList.remove('sendbird__offline'); + body?.classList.remove('sendbird__offline'); logger.info('Removed class sendbird__offline from body'); } catch (e) { // diff --git a/src/lib/selectors.ts b/src/lib/selectors.ts index 846f14d92..f3a68d863 100644 --- a/src/lib/selectors.ts +++ b/src/lib/selectors.ts @@ -472,7 +472,7 @@ export const getSendUserMessage = (state: SendBirdState, publishingModules: Publ topics.SEND_MESSAGE_FAILED, { error, message: message as UserMessage, channel, publishingModules }, ); - handler.triggerFailed(error, message); + handler.triggerFailed(error, message as SendableMessage); }) .onPending((message) => { pubSub.publish( diff --git a/src/modules/App/DesktopLayout.tsx b/src/modules/App/DesktopLayout.tsx index a3ded1470..423e3ae59 100644 --- a/src/modules/App/DesktopLayout.tsx +++ b/src/modules/App/DesktopLayout.tsx @@ -119,7 +119,7 @@ export const DesktopLayout: React.FC = (props: DesktopLayout sendbird-app__conversation-wrap `} > - {enableLegacyChannelModules ? : } + {enableLegacyChannelModules ? : } {showSettings && (
diff --git a/src/modules/App/MobileLayout.tsx b/src/modules/App/MobileLayout.tsx index 18040e09b..bcff0f9a5 100644 --- a/src/modules/App/MobileLayout.tsx +++ b/src/modules/App/MobileLayout.tsx @@ -62,7 +62,7 @@ export const MobileLayout: React.FC = (props: MobileLayoutPro useEffect(() => { if (panel !== PANELS.CHANNEL) { - goToMessage(null, () => setHighlightedMessage(null)); + goToMessage(null, () => setHighlightedMessage?.(null)); } }, [panel]); @@ -173,7 +173,7 @@ export const MobileLayout: React.FC = (props: MobileLayoutPro )} {panel === PANELS.CHANNEL && (
- {enableLegacyChannelModules ? : } + {enableLegacyChannelModules ? : }
)} {panel === PANELS.CHANNEL_SETTINGS && ( @@ -218,7 +218,7 @@ export const MobileLayout: React.FC = (props: MobileLayoutPro setCurrentChannel(channel); goToMessage(message, (messageId) => { setPanel(PANELS.CHANNEL); - setHighlightedMessage(messageId); + setHighlightedMessage?.(messageId); }); }} /> diff --git a/src/modules/App/index.tsx b/src/modules/App/index.tsx index ce0b736fd..bdc422149 100644 --- a/src/modules/App/index.tsx +++ b/src/modules/App/index.tsx @@ -12,6 +12,7 @@ import { AppLayout } from './AppLayout'; import './index.scss'; import { AppLayoutProps } from './types'; +import { GroupChannel } from '@sendbird/chat/groupChannel'; interface AppProps { appId: SendbirdProviderProps['appId']; @@ -102,27 +103,27 @@ export default function App(props: AppProps) { isUserIdUsedForNickname = true, enableLegacyChannelModules = false, } = props; - const [currentChannel, setCurrentChannel] = useState(null); + const [currentChannel, setCurrentChannel] = useState(null); return ( diff --git a/src/modules/Channel/components/Message/index.tsx b/src/modules/Channel/components/Message/index.tsx index 028893090..bca51b160 100644 --- a/src/modules/Channel/components/Message/index.tsx +++ b/src/modules/Channel/components/Message/index.tsx @@ -75,7 +75,7 @@ const Message = (props: MessageProps): React.ReactElement => { updateUserMessage={(messageId, params) => { updateMessage({ messageId, - message: params.message, + message: params.message ?? '', mentionedUsers: params.mentionedUsers, mentionTemplate: params.mentionedMessageTemplate, }); diff --git a/src/modules/Channel/components/MessageFeedbackModal/index.tsx b/src/modules/Channel/components/MessageFeedbackModal/index.tsx index a6461552b..057a1ca8a 100644 --- a/src/modules/Channel/components/MessageFeedbackModal/index.tsx +++ b/src/modules/Channel/components/MessageFeedbackModal/index.tsx @@ -37,9 +37,9 @@ export default function MessageFeedbackModal(props: MessageFeedbackModalProps): const onSubmitWrapper = () => { if (!selectedFeedback) return; - const comment = inputRef.current.value ?? ''; + const comment = inputRef.current?.value ?? ''; if (isEdit) { - if (comment !== message.myFeedback.comment) { + if (comment !== message.myFeedback?.comment) { onUpdate?.(selectedFeedback, comment); } else { onClose?.(); @@ -50,7 +50,7 @@ export default function MessageFeedbackModal(props: MessageFeedbackModalProps): }; const modalRef = useRef(null); - const inputRef = useRef(null); + const inputRef = useRef(null); const onKeyDown = useKeyDown(modalRef, { Enter: () => onSubmitWrapper(), diff --git a/src/modules/Channel/components/MessageInputWrapper/index.tsx b/src/modules/Channel/components/MessageInputWrapper/index.tsx index cae43fe01..266131462 100644 --- a/src/modules/Channel/components/MessageInputWrapper/index.tsx +++ b/src/modules/Channel/components/MessageInputWrapper/index.tsx @@ -30,10 +30,10 @@ export const MessageInputWrapper = (props: MessageInputWrapperProps) => { const lastMessage = currentGroupChannel?.lastMessage; const isLastMessageSuggestedRepliesEnabled = config?.groupChannel?.enableSuggestedReplies - && getSuggestedReplies(lastMessage).length > 0 + && getSuggestedReplies(lastMessage ?? undefined).length > 0 && localMessages?.length === 0; const disableMessageInput = props.disabled - || isLastMessageSuggestedRepliesEnabled && !!lastMessage.extendedMessagePayload?.['disable_chat_input']; + || isLastMessageSuggestedRepliesEnabled && !!lastMessage?.extendedMessagePayload?.['disable_chat_input']; return ( { message: params.message, mentionTemplate: params.mentionedMessageTemplate, mentionedUsers: params.mentionedUsers, - quoteMessage, + quoteMessage: quoteMessage ?? undefined, }); }} sendFileMessage={(params) => { - return sendFileMessage(params.file as File, quoteMessage); + return sendFileMessage(params.file as File, quoteMessage ?? undefined); }} sendVoiceMessage={({ file }, duration) => { - return sendVoiceMessage(file as File, duration, quoteMessage); + return sendVoiceMessage(file as File, duration, quoteMessage ?? undefined); }} sendMultipleFilesMessage={({ fileInfoList }) => { - return sendMultipleFilesMessage(fileInfoList.map((fileInfo) => fileInfo.file) as File[], quoteMessage); + return sendMultipleFilesMessage(fileInfoList.map((fileInfo) => fileInfo.file) as File[], quoteMessage ?? undefined); }} /> ); diff --git a/src/modules/Channel/context/ChannelProvider.tsx b/src/modules/Channel/context/ChannelProvider.tsx index 448e61e74..9fcbfde3a 100644 --- a/src/modules/Channel/context/ChannelProvider.tsx +++ b/src/modules/Channel/context/ChannelProvider.tsx @@ -163,7 +163,7 @@ export interface ChannelProviderInterface extends ChannelContextProps, MessageSt renderUserMentionItem?: (props: { user: User }) => JSX.Element; } -const ChannelContext = React.createContext(undefined); +const ChannelContext = React.createContext(null); const ChannelProvider: React.FC = (props: ChannelContextProps) => { const { @@ -225,7 +225,7 @@ const ChannelProvider: React.FC = (props: ChannelContextPro setHighLightedMessageId(highlightedMessage); }, [highlightedMessage]); const userFilledMessageListQuery = queries?.messageListParams; - const [quoteMessage, setQuoteMessage] = useState(null); + const [quoteMessage, setQuoteMessage] = useState(null); const [isScrolled, setIsScrolled] = useState(false); const [messagesStore, messagesDispatcher] = useReducer(messagesReducer, messagesInitialState); diff --git a/src/modules/Channel/context/hooks/useDeleteMessageCallback.ts b/src/modules/Channel/context/hooks/useDeleteMessageCallback.ts index 0d6b0f761..c87d546a1 100644 --- a/src/modules/Channel/context/hooks/useDeleteMessageCallback.ts +++ b/src/modules/Channel/context/hooks/useDeleteMessageCallback.ts @@ -36,7 +36,7 @@ function useDeleteMessageCallback( } else { logger.info('Channel | useDeleteMessageCallback: Deleting message from remote:', sendingStatus); currentGroupChannel - .deleteMessage(message) + ?.deleteMessage(message) .then(() => { logger.info('Channel | useDeleteMessageCallback: Deleting message success!', message); messagesDispatcher({ diff --git a/src/modules/Channel/context/hooks/useFileInfoListWithUploaded.ts b/src/modules/Channel/context/hooks/useFileInfoListWithUploaded.ts index 9d67419b7..03b1b697c 100644 --- a/src/modules/Channel/context/hooks/useFileInfoListWithUploaded.ts +++ b/src/modules/Channel/context/hooks/useFileInfoListWithUploaded.ts @@ -35,10 +35,10 @@ export const useFileInfoListWithUploaded = (message: CoreMessageType): UploadedF isUploaded: true, })); } else { - return message.messageParams.fileInfoList.map((it, index) => ({ + return message?.messageParams?.fileInfoList.map((it, index) => ({ ...it, url: getObjectURL(index) ?? it.fileUrl ?? (it.file instanceof Blob ? getObjectURL(index, it.file) : undefined), isUploaded: !it.file && typeof it.fileUrl === 'string' && it.fileUrl.length > 0, - })); + })) ?? []; } }; diff --git a/src/modules/Channel/context/hooks/useHandleChannelEvents.ts b/src/modules/Channel/context/hooks/useHandleChannelEvents.ts index a89eeb35b..78be69222 100644 --- a/src/modules/Channel/context/hooks/useHandleChannelEvents.ts +++ b/src/modules/Channel/context/hooks/useHandleChannelEvents.ts @@ -66,7 +66,9 @@ function useHandleChannelEvents({ let scrollToEnd = false; try { const { current } = scrollRef; - scrollToEnd = current.offsetHeight + current.scrollTop >= current.scrollHeight - 10; + if (current) { + scrollToEnd = current.offsetHeight + current.scrollTop >= current.scrollHeight - 10; + } // 10 is a buffer } catch (error) { // diff --git a/src/modules/Channel/context/hooks/useHandleReconnectForChannelList.ts b/src/modules/Channel/context/hooks/useHandleReconnectForChannelList.ts index 1ebb21318..32770a7f9 100644 --- a/src/modules/Channel/context/hooks/useHandleReconnectForChannelList.ts +++ b/src/modules/Channel/context/hooks/useHandleReconnectForChannelList.ts @@ -87,7 +87,7 @@ function useHandleReconnectForChannelList({ logger.info('ChannelList refresh - channel list sorted', sortedChannelList); } // select first channel - let newCurrentChannel: GroupChannel = !disableAutoSelect ? sortedChannelList[0] : null; + let newCurrentChannel: GroupChannel | null = !disableAutoSelect ? sortedChannelList[0] : null; if (currentGroupChannel?.url) { const foundChannel = sortedChannelList.find((channel) => ( channel.url === currentGroupChannel.url diff --git a/src/modules/Channel/context/hooks/useResendMessageCallback.ts b/src/modules/Channel/context/hooks/useResendMessageCallback.ts index c3f496f85..f99f223d6 100644 --- a/src/modules/Channel/context/hooks/useResendMessageCallback.ts +++ b/src/modules/Channel/context/hooks/useResendMessageCallback.ts @@ -28,7 +28,7 @@ function useResendMessageCallback( // userMessage if (failedMessage.isUserMessage()) { currentGroupChannel - .resendMessage(failedMessage) + ?.resendMessage(failedMessage) .onPending((message) => { logger.info('Channel: Resending message start!', message); messagesDispatcher({ @@ -52,7 +52,7 @@ function useResendMessageCallback( }); } else if (failedMessage.isFileMessage()) { currentGroupChannel - .resendMessage(failedMessage) + ?.resendMessage(failedMessage) .onPending((message) => { logger.info('Channel: Resending file message start!', message); messagesDispatcher({ @@ -76,7 +76,7 @@ function useResendMessageCallback( }); } else if (failedMessage.isMultipleFilesMessage()) { currentGroupChannel - .resendMessage(failedMessage) + ?.resendMessage(failedMessage) .onPending((message) => { logger.info('Channel: Resending multiple files message start!', message); messagesDispatcher({ @@ -93,7 +93,7 @@ function useResendMessageCallback( }); pubSub.publish(topics.ON_FILE_INFO_UPLOADED, { response: { - channelUrl: currentGroupChannel.url, + channelUrl: currentGroupChannel?.url ?? '', requestId, index, uploadableFileInfo, diff --git a/src/modules/Channel/context/hooks/useSendMessageCallback.ts b/src/modules/Channel/context/hooks/useSendMessageCallback.ts index 7bc6983cb..c42f8b568 100644 --- a/src/modules/Channel/context/hooks/useSendMessageCallback.ts +++ b/src/modules/Channel/context/hooks/useSendMessageCallback.ts @@ -49,12 +49,12 @@ export default function useSendMessageCallback( message, }; // if (isMentionEnabled && mentionedUserIds?.length > 0) { - if (isMentionEnabled && mentionedUsers?.length > 0) { + if (isMentionEnabled && mentionedUsers && mentionedUsers.length > 0) { // params.mentionedUserIds = mentionedUserIds; params.mentionedUsers = mentionedUsers; } // if (isMentionEnabled && mentionTemplate && mentionedUserIds?.length > 0) { - if (isMentionEnabled && mentionTemplate && mentionedUsers?.length > 0) { + if (isMentionEnabled && mentionTemplate && mentionedUsers && mentionedUsers.length > 0) { params.mentionedMessageTemplate = mentionTemplate; } if (quoteMessage) { @@ -74,7 +74,7 @@ export default function useSendMessageCallback( logger.info('Channel: Sending message has started', params); currentGroupChannel - .sendUserMessage(params) + ?.sendUserMessage(params) .onPending((pendingMsg) => { pubSub.publish(topics.SEND_MESSAGE_START, { /* pubSub is used instead of messagesDispatcher diff --git a/src/modules/Channel/context/hooks/useSendMultipleFilesMessage.ts b/src/modules/Channel/context/hooks/useSendMultipleFilesMessage.ts index 7dd1bf916..f196b6fdd 100644 --- a/src/modules/Channel/context/hooks/useSendMultipleFilesMessage.ts +++ b/src/modules/Channel/context/hooks/useSendMultipleFilesMessage.ts @@ -83,7 +83,7 @@ export const useSendMultipleFilesMessage = ({ } logger.info('Channel: Start sending MFM', { messageParams }); try { - currentChannel.sendMultipleFilesMessage(messageParams) + currentChannel?.sendMultipleFilesMessage(messageParams) .onFileUploaded((requestId, index, uploadableFileInfo: UploadableFileInfo, error) => { logger.info('Channel: onFileUploaded during sending MFM', { requestId, diff --git a/src/modules/Channel/context/hooks/useUpdateMessageCallback.ts b/src/modules/Channel/context/hooks/useUpdateMessageCallback.ts index b78871363..c5bc48592 100644 --- a/src/modules/Channel/context/hooks/useUpdateMessageCallback.ts +++ b/src/modules/Channel/context/hooks/useUpdateMessageCallback.ts @@ -42,7 +42,7 @@ function useUpdateMessageCallback( const params: UserMessageUpdateParams = { message, }; - if (isMentionEnabled && mentionedUsers?.length > 0) { + if (isMentionEnabled && mentionedUsers&& mentionedUsers.length > 0) { params.mentionedUsers = mentionedUsers; } if (isMentionEnabled && mentionTemplate) { @@ -63,7 +63,7 @@ function useUpdateMessageCallback( logger.info('Channel: Updating message!', params); currentGroupChannel - .updateUserMessage(messageId, params) + ?.updateUserMessage(messageId, params) .then((msg) => { if (callback) { callback(null, msg); diff --git a/src/modules/ChannelList/context/ChannelListProvider.tsx b/src/modules/ChannelList/context/ChannelListProvider.tsx index d50b1ef25..48c1c99cd 100644 --- a/src/modules/ChannelList/context/ChannelListProvider.tsx +++ b/src/modules/ChannelList/context/ChannelListProvider.tsx @@ -201,7 +201,7 @@ const ChannelListProvider: React.FC = (props: ChannelL channelListDispatcher, setChannelSource, onChannelSelect, - userFilledChannelListQuery, + userFilledChannelListQuery: { ...userFilledChannelListQuery }, logger, sortChannelList, disableAutoSelect, diff --git a/src/modules/ChannelList/utils.ts b/src/modules/ChannelList/utils.ts index 16439b856..b4724af34 100644 --- a/src/modules/ChannelList/utils.ts +++ b/src/modules/ChannelList/utils.ts @@ -83,7 +83,7 @@ const createEventHandler = ({ sdk, sdkChannelHandlerId, channelListDispatcher, l } }, onMessageUpdated: (channel, message) => { - if (channel.isGroupChannel() && channel.lastMessage.isEqual(message)) { + if (channel.isGroupChannel() && channel.lastMessage?.isEqual(message)) { logger.info('ChannelList: onMessageUpdated', channel); channelListDispatcher({ type: channelActions.ON_LAST_MESSAGE_UPDATED, diff --git a/src/modules/ChannelSettings/components/ChannelProfile/index.tsx b/src/modules/ChannelSettings/components/ChannelProfile/index.tsx index d745612d8..44ec58bb9 100644 --- a/src/modules/ChannelSettings/components/ChannelProfile/index.tsx +++ b/src/modules/ChannelSettings/components/ChannelProfile/index.tsx @@ -25,10 +25,10 @@ const ChannelProfile: React.FC = () => { const isOnline = state?.config?.isOnline; const disabled = !isOnline; - const { channel } = channelSettingStore; + const channel = channelSettingStore?.channel; const getChannelName = () => { - if (channel?.name && channel?.name !== 'Group Channel') { + if (channel?.name && channel.name !== 'Group Channel') { return channel.name; } if (channel?.name === 'Group Channel' || !channel?.name) { diff --git a/src/modules/ChannelSettings/components/ModerationPanel/AddOperatorsModal.tsx b/src/modules/ChannelSettings/components/ModerationPanel/AddOperatorsModal.tsx index af6005b2f..506164629 100644 --- a/src/modules/ChannelSettings/components/ModerationPanel/AddOperatorsModal.tsx +++ b/src/modules/ChannelSettings/components/ModerationPanel/AddOperatorsModal.tsx @@ -14,7 +14,7 @@ import Label, { import { ButtonTypes } from '../../../../ui/Button'; import UserListItem from '../../../../ui/UserListItem'; import { useChannelSettingsContext } from '../../context/ChannelSettingsProvider'; -import { OperatorFilter } from '@sendbird/chat/groupChannel'; +import { Member, MemberListQuery, OperatorFilter } from '@sendbird/chat/groupChannel'; interface Props { onCancel(): void; @@ -25,22 +25,22 @@ export default function AddOperatorsModal({ onCancel, onSubmit, }: Props): ReactElement { - const [members, setMembers] = useState([]); + const [members, setMembers] = useState([]); const [selectedMembers, setSelectedMembers] = useState({}); - const [memberQuery, setMemberQuery] = useState(null); + const [memberQuery, setMemberQuery] = useState(null); const { stringSet } = useContext(LocalizationContext); - const { channel } = useChannelSettingsContext(); + const channel = useChannelSettingsContext()?.channel; useEffect(() => { const memberListQuery = channel?.createMemberListQuery({ operatorFilter: OperatorFilter.NONOPERATOR, limit: 20, }); - memberListQuery.next().then((members) => { + memberListQuery?.next().then((members) => { setMembers(members); }); - setMemberQuery(memberListQuery); + setMemberQuery(memberListQuery ?? null); }, []); const selectedCount = Object.keys(selectedMembers).filter((m) => selectedMembers[m]).length; @@ -68,12 +68,12 @@ export default function AddOperatorsModal({
{ - const { hasNext } = memberQuery; + const hasNext = memberQuery ? memberQuery.hasNext : false; const target = e.target as HTMLTextAreaElement; const fetchMore = ( target.clientHeight + target.scrollTop === target.scrollHeight ); - if (hasNext && fetchMore) { + if (hasNext && fetchMore && memberQuery) { memberQuery.next().then((o) => { setMembers([ ...members, diff --git a/src/modules/ChannelSettings/components/ModerationPanel/BannedUserList.tsx b/src/modules/ChannelSettings/components/ModerationPanel/BannedUserList.tsx index 7b892937d..30670fe7f 100644 --- a/src/modules/ChannelSettings/components/ModerationPanel/BannedUserList.tsx +++ b/src/modules/ChannelSettings/components/ModerationPanel/BannedUserList.tsx @@ -5,7 +5,7 @@ import React, { useCallback, useContext, } from 'react'; -import { BannedUserListQueryParams } from '@sendbird/chat'; +import { BannedUserListQueryParams, RestrictedUser, User } from '@sendbird/chat'; import Button, { ButtonTypes, ButtonSizes } from '../../../../ui/Button'; import @@ -23,12 +23,12 @@ import { useChannelSettingsContext } from '../../context/ChannelSettingsProvider import { LocalizationContext } from '../../../../lib/LocalizationContext'; export const BannedMemberList = (): ReactElement => { - const [members, setMembers] = useState([]); + const [members, setMembers] = useState([]); const [hasNext, setHasNext] = useState(false); const [showModal, setShowModal] = useState(false); const { stringSet } = useContext(LocalizationContext); - const { channel } = useChannelSettingsContext(); + const channel = useChannelSettingsContext()?.channel; const bannedUserListQueryParams: BannedUserListQueryParams = { limit: 10 }; useEffect(() => { @@ -54,6 +54,7 @@ export const BannedMemberList = (): ReactElement => { setHasNext(bannedUserListQuery.hasNext); }); }, [channel]); + return ( <> { diff --git a/src/modules/ChannelSettings/components/ModerationPanel/BannedUsersModal.tsx b/src/modules/ChannelSettings/components/ModerationPanel/BannedUsersModal.tsx index d57eb88e5..6b6c8cc1a 100644 --- a/src/modules/ChannelSettings/components/ModerationPanel/BannedUsersModal.tsx +++ b/src/modules/ChannelSettings/components/ModerationPanel/BannedUsersModal.tsx @@ -13,6 +13,7 @@ import ContextMenu, { MenuItem, MenuItems } from '../../../../ui/ContextMenu'; import { noop } from '../../../../utils/utils'; import { useChannelSettingsContext } from '../../context/ChannelSettingsProvider'; import { useLocalization } from '../../../../lib/LocalizationContext'; +import { BannedUserListQuery, RestrictedUser } from '@sendbird/chat'; interface Props { onCancel(): void; @@ -21,17 +22,17 @@ interface Props { export default function BannedUsersModal({ onCancel, }: Props): ReactElement { - const [members, setMembers] = useState([]); - const [memberQuery, setMemberQuery] = useState(null); - const { channel } = useChannelSettingsContext(); + const [members, setMembers] = useState([]); + const [memberQuery, setMemberQuery] = useState(null); + const channel = useChannelSettingsContext()?.channel; const { stringSet } = useLocalization(); useEffect(() => { const bannedUserListQuery = channel?.createBannedUserListQuery(); - bannedUserListQuery.next().then((users) => { + bannedUserListQuery?.next().then((users) => { setMembers(users); }); - setMemberQuery(bannedUserListQuery); + setMemberQuery(bannedUserListQuery ?? null); }, []); return (
@@ -45,7 +46,7 @@ export default function BannedUsersModal({
{ - const { hasNext } = memberQuery; + const hasNext = memberQuery?.hasNext; const target = e.target as HTMLTextAreaElement; const fetchMore = ( target.clientHeight + target.scrollTop === target.scrollHeight diff --git a/src/modules/ChannelSettings/components/ModerationPanel/InviteUsersModal.tsx b/src/modules/ChannelSettings/components/ModerationPanel/InviteUsersModal.tsx index e55c4b9ce..dd5a7694a 100644 --- a/src/modules/ChannelSettings/components/ModerationPanel/InviteUsersModal.tsx +++ b/src/modules/ChannelSettings/components/ModerationPanel/InviteUsersModal.tsx @@ -17,15 +17,15 @@ interface Props { } export default function InviteUsers({ onCancel, onSubmit }: Props) { - const [users, setUsers] = useState([]); - const [userListQuery, setUserListQuery] = useState(null); + const [users, setUsers] = useState([]); + const [userListQuery, setUserListQuery] = useState(null); const [selectedUsers, setSelectedUsers] = useState>({}); const state = useSendbirdStateContext(); const sdk = state?.stores?.sdkStore?.sdk; const globalUserListQuery = state?.config?.userListQuery; - - const { channel, overrideInviteUser, queries } = useChannelSettingsContext(); + + const { channel, overrideInviteUser, queries } = useChannelSettingsContext() || {}; const { stringSet } = useLocalization(); const onScroll = useOnScrollPositionChangeDetector({ @@ -39,7 +39,7 @@ export default function InviteUsers({ onCancel, onSubmit }: Props) { const onInviteUsers = async () => { const userIdsToInvite = Object.keys(selectedUsers); - if (typeof overrideInviteUser === 'function') { + if (channel && typeof overrideInviteUser === 'function') { overrideInviteUser({ users: userIdsToInvite, onClose: onCancel, channel }); } else { await channel?.inviteWithUserIds(userIdsToInvite); @@ -95,7 +95,7 @@ export default function InviteUsers({ onCancel, onSubmit }: Props) {
{users.map((user) => { - const isMember = Boolean(membersMap[user.userId]); + const isMember = Boolean(membersMap ? membersMap[user.userId] : false); const isSelected = Boolean(selectedUsers[user.userId]); return ( { const { channel, setChannelUpdateId, - } = useChannelSettingsContext(); + } = useChannelSettingsContext() || {}; const { stringSet } = useContext(LocalizationContext); const sdk = state?.stores?.sdkStore?.sdk; @@ -58,7 +58,7 @@ export const MemberList = (): ReactElement => { memberUserListQuery.next().then((members) => { setMembers(members); setHasNext(memberUserListQuery.hasNext); - setChannelUpdateId(uuidv4()); + setChannelUpdateId?.(uuidv4()); }); }, [channel]); @@ -69,7 +69,7 @@ export const MemberList = (): ReactElement => { ( diff --git a/src/modules/ChannelSettings/components/ModerationPanel/MembersModal.tsx b/src/modules/ChannelSettings/components/ModerationPanel/MembersModal.tsx index 48973ad6a..e6178b399 100644 --- a/src/modules/ChannelSettings/components/ModerationPanel/MembersModal.tsx +++ b/src/modules/ChannelSettings/components/ModerationPanel/MembersModal.tsx @@ -15,17 +15,17 @@ import { noop } from '../../../../utils/utils'; import { useChannelSettingsContext } from '../../context/ChannelSettingsProvider'; import useSendbirdStateContext from '../../../../hooks/useSendbirdStateContext'; import { LocalizationContext } from '../../../../lib/LocalizationContext'; -import { Member } from '@sendbird/chat/groupChannel'; +import { Member, MemberListQuery } from '@sendbird/chat/groupChannel'; interface Props { onCancel(): void; } export default function MembersModal({ onCancel }: Props): ReactElement { - const [members, setMembers] = useState([]); - const [memberQuery, setMemberQuery] = useState(null); - - const { channel } = useChannelSettingsContext(); + const [members, setMembers] = useState([]); + const [memberQuery, setMemberQuery] = useState(null); + + const channel = useChannelSettingsContext()?.channel; const state = useSendbirdStateContext(); const currentUser = state?.config?.userId; const { stringSet } = useContext(LocalizationContext); @@ -34,10 +34,10 @@ export default function MembersModal({ onCancel }: Props): ReactElement { const memberListQuery = channel?.createMemberListQuery({ limit: 20, }); - memberListQuery.next().then((members) => { + memberListQuery?.next().then((members) => { setMembers(members); }); - setMemberQuery(memberListQuery); + setMemberQuery(memberListQuery ?? null); }, []); return (
@@ -51,7 +51,7 @@ export default function MembersModal({ onCancel }: Props): ReactElement {
{ - const { hasNext } = memberQuery; + const hasNext = memberQuery?.hasNext; const target = e.target as HTMLTextAreaElement; const fetchMore = ( target.clientHeight + target.scrollTop === target.scrollHeight diff --git a/src/modules/ChannelSettings/components/ModerationPanel/MutedMemberList.tsx b/src/modules/ChannelSettings/components/ModerationPanel/MutedMemberList.tsx index b731ca666..818078044 100644 --- a/src/modules/ChannelSettings/components/ModerationPanel/MutedMemberList.tsx +++ b/src/modules/ChannelSettings/components/ModerationPanel/MutedMemberList.tsx @@ -25,8 +25,8 @@ export const MutedMemberList = (): ReactElement => { const [hasNext, setHasNext] = useState(false); const [showModal, setShowModal] = useState(false); const { stringSet } = useLocalization(); - - const { channel } = useChannelSettingsContext(); + + const channel = useChannelSettingsContext()?.channel; const state = useSendbirdStateContext(); const currentUser = state?.config?.userId; diff --git a/src/modules/ChannelSettings/components/ModerationPanel/MutedMembersModal.tsx b/src/modules/ChannelSettings/components/ModerationPanel/MutedMembersModal.tsx index 093b11c4e..5da351470 100644 --- a/src/modules/ChannelSettings/components/ModerationPanel/MutedMembersModal.tsx +++ b/src/modules/ChannelSettings/components/ModerationPanel/MutedMembersModal.tsx @@ -13,6 +13,7 @@ import { noop } from '../../../../utils/utils'; import { useChannelSettingsContext } from '../../context/ChannelSettingsProvider'; import useSendbirdStateContext from '../../../../hooks/useSendbirdStateContext'; import { useLocalization } from '../../../../lib/LocalizationContext'; +import { Member, MemberListQuery } from '@sendbird/chat/groupChannel'; interface Props { onCancel(): void; @@ -21,10 +22,10 @@ interface Props { export default function MutedMembersModal({ onCancel, }: Props): ReactElement { - const [members, setMembers] = useState([]); - const [memberQuery, setMemberQuery] = useState(null); - - const { channel } = useChannelSettingsContext(); + const [members, setMembers] = useState([]); + const [memberQuery, setMemberQuery] = useState(null); + + const channel = useChannelSettingsContext()?.channel; const state = useSendbirdStateContext(); const currentUser = state?.config?.userId; const { stringSet } = useLocalization(); @@ -35,10 +36,10 @@ export default function MutedMembersModal({ // @ts-ignore mutedMemberFilter: 'muted', }); - memberUserListQuery.next().then((members) => { + memberUserListQuery?.next().then((members) => { setMembers(members); }); - setMemberQuery(memberUserListQuery); + setMemberQuery(memberUserListQuery ?? null); }, []); return (
@@ -52,7 +53,7 @@ export default function MutedMembersModal({
{ - const { hasNext } = memberQuery; + const hasNext = memberQuery?.hasNext; const target = e.target as HTMLTextAreaElement; const fetchMore = ( target.clientHeight + target.scrollTop === target.scrollHeight diff --git a/src/modules/ChannelSettings/components/ModerationPanel/OperatorList.tsx b/src/modules/ChannelSettings/components/ModerationPanel/OperatorList.tsx index 4ffc8a9e6..6c3359b83 100644 --- a/src/modules/ChannelSettings/components/ModerationPanel/OperatorList.tsx +++ b/src/modules/ChannelSettings/components/ModerationPanel/OperatorList.tsx @@ -17,16 +17,17 @@ import OperatorsModal from './OperatorsModal'; import AddOperatorsModal from './AddOperatorsModal'; import useSendbirdStateContext from '../../../../hooks/useSendbirdStateContext'; import { useChannelSettingsContext } from '../../context/ChannelSettingsProvider'; +import { User } from '@sendbird/chat'; export const OperatorList = (): ReactElement => { - const [operators, setOperators] = useState([]); + const [operators, setOperators] = useState([]); const [showMore, setShowMore] = useState(false); const [showAdd, setShowAdd] = useState(false); const [hasNext, setHasNext] = useState(false); const { stringSet } = useContext(LocalizationContext); const state = useSendbirdStateContext(); - const { channel } = useChannelSettingsContext(); + const channel = useChannelSettingsContext()?.channel; const userId = state?.config?.userId; diff --git a/src/modules/ChannelSettings/components/ModerationPanel/OperatorsModal.tsx b/src/modules/ChannelSettings/components/ModerationPanel/OperatorsModal.tsx index 9b0452e6a..92744709c 100644 --- a/src/modules/ChannelSettings/components/ModerationPanel/OperatorsModal.tsx +++ b/src/modules/ChannelSettings/components/ModerationPanel/OperatorsModal.tsx @@ -14,14 +14,15 @@ import ContextMenu, { MenuItem, MenuItems } from '../../../../ui/ContextMenu'; import { useChannelSettingsContext } from '../../context/ChannelSettingsProvider'; import useSendbirdStateContext from '../../../../hooks/useSendbirdStateContext'; import { LocalizationContext } from '../../../../lib/LocalizationContext'; +import { OperatorListQuery, User } from '@sendbird/chat'; interface Props { onCancel?(): void } export default function OperatorsModal({ onCancel }: Props): ReactElement { - const [operators, setOperators] = useState([]); - const [operatorQuery, setOperatorQuery] = useState(null); - - const { channel } = useChannelSettingsContext(); + const [operators, setOperators] = useState([]); + const [operatorQuery, setOperatorQuery] = useState(null); + + const channel = useChannelSettingsContext()?.channel; const state = useSendbirdStateContext(); const currentUserId = state?.config?.userId; const { stringSet } = useContext(LocalizationContext); @@ -30,10 +31,10 @@ export default function OperatorsModal({ onCancel }: Props): ReactElement { const operatorListQuery = channel?.createOperatorListQuery({ limit: 20, }); - operatorListQuery.next().then((operators) => { + operatorListQuery?.next().then((operators) => { setOperators(operators); }); - setOperatorQuery(operatorListQuery); + setOperatorQuery(operatorListQuery ?? null); }, []); return (
@@ -46,7 +47,7 @@ export default function OperatorsModal({ onCancel }: Props): ReactElement {
{ - const { hasNext } = operatorQuery; + const hasNext = operatorQuery?.hasNext; const target = e.target as HTMLTextAreaElement; const fetchMore = ( target.clientHeight + target.scrollTop === target.scrollHeight diff --git a/src/modules/ChannelSettings/components/ModerationPanel/index.tsx b/src/modules/ChannelSettings/components/ModerationPanel/index.tsx index 7c209a107..85077548e 100644 --- a/src/modules/ChannelSettings/components/ModerationPanel/index.tsx +++ b/src/modules/ChannelSettings/components/ModerationPanel/index.tsx @@ -34,13 +34,13 @@ export default function AdminPannel(): ReactElement { const [frozen, setFrozen] = useState(false); const { stringSet } = useContext(LocalizationContext); - const { channel } = useChannelSettingsContext(); + const channel = useChannelSettingsContext()?.channel; // work around for // https://sendbird.slack.com/archives/G01290GCDCN/p1595922832000900 // SDK bug - after frozen/unfrozen myRole becomes "none" useEffect(() => { - setFrozen(channel?.isFrozen); + setFrozen(channel?.isFrozen ?? false); }, [channel]); return ( @@ -89,7 +89,7 @@ export default function AdminPannel(): ReactElement { > {stringSet.CHANNEL_SETTING__MEMBERS__TITLE} - + )} renderContent={() => ( diff --git a/src/modules/ChannelSettings/components/UserPanel/index.tsx b/src/modules/ChannelSettings/components/UserPanel/index.tsx index c12a9d63c..791bdfdf4 100644 --- a/src/modules/ChannelSettings/components/UserPanel/index.tsx +++ b/src/modules/ChannelSettings/components/UserPanel/index.tsx @@ -23,7 +23,7 @@ const kFormatter = (num: number): string|number => { const UserPanel: React.FC = () => { const { stringSet } = useContext(LocalizationContext); const [showAccordion, setShowAccordion] = useState(false); - const { channel } = useChannelSettingsContext(); + const channel = useChannelSettingsContext()?.channel; return (
{ {stringSet.CHANNEL_SETTING__MEMBERS__TITLE} const { name, coverUrlOrImage } = params; if (sdkInitialized) { const params = {} as OpenChannelCreateParams; - params.operatorUserIds = [sdk?.currentUser?.userId]; + params.operatorUserIds = sdk?.currentUser?.userId ? [sdk.currentUser.userId] : []; params.name = name; params.coverUrlOrImage = coverUrlOrImage; sdk.openChannel.createChannel(onBeforeCreateChannel?.(params) || params) .then((openChannel) => { logger.info('CreateOpenChannel: Succeeded creating openChannel', openChannel); - onCreateChannel(openChannel); + onCreateChannel?.(openChannel); }) .catch((err) => { logger.warning('CreateOpenChannel: Failed creating openChannel', err); diff --git a/src/modules/EditUserProfile/components/EditUserProfileUI/EditUserProfileUIView.tsx b/src/modules/EditUserProfile/components/EditUserProfileUI/EditUserProfileUIView.tsx index b95f560b8..40e8791f0 100644 --- a/src/modules/EditUserProfile/components/EditUserProfileUI/EditUserProfileUIView.tsx +++ b/src/modules/EditUserProfile/components/EditUserProfileUI/EditUserProfileUIView.tsx @@ -24,9 +24,9 @@ export const EditUserProfileUIView = ({ const { theme, setCurrentTheme } = config; const user = stores.userStore?.user; const { stringSet } = useLocalization(); - - const [currentImg, setCurrentImg] = useState(null); - const hiddenInputRef = useRef(null); + + const [currentImg, setCurrentImg] = useState(null); + const hiddenInputRef = useRef(null); return (
{ - setCurrentImg(URL.createObjectURL(e.target.files[0])); - setProfileImage(e.target.files[0]); - hiddenInputRef.current.value = ''; + if(e.target.files) { + setCurrentImg(URL.createObjectURL(e.target.files[0])); + setProfileImage(e.target.files[0]); + } + + if (hiddenInputRef.current) { + hiddenInputRef.current.value = ''; + } }} /> hiddenInputRef.current.click()} + onClick={() => hiddenInputRef.current?.click()} >