lodash-es#round TypeScript Examples
The following examples show how to use
lodash-es#round.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: VideoCategoryHero.tsx From atlas with GNU General Public License v3.0 | 5 votes |
VideoCategoryHero: React.FC<VideoCategoryHeroProps> = ({ header, videos }) => {
const [activeVideoIdx, setActiveVideoIdx] = useState(0)
const [videoProgress, setVideoProgress] = useState(0)
const videosLength = videos?.length || 0
const handleVideoClick = (idx: number) => {
setActiveVideoIdx(idx)
setVideoProgress(0)
}
const handleEnded = () => {
const idx = activeVideoIdx + 1 >= videosLength ? 0 : activeVideoIdx + 1
setVideoProgress(0)
setActiveVideoIdx(idx)
}
const handleTimeUpdate = (e: React.SyntheticEvent<HTMLVideoElement, Event>) => {
const currentTime = e.currentTarget.currentTime
const duration = e.currentTarget.duration
if (duration && currentTime) {
const progressInPercentage = round((currentTime / duration) * 100, 2)
setVideoProgress(progressInPercentage)
} else {
setVideoProgress(0)
}
}
const videosWithProgress = videos?.map((video, idx) =>
video ? { ...video, progress: idx === activeVideoIdx ? videoProgress : 0 } : null
)
const shouldShowSlider = videosLength > 1
const currentVideoData = videos?.[activeVideoIdx]
return (
<VideoHero
isCategory
onTimeUpdate={handleTimeUpdate}
onEnded={handleEnded}
videoHeroData={{
video: currentVideoData?.video,
heroTitle: currentVideoData?.video.title || '',
heroVideoCutUrl: currentVideoData?.videoCutUrl || '',
heroPosterUrl: null,
}}
headerNode={
!!header.title &&
!!header.icon && <VideoHeroHeader icon={header.icon} title={header.title} loading={!videos?.[activeVideoIdx]} />
}
sliderNode={
shouldShowSlider ? (
<VideoHeroSlider activeVideoIdx={activeVideoIdx} videos={videosWithProgress} onTileClick={handleVideoClick} />
) : undefined
}
/>
)
}
Example #2
Source File: CustomTimeline.tsx From atlas with GNU General Public License v3.0 | 4 votes |
CustomTimeline: React.FC<CustomTimelineProps> = ({
player,
isFullScreen,
playerState,
playVideo,
pauseVideo,
setPlayerState,
}) => {
const playProgressThumbRef = useRef<HTMLButtonElement>(null)
const playProgressRef = useRef<HTMLDivElement>(null)
const seekBarRef = useRef<HTMLDivElement>(null)
const mouseDisplayTooltipRef = useRef<HTMLDivElement>(null)
const [playProgressWidth, setPlayProgressWidth] = useState(0)
const [loadProgressWidth, setLoadProgressWidth] = useState(0)
const [mouseDisplayWidth, setMouseDisplayWidth] = useState(0)
const [mouseDisplayTooltipTime, setMouseDisplayTooltipTime] = useState('0:00')
const [mouseDisplayTooltipWidth, setMouseDisplayTooltipWidth] = useState(0)
const [playProgressThumbWidth, setPlayProgressThumbWidth] = useState(0)
const [isScrubbing, setIsScrubbing] = useState(false)
const [playedBefore, setPlayedBefore] = useState(false)
useEffect(() => {
if (!player) {
return
}
const handler = (event: Event) => {
if (event.type === 'seeking' && isScrubbing && !player.paused()) {
setPlayedBefore(true)
pauseVideo(player)
}
if (event.type === 'seeked' && !isScrubbing) {
if (playedBefore) {
playVideo(player)
setPlayedBefore(false)
}
}
}
player.on(['seeking', 'seeked'], handler)
return () => {
player.off(['seeking', 'seeked'], handler)
}
}, [isScrubbing, player, playedBefore, pauseVideo, playVideo])
useEffect(() => {
const playProgress = playProgressRef.current
const playProgressThumb = playProgressThumbRef.current
if (
!player ||
!playerState ||
playerState === 'ended' ||
playerState === 'error' ||
!playProgress ||
isScrubbing ||
!playProgressThumb
) {
return
}
const interval = window.setInterval(() => {
const duration = player.duration()
const currentTime = player.currentTime()
const buffered = player.buffered()
// set playProgress
const progressPercentage = round((currentTime / duration) * 100, 2)
setPlayProgressWidth(progressPercentage)
setPlayProgressThumbWidth(playProgressThumb.clientWidth)
if (progressPercentage === 100) {
setPlayerState('ended')
}
// get all buffered time ranges
const bufferedTimeRanges = Array.from({ length: buffered.length }).map((_, idx) => ({
bufferedStart: buffered.start(idx),
bufferedEnd: buffered.end(idx),
}))
const currentBufferedTimeRange = bufferedTimeRanges.find(
(el) => el.bufferedEnd > currentTime && el.bufferedStart < currentTime
)
if (currentBufferedTimeRange) {
const loadProgressPercentage = round((currentBufferedTimeRange.bufferedEnd / duration) * 100, 2)
setLoadProgressWidth(loadProgressPercentage)
} else {
setLoadProgressWidth(0)
}
}, UPDATE_INTERVAL)
return () => {
clearInterval(interval)
}
}, [isScrubbing, player, playerState, setPlayerState])
const handleMouseAndTouchMove = (e: React.MouseEvent | React.TouchEvent) => {
const seekBar = seekBarRef.current
const mouseDisplayTooltip = mouseDisplayTooltipRef.current
if (!seekBar || !mouseDisplayTooltip || !player) {
return
}
// this will prevent hiding controls when scrubbing on mobile
player.enableTouchActivity()
const duration = player.duration()
// position of seekBar
const { x: seekBarPosition, width: seekBarWidth } = seekBar.getBoundingClientRect()
const position = 'clientX' in e ? e.clientX - seekBarPosition : e.touches[0].clientX - seekBarPosition
const percentage = clamp(round((position / seekBarWidth) * 100, 2), 0, 100)
setMouseDisplayWidth(percentage)
setMouseDisplayTooltipWidth(mouseDisplayTooltip.clientWidth)
// tooltip text
if (duration) {
setMouseDisplayTooltipTime(formatDurationShort(round((percentage / 100) * duration)))
}
if (isScrubbing) {
setPlayProgressWidth(percentage)
}
}
const handleJumpToTime = (e: React.MouseEvent | React.TouchEvent) => {
const seekBar = seekBarRef.current
if (!seekBar || (e.type === 'mouseleave' && !isScrubbing) || !player) {
return
}
const { x: seekBarPosition, width: seekBarWidth } = seekBar.getBoundingClientRect()
const mouseOrTouchPosition =
'clientX' in e ? e.clientX - seekBarPosition : e.changedTouches[0].clientX - seekBarPosition
const percentage = clamp(round(mouseOrTouchPosition / seekBarWidth, 4), 0, 100)
const newTime = percentage * (player?.duration() || 0)
player.currentTime(newTime)
setIsScrubbing(false)
}
return (
<ProgressControl
onClick={(event) => event.stopPropagation()}
isScrubbing={isScrubbing}
isFullScreen={isFullScreen}
onMouseMove={handleMouseAndTouchMove}
onTouchMove={handleMouseAndTouchMove}
onMouseLeave={handleJumpToTime}
onMouseDown={() => setIsScrubbing(true)}
onTouchStart={() => setIsScrubbing(true)}
onMouseUp={handleJumpToTime}
onTouchEnd={handleJumpToTime}
>
<SeekBar ref={seekBarRef}>
<LoadProgress style={{ width: loadProgressWidth + '%' }} />
<MouseDisplayWrapper>
<MouseDisplay style={{ width: mouseDisplayWidth + '%' }} />
<MouseDisplayTooltip
ref={mouseDisplayTooltipRef}
style={{
left: `clamp(0px, calc(${mouseDisplayWidth}% - ${
mouseDisplayTooltipWidth / 2
}px), calc(100% - ${mouseDisplayTooltipWidth}px))`,
}}
isFullScreen={isFullScreen}
>
<StyledTooltipText variant="t200">{mouseDisplayTooltipTime}</StyledTooltipText>
</MouseDisplayTooltip>
</MouseDisplayWrapper>
<PlayProgressWrapper>
<PlayProgress style={{ width: playProgressWidth + '%' }} ref={playProgressRef} />
<PlayProgressThumb
ref={playProgressThumbRef}
style={{
left: `clamp(0px, calc(${playProgressWidth}% - ${
playProgressThumbWidth / 2
}px), calc(100% - ${playProgressThumbWidth}px))`,
}}
/>
</PlayProgressWrapper>
</SeekBar>
</ProgressControl>
)
}
Example #3
Source File: VideoPlayer.tsx From atlas with GNU General Public License v3.0 | 4 votes |
VideoPlayerComponent: React.ForwardRefRenderFunction<HTMLVideoElement, VideoPlayerProps> = (
{
isVideoPending,
className,
playing,
nextVideo,
channelId,
videoId,
autoplay,
videoStyle,
isEmbedded,
isPlayNextDisabled,
...videoJsConfig
},
externalRef
) => {
const [player, playerRef] = useVideoJsPlayer(videoJsConfig)
const [isPlaying, setIsPlaying] = useState(false)
const {
currentVolume,
cachedVolume,
cinematicView,
actions: { setCurrentVolume, setCachedVolume, setCinematicView },
} = usePersonalDataStore((state) => state)
const [volumeToSave, setVolumeToSave] = useState(0)
const [videoTime, setVideoTime] = useState(0)
const [isFullScreen, setIsFullScreen] = useState(false)
const [isPiPEnabled, setIsPiPEnabled] = useState(false)
const [playerState, setPlayerState] = useState<PlayerState>('loading')
const [isLoaded, setIsLoaded] = useState(false)
const [needsManualPlay, setNeedsManualPlay] = useState(!autoplay)
const mdMatch = useMediaMatch('md')
const playVideo = useCallback(
async (player: VideoJsPlayer | null, withIndicator?: boolean, callback?: () => void) => {
if (!player) {
return
}
withIndicator && player.trigger(CustomVideojsEvents.PlayControl)
try {
setNeedsManualPlay(false)
const playPromise = await player.play()
if (playPromise && callback) callback()
} catch (error) {
if (error.name === 'AbortError') {
// this will prevent throwing harmless error `the play() request was interrupted by a call to pause()`
// Video.js doing something similiar, check:
// https://github.com/videojs/video.js/issues/6998
// https://github.com/videojs/video.js/blob/4238f5c1d88890547153e7e1de7bd0d1d8e0b236/src/js/utils/promise.js
return
}
if (error.name === 'NotAllowedError') {
ConsoleLogger.warn('Video playback failed', error)
} else {
SentryLogger.error('Video playback failed', 'VideoPlayer', error, {
video: { id: videoId, url: videoJsConfig.src },
})
}
}
},
[videoId, videoJsConfig.src]
)
const pauseVideo = useCallback((player: VideoJsPlayer | null, withIndicator?: boolean, callback?: () => void) => {
if (!player) {
return
}
withIndicator && player.trigger(CustomVideojsEvents.PauseControl)
callback?.()
player.pause()
}, [])
useEffect(() => {
if (!isVideoPending) {
return
}
setPlayerState('pending')
}, [isVideoPending])
// handle hotkeys
useEffect(() => {
if (!player) {
return
}
const handler = (event: KeyboardEvent) => {
if (
(document.activeElement?.tagName === 'BUTTON' && event.key === ' ') ||
document.activeElement?.tagName === 'INPUT'
) {
return
}
const playerReservedKeys = ['k', ' ', 'ArrowLeft', 'ArrowRight', 'j', 'l', 'ArrowUp', 'ArrowDown', 'm', 'f', 'c']
if (
!event.altKey &&
!event.ctrlKey &&
!event.metaKey &&
!event.shiftKey &&
playerReservedKeys.includes(event.key)
) {
event.preventDefault()
hotkeysHandler(event, player, playVideo, pauseVideo, () => setCinematicView(!cinematicView))
}
}
document.addEventListener('keydown', handler)
return () => document.removeEventListener('keydown', handler)
}, [cinematicView, pauseVideo, playVideo, player, playerState, setCinematicView])
// handle error
useEffect(() => {
if (!player) {
return
}
const handler = () => {
setPlayerState('error')
}
player.on('error', handler)
return () => {
player.off('error', handler)
}
})
// handle video loading
useEffect(() => {
if (!player) {
return
}
const handler = (event: Event) => {
if (event.type === 'waiting' || event.type === 'seeking') {
setPlayerState('loading')
}
if (event.type === 'canplay' || event.type === 'seeked') {
setPlayerState('playingOrPaused')
}
}
player.on(['waiting', 'canplay', 'seeking', 'seeked'], handler)
return () => {
player.off(['waiting', 'canplay', 'seeking', 'seeked'], handler)
}
}, [player, playerState])
useEffect(() => {
if (!player) {
return
}
const handler = () => {
setPlayerState('ended')
}
player.on('ended', handler)
return () => {
player.off('ended', handler)
}
}, [nextVideo, player])
// handle loadstart
useEffect(() => {
if (!player) {
return
}
const handler = () => {
setIsLoaded(true)
}
player.on('loadstart', handler)
return () => {
player.off('loadstart', handler)
}
}, [player])
// handle autoplay
useEffect(() => {
if (!player || !isLoaded || !autoplay) {
return
}
const playPromise = player.play()
if (playPromise) {
playPromise
.then(() => {
setIsPlaying(true)
})
.catch((e) => {
setNeedsManualPlay(true)
ConsoleLogger.warn('Video autoplay failed', e)
})
}
}, [player, isLoaded, autoplay])
// handle playing and pausing from outside the component
useEffect(() => {
if (!player) {
return
}
if (playing) {
playVideo(player)
} else {
player.pause()
}
}, [playVideo, player, playing])
// handle playing and pausing
useEffect(() => {
if (!player) {
return
}
const handler = (event: Event) => {
if (event.type === 'play') {
setIsPlaying(true)
}
if (event.type === 'pause') {
setIsPlaying(false)
}
}
player.on(['play', 'pause'], handler)
return () => {
player.off(['play', 'pause'], handler)
}
}, [player, playerState])
useEffect(() => {
if (!externalRef) {
return
}
if (typeof externalRef === 'function') {
externalRef(playerRef.current)
} else {
externalRef.current = playerRef.current
}
}, [externalRef, playerRef])
// handle video timer
useEffect(() => {
if (!player) {
return
}
const handler = () => {
const currentTime = round(player.currentTime())
setVideoTime(currentTime)
}
player.on('timeupdate', handler)
return () => {
player.off('timeupdate', handler)
}
}, [player])
// handle seeking
useEffect(() => {
if (!player) {
return
}
const handler = () => {
if (playerState === 'ended') {
playVideo(player)
}
}
player.on('seeking', handler)
return () => {
player.off('seeking', handler)
}
}, [playVideo, player, playerState])
// handle fullscreen mode
useEffect(() => {
if (!player) {
return
}
const handler = () => {
// will remove focus from fullscreen button and apply to player.
player.focus()
setIsFullScreen(player.isFullscreen())
}
player.on('fullscreenchange', handler)
return () => {
player.off('fullscreenchange', handler)
}
}, [player])
// handle picture in picture
useEffect(() => {
if (!player) {
return
}
const handler = (event: Event) => {
if (event.type === 'enterpictureinpicture') {
setIsPiPEnabled(true)
}
if (event.type === 'leavepictureinpicture') {
setIsPiPEnabled(false)
}
}
player.on(['enterpictureinpicture', 'leavepictureinpicture'], handler)
return () => {
player.off(['enterpictureinpicture', 'leavepictureinpicture'], handler)
}
}, [player])
// update volume on keyboard input
useEffect(() => {
if (!player) {
return
}
const events = [
CustomVideojsEvents.VolumeIncrease,
CustomVideojsEvents.VolumeDecrease,
CustomVideojsEvents.Muted,
CustomVideojsEvents.Unmuted,
]
const handler = (event: Event) => {
if (event.type === CustomVideojsEvents.Muted) {
if (currentVolume) {
setCachedVolume(currentVolume)
}
setCurrentVolume(0)
return
}
if (event.type === CustomVideojsEvents.Unmuted) {
setCurrentVolume(cachedVolume || VOLUME_STEP)
return
}
if (event.type === CustomVideojsEvents.VolumeIncrease || CustomVideojsEvents.VolumeDecrease) {
setCurrentVolume(player.volume())
}
}
player.on(events, handler)
return () => {
player.off(events, handler)
}
}, [currentVolume, player, cachedVolume, setCachedVolume, setCurrentVolume])
const debouncedVolumeChange = useRef(
debounce((volume: number) => {
setVolumeToSave(volume)
}, 125)
)
// update volume on mouse input
useEffect(() => {
if (!player) {
return
}
player?.volume(currentVolume)
debouncedVolumeChange.current(currentVolume)
if (currentVolume) {
player.muted(false)
} else {
if (volumeToSave) {
setCachedVolume(volumeToSave)
}
player.muted(true)
}
}, [currentVolume, volumeToSave, player, setCachedVolume])
// button/input handlers
const handlePlayPause = useCallback(() => {
if (playerState === 'error') {
return
}
if (isPlaying) {
pauseVideo(player, true, () => setIsPlaying(false))
} else {
playVideo(player, true, () => setIsPlaying(true))
}
}, [isPlaying, pauseVideo, playVideo, player, playerState])
const handleChangeVolume = (event: React.ChangeEvent<HTMLInputElement>) => {
setCurrentVolume(Number(event.target.value))
}
const handleMute = () => {
if (currentVolume === 0) {
setCurrentVolume(cachedVolume || 0.05)
} else {
setCurrentVolume(0)
}
}
const handlePictureInPicture = (event: React.MouseEvent) => {
event.stopPropagation()
if (document.pictureInPictureElement) {
// @ts-ignore @types/video.js is outdated and doesn't provide types for some newer video.js features
player.exitPictureInPicture()
} else {
if (document.pictureInPictureEnabled) {
// @ts-ignore @types/video.js is outdated and doesn't provide types for some newer video.js features
player.requestPictureInPicture().catch((e) => {
ConsoleLogger.warn('Picture in picture failed', e)
})
}
}
}
const handleFullScreen = (event: React.MouseEvent) => {
event.stopPropagation()
if (!isFullScreenEnabled) {
return
}
if (player?.isFullscreen()) {
player?.exitFullscreen()
} else {
player?.requestFullscreen()
}
}
const onVideoClick = useCallback(
() =>
player?.paused()
? player?.trigger(CustomVideojsEvents.PauseControl)
: player?.trigger(CustomVideojsEvents.PlayControl),
[player]
)
const renderVolumeButton = () => {
if (currentVolume === 0) {
return <StyledSvgPlayerSoundOff />
} else {
return currentVolume <= 0.5 ? <StyledSvgControlsSoundLowVolume /> : <StyledSvgPlayerSoundOn />
}
}
const toggleCinematicView = (event: React.MouseEvent) => {
event.stopPropagation()
setCinematicView(!cinematicView)
}
const showPlayerControls = isLoaded && playerState
const showControlsIndicator = playerState !== 'ended'
return (
<Container isFullScreen={isFullScreen} className={className}>
<div data-vjs-player onClick={handlePlayPause}>
{needsManualPlay && (
<BigPlayButtonContainer onClick={handlePlayPause}>
<BigPlayButton onClick={handlePlayPause}>
<StyledSvgControlsPlay />
</BigPlayButton>
</BigPlayButtonContainer>
)}
<video style={videoStyle} ref={playerRef} className="video-js" onClick={onVideoClick} />
{showPlayerControls && (
<>
<ControlsOverlay isFullScreen={isFullScreen}>
<CustomTimeline
playVideo={playVideo}
pauseVideo={pauseVideo}
player={player}
isFullScreen={isFullScreen}
playerState={playerState}
setPlayerState={setPlayerState}
/>
<CustomControls isFullScreen={isFullScreen} isEnded={playerState === 'ended'}>
<PlayControl isLoading={playerState === 'loading'}>
{(!needsManualPlay || mdMatch) && (
<PlayButton
isEnded={playerState === 'ended'}
onClick={handlePlayPause}
tooltipText={isPlaying ? 'Pause (k)' : playerState === 'ended' ? 'Play again (k)' : 'Play (k)'}
tooltipPosition="left"
>
{playerState === 'ended' ? (
<StyledSvgControlsReplay />
) : isPlaying ? (
<StyledSvgControlsPause />
) : (
<StyledSvgControlsPlay />
)}
</PlayButton>
)}
</PlayControl>
<VolumeControl onClick={(e) => e.stopPropagation()}>
<VolumeButton tooltipText="Volume" showTooltipOnlyOnFocus onClick={handleMute}>
{renderVolumeButton()}
</VolumeButton>
<VolumeSliderContainer>
<VolumeSlider
step={0.01}
max={1}
min={0}
value={currentVolume}
onChange={handleChangeVolume}
type="range"
/>
</VolumeSliderContainer>
</VolumeControl>
<CurrentTimeWrapper>
<CurrentTime variant="t200">
{formatDurationShort(videoTime)} / {formatDurationShort(round(player?.duration() || 0))}
</CurrentTime>
</CurrentTimeWrapper>
<ScreenControls>
{mdMatch && !isEmbedded && !player?.isFullscreen() && (
<PlayerControlButton
onClick={toggleCinematicView}
tooltipText={cinematicView ? 'Exit cinematic mode (c)' : 'Cinematic view (c)'}
>
{cinematicView ? (
<StyledSvgControlsVideoModeCompactView />
) : (
<StyledSvgControlsVideoModeCinemaView />
)}
</PlayerControlButton>
)}
{isPiPSupported && (
<PlayerControlButton onClick={handlePictureInPicture} tooltipText="Picture-in-picture">
{isPiPEnabled ? <StyledSvgControlsPipOff /> : <StyledSvgControlsPipOn />}
</PlayerControlButton>
)}
<PlayerControlButton
isDisabled={!isFullScreenEnabled}
tooltipPosition="right"
tooltipText={isFullScreen ? 'Exit full screen (f)' : 'Full screen (f)'}
onClick={handleFullScreen}
>
{isFullScreen ? <StyledSvgControlsSmallScreen /> : <StyledSvgControlsFullScreen />}
</PlayerControlButton>
</ScreenControls>
</CustomControls>
</ControlsOverlay>
</>
)}
<VideoOverlay
videoId={videoId}
isFullScreen={isFullScreen}
isPlayNextDisabled={isPlayNextDisabled}
playerState={playerState}
onPlay={handlePlayPause}
channelId={channelId}
currentThumbnailUrl={videoJsConfig.posterUrl}
playRandomVideoOnEnded={!isEmbedded}
/>
{showControlsIndicator && <ControlsIndicator player={player} isLoading={playerState === 'loading'} />}
</div>
</Container>
)
}
Example #4
Source File: store.ts From atlas with GNU General Public License v3.0 | 4 votes |
usePersonalDataStore = createStore<PersonalDataStoreState, PersonalDataStoreActions>(
{
state: initialState,
actionsFactory: (set) => ({
updateWatchedVideos: (__typename, id, timestamp) => {
set((state) => {
const currentVideo = state.watchedVideos.find((v) => v.id === id)
if (!currentVideo) {
const newVideo = __typename === 'COMPLETED' ? { __typename, id } : { __typename, id, timestamp }
state.watchedVideos.push(newVideo)
} else {
const index = state.watchedVideos.findIndex((v) => v.id === id)
if (index !== -1) state.watchedVideos[index] = { __typename, id, timestamp }
}
})
},
updateChannelFollowing: (id, follow) => {
set((state) => {
const isFollowing = state.followedChannels.some((channel) => channel.id === id)
if (isFollowing && !follow) {
state.followedChannels = state.followedChannels.filter((channel) => channel.id !== id)
}
if (!isFollowing && follow) {
state.followedChannels.push({ id })
}
})
},
addRecentSearch: (title) => {
set((state) => {
const filteredCurrentSearches = state.recentSearches.filter((item) => item.title !== title)
const newSearches = [{ title }, ...filteredCurrentSearches]
state.recentSearches = newSearches.slice(0, 6)
})
},
deleteRecentSearch: (title) => {
set((state) => {
state.recentSearches = state.recentSearches.filter((search) => search.title !== title)
})
},
updateDismissedMessages: (id, add = true) => {
set((state) => {
state.dismissedMessages = state.dismissedMessages.filter((dissmissedMessage) => dissmissedMessage.id !== id)
if (add) {
state.dismissedMessages.unshift({ id })
}
})
},
setCurrentVolume: (volume) =>
set((state) => {
state.currentVolume = round(volume, 2)
}),
setCachedVolume: (volume) =>
set((state) => {
state.cachedVolume = round(volume, 2)
}),
setCinematicView: (cinematicView) =>
set((state) => {
state.cinematicView = cinematicView
}),
setCookiesAccepted: (accept) =>
set((state) => {
state.cookiesAccepted = accept
}),
}),
},
{
persist: {
key: 'personalData',
whitelist: WHITELIST,
version: 2,
migrate: (oldState) => {
const typedOldState = oldState as PersonalDataStoreState
const migratedWatchedVideos = typedOldState.watchedVideos.reduce((acc, cur) => {
const migratedId = (videoIdsMapEntries as Record<string, string>)[cur.id]
if (migratedId) {
return [...acc, { ...cur, id: migratedId }]
}
return acc
}, [] as WatchedVideo[])
const migratedFollowedChannels = typedOldState.followedChannels.reduce((acc, cur) => {
const migratedId = (channelIdsMapEntries as Record<string, string>)[cur.id]
if (migratedId) {
return [...acc, { ...cur, id: migratedId }]
}
return acc
}, [] as FollowedChannel[])
const migratedState: PersonalDataStoreState = {
...typedOldState,
watchedVideos: migratedWatchedVideos,
followedChannels: migratedFollowedChannels,
}
return migratedState
},
},
}
)