@@ -5,11 +5,13 @@ | |||||
class="w-full flex bg-slate-800 font-semibold rounded-full backdrop-blur sticky top-0 justify-center items-center hover:ring"> | class="w-full flex bg-slate-800 font-semibold rounded-full backdrop-blur sticky top-0 justify-center items-center hover:ring"> | ||||
<input ref="searchInput" v-model="terms" autofocus placeholder="search" type="search" | <input ref="searchInput" v-model="terms" autofocus placeholder="search" type="search" | ||||
class="rounded-full text-xl w-full text-center text-white p-4 bg-transparent focus-visible:border-none" | class="rounded-full text-xl w-full text-center text-white p-4 bg-transparent focus-visible:border-none" | ||||
@keypress.enter="search()"> | |||||
<b v-if="films.length" class="px-4 py-2 absolute right-2 block bg-green-400 text-slate-800 rounded-full"> | |||||
@keypress.enter="pressEnter()"> | |||||
<b v-if="films && films.length" | |||||
class="px-4 py-2 absolute right-2 block bg-green-400 text-slate-800 rounded-full"> | |||||
{{ films.length }} | {{ films.length }} | ||||
</b> | </b> | ||||
</nav> | </nav> | ||||
<NuxtLink v-for="(film, index) in films" :key="index" :href="film.title" :to="'/details/' + film.id" | <NuxtLink v-for="(film, index) in films" :key="index" :href="film.title" :to="'/details/' + film.id" | ||||
class="hover:bg-slate-200 w-full p-4 flex-col border-b-2 border-GREY-100"> | class="hover:bg-slate-200 w-full p-4 flex-col border-b-2 border-GREY-100"> | ||||
<img :src="config.public.IMG_BASE_URL + film.poster_path" :alt="film.title"> | <img :src="config.public.IMG_BASE_URL + film.poster_path" :alt="film.title"> | ||||
@@ -29,26 +31,39 @@ | |||||
const config = useRuntimeConfig() | const config = useRuntimeConfig() | ||||
const terms = ref('') | const terms = ref('') | ||||
const films = ref([]) | const films = ref([]) | ||||
const page = ref(1) | |||||
const loading = ref(false) | |||||
const { onScrollEnd } = useScrollEnd() | |||||
const search = async () => { | const search = async () => { | ||||
loading.value = true | |||||
if (terms.value !== '') { | if (terms.value !== '') { | ||||
films.value = [] | |||||
const { data } = await useFetch(`/api/search/${terms.value}`) | |||||
films.value = data.value | |||||
const { data } = await useFetch(`/api/search/${terms.value}-${page.value}`) | |||||
console.log(data) | |||||
films.value.push(...data.value) | |||||
} else { | } else { | ||||
initList() | |||||
const { data } = await useFetch(`/api/explore/${page.value}`) | |||||
films.value.push(...data.value) | |||||
} | } | ||||
loading.value = false | |||||
} | } | ||||
const initList = async () => { | |||||
const { data } = await useFetch(`/api/list`) | |||||
films.value = data.value | |||||
const pressEnter = () => { | |||||
page.value = 1 | |||||
films.value = [] | |||||
search() | |||||
} | } | ||||
onScrollEnd(() => { | |||||
page.value += 1 | |||||
search() | |||||
}) | |||||
onMounted(() => { | onMounted(() => { | ||||
nextTick(() => { | nextTick(() => { | ||||
initList() | |||||
search() | |||||
}) | }) | ||||
}) | }) | ||||
</script> | </script> | ||||
@@ -1,19 +0,0 @@ | |||||
export function useMouse() { | |||||
// state encapsulated and managed by the composable | |||||
const x = ref(0) | |||||
const y = ref(0) | |||||
// a composable can update its managed state over time. | |||||
function update(event) { | |||||
x.value = event.pageX | |||||
y.value = event.pageY | |||||
} | |||||
// a composable can also hook into its owner component's | |||||
// lifecycle to setup and teardown side effects. | |||||
onMounted(() => window.addEventListener('mousemove', update)) | |||||
onUnmounted(() => window.removeEventListener('mousemove', update)) | |||||
// expose managed state as return value | |||||
return { x, y } | |||||
} |
@@ -0,0 +1,27 @@ | |||||
export function useScrollEnd() { | |||||
let callback = null | |||||
const handleScroll = () => { | |||||
const scrollPosition = window.innerHeight + window.scrollY | |||||
const pageHeight = document.documentElement.offsetHeight | |||||
if (scrollPosition >= pageHeight && typeof callback === 'function') { | |||||
callback() | |||||
} | |||||
} | |||||
const onScrollEnd = (cb) => { | |||||
callback = cb | |||||
} | |||||
onMounted(() => { | |||||
window.addEventListener('scroll', handleScroll) | |||||
}) | |||||
onUnmounted(() => { | |||||
window.removeEventListener('scroll', handleScroll) | |||||
}) | |||||
return { onScrollEnd } | |||||
} |
@@ -23,4 +23,6 @@ export default defineNuxtConfig({ | |||||
IMG_BASE_URL: 'https://media.themoviedb.org/t/p/w220_and_h330_face/', | IMG_BASE_URL: 'https://media.themoviedb.org/t/p/w220_and_h330_face/', | ||||
} | } | ||||
}, | }, | ||||
}) | |||||
compatibilityDate: '2024-10-06', | |||||
}) |
@@ -1,9 +1,8 @@ | |||||
import fetch from 'node-fetch' | import fetch from 'node-fetch' | ||||
export default eventHandler(async (req) => { | export default eventHandler(async (req) => { | ||||
const terms = req.context.params?.terms || '' | |||||
const url = 'https://api.themoviedb.org/3/discover/movie?include_adult=false&include_video=false&language=en-US&page=1&sort_by=popularity.desc' | |||||
const page = req.context.params?.page || 1 | |||||
const url = `https://api.themoviedb.org/3/discover/movie?include_adult=false&include_video=false&language=en-US&page=${page}&sort_by=popularity.desc` | |||||
const response = await fetch(url, { | const response = await fetch(url, { | ||||
method: 'get', | method: 'get', |
@@ -0,0 +1,13 @@ | |||||
import fetch from 'node-fetch' | |||||
export default eventHandler(async (req) => { | |||||
const page = req.context.params?.page || 1 | |||||
const url = `https://api.themoviedb.org/3/discover/movie?include_adult=false&include_video=false&language=en-US&page=${page}&sort_by=popularity.desc` | |||||
const response = await fetch(url, { | |||||
method: 'get', | |||||
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiI2MmVmZmYzYWU2YWYyNWQ0NTY4OGY3OTkxYjgyNmNhOCIsIm5iZiI6MTcyODA1NTIwMy4wNzcyNywic3ViIjoiNjcwMDA2ZjQ5ZWJlYTE5MDA2ZjgxZmJhIiwic2NvcGVzIjpbImFwaV9yZWFkIl0sInZlcnNpb24iOjF9.XuH-_0UggmULCgQQajKc-QsmlRYW2rqSenyhguE6wRU' } | |||||
}) | |||||
const data = await response.json() | |||||
return data.results | |||||
}) |
@@ -11,12 +11,9 @@ interface Film { | |||||
let films: Film[] = [] | let films: Film[] = [] | ||||
export default eventHandler(async (req) => { | export default eventHandler(async (req) => { | ||||
const terms = req.context.params?.terms || '' | |||||
const url = `https://api.themoviedb.org/3/search/movie?query=${terms}&include_adult=false&language=en-US&page=1` | |||||
const [terms, page] = req.context.params?.termspage.split('-') || '' | |||||
const url = `https://api.themoviedb.org/3/search/movie?query=${terms}&page=${page}&include_adult=false&language=en-US&page=1` | |||||
const response = await fetch(url, { | const response = await fetch(url, { | ||||
method: 'get', | method: 'get', |