Browse Source

#140 ぼちぼち

feature/140
みてるぞ 1 week ago
parent
commit
7df51fb34b
4 changed files with 43 additions and 22 deletions
  1. +8
    -4
      frontend/src/components/PrefetchLink.tsx
  2. +15
    -4
      frontend/src/components/TagLink.tsx
  3. +7
    -6
      frontend/src/components/TagSidebar.tsx
  4. +13
    -8
      frontend/src/pages/posts/PostListPage.tsx

+ 8
- 4
frontend/src/components/PrefetchLink.tsx View File

@@ -1,15 +1,16 @@
import { useQueryClient } from '@tanstack/react-query' import { useQueryClient } from '@tanstack/react-query'
import { useMemo } from 'react' import { useMemo } from 'react'
import { useNavigate } from 'react-router-dom'
import { createPath, useNavigate } from 'react-router-dom'


import { useOverlayStore } from '@/components/RouteBlockerOverlay' import { useOverlayStore } from '@/components/RouteBlockerOverlay'
import { prefetchForURL } from '@/lib/prefetchers' import { prefetchForURL } from '@/lib/prefetchers'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'


import type { AnchorHTMLAttributes, FC, MouseEvent, TouchEvent } from 'react' import type { AnchorHTMLAttributes, FC, MouseEvent, TouchEvent } from 'react'
import type { To } from 'react-router-dom'


type Props = AnchorHTMLAttributes<HTMLAnchorElement> & { type Props = AnchorHTMLAttributes<HTMLAnchorElement> & {
to: string
to: To
replace?: boolean replace?: boolean
className?: string className?: string
cancelOnError?: boolean } cancelOnError?: boolean }
@@ -25,7 +26,10 @@ export default (({ to,
...rest }: Props) => { ...rest }: Props) => {
const navigate = useNavigate () const navigate = useNavigate ()
const qc = useQueryClient () const qc = useQueryClient ()
const url = useMemo (() => (new URL (to, location.origin)).toString (), [to])
const url = useMemo (() => {
const path = (typeof to === 'string') ? to : createPath (to)
return (new URL (path, location.origin)).toString ()
}, [to])
const setOverlay = useOverlayStore (s => s.setActive) const setOverlay = useOverlayStore (s => s.setActive)


const doPrefetch = async () => { const doPrefetch = async () => {
@@ -74,7 +78,7 @@ export default (({ to,
} }


return ( return (
<a href={to}
<a href={typeof to === 'string' ? to : createPath (to)}
onMouseEnter={handleMouseEnter} onMouseEnter={handleMouseEnter}
onTouchStart={handleTouchStart} onTouchStart={handleTouchStart}
onClick={handleClick} onClick={handleClick}


+ 15
- 4
frontend/src/components/TagLink.tsx View File

@@ -1,5 +1,6 @@
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'


import PrefetchLink from '@/components/PrefetchLink'
import { LIGHT_COLOUR_SHADE, DARK_COLOUR_SHADE, TAG_COLOUR } from '@/consts' import { LIGHT_COLOUR_SHADE, DARK_COLOUR_SHADE, TAG_COLOUR } from '@/consts'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'


@@ -9,7 +10,8 @@ import type { Tag } from '@/types'


type CommonProps = { tag: Tag type CommonProps = { tag: Tag
withWiki?: boolean withWiki?: boolean
withCount?: boolean }
withCount?: boolean
prefetch?: boolean }


type PropsWithLink = type PropsWithLink =
CommonProps & { linkFlg?: true } & Partial<ComponentProps<typeof Link>> CommonProps & { linkFlg?: true } & Partial<ComponentProps<typeof Link>>
@@ -24,6 +26,7 @@ export default (({ tag,
linkFlg = true, linkFlg = true,
withWiki = true, withWiki = true,
withCount = true, withCount = true,
prefetch = false,
...props }: Props) => { ...props }: Props) => {
const spanClass = cn ( const spanClass = cn (
`text-${ TAG_COLOUR[tag.category] }-${ LIGHT_COLOUR_SHADE }`, `text-${ TAG_COLOUR[tag.category] }-${ LIGHT_COLOUR_SHADE }`,
@@ -44,11 +47,19 @@ export default (({ tag,
</span>)} </span>)}
{linkFlg {linkFlg
? ( ? (
<Link to={`/posts?${ (new URLSearchParams ({ tags: tag.name })).toString () }`}
prefetch
? <PrefetchLink
to={`/posts?${ (new URLSearchParams ({ tags: tag.name })).toString () }`}
className={linkClass} className={linkClass}
{...props}> {...props}>
{tag.name}
</Link>)
{tag.name}
</PrefetchLink>
: <Link
to={`/posts?${ (new URLSearchParams ({ tags: tag.name })).toString () }`}
className={linkClass}
{...props}>
{tag.name}
</Link>)
: ( : (
<span className={spanClass} <span className={spanClass}
{...props}> {...props}>


+ 7
- 6
frontend/src/components/TagSidebar.tsx View File

@@ -10,16 +10,17 @@ import { API_BASE_URL } from '@/config'
import { CATEGORIES } from '@/consts' import { CATEGORIES } from '@/consts'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'


import type { FC } from 'react'
import type { FC, MouseEvent } from 'react'


import type { Post, Tag } from '@/types' import type { Post, Tag } from '@/types'


type TagByCategory = Record<string, Tag[]> type TagByCategory = Record<string, Tag[]>


type Props = { posts: Post[] }
type Props = { posts: Post[]
onClick?: (event: MouseEvent<HTMLElement>) => void }




export default (({ posts }: Props) => {
export default (({ posts, onClick }: Props) => {
const navigate = useNavigate () const navigate = useNavigate ()


const [tagsVsbl, setTagsVsbl] = useState (false) const [tagsVsbl, setTagsVsbl] = useState (false)
@@ -64,11 +65,11 @@ export default (({ posts }: Props) => {
<div className={cn (!(tagsVsbl) && 'hidden', 'md:block mt-4')}> <div className={cn (!(tagsVsbl) && 'hidden', 'md:block mt-4')}>
<SectionTitle>タグ</SectionTitle> <SectionTitle>タグ</SectionTitle>
<ul> <ul>
{CATEGORIES.flatMap (cat => cat in tags ? (
{CATEGORIES.flatMap (cat => cat in tags ?
tags[cat].map (tag => ( tags[cat].map (tag => (
<li key={tag.id} className="mb-1"> <li key={tag.id} className="mb-1">
<TagLink tag={tag}/>
</li>))) : [])}
<TagLink tag={tag} prefetch onClick={onClick}/>
</li>)) : [])}
</ul> </ul>
<SectionTitle>関聯</SectionTitle> <SectionTitle>関聯</SectionTitle>
{posts.length > 0 && ( {posts.length > 0 && (


+ 13
- 8
frontend/src/pages/posts/PostListPage.tsx View File

@@ -10,6 +10,7 @@ import WikiBody from '@/components/WikiBody'
import TabGroup, { Tab } from '@/components/common/TabGroup' import TabGroup, { Tab } from '@/components/common/TabGroup'
import MainArea from '@/components/layout/MainArea' import MainArea from '@/components/layout/MainArea'
import { API_BASE_URL, SITE_TITLE } from '@/config' import { API_BASE_URL, SITE_TITLE } from '@/config'
import { fetchPosts } from '@/lib/posts'


import type { Post, WikiPage } from '@/types' import type { Post, WikiPage } from '@/types'


@@ -28,13 +29,11 @@ export default () => {
const loadMore = async (withCursor: boolean) => { const loadMore = async (withCursor: boolean) => {
setLoading (true) setLoading (true)


const res = await axios.get (`${ API_BASE_URL }/posts`, {
params: { tags: tags.join (' '),
match: anyFlg ? 'any' : 'all',
limit: '20',
...(withCursor && { cursor }) } })
const data = toCamel (res.data as any, { deep: true }) as { posts: Post[]
nextCursor: string }
const data = await fetchPosts ({
tags: tags.join (' '),
match: anyFlg ? 'any' : 'all',
limit: 20,
...(withCursor && { cursor }) })
setPosts (posts => ( setPosts (posts => (
[...((new Map ([...(withCursor ? posts : []), ...data.posts] [...((new Map ([...(withCursor ? posts : []), ...data.posts]
.map (post => [post.id, post]))) .map (post => [post.id, post])))
@@ -111,7 +110,13 @@ export default () => {
</title> </title>
</Helmet> </Helmet>


<TagSidebar posts={posts.slice (0, 20)}/>
<TagSidebar posts={posts.slice (0, 20)} onClick={() => {
const statesToSave = {
posts, cursor,
scroll: containerRef.current?.scrollTop ?? 0 }
sessionStorage.setItem (`posts:${ tagsQuery }`,
JSON.stringify (statesToSave))
}}/>


<MainArea> <MainArea>
<TabGroup> <TabGroup>


Loading…
Cancel
Save