Render internal links to card.

This commit is contained in:
nyne
2025-05-23 19:30:46 +08:00
parent 0bc97b1db5
commit 8a7af82b6c
5 changed files with 159 additions and 96 deletions

View File

@@ -67,6 +67,7 @@ export interface ResourceDetails {
author: User; author: User;
views: number; views: number;
downloads: number; downloads: number;
related: Resource[];
} }
export interface Storage { export interface Storage {

View File

@@ -27,10 +27,6 @@ class Network {
} }
init() { init() {
if (import.meta.env.MODE === 'development') {
this.baseUrl = 'http://localhost:3000';
this.apiBaseUrl = 'http://localhost:3000/api';
}
axios.defaults.validateStatus = _ => true axios.defaults.validateStatus = _ => true
axios.interceptors.request.use((config) => { axios.interceptors.request.use((config) => {
if (app.token) { if (app.token) {

View File

@@ -123,7 +123,7 @@ export default function ResourcePage() {
</span> </span>
</label> </label>
<div key={"article"} className="tab-content p-2"> <div key={"article"} className="tab-content p-2">
<Article article={resource.article}/> <Article resource={resource} />
</div> </div>
<label className="tab transition-all"> <label className="tab transition-all">
@@ -221,9 +221,67 @@ function DeleteResourceDialog({resourceId, uploaderId}: { resourceId: number, up
const context = createContext<() => void>(() => { const context = createContext<() => void>(() => {
}) })
function Article({article}: { article: string }) { function Article({ resource }: { resource: ResourceDetails }) {
return <article> const articleRef = useRef<HTMLDivElement>(null)
<Markdown>{article}</Markdown>
const navigate = useNavigate()
useEffect(() => {
if (articleRef.current) {
console.log("render")
for (let child of articleRef.current.children) {
console.log("child", child)
if (child.tagName === "P" && child.children.length === 1 && child.children[0].tagName === "A") {
const href = (child.children[0] as HTMLAnchorElement).href as string
console.log("href", href)
console.log("origin", window.location.origin)
if (href.startsWith(window.location.origin) || href.startsWith("/")) {
console.log("href starts with origin")
let path = href
if (path.startsWith(window.location.origin)) {
path = path.substring(window.location.origin.length)
}
if (path.startsWith("/resources/")) {
const content = child.children[0].innerHTML
const id = path.substring("/resources/".length)
for (let r of resource.related) {
if (r.id.toString() === id) {
child.children[0].classList.add("hidden")
let div = document.createElement("div")
div.innerHTML = `
${child.innerHTML}
<div class="card card-border max-w-72 sm:max-w-full border-base-300 my-2 sm:card-side">
${r.image ? `
<figure>
<img
class="w-full h-40 sm:h-full sm:w-32 object-cover"
src="${network.getImageUrl(r.image!.id)}"
alt="Cover" />
</figure>
` : ""}
<div class="card-body" style="padding: 1rem">
<h3>${r.title}</h4>
<p class="text-sm">${content}</p>
</div>
</div>
`
child.appendChild(div)
}
(child as HTMLParagraphElement).onclick = (e) => {
e.stopPropagation()
e.preventDefault()
navigate(`/resources/${r.id}`)
}
}
}
}
}
}
}
}, [resource])
return <article ref={articleRef}>
<Markdown>{resource.article}</Markdown>
</article> </article>
} }

View File

@@ -5,4 +5,12 @@ import tailwindcss from '@tailwindcss/vite'
// https://vite.dev/config/ // https://vite.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [react(), tailwindcss(),], plugins: [react(), tailwindcss(),],
server: {
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
},
},
},
}) })

View File

@@ -91,7 +91,7 @@ func parseResourceIfPresent(line string, host string) *model.ResourceView {
if err != nil { if err != nil {
return nil return nil
} }
if parsed.Hostname() != host { if parsed.IsAbs() && parsed.Hostname() != host {
return nil return nil
} }
path := parsed.Path path := parsed.Path