diff --git a/package.json b/package.json
index 4f53754f..ad47603a 100644
--- a/package.json
+++ b/package.json
@@ -21,9 +21,11 @@
"react": "^16.13.1",
"react-datepicker": "^3.1.3",
"react-dom": "^16.13.1",
+ "react-player": "^2.6.0",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
"react-scripts": "3.4.1",
+ "screenfull": "^5.0.2",
"styled-components": "^5.1.1"
},
"devDependencies": {
diff --git a/public/images/player-fullscreen-off.svg b/public/images/player-fullscreen-off.svg
new file mode 100644
index 00000000..91bb6bdf
--- /dev/null
+++ b/public/images/player-fullscreen-off.svg
@@ -0,0 +1,6 @@
+
diff --git a/public/images/player-fullscreen-on.svg b/public/images/player-fullscreen-on.svg
new file mode 100644
index 00000000..53315974
--- /dev/null
+++ b/public/images/player-fullscreen-on.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/images/player-pause.svg b/public/images/player-pause.svg
new file mode 100644
index 00000000..806217ed
--- /dev/null
+++ b/public/images/player-pause.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/images/player-play.svg b/public/images/player-play.svg
new file mode 100644
index 00000000..8ba6d772
--- /dev/null
+++ b/public/images/player-play.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/images/player-prev.svg b/public/images/player-prev.svg
new file mode 100644
index 00000000..88b896d3
--- /dev/null
+++ b/public/images/player-prev.svg
@@ -0,0 +1,5 @@
+
diff --git a/public/images/player-settings.svg b/public/images/player-settings.svg
new file mode 100644
index 00000000..4bbcfc2a
--- /dev/null
+++ b/public/images/player-settings.svg
@@ -0,0 +1,8 @@
+
diff --git a/public/images/player-volume-off.svg b/public/images/player-volume-off.svg
new file mode 100644
index 00000000..fd03f7bc
--- /dev/null
+++ b/public/images/player-volume-off.svg
@@ -0,0 +1,5 @@
+
diff --git a/public/images/player-volume-on.svg b/public/images/player-volume-on.svg
new file mode 100644
index 00000000..a3fb34bd
--- /dev/null
+++ b/public/images/player-volume-on.svg
@@ -0,0 +1,5 @@
+
diff --git a/src/features/MatchPage/index.tsx b/src/features/MatchPage/index.tsx
index 919190e9..971edfc6 100644
--- a/src/features/MatchPage/index.tsx
+++ b/src/features/MatchPage/index.tsx
@@ -1,13 +1,17 @@
import React from 'react'
+import { VideoPlayer } from 'features/VideoPlayer'
+
import { MatchProfileCard } from './MatchProfileCard'
+import { MainWrapper, Container } from './styled'
-import {
- MainWrapper,
-} from './styled'
+const url = 'https://bserv.instatfootball.tv/common/outhls.m3u8'
export const MatchPage = () => (
+
+
+
)
diff --git a/src/features/MatchPage/styled.tsx b/src/features/MatchPage/styled.tsx
index 75cdf835..8c42984b 100644
--- a/src/features/MatchPage/styled.tsx
+++ b/src/features/MatchPage/styled.tsx
@@ -1,6 +1,13 @@
import styled from 'styled-components/macro'
export const MainWrapper = styled.div`
- margin-left: 18px;
- margin-top: 63px;
+ margin: 63px 16px 0 16px;
+`
+
+export const Container = styled.div`
+ max-width: 1504px;
+ max-height: 896px;
+ margin-top: 14px;
+ display: flex;
+ flex-direction: column;
`
diff --git a/src/features/Menu/styled.tsx b/src/features/Menu/styled.tsx
index 11b6e771..e5a54910 100644
--- a/src/features/Menu/styled.tsx
+++ b/src/features/Menu/styled.tsx
@@ -31,6 +31,7 @@ export const MenuList = styled.ul`
border-radius: 2px;
background-color: #666;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
+ z-index: 1;
:before {
content: '';
diff --git a/src/features/VideoPlayer/components/ProgressBar/index.tsx b/src/features/VideoPlayer/components/ProgressBar/index.tsx
new file mode 100644
index 00000000..1bc9e6c7
--- /dev/null
+++ b/src/features/VideoPlayer/components/ProgressBar/index.tsx
@@ -0,0 +1,39 @@
+import React from 'react'
+
+import { secondsToHms } from 'helpers'
+
+import { useSlider } from 'features/VideoPlayer/hooks/useSlider'
+
+import { TimeTooltip } from '../TimeTooltip'
+import {
+ ProgressBarList,
+ LoadedProgress,
+ PlayedProgress,
+ Scrubber,
+} from './styled'
+
+type Props = {
+ loadedProgress: number,
+ onPlayedProgressChange: (progress: number) => void,
+ playedProgress: number,
+ playedSeconds: number,
+}
+
+export const ProgressBar = ({
+ loadedProgress,
+ onPlayedProgressChange,
+ playedProgress,
+ playedSeconds,
+}: Props) => {
+ const progressBarRef = useSlider({ onChange: onPlayedProgressChange })
+
+ return (
+
+
+
+
+
+
+
+ )
+}
diff --git a/src/features/VideoPlayer/components/ProgressBar/styled.tsx b/src/features/VideoPlayer/components/ProgressBar/styled.tsx
new file mode 100644
index 00000000..d43ab47c
--- /dev/null
+++ b/src/features/VideoPlayer/components/ProgressBar/styled.tsx
@@ -0,0 +1,44 @@
+import styled from 'styled-components/macro'
+
+export const ProgressBarList = styled.div`
+ flex-grow: 1;
+ height: 4px;
+ position: relative;
+ background-color: rgba(255, 255, 255, 0.3);
+ cursor: pointer;
+`
+
+type ProgressProps = {
+ value: number,
+}
+
+export const LoadedProgress = styled.div`
+ position: absolute;
+ z-index: 1;
+ background-color: rgba(255, 255, 255, 0.6);;
+ height: 100%;
+ width: ${({ value }) => value}%;
+`
+
+export const PlayedProgress = styled.div`
+ position: absolute;
+ z-index: 2;
+ background-color: #F2C94C;
+ height: 100%;
+ width: ${({ value }) => value}%;
+`
+
+export const Scrubber = styled.button`
+ border: none;
+ outline: none;
+ position: absolute;
+ top: 0;
+ left: ${({ value }) => value}%;
+ transform: translate(-50%, -38%);
+ z-index: 3;
+ width: 18px;
+ height: 18px;
+ background-color: #F2C94C;
+ border-radius: 50%;
+ cursor: pointer;
+`
diff --git a/src/features/VideoPlayer/components/TimeTooltip/index.tsx b/src/features/VideoPlayer/components/TimeTooltip/index.tsx
new file mode 100644
index 00000000..3738043a
--- /dev/null
+++ b/src/features/VideoPlayer/components/TimeTooltip/index.tsx
@@ -0,0 +1,13 @@
+import React from 'react'
+
+import { Wrapper, Time } from './styled'
+
+type Props = {
+ time: string,
+}
+
+export const TimeTooltip = ({ time }: Props) => (
+
+
+
+)
diff --git a/src/features/VideoPlayer/components/TimeTooltip/styled.tsx b/src/features/VideoPlayer/components/TimeTooltip/styled.tsx
new file mode 100644
index 00000000..893cd5c3
--- /dev/null
+++ b/src/features/VideoPlayer/components/TimeTooltip/styled.tsx
@@ -0,0 +1,33 @@
+import styled from 'styled-components/macro'
+
+export const Wrapper = styled.div`
+ position: absolute;
+ bottom: 100%;
+ transform: translate(-50%, -50%);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background-color: #000;
+ min-width: 60px;
+ height: 32px;
+
+ ::after {
+ content: '';
+ position: absolute;
+ bottom: 0;
+ left: 50%;
+ width: 9px;
+ height: 9px;
+ transform: rotate(45deg) translateY(50%);
+ background-color: #000;
+ }
+`
+
+export const Time = styled.span`
+ padding: 0 6px;
+ color: #F2C94C;
+ text-align: center;
+ font-weight: bold;
+ font-size: 16px;
+ line-height: 20px;
+`
diff --git a/src/features/VideoPlayer/components/VolumeBar/index.tsx b/src/features/VideoPlayer/components/VolumeBar/index.tsx
new file mode 100644
index 00000000..51d2806a
--- /dev/null
+++ b/src/features/VideoPlayer/components/VolumeBar/index.tsx
@@ -0,0 +1,36 @@
+import React from 'react'
+
+import { useSlider } from 'features/VideoPlayer/hooks/useSlider'
+
+import {
+ Wrapper,
+ VolumeButton,
+ VolumeProgressList,
+ VolumeProgress,
+ Scrubber,
+} from './styled'
+
+type Props = {
+ muted: boolean,
+ onChange: (progress: number) => void,
+ onClick: () => void,
+ value: number,
+}
+
+export const VolumeBar = ({
+ muted,
+ onChange,
+ onClick,
+ value,
+}: Props) => {
+ const progressRef = useSlider({ onChange })
+ return (
+
+
+
+
+
+
+
+ )
+}
diff --git a/src/features/VideoPlayer/components/VolumeBar/styled.tsx b/src/features/VideoPlayer/components/VolumeBar/styled.tsx
new file mode 100644
index 00000000..784a02f8
--- /dev/null
+++ b/src/features/VideoPlayer/components/VolumeBar/styled.tsx
@@ -0,0 +1,61 @@
+import styled from 'styled-components/macro'
+
+export const Wrapper = styled.div`
+ display: flex;
+ align-items: center;
+ margin-right: 18px;
+`
+
+type VolumeButtonProps = {
+ muted: boolean,
+}
+
+export const VolumeButton = styled.button`
+ outline: none;
+ border: none;
+ padding: 0;
+ width: 40px;
+ height: 26px;
+ margin-right: 9px;
+ cursor: pointer;
+ background-repeat: no-repeat;
+ background-position: center;
+ background-size: cover;
+ background-color: transparent;
+ background-image: ${({ muted }) => (
+ muted
+ ? 'url(/images/player-volume-off.svg)'
+ : 'url(/images/player-volume-on.svg)'
+ )};
+`
+
+export const VolumeProgressList = styled.div`
+ width: 77px;
+ height: 4px;
+ position: relative;
+ background-color: rgba(255, 255, 255, 0.3);
+ cursor: pointer;
+`
+
+type VolumeProgressProps = {
+ value: number,
+}
+
+export const VolumeProgress = styled.div`
+ height: 100%;
+ width: ${({ value }) => value}%;
+ background-color: #fff;
+`
+
+export const Scrubber = styled.button`
+ border: none;
+ outline: none;
+ width: 13px;
+ height: 13px;
+ left: ${({ value }) => value}%;;
+ position: absolute;
+ border-radius: 50%;
+ background-color: #fff;
+ transform: translate(-50%, -65%);
+ cursor: pointer;
+`
diff --git a/src/features/VideoPlayer/config.tsx b/src/features/VideoPlayer/config.tsx
new file mode 100644
index 00000000..490efda1
--- /dev/null
+++ b/src/features/VideoPlayer/config.tsx
@@ -0,0 +1,12 @@
+export const playerConfig = {
+ file: {
+ forceHLS: true,
+ hlsOptions: {
+ liveSyncDuration: 10,
+ maxBufferLength: 10,
+ maxBufferSize: 0,
+ },
+ },
+}
+
+export const progressCallbackInterval = 100
diff --git a/src/features/VideoPlayer/hooks/index.tsx b/src/features/VideoPlayer/hooks/index.tsx
new file mode 100644
index 00000000..1ae88352
--- /dev/null
+++ b/src/features/VideoPlayer/hooks/index.tsx
@@ -0,0 +1,91 @@
+import type { MouseEvent } from 'react'
+import {
+ useCallback,
+ useState,
+ useRef,
+} from 'react'
+import ReactPlayer from 'react-player'
+
+import throttle from 'lodash/throttle'
+
+import { useFullscreen } from './useFullscreen'
+import { useVolume } from './useVolume'
+
+type ProgressState = {
+ loaded: number,
+ loadedSeconds: number,
+ played: number,
+ playedSeconds: number,
+}
+
+export const useVideoPlayer = () => {
+ const [ready, setReady] = useState(false)
+ const [playing, setPlaying] = useState(false)
+ const [duration, setDuration] = useState(0)
+ const [loadedProgress, setLoadedProgress] = useState(0)
+ const [playedProgress, setPlayedProgress] = useState(0)
+ const [playedSeconds, setPlayedSeconds] = useState(0)
+ const wrapperRef = useRef(null)
+ const playerRef = useRef(null)
+
+ const startPlaying = () => {
+ setReady(true)
+ setPlaying(true)
+ }
+
+ const togglePlaying = () => {
+ if (ready) {
+ setPlaying(!playing)
+ }
+ }
+
+ const onPlayerClick = (e: MouseEvent) => {
+ if (e.target === playerRef.current?.getInternalPlayer()) {
+ togglePlaying()
+ }
+ }
+
+ const setPlayerProgress = useCallback(
+ throttle((value: number) => {
+ playerRef.current?.seekTo(value, 'fraction')
+ }, 100),
+ [],
+ )
+
+ const onProgressChange = useCallback((progress: number) => {
+ setPlayedProgress(progress * 100)
+ setPlayerProgress(progress)
+ }, [
+ setPlayedProgress,
+ setPlayerProgress,
+ ])
+
+ const setProgress = ({
+ loaded,
+ played,
+ playedSeconds: seconds,
+ }: ProgressState) => {
+ setLoadedProgress(loaded * 100)
+ setPlayedProgress(played * 100)
+ setPlayedSeconds(seconds)
+ }
+
+ return {
+ duration,
+ loadedProgress,
+ onPlayerClick,
+ onProgressChange,
+ playedProgress,
+ playedSeconds,
+ playerRef,
+ playing,
+ setDuration,
+ setProgress,
+ setReady,
+ startPlaying,
+ togglePlaying,
+ wrapperRef,
+ ...useFullscreen(wrapperRef),
+ ...useVolume(),
+ }
+}
diff --git a/src/features/VideoPlayer/hooks/useFullscreen.tsx b/src/features/VideoPlayer/hooks/useFullscreen.tsx
new file mode 100644
index 00000000..cb8004ed
--- /dev/null
+++ b/src/features/VideoPlayer/hooks/useFullscreen.tsx
@@ -0,0 +1,36 @@
+import type { RefObject } from 'react'
+import { useEffect } from 'react'
+import screenfull from 'screenfull'
+
+import noop from 'lodash/noop'
+
+import { useToggle } from 'hooks'
+
+export const useFullscreen = (wrapperRef: RefObject) => {
+ const {
+ isOpen: isFullscreen,
+ setIsOpen: setFullscreen,
+ } = useToggle()
+
+ useEffect(() => {
+ if (!screenfull.isEnabled) return noop
+ const changeListener = () => {
+ setFullscreen(screenfull.isEnabled && screenfull.isFullscreen)
+ }
+ screenfull.onchange(changeListener)
+
+ return () => {
+ if (screenfull.isEnabled) {
+ screenfull.off('change', changeListener)
+ }
+ }
+ }, [setFullscreen])
+
+ const onFullscreenClick = () => {
+ if (screenfull.isEnabled && wrapperRef.current) {
+ screenfull.toggle(wrapperRef.current)
+ }
+ }
+
+ return { isFullscreen, onFullscreenClick }
+}
diff --git a/src/features/VideoPlayer/hooks/useSlider.tsx b/src/features/VideoPlayer/hooks/useSlider.tsx
new file mode 100644
index 00000000..3598fc72
--- /dev/null
+++ b/src/features/VideoPlayer/hooks/useSlider.tsx
@@ -0,0 +1,81 @@
+import {
+ useCallback,
+ useEffect,
+ useRef,
+} from 'react'
+
+const getNormalizedProgress = (fraction: number) => {
+ if (fraction > 1) return 1
+ if (fraction < 0) return 0
+ return fraction
+}
+
+const useMouseState = () => {
+ const mouseDownRef = useRef(false)
+ const setMouseDown = useCallback(() => {
+ mouseDownRef.current = true
+ }, [])
+ const setMouseUp = useCallback(() => {
+ mouseDownRef.current = false
+ }, [])
+
+ return {
+ mouseDownRef,
+ setMouseDown,
+ setMouseUp,
+ }
+}
+
+type Args = {
+ onChange: (progress: number) => void,
+}
+
+export const useSlider = ({ onChange }: Args) => {
+ const ref = useRef(null)
+ const {
+ mouseDownRef,
+ setMouseDown,
+ setMouseUp,
+ } = useMouseState()
+
+ useEffect(() => {
+ const handleProgress = (mouseX: number) => {
+ const track = ref.current
+ if (!mouseDownRef.current || !track) return
+
+ const x = mouseX - track.getBoundingClientRect().left
+ const progress = getNormalizedProgress(x / track.offsetWidth)
+ onChange(progress)
+ }
+
+ const mouseDownListener = ({ clientX, target }: MouseEvent) => {
+ const track = ref.current
+ if (target === track || track?.contains(target as Node)) {
+ setMouseDown()
+ handleProgress(clientX)
+ }
+ }
+
+ const mouseUpListener = setMouseUp
+
+ const mouseMoveListener = (e: MouseEvent) => {
+ handleProgress(e.clientX)
+ }
+
+ window.addEventListener('mousedown', mouseDownListener)
+ window.addEventListener('mouseup', mouseUpListener)
+ window.addEventListener('mousemove', mouseMoveListener)
+ return () => {
+ window.removeEventListener('mousedown', mouseDownListener)
+ window.removeEventListener('mouseup', mouseUpListener)
+ window.removeEventListener('mousemove', mouseMoveListener)
+ }
+ }, [
+ onChange,
+ mouseDownRef,
+ setMouseDown,
+ setMouseUp,
+ ])
+
+ return ref
+}
diff --git a/src/features/VideoPlayer/hooks/useVolume.tsx b/src/features/VideoPlayer/hooks/useVolume.tsx
new file mode 100644
index 00000000..9d3802bf
--- /dev/null
+++ b/src/features/VideoPlayer/hooks/useVolume.tsx
@@ -0,0 +1,36 @@
+import { useState } from 'react'
+
+import { useToggle } from 'hooks'
+
+export const useVolume = () => {
+ const {
+ close: unmute,
+ isOpen: muted,
+ open: mute,
+ toggle: toggleMuted,
+ } = useToggle()
+ const [volume, setVolume] = useState(0.5)
+
+ const onVolumeChange = (value: number) => {
+ if (value === 0) {
+ mute()
+ } else {
+ unmute()
+ }
+ setVolume(value)
+ }
+
+ const onVolumeClick = () => {
+ if (muted && volume === 0) {
+ setVolume(0.1)
+ }
+ toggleMuted()
+ }
+
+ return {
+ muted,
+ onVolumeChange,
+ onVolumeClick,
+ volume,
+ }
+}
diff --git a/src/features/VideoPlayer/index.tsx b/src/features/VideoPlayer/index.tsx
new file mode 100644
index 00000000..7d2cd07a
--- /dev/null
+++ b/src/features/VideoPlayer/index.tsx
@@ -0,0 +1,87 @@
+import React from 'react'
+
+import { playerConfig, progressCallbackInterval } from './config'
+import { useVideoPlayer } from './hooks'
+import { ProgressBar } from './components/ProgressBar'
+import { VolumeBar } from './components/VolumeBar'
+import {
+ PlayerWrapper,
+ ReactPlayer,
+ Controls,
+ Prev,
+ Next,
+ PlayStop,
+ Fullscreen,
+ Settings,
+} from './styled'
+
+type Props = {
+ url: string,
+}
+
+export const VideoPlayer = ({ url }: Props) => {
+ const {
+ isFullscreen,
+ loadedProgress,
+ muted,
+ onFullscreenClick,
+ onPlayerClick,
+ onProgressChange,
+ onVolumeChange,
+ onVolumeClick,
+ playedProgress,
+ playedSeconds,
+ playerRef,
+ playing,
+ setDuration,
+ setProgress,
+ startPlaying,
+ togglePlaying,
+ volume,
+ wrapperRef,
+ } = useVideoPlayer()
+
+ const volumeInPercent = volume * 100
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/src/features/VideoPlayer/styled.tsx b/src/features/VideoPlayer/styled.tsx
new file mode 100644
index 00000000..6d5490e4
--- /dev/null
+++ b/src/features/VideoPlayer/styled.tsx
@@ -0,0 +1,106 @@
+import styled from 'styled-components/macro'
+import ReactPlayerBase from 'react-player'
+
+export const ReactPlayer = styled(ReactPlayerBase)`
+ position: absolute;
+ top: 0;
+ left: 0;
+`
+
+export const Controls = styled.div`
+ position: absolute;
+ width: 100%;
+ bottom: 22px;
+ display: flex;
+ align-items: center;
+ padding: 0 25px;
+ transition: opacity 0.3s ease-in-out;
+ filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.25));
+`
+
+export const PlayerWrapper = styled.div`
+ position: relative;
+ width: 100%;
+ height: 0px;
+ padding-top: 56.25%;
+ background-color: #000;
+
+ :hover ${Controls} {
+ opacity: 1;
+ }
+
+ ${Controls} {
+ opacity: ${({ playing }) => (
+ playing
+ ? '0'
+ : '1'
+ )};
+ }
+`
+
+const ButtonBase = styled.button`
+ outline: none;
+ border: none;
+ color: #fff;
+ cursor: pointer;
+ background-color: transparent;
+ background-repeat: no-repeat;
+ background-position: center;
+ background-size: contain;
+`
+
+type PlayStopProps = {
+ playing: boolean,
+}
+
+export const PlayStop = styled(ButtonBase)`
+ width: 24px;
+ height: 24px;
+ margin-right: 18px;
+ background-image: ${({ playing }) => (
+ playing
+ ? 'url(/images/player-pause.svg)'
+ : 'url(/images/player-play.svg)'
+ )};
+`
+
+export const Prev = styled(ButtonBase)`
+ width: 29px;
+ height: 28px;
+ margin-right: 19px;
+ background-image: url(/images/player-prev.svg);
+`
+
+export const Next = styled(Prev)`
+ margin-right: 24px;
+ transform: rotate(180deg);
+`
+
+export const Volume = styled(ButtonBase)`
+ width: 40px;
+ height: 26px;
+ margin-right: 23px;
+ background-image: url(/images/player-volume-off.svg);
+`
+
+type FullscreenProps = {
+ isFullscreen: boolean,
+}
+
+export const Fullscreen = styled(ButtonBase)`
+ width: 22px;
+ height: 20px;
+ margin-left: 31px;
+ margin-right: 23px;
+ background-image: ${({ isFullscreen }) => (
+ isFullscreen
+ ? 'url(/images/player-fullscreen-off.svg)'
+ : 'url(/images/player-fullscreen-on.svg)'
+ )};
+`
+
+export const Settings = styled(ButtonBase)`
+ width: 22px;
+ height: 20px;
+ background-image: url(/images/player-settings.svg);
+`
diff --git a/src/helpers/index.tsx b/src/helpers/index.tsx
index dcebcabb..2254fefc 100644
--- a/src/helpers/index.tsx
+++ b/src/helpers/index.tsx
@@ -8,3 +8,4 @@ export * from './getSportColor'
export * from './getSportLexic'
export * from './handleImg'
export * from './msToMinutesAndSeconds'
+export * from './secondsToHms'
diff --git a/src/helpers/secondsToHms/__tests__/index.tsx b/src/helpers/secondsToHms/__tests__/index.tsx
new file mode 100644
index 00000000..108e4f6e
--- /dev/null
+++ b/src/helpers/secondsToHms/__tests__/index.tsx
@@ -0,0 +1,11 @@
+import { secondsToHms } from '..'
+
+describe('secondsToHms helper', () => {
+ it('returns correct formatted time', () => {
+ expect(secondsToHms(0)).toBe('00:00')
+ expect(secondsToHms(120)).toBe('02:00')
+ expect(secondsToHms(600)).toBe('10:00')
+ expect(secondsToHms(3600)).toBe('01:00:00')
+ expect(secondsToHms(3700)).toBe('01:01:40')
+ })
+})
diff --git a/src/helpers/secondsToHms/index.tsx b/src/helpers/secondsToHms/index.tsx
new file mode 100644
index 00000000..a18e654b
--- /dev/null
+++ b/src/helpers/secondsToHms/index.tsx
@@ -0,0 +1,16 @@
+const prependZero = (value: number | string) => (
+ `0${value}`.slice(-2)
+)
+
+/**
+ * Форматирует секунды в hh:mm:ss
+ */
+export const secondsToHms = (seconds: number) => {
+ const hours = prependZero(Math.floor(seconds / 3600))
+ const minutes = prependZero(Math.floor(seconds % 3600 / 60))
+ const secondsStr = prependZero(Math.floor(seconds % 3600 % 60))
+
+ if (Number(hours) > 0) return `${hours}:${minutes}:${secondsStr}`
+
+ return `${minutes}:${secondsStr}`
+}
diff --git a/src/hooks/useToggle.tsx b/src/hooks/useToggle.tsx
index a2827d0f..f05f3d84 100644
--- a/src/hooks/useToggle.tsx
+++ b/src/hooks/useToggle.tsx
@@ -9,6 +9,7 @@ export const useToggle = () => {
close,
isOpen,
open,
+ setIsOpen,
toggle,
}
}