IB nav + fixes
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { TemplateCtx } from "../../ctx";
|
||||
import { Board } from "../../db/schema";
|
||||
import { TemplateCtx } from "../ctx";
|
||||
import { Board } from "../db/schema";
|
||||
import { Form, FormField } from "./form";
|
||||
import { PropsWithChildren } from "@kitajs/html";
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
import { TemplateCtx } from "../ctx";
|
||||
import { PropsWithChildren } from "@kitajs/html";
|
||||
|
||||
const BoardLinks = ({ ctx }: PropsWithChildren<{ ctx: TemplateCtx }>) => {
|
||||
return (
|
||||
<>
|
||||
[
|
||||
{ctx.listedBoards.map((board, index) => (
|
||||
<>
|
||||
<a href={`/ib/${board}`} safe>
|
||||
{board}
|
||||
</a>
|
||||
{index === ctx.listedBoards.length - 1 ? "" : " / "}
|
||||
</>
|
||||
))}
|
||||
]
|
||||
</>
|
||||
);
|
||||
};
|
||||
const BoardLinks = ({ ctx }: PropsWithChildren<{ ctx: TemplateCtx }>) => (
|
||||
<>
|
||||
[
|
||||
{ctx.listedBoards.map((board, index) => (
|
||||
<>
|
||||
<a href={`/ib/${board}`} safe>
|
||||
{board}
|
||||
</a>
|
||||
{index === ctx.listedBoards.length - 1 ? "" : " / "}
|
||||
</>
|
||||
))}
|
||||
]
|
||||
</>
|
||||
);
|
||||
|
||||
export default BoardLinks;
|
||||
|
||||
72
src/components/form.tsx
Normal file
72
src/components/form.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import { PropsWithChildren } from "@kitajs/html";
|
||||
|
||||
const Form = ({
|
||||
submit,
|
||||
action,
|
||||
id,
|
||||
center = false,
|
||||
fullWidth = false,
|
||||
disabled = false,
|
||||
children,
|
||||
}: PropsWithChildren<{
|
||||
submit: string;
|
||||
action: string;
|
||||
id?: string;
|
||||
center?: boolean;
|
||||
fullWidth?: boolean;
|
||||
disabled?: boolean;
|
||||
}>) => (
|
||||
<table
|
||||
id={id}
|
||||
class={`form-table${center ? " auto-center" : ""}${fullWidth ? " full-width" : ""}`}
|
||||
>
|
||||
<tbody>
|
||||
{children}
|
||||
{disabled ? (
|
||||
""
|
||||
) : (
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="input-cell">
|
||||
<button name="action" type="submit" value={action} safe>
|
||||
{submit}
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
|
||||
const FormField = ({
|
||||
label,
|
||||
fullWidth = false,
|
||||
wrapper = false,
|
||||
children,
|
||||
}: PropsWithChildren<{
|
||||
label: string | JSX.Element;
|
||||
fullWidth?: boolean;
|
||||
wrapper?: boolean;
|
||||
}>) => (
|
||||
<tr>
|
||||
<th>{label}</th>
|
||||
<td>
|
||||
<div
|
||||
class={`input-cell${fullWidth ? " full-width" : ""}${wrapper ? " input-wrapper" : ""}`}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
|
||||
const FormHeader = ({ children }: PropsWithChildren) => (
|
||||
<tr>
|
||||
<th class="center" colspan="2">
|
||||
{children}
|
||||
</th>
|
||||
</tr>
|
||||
);
|
||||
|
||||
export { Form, FormField, FormHeader };
|
||||
@@ -1,76 +0,0 @@
|
||||
import { PropsWithChildren } from "@kitajs/html";
|
||||
|
||||
const Form = ({
|
||||
submit,
|
||||
action,
|
||||
id,
|
||||
center = false,
|
||||
fullWidth = false,
|
||||
disabled = false,
|
||||
children,
|
||||
}: PropsWithChildren<{
|
||||
submit: string;
|
||||
action: string;
|
||||
id?: string;
|
||||
center?: boolean;
|
||||
fullWidth?: boolean;
|
||||
disabled?: boolean;
|
||||
}>) => {
|
||||
return (
|
||||
<table
|
||||
id={id}
|
||||
class={`form-table${center ? " auto-center" : ""}${fullWidth ? " full-width" : ""}`}
|
||||
>
|
||||
<tbody>
|
||||
{children}
|
||||
{disabled ? (
|
||||
""
|
||||
) : (
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="input-cell">
|
||||
<button name="action" type="submit" value={action} safe>
|
||||
{submit}
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
};
|
||||
|
||||
const FormField = ({
|
||||
label,
|
||||
fullWidth = false,
|
||||
wrapper = false,
|
||||
children,
|
||||
}: PropsWithChildren<{
|
||||
label: string | JSX.Element;
|
||||
fullWidth?: boolean;
|
||||
wrapper?: boolean;
|
||||
}>) => {
|
||||
return (
|
||||
<tr>
|
||||
<th>{label}</th>
|
||||
<td>
|
||||
<div
|
||||
class={`input-cell${fullWidth ? " full-width" : ""}${wrapper ? " input-wrapper" : ""}`}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
|
||||
const FormHeader = ({ children }: PropsWithChildren) => (
|
||||
<tr>
|
||||
<th class="center" colspan="2">
|
||||
{children}
|
||||
</th>
|
||||
</tr>
|
||||
);
|
||||
|
||||
export { Form, FormField, FormHeader };
|
||||
24
src/components/ib_links.tsx
Normal file
24
src/components/ib_links.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Board } from "../db/schema";
|
||||
import { PropsWithChildren } from "@kitajs/html";
|
||||
|
||||
const IBLinks = ({ board }: PropsWithChildren<{ board?: Board }>) => (
|
||||
<>
|
||||
[<a href="#top">Nahoru</a>] [<a href="#bottom">Dolů</a>] [
|
||||
<a href={board !== undefined ? `/ib/${board.id}` : `/ib/overboard`}>
|
||||
Index
|
||||
</a>
|
||||
] [
|
||||
<a
|
||||
href={
|
||||
board !== undefined
|
||||
? `/ib/${board.id}/catalog`
|
||||
: `/ib/overboard/catalog`
|
||||
}
|
||||
>
|
||||
Katalog
|
||||
</a>
|
||||
]
|
||||
</>
|
||||
);
|
||||
|
||||
export default IBLinks;
|
||||
@@ -15,11 +15,10 @@ const IPAddress = ({
|
||||
</>
|
||||
);
|
||||
|
||||
const cloakIP = (ctx: TemplateCtx, ip: string) => {
|
||||
return createHash("sha256")
|
||||
const cloakIP = (ctx: TemplateCtx, ip: string) =>
|
||||
createHash("sha256")
|
||||
.update(`${ip}$${ctx.config.site.cloak_secret}`)
|
||||
.digest("hex")
|
||||
.slice(0, 8);
|
||||
};
|
||||
|
||||
export default IPAddress;
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import { PropsWithChildren } from "@kitajs/html";
|
||||
|
||||
const JsxComment = ({ children }: PropsWithChildren) => {
|
||||
return (
|
||||
<>
|
||||
{"<!-- "}
|
||||
{children}
|
||||
{" --!>"}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const JsxComment = ({ children }: PropsWithChildren) => (
|
||||
<>
|
||||
{"<!-- "}
|
||||
{children}
|
||||
{" --!>"}
|
||||
</>
|
||||
);
|
||||
export default JsxComment;
|
||||
|
||||
@@ -2,42 +2,40 @@ import { TemplateCtx } from "../ctx";
|
||||
import { canUser, CzchanPerm } from "../permissions";
|
||||
import { PropsWithChildren } from "@kitajs/html";
|
||||
|
||||
const ModNav = ({ ctx }: PropsWithChildren<{ ctx: TemplateCtx }>) => {
|
||||
return (
|
||||
<nav class="pagination">
|
||||
[<a href="/mod/">Domov</a>] [<a href="/mod/users">Uživatelé</a>]{" "}
|
||||
{canUser(ctx.user, CzchanPerm.MANAGE_BOARDS) && (
|
||||
<>
|
||||
[<a href="/mod/boards">Nástěnky</a>]{" "}
|
||||
</>
|
||||
)}
|
||||
{canUser(ctx.user, CzchanPerm.MANAGE_BANS) && (
|
||||
<>
|
||||
[<a href="/mod/bans">Bany</a>]{" "}
|
||||
</>
|
||||
)}
|
||||
{canUser(ctx.user, CzchanPerm.MANAGE_BANNERS) && (
|
||||
<>
|
||||
[<a href="/mod/banners">Bannery</a>]{" "}
|
||||
</>
|
||||
)}
|
||||
{canUser(ctx.user, CzchanPerm.MANAGE_REPORTS) && (
|
||||
<>
|
||||
[<a href="/mod/reports">Hlášení</a>]{" "}
|
||||
</>
|
||||
)}
|
||||
{canUser(ctx.user, CzchanPerm.MANAGE_NEWS) && (
|
||||
<>
|
||||
[<a href="/mod/news">Novinky</a>]{" "}
|
||||
</>
|
||||
)}
|
||||
{canUser(ctx.user, CzchanPerm.VIEW_LOGS) && (
|
||||
<>
|
||||
[<a href="/mod/logs">Záznamy</a>]
|
||||
</>
|
||||
)}
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
const ModNav = ({ ctx }: PropsWithChildren<{ ctx: TemplateCtx }>) => (
|
||||
<nav class="pagination">
|
||||
[<a href="/mod/">Domov</a>] [<a href="/mod/users">Uživatelé</a>]{" "}
|
||||
{canUser(ctx.user, CzchanPerm.MANAGE_BOARDS) && (
|
||||
<>
|
||||
[<a href="/mod/boards">Nástěnky</a>]{" "}
|
||||
</>
|
||||
)}
|
||||
{canUser(ctx.user, CzchanPerm.MANAGE_BANS) && (
|
||||
<>
|
||||
[<a href="/mod/bans">Bany</a>]{" "}
|
||||
</>
|
||||
)}
|
||||
{canUser(ctx.user, CzchanPerm.MANAGE_BANNERS) && (
|
||||
<>
|
||||
[<a href="/mod/banners">Bannery</a>]{" "}
|
||||
</>
|
||||
)}
|
||||
{canUser(ctx.user, CzchanPerm.MANAGE_REPORTS) && (
|
||||
<>
|
||||
[<a href="/mod/reports">Hlášení</a>]{" "}
|
||||
</>
|
||||
)}
|
||||
{canUser(ctx.user, CzchanPerm.MANAGE_NEWS) && (
|
||||
<>
|
||||
[<a href="/mod/news">Novinky</a>]{" "}
|
||||
</>
|
||||
)}
|
||||
{canUser(ctx.user, CzchanPerm.VIEW_LOGS) && (
|
||||
<>
|
||||
[<a href="/mod/logs">Záznamy</a>]
|
||||
</>
|
||||
)}
|
||||
</nav>
|
||||
);
|
||||
|
||||
export default ModNav;
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import { type PropsWithChildren } from "@kitajs/html";
|
||||
|
||||
const Page = ({ children }: PropsWithChildren) => {
|
||||
return (
|
||||
<>
|
||||
{"<!DOCTYPE html>"}
|
||||
<html lang="cs">{children}</html>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Page = ({ children }: PropsWithChildren) => (
|
||||
<>
|
||||
{"<!DOCTYPE html>"}
|
||||
<html lang="cs">{children}</html>
|
||||
</>
|
||||
);
|
||||
export default Page;
|
||||
|
||||
@@ -6,47 +6,43 @@ import { type PropsWithChildren } from "@kitajs/html";
|
||||
const PageBody = ({
|
||||
ctx,
|
||||
children,
|
||||
}: PropsWithChildren<{ ctx: TemplateCtx }>) => {
|
||||
return (
|
||||
<body>
|
||||
<div id="top"></div>
|
||||
<header class="header clearfix">
|
||||
<nav>
|
||||
[<a href="/">domov</a>] <BoardLinks ctx={ctx} /> [
|
||||
<a href="/news">novinky</a>]
|
||||
<span class="float-r">
|
||||
{ctx.user ? (
|
||||
<>
|
||||
{" "}
|
||||
[<a href="/actions/logout">odhlásit</a>] [
|
||||
<a href="/mod/">mod</a>]
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{" "}
|
||||
[<a href="/login">přihlásit</a>]
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</nav>
|
||||
</header>
|
||||
<main>{children}</main>
|
||||
<footer class="footer">
|
||||
<div class="inner-footer">
|
||||
<small>
|
||||
<a href={pjson.repository.url}>czchan</a>{" "}
|
||||
<span safe>{pjson.version}</span> - © 2025{" "}
|
||||
<a href="mailto:dev@czchan.org">sneedmaster</a> - Licencováno{" "}
|
||||
<a href="/LICENSE.txt">AGPL 3.0</a>
|
||||
<br />
|
||||
Všechny příspěvky na této stránce byly vytvořeny náhodnými
|
||||
uživateli.
|
||||
</small>
|
||||
</div>
|
||||
</footer>
|
||||
<div id="bottom"></div>
|
||||
</body>
|
||||
);
|
||||
};
|
||||
}: PropsWithChildren<{ ctx: TemplateCtx }>) => (
|
||||
<body>
|
||||
<div id="top"></div>
|
||||
<header class="header clearfix">
|
||||
<nav>
|
||||
[<a href="/">domov</a>] <BoardLinks ctx={ctx} /> [
|
||||
<a href="/news">novinky</a>]
|
||||
<span class="float-r">
|
||||
{ctx.user ? (
|
||||
<>
|
||||
{" "}
|
||||
[<a href="/actions/logout">odhlásit</a>] [<a href="/mod/">mod</a>]
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{" "}
|
||||
[<a href="/login">přihlásit</a>]
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</nav>
|
||||
</header>
|
||||
<main>{children}</main>
|
||||
<footer class="footer">
|
||||
<div class="inner-footer">
|
||||
<small>
|
||||
<a href={pjson.repository.url}>czchan</a>{" "}
|
||||
<span safe>{pjson.version}</span> - © 2025{" "}
|
||||
<a href="mailto:dev@czchan.org">sneedmaster</a> - Licencováno{" "}
|
||||
<a href="/LICENSE.txt">AGPL 3.0</a>
|
||||
<br />
|
||||
Všechny příspěvky na této stránce byly vytvořeny náhodnými uživateli.
|
||||
</small>
|
||||
</div>
|
||||
</footer>
|
||||
<div id="bottom"></div>
|
||||
</body>
|
||||
);
|
||||
|
||||
export default PageBody;
|
||||
|
||||
@@ -12,23 +12,21 @@ const PageHead = ({
|
||||
title: string;
|
||||
theme?: string;
|
||||
description?: string;
|
||||
}>) => {
|
||||
return (
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title safe>{title}</title>
|
||||
{description !== undefined && (
|
||||
<meta name="description" content={description} />
|
||||
)}
|
||||
<link rel="stylesheet" href="/public/css/base.css" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href={`/public/css/themes/${theme || ctx.config.site.global_theme}.css`}
|
||||
/>
|
||||
{children}
|
||||
</head>
|
||||
);
|
||||
};
|
||||
}>) => (
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title safe>{title}</title>
|
||||
{description !== undefined && (
|
||||
<meta name="description" content={description} />
|
||||
)}
|
||||
<link rel="stylesheet" href="/public/css/base.css" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href={`/public/css/themes/${theme || ctx.config.site.global_theme}.css`}
|
||||
/>
|
||||
{children}
|
||||
</head>
|
||||
);
|
||||
|
||||
export default PageHead;
|
||||
|
||||
@@ -7,12 +7,14 @@ const Pagination = ({
|
||||
entries,
|
||||
current,
|
||||
max,
|
||||
otherLinks,
|
||||
}: PropsWithChildren<{
|
||||
ctx: TemplateCtx;
|
||||
base: string;
|
||||
entries: number;
|
||||
current: number;
|
||||
max?: number;
|
||||
otherLinks?: JSX.Element;
|
||||
}>) => {
|
||||
const impliedPages = Math.ceil(entries / ctx.config.site.page_size);
|
||||
const actualPages = Math.min(impliedPages, max || impliedPages);
|
||||
@@ -52,6 +54,7 @@ const Pagination = ({
|
||||
[<a href={`${base}?page=${current + 1}`}>Další</a>]
|
||||
</>
|
||||
)}
|
||||
{otherLinks !== undefined && <> {otherLinks}</>}
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -18,173 +18,171 @@ const PostComponent = ({
|
||||
board: Board;
|
||||
post: Post;
|
||||
reply?: boolean;
|
||||
}>) => {
|
||||
return (
|
||||
<div
|
||||
id={`${board.id}-${post.id}`}
|
||||
class={`post${reply ? " reply" : ""}`}
|
||||
data-board={board.id}
|
||||
>
|
||||
<div class="post-header">
|
||||
<input name="posts" type="checkbox" value={`${board.id}$${post.id}`} />{" "}
|
||||
{post.subject !== null && (
|
||||
<>
|
||||
<span class="subject" safe>
|
||||
{post.subject}
|
||||
</span>{" "}
|
||||
</>
|
||||
)}
|
||||
{post.email !== null ? (
|
||||
<a class="name" rel="nofollow" href={`mailto:${post.email}`} safe>
|
||||
{post.name}
|
||||
</a>
|
||||
) : (
|
||||
<span class="name" safe>
|
||||
{post.name}
|
||||
</span>
|
||||
)}{" "}
|
||||
{post.tripcode !== null && (
|
||||
<>
|
||||
<span class="tripcode" safe>
|
||||
{post.tripcode}
|
||||
</span>{" "}
|
||||
</>
|
||||
)}
|
||||
{post.capcode !== null && (
|
||||
<>
|
||||
<Capcode capcode={post.capcode} />{" "}
|
||||
</>
|
||||
)}
|
||||
{board.config.geo_flags && (
|
||||
<>
|
||||
<img
|
||||
class="icon"
|
||||
src={`/public/img/country_flags/${post.metadata.country || "xx"}.png`}
|
||||
/>{" "}
|
||||
</>
|
||||
)}
|
||||
<Datetime date={post.created} />{" "}
|
||||
<span class="post-number">
|
||||
<a href={canonicalPostURL(post)}>Č.</a>
|
||||
<a
|
||||
class="quote-link"
|
||||
href={`${threadURL(post)}#post-form`}
|
||||
data-thread-url={threadURL(post)}
|
||||
>
|
||||
{post.id}
|
||||
</a>
|
||||
</span>{" "}
|
||||
{board.config.user_ids && (
|
||||
<span
|
||||
class="user-id"
|
||||
style={{ backgroundColor: `#${post.user_id}` }}
|
||||
safe
|
||||
>
|
||||
{post.user_id}
|
||||
</span>
|
||||
)}
|
||||
{post.thread === null && (
|
||||
<>
|
||||
<a href={threadURL(post)}>[Otevřít]</a>{" "}
|
||||
</>
|
||||
)}
|
||||
{post.f_sticky !== 0 && (
|
||||
<>
|
||||
<img src="/public/img/icons/sticky.png" />{" "}
|
||||
</>
|
||||
)}
|
||||
{post.f_locked && (
|
||||
<>
|
||||
<img src="/public/img/icons/locked.png" />{" "}
|
||||
</>
|
||||
)}
|
||||
{post.f_bumplocked && (
|
||||
<>
|
||||
<img src="/public/img/icons/bumplock.png" />{" "}
|
||||
</>
|
||||
)}
|
||||
{post.f_looped && (
|
||||
<>
|
||||
<img src="/public/img/icons/loop.png" />{" "}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div class="clearfix">
|
||||
{
|
||||
<div
|
||||
class={`post-files${post.files.length > 1 ? " multiple-files" : ""}`}
|
||||
>
|
||||
{post.files.map((file) => (
|
||||
<div class="post-file">
|
||||
<div class="post-file-info">
|
||||
<a
|
||||
title={`Stáhnout ${file.original_filename}`}
|
||||
download={file.original_filename}
|
||||
href={file.url}
|
||||
safe
|
||||
>
|
||||
{truncateFilename(file.original_filename, 16)}
|
||||
</a>
|
||||
<br />
|
||||
<small>
|
||||
(<span safe>{bytesToSize(file.size)}</span>
|
||||
{file.dimensions !== null && (
|
||||
<span>
|
||||
, {file.dimensions[0]}x{file.dimensions[1]}
|
||||
</span>
|
||||
)}
|
||||
{file.duration !== null && (
|
||||
<span safe>, {secsToDuration(file.duration)}</span>
|
||||
)}
|
||||
)
|
||||
</small>
|
||||
</div>
|
||||
<a class="thumb-link" target="_blank" href={file.url}>
|
||||
<img
|
||||
class={"thumb thumb-" + file.type}
|
||||
src={(() => {
|
||||
if (file.thumb_url) {
|
||||
return file.thumb_url;
|
||||
}
|
||||
|
||||
switch (file.type) {
|
||||
case "image":
|
||||
return file.url;
|
||||
case "audio":
|
||||
return "/public/img/thumb/audio.png";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
})()}
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
}
|
||||
<div class="post-body post-content">{post.content as "safe"}</div>
|
||||
</div>
|
||||
{post.replies.length > 0 && (
|
||||
<small>
|
||||
Odpovědi:{" "}
|
||||
{post.replies.map((reply) => (
|
||||
<>
|
||||
<a
|
||||
class="quote"
|
||||
href={canonicalPostURL({
|
||||
board: board.id,
|
||||
id: reply,
|
||||
thread: post.id,
|
||||
} as Post)}
|
||||
>
|
||||
>>{reply}
|
||||
</a>{" "}
|
||||
</>
|
||||
))}
|
||||
</small>
|
||||
}>) => (
|
||||
<div
|
||||
id={`${board.id}-${post.id}`}
|
||||
class={`post${reply ? " reply" : ""}`}
|
||||
data-board={board.id}
|
||||
>
|
||||
<div class="post-header">
|
||||
<input name="posts" type="checkbox" value={`${board.id}$${post.id}`} />{" "}
|
||||
{post.subject !== null && (
|
||||
<>
|
||||
<span class="subject" safe>
|
||||
{post.subject}
|
||||
</span>{" "}
|
||||
</>
|
||||
)}
|
||||
{post.email !== null ? (
|
||||
<a class="name" rel="nofollow" href={`mailto:${post.email}`} safe>
|
||||
{post.name}
|
||||
</a>
|
||||
) : (
|
||||
<span class="name" safe>
|
||||
{post.name}
|
||||
</span>
|
||||
)}{" "}
|
||||
{post.tripcode !== null && (
|
||||
<>
|
||||
<span class="tripcode" safe>
|
||||
{post.tripcode}
|
||||
</span>{" "}
|
||||
</>
|
||||
)}
|
||||
{post.capcode !== null && (
|
||||
<>
|
||||
<Capcode capcode={post.capcode} />{" "}
|
||||
</>
|
||||
)}
|
||||
{board.config.geo_flags && (
|
||||
<>
|
||||
<img
|
||||
class="icon"
|
||||
src={`/public/img/country_flags/${post.metadata.country || "xx"}.png`}
|
||||
/>{" "}
|
||||
</>
|
||||
)}
|
||||
<Datetime date={post.created} />{" "}
|
||||
<span class="post-number">
|
||||
<a href={canonicalPostURL(post)}>Č.</a>
|
||||
<a
|
||||
class="quote-link"
|
||||
href={`${threadURL(post)}#post-form`}
|
||||
data-thread-url={threadURL(post)}
|
||||
>
|
||||
{post.id}
|
||||
</a>
|
||||
</span>{" "}
|
||||
{board.config.user_ids && (
|
||||
<span
|
||||
class="user-id"
|
||||
style={{ backgroundColor: `#${post.user_id}` }}
|
||||
safe
|
||||
>
|
||||
{post.user_id}
|
||||
</span>
|
||||
)}
|
||||
{post.thread === null && (
|
||||
<>
|
||||
<a href={threadURL(post)}>[Otevřít]</a>{" "}
|
||||
</>
|
||||
)}
|
||||
{post.f_sticky !== 0 && (
|
||||
<>
|
||||
<img src="/public/img/icons/sticky.png" />{" "}
|
||||
</>
|
||||
)}
|
||||
{post.f_locked && (
|
||||
<>
|
||||
<img src="/public/img/icons/locked.png" />{" "}
|
||||
</>
|
||||
)}
|
||||
{post.f_bumplocked && (
|
||||
<>
|
||||
<img src="/public/img/icons/bumplock.png" />{" "}
|
||||
</>
|
||||
)}
|
||||
{post.f_looped && (
|
||||
<>
|
||||
<img src="/public/img/icons/loop.png" />{" "}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
<div class="clearfix">
|
||||
{
|
||||
<div
|
||||
class={`post-files${post.files.length > 1 ? " multiple-files" : ""}`}
|
||||
>
|
||||
{post.files.map((file) => (
|
||||
<div class="post-file">
|
||||
<div class="post-file-info">
|
||||
<a
|
||||
title={`Stáhnout ${file.original_filename}`}
|
||||
download={file.original_filename}
|
||||
href={file.url}
|
||||
safe
|
||||
>
|
||||
{truncateFilename(file.original_filename, 16)}
|
||||
</a>
|
||||
<br />
|
||||
<small>
|
||||
(<span safe>{bytesToSize(file.size)}</span>
|
||||
{file.dimensions !== null && (
|
||||
<span>
|
||||
, {file.dimensions[0]}x{file.dimensions[1]}
|
||||
</span>
|
||||
)}
|
||||
{file.duration !== null && (
|
||||
<span safe>, {secsToDuration(file.duration)}</span>
|
||||
)}
|
||||
)
|
||||
</small>
|
||||
</div>
|
||||
<a class="thumb-link" target="_blank" href={file.url}>
|
||||
<img
|
||||
class={"thumb thumb-" + file.type}
|
||||
src={(() => {
|
||||
if (file.thumb_url) {
|
||||
return file.thumb_url;
|
||||
}
|
||||
|
||||
switch (file.type) {
|
||||
case "image":
|
||||
return file.url;
|
||||
case "audio":
|
||||
return "/public/img/thumb/audio.png";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
})()}
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
}
|
||||
<div class="post-body post-content">{post.content as "safe"}</div>
|
||||
</div>
|
||||
{post.replies.length > 0 && (
|
||||
<small>
|
||||
Odpovědi:{" "}
|
||||
{post.replies.map((reply) => (
|
||||
<>
|
||||
<a
|
||||
class="quote"
|
||||
href={canonicalPostURL({
|
||||
board: board.id,
|
||||
id: reply,
|
||||
thread: post.id,
|
||||
} as Post)}
|
||||
>
|
||||
>>{reply}
|
||||
</a>{" "}
|
||||
</>
|
||||
))}
|
||||
</small>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
export default PostComponent;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { TemplateCtx } from "../../ctx";
|
||||
import { Board } from "../../db/schema";
|
||||
import { canUser, CzchanPerm } from "../../permissions";
|
||||
import { TemplateCtx } from "../ctx";
|
||||
import { Board } from "../db/schema";
|
||||
import { canUser, CzchanPerm } from "../permissions";
|
||||
import { Form, FormField, FormHeader } from "./form";
|
||||
import { PropsWithChildren } from "@kitajs/html";
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { valkey } from "../../cache";
|
||||
import PostForm from "../../components/forms/post_form";
|
||||
import IBHeader from "../../components/ib_header";
|
||||
import IBLinks from "../../components/ib_links";
|
||||
import Page from "../../components/page";
|
||||
import PageBody from "../../components/page_body";
|
||||
import PageHead from "../../components/page_head";
|
||||
import Pagination from "../../components/pagination";
|
||||
import PostComponent from "../../components/post";
|
||||
import PostForm from "../../components/post_form";
|
||||
import { getCtx, TemplateCtx } from "../../ctx";
|
||||
import { readRanomBanner } from "../../db/banner";
|
||||
import { readBoard } from "../../db/board";
|
||||
@@ -65,51 +66,64 @@ const Template = (
|
||||
page: number,
|
||||
threads: { op: Post; replies: Post[] }[],
|
||||
totalThreads: number,
|
||||
) => {
|
||||
return (
|
||||
<Page>
|
||||
<PageHead
|
||||
) => (
|
||||
<Page>
|
||||
<PageHead
|
||||
ctx={ctx}
|
||||
title={`/${board.id}/ - ${board.name} - Strana ${page}`}
|
||||
theme={board.config.theme}
|
||||
description={board.description}
|
||||
/>
|
||||
<PageBody ctx={ctx}>
|
||||
<IBHeader banner={banner} board={board} />
|
||||
<hr />
|
||||
<PostForm ctx={ctx} board={board} />
|
||||
<hr />
|
||||
<Pagination
|
||||
ctx={ctx}
|
||||
title={`/${board.id}/ - ${board.name} - Strana ${page}`}
|
||||
theme={board.config.theme}
|
||||
description={board.description}
|
||||
base={`/ib/${board.id}`}
|
||||
entries={totalThreads}
|
||||
current={page}
|
||||
max={board.config.pages}
|
||||
otherLinks={<IBLinks board={board} />}
|
||||
/>
|
||||
<PageBody ctx={ctx}>
|
||||
<IBHeader banner={banner} board={board} />
|
||||
<hr />
|
||||
<PostForm ctx={ctx} board={board} />
|
||||
<hr />
|
||||
{threads.map((thread) => (
|
||||
<div class="thread">
|
||||
<PostComponent board={board} post={thread.op} />
|
||||
{thread.replies.length > ctx.config.site.preview_replies && (
|
||||
<p>
|
||||
Bylo vynecháno {thread.replies.length} odpovědí.{" "}
|
||||
<a href={threadURL(thread.op)}>Kliknutím se otevře vlákno.</a>
|
||||
</p>
|
||||
)}
|
||||
{thread.replies
|
||||
.reverse()
|
||||
.slice(thread.replies.length - ctx.config.site.preview_replies)
|
||||
.reverse()
|
||||
.map((reply) => (
|
||||
<>
|
||||
<PostComponent board={board} post={reply} reply />
|
||||
<br />
|
||||
</>
|
||||
))}
|
||||
<hr />
|
||||
</div>
|
||||
))}
|
||||
<Pagination
|
||||
ctx={ctx}
|
||||
base={`/ib/${board.id}`}
|
||||
entries={totalThreads}
|
||||
current={page}
|
||||
max={board.config.pages}
|
||||
/>
|
||||
<hr />
|
||||
</PageBody>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
<hr />
|
||||
{threads.map((thread) => (
|
||||
<div class="thread">
|
||||
<PostComponent board={board} post={thread.op} />
|
||||
{thread.replies.length > ctx.config.site.preview_replies && (
|
||||
<p>
|
||||
Bylo vynecháno {thread.replies.length} odpovědí.{" "}
|
||||
<a href={threadURL(thread.op)}>Kliknutím se otevře vlákno.</a>
|
||||
</p>
|
||||
)}
|
||||
{thread.replies
|
||||
.reverse()
|
||||
.slice(
|
||||
Math.max(
|
||||
0,
|
||||
thread.replies.length - ctx.config.site.preview_replies,
|
||||
),
|
||||
)
|
||||
.reverse()
|
||||
.map((reply) => (
|
||||
<>
|
||||
<PostComponent board={board} post={reply} reply />
|
||||
<br />
|
||||
</>
|
||||
))}
|
||||
<hr />
|
||||
</div>
|
||||
))}
|
||||
<Pagination
|
||||
ctx={ctx}
|
||||
base={`/ib/${board.id}`}
|
||||
entries={totalThreads}
|
||||
current={page}
|
||||
max={board.config.pages}
|
||||
otherLinks={<IBLinks board={board} />}
|
||||
/>{" "}
|
||||
<hr />
|
||||
</PageBody>
|
||||
</Page>
|
||||
);
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import CatalogTile from "../../components/catalog_tile";
|
||||
import PostForm from "../../components/forms/post_form";
|
||||
import IBHeader from "../../components/ib_header";
|
||||
import IBLinks from "../../components/ib_links";
|
||||
import Page from "../../components/page";
|
||||
import PageBody from "../../components/page_body";
|
||||
import PageHead from "../../components/page_head";
|
||||
import PostForm from "../../components/post_form";
|
||||
import { getCtx, TemplateCtx } from "../../ctx";
|
||||
import { readRanomBanner } from "../../db/banner";
|
||||
import { readBoard } from "../../db/board";
|
||||
import { readAllBoardThreads } from "../../db/post";
|
||||
import { readBoardCatalog } from "../../db/post";
|
||||
import { Board, Post } from "../../db/schema";
|
||||
import { CzchanError } from "../../error";
|
||||
import { canUser, CzchanPerm } from "../../permissions";
|
||||
@@ -26,7 +27,7 @@ export default async (req: Request, res: Response) => {
|
||||
throw new CzchanError(`K nástěnce /${board.id}/ nemáš přístup.`, 403);
|
||||
}
|
||||
|
||||
const posts = await readAllBoardThreads(board.id);
|
||||
const posts = await readBoardCatalog(board.id);
|
||||
const html = Template(ctx, banner, board, posts);
|
||||
|
||||
res.send(html);
|
||||
@@ -37,27 +38,33 @@ const Template = (
|
||||
banner: string,
|
||||
board: Board,
|
||||
posts: Post[],
|
||||
) => {
|
||||
return (
|
||||
<Page>
|
||||
<PageHead
|
||||
ctx={ctx}
|
||||
title={`/${board.id}/ - ${board.name} - Katalog`}
|
||||
theme={board.config.theme}
|
||||
description={board.description}
|
||||
/>
|
||||
<PageBody ctx={ctx}>
|
||||
<IBHeader banner={banner} board={board} catalog />
|
||||
<hr />
|
||||
<PostForm ctx={ctx} board={board} />
|
||||
<hr />
|
||||
<div class="center">
|
||||
{posts.map((post) => (
|
||||
<CatalogTile post={post} />
|
||||
))}
|
||||
</div>
|
||||
<hr />
|
||||
</PageBody>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
) => (
|
||||
<Page>
|
||||
<PageHead
|
||||
ctx={ctx}
|
||||
title={`/${board.id}/ - ${board.name} - Katalog`}
|
||||
theme={board.config.theme}
|
||||
description={board.description}
|
||||
/>
|
||||
<PageBody ctx={ctx}>
|
||||
<IBHeader banner={banner} board={board} catalog />
|
||||
<hr />
|
||||
<PostForm ctx={ctx} board={board} />
|
||||
<hr />
|
||||
<div class="pagination">
|
||||
<IBLinks board={board} />
|
||||
</div>
|
||||
<hr />
|
||||
<div class="center">
|
||||
{posts.map((post) => (
|
||||
<CatalogTile post={post} />
|
||||
))}
|
||||
</div>
|
||||
<hr />
|
||||
<div class="pagination">
|
||||
<IBLinks board={board} />
|
||||
</div>
|
||||
<hr />
|
||||
</PageBody>
|
||||
</Page>
|
||||
);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import PostForm from "../../components/forms/post_form";
|
||||
import IBHeader from "../../components/ib_header";
|
||||
import IBLinks from "../../components/ib_links";
|
||||
import Page from "../../components/page";
|
||||
import PageBody from "../../components/page_body";
|
||||
import PageHead from "../../components/page_head";
|
||||
import PostComponent from "../../components/post";
|
||||
import PostForm from "../../components/post_form";
|
||||
import { getCtx, TemplateCtx } from "../../ctx";
|
||||
import { readRanomBanner } from "../../db/banner";
|
||||
import { readBoard } from "../../db/board";
|
||||
@@ -47,31 +48,37 @@ const Template = (
|
||||
board: Board,
|
||||
op: Post,
|
||||
replies: Post[],
|
||||
) => {
|
||||
return (
|
||||
<Page>
|
||||
<PageHead
|
||||
ctx={ctx}
|
||||
title={`/${board.id}/ - ${op.subject || truncate(op.content_unformatted, 64)}`}
|
||||
theme={board.config.theme}
|
||||
description={truncate(op.content_unformatted, 128)}
|
||||
/>
|
||||
<PageBody ctx={ctx}>
|
||||
<IBHeader banner={banner} board={board} />
|
||||
<hr />
|
||||
<PostForm ctx={ctx} board={board} thread={op.id} />
|
||||
<hr />
|
||||
) => (
|
||||
<Page>
|
||||
<PageHead
|
||||
ctx={ctx}
|
||||
title={`/${board.id}/ - ${op.subject || truncate(op.content_unformatted, 64)}`}
|
||||
theme={board.config.theme}
|
||||
description={truncate(op.content_unformatted, 128)}
|
||||
/>
|
||||
<PageBody ctx={ctx}>
|
||||
<IBHeader banner={banner} board={board} />
|
||||
<hr />
|
||||
<PostForm ctx={ctx} board={board} thread={op.id} />
|
||||
<hr />
|
||||
<div class="pagination">
|
||||
<IBLinks board={board} />
|
||||
</div>
|
||||
<hr />
|
||||
<div class="thread">
|
||||
<PostComponent board={board} post={op} />
|
||||
<div class="thread">
|
||||
{replies.map((reply) => (
|
||||
<>
|
||||
<PostComponent board={board} post={reply} reply />
|
||||
<br />
|
||||
</>
|
||||
))}
|
||||
</div>
|
||||
<hr />
|
||||
</PageBody>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
{replies.map((reply) => (
|
||||
<>
|
||||
<PostComponent board={board} post={reply} reply />
|
||||
<br />
|
||||
</>
|
||||
))}
|
||||
</div>
|
||||
<hr />
|
||||
<div class="pagination">
|
||||
<IBLinks board={board} />
|
||||
</div>
|
||||
<hr />
|
||||
</PageBody>
|
||||
</Page>
|
||||
);
|
||||
|
||||
@@ -11,22 +11,20 @@ export default async (req: Request, res: Response) => {
|
||||
res.send(html);
|
||||
};
|
||||
|
||||
const Template = (ctx: TemplateCtx) => {
|
||||
return (
|
||||
<Page>
|
||||
<PageHead
|
||||
ctx={ctx}
|
||||
title="czchan"
|
||||
description="Český anonymní imageboard"
|
||||
/>
|
||||
<PageBody ctx={ctx}>
|
||||
<div class="center">
|
||||
<img class="logo" src="/public/img/logo.png" />
|
||||
<p class="subtitle">Český anonymní imageboard</p>
|
||||
<hr />
|
||||
<hr />
|
||||
</div>
|
||||
</PageBody>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
const Template = (ctx: TemplateCtx) => (
|
||||
<Page>
|
||||
<PageHead
|
||||
ctx={ctx}
|
||||
title="czchan"
|
||||
description="Český anonymní imageboard"
|
||||
/>
|
||||
<PageBody ctx={ctx}>
|
||||
<div class="center">
|
||||
<img class="logo" src="/public/img/logo.png" />
|
||||
<p class="subtitle">Český anonymní imageboard</p>
|
||||
<hr />
|
||||
<hr />
|
||||
</div>
|
||||
</PageBody>
|
||||
</Page>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Form, FormField } from "../components/forms/form";
|
||||
import { Form, FormField } from "../components/form";
|
||||
import Page from "../components/page";
|
||||
import PageBody from "../components/page_body";
|
||||
import PageHead from "../components/page_head";
|
||||
@@ -12,25 +12,23 @@ export default async (req: Request, res: Response) => {
|
||||
res.send(html);
|
||||
};
|
||||
|
||||
const Template = (ctx: TemplateCtx) => {
|
||||
return (
|
||||
<Page>
|
||||
<PageHead ctx={ctx} title="Přihlásit se" description="" />
|
||||
<PageBody ctx={ctx}>
|
||||
<h1 class="title center">Přihlásit se</h1>
|
||||
<hr />
|
||||
<form method="post" action="/actions/login">
|
||||
<Form submit="Přihlásit se" action="$login" center>
|
||||
<FormField label="Jméno">
|
||||
<input name="username" type="text" />
|
||||
</FormField>
|
||||
<FormField label="Heslo">
|
||||
<input name="password" type="password" />
|
||||
</FormField>
|
||||
</Form>
|
||||
</form>
|
||||
<hr />
|
||||
</PageBody>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
const Template = (ctx: TemplateCtx) => (
|
||||
<Page>
|
||||
<PageHead ctx={ctx} title="Přihlásit se" description="" />
|
||||
<PageBody ctx={ctx}>
|
||||
<h1 class="title center">Přihlásit se</h1>
|
||||
<hr />
|
||||
<form method="post" action="/actions/login">
|
||||
<Form submit="Přihlásit se" action="$login" center>
|
||||
<FormField label="Jméno">
|
||||
<input name="username" type="text" />
|
||||
</FormField>
|
||||
<FormField label="Heslo">
|
||||
<input name="password" type="password" />
|
||||
</FormField>
|
||||
</Form>
|
||||
</form>
|
||||
<hr />
|
||||
</PageBody>
|
||||
</Page>
|
||||
);
|
||||
|
||||
@@ -145,7 +145,7 @@ const updateCapcode = async (
|
||||
const segments = capcode_icon.split("$");
|
||||
|
||||
if (
|
||||
segments[0] === "file" &&
|
||||
segments[0] === "icon" &&
|
||||
!req.config.assets.capcodeIcons.includes(segments[1])
|
||||
) {
|
||||
throw new CzchanError(`Ikona ${segments[1]} neexistuje.`, 400);
|
||||
@@ -172,7 +172,7 @@ const updateCapcode = async (
|
||||
case "none":
|
||||
icon = null;
|
||||
break;
|
||||
case "file":
|
||||
case "icon":
|
||||
icon = segments[1];
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Datetime from "../../components/datetime";
|
||||
import { Form, FormField } from "../../components/forms/form";
|
||||
import { Form, FormField } from "../../components/form";
|
||||
import ModNav from "../../components/mod_nav";
|
||||
import Page from "../../components/page";
|
||||
import PageBody from "../../components/page_body";
|
||||
@@ -24,32 +24,30 @@ export default async (req: Request, res: Response) => {
|
||||
res.send(html);
|
||||
};
|
||||
|
||||
const Template = (ctx: TemplateCtx, banners: Banner[]) => {
|
||||
return (
|
||||
<Page>
|
||||
<PageHead ctx={ctx} title="Bannery" description="" />
|
||||
<PageBody ctx={ctx}>
|
||||
<h1 class="title center">Bannery</h1>
|
||||
<hr />
|
||||
<ModNav ctx={ctx} />
|
||||
<hr />
|
||||
<form method="post" action="/mod/actions/banner_actions">
|
||||
<BannerTable ctx={ctx} banners={banners} />
|
||||
{canUser(ctx.user, CzchanPerm.MANAGE_BANNERS) && (
|
||||
<>
|
||||
<hr />
|
||||
<button name="action" type="submit" value="$delete_banner">
|
||||
Odstranit
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</form>
|
||||
<CreateForm />
|
||||
<hr />
|
||||
</PageBody>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
const Template = (ctx: TemplateCtx, banners: Banner[]) => (
|
||||
<Page>
|
||||
<PageHead ctx={ctx} title="Bannery" description="" />
|
||||
<PageBody ctx={ctx}>
|
||||
<h1 class="title center">Bannery</h1>
|
||||
<hr />
|
||||
<ModNav ctx={ctx} />
|
||||
<hr />
|
||||
<form method="post" action="/mod/actions/banner_actions">
|
||||
<BannerTable ctx={ctx} banners={banners} />
|
||||
{canUser(ctx.user, CzchanPerm.MANAGE_BANNERS) && (
|
||||
<>
|
||||
<hr />
|
||||
<button name="action" type="submit" value="$delete_banner">
|
||||
Odstranit
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</form>
|
||||
<CreateForm />
|
||||
<hr />
|
||||
</PageBody>
|
||||
</Page>
|
||||
);
|
||||
|
||||
const BannerTable = ({
|
||||
ctx,
|
||||
|
||||
@@ -25,27 +25,25 @@ export default async (req: Request, res: Response) => {
|
||||
res.send(html);
|
||||
};
|
||||
|
||||
const Template = (ctx: TemplateCtx, bans: Ban[]) => {
|
||||
return (
|
||||
<Page>
|
||||
<PageHead ctx={ctx} title="Bany" description="" />
|
||||
<PageBody ctx={ctx}>
|
||||
<h1 class="title center">Bany</h1>
|
||||
const Template = (ctx: TemplateCtx, bans: Ban[]) => (
|
||||
<Page>
|
||||
<PageHead ctx={ctx} title="Bany" description="" />
|
||||
<PageBody ctx={ctx}>
|
||||
<h1 class="title center">Bany</h1>
|
||||
<hr />
|
||||
<ModNav ctx={ctx} />
|
||||
<hr />
|
||||
<form method="post" action="/mod/actions/ban_actions">
|
||||
<BanTable ctx={ctx} bans={bans} />
|
||||
<hr />
|
||||
<ModNav ctx={ctx} />
|
||||
<hr />
|
||||
<form method="post" action="/mod/actions/ban_actions">
|
||||
<BanTable ctx={ctx} bans={bans} />
|
||||
<hr />
|
||||
<button name="action" type="submit" value="$delete_ban">
|
||||
Odstranit
|
||||
</button>
|
||||
</form>
|
||||
<hr />
|
||||
</PageBody>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
<button name="action" type="submit" value="$delete_ban">
|
||||
Odstranit
|
||||
</button>
|
||||
</form>
|
||||
<hr />
|
||||
</PageBody>
|
||||
</Page>
|
||||
);
|
||||
|
||||
const BanTable = ({
|
||||
ctx,
|
||||
@@ -89,41 +87,37 @@ const BanRow = ({
|
||||
}: PropsWithChildren<{ ctx: TemplateCtx; ban: Ban }>) => (
|
||||
<tr>
|
||||
<td>
|
||||
{(() => {
|
||||
return (
|
||||
<input
|
||||
name="bans"
|
||||
type="checkbox"
|
||||
value={ban.id.toString()}
|
||||
disabled={
|
||||
!(
|
||||
ctx.user?.username === ban.created_by ||
|
||||
canUser(ctx.user, CzchanPerm.ALL_BANS)
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
})()}
|
||||
<input
|
||||
name="bans"
|
||||
type="checkbox"
|
||||
value={ban.id.toString()}
|
||||
disabled={
|
||||
!(
|
||||
ctx.user?.username === ban.created_by ||
|
||||
canUser(ctx.user, CzchanPerm.ALL_BANS)
|
||||
)
|
||||
}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<IPAddress ctx={ctx} ip={ban.ip_range} />
|
||||
</td>
|
||||
<td>
|
||||
{ban.board === null ? (
|
||||
<i>Všechny</i>
|
||||
) : (
|
||||
{ban.board !== null ? (
|
||||
<span safe>{`/${ban.board}/`}</span>
|
||||
) : (
|
||||
<i>Všechny</i>
|
||||
)}
|
||||
</td>
|
||||
<td>
|
||||
<BoolIcon value={ban.appealable} />
|
||||
</td>
|
||||
<td>{ban.appeal === null ? "-" : <span safe>{ban.appeal}</span>}</td>
|
||||
<td>{ban.appeal !== null ? <span safe>{ban.appeal}</span> : "-"}</td>
|
||||
<td>
|
||||
{ban.appeal_response === null ? (
|
||||
"-"
|
||||
) : (
|
||||
{ban.appeal_response !== null ? (
|
||||
<span safe>{ban.appeal_response}</span>
|
||||
) : (
|
||||
"-"
|
||||
)}
|
||||
</td>
|
||||
<td safe>{ban.created_by}</td>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import BoardConfigForm from "../../components/forms/board_config_form";
|
||||
import BoardConfigForm from "../../components/board_config_form";
|
||||
import Page from "../../components/page";
|
||||
import PageBody from "../../components/page_body";
|
||||
import PageHead from "../../components/page_head";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Datetime from "../../components/datetime";
|
||||
import { Form, FormField } from "../../components/forms/form";
|
||||
import { Form, FormField } from "../../components/form";
|
||||
import ModNav from "../../components/mod_nav";
|
||||
import Page from "../../components/page";
|
||||
import PageBody from "../../components/page_body";
|
||||
@@ -24,29 +24,27 @@ export default async (req: Request, res: Response) => {
|
||||
res.send(html);
|
||||
};
|
||||
|
||||
const Template = (ctx: TemplateCtx, boards: Board[]) => {
|
||||
return (
|
||||
<Page>
|
||||
<PageHead ctx={ctx} title="Nástěnky" description="" />
|
||||
<PageBody ctx={ctx}>
|
||||
<h1 class="title center">Nástěnky</h1>
|
||||
const Template = (ctx: TemplateCtx, boards: Board[]) => (
|
||||
<Page>
|
||||
<PageHead ctx={ctx} title="Nástěnky" description="" />
|
||||
<PageBody ctx={ctx}>
|
||||
<h1 class="title center">Nástěnky</h1>
|
||||
<hr />
|
||||
<ModNav ctx={ctx} />
|
||||
<hr />
|
||||
<form method="post" action="/mod/actions/board_actions">
|
||||
<BoardTable ctx={ctx} boards={boards} />
|
||||
<hr />
|
||||
<ModNav ctx={ctx} />
|
||||
<hr />
|
||||
<form method="post" action="/mod/actions/board_actions">
|
||||
<BoardTable ctx={ctx} boards={boards} />
|
||||
<hr />
|
||||
<button name="action" type="submit" value="$delete_board">
|
||||
Odstranit
|
||||
</button>
|
||||
<UpdateForm />
|
||||
</form>
|
||||
<CreateForm />
|
||||
<hr />
|
||||
</PageBody>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
<button name="action" type="submit" value="$delete_board">
|
||||
Odstranit
|
||||
</button>
|
||||
<UpdateForm />
|
||||
</form>
|
||||
<CreateForm />
|
||||
<hr />
|
||||
</PageBody>
|
||||
</Page>
|
||||
);
|
||||
|
||||
const BoardTable = ({
|
||||
ctx,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Capcode from "../../components/capcode";
|
||||
import { Form, FormField } from "../../components/forms/form";
|
||||
import { Form, FormField } from "../../components/form";
|
||||
import ModNav from "../../components/mod_nav";
|
||||
import Page from "../../components/page";
|
||||
import PageBody from "../../components/page_body";
|
||||
@@ -19,24 +19,22 @@ export default async (req: Request, res: Response) => {
|
||||
res.send(html);
|
||||
};
|
||||
|
||||
const Template = (ctx: TemplateCtx, boards: Board[]) => {
|
||||
return (
|
||||
<Page>
|
||||
<PageHead ctx={ctx} title="Domov" description="" />
|
||||
<PageBody ctx={ctx}>
|
||||
<h1 class="title center">Domov</h1>
|
||||
<hr />
|
||||
<ModNav ctx={ctx} />
|
||||
<hr />
|
||||
<UserTable ctx={ctx} />
|
||||
<hr />
|
||||
<BoardTable ctx={ctx} boards={boards} />
|
||||
<PasswordChangeForm />
|
||||
<hr />
|
||||
</PageBody>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
const Template = (ctx: TemplateCtx, boards: Board[]) => (
|
||||
<Page>
|
||||
<PageHead ctx={ctx} title="Domov" description="" />
|
||||
<PageBody ctx={ctx}>
|
||||
<h1 class="title center">Domov</h1>
|
||||
<hr />
|
||||
<ModNav ctx={ctx} />
|
||||
<hr />
|
||||
<UserTable ctx={ctx} />
|
||||
<hr />
|
||||
<BoardTable ctx={ctx} boards={boards} />
|
||||
<PasswordChangeForm />
|
||||
<hr />
|
||||
</PageBody>
|
||||
</Page>
|
||||
);
|
||||
|
||||
const UserTable = ({ ctx }: PropsWithChildren<{ ctx: TemplateCtx }>) => (
|
||||
<div class="table-wrapper">
|
||||
|
||||
@@ -18,17 +18,15 @@ export default async (req: Request, res: Response) => {
|
||||
res.send(html);
|
||||
};
|
||||
|
||||
const Template = (ctx: TemplateCtx) => {
|
||||
return (
|
||||
<Page>
|
||||
<PageHead ctx={ctx} title="Záznamy" description="" />
|
||||
<PageBody ctx={ctx}>
|
||||
<h1 class="title center">Záznamy</h1>
|
||||
<p class="subtitle center">TODO</p>
|
||||
<hr />
|
||||
<ModNav ctx={ctx} />
|
||||
<hr />
|
||||
</PageBody>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
const Template = (ctx: TemplateCtx) => (
|
||||
<Page>
|
||||
<PageHead ctx={ctx} title="Záznamy" description="" />
|
||||
<PageBody ctx={ctx}>
|
||||
<h1 class="title center">Záznamy</h1>
|
||||
<p class="subtitle center">TODO</p>
|
||||
<hr />
|
||||
<ModNav ctx={ctx} />
|
||||
<hr />
|
||||
</PageBody>
|
||||
</Page>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Datetime from "../../components/datetime";
|
||||
import { Form, FormField } from "../../components/forms/form";
|
||||
import { Form, FormField } from "../../components/form";
|
||||
import ModNav from "../../components/mod_nav";
|
||||
import Page from "../../components/page";
|
||||
import PageBody from "../../components/page_body";
|
||||
@@ -24,28 +24,26 @@ export default async (req: Request, res: Response) => {
|
||||
res.send(html);
|
||||
};
|
||||
|
||||
const Template = (ctx: TemplateCtx, news: News[]) => {
|
||||
return (
|
||||
<Page>
|
||||
<PageHead ctx={ctx} title="Novinky" description="" />
|
||||
<PageBody ctx={ctx}>
|
||||
<h1 class="title center">Novinky</h1>
|
||||
const Template = (ctx: TemplateCtx, news: News[]) => (
|
||||
<Page>
|
||||
<PageHead ctx={ctx} title="Novinky" description="" />
|
||||
<PageBody ctx={ctx}>
|
||||
<h1 class="title center">Novinky</h1>
|
||||
<hr />
|
||||
<ModNav ctx={ctx} />
|
||||
<hr />
|
||||
<form method="post" action="/mod/actions/news_actions">
|
||||
<NewsTable ctx={ctx} news={news} />
|
||||
<hr />
|
||||
<ModNav ctx={ctx} />
|
||||
<hr />
|
||||
<form method="post" action="/mod/actions/news_actions">
|
||||
<NewsTable ctx={ctx} news={news} />
|
||||
<hr />
|
||||
<button name="action" type="submit" value="$delete_news">
|
||||
Odstranit
|
||||
</button>
|
||||
</form>
|
||||
<CreateForm />
|
||||
<hr />
|
||||
</PageBody>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
<button name="action" type="submit" value="$delete_news">
|
||||
Odstranit
|
||||
</button>
|
||||
</form>
|
||||
<CreateForm />
|
||||
<hr />
|
||||
</PageBody>
|
||||
</Page>
|
||||
);
|
||||
|
||||
const NewsTable = ({
|
||||
ctx,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Form, FormField } from "../../components/forms/form";
|
||||
import { Form, FormField } from "../../components/form";
|
||||
import Page from "../../components/page";
|
||||
import PageBody from "../../components/page_body";
|
||||
import PageHead from "../../components/page_head";
|
||||
|
||||
@@ -18,17 +18,15 @@ export default async (req: Request, res: Response) => {
|
||||
res.send(html);
|
||||
};
|
||||
|
||||
const Template = (ctx: TemplateCtx) => {
|
||||
return (
|
||||
<Page>
|
||||
<PageHead ctx={ctx} title="Hlášení" description="" />
|
||||
<PageBody ctx={ctx}>
|
||||
<h1 class="title center">Hlášení</h1>
|
||||
<p class="subtitle center">TODO</p>
|
||||
<hr />
|
||||
<ModNav ctx={ctx} />
|
||||
<hr />
|
||||
</PageBody>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
const Template = (ctx: TemplateCtx) => (
|
||||
<Page>
|
||||
<PageHead ctx={ctx} title="Hlášení" description="" />
|
||||
<PageBody ctx={ctx}>
|
||||
<h1 class="title center">Hlášení</h1>
|
||||
<p class="subtitle center">TODO</p>
|
||||
<hr />
|
||||
<ModNav ctx={ctx} />
|
||||
<hr />
|
||||
</PageBody>
|
||||
</Page>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Form, FormField } from "../../components/forms/form";
|
||||
import { Form, FormField } from "../../components/form";
|
||||
import Page from "../../components/page";
|
||||
import PageBody from "../../components/page_body";
|
||||
import PageHead from "../../components/page_head";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Capcode from "../../components/capcode";
|
||||
import Datetime from "../../components/datetime";
|
||||
import { Form, FormField } from "../../components/forms/form";
|
||||
import { Form, FormField } from "../../components/form";
|
||||
import ModNav from "../../components/mod_nav";
|
||||
import Page from "../../components/page";
|
||||
import PageBody from "../../components/page_body";
|
||||
@@ -25,36 +25,34 @@ export default async (req: Request, res: Response) => {
|
||||
res.send(html);
|
||||
};
|
||||
|
||||
const Template = (ctx: TemplateCtx, users: User[]) => {
|
||||
return (
|
||||
<Page>
|
||||
<PageHead ctx={ctx} title="Uživatelé" description="" />
|
||||
<PageBody ctx={ctx}>
|
||||
<h1 class="title center">Uživatelé</h1>
|
||||
const Template = (ctx: TemplateCtx, users: User[]) => (
|
||||
<Page>
|
||||
<PageHead ctx={ctx} title="Uživatelé" description="" />
|
||||
<PageBody ctx={ctx}>
|
||||
<h1 class="title center">Uživatelé</h1>
|
||||
<hr />
|
||||
<ModNav ctx={ctx} />
|
||||
<hr />
|
||||
<form method="post" action="/mod/actions/user_actions">
|
||||
<UserTable ctx={ctx} users={users} />
|
||||
<hr />
|
||||
<ModNav ctx={ctx} />
|
||||
<hr />
|
||||
<form method="post" action="/mod/actions/user_actions">
|
||||
<UserTable ctx={ctx} users={users} />
|
||||
<hr />
|
||||
<button name="action" type="submit" value="$delete_user">
|
||||
Odstranit
|
||||
</button>
|
||||
{canUser(ctx.user, CzchanPerm.MANAGE_USERS) && (
|
||||
<UpdateRankForm ctx={ctx} />
|
||||
)}
|
||||
{(canUser(ctx.user, CzchanPerm.MANAGE_USERS) ||
|
||||
canUser(ctx.user, CzchanPerm.CUSTOM_CAPCODE)) && (
|
||||
<UpdateCapcodeForm ctx={ctx} />
|
||||
)}
|
||||
{canUser(ctx.user, CzchanPerm.MANAGE_USERS) && <RoleForm ctx={ctx} />}
|
||||
</form>
|
||||
{canUser(ctx.user, CzchanPerm.MANAGE_USERS) && <CreateForm ctx={ctx} />}
|
||||
<hr />
|
||||
</PageBody>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
<button name="action" type="submit" value="$delete_user">
|
||||
Odstranit
|
||||
</button>
|
||||
{canUser(ctx.user, CzchanPerm.MANAGE_USERS) && (
|
||||
<UpdateRankForm ctx={ctx} />
|
||||
)}
|
||||
{(canUser(ctx.user, CzchanPerm.MANAGE_USERS) ||
|
||||
canUser(ctx.user, CzchanPerm.CUSTOM_CAPCODE)) && (
|
||||
<UpdateCapcodeForm ctx={ctx} />
|
||||
)}
|
||||
{canUser(ctx.user, CzchanPerm.MANAGE_USERS) && <RoleForm ctx={ctx} />}
|
||||
</form>
|
||||
{canUser(ctx.user, CzchanPerm.MANAGE_USERS) && <CreateForm ctx={ctx} />}
|
||||
<hr />
|
||||
</PageBody>
|
||||
</Page>
|
||||
);
|
||||
|
||||
const UserTable = ({
|
||||
ctx,
|
||||
|
||||
@@ -16,21 +16,19 @@ export default async (req: Request, res: Response) => {
|
||||
res.send(html);
|
||||
};
|
||||
|
||||
const Template = (ctx: TemplateCtx, news: News[]) => {
|
||||
return (
|
||||
<Page>
|
||||
<PageHead ctx={ctx} title="Novinky" />
|
||||
<PageBody ctx={ctx}>
|
||||
<div class="container">
|
||||
<h1 class="title center">Novinky</h1>
|
||||
{news.map((news) => (
|
||||
<NewsPost news={news} />
|
||||
))}
|
||||
</div>
|
||||
</PageBody>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
const Template = (ctx: TemplateCtx, news: News[]) => (
|
||||
<Page>
|
||||
<PageHead ctx={ctx} title="Novinky" />
|
||||
<PageBody ctx={ctx}>
|
||||
<div class="container">
|
||||
<h1 class="title center">Novinky</h1>
|
||||
{news.map((news) => (
|
||||
<NewsPost news={news} />
|
||||
))}
|
||||
</div>
|
||||
</PageBody>
|
||||
</Page>
|
||||
);
|
||||
|
||||
const NewsPost = ({ news }: PropsWithChildren<{ news: News }>) => (
|
||||
<article class="infobox">
|
||||
|
||||
Reference in New Issue
Block a user