Skip to content

Commit 9be7a49

Browse files
AhyoungRyuHoonBaekgit-babelchrisallo
authored
[CLNP-4657] Provider migration - P0 items (#1269)
Addresses P0 items in https://sendbird.atlassian.net/browse/CLNP-4657 This PR is for merging a mega branch `feat/state-mgmt-migration-1` into `main`. --------- Co-authored-by: Baek EunSeo <[email protected]> Co-authored-by: Junyoung Lim <[email protected]> Co-authored-by: junyoung.lim <[email protected]> Co-authored-by: Chris Heo <[email protected]>
1 parent 79bfe84 commit 9be7a49

File tree

341 files changed

+14805
-5321
lines changed

Some content is hidden

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

341 files changed

+14805
-5321
lines changed

.storybook/preview.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react';
22
import type { Preview } from '@storybook/react';
3-
import { SendbirdSdkContext } from '../src/lib/SendbirdSdkContext';
3+
import { SendbirdContext } from '../src/lib/Sendbird/context/SendbirdContext';
44

55
import '../src/lib/index.scss';
66
import './index.css';
@@ -28,9 +28,9 @@ const preview: Preview = {
2828
decorators: [
2929
(Story) => (
3030
<div className="sendbird-theme--light">
31-
<SendbirdSdkContext.Provider value={{} as any}>
31+
<SendbirdContext.Provider value={{} as any}>
3232
{Story()}
33-
</SendbirdSdkContext.Provider>
33+
</SendbirdContext.Provider>
3434
</div>
3535
),
3636
],

apps/testing/src/utils/paramsBuilder.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { UIKitOptions } from '../../../../src/lib/types.ts';
1+
import { UIKitOptions } from '../../../../src/lib/Sendbird/types';
22
import { useSearchParams } from 'react-router-dom';
33

44
export interface InitialParams {

apps/testing/vite.config.ts

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ import postcssRtlOptions from '../../postcssRtlOptions.mjs';
99
export default defineConfig({
1010
plugins: [react(), vitePluginSvgr({ include: '**/*.svg' })],
1111
css: {
12+
preprocessorOptions: {
13+
scss: {
14+
silenceDeprecations: ['legacy-js-api'],
15+
},
16+
},
1217
postcss: {
1318
plugins: [postcssRtl(postcssRtlOptions)],
1419
},

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@
106106
"@testing-library/react": "^16.2.0",
107107
"@testing-library/user-event": "^14.4.3",
108108
"@types/node": "^22.7.2",
109+
"@types/use-sync-external-store": "^0.0.6",
109110
"@typescript-eslint/eslint-plugin": "^6.17.0",
110111
"@typescript-eslint/parser": "^6.17.0",
111112
"autoprefixer": "^9.7.4",
@@ -148,6 +149,7 @@
148149
"ts-pattern": "^4.2.2",
149150
"typedoc": "^0.25.13",
150151
"typescript": "^5.4.5",
152+
"use-sync-external-store": "^1.2.2",
151153
"vite": "^5.1.5",
152154
"vite-plugin-svgr": "^4.2.0"
153155
},

rollup.module-exports.mjs

+10-9
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ export default {
1010
App: 'src/modules/App/index.tsx',
1111

1212
// SendbirdProvider
13-
SendbirdProvider: 'src/lib/Sendbird.tsx',
13+
SendbirdProvider: 'src/lib/Sendbird/index.tsx',
1414
sendbirdSelectors: 'src/lib/selectors.ts',
15-
useSendbirdStateContext: 'src/hooks/useSendbirdStateContext.tsx',
16-
withSendbird: 'src/lib/SendbirdSdkContext.tsx',
15+
// TODO: Support below legacy exports
16+
// useSendbirdStateContext: 'src/hooks/useSendbirdStateContext.tsx',
17+
// withSendbird: 'src/lib/SendbirdSdkContext.tsx',
1718

1819
// Voice message
1920
'VoiceRecorder/context': 'src/hooks/VoiceRecorder/index.tsx',
@@ -49,7 +50,7 @@ export default {
4950

5051
// GroupChannelList
5152
GroupChannelList: 'src/modules/GroupChannelList/index.tsx',
52-
'GroupChannelList/context': 'src/modules/GroupChannelList/context/GroupChannelListProvider.tsx',
53+
'GroupChannelList/context': 'src/modules/GroupChannelList/context/index.tsx',
5354
'GroupChannelList/components/AddGroupChannel': 'src/modules/GroupChannelList/components/AddGroupChannel/index.tsx',
5455
'GroupChannelList/components/GroupChannelListUI': 'src/modules/GroupChannelList/components/GroupChannelListUI/index.tsx',
5556
'GroupChannelList/components/GroupChannelListHeader': 'src/modules/GroupChannelList/components/GroupChannelListHeader/index.tsx',
@@ -58,7 +59,7 @@ export default {
5859

5960
// ChannelSettings
6061
ChannelSettings: 'src/modules/ChannelSettings/index.tsx',
61-
'ChannelSettings/context': 'src/modules/ChannelSettings/context/ChannelSettingsProvider.tsx',
62+
'ChannelSettings/context': 'src/modules/ChannelSettings/context/index.tsx',
6263
'ChannelSettings/hooks/useMenuList': 'src/modules/ChannelSettings/components/ChannelSettingsUI/hooks/useMenuItems.tsx',
6364
'ChannelSettings/components/ChannelProfile': 'src/modules/ChannelSettings/components/ChannelProfile/index.tsx',
6465
'ChannelSettings/components/ChannelSettingsUI': 'src/modules/ChannelSettings/components/ChannelSettingsUI/index.tsx',
@@ -93,7 +94,7 @@ export default {
9394
'Channel/components/SuggestedMentionList': 'src/modules/Channel/components/SuggestedMentionList/index.tsx',
9495

9596
GroupChannel: 'src/modules/GroupChannel/index.tsx',
96-
'GroupChannel/context': 'src/modules/GroupChannel/context/GroupChannelProvider.tsx',
97+
'GroupChannel/context': 'src/modules/GroupChannel/context/index.tsx',
9798
'GroupChannel/components/GroupChannelHeader': 'src/modules/GroupChannel/components/GroupChannelHeader/index.tsx',
9899
'GroupChannel/components/GroupChannelUI': 'src/modules/GroupChannel/components/GroupChannelUI/index.tsx',
99100
'GroupChannel/components/FileViewer': 'src/modules/GroupChannel/components/FileViewer/index.tsx',
@@ -139,7 +140,7 @@ export default {
139140

140141
// MessageSearch
141142
MessageSearch: 'src/modules/MessageSearch/index.tsx',
142-
'MessageSearch/context': 'src/modules/MessageSearch/context/MessageSearchProvider.tsx',
143+
'MessageSearch/context': 'src/modules/MessageSearch/context/index.tsx',
143144
'MessageSearch/components/MessageSearchUI': 'src/modules/MessageSearch/components/MessageSearchUI/index.tsx',
144145

145146
// Message
@@ -148,7 +149,7 @@ export default {
148149

149150
// Thread
150151
Thread: 'src/modules/Thread/index.tsx',
151-
'Thread/context': 'src/modules/Thread/context/ThreadProvider.tsx',
152+
'Thread/context': 'src/modules/Thread/context/index.tsx',
152153
'Thread/context/types': 'src/modules/Thread/types.tsx',
153154
'Thread/components/ThreadUI': 'src/modules/Thread/components/ThreadUI/index.tsx',
154155
'Thread/components/ThreadHeader': 'src/modules/Thread/components/ThreadHeader/index.tsx',
@@ -160,7 +161,7 @@ export default {
160161

161162
// CreateChannel
162163
CreateChannel: 'src/modules/CreateChannel/index.tsx',
163-
'CreateChannel/context': 'src/modules/CreateChannel/context/CreateChannelProvider.tsx',
164+
'CreateChannel/context': 'src/modules/CreateChannel/context/index.tsx',
164165
'CreateChannel/components/CreateChannelUI': 'src/modules/CreateChannel/components/CreateChannelUI/index.tsx',
165166
'CreateChannel/components/InviteUsers': 'src/modules/CreateChannel/components/InviteUsers/index.tsx',
166167
'CreateChannel/components/SelectChannelType': 'src/modules/CreateChannel/components/SelectChannelType.tsx',

src/hooks/VoicePlayer/index.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ import {
1919
VOICE_PLAYER_AUDIO_ID,
2020
VOICE_PLAYER_ROOT_ID,
2121
} from '../../utils/consts';
22-
import useSendbirdStateContext from '../useSendbirdStateContext';
2322
import { getParsedVoiceAudioFileInfo } from './utils';
23+
import useSendbird from '../../lib/Sendbird/context/hooks/useSendbird';
2424

2525
// VoicePlayerProvider interface
2626
export interface VoicePlayerProps {
@@ -64,7 +64,8 @@ export const VoicePlayerProvider = ({
6464
currentPlayer,
6565
audioStorage,
6666
} = voicePlayerStore;
67-
const { config } = useSendbirdStateContext();
67+
const { state } = useSendbird();
68+
const { config } = state;
6869
const { logger } = config;
6970

7071
const stop = (text = '') => {

src/hooks/VoiceRecorder/index.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import {
88
VOICE_MESSAGE_MIME_TYPE,
99
VOICE_RECORDER_AUDIO_BIT_RATE,
1010
} from '../../utils/consts';
11-
import useSendbirdStateContext from '../useSendbirdStateContext';
1211
import { type WebAudioUtils } from './WebAudioUtils';
1312
import { noop } from '../../utils/utils';
13+
import useSendbird from '../../lib/Sendbird/context/hooks/useSendbird';
1414

1515
// Input props of VoiceRecorder
1616
export interface VoiceRecorderProps {
@@ -37,7 +37,8 @@ const Context = createContext<VoiceRecorderContext>({
3737

3838
export const VoiceRecorderProvider = (props: VoiceRecorderProps): React.ReactElement => {
3939
const { children } = props;
40-
const { config } = useSendbirdStateContext();
40+
const { state } = useSendbird();
41+
const { config } = state;
4142
const { logger, groupChannel } = config;
4243
const [mediaRecorder, setMediaRecorder] = useState<MediaRecorder | null>(null);
4344
const [isRecordable, setIsRecordable] = useState<boolean>(false);

src/hooks/VoiceRecorder/useVoiceRecorder.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useCallback, useEffect, useRef, useState } from 'react';
22
import { VoiceRecorderEventHandler, useVoiceRecorderContext } from '.';
3-
import useSendbirdStateContext from '../useSendbirdStateContext';
43
import { noop } from '../../utils/utils';
4+
import useSendbird from '../../lib/Sendbird/context/hooks/useSendbird';
55

66
// export interface UseVoiceRecorderProps extends VoiceRecorderEventHandler {
77
// /**
@@ -31,7 +31,8 @@ export const useVoiceRecorder = ({
3131
onRecordingStarted = noop,
3232
onRecordingEnded = noop,
3333
}: VoiceRecorderEventHandler): UseVoiceRecorderContext => {
34-
const { config } = useSendbirdStateContext();
34+
const { state } = useSendbird();
35+
const { config } = state;
3536
const { voiceRecord } = config;
3637
const maxRecordingTime = voiceRecord.maxRecordingTime;
3738
const voiceRecorder = useVoiceRecorderContext();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { act, renderHook } from '@testing-library/react';
2+
import { useAsyncRequest } from '../useAsyncRequest';
3+
4+
describe('useAsyncRequest', () => {
5+
beforeEach(() => {
6+
jest.clearAllMocks();
7+
});
8+
9+
it('handle request with no response correctly', async () => {
10+
const mockPromise = Promise.resolve();
11+
const mockRequest = jest.fn().mockReturnValue(mockPromise);
12+
13+
const { result } = renderHook(() => useAsyncRequest(mockRequest));
14+
15+
await act(async () => {
16+
await mockPromise;
17+
});
18+
19+
expect(result.current.loading).toBe(false);
20+
});
21+
22+
it('handle request with response correctly', async () => {
23+
const mockResponse = { code: 'ok' };
24+
const mockPromise = Promise.resolve(mockResponse);
25+
const mockRequest = jest.fn().mockReturnValue(mockPromise);
26+
27+
const { result } = renderHook(() => useAsyncRequest(mockRequest));
28+
29+
await act(async () => {
30+
await mockPromise;
31+
});
32+
33+
expect(result.current.response).toBe(mockResponse);
34+
expect(result.current.loading).toBe(false);
35+
});
36+
37+
it('cancel request correctly', async () => {
38+
const mockCancel = jest.fn();
39+
const mockRequest = { cancel: mockCancel };
40+
41+
const { unmount } = renderHook(() => useAsyncRequest(mockRequest));
42+
43+
unmount();
44+
45+
expect(mockCancel).toBeCalled();
46+
});
47+
48+
});
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { renderHook } from '@testing-library/react';
2+
import { useDebounce } from '../useDebounce';
3+
4+
describe('useAsyncRequest', () => {
5+
beforeEach(() => {
6+
jest.clearAllMocks();
7+
});
8+
9+
it('handle useDebounce correctly', async () => {
10+
const mockFunction = jest.fn();
11+
const { result } = renderHook(() => useDebounce(mockFunction, 1000));
12+
13+
const debounceFunction = result.current;
14+
15+
debounceFunction();
16+
debounceFunction();
17+
debounceFunction();
18+
debounceFunction();
19+
debounceFunction();
20+
21+
await new Promise(resolve => {
22+
setTimeout(resolve, 1000);
23+
});
24+
25+
expect(mockFunction).toBeCalledTimes(1);
26+
});
27+
28+
});
+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import React from 'react';
2+
import { renderHook, screen, fireEvent, render, waitFor } from '@testing-library/react';
3+
import useLongPress from '../useLongPress';
4+
5+
describe('useLongPress', () => {
6+
7+
beforeEach(() => {
8+
jest.clearAllMocks();
9+
});
10+
11+
it('handle long press correctly', async () => {
12+
const mockOnLongPress = jest.fn();
13+
const mockOnClick = jest.fn();
14+
15+
const { result } = renderHook(() => useLongPress({
16+
onLongPress: mockOnLongPress,
17+
onClick: mockOnClick,
18+
}));
19+
const { onTouchStart, onTouchEnd } = result.current;
20+
21+
const targetComponent = <div id="target" onTouchStart={onTouchStart} onTouchEnd={onTouchEnd}>touch this</div>;
22+
render(targetComponent);
23+
24+
const element = screen.getByText('touch this');
25+
fireEvent.touchStart(element);
26+
await new Promise(resolve => {
27+
setTimeout(resolve, 1000);
28+
});
29+
fireEvent.touchEnd(element);
30+
31+
await waitFor(() => {
32+
expect(mockOnLongPress).toHaveBeenCalled();
33+
});
34+
});
35+
36+
it('cancel long press if touch is too short', async () => {
37+
const mockOnLongPress = jest.fn();
38+
const mockOnClick = jest.fn();
39+
40+
const { result } = renderHook(() => useLongPress({
41+
onLongPress: mockOnLongPress,
42+
onClick: mockOnClick,
43+
}));
44+
const { onTouchStart, onTouchEnd } = result.current;
45+
46+
const targetComponent = <div id="target" onTouchStart={onTouchStart} onTouchEnd={onTouchEnd}>touch this</div>;
47+
render(targetComponent);
48+
49+
const element = screen.getByText('touch this');
50+
fireEvent.touchStart(element);
51+
await new Promise(resolve => {
52+
setTimeout(resolve, 100);
53+
});
54+
fireEvent.touchEnd(element);
55+
56+
await waitFor(() => {
57+
expect(mockOnClick).toHaveBeenCalled();
58+
expect(mockOnLongPress).not.toHaveBeenCalled();
59+
});
60+
});
61+
62+
});
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import React from 'react';
2+
import { renderHook, screen, fireEvent, render, waitFor } from '@testing-library/react';
3+
import useMouseHover from '../useMouseHover';
4+
5+
describe('useMouseHover', () => {
6+
7+
beforeEach(() => {
8+
jest.clearAllMocks();
9+
});
10+
11+
it('handle mouse over and out correctly', async () => {
12+
const mockSetHover = jest.fn();
13+
14+
const targetComponent = <div id="target">hover</div>;
15+
render(targetComponent);
16+
17+
const hoverElement = screen.getByText('hover');
18+
const ref = {
19+
current: hoverElement,
20+
};
21+
22+
renderHook(() => useMouseHover({
23+
ref,
24+
setHover: mockSetHover,
25+
}));
26+
27+
fireEvent.mouseEnter(hoverElement);
28+
fireEvent.mouseLeave(hoverElement);
29+
30+
await waitFor(() => {
31+
expect(mockSetHover).toHaveBeenCalledTimes(2);
32+
expect(mockSetHover).toHaveBeenCalledWith(true);
33+
expect(mockSetHover).toHaveBeenCalledWith(false);
34+
});
35+
});
36+
37+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import React from 'react';
2+
import { renderHook, screen, fireEvent, render, waitFor } from '@testing-library/react';
3+
import useOutsideAlerter from '../useOutsideAlerter';
4+
5+
describe('useOutsideAlerter', () => {
6+
7+
beforeEach(() => {
8+
jest.clearAllMocks();
9+
});
10+
11+
it('handle click outside correctly', async () => {
12+
const mockClickOutside = jest.fn();
13+
14+
const targetComponent = <div id="target">inside</div>;
15+
render(targetComponent);
16+
17+
const insideElement = screen.getByText('inside');
18+
const ref = {
19+
current: insideElement,
20+
};
21+
22+
renderHook(() => useOutsideAlerter({
23+
ref,
24+
callback: mockClickOutside,
25+
}));
26+
27+
fireEvent.mouseDown(insideElement);
28+
29+
await waitFor(() => {
30+
expect(mockClickOutside).toHaveBeenCalledTimes(1);
31+
});
32+
});
33+
34+
});

0 commit comments

Comments
 (0)