import { getNavigationType, getPathId, isBackNavigation, shouldNotIntercept, updateTheDOMSomehow, useTvFragment, } from './utils' // View Transitions support cross-document navigations. // Should compare performace. // https://github.com/WICG/view-transitions/blob/main/explainer.md#cross-document-same-origin-transitions // https://github.com/WICG/view-transitions/blob/main/explainer.md#script-events function shouldDisableSpa() { return false; } navigation.addEventListener('navigate', (navigateEvent) => { if (shouldDisableSpa()) return if (shouldNotIntercept(navigateEvent)) return const toUrl = new URL(navigateEvent.destination.url) const toPath = toUrl.pathname const fromPath = location.pathname const navigationType = getNavigationType(fromPath, toPath) if (location.origin !== toUrl.origin) return switch (navigationType) { case 'home-to-movie': case 'tv-to-show': handleHomeToMovieTransition(navigateEvent, getPathId(toPath)) break case 'movie-to-home': case 'show-to-tv': handleMovieToHomeTransition(navigateEvent, getPathId(fromPath)) break case 'movie-to-person': handleMovieToPersonTransition( navigateEvent, getPathId(fromPath), getPathId(toPath) ) break case 'person-to-movie': case 'person-to-show': handlePersonToMovieTransition( navigateEvent, getPathId(fromPath), getPathId(toPath) ) break default: return } }) // TODO: https://developer.chrome.com/docs/web-platform/view-transitions/#transitions-as-an-enhancement function handleHomeToMovieTransition(navigateEvent, movieId) { navigateEvent.intercept({ async handler() { const fragmentUrl = useTvFragment(navigateEvent) ? '/fragments/TvDetails' : '/fragments/MovieDetails' const response = await fetch(`${fragmentUrl}/${movieId}`) const data = await response.text() if (!document.startViewTransition) { updateTheDOMSomehow(data); return; } const thumbnail = document.getElementById(`movie-poster-${movieId}`) if (thumbnail) { thumbnail.style.viewTransitionName = 'movie-poster' } const transition = document.startViewTransition(() => { if (thumbnail) { thumbnail.style.viewTransitionName = '' } document.getElementById('container').scrollTop = 0 updateTheDOMSomehow(data) }) await transition.finished }, }) } function handleMovieToHomeTransition(navigateEvent, movieId) { navigateEvent.intercept({ scroll: 'manual', async handler() { const fragmentUrl = useTvFragment(navigateEvent) ? '/fragments/TvList' : '/fragments/MovieList' const response = await fetch(fragmentUrl) const data = await response.text() if (!document.startViewTransition) { updateTheDOMSomehow(data) return } const tempHomePage = document.createElement('div') const moviePoster = document.getElementById(`movie-poster`) let thumbnail // If the movie poster is not in the home page, removes the transition style so that // the poster doesn't stay on the page while transitioning tempHomePage.innerHTML = data if (!tempHomePage.querySelector(`#movie-poster-${movieId}`)) { moviePoster?.classList.remove('movie-poster') } const transition = document.startViewTransition(() => { updateTheDOMSomehow(data) thumbnail = document.getElementById(`movie-poster-${movieId}`) if (thumbnail) { thumbnail.scrollIntoViewIfNeeded() thumbnail.style.viewTransitionName = 'movie-poster' } }) await transition.finished if (thumbnail) { thumbnail.style.viewTransitionName = '' } }, }) } function handleMovieToPersonTransition(navigateEvent, movieId, personId) { // TODO: https://developer.chrome.com/docs/web-platform/view-transitions/#not-a-polyfill // ...has example of `back-transition` class applied to document const isBack = isBackNavigation(navigateEvent) navigateEvent.intercept({ async handler() { const response = await fetch('/fragments/PersonDetails/' + personId) const data = await response.text() if (!document.startViewTransition) { updateTheDOMSomehow(data) return } let personThumbnail let moviePoster let movieThumbnail if (!isBack) { // We're transitioning the person photo; we need to remove the transition of the poster // so that it doesn't stay on the page while transitioning moviePoster = document.getElementById(`movie-poster`) if (moviePoster) { moviePoster.classList.remove('movie-poster') } personThumbnail = document.getElementById(`person-photo-${personId}`) if (personThumbnail) { personThumbnail.style.viewTransitionName = 'person-photo' } } const transition = document.startViewTransition(() => { updateTheDOMSomehow(data) if (personThumbnail) { personThumbnail.style.viewTransitionName = '' } if (isBack) { // If we're coming back to the person page, we're transitioning // into the movie poster thumbnail, so we need to add the tag to it movieThumbnail = document.getElementById(`movie-poster-${movieId}`) if (movieThumbnail) { movieThumbnail.scrollIntoViewIfNeeded() movieThumbnail.style.viewTransitionName = 'movie-poster' } } document.getElementById('container').scrollTop = 0 }) await transition.finished if (movieThumbnail) { movieThumbnail.style.viewTransitionName = '' } }, }) } function handlePersonToMovieTransition(navigateEvent, personId, movieId) { const isBack = isBackNavigation(navigateEvent) navigateEvent.intercept({ scroll: 'manual', async handler() { const fragmentUrl = useTvFragment(navigateEvent) ? '/fragments/TvDetails' : '/fragments/MovieDetails' const response = await fetch(`${fragmentUrl}/${movieId}`) const data = await response.text() if (!document.startViewTransition) { updateTheDOMSomehow(data) return } let thumbnail let moviePoster let movieThumbnail if (!isBack) { movieThumbnail = document.getElementById(`movie-poster-${movieId}`) if (movieThumbnail) { movieThumbnail.style.viewTransitionName = 'movie-poster' } } const transition = document.startViewTransition(() => { updateTheDOMSomehow(data) if (isBack) { moviePoster = document.getElementById(`movie-poster`) if (moviePoster) { moviePoster.classList.remove('movie-poster') } if (personId) { thumbnail = document.getElementById(`person-photo-${personId}`) if (thumbnail) { thumbnail.scrollIntoViewIfNeeded() thumbnail.style.viewTransitionName = 'person-photo' } } } else { document.getElementById('container').scrollTop = 0 if (movieThumbnail) { movieThumbnail.style.viewTransitionName = '' } } }) await transition.finished if (thumbnail) { thumbnail.style.viewTransitionName = '' } if (moviePoster) { moviePoster.classList.add('movie-poster') } }, }) }