diff --git a/client/assets/img/pause.svg b/client/assets/img/pause.svg index c8022be1..dd60308d 100644 --- a/client/assets/img/pause.svg +++ b/client/assets/img/pause.svg @@ -1,4 +1,3 @@ - - - + + diff --git a/client/assets/img/play.svg b/client/assets/img/play.svg index 7e3a45f3..51771555 100644 --- a/client/assets/img/play.svg +++ b/client/assets/img/play.svg @@ -1,3 +1,3 @@ - - + + diff --git a/client/assets/img/volume.svg b/client/assets/img/volume.svg new file mode 100644 index 00000000..15ddb20c --- /dev/null +++ b/client/assets/img/volume.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/client/assets/img/volume_low.svg b/client/assets/img/volume_low.svg new file mode 100644 index 00000000..f9531c6c --- /dev/null +++ b/client/assets/img/volume_low.svg @@ -0,0 +1,4 @@ + + + + diff --git a/client/assets/img/volume_mute.svg b/client/assets/img/volume_mute.svg new file mode 100644 index 00000000..606b0079 --- /dev/null +++ b/client/assets/img/volume_mute.svg @@ -0,0 +1,4 @@ + + + + diff --git a/client/components/icon.js b/client/components/icon.js index 08de70c1..63ee6695 100644 --- a/client/components/icon.js +++ b/client/components/icon.js @@ -42,6 +42,10 @@ import img_stop from "../assets/img/stop.svg"; import img_refresh from "../assets/img/refresh.svg"; import img_copy from "../assets/img/copy.svg"; import img_eye from "../assets/img/eye.svg"; +import img_volume from "../assets/img/volume.svg"; +import img_volume_mute from "../assets/img/volume_mute.svg"; +import img_volume_low from "../assets/img/volume_low.svg"; + export const img_placeholder = "/assets/icons/placeholder.png"; export const Icon = (props) => { @@ -141,6 +145,12 @@ export const Icon = (props) => { img = "/assets/icons/empty_search.svg"; } else if (props.name === "eye") { img = img_eye; + } else if (props.name === "volume") { + img = img_volume; + } else if (props.name === "volume_mute") { + img = img_volume_mute; + } else if (props.name === "volume_low") { + img = img_volume_low; } else { throw (new Error(`unknown icon: "${props.name}"`)); } diff --git a/client/pages/viewerpage/audioplayer.js b/client/pages/viewerpage/audioplayer.js index 816a3658..d1507824 100644 --- a/client/pages/viewerpage/audioplayer.js +++ b/client/pages/viewerpage/audioplayer.js @@ -3,11 +3,14 @@ import WaveSurfer from "wavesurfer.js"; import { MenuBar } from "./menubar"; import { NgIf, Icon } from "../../components/"; +import { settings_get, settings_put } from "../../helpers/"; import "./audioplayer.scss"; export function AudioPlayer({ filename, data }) { const [isPlaying, setIsPlaying] = useState(false); const [isLoading, setIsLoading] = useState(true); + const [purcentLoading, setPurcentLoading] = useState(0); + const [volume, setVolume] = useState(settings_get("volume") === null ? 50 : settings_get("volume")); const [error, setError] = useState(null); const wavesurfer = useRef(null); @@ -15,19 +18,30 @@ export function AudioPlayer({ filename, data }) { wavesurfer.current = WaveSurfer.create({ container: "#waveform", waveColor: "#323639", - progressColor: "#6f6f6f", - cursorColor: "#323639", - cursorWidth: 2, - height: 250, + progressColor: "#808080", + cursorColor: "#6f6f6f", + cursorWidth: 3, + height: 200, barWidth: 1, }); wavesurfer.current.load(data); + + let $currentTime = document.getElementById("currentTime"); + let $totalDuration = document.getElementById("totalDuration"); wavesurfer.current.on("ready", () => { setIsLoading(false); + wavesurfer.current.setVolume(volume / 100); + $totalDuration.innerHTML = formatTimecode(wavesurfer.current.getDuration()); + }); + wavesurfer.current.on("audioprocess", () => { + $currentTime.innerHTML = formatTimecode(wavesurfer.current.getCurrentTime()); + }) + wavesurfer.current.on("loading", (n) => { + setPurcentLoading(n); }); wavesurfer.current.on("error", (err) => { setIsLoading(false); - setError(err) + setError(err); }); return () => wavesurfer.current.destroy(); }, []); @@ -50,14 +64,26 @@ export function AudioPlayer({ filename, data }) { e.stopPropagation(); wavesurfer.current.play(); setIsPlaying(true); - } + }; const onPause = (e) => { e.preventDefault(); e.stopPropagation(); wavesurfer.current.pause(); setIsPlaying(false); - } + }; + + const onVolumeChange = (e) => { + const v = Number(e.target.value); + settings_put("volume", v); + setVolume(v); + wavesurfer.current.setVolume(v / 100); + }; + const onVolumeClick = () => { + onVolumeChange({ target: { value: 0 }}); + }; + + const formatTimecode = (seconds) => (new Date(seconds * 1000).toISOString().substr(11, 8)); return (
@@ -67,25 +93,36 @@ export function AudioPlayer({ filename, data }) { {error} - - - -
-
- - - - - - - - - - -
+
+ +
+ + {purcentLoading}% +
+
+
+ { + isPlaying ? ( + + + + ) : ( + + + + ) + } + + +
+ +
+ 00:00:00 + / + 00:00:00 +
+
diff --git a/client/pages/viewerpage/audioplayer.scss b/client/pages/viewerpage/audioplayer.scss index 8693eb93..ee960d6c 100644 --- a/client/pages/viewerpage/audioplayer.scss +++ b/client/pages/viewerpage/audioplayer.scss @@ -9,8 +9,6 @@ flex-direction: column; flex: 1; width: 100%; - - text-align: center; background: #525659; height: 100%; @@ -26,21 +24,81 @@ background: #f1f1f1; box-shadow: rgba(0, 0, 0, 0.14) 0px 4px 5px 0px, rgba(0, 0, 0, 0.12) 0px 1px 10px 0px, rgba(0, 0, 0, 0.2) 0px 2px 4px -1px; position: relative; + border-radius: 3px; + padding: 10px 0 30px 0; .audioplayer_control { - position: absolute; - top: 10px; - right: 10px; - z-index: 2; - height: 30px; - width: 30px; + padding-top: 20px; + img { + height: 25px; + width: 25px; + cursor: pointer; + } - > div { - display: inline; + .buttons { + float: left; + padding-left: 15px; + margin-top: -10px; + display: flex; > span { + margin-right: 10px; + } + input[type="range"] { + margin-left: -5px; cursor: pointer; + width: 100%; + outline: none; + -webkit-appearance: none; + background: transparent; + + &::-webkit-slider-thumb { + -webkit-appearance: none; + height: 15px; + width: 15px; + border: none; + border-radius: 50%; + background: #6f6f6f; + margin-top: -6px; + } + &::-webkit-slider-runnable-track { + width: 100%; + height: 3px; + background-color: #6f6f6f; + border-radius: 2px; + } } } + + .timecode { + float: right; + padding-right: 25px; + margin-top: -5px; + #separator { + padding: 0 5px; + } + @media screen and (max-width: 450px) { + #separator, #currentTime { display: none; } + } + } + } + + .audioplayer_loader { + height: 260px; + background: var(--color); + position: absolute; + opacity: 0.1; + top: 0; + left: 0; + } + .percent { + position: absolute; + margin: 100px 0px; + width: 120px; + font-size: 1.4rem; + } + .component_icon[alt="loading"] { + position: absolute; + margin: 50px 0px } } } diff --git a/webpack.config.js b/webpack.config.js index 115127a1..1e908d11 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -69,6 +69,7 @@ const config = { { from: "locales/*.json", to: "assets/" }, { from: "worker/*.js", to: "assets/" }, { from: "assets/logo/*" }, + { from: "assets/img/*" }, { from: "assets/icons/*" }, { from: "assets/fonts/*" }, ], { context: path.join(__dirname, "client") }),