feat: start FAQ page
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
Kieran 2024-01-20 22:24:57 +00:00
parent a3ab088a82
commit 96467bd979
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
7 changed files with 141 additions and 100 deletions

View File

@ -1,35 +1,16 @@
/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-unused-vars */
/// <reference types="@webbtc/webln-types" /> /// <reference types="@webbtc/webln-types" />
/// <reference types="vite/client" />
declare const __XXX: boolean; declare const __XXX: boolean;
declare const __XXX_HOST: string; declare const __XXX_HOST: string;
declare const __ZAP_STREAM_VERSION__: string; declare const __ZAP_STREAM_VERSION__: string;
declare module "*.jpg" { declare module "*.md" {
const value: unknown;
export default value;
}
declare module "*.svg" {
const value: unknown;
export default value;
}
declare module "*.webp" {
const value: string; const value: string;
export default value; export default value;
} }
declare module "*.png" {
const value: string;
export default value;
}
declare module "*.css" {
const stylesheet: CSSStyleSheet;
export default stylesheet;
}
declare module "translations/*.json" { declare module "translations/*.json" {
const value: Record<string, string>; const value: Record<string, string>;
export default value; export default value;

View File

@ -0,0 +1,10 @@
import { ReactNode, useEffect, useState } from "react";
export function Async<T>({ loader, then }: { loader: () => Promise<T>, then: (v: T) => ReactNode }) {
const [res, setResult] = useState<T>();
useEffect(() => {
loader().then(setResult);
}, []);
if (!res) return;
return then(res);
}

View File

@ -1,9 +1,3 @@
.markdown {
font-size: 18px;
font-weight: 400;
line-height: 29px;
}
.markdown a { .markdown a {
color: var(--primary); color: var(--primary);
} }
@ -26,7 +20,7 @@
.markdown video, .markdown video,
.markdown iframe, .markdown iframe,
.markdown audio { .markdown audio {
width: 100%; max-width: 100%;
display: block; display: block;
} }
@ -35,3 +29,12 @@
width: -webkit-fill-available; width: -webkit-fill-available;
aspect-ratio: 16 / 9; aspect-ratio: 16 / 9;
} }
.markdown h1,
.markdown h2,
.markdown h3,
.markdown h4,
.markdown h5,
.markdown h6 {
margin: 0.5em 0;
}

View File

@ -1,16 +1,21 @@
import "./markdown.css"; import "./markdown.css";
import { ReactNode, forwardRef, useMemo } from "react"; import { ReactNode, forwardRef, useMemo } from "react";
import { Token, marked } from "marked"; import { Token, Tokens, marked } from "marked";
import { HyperText } from "./hypertext"; import { HyperText } from "./hypertext";
import { Text } from "./text"; import { Text } from "./text";
interface MarkdownProps { interface MarkdownProps {
content: string; content: string;
tags?: Array<Array<string>>; tags?: Array<Array<string>>;
// Render plain text directly without parsing nostr/http links
plainText?: boolean;
} }
function renderToken(t: Token): ReactNode { const Markdown = forwardRef<HTMLDivElement, MarkdownProps>((props: MarkdownProps, ref) => {
function renderToken(t: Token): ReactNode {
try { try {
switch (t.type) { switch (t.type) {
case "paragraph": { case "paragraph": {
@ -70,19 +75,36 @@ function renderToken(t: Token): ReactNode {
case "del": { case "del": {
return <s>{t.tokens ? t.tokens.map(renderToken) : t.raw}</s>; return <s>{t.tokens ? t.tokens.map(renderToken) : t.raw}</s>;
} }
case "table": {
return <table className="table-auto border-collapse">
<thead>
<tr>
{(t.header as Tokens.TableCell[]).map(v => <th className="border">{v.tokens ? v.tokens.map(renderToken) : v.text}</th>)}
</tr>
</thead>
<tbody>
{(t.rows as Tokens.TableCell[][]).map(v => <tr>
{v.map(d => <td className="border px-2 py-1">{d.tokens ? d.tokens.map(renderToken) : d.text}</td>)}
</tr>)}
</tbody>
</table>;
}
default: { default: {
if ("tokens" in t) { if ("tokens" in t) {
return (t.tokens as Array<Token>).map(renderToken); return (t.tokens as Array<Token>).map(renderToken);
} }
if (props.plainText ?? false) {
return t.raw;
}
return <Text content={t.raw} tags={[]} />; return <Text content={t.raw} tags={[]} />;
} }
} }
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }
} }
const Markdown = forwardRef<HTMLDivElement, MarkdownProps>((props: MarkdownProps, ref) => {
const parsed = useMemo(() => { const parsed = useMemo(() => {
return marked.lexer(props.content); return marked.lexer(props.content);
}, [props.content, props.tags]); }, [props.content, props.tags]);

13
src/faq.md Normal file
View File

@ -0,0 +1,13 @@
# FAQ
# Reccomended Stream Settings
| Name | Value |
| - | - |
| Video Codec | h264 |
| Audio Codec | AAC |
| Max Video Bitrate | 7000k |
| Max Audio Bitrate | 320k |
| Keyframe Interval | 2s |
### Example settings in OBS (Apple M1 Mac):
![OBS Apple](https://void.cat/d/VQQ75R6tmbVQJ9eqiwJhoj.webp)

View File

@ -27,8 +27,12 @@ import { WidgetsPage } from "@/pages/widgets";
import { AlertsPage } from "@/pages/alerts"; import { AlertsPage } from "@/pages/alerts";
import { StreamSummaryPage } from "@/pages/summary"; import { StreamSummaryPage } from "@/pages/summary";
import { EmbededPage } from "./pages/embed"; import { EmbededPage } from "./pages/embed";
import Markdown from "./element/markdown";
const DashboardPage = lazy(() => import("./pages/dashboard")); const DashboardPage = lazy(() => import("./pages/dashboard"));
import Faq from "@/faq.md";
import { Async } from "./element/async-loader";
const db = new SnortSystemDb(); const db = new SnortSystemDb();
const System = new NostrSystem({ const System = new NostrSystem({
db, db,
@ -103,6 +107,13 @@ const router = createBrowserRouter([
</Suspense> </Suspense>
), ),
}, },
{
path: "/faq",
element: <Async loader={async () => {
const req = await fetch(Faq);
return await req.text();
}} then={(v) => <Markdown content={v} tags={[]} plainText={true} />} />
},
{ {
path: "*", path: "*",
element: <CatchAllRoutePage />, element: <CatchAllRoutePage />,

View File

@ -28,6 +28,7 @@ export default defineConfig({
filename: "build/stats.html", filename: "build/stats.html",
}), }),
], ],
assetsInclude: ["**/*.md"],
build: { build: {
outDir: "build", outDir: "build",
sourcemap: true, sourcemap: true,