Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
4669472
Update tsconfig.json
chrisallo Mar 6, 2024
0ddd198
fix: Fixed type error. (#1011)
OnestarLee Mar 18, 2024
5a75729
fix: Fixed type error. (#1024)
OnestarLee Mar 25, 2024
054f88c
fix: Fixed type error. (#1037)
OnestarLee Apr 5, 2024
34e052c
chore:sub tsconfig (#1046)
OnestarLee Apr 7, 2024
45d5413
fix: Fixed type error. (#1043)
OnestarLee Apr 9, 2024
cc06d18
typescript error fix
chrisallo Apr 15, 2024
e41b93a
rebase
chrisallo Apr 16, 2024
ecd9a96
Merge branch 'main' into feat/clnp-2908-ts-fix-hooks
chrisallo Apr 29, 2024
1b7dea1
review fix
chrisallo Apr 29, 2024
d822829
restore stash
chrisallo Apr 29, 2024
903183d
Update yarn.lock
chrisallo Apr 29, 2024
6ab0bab
default values
chrisallo Apr 30, 2024
671cccc
Merge branch 'main' into feat/clnp-2908-ts-fix-hooks
bang9 Apr 30, 2024
2392997
chore: test fix
bang9 Apr 30, 2024
1307b2a
build error fix
chrisallo May 1, 2024
7ff7dd9
chore: remove package lock file
bang9 May 2, 2024
e8bb603
fix: groupKey should be update with channelUrl and key
bang9 May 2, 2024
002d1e9
type fixes to build
chrisallo May 2, 2024
295f010
Merge branch 'feat/clnp-2908-ts-fix-hooks' of https://github.com/send…
chrisallo May 2, 2024
c4704b9
unset ts option
chrisallo May 2, 2024
4a44a36
lint fix
chrisallo May 2, 2024
9192efd
Merge branch 'main' into feat/clnp-2908-ts-fix-hooks
chrisallo May 2, 2024
48ca3a4
Merge branch 'main' into feat/clnp-2908-ts-fix-hooks
chrisallo May 2, 2024
d8f86d1
chore: update hooks/VoiceRecorder
bang9 May 2, 2024
49b73d1
chore: remove legacy stories
bang9 May 2, 2024
44781c0
chore: voiceConfig is not optional type
bang9 May 2, 2024
6f73ee0
chore: throttle trailingArgs should be nullable
bang9 May 2, 2024
aa0f702
chore: remove redundant type
bang9 May 2, 2024
c75c449
chore: remove null types from Sendbird.tsx
bang9 May 2, 2024
a4ec245
chore: update Sendbird
bang9 May 2, 2024
543673c
chore: lib/emojiManager
bang9 May 2, 2024
828c694
chore: update src/lib
bang9 May 2, 2024
6feb712
chore: src/modules/App
bang9 May 2, 2024
272f08b
chore: update src/modules/App
bang9 May 2, 2024
643d80f
chore: update
bang9 May 2, 2024
c72ec27
chore: update src/modules/Channel
bang9 May 2, 2024
850d5ac
chore: update modules/Channel
bang9 May 2, 2024
f982e5d
chore: update modules/ChannelList
bang9 May 2, 2024
00cf9e1
chore: build fix
bang9 May 2, 2024
8b61cfd
chore: update modules/ChannelSettings
bang9 May 2, 2024
58e0dfc
chore: update modules/ChannelSettings
bang9 May 2, 2024
d27a359
chore: update channel settings
bang9 May 2, 2024
baf96bc
chore: update channel settings
bang9 May 2, 2024
34c5bf7
chore: update channel settings
bang9 May 2, 2024
b039d90
chore: update create channel
bang9 May 2, 2024
2d946be
chore: update edit user profile
bang9 May 2, 2024
5ab6f75
chore: update
bang9 May 2, 2024
a962d05
chore: update
bang9 May 2, 2024
eaace78
chore: update group channel
bang9 May 2, 2024
6284d0a
chore: update message search
bang9 May 7, 2024
e19ac28
chore: update open channel
bang9 May 7, 2024
6df5201
chore: update open channel list
bang9 May 7, 2024
7b4eae3
context menu fix
chrisallo May 9, 2024
c6e49b8
Merge branch 'main' into feat/clnp-2908-ts-fix-hooks
chrisallo May 9, 2024
b526917
revert icon/type fix
chrisallo May 9, 2024
24d9d49
chore: update
bang9 May 9, 2024
70058b0
chore: update
bang9 May 9, 2024
730b4ed
chore: update
bang9 May 9, 2024
f4542f8
chore: fix test
bang9 May 9, 2024
fe86811
chore: update
bang9 May 9, 2024
72b2fba
Merge branch 'feat/clnp-2908-ts-fix-hooks' of https://github.com/send…
chrisallo May 9, 2024
824e15b
ts fix + improvement
chrisallo May 9, 2024
a12508f
code alignment
chrisallo May 9, 2024
a77bcdb
Update MessageContent.spec.js.snap
chrisallo May 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 13 additions & 10 deletions src/hooks/VoicePlayer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,20 +73,23 @@ export const VoicePlayerProvider = ({
}
};

const pause = (groupKey: string | null) => {
if (currentGroupKey === groupKey && currentPlayer !== null) {
logger.info('VoicePlayer: Pause playing(by group key).');
currentPlayer?.pause();
}
if (groupKey === ALL) {
logger.info('VoicePlayer: Pause playing(all).');
currentPlayer?.pause();
const pause = (groupKey?: string) => {
if (currentPlayer) {
if (groupKey === currentGroupKey) {
logger.info('VoicePlayer: Pause playing(by group key).');
currentPlayer.pause();
} else if (groupKey === ALL) {
logger.info('VoicePlayer: Pause playing(all).');
currentPlayer.pause();
}
} else {
logger.warning('VoicePlayer: No currentPlayer to pause.');
}
};

const play = ({
groupKey,
audioFile = null,
audioFile,
audioFileUrl = '',
}: VoicePlayerPlayProps): void => {
if (groupKey !== currentGroupKey) {
Expand All @@ -96,7 +99,7 @@ export const VoicePlayerProvider = ({
// Clear the previous AudioPlayer element
const voicePlayerRoot = document.getElementById(VOICE_PLAYER_ROOT_ID);
const voicePlayerAudioElement = document.getElementById(VOICE_PLAYER_AUDIO_ID);
if (voicePlayerAudioElement) {
if (voicePlayerRoot && voicePlayerAudioElement) {
voicePlayerRoot.removeChild(voicePlayerAudioElement);
}

Expand Down
10 changes: 5 additions & 5 deletions src/hooks/VoicePlayer/useVoicePlayer.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useState } from 'react';
import { useEffect } from 'react';
import { useVoicePlayerContext } from '.';
import { VOICE_PLAYER_AUDIO_ID } from '../../utils/consts';
import { useVoiceRecorderContext } from '../VoiceRecorder';
Expand All @@ -7,8 +7,8 @@ import { AudioUnitDefaultValue, VoicePlayerStatusType } from './dux/initialState
import { generateGroupKey } from './utils';

export interface UseVoicePlayerProps {
key: string;
channelUrl: string;
key?: string;
channelUrl?: string;
audioFile?: File;
audioFileUrl?: string;
}
Expand All @@ -25,10 +25,10 @@ export interface UseVoicePlayerContext {
export const useVoicePlayer = ({
key = '',
channelUrl = '',
audioFile = null,
audioFile,
audioFileUrl = '',
}: UseVoicePlayerProps): UseVoicePlayerContext => {
const [groupKey] = useState<string>(generateGroupKey(channelUrl, key));
const groupKey = generateGroupKey(channelUrl, key);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: state 로 관리하면 url,key 업데이트를 해주는 로직이 필요, 일반 string 사용하도록 수정

const {
play,
pause,
Expand Down
32 changes: 21 additions & 11 deletions src/hooks/VoiceRecorder/WebAudioUtils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// Thanks to https://codesandbox.io/s/media-recorder-api-downsampling-16k-mp3-encode-using-lame-js-forked-n1pblw
import { VOICE_RECORDER_AUDIO_SAMPLE_RATE } from '../../utils/consts';
// @ts-ignore
import { WavHeader, Mp3Encoder } from '../../_externals/lamejs/lame.all';

function encodeMp3(arrayBuffer: ArrayBuffer): WavHeader {
function encodeMp3(arrayBuffer: ArrayBuffer) {
const wav = WavHeader.readHeader(new DataView(arrayBuffer));
const dataView = new Int16Array(arrayBuffer, wav.dataOffset, wav.dataLen / 2);
const mp3Encoder = new Mp3Encoder(wav.channels, wav.sampleRate, 128);
Expand All @@ -14,11 +15,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);
Expand All @@ -44,7 +47,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);
Expand All @@ -55,8 +58,8 @@ function downsampleToWav(file: File, callback: (buffer: ArrayBuffer) => void): v

const reader = new FileReader();
reader.onload = function () {
const renderCompleteHandler = (evt): void => {
const renderedBuffer = usingWebkit ? evt.renderedBuffer : evt;
const renderCompleteHandler = (evt: OfflineAudioCompletionEvent | AudioBuffer) => {
const renderedBuffer = usingWebkit ? (evt as OfflineAudioCompletionEvent).renderedBuffer : (evt as AudioBuffer);
const buffer = bufferToWav(renderedBuffer, renderedBuffer.length);
if (callback) {
callback(buffer);
Expand All @@ -80,12 +83,12 @@ function downsampleToWav(file: File, callback: (buffer: ArrayBuffer) => void): v
fileReader.readAsArrayBuffer(file);
}

function bufferToWav(abuffer, len) {
function bufferToWav(abuffer: AudioBuffer, len: number) {
const numOfChan = abuffer.numberOfChannels;
const length = len * numOfChan * 2 + 44;
const buffer = new ArrayBuffer(length);
const view = new DataView(buffer);
const channels = [];
const channels: any[] = [];
let i = 0;
let sample;
let offset = 0;
Expand All @@ -105,9 +108,11 @@ function bufferToWav(abuffer, len) {
setUint16(16); // 16-bit (hardcoded in this demo)
setUint32(0x61746164); // "data" - chunk
setUint32(length - pos - 4); // chunk length

// write interleaved data
for (i = 0; i < abuffer.numberOfChannels; i++)
for (i = 0; i < abuffer.numberOfChannels; i++) {
channels.push(abuffer.getChannelData(i));
}

while (pos < length) {
for (i = 0; i < numOfChan; i++) {
Expand All @@ -122,15 +127,20 @@ function bufferToWav(abuffer, len) {

return buffer;

function setUint16(data) {
function setUint16(data: number) {
view.setUint16(pos, data, true);
pos += 2;
}

function setUint32(data) {
function setUint32(data: number) {
view.setUint32(pos, data, true);
pos += 4;
}
}

export interface WebAudioUtils {
downsampleToWav: typeof downsampleToWav;
encodeMp3: typeof encodeMp3;
}

export { downsampleToWav, encodeMp3 };
47 changes: 20 additions & 27 deletions src/hooks/VoiceRecorder/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
VOICE_RECORDER_AUDIO_BIT_RATE,
} from '../../utils/consts';
import useSendbirdStateContext from '../useSendbirdStateContext';
import { type WebAudioUtils } from './WebAudioUtils';
import { noop } from '../../utils/utils';

// Input props of VoiceRecorder
export interface VoiceRecorderProps {
Expand All @@ -22,11 +24,11 @@ export interface VoiceRecorderEventHandler {

// Output of VoiceRecorder
export interface VoiceRecorderContext {
start: (eventHandler?: VoiceRecorderEventHandler) => void,
stop: () => void,
start: (eventHandler?: VoiceRecorderEventHandler) => void;
stop: () => void;
isRecordable: boolean;
}
const noop = () => { /* noop */ };

const Context = createContext<VoiceRecorderContext>({
start: noop,
stop: noop,
Expand All @@ -37,13 +39,13 @@ export const VoiceRecorderProvider = (props: VoiceRecorderProps): React.ReactEle
const { children } = props;
const { config } = useSendbirdStateContext();
const { logger, groupChannel } = config;
const [mediaRecorder, setMediaRecorder] = useState<MediaRecorder>(null);
const [mediaRecorder, setMediaRecorder] = useState<MediaRecorder | null>(null);
const [isRecordable, setIsRecordable] = useState<boolean>(false);
const [permissionWarning, setPermissionWarning] = useState<boolean>(false);
const { stringSet } = useLocalization();

const isVoiceMessageEnabled = groupChannel.enableVoiceMessage;
const [webAudioUtils, setWebAudioUtils] = useState(null);
const [webAudioUtils, setWebAudioUtils] = useState<WebAudioUtils | null>(null);

const browserSupportMimeType = BROWSER_SUPPORT_MIME_TYPE_LIST.find((mimeType) => MediaRecorder.isTypeSupported(mimeType)) ?? '';
if (isVoiceMessageEnabled && !browserSupportMimeType) {
Expand All @@ -52,13 +54,11 @@ export const VoiceRecorderProvider = (props: VoiceRecorderProps): React.ReactEle

useEffect(() => {
if (isVoiceMessageEnabled && !webAudioUtils) {
import('./WebAudioUtils').then((data) => {
setWebAudioUtils(data);
});
import('./WebAudioUtils').then((module) => setWebAudioUtils(module));
}
}, [isVoiceMessageEnabled, webAudioUtils]);

const start = useCallback((eventHandler: VoiceRecorderEventHandler): void => {
const start = useCallback((eventHandler?: VoiceRecorderEventHandler): void => {
if (isVoiceMessageEnabled && !webAudioUtils) {
logger.error('VoiceRecorder: Recording audio processor is being loaded.');
return;
Expand Down Expand Up @@ -95,7 +95,8 @@ export const VoiceRecorderProvider = (props: VoiceRecorderProps): React.ReactEle
mimeType: browserSupportMimeType,
audioBitsPerSecond: VOICE_RECORDER_AUDIO_BIT_RATE,
});
mediaRecorder.ondataavailable = (e) => { // when recording stops
// when recording stops
mediaRecorder.ondataavailable = (e) => {
logger.info('VoiceRecorder: Succeeded getting an available data.', e.data);
const audioFile = new File([e.data], VOICE_MESSAGE_FILE_NAME, {
lastModified: new Date().getTime(),
Expand All @@ -108,10 +109,11 @@ 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());
const tracks = stream.getAudioTracks();
tracks.forEach((track) => track.stop());
setIsRecordable(false);
};
mediaRecorder.onstart = eventHandler?.onRecordingStarted ?? noop;
Expand All @@ -133,22 +135,13 @@ export const VoiceRecorderProvider = (props: VoiceRecorderProps): React.ReactEle
}, [mediaRecorder]);

return (
<Context.Provider value={{
start,
stop,
isRecordable,
}}>
<Context.Provider value={{ start, stop, isRecordable }}>
{children}
{
permissionWarning && (
<Modal
hideFooter
onCancel={() => setPermissionWarning(false)}
>
<>{stringSet.VOICE_RECORDING_PERMISSION_DENIED}</>
</Modal>
)
}
{permissionWarning && (
<Modal hideFooter onClose={() => setPermissionWarning(false)}>
<>{stringSet.VOICE_RECORDING_PERMISSION_DENIED}</>
</Modal>
)}
</Context.Provider>
);
};
Expand Down
23 changes: 12 additions & 11 deletions src/hooks/VoiceRecorder/useVoiceRecorder.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useCallback, useEffect, useState } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { VoiceRecorderEventHandler, useVoiceRecorderContext } from '.';
import useSendbirdStateContext from '../useSendbirdStateContext';
import { noop } from '../../utils/utils';

// export interface UseVoiceRecorderProps extends VoiceRecorderEventHandler {
// /**
Expand All @@ -22,23 +23,21 @@ export interface UseVoiceRecorderContext {
cancel: () => void;
recordingLimit: number;
recordingTime: number;
recordedFile: File;
recordedFile: File | null;
recordingStatus: VoiceRecorderStatus;
}

const noop = () => { /* noop */ };

export const useVoiceRecorder = ({
onRecordingStarted = noop,
onRecordingEnded = noop,
}: VoiceRecorderEventHandler): UseVoiceRecorderContext => {
const { config } = useSendbirdStateContext();
const { voiceRecord } = config;
const { maxRecordingTime } = voiceRecord;
const maxRecordingTime = voiceRecord.maxRecordingTime;
const voiceRecorder = useVoiceRecorderContext();
const { isRecordable } = voiceRecorder;

const [recordedFile, setRecordedFile] = useState<File>(null);
const [recordedFile, setRecordedFile] = useState<File | null>(null);
const [recordingStatus, setRecordingStatus] = useState<VoiceRecorderStatus>(VoiceRecorderStatus.PREPARING);
useEffect(() => {
if (isRecordable && recordingStatus === VoiceRecorderStatus.PREPARING) {
Expand Down Expand Up @@ -72,11 +71,12 @@ export const useVoiceRecorder = ({

// Timer
const [recordingTime, setRecordingTime] = useState<number>(0);
let timer: ReturnType<typeof setInterval> = null;
const timer = useRef<ReturnType<typeof setInterval> | null>(null);
function startTimer() {
stopTimer();
setRecordingTime(0);
const interval = setInterval(() => {

timer.current = setInterval(() => {
setRecordingTime(prevTime => {
const newTime = prevTime + 100;
if (newTime > maxRecordingTime) {
Expand All @@ -85,11 +85,12 @@ export const useVoiceRecorder = ({
return newTime;
});
}, 100);
timer = interval;
}
function stopTimer() {
clearInterval(timer);
timer = null;
if (timer.current) {
clearInterval(timer.current);
timer.current = null;
}
}
useEffect(() => {
if (recordingTime > maxRecordingTime) {
Expand Down
8 changes: 4 additions & 4 deletions src/hooks/useThrottleCallback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ export function useThrottleCallback<T extends(...args: any[]) => void>(
trailing: false,
},
) {
const timer = useRef(null);
const trailingArgs = useRef(null);
const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
const trailingArgs = useRef<any[] | null>(null);

useEffect(() => {
return () => {
Expand Down Expand Up @@ -58,8 +58,8 @@ export function throttle<T extends(...args: any[]) => void>(
trailing: false,
},
) {
let timer = null;
let trailingArgs = null;
let timer: ReturnType<typeof setTimeout> | null = null;
let trailingArgs: null | any[] = null;

return ((...args: any[]) => {
if (timer) {
Expand Down
Loading