Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Project in general #23

Open
wants to merge 6 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 3 additions & 159 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,140 +1,16 @@
import { useEffect } from 'react'
import { useAtom, useAtomValue, useSetAtom } from 'jotai'
import { useResetAtom } from 'jotai/utils'

import usePlayer from './hooks/usePlayer'
import PlayerControl from './components/PlayerControl'

import {
playerSourceAtom,
playerVolumeAtom,
playerMutedAtom,
} from './atoms/player'
import {
audioMomentsAtom,
generateRandomAudioMomentsAtom,
removeActualAudioMomentAtom,
audioMomentShouldUnpauseAtom,
audioMomentShouldPlayAtom,
} from './atoms/audioMoments'
import {
timerIsRunningAtom,
startTimerAtom,
pauseTimerAtom,
resetTimerAtom,
timerCanResetAtom,
timeTickingEffect,
} from './atoms/timer'
import { useTranslation } from 'react-i18next'

import cn from './lib/cn'

import { MediaPlayer, MediaProvider } from '@vidstack/react'
import '@vidstack/react/player/styles/base.css'

import {
AudioOrVideoSourceInput,
Button,
StartOrPauseTimerButton,
Timer,
VolumeControl,
} from './components'

import { useTranslation } from 'react-i18next'

import { FaGithub } from 'react-icons/fa'

export default function App() {
const {
player,
playerPaused,
playerCurrentTime,
playerDuration,
playerCanPlay,
resumePlayer,
pausePlayer,
resetPlayerCurrentTime,
resetPlayer,
} = usePlayer()

const [playerSource, setPlayerSource] = useAtom(playerSourceAtom)
const playerVolume = useAtomValue(playerVolumeAtom)
const playerMuted = useAtomValue(playerMutedAtom)

const audioMoments = useAtomValue(audioMomentsAtom)
const generateRandomAudioMoments = useSetAtom(generateRandomAudioMomentsAtom)
const removeActualAudioMoment = useSetAtom(removeActualAudioMomentAtom)
const resetAudioMoments = useResetAtom(audioMomentsAtom)
const [audioMomentShouldUnpause, setAudioMomentShouldUnpause] = useAtom(
audioMomentShouldUnpauseAtom,
)
const audioMomentShouldPlay = useAtomValue(audioMomentShouldPlayAtom)

const timerIsRunning = useAtomValue(timerIsRunningAtom)
const startTimer = useSetAtom(startTimerAtom)
const pauseTimer = useSetAtom(pauseTimerAtom)
const resetTimer = useSetAtom(resetTimerAtom)
const timerCanReset = useAtomValue(timerCanResetAtom)
useAtom(timeTickingEffect)

const { t, i18n } = useTranslation('', { keyPrefix: 'app' })

function handleAudioOrVideoSourceInputChange(input: string | File): void {
resetTimer()
if (playerSource !== '') {
resetAudioMoments()
pausePlayer()
resetPlayerCurrentTime()
}

setPlayerSource(input)
}

function handleStartTimer(): void {
startTimer()
if (playerPaused && audioMomentShouldUnpause) {
resumePlayer()
setAudioMomentShouldUnpause(false)
}
}

function handlePauseTimer(): void {
pauseTimer()
if (!playerPaused) {
pausePlayer()
setAudioMomentShouldUnpause(true)
}
}

function handleStartOrPauseTimerButtonClick(): void {
if (!audioMoments) generateRandomAudioMoments(playerDuration)

if (timerIsRunning) {
handlePauseTimer()
return
}

handleStartTimer()
}

function handleResetTimerButtonClick(): void {
resetTimer()

if (playerCurrentTime > 0) {
resetPlayer()
resetAudioMoments()
}
}

useEffect(() => {
function handleAudioMoments() {
if (audioMomentShouldPlay) {
resumePlayer()
removeActualAudioMoment()
}
}

handleAudioMoments()
}, [audioMomentShouldPlay, resumePlayer, removeActualAudioMoment])

useEffect(() => {
document.title = t('pageTitle')
document.documentElement.lang = i18n.language
Expand All @@ -155,39 +31,7 @@ export default function App() {
>
{t('title')}
</h1>
<section className='flex flex-col items-center gap-12'>
<Timer className='mb-10' />
<VolumeControl />
<AudioOrVideoSourceInput
onChange={handleAudioOrVideoSourceInputChange}
/>
<div className='flex gap-4'>
<StartOrPauseTimerButton
disabled={!playerCanPlay}
onClick={handleStartOrPauseTimerButtonClick}
/>
<Button
className={cn(
'bg-red-500 hover:bg-red-600 disabled:hover:bg-red-500',
)}
disabled={!timerCanReset}
onClick={handleResetTimerButtonClick}
>
{t('resetButton')}
</Button>
</div>
</section>
<div className='absolute bottom-0 -z-50 opacity-0'>
<MediaPlayer
src={playerSource}
ref={player}
volume={playerVolume}
muted={playerMuted}
onEnd={resetPlayer}
>
<MediaProvider />
</MediaPlayer>
</div>
<PlayerControl />
<a
className='absolute right-5 top-4'
target='_blank'
Expand Down
7 changes: 6 additions & 1 deletion src/atoms/player.ts → src/atoms/player/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import type { RefObject } from 'react'

import { atom } from 'jotai'

import type { MediaPlayerInstance } from '@vidstack/react'

const volumeAtFifthPercent = 0.5

const playerAtom = atom<RefObject<MediaPlayerInstance> | undefined>(undefined)
const playerSourceAtom = atom<string | File>('')
const playerMutedAtom = atom<boolean>(false)
const playerVolumeAtom = atom(
Expand All @@ -19,4 +24,4 @@ const playerVolumeAtom = atom(
},
)

export { playerSourceAtom, playerMutedAtom, playerVolumeAtom }
export { playerAtom, playerSourceAtom, playerMutedAtom, playerVolumeAtom }
34 changes: 34 additions & 0 deletions src/atoms/player/remote.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { atom } from 'jotai'

import { playerAtom } from '.'

import { type MediaRemoteControl, useMediaRemote } from '@vidstack/react'

const remoteAtom = atom<MediaRemoteControl>((get) =>
useMediaRemote(get(playerAtom)),
)

const resumePlayerAtom = atom(null, (get) => {
const remote = get(remoteAtom)

remote.play()
})

const pausePlayerAtom = atom(null, (get) => {
const remote = get(remoteAtom)

remote.pause()
})

const resetPlayerCurrentTimeAtom = atom(null, (get) => {
const remote = get(remoteAtom)

remote.seek(0)
})

export {
remoteAtom,
resumePlayerAtom,
pausePlayerAtom,
resetPlayerCurrentTimeAtom,
}
20 changes: 20 additions & 0 deletions src/atoms/player/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { atom } from 'jotai'

import { playerAtom } from '.'

import { useMediaStore, type MediaState } from '@vidstack/react'

const storeAtom = atom<Readonly<MediaState>>((get) =>
useMediaStore(get(playerAtom)),
)
const playerPausedAtom = atom((get) => get(storeAtom).paused)
const playerDurationAtom = atom((get) => get(storeAtom).duration)
const playerCanPlayAtom = atom((get) => get(storeAtom).canPlay)
const playerCurrentTimeAtom = atom((get) => get(storeAtom).currentTime)

export {
playerPausedAtom,
playerDurationAtom,
playerCanPlayAtom,
playerCurrentTimeAtom,
}
40 changes: 40 additions & 0 deletions src/atoms/timer/formattedTimeAtom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { atom } from 'jotai'

import { timerTotalSecondsAtom } from '.'

import { ONE_HOUR_IN_SECONDS } from '../../constants'

const ONE_MINUTE_IN_SECONDS = 60

function getHoursDigit(totalSeconds: number): number {
return Math.floor(totalSeconds / ONE_HOUR_IN_SECONDS)
}

function getMinutesDigit(totalSeconds: number): number {
return Math.floor(
(totalSeconds % ONE_HOUR_IN_SECONDS) / ONE_MINUTE_IN_SECONDS,
)
}

function getSecondsDigit(totalSeconds: number): number {
return totalSeconds % ONE_MINUTE_IN_SECONDS
}

const TIMER_FORMAT_LENGTH = 2
const TIMER_FORMAT_PADDING = '0'

function formatDigit(digit: number): string {
return digit.toString().padStart(TIMER_FORMAT_LENGTH, TIMER_FORMAT_PADDING)
}

const formattedTimeAtom = atom<string>((get) => {
const timerTotalSeconds = get(timerTotalSecondsAtom)

const hoursDigit = getHoursDigit(timerTotalSeconds)
const minutesDigit = getMinutesDigit(timerTotalSeconds)
const secondsDigit = getSecondsDigit(timerTotalSeconds)

return [hoursDigit, minutesDigit, secondsDigit].map(formatDigit).join(':')
})

export default formattedTimeAtom
19 changes: 3 additions & 16 deletions src/atoms/timer.ts → src/atoms/timer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { atom } from 'jotai'
import { atomWithReset, RESET } from 'jotai/utils'
import { atomEffect } from 'jotai-effect'

import { ONE_HOUR_IN_SECONDS } from '../constants'
import formattedTimeAtom from './formattedTimeAtom'

const ONE_MINUTE_IN_SECONDS = 60
import { ONE_HOUR_IN_SECONDS } from '../../constants'

const timerTotalSecondsAtom = atomWithReset(ONE_HOUR_IN_SECONDS)
const decreaseTimerTotalSecondsAtom = atom(null, (_get, set) => {
Expand All @@ -13,17 +13,6 @@ const decreaseTimerTotalSecondsAtom = atom(null, (_get, set) => {
(previousTimerTotalSeconds) => previousTimerTotalSeconds - 1,
)
})
const timerHoursAtom = atom((get) =>
Math.floor(get(timerTotalSecondsAtom) / ONE_HOUR_IN_SECONDS),
)
const timerMinutesAtom = atom((get) =>
Math.floor(
(get(timerTotalSecondsAtom) % ONE_HOUR_IN_SECONDS) / ONE_MINUTE_IN_SECONDS,
),
)
const timerSecondsAtom = atom(
(get) => get(timerTotalSecondsAtom) % ONE_MINUTE_IN_SECONDS,
)

const timerIsRunningAtom = atom<boolean>(false)

Expand Down Expand Up @@ -55,9 +44,7 @@ const timeTickingEffect = atomEffect((get, set) => {

export {
timerTotalSecondsAtom,
timerHoursAtom,
timerMinutesAtom,
timerSecondsAtom,
formattedTimeAtom,
timerIsRunningAtom,
startTimerAtom,
pauseTimerAtom,
Expand Down
53 changes: 0 additions & 53 deletions src/components/AudioOrVideoSourceInput/FileInputButton.tsx

This file was deleted.

Loading
Loading