From 95c1d73bcde9767bbddc3adc863982449c0d68f5 Mon Sep 17 00:00:00 2001 From: "junyoung.lim" Date: Wed, 26 Feb 2025 17:03:15 +0900 Subject: [PATCH 1/3] Create store with the props as a default value --- src/lib/Sendbird/context/SendbirdContext.tsx | 20 ++++- src/lib/Sendbird/context/SendbirdProvider.tsx | 84 +++++++++++++++---- .../context/ChannelSettingsProvider.tsx | 29 +++++-- src/modules/ChannelSettings/context/types.ts | 2 +- .../context/CreateChannelProvider.tsx | 25 +++++- .../context/GroupChannelProvider.tsx | 48 ++++++++++- .../__tests__/useGroupChannel.spec.tsx | 4 +- .../context/GroupChannelListProvider.tsx | 34 ++++++-- .../context/MessageSearchProvider.tsx | 41 ++++++--- src/modules/Thread/context/ThreadProvider.tsx | 28 ++++++- 10 files changed, 262 insertions(+), 53 deletions(-) diff --git a/src/lib/Sendbird/context/SendbirdContext.tsx b/src/lib/Sendbird/context/SendbirdContext.tsx index 5f495f841..c7aed97be 100644 --- a/src/lib/Sendbird/context/SendbirdContext.tsx +++ b/src/lib/Sendbird/context/SendbirdContext.tsx @@ -12,7 +12,25 @@ export const SendbirdContext = React.createContext createStore(initialState); +export const createSendbirdContextStore = (props?: any) => createStore({ + config: { + ...initialState.config, + ...props.config, + }, + stores: { + ...initialState.stores, + ...props.stores, + }, + eventHandlers: { + ...initialState.eventHandlers, + ...props.eventHandlers, + }, + emojiManager: props.emojiManager ?? initialState.emojiManager, + utils: { + ...initialState.utils, + ...props.utils, + }, +}); /** * A specialized hook for Ssendbird state management diff --git a/src/lib/Sendbird/context/SendbirdProvider.tsx b/src/lib/Sendbird/context/SendbirdProvider.tsx index 71c03d957..df2ad39d1 100644 --- a/src/lib/Sendbird/context/SendbirdProvider.tsx +++ b/src/lib/Sendbird/context/SendbirdProvider.tsx @@ -3,7 +3,13 @@ import React, { ReactElement, useEffect, useMemo, useRef, useState } from 'react import { useUIKitConfig } from '@sendbird/uikit-tools'; /* Types */ -import type { ImageCompressionOptions, SendbirdProviderProps, SendbirdStateConfig } from '../types'; +import { + ImageCompressionOptions, + Logger, + SendbirdProviderProps, + SendbirdState, + SendbirdStateConfig, +} from '../types'; /* Providers */ import VoiceMessageProvider from '../../VoiceMessageProvider'; @@ -33,10 +39,10 @@ import { DEFAULT_MULTIPLE_FILES_MESSAGE_LIMIT, DEFAULT_UPLOAD_SIZE_LIMIT, VOICE_ import { EmojiReactionListRoot, MenuRoot } from '../../../ui/ContextMenu'; import useSendbird from './hooks/useSendbird'; -import { SendbirdContext, useSendbirdStore } from './SendbirdContext'; -import { createStore } from '../../../utils/storeManager'; -import { initialState } from './initialState'; +import { createSendbirdContextStore, SendbirdContext, useSendbirdStore } from './SendbirdContext'; import useDeepCompareEffect from '../../../hooks/useDeepCompareEffect'; +import { PartialDeep } from '../../../utils/typeHelpers/partialDeep'; +import { deleteNullish } from '../../../utils/utils'; /** * SendbirdContext - Manager @@ -49,6 +55,7 @@ const SendbirdContextManager = ({ customWebSocketHost, configureSession, theme = 'light', + logger, config = {}, nickname = '', colorSet, @@ -68,11 +75,10 @@ const SendbirdContextManager = ({ eventHandlers, htmlTextDirection = 'ltr', forceLeftToRightMessageLayout = false, -}: SendbirdProviderProps): ReactElement => { +}: SendbirdProviderProps & { logger: Logger }): ReactElement => { const onStartDirectMessage = _onStartDirectMessage ?? _onUserProfileMessage; - const { logLevel = '', userMention = {}, isREMUnitEnabled = false, pubSub: customPubSub } = config; + const { userMention = {}, isREMUnitEnabled = false, pubSub: customPubSub } = config; const { isMobile } = useMediaQueryContext(); - const [logger, setLogger] = useState(LoggerFactory(logLevel as LogLevel)); const [pubSub] = useState(customPubSub ?? pubSubFactory()); const { state, updateState } = useSendbirdStore(); @@ -121,11 +127,6 @@ const SendbirdContextManager = ({ actions.disconnect({ logger }); }); - // to create a pubsub to communicate between parent and child - useEffect(() => { - setLogger(LoggerFactory(logLevel as LogLevel)); - }, [logLevel]); - // should move to reducer const [currentTheme, setCurrentTheme] = useState(theme); useEffect(() => { @@ -365,8 +366,49 @@ const SendbirdContextManager = ({ return null; }; -const InternalSendbirdProvider = ({ children, stringSet, breakpoint, dateLocale }) => { - const storeRef = useRef(createStore(initialState)); +const InternalSendbirdProvider = (props: SendbirdProviderProps & { logger: Logger }) => { + const { + children, + stringSet, + breakpoint, + dateLocale, + } = props; + + const defaultProps: PartialDeep = deleteNullish({ + config: { + renderUserProfile: props?.renderUserProfile, + onStartDirectMessage: props?.onStartDirectMessage, + allowProfileEdit: props?.allowProfileEdit, + appId: props?.appId, + userId: props?.userId, + accessToken: props?.accessToken, + theme: props?.theme, + htmlTextDirection: props?.htmlTextDirection, + forceLeftToRightMessageLayout: props?.forceLeftToRightMessageLayout, + pubSub: props?.config?.pubSub, + logger: props?.logger, + userListQuery: props?.userListQuery, + voiceRecord: { + maxRecordingTime: props?.voiceRecord?.maxRecordingTime ?? VOICE_RECORDER_DEFAULT_MAX, + minRecordingTime: props?.voiceRecord?.minRecordingTime ?? VOICE_RECORDER_DEFAULT_MIN, + }, + userMention: { + maxMentionCount: props?.config?.userMention?.maxMentionCount || 10, + maxSuggestionCount: props?.config?.userMention?.maxSuggestionCount || 15, + }, + imageCompression: { + compressionRate: 0.7, + outputFormat: 'preserve', + ...props?.imageCompression, + }, + disableMarkAsDelivered: props?.disableMarkAsDelivered, + isMultipleFilesMessageEnabled: props?.isMultipleFilesMessageEnabled, + }, + eventHandlers: props?.eventHandlers, + }); + + const storeRef = useRef(createSendbirdContextStore(defaultProps)); + const localeStringSet = useMemo(() => { return { ...getStringSet('en'), ...stringSet }; }, [stringSet]); @@ -391,11 +433,19 @@ const InternalSendbirdProvider = ({ children, stringSet, breakpoint, dateLocale }; export const SendbirdContextProvider = (props: SendbirdProviderProps) => { - const { children } = props; + const { children, config } = props; + const logLevel = config?.logLevel; + + const [logger, setLogger] = useState(LoggerFactory(logLevel as LogLevel)); + + // to create a pubsub to communicate between parent and child + useEffect(() => { + setLogger(LoggerFactory(logLevel as LogLevel)); + }, [logLevel]); return ( - - + + {children} ); diff --git a/src/modules/ChannelSettings/context/ChannelSettingsProvider.tsx b/src/modules/ChannelSettings/context/ChannelSettingsProvider.tsx index f6a783d5a..e6fced3e6 100644 --- a/src/modules/ChannelSettings/context/ChannelSettingsProvider.tsx +++ b/src/modules/ChannelSettings/context/ChannelSettingsProvider.tsx @@ -7,11 +7,12 @@ import { useStore } from '../../../hooks/useStore'; import { useChannelHandler } from './hooks/useChannelHandler'; import uuidv4 from '../../../utils/uuid'; -import { classnames } from '../../../utils/utils'; +import { classnames, deleteNullish } from '../../../utils/utils'; import { createStore } from '../../../utils/storeManager'; import { UserProfileProvider } from '../../../lib/UserProfileContext'; import useSendbird from '../../../lib/Sendbird/context/hooks/useSendbird'; import useChannelSettings from './useChannelSettings'; +import { PartialDeep } from '../../../utils/typeHelpers/partialDeep'; export const ChannelSettingsContext = createContext> | null>(null); @@ -103,9 +104,27 @@ const ChannelSettingsManager = ({ return null; }; -const createChannelSettingsStore = () => createStore(initialState); -const InternalChannelSettingsProvider = ({ children }) => { - const storeRef = useRef(createChannelSettingsStore()); +const createChannelSettingsStore = (props?: Omit, 'channel'>) => createStore({ + ...initialState, + ...props, +}); + +const InternalChannelSettingsProvider = (props: ChannelSettingsContextProps) => { + const { children } = props; + + const defaultProps: PartialDeep = deleteNullish({ + channelUrl: props?.channelUrl, + onCloseClick: props?.onCloseClick, + onLeaveChannel: props?.onLeaveChannel, + onChannelModified: props?.onChannelModified, + onBeforeUpdateChannel: props?.onBeforeUpdateChannel, + renderUserListItem: props?.renderUserListItem, + queries: props?.queries, + overrideInviteUser: props?.overrideInviteUser, + }); + + const storeRef = useRef(createChannelSettingsStore(defaultProps)); + return ( {children} @@ -116,7 +135,7 @@ const InternalChannelSettingsProvider = ({ children }) => { const ChannelSettingsProvider = (props: ChannelSettingsContextProps) => { const { children, className } = props; return ( - +
diff --git a/src/modules/ChannelSettings/context/types.ts b/src/modules/ChannelSettings/context/types.ts index 685c0e586..8c9585d54 100644 --- a/src/modules/ChannelSettings/context/types.ts +++ b/src/modules/ChannelSettings/context/types.ts @@ -42,6 +42,6 @@ export interface ChannelSettingsState extends CommonChannelSettingsProps { export interface ChannelSettingsContextProps extends CommonChannelSettingsProps, Pick { - children?: React.ReactElement; + children?: ReactNode; className?: string; } diff --git a/src/modules/CreateChannel/context/CreateChannelProvider.tsx b/src/modules/CreateChannel/context/CreateChannelProvider.tsx index a7fbfced1..d75a5acfb 100644 --- a/src/modules/CreateChannel/context/CreateChannelProvider.tsx +++ b/src/modules/CreateChannel/context/CreateChannelProvider.tsx @@ -11,6 +11,8 @@ import { createStore } from '../../../utils/storeManager'; import { useStore } from '../../../hooks/useStore'; import useCreateChannel from './useCreateChannel'; import useSendbird from '../../../lib/Sendbird/context/hooks/useSendbird'; +import { PartialDeep } from '../../../utils/typeHelpers/partialDeep'; +import { deleteNullish } from '../../../utils/utils'; const CreateChannelContext = React.createContext> | null>(null); @@ -139,16 +141,31 @@ const CreateChannelProvider: React.FC = (props: Crea const { children } = props; return ( - + {children} ); }; -const createCreateChannelStore = () => createStore(initialState); -const InternalCreateChannelProvider: React.FC> = ({ children }) => { - const storeRef = useRef(createCreateChannelStore()); +const createCreateChannelStore = (props?: PartialDeep) => createStore({ + ...initialState, + ...props, +}); + +const InternalCreateChannelProvider: React.FC> = (props: CreateChannelProviderProps) => { + const { children } = props; + + const defaultProps: PartialDeep = deleteNullish({ + userListQuery: props?.userListQuery, + onCreateChannelClick: props?.onCreateChannelClick, + onChannelCreated: props?.onChannelCreated, + onBeforeCreateChannel: props?.onBeforeCreateChannel, + onCreateChannel: props?.onCreateChannel, + overrideInviteUser: props?.overrideInviteUser, + }); + + const storeRef = useRef(createCreateChannelStore(defaultProps)); return ( diff --git a/src/modules/GroupChannel/context/GroupChannelProvider.tsx b/src/modules/GroupChannel/context/GroupChannelProvider.tsx index bd76b0d8a..94c0540dc 100644 --- a/src/modules/GroupChannel/context/GroupChannelProvider.tsx +++ b/src/modules/GroupChannel/context/GroupChannelProvider.tsx @@ -31,6 +31,8 @@ import type { } from './types'; import useSendbird from '../../../lib/Sendbird/context/hooks/useSendbird'; import useDeepCompareEffect from '../../../hooks/useDeepCompareEffect'; +import { PartialDeep } from '../../../utils/typeHelpers/partialDeep'; +import { deleteNullish } from '../../../utils/utils'; const initialState = { currentChannel: null, @@ -60,8 +62,48 @@ const initialState = { export const GroupChannelContext = createContext> | null>(null); -export const InternalGroupChannelProvider: React.FC> = ({ children }) => { - const storeRef = useRef(createStore(initialState)); +const createGroupChannelListStore = (props?: any) => createStore({ + ...initialState, + ...props, +}); + +export const InternalGroupChannelProvider = (props: GroupChannelProviderProps) => { + const { children } = props; + + const defaultProps: PartialDeep = deleteNullish({ + channelUrl: props?.channelUrl, + renderUserProfile: props?.renderUserProfile, + disableUserProfile: props?.disableUserProfile, + onUserProfileMessage: props?.onUserProfileMessage, + onStartDirectMessage: props?.onStartDirectMessage, + isReactionEnabled: props?.isReactionEnabled, + isMessageGroupingEnabled: props?.isMessageGroupingEnabled, + isMultipleFilesMessageEnabled: props?.isMultipleFilesMessageEnabled, + showSearchIcon: props?.showSearchIcon, + threadReplySelectType: props?.threadReplySelectType, + disableMarkAsRead: props?.disableMarkAsRead, + scrollBehavior: props?.scrollBehavior, + forceLeftToRightMessageLayout: props?.forceLeftToRightMessageLayout, + startingPoint: props?.startingPoint, + animatedMessageId: props?.animatedMessageId, + onMessageAnimated: props?.onMessageAnimated, + messageListQueryParams: props?.messageListQueryParams, + filterEmojiCategoryIds: props?.filterEmojiCategoryIds, + onBeforeSendUserMessage: props?.onBeforeSendUserMessage, + onBeforeSendFileMessage: props?.onBeforeSendFileMessage, + onBeforeSendVoiceMessage: props?.onBeforeSendVoiceMessage, + onBeforeSendMultipleFilesMessage: props?.onBeforeSendMultipleFilesMessage, + onBeforeUpdateUserMessage: props?.onBeforeUpdateUserMessage, + onBeforeDownloadFileMessage: props?.onBeforeDownloadFileMessage, + onBackClick: props?.onBackClick, + onChatHeaderActionClick: props?.onChatHeaderActionClick, + onReplyInThreadClick: props?.onReplyInThreadClick, + onSearchClick: props?.onSearchClick, + onQuoteMessageClick: props?.onQuoteMessageClick, + renderUserMentionItem: props?.renderUserMentionItem, + }); + + const storeRef = useRef(createGroupChannelListStore(defaultProps)); return ( @@ -319,7 +361,7 @@ const GroupChannelManager :React.FC = (props) => { return ( - + {props.children} diff --git a/src/modules/GroupChannel/context/__tests__/useGroupChannel.spec.tsx b/src/modules/GroupChannel/context/__tests__/useGroupChannel.spec.tsx index ef336312d..9753768b7 100644 --- a/src/modules/GroupChannel/context/__tests__/useGroupChannel.spec.tsx +++ b/src/modules/GroupChannel/context/__tests__/useGroupChannel.spec.tsx @@ -113,14 +113,14 @@ describe('useGroupChannel', () => { it('provides initial state', () => { const { result } = renderHook(() => useGroupChannel(), { wrapper }); - expect(result.current.state).toEqual(expect.objectContaining({ + expect(result.current.state).toMatchObject({ currentChannel: null, channelUrl: mockChannel.url, fetchChannelError: null, quoteMessage: null, animatedMessageId: null, isScrollBottomReached: true, - })); + }); }); it('updates channel state', () => { diff --git a/src/modules/GroupChannelList/context/GroupChannelListProvider.tsx b/src/modules/GroupChannelList/context/GroupChannelListProvider.tsx index cd1d41e14..75a438a46 100644 --- a/src/modules/GroupChannelList/context/GroupChannelListProvider.tsx +++ b/src/modules/GroupChannelList/context/GroupChannelListProvider.tsx @@ -14,10 +14,11 @@ import type { SdkStore } from '../../../lib/Sendbird/types'; import { PartialRequired } from '../../../utils/typeHelpers/partialRequired'; import { createStore } from '../../../utils/storeManager'; import { useStore } from '../../../hooks/useStore'; -import { noop } from '../../../utils/utils'; +import { deleteNullish, noop } from '../../../utils/utils'; import useSendbird from '../../../lib/Sendbird/context/hooks/useSendbird'; import useGroupChannelList from './useGroupChannelList'; import useDeepCompareEffect from '../../../hooks/useDeepCompareEffect'; +import { PartialDeep } from '../../../utils/typeHelpers/partialDeep'; type OnCreateChannelClickParams = { users: Array; onClose: () => void; channelType: CHANNEL_TYPE }; type ChannelListDataSource = ReturnType; @@ -224,9 +225,32 @@ export const GroupChannelListManager: React.FC = return null; }; -const createGroupChannelListStore = () => createStore(initialState); -const InternalGroupChannelListProvider = ({ children }) => { - const storeRef = useRef(createGroupChannelListStore()); +const createGroupChannelListStore = (props?: Omit, 'scrollRef' | 'groupChannels'>) => createStore({ + ...initialState, + ...props, +}); + +const InternalGroupChannelListProvider = (props: GroupChannelListProviderProps) => { + const { children } = props; + + const defaultProps: PartialDeep = deleteNullish({ + onChannelSelect: props?.onChannelSelect, + onChannelCreated: props?.onChannelCreated, + className: props?.className, + selectedChannelUrl: props?.selectedChannelUrl, + allowProfileEdit: props?.allowProfileEdit, + disableAutoSelect: props?.disableAutoSelect, + isTypingIndicatorEnabled: props?.isTypingIndicatorEnabled, + isMessageReceiptStatusEnabled: props?.isMessageReceiptStatusEnabled, + channelListQueryParams: props?.channelListQueryParams, + onThemeChange: props?.onThemeChange, + onCreateChannelClick: props?.onCreateChannelClick, + onBeforeCreateChannel: props?.onBeforeCreateChannel, + onUserProfileUpdated: props?.onUserProfileUpdated, + }); + + const storeRef = useRef(createGroupChannelListStore(defaultProps)); + return ( {children} @@ -238,7 +262,7 @@ export const GroupChannelListProvider = (props: GroupChannelListProviderProps) = const { children, className } = props; return ( - +
{children}
diff --git a/src/modules/MessageSearch/context/MessageSearchProvider.tsx b/src/modules/MessageSearch/context/MessageSearchProvider.tsx index 330582028..dc361a082 100644 --- a/src/modules/MessageSearch/context/MessageSearchProvider.tsx +++ b/src/modules/MessageSearch/context/MessageSearchProvider.tsx @@ -14,6 +14,8 @@ import { createStore } from '../../../utils/storeManager'; import { useStore } from '../../../hooks/useStore'; import useMessageSearch from './hooks/useMessageSearch'; import useSendbird from '../../../lib/Sendbird/context/hooks/useSendbird'; +import { PartialDeep } from '../../../utils/typeHelpers/partialDeep'; +import { deleteNullish } from '../../../utils/utils'; export interface MessageSearchProviderProps { channelUrl: string; @@ -134,9 +136,23 @@ const MessageSearchManager: React.FC = ({ return null; }; -const createMessageSearchStore = () => createStore(initialState); -const InternalMessageSearchProvider: React.FC> = ({ children }) => { - const storeRef = useRef(createMessageSearchStore()); +const createMessageSearchStore = (props?: any) => createStore({ + ...initialState, + ...props, +}); + +const InternalMessageSearchProvider: React.FC> = (props: MessageSearchProviderProps) => { + const { children } = props; + + const defaultProps: PartialDeep = deleteNullish({ + channelUrl: props?.channelUrl, + messageSearchQuery: props?.messageSearchQuery, + searchString: props?.searchString, + onResultLoaded: props?.onResultLoaded, + onResultClick: props?.onResultClick, + }); + + const storeRef = useRef(createMessageSearchStore(defaultProps)); return ( @@ -145,17 +161,18 @@ const InternalMessageSearchProvider: React.FC> ); }; -const MessageSearchProvider: React.FC = ({ - children, - channelUrl, - searchString, - messageSearchQuery, - onResultLoaded, - onResultClick, -}) => { +const MessageSearchProvider: React.FC = (props: MessageSearchProviderProps) => { + const { + children, + channelUrl, + searchString, + messageSearchQuery, + onResultLoaded, + onResultClick, + } = props; return ( - + { @@ -90,8 +91,29 @@ const initialState: ThreadState = { export const ThreadContext = React.createContext> | null>(null); -export const InternalThreadProvider: React.FC> = ({ children }) => { - const storeRef = useRef(createStore(initialState)); +const createThreadStore = (props?: any) => createStore({ + ...initialState, + ...props, +}); + +export const InternalThreadProvider: React.FC> = (props: ThreadProviderProps) => { + const { children } = props; + + const defaultProps: PartialDeep = { + channelUrl: props?.channelUrl, + message: props?.message, + onHeaderActionClick: props?.onHeaderActionClick, + onMoveToParentMessage: props?.onMoveToParentMessage, + onBeforeSendUserMessage: props?.onBeforeSendUserMessage, + onBeforeSendFileMessage: props?.onBeforeSendFileMessage, + onBeforeSendVoiceMessage: props?.onBeforeSendVoiceMessage, + onBeforeSendMultipleFilesMessage: props?.onBeforeSendMultipleFilesMessage, + onBeforeDownloadFileMessage: props?.onBeforeDownloadFileMessage, + isMultipleFilesMessageEnabled: props?.isMultipleFilesMessageEnabled, + filterEmojiCategoryIds: props?.filterEmojiCategoryIds, + }; + + const storeRef = useRef(createThreadStore(defaultProps)); return ( @@ -211,7 +233,7 @@ export const ThreadProvider = (props: ThreadProviderProps) => { const { children } = props; return ( - + {/* UserProfileProvider */} From 9b5395635aa1bea1247a0db3c7a12a609632fe71 Mon Sep 17 00:00:00 2001 From: "junyoung.lim" Date: Wed, 26 Feb 2025 17:13:21 +0900 Subject: [PATCH 2/3] Fix undefined error --- src/lib/Sendbird/context/SendbirdContext.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/Sendbird/context/SendbirdContext.tsx b/src/lib/Sendbird/context/SendbirdContext.tsx index c7aed97be..07efc15b6 100644 --- a/src/lib/Sendbird/context/SendbirdContext.tsx +++ b/src/lib/Sendbird/context/SendbirdContext.tsx @@ -15,20 +15,20 @@ export const SendbirdContext = React.createContext createStore({ config: { ...initialState.config, - ...props.config, + ...props?.config, }, stores: { ...initialState.stores, - ...props.stores, + ...props?.stores, }, eventHandlers: { ...initialState.eventHandlers, - ...props.eventHandlers, + ...props?.eventHandlers, }, - emojiManager: props.emojiManager ?? initialState.emojiManager, + emojiManager: props?.emojiManager ?? initialState.emojiManager, utils: { ...initialState.utils, - ...props.utils, + ...props?.utils, }, }); From a161730dc40746b1ac46f1ec3cdf5ad275a371ac Mon Sep 17 00:00:00 2001 From: "junyoung.lim" Date: Thu, 27 Feb 2025 12:43:09 +0900 Subject: [PATCH 3/3] Replace inappropriate PartialDeep --- src/lib/Sendbird/context/SendbirdContext.tsx | 5 +++-- src/lib/Sendbird/context/SendbirdProvider.tsx | 4 ++-- .../context/ChannelSettingsProvider.tsx | 5 ++--- .../CreateChannel/context/CreateChannelProvider.tsx | 5 ++--- .../GroupChannel/context/GroupChannelProvider.tsx | 7 +++---- .../context/GroupChannelListProvider.tsx | 5 ++--- .../MessageSearch/context/MessageSearchProvider.tsx | 5 ++--- src/modules/Thread/context/ThreadProvider.tsx | 5 ++--- src/utils/typeHelpers/partialDeep.ts | 10 ++++++++++ 9 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/lib/Sendbird/context/SendbirdContext.tsx b/src/lib/Sendbird/context/SendbirdContext.tsx index 07efc15b6..8556326ea 100644 --- a/src/lib/Sendbird/context/SendbirdContext.tsx +++ b/src/lib/Sendbird/context/SendbirdContext.tsx @@ -3,6 +3,7 @@ import { createStore } from '../../../utils/storeManager'; import { initialState } from './initialState'; import { SendbirdState } from '../types'; import { useStore } from '../../../hooks/useStore'; +import { TwoDepthPartial } from '../../../utils/typeHelpers/partialDeep'; /** * SendbirdContext @@ -12,7 +13,7 @@ export const SendbirdContext = React.createContext createStore({ +export const createSendbirdContextStore = (props?: TwoDepthPartial) => createStore({ config: { ...initialState.config, ...props?.config, @@ -25,7 +26,7 @@ export const createSendbirdContextStore = (props?: any) => createStore({ ...initialState.eventHandlers, ...props?.eventHandlers, }, - emojiManager: props?.emojiManager ?? initialState.emojiManager, + emojiManager: initialState.emojiManager, utils: { ...initialState.utils, ...props?.utils, diff --git a/src/lib/Sendbird/context/SendbirdProvider.tsx b/src/lib/Sendbird/context/SendbirdProvider.tsx index df2ad39d1..6b13c576e 100644 --- a/src/lib/Sendbird/context/SendbirdProvider.tsx +++ b/src/lib/Sendbird/context/SendbirdProvider.tsx @@ -41,8 +41,8 @@ import { EmojiReactionListRoot, MenuRoot } from '../../../ui/ContextMenu'; import useSendbird from './hooks/useSendbird'; import { createSendbirdContextStore, SendbirdContext, useSendbirdStore } from './SendbirdContext'; import useDeepCompareEffect from '../../../hooks/useDeepCompareEffect'; -import { PartialDeep } from '../../../utils/typeHelpers/partialDeep'; import { deleteNullish } from '../../../utils/utils'; +import { TwoDepthPartial } from '../../../utils/typeHelpers/partialDeep'; /** * SendbirdContext - Manager @@ -374,7 +374,7 @@ const InternalSendbirdProvider = (props: SendbirdProviderProps & { logger: Logge dateLocale, } = props; - const defaultProps: PartialDeep = deleteNullish({ + const defaultProps: TwoDepthPartial = deleteNullish({ config: { renderUserProfile: props?.renderUserProfile, onStartDirectMessage: props?.onStartDirectMessage, diff --git a/src/modules/ChannelSettings/context/ChannelSettingsProvider.tsx b/src/modules/ChannelSettings/context/ChannelSettingsProvider.tsx index e6fced3e6..56d06d7de 100644 --- a/src/modules/ChannelSettings/context/ChannelSettingsProvider.tsx +++ b/src/modules/ChannelSettings/context/ChannelSettingsProvider.tsx @@ -12,7 +12,6 @@ import { createStore } from '../../../utils/storeManager'; import { UserProfileProvider } from '../../../lib/UserProfileContext'; import useSendbird from '../../../lib/Sendbird/context/hooks/useSendbird'; import useChannelSettings from './useChannelSettings'; -import { PartialDeep } from '../../../utils/typeHelpers/partialDeep'; export const ChannelSettingsContext = createContext> | null>(null); @@ -104,7 +103,7 @@ const ChannelSettingsManager = ({ return null; }; -const createChannelSettingsStore = (props?: Omit, 'channel'>) => createStore({ +const createChannelSettingsStore = (props?: Partial) => createStore({ ...initialState, ...props, }); @@ -112,7 +111,7 @@ const createChannelSettingsStore = (props?: Omit { const { children } = props; - const defaultProps: PartialDeep = deleteNullish({ + const defaultProps: Partial = deleteNullish({ channelUrl: props?.channelUrl, onCloseClick: props?.onCloseClick, onLeaveChannel: props?.onLeaveChannel, diff --git a/src/modules/CreateChannel/context/CreateChannelProvider.tsx b/src/modules/CreateChannel/context/CreateChannelProvider.tsx index d75a5acfb..983cf53b5 100644 --- a/src/modules/CreateChannel/context/CreateChannelProvider.tsx +++ b/src/modules/CreateChannel/context/CreateChannelProvider.tsx @@ -11,7 +11,6 @@ import { createStore } from '../../../utils/storeManager'; import { useStore } from '../../../hooks/useStore'; import useCreateChannel from './useCreateChannel'; import useSendbird from '../../../lib/Sendbird/context/hooks/useSendbird'; -import { PartialDeep } from '../../../utils/typeHelpers/partialDeep'; import { deleteNullish } from '../../../utils/utils'; const CreateChannelContext = React.createContext> | null>(null); @@ -148,7 +147,7 @@ const CreateChannelProvider: React.FC = (props: Crea ); }; -const createCreateChannelStore = (props?: PartialDeep) => createStore({ +const createCreateChannelStore = (props?: Partial) => createStore({ ...initialState, ...props, }); @@ -156,7 +155,7 @@ const createCreateChannelStore = (props?: PartialDeep) => cr const InternalCreateChannelProvider: React.FC> = (props: CreateChannelProviderProps) => { const { children } = props; - const defaultProps: PartialDeep = deleteNullish({ + const defaultProps: Partial = deleteNullish({ userListQuery: props?.userListQuery, onCreateChannelClick: props?.onCreateChannelClick, onChannelCreated: props?.onChannelCreated, diff --git a/src/modules/GroupChannel/context/GroupChannelProvider.tsx b/src/modules/GroupChannel/context/GroupChannelProvider.tsx index 94c0540dc..117a1dbdc 100644 --- a/src/modules/GroupChannel/context/GroupChannelProvider.tsx +++ b/src/modules/GroupChannel/context/GroupChannelProvider.tsx @@ -31,7 +31,6 @@ import type { } from './types'; import useSendbird from '../../../lib/Sendbird/context/hooks/useSendbird'; import useDeepCompareEffect from '../../../hooks/useDeepCompareEffect'; -import { PartialDeep } from '../../../utils/typeHelpers/partialDeep'; import { deleteNullish } from '../../../utils/utils'; const initialState = { @@ -62,7 +61,7 @@ const initialState = { export const GroupChannelContext = createContext> | null>(null); -const createGroupChannelListStore = (props?: any) => createStore({ +const createGroupChannelStore = (props?: Partial) => createStore({ ...initialState, ...props, }); @@ -70,7 +69,7 @@ const createGroupChannelListStore = (props?: any) => createStore({ export const InternalGroupChannelProvider = (props: GroupChannelProviderProps) => { const { children } = props; - const defaultProps: PartialDeep = deleteNullish({ + const defaultProps: Partial = deleteNullish({ channelUrl: props?.channelUrl, renderUserProfile: props?.renderUserProfile, disableUserProfile: props?.disableUserProfile, @@ -103,7 +102,7 @@ export const InternalGroupChannelProvider = (props: GroupChannelProviderProps) = renderUserMentionItem: props?.renderUserMentionItem, }); - const storeRef = useRef(createGroupChannelListStore(defaultProps)); + const storeRef = useRef(createGroupChannelStore(defaultProps)); return ( diff --git a/src/modules/GroupChannelList/context/GroupChannelListProvider.tsx b/src/modules/GroupChannelList/context/GroupChannelListProvider.tsx index 75a438a46..f2980c9ba 100644 --- a/src/modules/GroupChannelList/context/GroupChannelListProvider.tsx +++ b/src/modules/GroupChannelList/context/GroupChannelListProvider.tsx @@ -18,7 +18,6 @@ import { deleteNullish, noop } from '../../../utils/utils'; import useSendbird from '../../../lib/Sendbird/context/hooks/useSendbird'; import useGroupChannelList from './useGroupChannelList'; import useDeepCompareEffect from '../../../hooks/useDeepCompareEffect'; -import { PartialDeep } from '../../../utils/typeHelpers/partialDeep'; type OnCreateChannelClickParams = { users: Array; onClose: () => void; channelType: CHANNEL_TYPE }; type ChannelListDataSource = ReturnType; @@ -225,7 +224,7 @@ export const GroupChannelListManager: React.FC = return null; }; -const createGroupChannelListStore = (props?: Omit, 'scrollRef' | 'groupChannels'>) => createStore({ +const createGroupChannelListStore = (props?: Partial) => createStore({ ...initialState, ...props, }); @@ -233,7 +232,7 @@ const createGroupChannelListStore = (props?: Omit { const { children } = props; - const defaultProps: PartialDeep = deleteNullish({ + const defaultProps: Partial = deleteNullish({ onChannelSelect: props?.onChannelSelect, onChannelCreated: props?.onChannelCreated, className: props?.className, diff --git a/src/modules/MessageSearch/context/MessageSearchProvider.tsx b/src/modules/MessageSearch/context/MessageSearchProvider.tsx index dc361a082..216179380 100644 --- a/src/modules/MessageSearch/context/MessageSearchProvider.tsx +++ b/src/modules/MessageSearch/context/MessageSearchProvider.tsx @@ -14,7 +14,6 @@ import { createStore } from '../../../utils/storeManager'; import { useStore } from '../../../hooks/useStore'; import useMessageSearch from './hooks/useMessageSearch'; import useSendbird from '../../../lib/Sendbird/context/hooks/useSendbird'; -import { PartialDeep } from '../../../utils/typeHelpers/partialDeep'; import { deleteNullish } from '../../../utils/utils'; export interface MessageSearchProviderProps { @@ -136,7 +135,7 @@ const MessageSearchManager: React.FC = ({ return null; }; -const createMessageSearchStore = (props?: any) => createStore({ +const createMessageSearchStore = (props?: Partial) => createStore({ ...initialState, ...props, }); @@ -144,7 +143,7 @@ const createMessageSearchStore = (props?: any) => createStore({ const InternalMessageSearchProvider: React.FC> = (props: MessageSearchProviderProps) => { const { children } = props; - const defaultProps: PartialDeep = deleteNullish({ + const defaultProps: Partial = deleteNullish({ channelUrl: props?.channelUrl, messageSearchQuery: props?.messageSearchQuery, searchString: props?.searchString, diff --git a/src/modules/Thread/context/ThreadProvider.tsx b/src/modules/Thread/context/ThreadProvider.tsx index 5fd2986e9..8833a5075 100644 --- a/src/modules/Thread/context/ThreadProvider.tsx +++ b/src/modules/Thread/context/ThreadProvider.tsx @@ -24,7 +24,6 @@ import useSetCurrentUserId from './hooks/useSetCurrentUserId'; import useThread from './useThread'; import useSendbird from '../../../lib/Sendbird/context/hooks/useSendbird'; import useDeepCompareEffect from '../../../hooks/useDeepCompareEffect'; -import { PartialDeep } from '../../../utils/typeHelpers/partialDeep'; export interface ThreadProviderProps extends Pick { @@ -91,7 +90,7 @@ const initialState: ThreadState = { export const ThreadContext = React.createContext> | null>(null); -const createThreadStore = (props?: any) => createStore({ +const createThreadStore = (props?: Partial) => createStore({ ...initialState, ...props, }); @@ -99,7 +98,7 @@ const createThreadStore = (props?: any) => createStore({ export const InternalThreadProvider: React.FC> = (props: ThreadProviderProps) => { const { children } = props; - const defaultProps: PartialDeep = { + const defaultProps: Partial = { channelUrl: props?.channelUrl, message: props?.message, onHeaderActionClick: props?.onHeaderActionClick, diff --git a/src/utils/typeHelpers/partialDeep.ts b/src/utils/typeHelpers/partialDeep.ts index 29368ddfb..0b8502bd3 100644 --- a/src/utils/typeHelpers/partialDeep.ts +++ b/src/utils/typeHelpers/partialDeep.ts @@ -18,3 +18,13 @@ export type PartialDeep = T extends object [P in keyof T]?: PartialDeep; } : T; + +export type TwoDepthPartial = T extends object + ? T extends Set // Set, Map, Function, etc. are also treated as an object so we need this to skip recursion for them. + ? T + : T extends (...args: any[]) => any + ? T + : { + [P in keyof T]?: Partial; + } + : T;