121 lines
3 KiB
TypeScript
121 lines
3 KiB
TypeScript
'use client'
|
|
|
|
import { useState, useEffect } from 'react'
|
|
|
|
import NetworkError from './types/error/network';
|
|
import ContentTypeError from './types/error/content_type';
|
|
import ImageContent from './types/image';
|
|
import Terminal from './types/terminal';
|
|
import Text from './types/text';
|
|
|
|
type ContentType<T> = {
|
|
content_type: RegExp,
|
|
path?: RegExp,
|
|
emit: () => void | Processor<T>,
|
|
};
|
|
type Processor<T> = {
|
|
process: (response: Response) => Promise<T>,
|
|
postprocess: (data: T) => undefined,
|
|
};
|
|
|
|
function not_match(regex: RegExp | undefined, str: string) {
|
|
return !(regex ?? /(?:)/).test(str);
|
|
}
|
|
|
|
function is_type(response: Response, type: ContentType<any>) {
|
|
if (not_match(type.content_type, response.headers.get('Content-Type')!.split(';')[0])) {
|
|
return false;
|
|
} else if (not_match(type.path, window.location.pathname)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
export default function Content({ src }: { src: string}) {
|
|
const [content, set_content] = useState<JSX.Element>();
|
|
|
|
const recognized_types: ContentType<any>[] = [{
|
|
content_type: /image\/\w+/,
|
|
emit: () => {
|
|
set_content(<ImageContent src={src} />);
|
|
},
|
|
}, {
|
|
content_type: /application\/octet-stream/,
|
|
path: /.*\.term/,
|
|
emit: () => {
|
|
return {
|
|
process: (response: Response) => {
|
|
return response.text();
|
|
},
|
|
postprocess: (data: string) => {
|
|
set_content(<Terminal text={data} />);
|
|
}
|
|
};
|
|
}
|
|
}, {
|
|
content_type: /(text\/\w+)|(application\/octet-stream)/,
|
|
emit: () => {
|
|
return {
|
|
process: (response: Response) => {
|
|
return response.text();
|
|
},
|
|
postprocess: (data: string) => {
|
|
const languages = {
|
|
// "markup",
|
|
js: "jsx",
|
|
mjs: "jsx",
|
|
jsx: "jsx",
|
|
// "js-extras",
|
|
ts: "tsx",
|
|
tsx: "tsx",
|
|
swift: "swift",
|
|
kt: "kotlin",
|
|
kts: "kotlin",
|
|
ktm: "kotlin",
|
|
// "objectivec",
|
|
// "reason",
|
|
rs: "rust",
|
|
// "graphql",
|
|
yml: "yaml",
|
|
yaml: "yaml",
|
|
go: "go",
|
|
c: "cpp",
|
|
cpp: "cpp",
|
|
cxx: "cpp",
|
|
h: "cpp",
|
|
hpp: "cpp",
|
|
hxx: "cpp",
|
|
py: "python",
|
|
json: "json",
|
|
}
|
|
const split = window.location.pathname.split('.');
|
|
set_content(<Text language={split.length == 0 ? 'none' : (languages[split[split.length - 1] as keyof typeof languages] ?? 'none')} text={data} />);
|
|
}
|
|
};
|
|
}
|
|
}];
|
|
|
|
useEffect(() => {
|
|
if (content == undefined) {
|
|
const result = fetch(src)
|
|
.then(response => {
|
|
for (const type of recognized_types) {
|
|
if (is_type(response, type)) {
|
|
const emitted = type.emit();
|
|
if (emitted != undefined) {
|
|
result.then(emitted.postprocess);
|
|
return emitted.process(response);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
set_content(<ContentTypeError content_type={response.headers.get('Content-Type')!.split(';')[0]} />);
|
|
})
|
|
.catch(err => {
|
|
set_content(<NetworkError err={err} />);
|
|
});
|
|
}
|
|
});
|
|
|
|
return content ?? <p>Loading...</p>;
|
|
}
|