document loading working
This commit is contained in:
parent
3e97241cf6
commit
7dc55799b8
14 changed files with 236 additions and 136 deletions
25
package-lock.json
generated
25
package-lock.json
generated
|
@ -8,6 +8,7 @@
|
||||||
"name": "eexiv-2",
|
"name": "eexiv-2",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@tanstack/react-query": "^5.20.2",
|
||||||
"minisearch": "^6.3.0",
|
"minisearch": "^6.3.0",
|
||||||
"next": "14.1.0",
|
"next": "14.1.0",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
|
@ -466,6 +467,30 @@
|
||||||
"tslib": "^2.4.0"
|
"tslib": "^2.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tanstack/query-core": {
|
||||||
|
"version": "5.20.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.20.2.tgz",
|
||||||
|
"integrity": "sha512-sAILwNiyA1I52e6imOsmNDUA/PuOayOzqz5jcLiIB5wBXqVk+HIiriWouPcAkjS8RqARfHUehuoPwcZ7Uzh0GQ==",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tanstack/react-query": {
|
||||||
|
"version": "5.20.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.20.2.tgz",
|
||||||
|
"integrity": "sha512-949myvMY77cPqwb71m3wRG2ypgwPijshO5kN9w0CDKWrFC0X8Wh1mwSqst88kIr58tWlWNsGy3U40AK23RgYQA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@tanstack/query-core": "5.20.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/file-saver": {
|
"node_modules/@types/file-saver": {
|
||||||
"version": "2.0.7",
|
"version": "2.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.7.tgz",
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
"format": "prettier --write ."
|
"format": "prettier --write ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@tanstack/react-query": "^5.20.2",
|
||||||
"minisearch": "^6.3.0",
|
"minisearch": "^6.3.0",
|
||||||
"next": "14.1.0",
|
"next": "14.1.0",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
|
|
|
@ -346,22 +346,20 @@ authorName (as a slug): {
|
||||||
website: website url
|
website: website url
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
export interface Authors {
|
export interface Author {
|
||||||
[key: string]: {
|
name: {
|
||||||
name: {
|
first: string
|
||||||
first: string
|
last: string
|
||||||
last: string
|
nickname?: string
|
||||||
nickname?: string
|
|
||||||
}
|
|
||||||
affiliation: string[]
|
|
||||||
image: string
|
|
||||||
nationality: string[]
|
|
||||||
formerAffiliations?: string[]
|
|
||||||
bio?: string
|
|
||||||
website?: string
|
|
||||||
}
|
}
|
||||||
|
affiliation: string[]
|
||||||
|
image: string
|
||||||
|
nationality: string[]
|
||||||
|
formerAffiliations?: string[]
|
||||||
|
bio?: string
|
||||||
|
website?: string
|
||||||
}
|
}
|
||||||
export const authors: Authors = {
|
export const authors: { [key: string]: Author } = {
|
||||||
shasan: {
|
shasan: {
|
||||||
name: {
|
name: {
|
||||||
first: 'Saim',
|
first: 'Saim',
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Document } from './data'
|
import { Document, Author } from './data'
|
||||||
|
|
||||||
export const loadDocument = (id: string): Promise<Document> => {
|
export const loadDocument = (id: string): Promise<Document> => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
@ -62,3 +62,32 @@ export const loadAllDocuments = (): Promise<{ [key: string]: Document }> => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const loadAllAuthors = (): Promise<{ [key: string]: Author }> => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (typeof Worker !== 'undefined') {
|
||||||
|
const worker = new Worker(
|
||||||
|
new URL('./workers/documentLoader.worker.ts', import.meta.url),
|
||||||
|
{ type: 'module' }
|
||||||
|
)
|
||||||
|
|
||||||
|
worker.onmessage = (e: MessageEvent<{ [key: string]: Author }>) => {
|
||||||
|
resolve(e.data)
|
||||||
|
worker.terminate()
|
||||||
|
}
|
||||||
|
|
||||||
|
worker.onerror = (error) => {
|
||||||
|
reject(error)
|
||||||
|
worker.terminate()
|
||||||
|
}
|
||||||
|
|
||||||
|
worker.postMessage('LOAD')
|
||||||
|
} else {
|
||||||
|
reject(
|
||||||
|
new Error(
|
||||||
|
'Web Workers are not supported in this environment. Please avoid using a prehistoric browser.'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
7
src/app/db/workers/affiliationLoader.worker.ts
Normal file
7
src/app/db/workers/affiliationLoader.worker.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { affiliations } from '../data'
|
||||||
|
|
||||||
|
onmessage = (e) => {
|
||||||
|
if (e.data === 'LOAD') {
|
||||||
|
self.postMessage(affiliations)
|
||||||
|
}
|
||||||
|
}
|
7
src/app/db/workers/authorLoader.worker.ts
Normal file
7
src/app/db/workers/authorLoader.worker.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { authors } from '../data'
|
||||||
|
|
||||||
|
onmessage = (e) => {
|
||||||
|
if (e.data === 'LOAD') {
|
||||||
|
self.postMessage(authors)
|
||||||
|
}
|
||||||
|
}
|
7
src/app/db/workers/nationalityLoader.worker.ts
Normal file
7
src/app/db/workers/nationalityLoader.worker.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { nationalities } from '../data'
|
||||||
|
|
||||||
|
onmessage = (e) => {
|
||||||
|
if (e.data === 'LOAD') {
|
||||||
|
self.postMessage(nationalities)
|
||||||
|
}
|
||||||
|
}
|
7
src/app/db/workers/topicLoader.worker.ts
Normal file
7
src/app/db/workers/topicLoader.worker.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { topics } from '../data'
|
||||||
|
|
||||||
|
onmessage = (e) => {
|
||||||
|
if (e.data === 'LOAD') {
|
||||||
|
self.postMessage(topics)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import { DocumentType, Document } from '@/app/db/data'
|
import { DocumentType } from '@/app/db/data'
|
||||||
import { Zilla_Slab } from 'next/font/google'
|
import { Zilla_Slab } from 'next/font/google'
|
||||||
import { epoch2datestring } from '@/app/utils/epoch2datestring'
|
import { epoch2datestring } from '@/app/utils/epoch2datestring'
|
||||||
import {
|
import {
|
||||||
|
@ -10,14 +10,21 @@ import {
|
||||||
} from '@/app/components/DataDisplay'
|
} from '@/app/components/DataDisplay'
|
||||||
import { ItemBadge, Status } from '@/app/components/Badges'
|
import { ItemBadge, Status } from '@/app/components/Badges'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
|
import { useSuspenseQuery } from '@tanstack/react-query'
|
||||||
|
import { loadDocument } from '@/app/db/loaders'
|
||||||
|
|
||||||
const zillaSlab = Zilla_Slab({ subsets: ['latin'], weight: ['500', '700'] })
|
const zillaSlab = Zilla_Slab({ subsets: ['latin'], weight: ['500', '700'] })
|
||||||
|
|
||||||
export default function DocumentViewer({
|
export default function DocumentViewer({ slug }: Readonly<{ slug: string }>) {
|
||||||
doc,
|
const { data, error } = useSuspenseQuery({
|
||||||
slug,
|
queryKey: [slug],
|
||||||
}: Readonly<{ doc: Document; slug: string }>) {
|
queryFn: () => {
|
||||||
const { manifest, abstract, file, citation } = doc
|
const data = loadDocument(slug)
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const { manifest, abstract, file, citation } = data
|
||||||
const {
|
const {
|
||||||
title,
|
title,
|
||||||
authors,
|
authors,
|
||||||
|
@ -31,6 +38,8 @@ export default function DocumentViewer({
|
||||||
status,
|
status,
|
||||||
} = manifest
|
} = manifest
|
||||||
|
|
||||||
|
if (error) throw error
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='max-w-4xl lg:max-w-6xl mx-auto'>
|
<div className='max-w-4xl lg:max-w-6xl mx-auto'>
|
||||||
<h1
|
<h1
|
||||||
|
|
|
@ -1,29 +1,13 @@
|
||||||
'use client'
|
'use client'
|
||||||
import { Zilla_Slab } from 'next/font/google'
|
|
||||||
import { DocumentType, reviewer, Document, documents } from '@/app/db/data'
|
|
||||||
import { loadDocument, loadAllDocuments } from '@/app/db/loaders'
|
|
||||||
import DocumentViewer from './DocumentViewer'
|
import DocumentViewer from './DocumentViewer'
|
||||||
import createResource from '@/app/utils/createResource'
|
import ErrorBoundary from '@/app/utils/ErrorBoundary'
|
||||||
import { Suspense, useEffect, useMemo } from 'react'
|
|
||||||
|
|
||||||
export default function Page({
|
export default function Page({
|
||||||
params,
|
params,
|
||||||
}: Readonly<{ params: { slug: string } }>) {
|
}: Readonly<{ params: { slug: string } }>) {
|
||||||
// const docResource = createResource(loadDocument(params.slug))
|
|
||||||
// const doc = docResource.read()
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const check = async () => {
|
|
||||||
const doc = await loadDocument(params.slug)
|
|
||||||
console.log(doc)
|
|
||||||
}
|
|
||||||
check()
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<ErrorBoundary>
|
||||||
<div>hi</div>
|
<DocumentViewer slug={params.slug} />
|
||||||
<DocumentViewer doc={documents[params.slug]} slug={params.slug} />
|
</ErrorBoundary>
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import Container from './container/Container'
|
||||||
import MobileMenu from './components/MobileMenu'
|
import MobileMenu from './components/MobileMenu'
|
||||||
import { ToastContainer } from 'react-toastify'
|
import { ToastContainer } from 'react-toastify'
|
||||||
import 'react-toastify/dist/ReactToastify.css'
|
import 'react-toastify/dist/ReactToastify.css'
|
||||||
|
import Providers from './providers'
|
||||||
|
|
||||||
/* The default font is Inter. If you want to use Zilla Slab (or any other Google Font,
|
/* The default font is Inter. If you want to use Zilla Slab (or any other Google Font,
|
||||||
which are pre-provided by Next.js in the 'next/font/google' module), you need to
|
which are pre-provided by Next.js in the 'next/font/google' module), you need to
|
||||||
|
@ -31,80 +32,82 @@ export default function RootLayout({
|
||||||
return (
|
return (
|
||||||
<html lang='en'>
|
<html lang='en'>
|
||||||
<body className={inter.className}>
|
<body className={inter.className}>
|
||||||
<ToastContainer />
|
<Providers>
|
||||||
<div className={styles.header}>
|
<ToastContainer />
|
||||||
<div className='max-w-[1200px] flex flex-nowrap mx-auto justify-between items-center'>
|
<div className={styles.header}>
|
||||||
<Link href='/affiliation/1280-eecs'>
|
<div className='max-w-[1200px] flex flex-nowrap mx-auto justify-between items-center'>
|
||||||
<img
|
<Link href='/affiliation/1280-eecs'>
|
||||||
className='h-[100px] mt-4'
|
<img
|
||||||
src='/img/logos/eecs-wordmark.png'
|
className='h-[100px] mt-4'
|
||||||
alt='EECS'
|
src='/img/logos/eecs-wordmark.png'
|
||||||
/>
|
alt='EECS'
|
||||||
</Link>
|
/>
|
||||||
<p className={`max-w-[600px] hidden md:inline`}>
|
|
||||||
We gratefully acknowledge support from our volunteer peer
|
|
||||||
reviewers, member institutions, and all{' '}
|
|
||||||
<a
|
|
||||||
href='https://github.com/couscousdude/eeXiv-2/graphs/contributors'
|
|
||||||
target='_blank'
|
|
||||||
>
|
|
||||||
open-source contributors
|
|
||||||
</a>
|
|
||||||
.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className={`${styles.banner} w-full h-[100px] mb-[50px]`}>
|
|
||||||
<div className='max-w-[1200px] flex justify-between mx-auto items-center pt-3 flex-nowrap'>
|
|
||||||
<h1 className={`${styles.title} ${zillaSlab.className} mx-10`}>
|
|
||||||
<Link href='/' className='no-link-style'>
|
|
||||||
eeXiv
|
|
||||||
</Link>
|
</Link>
|
||||||
</h1>
|
<p className={`max-w-[600px] hidden md:inline`}>
|
||||||
<div className='hidden md:inline'>
|
We gratefully acknowledge support from our volunteer peer
|
||||||
<SearchBar />
|
reviewers, member institutions, and all{' '}
|
||||||
</div>
|
<a
|
||||||
<div className='md:hidden'>
|
href='https://github.com/couscousdude/eeXiv-2/graphs/contributors'
|
||||||
<MobileMenu />
|
target='_blank'
|
||||||
|
>
|
||||||
|
open-source contributors
|
||||||
|
</a>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className={`${styles.banner} w-full h-[100px] mb-[50px]`}>
|
||||||
<Container>{children}</Container>
|
<div className='max-w-[1200px] flex justify-between mx-auto items-center pt-3 flex-nowrap'>
|
||||||
<footer>
|
<h1 className={`${styles.title} ${zillaSlab.className} mx-10`}>
|
||||||
<div className={styles.footerContent}>
|
<Link href='/' className='no-link-style'>
|
||||||
<ul>
|
eeXiv
|
||||||
<li key='about'>
|
|
||||||
<Link href='/about'>About</Link>
|
|
||||||
</li>
|
|
||||||
<li key='help'>
|
|
||||||
<Link href='/help'>Help</Link>
|
|
||||||
</li>
|
|
||||||
<li key='contact'>
|
|
||||||
<Link href='/contact'>Contact</Link>
|
|
||||||
</li>
|
|
||||||
<li key='subscribe'>
|
|
||||||
<Link href='/subscribe'>Subscribe</Link>
|
|
||||||
</li>
|
|
||||||
<li key='copyright'>
|
|
||||||
<Link href='/legal/copyright'>Copyright</Link>
|
|
||||||
</li>
|
|
||||||
<li key='privacy'>
|
|
||||||
<Link href='/legal/privacy'>Privacy Policy</Link>
|
|
||||||
</li>
|
|
||||||
<li key='accessibility'>
|
|
||||||
<Link href='/help/accessibility'>Accessibility</Link>
|
|
||||||
</li>
|
|
||||||
<li key='status'>
|
|
||||||
<Link href='/status'>eeXiv status</Link>
|
|
||||||
</li>
|
|
||||||
<li key='notifications'>
|
|
||||||
<Link href='/status/notifications'>
|
|
||||||
Get status notifications
|
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</h1>
|
||||||
</ul>
|
<div className='hidden md:inline'>
|
||||||
|
<SearchBar />
|
||||||
|
</div>
|
||||||
|
<div className='md:hidden'>
|
||||||
|
<MobileMenu />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
<Container>{children}</Container>
|
||||||
|
<footer>
|
||||||
|
<div className={styles.footerContent}>
|
||||||
|
<ul>
|
||||||
|
<li key='about'>
|
||||||
|
<Link href='/about'>About</Link>
|
||||||
|
</li>
|
||||||
|
<li key='help'>
|
||||||
|
<Link href='/help'>Help</Link>
|
||||||
|
</li>
|
||||||
|
<li key='contact'>
|
||||||
|
<Link href='/contact'>Contact</Link>
|
||||||
|
</li>
|
||||||
|
<li key='subscribe'>
|
||||||
|
<Link href='/subscribe'>Subscribe</Link>
|
||||||
|
</li>
|
||||||
|
<li key='copyright'>
|
||||||
|
<Link href='/legal/copyright'>Copyright</Link>
|
||||||
|
</li>
|
||||||
|
<li key='privacy'>
|
||||||
|
<Link href='/legal/privacy'>Privacy Policy</Link>
|
||||||
|
</li>
|
||||||
|
<li key='accessibility'>
|
||||||
|
<Link href='/help/accessibility'>Accessibility</Link>
|
||||||
|
</li>
|
||||||
|
<li key='status'>
|
||||||
|
<Link href='/status'>eeXiv status</Link>
|
||||||
|
</li>
|
||||||
|
<li key='notifications'>
|
||||||
|
<Link href='/status/notifications'>
|
||||||
|
Get status notifications
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</Providers>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
)
|
)
|
||||||
|
|
20
src/app/providers.jsx
Normal file
20
src/app/providers.jsx
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||||
|
|
||||||
|
export default function Providers({ children }) {
|
||||||
|
const [queryClient] = useState(
|
||||||
|
() =>
|
||||||
|
new QueryClient({
|
||||||
|
defaultOptions: {
|
||||||
|
queries: {
|
||||||
|
staleTime: 60 * 1000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||||
|
)
|
||||||
|
}
|
29
src/app/utils/ErrorBoundary.jsx
Normal file
29
src/app/utils/ErrorBoundary.jsx
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import React from 'react'
|
||||||
|
import { notFound } from 'next/navigation'
|
||||||
|
|
||||||
|
export default class ErrorBoundary extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
this.state = { hasError: false, error: null }
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDerivedStateFromError(error) {
|
||||||
|
// Update state so the next render will show the fallback UI.
|
||||||
|
return { hasError: true, error }
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidCatch(error, errorInfo) {
|
||||||
|
// You can also log the error to an error reporting service
|
||||||
|
console.error('Error caught by Error Boundary:', error, errorInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.state.hasError) {
|
||||||
|
// You can render any custom fallback UI
|
||||||
|
if (this.state.error.message === '404') notFound()
|
||||||
|
return <h1>Something went wrong.</h1>
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.props.children
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,26 +0,0 @@
|
||||||
export default function wrapPromise(promise: Promise<any>) {
|
|
||||||
let status = 'pending'
|
|
||||||
let result: any
|
|
||||||
let suspender = promise.then(
|
|
||||||
(r) => {
|
|
||||||
status = 'success'
|
|
||||||
result = r
|
|
||||||
},
|
|
||||||
(e) => {
|
|
||||||
status = 'error'
|
|
||||||
result = e
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
read() {
|
|
||||||
if (status === 'pending') {
|
|
||||||
throw suspender
|
|
||||||
} else if (status === 'error') {
|
|
||||||
throw result
|
|
||||||
} else if (status === 'success') {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue