diff --git a/src/components/MdToggleButton.jsx b/src/components/MdToggleButton.jsx index 4971fe2..378311a 100644 --- a/src/components/MdToggleButton.jsx +++ b/src/components/MdToggleButton.jsx @@ -6,12 +6,29 @@ import ToggleButton from 'react-toggle-button'; import { MdCheck, MdClose } from 'react-icons/md'; -const MdToggleButton = ({ value, onToggle }) => ( +const MdToggleButton = ({ value, onToggle, deactivated }) => ( } activeLabel={} value={value} onToggle={onToggle} + colors={(deactivated) + ? { + activeThumb: { + base: '#b2b2b2', + }, + inactiveTumb: { + base: '#b2b2b2', + }, + active: { + base: '#cbcbcb', + }, + inactive: { + base: '#cbcbcb', + }, + } + : {}} + thumbStyleHover={{ backgroundColor: '#ededed' }} /> ); diff --git a/src/components/MdToggleButtonHover.jsx b/src/components/MdToggleButtonHover.jsx deleted file mode 100644 index 072f898..0000000 --- a/src/components/MdToggleButtonHover.jsx +++ /dev/null @@ -1,33 +0,0 @@ -/** - */ - -import React from 'react'; -import MdToggleButton from './MdToggleButton'; - - -const MdToggleButtonHover = ({ value, onToggle }) => ( - ({ - // eslint-disable-next-line max-len - boxShadow: `0 0 ${2 + (4 * n)}px rgba(0,0,0,.16),0 ${2 + (3 * n)}px ${4 + (8 * n)}px rgba(0,0,0,.32)`, - })} - /> -); - -export default MdToggleButtonHover; diff --git a/src/components/SocialSettings.jsx b/src/components/SocialSettings.jsx index aecc2f5..148abd4 100644 --- a/src/components/SocialSettings.jsx +++ b/src/components/SocialSettings.jsx @@ -11,7 +11,7 @@ import { setBlockingDm, setUserBlock, } from '../actions'; -import MdToggleButtonHover from './MdToggleButtonHover'; +import MdToggleButton from './MdToggleButton'; const SocialSettings = ({ done }) => { const blocked = useSelector((state) => state.chat.blocked); @@ -37,7 +37,7 @@ const SocialSettings = ({ done }) => { > {t`Block all Private Messages`} - { if (!fetching) { diff --git a/src/components/windows/Settings.jsx b/src/components/windows/Settings.jsx index fb7d42f..bb72582 100644 --- a/src/components/windows/Settings.jsx +++ b/src/components/windows/Settings.jsx @@ -8,7 +8,7 @@ import { useSelector, useDispatch, shallowEqual } from 'react-redux'; import { c, t } from 'ttag'; import LanguageSelect from '../LanguageSelect'; -import MdToggleButtonHover from '../MdToggleButtonHover'; +import MdToggleButton from '../MdToggleButton'; import { toggleGrid, togglePixelNotify, @@ -48,7 +48,7 @@ const rowStyles = { }; const SettingsItemSelect = ({ - title, description, values, selected, onSelect, icon, + title, values, selected, onSelect, icon, children, }) => (
@@ -72,13 +72,13 @@ const SettingsItemSelect = ({ }
- {description &&
{description}
} +
{children}
); -const SettingsItem = ({ - title, description, keyBind, value, onToggle, +const SettingsItem = React.memo(({ + title, keyBind, value, onToggle, children, deactivated, }) => (
@@ -88,12 +88,16 @@ const SettingsItem = ({ > {title} {keyBind && {keyBind}} - +
- {description &&
{description}
} +
{children}
-); +), (prevProps, nextProps) => prevProps.value === nextProps.value); function Settings() { const [ @@ -121,79 +125,98 @@ function Settings() { ], shallowEqual); const dispatch = useDispatch(); + const audioAvailable = window.AudioContext || window.webkitAudioContext; + return (
dispatch(toggleGrid())} - /> + > + {t`Turn on grid to highlight pixel borders.`} + dispatch(togglePixelNotify())} - /> + > + {t`Show circles where pixels are placed.`} + dispatch(toggleMute())} - /> + > + {[t`All sound effects will be disabled.`, + (!audioAvailable) && ( +

+ {/* eslint-disable-next-line max-len */} + {t`Your Browser doesn't allow us to use AudioContext to play sounds. Do you have some privacy feature blocking us?`} +

+ ), + ]} +
dispatch(toggleChatNotify())} - /> + > + {t`Play a sound when new chat messages arrive`} + dispatch(toggleAutoZoomIn())} - /> + > + {/* eslint-disable-next-line max-len */} + {t`Zoom in instead of placing a pixel when you tap the canvas and your zoom is small.`} + dispatch(toggleCompactPalette())} - /> + > + {t`Display Palette in a compact form that takes less screen space.`} + dispatch(togglePotatoMode())} - /> + > + {t`For when you are playing on a potato.`} + dispatch(toggleLightGrid())} - /> + > + {t`Show Grid in white instead of black.`} + {(window.ssv && window.ssv.backupurl) && ( dispatch(toggleHistoricalView())} - /> + > + {t`Check out past versions of the canvas.`} + )} {(window.ssv && window.ssv.availableStyles) && ( dispatch(selectStyle(style))} - /> + > + {t`How pixelplanet should look like.`} + )} {(window.ssv && navigator.cookieEnabled && window.ssv.langs) && (
diff --git a/src/store/audio.js b/src/store/audio.js index a688410..7777065 100644 --- a/src/store/audio.js +++ b/src/store/audio.js @@ -7,245 +7,240 @@ // iPhone needs this const AudioContext = window.AudioContext || window.webkitAudioContext; -const context = new AudioContext(); +const context = AudioContext && new AudioContext(); export default (store) => (next) => (action) => { const state = store.getState(); const { mute, chatNotify } = state.audio; - switch (action.type) { - case 'SELECT_COLOR': { - if (mute) break; - const oscillatorNode = context.createOscillator(); - const gainNode = context.createGain(); + if (!mute && context) { + switch (action.type) { + case 'SELECT_COLOR': { + const oscillatorNode = context.createOscillator(); + const gainNode = context.createGain(); - oscillatorNode.type = 'sine'; - oscillatorNode.detune.value = -600; + oscillatorNode.type = 'sine'; + oscillatorNode.detune.value = -600; - oscillatorNode.frequency.setValueAtTime(600, context.currentTime); - oscillatorNode.frequency.setValueAtTime(700, context.currentTime + 0.1); + oscillatorNode.frequency.setValueAtTime(600, context.currentTime); + oscillatorNode.frequency.setValueAtTime(700, context.currentTime + 0.1); - gainNode.gain.setValueAtTime(0.3, context.currentTime); - gainNode.gain.exponentialRampToValueAtTime( - 0.2, - context.currentTime + 0.1, - ); + gainNode.gain.setValueAtTime(0.3, context.currentTime); + gainNode.gain.exponentialRampToValueAtTime( + 0.2, + context.currentTime + 0.1, + ); - oscillatorNode.connect(gainNode); - gainNode.connect(context.destination); + oscillatorNode.connect(gainNode); + gainNode.connect(context.destination); - oscillatorNode.start(); - oscillatorNode.stop(context.currentTime + 0.2); - break; - } - - case 'SET_NOTIFICATION': { - if (mute) break; - const { notification } = action; - if (typeof notification !== 'string') { - break; - } - const oscillatorNode = context.createOscillator(); - const gainNode = context.createGain(); - - oscillatorNode.type = 'sine'; - oscillatorNode.detune.value = -1200; - - oscillatorNode.frequency.setValueAtTime(500, context.currentTime); - oscillatorNode.frequency.setValueAtTime(600, context.currentTime + 0.1); - - gainNode.gain.setValueAtTime(0.3, context.currentTime); - gainNode.gain.exponentialRampToValueAtTime( - 0.2, - context.currentTime + 0.1, - ); - - oscillatorNode.connect(gainNode); - gainNode.connect(context.destination); - - oscillatorNode.start(); - oscillatorNode.stop(context.currentTime + 0.2); - break; - } - - case 'PIXEL_WAIT': { - if (mute) break; - const oscillatorNode = context.createOscillator(); - const gainNode = context.createGain(); - - oscillatorNode.type = 'sine'; - // oscillatorNode.detune.value = -600 - - oscillatorNode.frequency.setValueAtTime(1479.98, context.currentTime); - oscillatorNode.frequency.exponentialRampToValueAtTime( - 493.88, - context.currentTime + 0.01, - ); - - - gainNode.gain.setValueAtTime(0.5, context.currentTime); - gainNode.gain.exponentialRampToValueAtTime( - 0.2, - context.currentTime + 0.1, - ); - - oscillatorNode.connect(gainNode); - gainNode.connect(context.destination); - - oscillatorNode.start(); - oscillatorNode.stop(context.currentTime + 0.1); - break; - } - - case 'PIXEL_FAILURE': { - if (mute) break; - const oscillatorNode = context.createOscillator(); - const gainNode = context.createGain(); - - oscillatorNode.type = 'sine'; - oscillatorNode.detune.value = -900; - oscillatorNode.frequency.setValueAtTime(600, context.currentTime); - oscillatorNode.frequency.setValueAtTime( - 1400, - context.currentTime + 0.025, - ); - oscillatorNode.frequency.setValueAtTime( - 1200, - context.currentTime + 0.05, - ); - oscillatorNode.frequency.setValueAtTime( - 900, - context.currentTime + 0.075, - ); - - const lfo = context.createOscillator(); - lfo.type = 'sine'; - lfo.frequency.value = 2.0; - lfo.connect(gainNode.gain); - oscillatorNode.connect(gainNode); - gainNode.connect(context.destination); - - oscillatorNode.start(); - lfo.start(); - oscillatorNode.stop(context.currentTime + 0.3); - break; - } - - case 'PLACED_PIXELS': { - if (mute) break; - const { palette, selectedColor: color } = state.canvas; - const colorsAmount = palette.colors.length; - - const clrFreq = 100 + Math.log(color / colorsAmount + 1) * 300; - const oscillatorNode = context.createOscillator(); - const gainNode = context.createGain(); - - oscillatorNode.type = 'sine'; - oscillatorNode.frequency.setValueAtTime(clrFreq, context.currentTime); - oscillatorNode.frequency.exponentialRampToValueAtTime( - 1400, - context.currentTime + 0.2, - ); - - gainNode.gain.setValueAtTime(0.5, context.currentTime); - gainNode.gain.exponentialRampToValueAtTime( - 0.2, - context.currentTime + 0.1, - ); - - oscillatorNode.connect(gainNode); - gainNode.connect(context.destination); - - oscillatorNode.start(); - oscillatorNode.stop(context.currentTime + 0.1); - break; - } - - case 'COOLDOWN_END': { - if (mute) break; - - // do not play sound if last cooldown end was <5s ago - const { lastCoolDownEnd } = state.user; - if (lastCoolDownEnd && lastCoolDownEnd.getTime() + 5000 > Date.now()) { + oscillatorNode.start(); + oscillatorNode.stop(context.currentTime + 0.2); break; } - const oscillatorNode = context.createOscillator(); - const gainNode = context.createGain(); + case 'SET_NOTIFICATION': { + const { notification } = action; + if (typeof notification !== 'string') { + break; + } + const oscillatorNode = context.createOscillator(); + const gainNode = context.createGain(); - oscillatorNode.type = 'sine'; - oscillatorNode.frequency.setValueAtTime(349.23, context.currentTime); - oscillatorNode.frequency.setValueAtTime( - 523.25, - context.currentTime + 0.1, - ); - oscillatorNode.frequency.setValueAtTime( - 698.46, - context.currentTime + 0.2, - ); + oscillatorNode.type = 'sine'; + oscillatorNode.detune.value = -1200; - gainNode.gain.setValueAtTime(0.5, context.currentTime); - gainNode.gain.exponentialRampToValueAtTime( - 0.2, - context.currentTime + 0.15, - ); + oscillatorNode.frequency.setValueAtTime(500, context.currentTime); + oscillatorNode.frequency.setValueAtTime(600, context.currentTime + 0.1); - oscillatorNode.connect(gainNode); - gainNode.connect(context.destination); + gainNode.gain.setValueAtTime(0.3, context.currentTime); + gainNode.gain.exponentialRampToValueAtTime( + 0.2, + context.currentTime + 0.1, + ); - oscillatorNode.start(); - oscillatorNode.stop(context.currentTime + 0.3); - break; + oscillatorNode.connect(gainNode); + gainNode.connect(context.destination); + + oscillatorNode.start(); + oscillatorNode.stop(context.currentTime + 0.2); + break; + } + + case 'PIXEL_WAIT': { + const oscillatorNode = context.createOscillator(); + const gainNode = context.createGain(); + + oscillatorNode.type = 'sine'; + // oscillatorNode.detune.value = -600 + + oscillatorNode.frequency.setValueAtTime(1479.98, context.currentTime); + oscillatorNode.frequency.exponentialRampToValueAtTime( + 493.88, + context.currentTime + 0.01, + ); + + + gainNode.gain.setValueAtTime(0.5, context.currentTime); + gainNode.gain.exponentialRampToValueAtTime( + 0.2, + context.currentTime + 0.1, + ); + + oscillatorNode.connect(gainNode); + gainNode.connect(context.destination); + + oscillatorNode.start(); + oscillatorNode.stop(context.currentTime + 0.1); + break; + } + + case 'PIXEL_FAILURE': { + const oscillatorNode = context.createOscillator(); + const gainNode = context.createGain(); + + oscillatorNode.type = 'sine'; + oscillatorNode.detune.value = -900; + oscillatorNode.frequency.setValueAtTime(600, context.currentTime); + oscillatorNode.frequency.setValueAtTime( + 1400, + context.currentTime + 0.025, + ); + oscillatorNode.frequency.setValueAtTime( + 1200, + context.currentTime + 0.05, + ); + oscillatorNode.frequency.setValueAtTime( + 900, + context.currentTime + 0.075, + ); + + const lfo = context.createOscillator(); + lfo.type = 'sine'; + lfo.frequency.value = 2.0; + lfo.connect(gainNode.gain); + oscillatorNode.connect(gainNode); + gainNode.connect(context.destination); + + oscillatorNode.start(); + lfo.start(); + oscillatorNode.stop(context.currentTime + 0.3); + break; + } + + case 'PLACED_PIXELS': { + const { palette, selectedColor: color } = state.canvas; + const colorsAmount = palette.colors.length; + + const clrFreq = 100 + Math.log(color / colorsAmount + 1) * 300; + const oscillatorNode = context.createOscillator(); + const gainNode = context.createGain(); + + oscillatorNode.type = 'sine'; + oscillatorNode.frequency.setValueAtTime(clrFreq, context.currentTime); + oscillatorNode.frequency.exponentialRampToValueAtTime( + 1400, + context.currentTime + 0.2, + ); + + gainNode.gain.setValueAtTime(0.5, context.currentTime); + gainNode.gain.exponentialRampToValueAtTime( + 0.2, + context.currentTime + 0.1, + ); + + oscillatorNode.connect(gainNode); + gainNode.connect(context.destination); + + oscillatorNode.start(); + oscillatorNode.stop(context.currentTime + 0.1); + break; + } + + case 'COOLDOWN_END': { + // do not play sound if last cooldown end was <5s ago + const { lastCoolDownEnd } = state.user; + if (lastCoolDownEnd && lastCoolDownEnd.getTime() + 5000 > Date.now()) { + break; + } + + const oscillatorNode = context.createOscillator(); + const gainNode = context.createGain(); + + oscillatorNode.type = 'sine'; + oscillatorNode.frequency.setValueAtTime(349.23, context.currentTime); + oscillatorNode.frequency.setValueAtTime( + 523.25, + context.currentTime + 0.1, + ); + oscillatorNode.frequency.setValueAtTime( + 698.46, + context.currentTime + 0.2, + ); + + gainNode.gain.setValueAtTime(0.5, context.currentTime); + gainNode.gain.exponentialRampToValueAtTime( + 0.2, + context.currentTime + 0.15, + ); + + oscillatorNode.connect(gainNode); + gainNode.connect(context.destination); + + oscillatorNode.start(); + oscillatorNode.stop(context.currentTime + 0.3); + break; + } + + case 'RECEIVE_CHAT_MESSAGE': { + if (chatNotify) break; + + const { isPing, channel } = action; + const { mute: muteCh, chatChannel } = state.chatRead; + if (muteCh.includes(channel)) break; + if (muteCh.includes(`${channel}`)) break; + const { channels } = state.chat; + + const oscillatorNode = context.createOscillator(); + const gainNode = context.createGain(); + + oscillatorNode.type = 'sine'; + oscillatorNode.frequency.setValueAtTime(310, context.currentTime); + /* + * ping if user mention or + * message in DM channel that is not currently open + */ + const freq = (isPing + || ( + channels[channel] + && channels[channel][1] === 1 + // eslint-disable-next-line eqeqeq + && channel != chatChannel + ) + ) ? 540 : 355; + oscillatorNode.frequency.exponentialRampToValueAtTime( + freq, + context.currentTime + 0.025, + ); + + gainNode.gain.setValueAtTime(0.1, context.currentTime); + gainNode.gain.exponentialRampToValueAtTime( + 0.1, + context.currentTime + 0.1, + ); + + oscillatorNode.connect(gainNode); + gainNode.connect(context.destination); + + oscillatorNode.start(); + oscillatorNode.stop(context.currentTime + 0.075); + break; + } + + default: + // nothing } - - case 'RECEIVE_CHAT_MESSAGE': { - if (mute || !chatNotify) break; - - const { isPing, channel } = action; - const { mute: muteCh, chatChannel } = state.chatRead; - if (muteCh.includes(channel)) break; - if (muteCh.includes(`${channel}`)) break; - const { channels } = state.chat; - - const oscillatorNode = context.createOscillator(); - const gainNode = context.createGain(); - - oscillatorNode.type = 'sine'; - oscillatorNode.frequency.setValueAtTime(310, context.currentTime); - /* - * ping if user mention or - * message in DM channel that is not currently open - */ - const freq = (isPing - || ( - channels[channel] - && channels[channel][1] === 1 - // eslint-disable-next-line eqeqeq - && channel != chatChannel - ) - ) ? 540 : 355; - oscillatorNode.frequency.exponentialRampToValueAtTime( - freq, - context.currentTime + 0.025, - ); - - gainNode.gain.setValueAtTime(0.1, context.currentTime); - gainNode.gain.exponentialRampToValueAtTime( - 0.1, - context.currentTime + 0.1, - ); - - oscillatorNode.connect(gainNode); - gainNode.connect(context.destination); - - oscillatorNode.start(); - oscillatorNode.stop(context.currentTime + 0.075); - break; - } - - default: - // nothing } return next(action); diff --git a/src/styles/default.css b/src/styles/default.css index e10be24..f219281 100644 --- a/src/styles/default.css +++ b/src/styles/default.css @@ -57,6 +57,10 @@ a:hover { font-weight: bold; } +.warn { + color: #e53737; +} + .modallink { text-decoration: none; color: #428bca;