Skip to content

Commit

Permalink
[CLNP-4657] Provider migration - P0 items (#1269)
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
5 people authored Feb 7, 2025
1 parent 79bfe84 commit 9be7a49
Show file tree
Hide file tree
Showing 341 changed files with 14,805 additions and 5,321 deletions.
6 changes: 3 additions & 3 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import type { Preview } from '@storybook/react';
import { SendbirdSdkContext } from '../src/lib/SendbirdSdkContext';
import { SendbirdContext } from '../src/lib/Sendbird/context/SendbirdContext';

import '../src/lib/index.scss';
import './index.css';
Expand Down Expand Up @@ -28,9 +28,9 @@ const preview: Preview = {
decorators: [
(Story) => (
<div className="sendbird-theme--light">
<SendbirdSdkContext.Provider value={{} as any}>
<SendbirdContext.Provider value={{} as any}>
{Story()}
</SendbirdSdkContext.Provider>
</SendbirdContext.Provider>
</div>
),
],
Expand Down
2 changes: 1 addition & 1 deletion apps/testing/src/utils/paramsBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { UIKitOptions } from '../../../../src/lib/types.ts';
import { UIKitOptions } from '../../../../src/lib/Sendbird/types';
import { useSearchParams } from 'react-router-dom';

export interface InitialParams {
Expand Down
5 changes: 5 additions & 0 deletions apps/testing/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ import postcssRtlOptions from '../../postcssRtlOptions.mjs';
export default defineConfig({
plugins: [react(), vitePluginSvgr({ include: '**/*.svg' })],
css: {
preprocessorOptions: {
scss: {
silenceDeprecations: ['legacy-js-api'],
},
},
postcss: {
plugins: [postcssRtl(postcssRtlOptions)],
},
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
"@testing-library/react": "^16.2.0",
"@testing-library/user-event": "^14.4.3",
"@types/node": "^22.7.2",
"@types/use-sync-external-store": "^0.0.6",
"@typescript-eslint/eslint-plugin": "^6.17.0",
"@typescript-eslint/parser": "^6.17.0",
"autoprefixer": "^9.7.4",
Expand Down Expand Up @@ -148,6 +149,7 @@
"ts-pattern": "^4.2.2",
"typedoc": "^0.25.13",
"typescript": "^5.4.5",
"use-sync-external-store": "^1.2.2",
"vite": "^5.1.5",
"vite-plugin-svgr": "^4.2.0"
},
Expand Down
19 changes: 10 additions & 9 deletions rollup.module-exports.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ export default {
App: 'src/modules/App/index.tsx',

// SendbirdProvider
SendbirdProvider: 'src/lib/Sendbird.tsx',
SendbirdProvider: 'src/lib/Sendbird/index.tsx',
sendbirdSelectors: 'src/lib/selectors.ts',
useSendbirdStateContext: 'src/hooks/useSendbirdStateContext.tsx',
withSendbird: 'src/lib/SendbirdSdkContext.tsx',
// TODO: Support below legacy exports
// useSendbirdStateContext: 'src/hooks/useSendbirdStateContext.tsx',
// withSendbird: 'src/lib/SendbirdSdkContext.tsx',

// Voice message
'VoiceRecorder/context': 'src/hooks/VoiceRecorder/index.tsx',
Expand Down Expand Up @@ -49,7 +50,7 @@ export default {

// GroupChannelList
GroupChannelList: 'src/modules/GroupChannelList/index.tsx',
'GroupChannelList/context': 'src/modules/GroupChannelList/context/GroupChannelListProvider.tsx',
'GroupChannelList/context': 'src/modules/GroupChannelList/context/index.tsx',
'GroupChannelList/components/AddGroupChannel': 'src/modules/GroupChannelList/components/AddGroupChannel/index.tsx',
'GroupChannelList/components/GroupChannelListUI': 'src/modules/GroupChannelList/components/GroupChannelListUI/index.tsx',
'GroupChannelList/components/GroupChannelListHeader': 'src/modules/GroupChannelList/components/GroupChannelListHeader/index.tsx',
Expand All @@ -58,7 +59,7 @@ export default {

// ChannelSettings
ChannelSettings: 'src/modules/ChannelSettings/index.tsx',
'ChannelSettings/context': 'src/modules/ChannelSettings/context/ChannelSettingsProvider.tsx',
'ChannelSettings/context': 'src/modules/ChannelSettings/context/index.tsx',
'ChannelSettings/hooks/useMenuList': 'src/modules/ChannelSettings/components/ChannelSettingsUI/hooks/useMenuItems.tsx',
'ChannelSettings/components/ChannelProfile': 'src/modules/ChannelSettings/components/ChannelProfile/index.tsx',
'ChannelSettings/components/ChannelSettingsUI': 'src/modules/ChannelSettings/components/ChannelSettingsUI/index.tsx',
Expand Down Expand Up @@ -93,7 +94,7 @@ export default {
'Channel/components/SuggestedMentionList': 'src/modules/Channel/components/SuggestedMentionList/index.tsx',

GroupChannel: 'src/modules/GroupChannel/index.tsx',
'GroupChannel/context': 'src/modules/GroupChannel/context/GroupChannelProvider.tsx',
'GroupChannel/context': 'src/modules/GroupChannel/context/index.tsx',
'GroupChannel/components/GroupChannelHeader': 'src/modules/GroupChannel/components/GroupChannelHeader/index.tsx',
'GroupChannel/components/GroupChannelUI': 'src/modules/GroupChannel/components/GroupChannelUI/index.tsx',
'GroupChannel/components/FileViewer': 'src/modules/GroupChannel/components/FileViewer/index.tsx',
Expand Down Expand Up @@ -139,7 +140,7 @@ export default {

// MessageSearch
MessageSearch: 'src/modules/MessageSearch/index.tsx',
'MessageSearch/context': 'src/modules/MessageSearch/context/MessageSearchProvider.tsx',
'MessageSearch/context': 'src/modules/MessageSearch/context/index.tsx',
'MessageSearch/components/MessageSearchUI': 'src/modules/MessageSearch/components/MessageSearchUI/index.tsx',

// Message
Expand All @@ -148,7 +149,7 @@ export default {

// Thread
Thread: 'src/modules/Thread/index.tsx',
'Thread/context': 'src/modules/Thread/context/ThreadProvider.tsx',
'Thread/context': 'src/modules/Thread/context/index.tsx',
'Thread/context/types': 'src/modules/Thread/types.tsx',
'Thread/components/ThreadUI': 'src/modules/Thread/components/ThreadUI/index.tsx',
'Thread/components/ThreadHeader': 'src/modules/Thread/components/ThreadHeader/index.tsx',
Expand All @@ -160,7 +161,7 @@ export default {

// CreateChannel
CreateChannel: 'src/modules/CreateChannel/index.tsx',
'CreateChannel/context': 'src/modules/CreateChannel/context/CreateChannelProvider.tsx',
'CreateChannel/context': 'src/modules/CreateChannel/context/index.tsx',
'CreateChannel/components/CreateChannelUI': 'src/modules/CreateChannel/components/CreateChannelUI/index.tsx',
'CreateChannel/components/InviteUsers': 'src/modules/CreateChannel/components/InviteUsers/index.tsx',
'CreateChannel/components/SelectChannelType': 'src/modules/CreateChannel/components/SelectChannelType.tsx',
Expand Down
5 changes: 3 additions & 2 deletions src/hooks/VoicePlayer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import {
VOICE_PLAYER_AUDIO_ID,
VOICE_PLAYER_ROOT_ID,
} from '../../utils/consts';
import useSendbirdStateContext from '../useSendbirdStateContext';
import { getParsedVoiceAudioFileInfo } from './utils';
import useSendbird from '../../lib/Sendbird/context/hooks/useSendbird';

// VoicePlayerProvider interface
export interface VoicePlayerProps {
Expand Down Expand Up @@ -64,7 +64,8 @@ export const VoicePlayerProvider = ({
currentPlayer,
audioStorage,
} = voicePlayerStore;
const { config } = useSendbirdStateContext();
const { state } = useSendbird();
const { config } = state;
const { logger } = config;

const stop = (text = '') => {
Expand Down
5 changes: 3 additions & 2 deletions src/hooks/VoiceRecorder/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import {
VOICE_MESSAGE_MIME_TYPE,
VOICE_RECORDER_AUDIO_BIT_RATE,
} from '../../utils/consts';
import useSendbirdStateContext from '../useSendbirdStateContext';
import { type WebAudioUtils } from './WebAudioUtils';
import { noop } from '../../utils/utils';
import useSendbird from '../../lib/Sendbird/context/hooks/useSendbird';

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

export const VoiceRecorderProvider = (props: VoiceRecorderProps): React.ReactElement => {
const { children } = props;
const { config } = useSendbirdStateContext();
const { state } = useSendbird();
const { config } = state;
const { logger, groupChannel } = config;
const [mediaRecorder, setMediaRecorder] = useState<MediaRecorder | null>(null);
const [isRecordable, setIsRecordable] = useState<boolean>(false);
Expand Down
5 changes: 3 additions & 2 deletions src/hooks/VoiceRecorder/useVoiceRecorder.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import { VoiceRecorderEventHandler, useVoiceRecorderContext } from '.';
import useSendbirdStateContext from '../useSendbirdStateContext';
import { noop } from '../../utils/utils';
import useSendbird from '../../lib/Sendbird/context/hooks/useSendbird';

// export interface UseVoiceRecorderProps extends VoiceRecorderEventHandler {
// /**
Expand Down Expand Up @@ -31,7 +31,8 @@ export const useVoiceRecorder = ({
onRecordingStarted = noop,
onRecordingEnded = noop,
}: VoiceRecorderEventHandler): UseVoiceRecorderContext => {
const { config } = useSendbirdStateContext();
const { state } = useSendbird();
const { config } = state;
const { voiceRecord } = config;
const maxRecordingTime = voiceRecord.maxRecordingTime;
const voiceRecorder = useVoiceRecorderContext();
Expand Down
48 changes: 48 additions & 0 deletions src/hooks/__tests__/useAsyncRequest.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { act, renderHook } from '@testing-library/react';
import { useAsyncRequest } from '../useAsyncRequest';

describe('useAsyncRequest', () => {
beforeEach(() => {
jest.clearAllMocks();
});

it('handle request with no response correctly', async () => {
const mockPromise = Promise.resolve();
const mockRequest = jest.fn().mockReturnValue(mockPromise);

const { result } = renderHook(() => useAsyncRequest(mockRequest));

await act(async () => {
await mockPromise;
});

expect(result.current.loading).toBe(false);
});

it('handle request with response correctly', async () => {
const mockResponse = { code: 'ok' };
const mockPromise = Promise.resolve(mockResponse);
const mockRequest = jest.fn().mockReturnValue(mockPromise);

const { result } = renderHook(() => useAsyncRequest(mockRequest));

await act(async () => {
await mockPromise;
});

expect(result.current.response).toBe(mockResponse);
expect(result.current.loading).toBe(false);
});

it('cancel request correctly', async () => {
const mockCancel = jest.fn();
const mockRequest = { cancel: mockCancel };

const { unmount } = renderHook(() => useAsyncRequest(mockRequest));

unmount();

expect(mockCancel).toBeCalled();
});

});
28 changes: 28 additions & 0 deletions src/hooks/__tests__/useDebounce.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { renderHook } from '@testing-library/react';
import { useDebounce } from '../useDebounce';

describe('useAsyncRequest', () => {
beforeEach(() => {
jest.clearAllMocks();
});

it('handle useDebounce correctly', async () => {
const mockFunction = jest.fn();
const { result } = renderHook(() => useDebounce(mockFunction, 1000));

const debounceFunction = result.current;

debounceFunction();
debounceFunction();
debounceFunction();
debounceFunction();
debounceFunction();

await new Promise(resolve => {
setTimeout(resolve, 1000);
});

expect(mockFunction).toBeCalledTimes(1);
});

});
62 changes: 62 additions & 0 deletions src/hooks/__tests__/useLongPress.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from 'react';
import { renderHook, screen, fireEvent, render, waitFor } from '@testing-library/react';
import useLongPress from '../useLongPress';

describe('useLongPress', () => {

beforeEach(() => {
jest.clearAllMocks();
});

it('handle long press correctly', async () => {
const mockOnLongPress = jest.fn();
const mockOnClick = jest.fn();

const { result } = renderHook(() => useLongPress({
onLongPress: mockOnLongPress,
onClick: mockOnClick,
}));
const { onTouchStart, onTouchEnd } = result.current;

const targetComponent = <div id="target" onTouchStart={onTouchStart} onTouchEnd={onTouchEnd}>touch this</div>;
render(targetComponent);

const element = screen.getByText('touch this');
fireEvent.touchStart(element);
await new Promise(resolve => {
setTimeout(resolve, 1000);
});
fireEvent.touchEnd(element);

await waitFor(() => {
expect(mockOnLongPress).toHaveBeenCalled();
});
});

it('cancel long press if touch is too short', async () => {
const mockOnLongPress = jest.fn();
const mockOnClick = jest.fn();

const { result } = renderHook(() => useLongPress({
onLongPress: mockOnLongPress,
onClick: mockOnClick,
}));
const { onTouchStart, onTouchEnd } = result.current;

const targetComponent = <div id="target" onTouchStart={onTouchStart} onTouchEnd={onTouchEnd}>touch this</div>;
render(targetComponent);

const element = screen.getByText('touch this');
fireEvent.touchStart(element);
await new Promise(resolve => {
setTimeout(resolve, 100);
});
fireEvent.touchEnd(element);

await waitFor(() => {
expect(mockOnClick).toHaveBeenCalled();
expect(mockOnLongPress).not.toHaveBeenCalled();
});
});

});
37 changes: 37 additions & 0 deletions src/hooks/__tests__/useMouseHover.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react';
import { renderHook, screen, fireEvent, render, waitFor } from '@testing-library/react';
import useMouseHover from '../useMouseHover';

describe('useMouseHover', () => {

beforeEach(() => {
jest.clearAllMocks();
});

it('handle mouse over and out correctly', async () => {
const mockSetHover = jest.fn();

const targetComponent = <div id="target">hover</div>;
render(targetComponent);

const hoverElement = screen.getByText('hover');
const ref = {
current: hoverElement,
};

renderHook(() => useMouseHover({
ref,
setHover: mockSetHover,
}));

fireEvent.mouseEnter(hoverElement);
fireEvent.mouseLeave(hoverElement);

await waitFor(() => {
expect(mockSetHover).toHaveBeenCalledTimes(2);
expect(mockSetHover).toHaveBeenCalledWith(true);
expect(mockSetHover).toHaveBeenCalledWith(false);
});
});

});
34 changes: 34 additions & 0 deletions src/hooks/__tests__/useOutsideAlerter.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';
import { renderHook, screen, fireEvent, render, waitFor } from '@testing-library/react';
import useOutsideAlerter from '../useOutsideAlerter';

describe('useOutsideAlerter', () => {

beforeEach(() => {
jest.clearAllMocks();
});

it('handle click outside correctly', async () => {
const mockClickOutside = jest.fn();

const targetComponent = <div id="target">inside</div>;
render(targetComponent);

const insideElement = screen.getByText('inside');
const ref = {
current: insideElement,
};

renderHook(() => useOutsideAlerter({
ref,
callback: mockClickOutside,
}));

fireEvent.mouseDown(insideElement);

await waitFor(() => {
expect(mockClickOutside).toHaveBeenCalledTimes(1);
});
});

});
Loading

0 comments on commit 9be7a49

Please sign in to comment.