refs #4653 Display hashtag timeline in detail

This commit is contained in:
AkiraFukushima 2024-01-02 13:24:18 +09:00
parent aa8f0922fb
commit a2c61be256
No known key found for this signature in database
GPG Key ID: B6E51BAC4DE1A957
6 changed files with 72 additions and 7 deletions

View File

@ -5,6 +5,7 @@ import Thread from './Thread'
import { Entity, MegalodonInterface } from 'megalodon'
import Reply from './Reply'
import Profile from './Profile'
import Tag from './Tag'
import { Account } from '@/db'
type Props = {
@ -14,7 +15,7 @@ type Props = {
} & HTMLAttributes<HTMLElement>
export default function Detail(props: Props) {
const [target, setTarget] = useState<'status' | 'reply' | 'profile' | null>(null)
const [target, setTarget] = useState<'status' | 'reply' | 'profile' | 'tag' | null>(null)
const router = useRouter()
useEffect(() => {
@ -24,6 +25,8 @@ export default function Detail(props: Props) {
setTarget('reply')
} else if (router.query.user_id) {
setTarget('profile')
} else if (router.query.tag) {
setTarget('tag')
} else {
setTarget(null)
}
@ -42,8 +45,13 @@ export default function Detail(props: Props) {
{target && (
<div className={`bg-white ${props.className}`} style={props.style}>
<div className="bg-blue-900 text-gray-200 flex justify-between p-2 items-center" style={{ height: '50px' }}>
<FaChevronLeft onClick={back} className="cursor-pointer text-lg" />
<FaX onClick={close} className="cursor-pointer text-lg" />
<div className="flex gap-4 items-center">
<FaChevronLeft onClick={back} className="cursor-pointer text-lg" />
{target === 'tag' && `#${router.query.tag}`}
</div>
<div>
<FaX onClick={close} className="cursor-pointer text-lg" />
</div>
</div>
{target === 'status' && (
<Thread
@ -64,6 +72,9 @@ export default function Detail(props: Props) {
{target === 'profile' && (
<Profile client={props.client} account={props.account} user_id={router.query.user_id as string} openMedia={props.openMedia} />
)}
{target === 'tag' && (
<Tag client={props.client} account={props.account} tag={router.query.tag as string} openMedia={props.openMedia} />
)}
</div>
)}
</>

View File

@ -49,6 +49,7 @@ export default function Reply(props: Props) {
<div className="overflow-x-hidden" style={{ height: 'calc(100% - 50px)' }}>
<Virtuoso
style={{ height: `calc(100% - ${composeHeight}px)` }}
className="timeline-scrollable"
data={[...ancestors, status].filter(s => s !== null)}
itemContent={(_, status) => (
<Status

View File

@ -0,0 +1,46 @@
import { Account } from '@/db'
import { Entity, MegalodonInterface } from 'megalodon'
import { useEffect, useState } from 'react'
import { Virtuoso } from 'react-virtuoso'
import Status from '../timelines/status/Status'
type Props = {
client: MegalodonInterface
account: Account
tag: string
openMedia: (media: Entity.Attachment) => void
}
export default function Tag(props: Props) {
const [statuses, setStatuses] = useState<Array<Entity.Status>>([])
useEffect(() => {
if (props.client && props.tag) {
const f = async () => {
const res = await props.client.getTagTimeline(props.tag)
setStatuses(res.data)
}
f()
}
}, [props.client, props.tag])
return (
<div className="overflow-x-hidden" style={{ height: 'calc(100% - 50px)' }}>
<Virtuoso
style={{ height: '100%' }}
data={statuses}
className="timeline-scrollable"
itemContent={(_, status) => (
<Status
client={props.client}
account={props.account}
status={status}
key={status.id}
onRefresh={() => {}}
openMedia={props.openMedia}
/>
)}
/>
</div>
)
}

View File

@ -33,6 +33,7 @@ export default function Thread(props: Props) {
<>
<Virtuoso
style={{ height: 'calc(100% - 50px)' }}
className="timeline-scrollable"
data={[...ancestors, status, ...descendants].filter(s => s !== null)}
itemContent={(_, status) => (
<Status

View File

@ -3,7 +3,7 @@ import { CustomFlowbiteTheme, Flowbite, Sidebar } from 'flowbite-react'
import generator, { Entity } from 'megalodon'
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'
import { FaBell, FaGlobe, FaHashtag, FaHouse, FaUsers } from 'react-icons/fa6'
import { FaBell, FaGlobe, FaHouse, FaList, FaUsers } from 'react-icons/fa6'
import { useIntl } from 'react-intl'
type LayoutProps = {
@ -107,7 +107,7 @@ export default function Layout({ children }: LayoutProps) {
key={list.id}
active={router.asPath.includes(`list_${list.id}`)}
onClick={() => router.push({ pathname: `/accounts/${router.query.id}/list_${list.id}` })}
icon={FaHashtag}
icon={FaList}
className="sidebar-menu-item"
>
<span className="sidebar-menu">{list.title}</span>

View File

@ -10,7 +10,7 @@ import { FormattedMessage } from 'react-intl'
import Actions from './Actions'
import { useRouter } from 'next/router'
import { MouseEventHandler, useState } from 'react'
import { findAccount, findLink, ParsedAccount, accountMatch } from '@/utils/statusParser'
import { findAccount, findLink, ParsedAccount, accountMatch, findTag } from '@/utils/statusParser'
import { Account } from '@/db'
type Props = {
@ -54,7 +54,13 @@ export default function Status(props: Props) {
return
}
// TODO: find tag
const parsedTag = findTag(e.target as HTMLElement, 'status-body')
if (parsedTag) {
e.preventDefault()
e.stopPropagation()
router.push({ query: { id: router.query.id, timeline: router.query.timeline, tag: parsedTag, detail: true } })
return
}
const url = findLink(e.target as HTMLElement, 'status-body')
if (url) {