Skip to content

Commit

Permalink
Merge pull request #280 from Front-line-dev/feat/voice-left-tab
Browse files Browse the repository at this point in the history
์Œ์„ฑ ์ฑ„ํŒ… ๋ฒ„ํŠผ ์Šคํƒ€์ผ, ์Šคํ”ผ์ปค ๋ฒ„ํŠผ
  • Loading branch information
Front-line-dev authored Dec 17, 2020
2 parents 86369be + 77f825e commit fb9f327
Show file tree
Hide file tree
Showing 10 changed files with 139 additions and 21 deletions.
2 changes: 2 additions & 0 deletions src/frontend/engine/DuckLeftTabObject.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class DuckLeftTabObject extends DuckObject {
this.hatWidth = 45;

this.addClass('left-duck-wrapper');
this.instance.dataset.socketId = this.socketID;
this.render();
}

Expand All @@ -32,6 +33,7 @@ class DuckLeftTabObject extends DuckObject {
${DuckHat({ width: hatWidth })}
${Duck({ color, width: duckWidth })}
<span class="duck-score">${score}</span>
<img alt="speaker button" class="duck-speaker"/>
</div>
<span class="duck-nickname">${nickname}</span>
`;
Expand Down
6 changes: 3 additions & 3 deletions src/frontend/game/game.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@
<span id="participants-count" class="left-title-number">0</span>
</div>
<div id="participants-wrapper" class="participants-wrapper"></div>
<div class="microphone-controller" id="microphone-controller">
<span>๋งˆ์ดํฌ ์—ฐ๊ฒฐ</span>
<button class="microphone-controller" id="microphone-controller">
<span>์Œ์„ฑ ์ฑ„ํŒ… ์ฐธ์—ฌ</span>
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 14c1.66 0 2.99-1.34 2.99-3L15 5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm5.3-3c0 3-2.54 5.1-5.3 5.1S6.7 14 6.7 11H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c3.28-.48 6-3.3 6-6.72h-1.7z"/></svg>
</div>
</button>
</div>
<div id="background" class="background">
<div id="root" class="main"></div>
Expand Down
22 changes: 22 additions & 0 deletions src/frontend/game/left.scss
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@
font-weight: 700;
}

.microphone-controller-exit {
background-color: $red-color;
}

.participants-wrapper {
@include flex-column;
height: 100%;
Expand Down Expand Up @@ -63,6 +67,24 @@
background-color: $gray-color;
border-radius: 1rem;
}

.duck-speaker {
position: absolute;
top: 0%;
left: 45%;
width: 100px;
height: 100px;
cursor: pointer;
visibility: hidden;
}
.duck-speaker-active {
content: url('@resources/speaker-active.png');
visibility: visible;
}
.duck-speaker-deactive {
content: url('@resources/speaker-deactive.png');
visibility: visible;
}
}

.duck-hat {
Expand Down
26 changes: 14 additions & 12 deletions src/frontend/game/voiceChat.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
import socket from '@utils/socket';
import Peer from 'peerjs';
import leftSound from '@resources/left.mp3';
import {
getAudioStream,
setAnswerBehavior,
connectToNewUser,
peerMap,
deleteOtherPeer,
closeVoiceButton,
transformVoiceButton,
putBackVoiceButton,
} from '@utils/voiceChatUtil';

let myPeerJSClient = null;
let myAudioStream = null;

const peerMap = new Map();

const deleteOtherPeer = (socketID) => {
if (peerMap.has(socketID)) {
peerMap.get(socketID).mediaConnection.close();
peerMap.delete(socketID);
}
};

const isConnectedToVoiceChat = () => {
return myPeerJSClient && myAudioStream;
};
Expand All @@ -28,10 +25,11 @@ const activateVoiceChat = () => {
myPeerJSClient.on('open', async () => {
try {
// ๋งˆ์ดํฌ ์—ฐ๊ฒฐ
closeVoiceButton();
myAudioStream = await getAudioStream();
transformVoiceButton();
setAnswerBehavior({
stream: myAudioStream,
peerMap,
peer: myPeerJSClient,
});
} catch (err) {
Expand All @@ -51,15 +49,20 @@ const deactivateVoiceChat = () => {
track.stop();
});

peerMap.forEach((_, socketID) => {
[...peerMap.keys()].forEach((socketID) => {
deleteOtherPeer(socketID);
});

putBackVoiceButton();
};

// ์—ฐ๊ฒฐ๋œ ์œ ์ €๊ฐ€ ๋ณด์ด์Šค ์ฑ„ํŒ… ์ ‘์†์„ ๋Š์—ˆ์„ ๋•Œ
socket.on('voice disconnected', ({ socketID }) => {
if (!isConnectedToVoiceChat()) return;

const se = new Audio(leftSound);
se.play();

deleteOtherPeer(socketID);
});

Expand All @@ -70,7 +73,6 @@ socket.on('another voice connected', ({ socketID }) => {
peer: myPeerJSClient,
socketID,
stream: myAudioStream,
peerMap,
});
});

Expand Down
Binary file added src/frontend/resources/joined.mp3
Binary file not shown.
Binary file added src/frontend/resources/left.mp3
Binary file not shown.
Binary file added src/frontend/resources/speaker-active.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/frontend/resources/speaker-deactive.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
102 changes: 97 additions & 5 deletions src/frontend/utils/voiceChatUtil.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,41 @@
const audioContainer = document.getElementById('voice-chat-audio-container');
import { $id, $qs } from '@utils/dom';
import joinSound from '@resources/joined.mp3';
import leftSound from '@resources/left.mp3';

const addAudioStream = ({ mediaConnection, peerMap }) => {
const peerMap = new Map();
const audioContainer = $id('voice-chat-audio-container');

const getDuckSpeaker = (socketID) => {
const duckWrapper = $qs(`.left-duck-wrapper[data-socket-id="${socketID}"]`);

if (!duckWrapper) return null;

const duckSpeaker = duckWrapper.querySelector('.duck-speaker');

return duckSpeaker;
};

const getVoiceButton = () => {
return $id('microphone-controller');
};

const duckSpeakerHandler = ({ target }) => {
const duckSpeaker = target;
const socketID = duckSpeaker.closest('.left-duck-wrapper').dataset.socketId;
const { audioElement } = peerMap.get(socketID);
if (duckSpeaker.classList.contains('duck-speaker-deactive')) {
audioElement.muted = false;
duckSpeaker.classList.add('duck-speaker-active');
duckSpeaker.classList.remove('duck-speaker-deactive');
} else {
audioElement.muted = true;
duckSpeaker.classList.add('duck-speaker-deactive');
duckSpeaker.classList.remove('duck-speaker-active');
}
};

// ๋ฐ›์€ mediaConnection์œผ๋กœ ์‹ค์ œ ์˜ค๋””์˜ค ์—˜๋ฆฌ๋จผํŠธ ์ƒ์„ฑ
const addAudioStream = ({ mediaConnection }) => {
const socketID = mediaConnection.peer;

if (peerMap.has(socketID)) return;
Expand All @@ -20,6 +55,10 @@ const addAudioStream = ({ mediaConnection, peerMap }) => {
audioElement.remove();
});

const duckSpeaker = getDuckSpeaker(socketID);
duckSpeaker.classList.add('duck-speaker-active');
duckSpeaker.addEventListener('click', duckSpeakerHandler);

peerMap.set(socketID, {
mediaConnection,
audioElement,
Expand All @@ -28,13 +67,15 @@ const addAudioStream = ({ mediaConnection, peerMap }) => {

// socket์„ ํ†ตํ•ด ๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด ์ ‘์†ํ•œ๊ฑธ ๋ฐ›์•˜์„ ๋•Œ
// ๋‹ค๋ฅธ ์‚ฌ๋žŒ์—๊ฒŒ mediaConnection ์š”์ฒญ์„ ๋ณด๋ƒ„
const connectToNewUser = ({ peer, socketID, stream, peerMap }) => {
const connectToNewUser = ({ peer, socketID, stream }) => {
const mediaConnection = peer.call(socketID, stream);
addAudioStream({ mediaConnection, peerMap });
const se = new Audio(joinSound);
se.play();
};

// ๋‚ด๊ฐ€ ๋‹ค๋ฅธ ์‚ฌ๋žŒ์˜ mediaConnection ์š”์ฒญ์„ ๋ฐ›์•˜์„ ๋•Œ
const setAnswerBehavior = ({ stream, peerMap, peer }) => {
const setAnswerBehavior = ({ stream, peer }) => {
peer.on('call', (mediaConnection) => {
// ๋‹ค๋ฅธ ์‚ฌ๋žŒ์˜ ์š”์ฒญ์— answer๋ฅผ ๋‚ ๋ฆผ
mediaConnection.answer(stream);
Expand All @@ -47,4 +88,55 @@ const getAudioStream = () =>
audio: true,
});

export { getAudioStream, setAnswerBehavior, connectToNewUser };
const closeVoiceButton = () => {
const voiceButton = getVoiceButton();
voiceButton.disabled = true;
const se = new Audio(joinSound);
se.play();
};

const transformVoiceButton = () => {
const voiceButton = getVoiceButton();
const voiceButtonSpan = voiceButton.querySelector('span');
voiceButtonSpan.innerText = '์Œ์„ฑ ์ฑ„ํŒ… ๋‚˜๊ฐ€๊ธฐ';
voiceButton.classList.add('microphone-controller-exit');
voiceButton.disabled = false;
};

const putBackVoiceButton = () => {
const voiceButton = getVoiceButton();
const voiceButtonSpan = voiceButton.querySelector('span');
voiceButtonSpan.innerText = '์Œ์„ฑ ์ฑ„ํŒ… ์ฐธ์—ฌ';
voiceButton.classList.remove('microphone-controller-exit');
const se = new Audio(leftSound);
se.play();
};

const removeDuckSpeaker = (socketID) => {
const duckSpeaker = getDuckSpeaker(socketID);

if (!duckSpeaker) return;

duckSpeaker.removeEventListener('click', duckSpeakerHandler);

duckSpeaker.classList.remove('duck-speaker-active', 'duck-speaker-deactive');
};

const deleteOtherPeer = (socketID) => {
if (peerMap.has(socketID)) {
peerMap.get(socketID).mediaConnection.close();
peerMap.delete(socketID);
removeDuckSpeaker(socketID);
}
};

export {
getAudioStream,
setAnswerBehavior,
connectToNewUser,
peerMap,
deleteOtherPeer,
closeVoiceButton,
transformVoiceButton,
putBackVoiceButton,
};
2 changes: 1 addition & 1 deletion src/frontend/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ module.exports = (webpackEnv) => {
],
},
{
test: /\.(png|jpe?g|gif)$/i,
test: /\.(png|jpe?g|gif|mp3)$/i,
loader: 'file-loader',
options: {
outputPath: 'assets',
Expand Down

0 comments on commit fb9f327

Please sign in to comment.