Merge pull request #5 from Team-1280/better-documents
This commit is contained in:
commit
27fc474a62
22 changed files with 450 additions and 101 deletions
27
.github/workflows/style.yml
vendored
Normal file
27
.github/workflows/style.yml
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
name: npm run format
|
||||
|
||||
on: [push, workflow_dispatch]
|
||||
|
||||
jobs:
|
||||
style:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
# Give the default GITHUB_TOKEN write permission to commit and push the
|
||||
# added or changed files to the repository.
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '20.x'
|
||||
- run: npm install
|
||||
- run: npm run format
|
||||
|
||||
- uses: stefanzweifel/git-auto-commit-action@v5
|
||||
with:
|
||||
commit_message: "style: format"
|
1
.prettierignore
Normal file
1
.prettierignore
Normal file
|
@ -0,0 +1 @@
|
|||
*.yml
|
168
package-lock.json
generated
168
package-lock.json
generated
|
@ -521,6 +521,32 @@
|
|||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "0.3.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.9",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
||||
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint-community/eslint-utils": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
|
||||
|
@ -971,6 +997,38 @@
|
|||
"react": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tsconfig/node10": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
|
||||
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@tsconfig/node12": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
||||
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@tsconfig/node14": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
||||
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@tsconfig/node16": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
|
||||
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
||||
|
@ -1182,6 +1240,17 @@
|
|||
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-walk": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz",
|
||||
"integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
|
@ -1729,6 +1798,14 @@
|
|||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
||||
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="
|
||||
},
|
||||
"node_modules/create-require": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
|
@ -3568,6 +3645,14 @@
|
|||
"node": "14 || >=16.14"
|
||||
}
|
||||
},
|
||||
"node_modules/make-error": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/merge2": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||
|
@ -5029,6 +5114,70 @@
|
|||
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ts-node": {
|
||||
"version": "10.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
|
||||
"integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@cspotcode/source-map-support": "^0.8.0",
|
||||
"@tsconfig/node10": "^1.0.7",
|
||||
"@tsconfig/node12": "^1.0.7",
|
||||
"@tsconfig/node14": "^1.0.0",
|
||||
"@tsconfig/node16": "^1.0.2",
|
||||
"acorn": "^8.4.1",
|
||||
"acorn-walk": "^8.1.1",
|
||||
"arg": "^4.1.0",
|
||||
"create-require": "^1.1.0",
|
||||
"diff": "^4.0.1",
|
||||
"make-error": "^1.1.1",
|
||||
"v8-compile-cache-lib": "^3.0.1",
|
||||
"yn": "3.1.1"
|
||||
},
|
||||
"bin": {
|
||||
"ts-node": "dist/bin.js",
|
||||
"ts-node-cwd": "dist/bin-cwd.js",
|
||||
"ts-node-esm": "dist/bin-esm.js",
|
||||
"ts-node-script": "dist/bin-script.js",
|
||||
"ts-node-transpile-only": "dist/bin-transpile.js",
|
||||
"ts-script": "dist/bin-script-deprecated.js"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@swc/core": ">=1.2.50",
|
||||
"@swc/wasm": ">=1.2.50",
|
||||
"@types/node": "*",
|
||||
"typescript": ">=2.7"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@swc/core": {
|
||||
"optional": true
|
||||
},
|
||||
"@swc/wasm": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/ts-node/node_modules/arg": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/ts-node/node_modules/diff": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/tsconfig-paths": {
|
||||
"version": "3.15.0",
|
||||
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
|
||||
|
@ -5243,6 +5392,14 @@
|
|||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/v8-compile-cache-lib": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/webpack-sources": {
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
|
||||
|
@ -5462,6 +5619,17 @@
|
|||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/yn": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/yocto-queue": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
||||
|
|
|
@ -28,10 +28,7 @@ export default function Page() {
|
|||
<li>
|
||||
You can contribute to our website development or add your
|
||||
documents and user account to eeXiv on our{' '}
|
||||
<Link
|
||||
href='https://github.com/team-1280/eexiv'
|
||||
target='_blank'
|
||||
>
|
||||
<Link href='https://github.com/team-1280/eexiv' target='_blank'>
|
||||
GitHub repository
|
||||
</Link>
|
||||
.
|
||||
|
|
|
@ -178,10 +178,10 @@ export default function AuthorDisplay({
|
|||
))}
|
||||
</div>
|
||||
<Bio />
|
||||
<hr className='mx-auto w-full h-1 border-0 bg-slate-200 my-2 rounded-md' />
|
||||
<br />
|
||||
{authorsDocuments.length > 0 && (
|
||||
<>
|
||||
<hr className='mx-auto w-full h-1 border-0 bg-slate-200 my-2 rounded-md' />
|
||||
<br />
|
||||
<h1 className='text-3xl md:my-6 my-4 font-serif'>
|
||||
Published documents
|
||||
</h1>
|
||||
|
|
|
@ -31,14 +31,21 @@ export default function News() {
|
|||
eeXiv is currently under active development! There may be major
|
||||
updates, breaking changes, or weird bugs. Report bugs, suggest new
|
||||
features, or give us feedback at{' '}
|
||||
<a href='https://github.com/team-1280/eexiv/issues' target='_blank' className='text-blue-300'>
|
||||
<a
|
||||
href='https://github.com/team-1280/eexiv/issues'
|
||||
target='_blank'
|
||||
className='text-blue-300'
|
||||
>
|
||||
our issue tracker.
|
||||
</a>
|
||||
</li>
|
||||
<li key={4}>
|
||||
Want to upload your documents or just make yourself a profile on
|
||||
eeXiv? Check our <Link href='/about' className='text-blue-300'>about page</Link> for more
|
||||
information!
|
||||
eeXiv? Check our{' '}
|
||||
<Link href='/about' className='text-blue-300'>
|
||||
about page
|
||||
</Link>{' '}
|
||||
for more information!
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -1,31 +1,3 @@
|
|||
/*
|
||||
documents db schema
|
||||
documents {
|
||||
slug: {
|
||||
manifest: {
|
||||
title: string
|
||||
authors: string[]
|
||||
date: unix epoch integer[] -> if multiple revisions, put the earlier dates first
|
||||
type: presentation | report | whitepaper | other
|
||||
latest: integer >= 1 -> the latest revision of the document (earliest = 1)
|
||||
keywords: string[]
|
||||
topics: string[]
|
||||
references: string[],
|
||||
code: url[]
|
||||
},
|
||||
abstract: string,
|
||||
file: pdf | docx | pptx | targz | other, named file[rev].[ext]
|
||||
(eg. revision 1 = file1.pdf, revision 2 = file2.pdf, etc)
|
||||
the "latest" should be the latest revision
|
||||
citation: a string that can be used to cite the document
|
||||
reviewers: an array of reviewers, following the reviewer format. if you specify a local
|
||||
profile username, it will link to the author's profile, and take priority over the link
|
||||
status: draft | under review | reviewed | published no review
|
||||
note: published no review should be used for documents where peer review
|
||||
is not appropriate or unnecessary
|
||||
}
|
||||
}
|
||||
*/
|
||||
export type FileType = 'pdf' | 'docx' | 'pptx' | 'tar.gz' | 'other'
|
||||
export type DocumentType =
|
||||
| 'presentation'
|
||||
|
@ -42,11 +14,13 @@ export type reviewer = {
|
|||
profile?: string
|
||||
url?: string
|
||||
}
|
||||
|
||||
export type DocumentStatus =
|
||||
| 'draft'
|
||||
| 'under review'
|
||||
| 'reviewed'
|
||||
| 'published no review'
|
||||
|
||||
export interface Document {
|
||||
manifest: DocumentManifest
|
||||
abstract: string
|
||||
|
|
|
@ -83,16 +83,15 @@ export const loadAllAuthors = (): Promise<{ [key: string]: Author }> => {
|
|||
|
||||
worker.postMessage('LOAD')
|
||||
} else {
|
||||
reject(
|
||||
new Error(
|
||||
'Web Workers are not supported in this environment. Please avoid using a prehistoric browser.'
|
||||
)
|
||||
)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const loadAuthor = (id: string): Promise<Author> => {
|
||||
export const loadAuthors = (
|
||||
authorIds: string[]
|
||||
): Promise<{ [key: string]: Author }> => {
|
||||
'use client'
|
||||
return new Promise((resolve, reject) => {
|
||||
if (typeof Worker !== 'undefined') {
|
||||
const worker = new Worker(
|
||||
|
@ -101,12 +100,10 @@ export const loadAuthor = (id: string): Promise<Author> => {
|
|||
)
|
||||
|
||||
worker.onmessage = (e: MessageEvent<{ [key: string]: Author }>) => {
|
||||
const data = e.data
|
||||
const author: Author | undefined = data[id]
|
||||
if (!author) {
|
||||
return reject(new Error('404'))
|
||||
if (typeof e.data === 'object' && Object.keys(e.data).length > 0) {
|
||||
resolve(e.data)
|
||||
} else {
|
||||
resolve(author)
|
||||
reject(new Error('404'))
|
||||
}
|
||||
worker.terminate()
|
||||
}
|
||||
|
@ -116,7 +113,7 @@ export const loadAuthor = (id: string): Promise<Author> => {
|
|||
worker.terminate()
|
||||
}
|
||||
|
||||
worker.postMessage('LOAD')
|
||||
worker.postMessage(authorIds)
|
||||
} else {
|
||||
reject(
|
||||
new Error(
|
||||
|
|
|
@ -1,7 +1,30 @@
|
|||
import { authors } from '../data'
|
||||
import { authors, Author } from '../data'
|
||||
|
||||
export function getAuthorsById(authorIds: string[]): { [key: string]: Author } {
|
||||
const result: { [key: string]: Author } = {}
|
||||
|
||||
// Iterate through the array of author IDs
|
||||
for (const id of authorIds) {
|
||||
const author = authors[id] // Retrieve the author entry by ID
|
||||
if (author) {
|
||||
result[id] = author // If the author exists, add it to the result object
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
const checkIsStringArray = (data: unknown): data is string[] => {
|
||||
if (Array.isArray(data)) {
|
||||
return data.every((d) => typeof d === 'string')
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
onmessage = (e) => {
|
||||
if (e.data === 'LOAD') {
|
||||
self.postMessage(authors)
|
||||
}
|
||||
let authorIds: string[] = []
|
||||
checkIsStringArray(e.data) && (authorIds = e.data as string[])
|
||||
let results = getAuthorsById(authorIds)
|
||||
|
||||
postMessage(results)
|
||||
}
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
export default function Page() {
|
||||
return (
|
||||
<h1>
|
||||
This is a placeholder for a document browser that will be implemented in
|
||||
the future. In the meantime, you can view specific documents by searching.
|
||||
</h1>
|
||||
)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { DocumentType, documents } from '@/app/db/data'
|
||||
'use client'
|
||||
import { Zilla_Slab } from 'next/font/google'
|
||||
import { epoch2datestring } from '@/app/utils/epoch2datestring'
|
||||
import {
|
||||
|
@ -9,14 +9,29 @@ import {
|
|||
Reviewers,
|
||||
} from '@/app/components/DataDisplay'
|
||||
import { ItemBadge, Status } from '@/app/components/Badges'
|
||||
import Link from 'next/link'
|
||||
import { notFound } from 'next/navigation'
|
||||
import VersionChooser from './VersionChooser'
|
||||
import crypto from 'crypto'
|
||||
import { Suspense } from 'react'
|
||||
import { loadDocument } from '@/app/db/loaders'
|
||||
import { useSuspenseQuery } from '@tanstack/react-query'
|
||||
|
||||
const zillaSlab = Zilla_Slab({ subsets: ['latin'], weight: ['500'] })
|
||||
|
||||
export default function DocumentViewer({ slug }: Readonly<{ slug: string }>) {
|
||||
const { manifest, abstract, file, citation } = documents[slug]
|
||||
if (!manifest) return notFound()
|
||||
const DocumentViewer = ({ slug }: Readonly<{ slug: string }>) => {
|
||||
const { data, error } = useSuspenseQuery({
|
||||
queryKey: [slug],
|
||||
queryFn: () => {
|
||||
const data = loadDocument(slug)
|
||||
return data
|
||||
},
|
||||
})
|
||||
if (error) throw error
|
||||
let doc = data
|
||||
// const doc = documents[slug]
|
||||
const { manifest, abstract, citation } = doc
|
||||
|
||||
// if (!manifest) return notFound()
|
||||
const {
|
||||
title,
|
||||
authors,
|
||||
|
@ -30,6 +45,13 @@ export default function DocumentViewer({ slug }: Readonly<{ slug: string }>) {
|
|||
status,
|
||||
} = manifest
|
||||
|
||||
// git style hash
|
||||
const hash = crypto
|
||||
.createHash('sha256')
|
||||
.update(slug)
|
||||
.digest('hex')
|
||||
.substring(0, 7)
|
||||
|
||||
return (
|
||||
<div className='max-w-4xl lg:max-w-6xl mx-auto'>
|
||||
<h1
|
||||
|
@ -51,7 +73,7 @@ export default function DocumentViewer({ slug }: Readonly<{ slug: string }>) {
|
|||
</span>
|
||||
</p>
|
||||
<div className='flex flex-wrap gap-2'>
|
||||
<ItemBadge itemName={type as DocumentType} />
|
||||
<ItemBadge itemName={type} />
|
||||
<Status statusName={status} />
|
||||
<span className='border-gray-200 border-2 rounded px-2 py-1.5 mr-2 shadow-sm shadow-slate-300'>
|
||||
Revision {latest}
|
||||
|
@ -76,27 +98,21 @@ export default function DocumentViewer({ slug }: Readonly<{ slug: string }>) {
|
|||
</p>
|
||||
<p className='my-2'>
|
||||
<span className='font-bold'>Cite as: </span>
|
||||
{citation ? <>{citation}</> : <>eeXiv:{slug}</>}
|
||||
{citation ? <>{citation}</> : <>eeXiv:{hash}</>}
|
||||
</p>
|
||||
<Link
|
||||
href={`/download/${slug}/file${latest}${file === 'other' ? '' : `.${file}`}`}
|
||||
download={`${slug}-rev-${latest}${file === 'other' ? '' : `.${file}`}`}
|
||||
target='_blank'
|
||||
<Suspense
|
||||
fallback={
|
||||
<div className='max-w-sm animate-pulse flex flex-wrap gap-2'>
|
||||
<div className='rounded-sm h-10 bg-gray-300 w-3 flex-grow basis-1 mt-2 mb-2'></div>
|
||||
<div className='rounded-sm h-10 bg-gray-300 w-3 flex-grow basis-1.5 mt-2 mb-2'></div>
|
||||
<div className='rounded-sm h-10 bg-gray-300 w-1 flex-grow basis-1 mt-2 mb-2'></div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<button className='button-default'>
|
||||
Download{' '}
|
||||
{(() => {
|
||||
switch (file) {
|
||||
case 'other':
|
||||
return <></>
|
||||
case 'tar.gz':
|
||||
return 'Tarball'
|
||||
default:
|
||||
return file.toUpperCase()
|
||||
}
|
||||
})()}
|
||||
</button>
|
||||
</Link>
|
||||
<VersionChooser doc={doc} slug={slug} />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DocumentViewer
|
||||
|
|
89
src/app/document/view/[slug]/VersionChooser.tsx
Normal file
89
src/app/document/view/[slug]/VersionChooser.tsx
Normal file
|
@ -0,0 +1,89 @@
|
|||
'use client'
|
||||
import { useState } from 'react'
|
||||
import { Document } from '@/app/db/data'
|
||||
import Link from 'next/link'
|
||||
import { loadAuthors } from '@/app/db/loaders'
|
||||
import { useSuspenseQuery } from '@tanstack/react-query'
|
||||
import { epoch2date } from '@/app/utils/epoch2datestring'
|
||||
|
||||
const VersionChooser = ({
|
||||
doc,
|
||||
slug,
|
||||
}: Readonly<{ doc: Document; slug: string }>) => {
|
||||
const { data, error } = useSuspenseQuery({
|
||||
queryKey: ['authors_all'],
|
||||
queryFn: () => {
|
||||
const data = loadAuthors(doc.manifest.authors)
|
||||
return data
|
||||
},
|
||||
})
|
||||
if (error) throw error
|
||||
|
||||
const { file } = doc
|
||||
const { authors, latest } = doc.manifest
|
||||
const date = epoch2date(doc.manifest.dates[doc.manifest.dates.length - 1])
|
||||
|
||||
const fileEnding = file === 'other' ? '' : `.${file}`
|
||||
const [selectedRevision, setSelectedRevision] = useState<number>(latest) // Initialize the selected revision with the latest revision
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Link
|
||||
href={`/download/${slug}/file${selectedRevision}${fileEnding}`}
|
||||
target='_blank'
|
||||
>
|
||||
<button className='button-default'>
|
||||
Download{' '}
|
||||
{(() => {
|
||||
switch (file) {
|
||||
case 'other':
|
||||
return <></>
|
||||
case 'tar.gz':
|
||||
return 'Tarball'
|
||||
}
|
||||
})()}
|
||||
</button>
|
||||
</Link>
|
||||
<button
|
||||
className='ml-2 h-10 px-2.5 bg-slate-300 rounded-md'
|
||||
onClick={() => {
|
||||
const bibtex = `@article{
|
||||
author={${authors
|
||||
.map((a: string, i) => {
|
||||
const author = data[a].name.first + ' ' + data[a].name.last
|
||||
if (i === 0) return author
|
||||
else if (i === authors.length - 1) return ` and ${author}`
|
||||
else return `, ${author}`
|
||||
})
|
||||
.join('')}},
|
||||
title={${doc.manifest.title}},
|
||||
journal={eeXiv journal},
|
||||
year={${date.getFullYear()}},
|
||||
month={${date.toLocaleString('default', { month: 'short' })}},
|
||||
url={${window.location.href}}
|
||||
}`
|
||||
navigator.clipboard.writeText(bibtex)
|
||||
}}
|
||||
>
|
||||
Export BibTeX
|
||||
</button>
|
||||
<select
|
||||
className='ml-2 h-10 px-2.5 bg-slate-300 rounded-md'
|
||||
value={`v${selectedRevision}`}
|
||||
onChange={(e) => {
|
||||
setSelectedRevision(parseInt(e.target.value.replace(/\D/g, ''), 10))
|
||||
}}
|
||||
>
|
||||
{Array.from({ length: latest }, (_, index) => index + 1).map(
|
||||
(version) => (
|
||||
<option key={version} value={`v${version}`} className='p-2.5'>
|
||||
{version == latest ? 'Latest' : `v${version}`}
|
||||
</option>
|
||||
)
|
||||
)}
|
||||
</select>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default VersionChooser
|
31
src/app/document/view/[slug]/loading.module.css
Normal file
31
src/app/document/view/[slug]/loading.module.css
Normal file
|
@ -0,0 +1,31 @@
|
|||
.skeleton-box {
|
||||
display: inline-block;
|
||||
height: 1em;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background-color: #dddbdd;
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
transform: translateX(-100%);
|
||||
background-image: linear-gradient(
|
||||
90deg,
|
||||
rgba(#fff, 0) 0,
|
||||
rgba(#fff, 0.2) 20%,
|
||||
rgba(#fff, 0.5) 60%,
|
||||
rgba(#fff, 0)
|
||||
);
|
||||
animation: shimmer 5s infinite;
|
||||
content: '';
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
100% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +1,18 @@
|
|||
'use client'
|
||||
import DocumentViewer from './DocumentViewer'
|
||||
import { documents } from '@/app/db/data'
|
||||
import ErrorBoundary from '@/app/utils/ErrorBoundary'
|
||||
|
||||
export function generateStaticParams() {
|
||||
const docsList = Object.keys(documents)
|
||||
return docsList.map((doc) => ({ doc }))
|
||||
}
|
||||
// export function generateStaticParams() {
|
||||
// const docsList = Object.keys(documents)
|
||||
// return docsList.map((doc) => ({ doc }))
|
||||
// }
|
||||
|
||||
export default function Page({
|
||||
params,
|
||||
}: Readonly<{ params: { slug: string } }>) {
|
||||
return <DocumentViewer slug={params.slug} />
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<DocumentViewer slug={params.slug} />
|
||||
</ErrorBoundary>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -20,6 +20,11 @@ a:hover {
|
|||
text-decoration: underline;
|
||||
}
|
||||
|
||||
option {
|
||||
@apply font-sans;
|
||||
@apply p-4;
|
||||
}
|
||||
|
||||
.button-default {
|
||||
@apply bg-blue-600 text-slate-100 hover:bg-blue-400 font-semibold rounded py-2 px-4 my-2 shadow-sm shadow-slate-400;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
export default function Page() {
|
||||
return (
|
||||
<div className='content text-slate-800'>
|
||||
<h1 className='text-3xl text-slate-800 mt-4 mb-2 font-serif'>Accessibility</h1>
|
||||
<h1 className='text-3xl text-slate-800 mt-4 mb-2 font-serif'>
|
||||
Accessibility
|
||||
</h1>
|
||||
<p>
|
||||
If you encounter any accessibility-related issues related to your use of
|
||||
our site, it is likely because of our jank code architecture.
|
||||
|
|
|
@ -2,7 +2,9 @@ import Link from 'next/link'
|
|||
export default function Page() {
|
||||
return (
|
||||
<div className='content text-slate-800'>
|
||||
<h1 className='text-3xl text-slate-800 mt-4 mb-2 font-serif'>Copyright</h1>
|
||||
<h1 className='text-3xl text-slate-800 mt-4 mb-2 font-serif'>
|
||||
Copyright
|
||||
</h1>
|
||||
<p>
|
||||
All content on this site is licensed under the{' '}
|
||||
<a href='https://creativecommons.org/licenses/by-sa/4.0/'>
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
export default function Page() {
|
||||
return (
|
||||
<div className='content text-slate-800'>
|
||||
<h1 className='text-3xl text-slate-800 mt-4 mb-2 font-serif'>Privacy Policy</h1>
|
||||
<h1 className='text-3xl text-slate-800 mt-4 mb-2 font-serif'>
|
||||
Privacy Policy
|
||||
</h1>
|
||||
<p>
|
||||
{`User privacy is important to us. Just kidding. We don't collect any
|
||||
personal information. We only use it to help us improve eeXiv. Your
|
||||
|
|
|
@ -3,12 +3,15 @@ import Link from 'next/link'
|
|||
export default function Page() {
|
||||
return (
|
||||
<div className='content text-slate-800'>
|
||||
<h1 className='text-3xl text-slate-800 mt-4 mb-2 font-serif'>Get status notifications</h1>
|
||||
<h1 className='text-3xl text-slate-800 mt-4 mb-2 font-serif'>
|
||||
Get status notifications
|
||||
</h1>
|
||||
<p>
|
||||
We currently do not have the technical support to implement mailing
|
||||
lists in eeXiv. Check <Link href='/status'>status</Link> frequently for updates.
|
||||
The best way to stay connected with the status of the eeXiv project is
|
||||
to watch it on <a href='https://github.com/Team-1280/eeXiv'>GitHub</a>.
|
||||
lists in eeXiv. Check <Link href='/status'>status</Link> frequently for
|
||||
updates. The best way to stay connected with the status of the eeXiv
|
||||
project is to watch it on{' '}
|
||||
<a href='https://github.com/Team-1280/eeXiv'>GitHub</a>.
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -6,7 +6,9 @@ export default function Page() {
|
|||
return (
|
||||
<div className='content text-slate-800'>
|
||||
<h1 className='text-3xl text-slate-800 mt-4 mb-4 font-serif'>Status</h1>
|
||||
<p className={`${zillaSlab.className} p-6 rounded-lg bg-green-600 text-xl text-white text-center`}>
|
||||
<p
|
||||
className={`${zillaSlab.className} p-6 rounded-lg bg-green-600 text-xl text-white text-center`}
|
||||
>
|
||||
eeXiv is <strong>online</strong>. All systems{' '}
|
||||
<strong>operational</strong>.
|
||||
</p>
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
export default function Page() {
|
||||
return (
|
||||
<div className='content text-slate-800'>
|
||||
<h1 className='text-3xl text-slate-800 mt-4 mb-2 font-serif'>Subscribe</h1>
|
||||
<h1 className='text-3xl text-slate-800 mt-4 mb-2 font-serif'>
|
||||
Subscribe
|
||||
</h1>
|
||||
<p>
|
||||
We currently do not have the technical support to implement mailing
|
||||
lists in eeXiv. Check back later for updates. The best way to stay
|
||||
|
|
|
@ -11,3 +11,7 @@ export function epoch2datestring(epoch: number): string {
|
|||
|
||||
return formattedDate
|
||||
}
|
||||
|
||||
export function epoch2date(epoch: number): Date {
|
||||
return new Date(epoch * 1000)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue