Skip to content

Commit

Permalink
refactor(tag): make display tag route the main route
Browse files Browse the repository at this point in the history
The old tags/$tag route is moved to tags/single/$tag.
tags/single is used by the questions route and so could be removed if questions route is removed.
  • Loading branch information
jrhender committed Feb 9, 2024
1 parent eb94e9b commit 7ea13d7
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 204 deletions.
2 changes: 1 addition & 1 deletion app/routes/questions.$questionId.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import AutoHeight from 'react-auto-height'
import type {Question, Glossary, PageId, Banner as BannerType, Tag} from '~/server-utils/stampy'
import type useQuestionStateInUrl from '~/hooks/useQuestionStateInUrl'
import {Edit, Link as LinkIcon} from '~/components/icons-generated'
import {Tags} from '~/routes/tags.$tag'
import {Tags} from '~/routes/tags.single.$tag'
import CopyLink from '~/components/copyLink'
import {Action, ActionType} from '~/routes/questions.actions'
import {reloadInBackgroundIfNeeded} from '~/server-utils/kv-cache'
Expand Down
175 changes: 68 additions & 107 deletions app/routes/tags.$tag.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,28 @@
import {useState, useEffect, ReactNode} from 'react'
import {useState, useEffect} from 'react'
import {LoaderFunction} from '@remix-run/cloudflare'
import {reloadInBackgroundIfNeeded} from '~/server-utils/kv-cache'
import {Tag as TagType, QuestionState, RelatedQuestions, loadTag} from '~/server-utils/stampy'
import Dialog from '~/components/dialog'

type Props = {
tags: string[]
selectQuestion: (pageid: string, title: string) => void
[k: string]: unknown
}
import {Tag as TagType, loadTags} from '~/server-utils/stampy'
import Footer from '~/components/Footer'
import Header from '~/components/Header'
import useToC from '~/hooks/useToC'
import {useLoaderData, useNavigate} from '@remix-run/react'
import {ListTable} from '~/components/Table/ListTable'
import {CategoriesNav} from '~/components/CategoriesNav/Menu'

export const loader = async ({request, params}: Parameters<LoaderFunction>[0]) => {
const {tag: tagFromUrl} = params
if (!tagFromUrl) {
throw Error('missing tag name')
}

try {
return await loadTag(request, tagFromUrl)
} catch (error: unknown) {
console.error(`error fetching tag "${tagFromUrl}":`, error)
return {
error: error?.toString(),
timestamp: new Date().toISOString(),
data: [],
}
const tags = await loadTags(request)
const currentTag = tags.data.find((tagData) => tagData.name === tagFromUrl)
if (currentTag === undefined) {
throw new Response(null, {
status: 404,
statusText: 'Unable to find requested tag',
})
}
return {...tags, currentTag}
}

export const sortFuncs = {
Expand All @@ -34,102 +31,66 @@ export const sortFuncs = {
'by number of questions': (a: TagType, b: TagType) => b.questions.length - a.questions.length,
}

export async function fetchTag(tagName: string): Promise<TagType | never[]> {
const url = `/tags/${encodeURIComponent(tagName)}`
return fetch(url).then(async (response) => {
const json: Awaited<ReturnType<typeof loader>> = await response.json()
if ('error' in json) console.error(json.error)
const {data, timestamp} = json
export default function App() {
const {currentTag, data} = useLoaderData<ReturnType<typeof loader>>()
const [selectedTag, setSelectedTag] = useState<TagType | null>(null)
const [tagsFilter, setTagsFilter] = useState<string>('')
const {toc} = useToC()
const navigate = useNavigate()

reloadInBackgroundIfNeeded(url, timestamp)

return data
})
}

export function Tag({
name,
questions: tqs,
showCount,
}: {
name: string
questions?: RelatedQuestions
showCount?: boolean
}) {
const [questions, setQuestions] = useState(tqs)
const pageIds = questions?.map((q) => q.pageid).join(QuestionState.COLLAPSED)
const [sortBy] = useState<keyof typeof sortFuncs>('alphabetically')

useEffect(() => {
const fetcher = async () => {
if (!questions) {
const tag = (await fetchTag(name)) as TagType
if (tag) setQuestions(tag.questions)
}
if (selectedTag !== currentTag) {
setSelectedTag(currentTag)
}
fetcher()
}, [questions, name])

}, [selectedTag, data, currentTag])
if (selectedTag === null) {
return null
}
return (
<a className="tag" href={`/?state=${pageIds}${QuestionState.COLLAPSED}`} key={name}>
<span className="tag-name">{name}</span>
{showCount && <span className="tag-stat">({questions?.length})</span>}
</a>
)
}
<>
<Header toc={toc} categories={data} />
<div className={'top-margin-large'} />
<main>
<div className={'group-elements'}>
<CategoriesNav
categories={
data
.filter((tag) => tag.questions.length > 0)
.filter((tag) => tag.name.toLowerCase().includes(tagsFilter.toLowerCase()))
.sort(sortFuncs[sortBy])

// {title: "AI Safety", id: 1},
}
active={selectedTag}
onClick={(selectedTag) => {
navigate(`../${selectedTag.name}`, {relative: 'path'})
}}
onChange={setTagsFilter}
/>

export function TagQuestions({
tag,
selectQuestion,
clearTag,
}: {
tag: TagType
selectQuestion: (pageid: string, title: string) => void
clearTag: () => void
}) {
const TagDialog = ({children}: {children: ReactNode | ReactNode[]}) => (
<Dialog onClose={() => clearTag()}>
<div className="dialog-title">
<div className="dialog-title-header">
Select a question from the <b>{tag.name}</b> tag...
{selectedTag === null ? null : (
<div>
<h1 style={{marginTop: '0px'}}>{selectedTag.name}</h1>
{selectedTag.questions.length === 0 ? (
<div className={'no-questions'}>No questions found</div>
) : (
<p>
{selectedTag.questions.length} pages tagged {`"${selectedTag.name}"`}
</p>
)}
{selectedTag && <ListTable elements={selectedTag.questions} />}
</div>
)}
</div>
</div>
{children}
</Dialog>
)
</main>

if (!tag.rowId) {
return (
<TagDialog>
<div className="loader"></div>
</TagDialog>
)
}
<div className={'top-margin-large-with-border'} />

return (
<TagDialog>
<div className="tag-questions">
{tag.questions.map((question) => (
<div key={tag.rowId + '-' + question.pageid}>
<button
className="question-tag"
onClick={() => {
selectQuestion(question.pageid, question.title)
clearTag()
}}
>
{question.title}
</button>
</div>
))}
</div>
</TagDialog>
)
}
<div className={'top-margin-large'} />

export function Tags({tags}: Props) {
return (
<div className="tags-container">
<div>{tags && tags.map((name) => <Tag key={name} name={name} />)}</div>
</div>
<Footer />
</>
)
}
}
96 changes: 0 additions & 96 deletions app/routes/tags.display.$tag.tsx

This file was deleted.

Loading

0 comments on commit 7ea13d7

Please sign in to comment.