Skip to content

Commit 68eb3b3

Browse files
bang9HoonBaek
andauthored
feat: GroupChannel, GroupChannelList (#912)
Co-authored-by: Baek EunSeo <[email protected]>
1 parent c9cbff1 commit 68eb3b3

File tree

168 files changed

+6239
-3637
lines changed

Some content is hidden

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

168 files changed

+6239
-3637
lines changed

.eslintrc.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
"@typescript-eslint/no-unused-expressions": "error",
5454
"react/display-name": "off",
5555
"react/function-component-definition": "off",
56+
"react/prop-types": "off",
5657
"import/extensions": [
5758
"error",
5859
"ignorePackages",

package.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
},
6969
"dependencies": {
7070
"@sendbird/chat": "^4.10.6",
71-
"@sendbird/uikit-tools": "0.0.1-alpha.57",
71+
"@sendbird/uikit-tools": "0.0.1-alpha.58",
7272
"css-vars-ponyfill": "^2.3.2",
7373
"date-fns": "^2.16.1",
7474
"dompurify": "^3.0.1"
@@ -152,5 +152,10 @@
152152
"defaults",
153153
"IE 11"
154154
],
155-
"packageManager": "[email protected]"
155+
"packageManager": "[email protected]",
156+
"prettier": {
157+
"singleQuote": true,
158+
"printWidth": 140,
159+
"tabWidth": 2
160+
}
156161
}

rollup.module-exports.mjs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,21 +61,23 @@ export default {
6161
'Channel/context': 'src/modules/Channel/context/ChannelProvider.tsx',
6262
'Channel/hooks/useInitialMessagesFetch': 'src/modules/Channel/context/hooks/useInitialMessagesFetch.ts',
6363
'Channel/hooks/useHandleUploadFiles': 'src/modules/Channel/context/hooks/useHandleUploadFiles.tsx',
64-
'Channel/utils/getMessagePartsInfo': 'src/modules/Channel/components/MessageList/getMessagePartsInfo.ts',
65-
'Channel/utils/compareMessagesForGrouping': 'src/modules/Channel/context/compareMessagesForGrouping.ts',
64+
'Channel/utils/getMessagePartsInfo': 'src/modules/GroupChannel/components/MessageList/getMessagePartsInfo.ts', // FIXME: leave for legacy
65+
'Channel/utils/compareMessagesForGrouping': 'src/utils/messages.ts', // TODO: export from global utils path
6666
'Channel/components/ChannelHeader': 'src/modules/Channel/components/ChannelHeader/index.tsx',
6767
'Channel/components/ChannelUI': 'src/modules/Channel/components/ChannelUI/index.tsx',
6868
'Channel/components/FileViewer': 'src/modules/Channel/components/FileViewer/index.tsx',
6969
'Channel/components/FrozenNotification': 'src/modules/Channel/components/FrozenNotification/index.tsx',
7070
'Channel/components/Message': 'src/modules/Channel/components/Message/index.tsx',
71-
'Channel/components/MessageInput': 'src/modules/Channel/components/MessageInput/index.tsx',
71+
'Channel/components/MessageInput': 'src/modules/Channel/components/MessageInputWrapper/index.tsx',
7272
'Channel/components/MessageList': 'src/modules/Channel/components/MessageList/index.tsx',
73-
'Channel/components/RemoveMessageModal': 'src/modules/Channel/components/RemoveMessageModal.tsx',
73+
'Channel/components/RemoveMessageModal': 'src/modules/Channel/components/RemoveMessageModal/index.tsx',
7474
'Channel/components/MessageFeedbackModal': 'src/modules/Channel/components/MessageFeedbackModal/index.tsx',
7575
'Channel/components/TypingIndicator': 'src/modules/Channel/components/TypingIndicator.tsx',
7676
'Channel/components/UnreadCount': 'src/modules/Channel/components/UnreadCount/index.tsx',
7777
'Channel/components/SuggestedMentionList': 'src/modules/Channel/components/SuggestedMentionList/index.tsx',
7878

79+
// TODO: Export components for GroupChannel
80+
7981
// OpenChannel
8082
OpenChannel: 'src/modules/OpenChannel/index.tsx',
8183
'OpenChannel/context': 'src/modules/OpenChannel/context/OpenChannelProvider.tsx',

src/hooks/VoicePlayer/dux/actionTypes.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ObjectValues } from '../../../utils/typeHelpers/objectValues';
22

33
export const actionTypes = {
44
INITIALIZE_AUDIO_UNIT: 'INITIALIZE_AUDIO_UNIT',
5+
RESET_AUDIO_UNIT: 'RESET_AUDIO_UNIT',
56
SET_CURRENT_PLAYER: 'SET_CURRENT_PLAYER',
67
ON_VOICE_PLAYER_PLAY: 'ON_VOICE_PLAYER_PLAY',
78
ON_VOICE_PLAYER_PAUSE: 'ON_VOICE_PLAYER_PAUSE',
@@ -11,6 +12,7 @@ export const actionTypes = {
1112
export type VoicePlayerActionType = ObjectValues<typeof actionTypes>;
1213

1314
export const INITIALIZE_AUDIO_UNIT: VoicePlayerActionType = 'INITIALIZE_AUDIO_UNIT';
15+
export const RESET_AUDIO_UNIT: VoicePlayerActionType = 'RESET_AUDIO_UNIT';
1416
export const SET_CURRENT_PLAYER: VoicePlayerActionType = 'SET_CURRENT_PLAYER';
1517
export const ON_VOICE_PLAYER_PLAY: VoicePlayerActionType = 'ON_VOICE_PLAYER_PLAY';
1618
export const ON_VOICE_PLAYER_PAUSE: VoicePlayerActionType = 'ON_VOICE_PLAYER_PAUSE';

src/hooks/VoicePlayer/dux/reducer.ts

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import {
22
INITIALIZE_AUDIO_UNIT,
3+
RESET_AUDIO_UNIT,
4+
SET_CURRENT_PLAYER,
35
ON_CURRENT_TIME_UPDATE,
46
ON_VOICE_PLAYER_PAUSE,
57
ON_VOICE_PLAYER_PLAY,
6-
SET_CURRENT_PLAYER,
78
} from './actionTypes';
89
import {
910
AudioStorageUnit,
@@ -15,7 +16,7 @@ import {
1516
type InitializeAudioUnitPayload = { groupKey: string };
1617
type SetCurrentPlayerPayload = { audioPlayer: HTMLAudioElement, groupKey: string };
1718
type OnVoicePlayerPlayPayload = { groupKey: string, audioFile: File };
18-
type OnVoicePlayerPausePayload = { groupKey: string };
19+
type OnVoicePlayerPausePayload = { groupKey: string, duration: number, currentTime: number };
1920
type OnCurrentTimeUpdatePayload = { groupKey: string };
2021
type PayloadType = (
2122
InitializeAudioUnitPayload
@@ -46,6 +47,16 @@ export default function voicePlayerReducer(
4647
},
4748
};
4849
}
50+
case RESET_AUDIO_UNIT: {
51+
const { groupKey } = action.payload;
52+
return {
53+
...state,
54+
audioStorage: {
55+
...state.audioStorage,
56+
[groupKey]: AudioUnitDefaultValue(),
57+
},
58+
};
59+
}
4960
case SET_CURRENT_PLAYER: {
5061
const { audioPlayer, groupKey } = action.payload as SetCurrentPlayerPayload;
5162
return {
@@ -68,15 +79,11 @@ export default function voicePlayerReducer(
6879
};
6980
}
7081
case ON_VOICE_PLAYER_PAUSE: {
71-
const { groupKey } = action.payload as OnVoicePlayerPausePayload;
82+
const { groupKey, duration, currentTime } = action.payload as OnVoicePlayerPausePayload;
7283
const audioUnit = (state.audioStorage?.[groupKey] ? state.audioStorage[groupKey] : AudioUnitDefaultValue()) as AudioStorageUnit;
7384
audioUnit.playingStatus = VOICE_PLAYER_STATUS.PAUSED;
74-
const { currentTime, duration } = state.currentPlayer as HTMLAudioElement;
75-
if (audioUnit.playbackTime === audioUnit.duration) {
85+
if (duration === currentTime) {
7686
audioUnit.playbackTime = 0;
77-
} else if (currentTime > 0 && duration > 0) {
78-
audioUnit.playbackTime = currentTime;
79-
audioUnit.duration = duration;
8087
}
8188
return {
8289
...state,

src/hooks/VoicePlayer/index.tsx

Lines changed: 68 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
ON_CURRENT_TIME_UPDATE,
1212
ON_VOICE_PLAYER_PAUSE,
1313
ON_VOICE_PLAYER_PLAY,
14+
RESET_AUDIO_UNIT,
1415
SET_CURRENT_PLAYER,
1516
} from './dux/actionTypes';
1617
import {
@@ -72,7 +73,7 @@ export const VoicePlayerProvider = ({
7273
}
7374
};
7475

75-
const pause = (groupKey: string|null) => {
76+
const pause = (groupKey: string | null) => {
7677
if (currentGroupKey === groupKey && currentPlayer !== null) {
7778
logger.info('VoicePlayer: Pause playing(by group key).');
7879
currentPlayer?.pause();
@@ -100,7 +101,7 @@ export const VoicePlayerProvider = ({
100101
}
101102

102103
logger.info('VoicePlayer: Start getting audio file.');
103-
new Promise<File>((resolve) => {
104+
new Promise<File>((resolve, reject) => {
104105
voicePlayerDispatcher({
105106
type: INITIALIZE_AUDIO_UNIT,
106107
payload: { groupKey },
@@ -128,62 +129,74 @@ export const VoicePlayerProvider = ({
128129
});
129130
resolve(audioFile);
130131
logger.info('VoicePlayer: Get the audioFile from URL.');
131-
});
132-
}).then((audioFile: File) => {
133-
const voicePlayerRoot = document.getElementById(VOICE_PLAYER_ROOT_ID);
134-
logger.info('VoicePlayer: Succeeded getting audio file.', { audioFile });
135-
const currentAudioUnit = audioStorage[groupKey] || AudioUnitDefaultValue() as AudioStorageUnit;
136-
const audioPlayer = new Audio(URL?.createObjectURL?.(audioFile));
137-
audioPlayer.id = VOICE_PLAYER_AUDIO_ID;
138-
audioPlayer.currentTime = currentAudioUnit.playbackTime;
139-
audioPlayer.volume = 1;
140-
audioPlayer.loop = false;
141-
audioPlayer.onplaying = () => {
142-
logger.info('VoicePlayer: OnPlaying event is called from audioPlayer', { groupKey, audioPlayer });
143-
voicePlayerDispatcher({
144-
type: ON_VOICE_PLAYER_PLAY,
145-
payload: { groupKey, audioFile },
146-
});
147-
};
148-
audioPlayer.onpause = () => {
149-
logger.info('VoicePlayer: OnPause event is called from audioPlayer', { groupKey, audioPlayer });
150-
voicePlayerDispatcher({
151-
type: ON_VOICE_PLAYER_PAUSE,
152-
payload: { groupKey },
153-
});
154-
};
155-
audioPlayer.ontimeupdate = () => {
132+
})
133+
.catch(reject);
134+
})
135+
.then((audioFile: File) => {
136+
const voicePlayerRoot = document.getElementById(VOICE_PLAYER_ROOT_ID);
137+
logger.info('VoicePlayer: Succeeded getting audio file.', { audioFile });
138+
const currentAudioUnit = audioStorage[groupKey] || AudioUnitDefaultValue() as AudioStorageUnit;
139+
const audioPlayer = new Audio(URL?.createObjectURL?.(audioFile));
140+
audioPlayer.id = VOICE_PLAYER_AUDIO_ID;
141+
audioPlayer.currentTime = currentAudioUnit.playbackTime;
142+
audioPlayer.volume = 1;
143+
audioPlayer.loop = false;
144+
audioPlayer.onplaying = () => {
145+
logger.info('VoicePlayer: OnPlaying event is called from audioPlayer', { groupKey, audioPlayer });
146+
voicePlayerDispatcher({
147+
type: ON_VOICE_PLAYER_PLAY,
148+
payload: { groupKey, audioFile },
149+
});
150+
};
151+
audioPlayer.onpause = () => {
152+
logger.info('VoicePlayer: OnPause event is called from audioPlayer', { groupKey, audioPlayer });
153+
voicePlayerDispatcher({
154+
type: ON_VOICE_PLAYER_PAUSE,
155+
payload: { groupKey, duration: audioPlayer.duration, currentTime: audioPlayer.currentTime },
156+
});
157+
};
158+
audioPlayer.ontimeupdate = () => {
159+
voicePlayerDispatcher({
160+
type: ON_CURRENT_TIME_UPDATE,
161+
payload: { groupKey },
162+
});
163+
};
164+
audioPlayer.onerror = (error) => {
165+
logger.error('VoicePlayer: Failed to load the audioFile on the audio player.', error);
166+
voicePlayerDispatcher({
167+
type: RESET_AUDIO_UNIT,
168+
payload: { groupKey },
169+
});
170+
};
171+
audioPlayer.dataset.sbGroupId = groupKey;
172+
// clean up the previous audio player
173+
try {
174+
voicePlayerRoot?.childNodes.forEach((node) => {
175+
const element = node as HTMLAudioElement;
176+
const thisGroupKey = element.dataset?.sbGroupKey;
177+
if (thisGroupKey !== groupKey) {
178+
element?.pause?.();
179+
voicePlayerRoot.removeChild(element);
180+
logger.info('VoicePlayer: Removed other player.', { element });
181+
}
182+
});
183+
} finally {
184+
audioPlayer?.play();
185+
voicePlayerRoot?.appendChild(audioPlayer);
186+
voicePlayerDispatcher({
187+
type: SET_CURRENT_PLAYER,
188+
payload: { groupKey, audioPlayer },
189+
});
190+
logger.info('VoicePlayer: Succeeded playing audio player.', { groupKey, audioPlayer });
191+
}
192+
})
193+
.catch((error) => {
194+
logger.warning('VoicePlayer: Failed loading audio file with URL.', error);
156195
voicePlayerDispatcher({
157-
type: ON_CURRENT_TIME_UPDATE,
196+
type: RESET_AUDIO_UNIT,
158197
payload: { groupKey },
159198
});
160-
};
161-
audioPlayer.dataset.sbGroupId = groupKey;
162-
// clean up the previous audio player
163-
try {
164-
voicePlayerRoot?.childNodes.forEach((node) => {
165-
const element = node as HTMLAudioElement;
166-
const thisGroupKey = element.dataset?.sbGroupKey;
167-
if (thisGroupKey !== groupKey) {
168-
element?.pause?.();
169-
voicePlayerDispatcher({
170-
type: ON_VOICE_PLAYER_PAUSE,
171-
payload: { groupKey },
172-
});
173-
voicePlayerRoot.removeChild(element);
174-
logger.info('VoicePlayer: Removed other player.', { element });
175-
}
176-
});
177-
} finally {
178-
audioPlayer?.play();
179-
voicePlayerRoot?.appendChild(audioPlayer);
180-
voicePlayerDispatcher({
181-
type: SET_CURRENT_PLAYER,
182-
payload: { groupKey, audioPlayer },
183-
});
184-
logger.info('VoicePlayer: Succeeded playing audio player.', { groupKey, audioPlayer });
185-
}
186-
});
199+
});
187200
};
188201

189202
return (

src/hooks/VoiceRecorder/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,9 @@ export const VoiceRecorderProvider = (props: VoiceRecorderProps): React.ReactEle
112112
stream?.getAudioTracks?.().forEach?.(track => track?.stop());
113113
setIsRecordable(false);
114114
};
115+
mediaRecorder.onstart = eventHandler?.onRecordingStarted ?? noop;
115116
mediaRecorder?.start();
116117
setMediaRecorder(mediaRecorder);
117-
eventHandler?.onRecordingStarted();
118118
})
119119
.catch((err) => {
120120
logger.error('VoiceRecorder: Failed getting media stream.', err);

src/hooks/useOnScrollReachedEndDetector/__tests__/useHandleOnScrollCallback.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import React from 'react';
22
import { renderHook } from '@testing-library/react';
3-
import { useOnScrollPositionChangeDetector, UseOnScrollReachedEndDetectorProps } from '../index';
3+
import { useOnScrollPositionChangeDetector, UseOnScrollReachedEndDetectorParams } from '../index';
44
import { SCROLL_BUFFER } from '../../../utils/consts';
55

66
jest.useFakeTimers();
77

88
const SAFE_DELAY = 550;
99

10-
const prepareMockParams = (): UseOnScrollReachedEndDetectorProps => {
10+
const prepareMockParams = (): UseOnScrollReachedEndDetectorParams => {
1111
return {
1212
onReachedTop: jest.fn(),
1313
onReachedBottom: jest.fn(),

0 commit comments

Comments
 (0)