Unverified Commit c6df1940 authored by Andreas Sonnleitner's avatar Andreas Sonnleitner Committed by GitHub

fix: migrate from reactivity transform and some fixes (#125)

parent 657c0b48
......@@ -5,7 +5,7 @@ const props = defineProps<{
maxPage: number
}>()
const hasMore = $computed(() => props.page < props.maxPage)
const hasMore = computed(() => props.page < props.maxPage)
</script>
<template>
......
import { WritableComputedOptions } from 'vue'
import { Item, User } from '~/types'
export interface StoreState {
items: Record<number, Item>
comments: Record<number, Item[]>
users: Record<number, User>
users: Record<string, User>
feeds: Record<string, Record<number, number[]>>
}
......@@ -28,56 +29,56 @@ export function getFeed (state:StoreState, { feed, page }: FeedQuery) {
}
export function fetchFeed (query: FeedQuery) {
const state = $(useStore())
const state = useStore()
const { feed, page } = query
return reactiveLoad<Item[]>(
() => getFeed(state, query),
() => getFeed(state.value, query),
(items) => {
const ids = items.map(item => item.id)
state.feeds[feed][page] = ids
state.value.feeds[feed][page] = ids
items
.filter(Boolean)
.forEach((item) => {
if (state.items[item.id]) {
Object.assign(state.items[item.id], item)
if (state.value.items[item.id]) {
Object.assign(state.value.items[item.id], item)
} else {
state.items[item.id] = item
state.value.items[item.id] = item
}
})
},
() => $fetch('/api/hn/feeds', { params: { feed, page } }),
(state.feeds[feed][page] || []).map(id => state.items[id])
(state.value.feeds[feed][page] || []).map(id => state.value.items[id])
)
}
export function fetchItem (id: string) {
const state = $(useStore())
export function fetchItem (id: number) {
const state = useStore()
return reactiveLoad<Item>(
() => state.items[id],
(item) => { state.items[id] = item },
() => state.value.items[id],
(item) => { state.value.items[id] = item },
() => $fetch('/api/hn/item', { params: { id } })
)
}
export function fetchComments (id: string) {
const state = $(useStore())
export function fetchComments (id: number) {
const state = useStore()
return reactiveLoad<Item[]>(
() => state.comments[id],
(comments) => { state.comments[id] = comments },
() => $fetch('/api/hn/item', { params: { id } }).then(i => i.comments)
() => state.value.comments[id],
(comments) => { state.value.comments[id] = comments },
() => $fetch('/api/hn/item', { params: { id } }).then(i => i.comments!)
)
}
export function fetchUser (id: string) {
const state = $(useStore())
const state = useStore()
return reactiveLoad<User>(
() => state.users[id],
(user) => { state.users[id] = user },
() => state.value.users[id],
(user) => { state.value.users[id] = user },
() => $fetch('/api/hn/user', { params: { id } })
)
}
......@@ -96,7 +97,7 @@ export async function reactiveLoad<T> (
const data = computed({
get,
set
})
} as WritableComputedOptions<T | undefined>)
const loading = ref(false)
if (data.value == null) {
......
......@@ -3,8 +3,5 @@ export default defineNuxtConfig({
plugins: {
'postcss-nested': {}
}
},
experimental: {
reactivityTransform: true
}
})
{
"name": "nuxt-hn",
"private": true,
"packageManager": "pnpm@7.26.2",
"packageManager": "pnpm@7.29.1",
"description": "Nuxt Hacker News",
"author": "Evan You <yyx990803@gmail.com>",
"contributors": [
......@@ -29,9 +29,10 @@
},
"devDependencies": {
"@nuxtjs/eslint-config-typescript": "^12.0.0",
"eslint": "^8.33.0",
"nuxt": "^3.1.1",
"postcss-nested": "^6.0.0",
"@types/node": "^18.14.6",
"eslint": "^8.35.0",
"nuxt": "^3.2.3",
"postcss-nested": "^6.0.1",
"typescript": "^4.9.5"
}
}
......@@ -7,45 +7,45 @@ definePageMeta({
const route = useRoute()
const router = useRouter()
const page = $computed(() => +route.params.page || 1)
const feed = $computed(() => route.params.feed as string)
const isValidFeed = $computed(() => !!feedsInfo[feed])
const page = computed(() => +route.params.page || 1)
const feed = computed(() => route.params.feed as keyof typeof feedsInfo)
const isValidFeed = computed(() => !!feedsInfo[feed.value])
// const transition = $ref('slide-right')
const pageNo = $computed(() => Number(page) || 1)
const displayedPage = ref(pageNo)
// const transition = ref('slide-right')
const pageNo = computed(() => Number(page.value) || 1)
const displayedPage = ref(pageNo.value)
useHead({
title: feedsInfo[feed]?.title
title: feedsInfo[feed.value]?.title
})
const state = $(useStore())
const state = useStore()
if (isValidFeed) {
await fetchFeed({ page: pageNo, feed })
if (isValidFeed.value) {
await fetchFeed({ page: pageNo.value, feed: feed.value })
}
const items = $computed(() => getFeed(state, { page: pageNo, feed }) || [])
const loading = $computed(() => items.length === 0)
const items = computed(() => getFeed(state.value, { page: pageNo.value, feed: feed.value }) || [])
const loading = computed(() => items.value.length === 0)
const maxPage = $computed(() => {
return +(feedsInfo[feed]?.pages) || 0
const maxPage = computed(() => {
return +(feedsInfo[feed.value]?.pages) || 0
})
function pageChanged (to: number, from = -1) {
if (!isValidFeed) { return }
function pageChanged (to: number, _from = -1) {
if (!isValidFeed.value) { return }
if (to <= 0 || to > maxPage) {
router.replace(`/${feed}/1`)
if (to <= 0 || to > maxPage.value) {
router.replace(`/${feed.value}/1`)
return
}
// Prefetch next page
fetchFeed({
feed,
page: page + 1
feed: feed.value,
page: page.value + 1
}).catch(() => {})
// transition = from === -1
// transition.value = from === -1
// ? null
// : to > from
// ? 'slide-left'
......@@ -54,8 +54,8 @@ function pageChanged (to: number, from = -1) {
displayedPage.value = to
}
onMounted(() => pageChanged(page))
watch(() => page, (to, old) => pageChanged(to, old))
onMounted(() => pageChanged(page.value))
watch(page, (to, old) => pageChanged(to, old))
</script>
<template>
......
......@@ -2,14 +2,14 @@
import { host, timeAgo, isAbsolute } from '~/composables/utils'
const route = useRoute()
const id = $computed(() => route.params.id as string)
const id = computed(() => +route.params.id)
const [resultItem, resultComments] = await Promise.all([fetchItem(id), fetchComments(id)])
const { data: item } = $(resultItem)
const { data: comments, loading: commentsLoading } = $(resultComments)
const [resultItem, resultComments] = await Promise.all([fetchItem(id.value), fetchComments(id.value)])
const { data: item } = toRefs(resultItem)
const { data: comments, loading: commentsLoading } = toRefs(resultComments)
useHead({
title: item?.title
title: item.value?.title
})
</script>
......
......@@ -2,16 +2,16 @@
import { timeAgo } from '~/composables/utils'
const route = useRoute()
const id = $computed(() => route.params.id as string)
const id = computed(() => route.params.id as string)
const result = await fetchUser(id)
const { data: user, loading } = $(result)
const result = await fetchUser(id.value)
const { data: user, loading } = toRefs(result)
useHead({
title: loading
title: loading.value
? 'Loading'
: user
? user.id
: user.value
? user.value.id
: 'User not found'
})
</script>
......
This source diff could not be displayed because it is too large. You can view the blob instead.
import { createError } from 'h3'
import { $fetch } from 'ofetch'
import { getQuery, parseURL } from 'ufo'
import { feedsInfo, validFeeds } from '~/composables/api'
import { baseURL } from '~/server/constants'
import { configureSWRHeaders } from '~/server/swr'
const feedUrls: Record<keyof typeof feedsInfo, string> = {
ask: 'askstories',
......@@ -15,7 +12,7 @@ const feedUrls: Record<keyof typeof feedsInfo, string> = {
news: 'topstories'
}
async function fetchFeed (feed: string, page = '1') {
async function fetchFeed (feed: keyof typeof feedsInfo, page = '1') {
const { fetchItem } = await import('./item')
const entries = Object.values(
await $fetch(`${baseURL}/${feedUrls[feed]}.json`)
......@@ -23,10 +20,9 @@ async function fetchFeed (feed: string, page = '1') {
return Promise.all(entries.map(id => fetchItem(id)))
}
export default defineEventHandler(({ req, res }) => {
configureSWRHeaders(res)
const { search } = parseURL(req.url)
const { page = '1', feed = 'news' } = getQuery(search) as { page: string, feed: string }
export default defineEventHandler((event) => {
configureSWRHeaders(event)
const { page = '1', feed = 'news' } = getQuery(event) as { page: string, feed: keyof typeof feedsInfo }
if (!validFeeds.includes(feed) || String(Number(page)) !== page) {
throw createError({
......
import { createError } from 'h3'
import { $fetch } from 'ofetch'
import { parseURL, getQuery } from 'ufo'
import { baseURL } from '~/server/constants'
import { Item } from '~/types'
import { configureSWRHeaders } from '~/server/swr'
export async function fetchItem (
id: string,
......@@ -31,10 +28,9 @@ export async function fetchItem (
}
}
export default defineEventHandler(({ req, res }) => {
configureSWRHeaders(res)
const { search } = parseURL(req.url)
const { id } = getQuery(search) as { id: string }
export default defineEventHandler((event) => {
configureSWRHeaders(event)
const { id } = getQuery(event) as { id?: string }
if (!id) {
throw createError({
......
import { createError } from 'h3'
import { $fetch } from 'ofetch'
import { parseURL, getQuery } from 'ufo'
import { User } from '~/types'
import { baseURL } from '~/server/constants'
import { configureSWRHeaders } from '~/server/swr'
async function fetchUser (id: string): Promise<User> {
const user = await $fetch(`${baseURL}/user/${id}.json`)
......@@ -15,10 +12,9 @@ async function fetchUser (id: string): Promise<User> {
}
}
export default defineEventHandler(({ req, res }) => {
configureSWRHeaders(res)
const { search } = parseURL(req.url)
const { id } = getQuery(search) as { id: string }
export default defineEventHandler((event) => {
configureSWRHeaders(event)
const { id } = getQuery(event) as { id?: string }
if (!id) {
throw createError({
......
......@@ -2,6 +2,6 @@ export default defineEventHandler((event) => {
const query = getQuery(event)
if (typeof query.csr !== 'undefined') {
event.req.headers['x-nuxt-no-ssr'] = 'true'
event.node.req.headers['x-nuxt-no-ssr'] = 'true'
}
})
import type { H3Response } from 'h3'
export function configureSWRHeaders (res: H3Response) {
res.setHeader('Cache-Control', 's-maxage=10, stale-while-revalidate')
}
import { H3Event } from 'h3'
export function configureSWRHeaders (event: H3Event) {
setHeader(event, 'Cache-Control', 's-maxage=10, stale-while-revalidate')
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment