Skip to content

Commit e3c84a5

Browse files
committed
add volume toggle hotkey and icon
1 parent 2cc8e16 commit e3c84a5

5 files changed

Lines changed: 74 additions & 4 deletions

File tree

services/app/assets/css/style.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,13 @@ a {
320320
}
321321
}
322322

323+
.cb-mute-icon {
324+
position: fixed;
325+
bottom: 32px;
326+
right: 190px;
327+
z-index: 5000;
328+
}
329+
323330
.cursor-pointer {
324331
cursor: pointer;
325332
}

services/app/assets/js/widgets/containers/Editor.jsx

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { connect } from 'react-redux';
99
import { gameTypeSelector } from '../selectors/index';
1010
import languages from '../config/languages';
1111
import GameTypeCodes from '../config/gameTypeCodes';
12+
import sound from '../lib/sound';
13+
import { actions } from '../slices';
1214

1315
class Editor extends PureComponent {
1416
static propTypes = {
@@ -17,13 +19,15 @@ class Editor extends PureComponent {
1719
syntax: PropTypes.string,
1820
onChange: PropTypes.func,
1921
mode: PropTypes.string.isRequired,
22+
mute: PropTypes.bool,
2023
};
2124

2225
static defaultProps = {
2326
value: '',
2427
editable: false,
2528
onChange: null,
2629
syntax: 'javascript',
30+
mute: false,
2731
};
2832

2933
// eslint-disable-next-line react/sort-comp
@@ -59,6 +63,7 @@ class Editor extends PureComponent {
5963
mode,
6064
syntax,
6165
checkResult,
66+
toggleMuteSound,
6267
} = this.props;
6368

6469
this.modes = {
@@ -69,7 +74,7 @@ class Editor extends PureComponent {
6974
this.currentMode = this.modes[mode]();
7075

7176
this.editor.addAction({
72-
id: 'codebattle-hot-keys',
77+
id: 'codebattle-check-keys',
7378
label: 'Codebattle check start',
7479
keybindings: [this.monaco.KeyMod.CtrlCmd | this.monaco.KeyCode.Enter],
7580
run: () => {
@@ -78,6 +83,18 @@ class Editor extends PureComponent {
7883
}
7984
},
8085
});
86+
87+
this.editor.addAction({
88+
id: 'codebattle-mute-keys',
89+
label: 'Codebattle mute sound',
90+
keybindings: [this.monaco.KeyMod.CtrlCmd | this.monaco.KeyCode.KEY_M],
91+
run: () => {
92+
const { mute } = this.props;
93+
// eslint-disable-next-line no-unused-expressions
94+
mute ? sound.toggle() : sound.toggle(0);
95+
toggleMuteSound();
96+
},
97+
});
8198
window.addEventListener('keydown', this.ctrPlusS);
8299
}
83100

@@ -169,6 +186,11 @@ class Editor extends PureComponent {
169186
() => null,
170187
);
171188

189+
this.editor.addCommand(
190+
this.monaco.KeyMod.CtrlCmd | this.monaco.KeyCode.KEY_M,
191+
() => null,
192+
);
193+
172194
window.addEventListener('resize', this.handleResize);
173195
};
174196

@@ -205,7 +227,10 @@ const mapStateToProps = state => {
205227
const gameType = gameTypeSelector(state);
206228
return {
207229
gameType,
230+
mute: state.userSettings.mute,
208231
};
209232
};
210233

211-
export default connect(mapStateToProps)(Editor);
234+
const mapDispatchToProps = { toggleMuteSound: actions.toggleMuteSound };
235+
236+
export default connect(mapStateToProps, mapDispatchToProps)(Editor);

services/app/assets/js/widgets/containers/RootContainer.jsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import ReactJoyride, { STATUS } from 'react-joyride';
66
import { CSSTransition, SwitchTransition } from 'react-transition-group';
77
import { useMachine } from '@xstate/react';
88

9+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
910
import GameWidget from './GameWidget';
1011
import GameContext from './GameContext';
1112
import InfoWidget from './InfoWidget';
@@ -21,6 +22,7 @@ import GamePreview from '../components/Game/GamePreview';
2122
import { replayerMachineStates } from '../machines/game';
2223
import AnimationModal from '../components/AnimationModal';
2324
import NetworkAlert from './NetworkAlert';
25+
import sound from '../lib/sound';
2426

2527
const steps = [
2628
{
@@ -182,8 +184,10 @@ const RootContainer = ({
182184
setCurrentUser,
183185
gameMachine,
184186
editorMachine,
187+
toggleMuteSound,
185188
}) => {
186189
const [modalShowing, setModalShowing] = useState(false);
190+
const mute = useSelector(state => state.userSettings.mute);
187191
const [current, send, service] = useMachine(gameMachine, {
188192
devTools: true,
189193
actions: {
@@ -201,6 +205,23 @@ const RootContainer = ({
201205
// eslint-disable-next-line react-hooks/exhaustive-deps
202206
}, []);
203207

208+
useEffect(() => {
209+
const muteSound = e => {
210+
if ((e.ctrlKey || e.metaKey) && e.key === 'm') {
211+
e.preventDefault();
212+
// eslint-disable-next-line no-unused-expressions
213+
mute ? sound.toggle() : sound.toggle(0);
214+
toggleMuteSound();
215+
}
216+
};
217+
218+
window.addEventListener('keydown', muteSound);
219+
220+
return () => {
221+
window.removeEventListener('keydown', muteSound);
222+
};
223+
}, [mute, toggleMuteSound]);
224+
204225
if (current.matches({ game: 'waiting' })) {
205226
const gameUrl = window.location.href;
206227
return <WaitingOpponentInfo gameUrl={gameUrl} />;
@@ -242,6 +263,12 @@ const RootContainer = ({
242263
<AnimationModal setModalShowing={setModalShowing} modalShowing={modalShowing} />
243264
<InfoWidget />
244265
<GameWidget editorMachine={editorMachine} />
266+
{mute
267+
&& (
268+
<div className="rounded p-2 bg-dark cb-mute-icon">
269+
<FontAwesomeIcon size="lg" color="white" icon={['fas', 'volume-mute']} />
270+
</div>
271+
)}
245272
<FeedBackWidget />
246273
</div>
247274
</div>
@@ -265,6 +292,7 @@ const mapDispatchToProps = {
265292
setCurrentUser: actions.setCurrentUser,
266293
connectToGame: GameActions.connectToGame,
267294
connectToChat: ChatActions.connectToChat,
295+
toggleMuteSound: actions.toggleMuteSound,
268296
};
269297

270298
export default connect(null, mapDispatchToProps)(RootContainer);

services/app/assets/js/widgets/lib/sound.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,16 @@ const audio = () => new Howl({
3232

3333
const sound = {
3434
play: (type, soundLevel) => {
35+
const isMute = JSON.parse(localStorage.getItem('ui_mute_sound')) || false;
3536
const soundEffect = audio();
36-
if (soundType === 'silent') return;
37+
if (soundType === 'silent' || isMute) return;
3738
Howler.volume(_.isUndefined(soundLevel) ? defaultSoundLevel : soundLevel);
3839
soundEffect.play(type);
3940
},
4041
stop: () => Howler.stop(),
42+
toggle: (volume = defaultSoundLevel) => {
43+
Howler.volume(volume);
44+
},
4145
};
4246

4347
function createSound(slug) {

services/app/assets/js/widgets/slices/userSettings.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,18 @@ export const updateUserSettings = createAsyncThunk(
3535
const initialState = {
3636
...userSettings,
3737
error: '',
38+
mute: JSON.parse(localStorage.getItem('ui_mute_sound')),
3839
};
3940

4041
const userSettingsSlice = createSlice({
4142
name: 'userSettings',
4243
initialState,
43-
reducers: {},
44+
reducers: {
45+
toggleMuteSound: state => {
46+
localStorage.setItem('ui_mute_sound', !state.mute);
47+
return ({ ...state, mute: !state.mute });
48+
},
49+
},
4450
extraReducers: builder => {
4551
// The `builder` callback form is used here because it provides correctly typed reducers from the action creators
4652
builder.addCase(updateUserSettings.fulfilled, (state, { payload }) => {

0 commit comments

Comments
 (0)