IDs + time
This commit is contained in:
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true,
|
||||
"editor.formatOnSave": true
|
||||
"editor.formatOnSave": true,
|
||||
"editor.tabSize": 2
|
||||
}
|
||||
|
||||
@@ -24,10 +24,10 @@
|
||||
"limit_ip": 50,
|
||||
"interval_ip": 600,
|
||||
"limit_content": 10,
|
||||
"interval_content": 6000,
|
||||
"interval_content": 600,
|
||||
"limit_both": 10,
|
||||
"interval_both": 36000,
|
||||
"code_deletion_timeout": 6000,
|
||||
"interval_both": 3600,
|
||||
"code_deletion_timeout": 600,
|
||||
"reply_limit": 10000,
|
||||
"bump_limit": 8000,
|
||||
"noko": true,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { TemplateCtx } from "../ctx";
|
||||
import { Board } from "../db/schema";
|
||||
import { secsToTimestring } from "../time";
|
||||
import { Form, FormField } from "./form";
|
||||
import { PropsWithChildren } from "@kitajs/html";
|
||||
|
||||
@@ -127,11 +128,11 @@ const BoardConfigForm = ({
|
||||
value={board.config.limit_ip.toString()}
|
||||
/>
|
||||
</FormField>
|
||||
<FormField label="Interval: IP (s)">
|
||||
<FormField label="Interval: IP">
|
||||
<input
|
||||
name="interval_ip"
|
||||
type="number"
|
||||
value={board.config.interval_ip.toString()}
|
||||
type="text"
|
||||
value={secsToTimestring(board.config.interval_ip)}
|
||||
/>
|
||||
</FormField>
|
||||
<FormField label="Limit: Obsah">
|
||||
@@ -141,11 +142,11 @@ const BoardConfigForm = ({
|
||||
value={board.config.limit_content.toString()}
|
||||
/>
|
||||
</FormField>
|
||||
<FormField label="Interval: Obsah (s)">
|
||||
<FormField label="Interval: Obsah">
|
||||
<input
|
||||
name="interval_content"
|
||||
type="number"
|
||||
value={board.config.interval_ip.toString()}
|
||||
type="text"
|
||||
value={secsToTimestring(board.config.interval_content)}
|
||||
/>
|
||||
</FormField>
|
||||
<FormField label="Limit: IP + Obsah">
|
||||
@@ -155,18 +156,18 @@ const BoardConfigForm = ({
|
||||
value={board.config.limit_both.toString()}
|
||||
/>
|
||||
</FormField>
|
||||
<FormField label="Interval: IP + Obsah (s)">
|
||||
<FormField label="Interval: IP + Obsah">
|
||||
<input
|
||||
name="interval_both"
|
||||
type="number"
|
||||
value={board.config.interval_both.toString()}
|
||||
type="text"
|
||||
value={secsToTimestring(board.config.interval_both)}
|
||||
/>
|
||||
</FormField>
|
||||
<FormField label="Vymazání kódem do (s)">
|
||||
<FormField label="Vymazání kódem do">
|
||||
<input
|
||||
name="code_deletion_timeout"
|
||||
type="number"
|
||||
value={board.config.max_length.toString()}
|
||||
type="text"
|
||||
value={secsToTimestring(board.config.code_deletion_timeout)}
|
||||
/>
|
||||
</FormField>
|
||||
<FormField label="Limit odpvědí">
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Board, Post } from "../db/schema";
|
||||
import { secsToDuration } from "../time";
|
||||
import {
|
||||
bytesToSize,
|
||||
canonicalPostURL,
|
||||
secsToDuration,
|
||||
contrastingColor,
|
||||
threadURL,
|
||||
truncateFilename,
|
||||
} from "../util";
|
||||
@@ -75,13 +76,18 @@ const PostComponent = ({
|
||||
</a>
|
||||
</span>{" "}
|
||||
{board.config.user_ids && (
|
||||
<span
|
||||
class="user-id"
|
||||
style={{ backgroundColor: `#${post.user_id}` }}
|
||||
safe
|
||||
>
|
||||
{post.user_id}
|
||||
</span>
|
||||
<>
|
||||
<span
|
||||
class="user-id"
|
||||
style={{
|
||||
backgroundColor: `#${post.user_id.slice(0, 6)}`,
|
||||
color: `#${contrastingColor(post.user_id.slice(0, 6))}`,
|
||||
}}
|
||||
safe
|
||||
>
|
||||
{post.user_id}
|
||||
</span>{" "}
|
||||
</>
|
||||
)}
|
||||
{post.thread === null && (
|
||||
<>
|
||||
|
||||
@@ -92,10 +92,8 @@ const PostForm = ({
|
||||
(thread !== undefined && board.config.reply_captcha)) &&
|
||||
!canUser(ctx.user, CzchanPerm.BYPASS_CAPTCHA)) && (
|
||||
<>
|
||||
<FormField label="CAPTCHA" wrapper>
|
||||
<FormField label="CAPTCHA" wrapper required>
|
||||
<img class="full-width" src="/captcha" />
|
||||
</FormField>
|
||||
<FormField label="Řešení" required>
|
||||
<input name="captcha" type="text" />
|
||||
</FormField>
|
||||
</>
|
||||
|
||||
86
src/time.ts
Normal file
86
src/time.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import timestring from "timestring";
|
||||
|
||||
const SECOND = 1,
|
||||
MINUTE = 60,
|
||||
HOUR = 3600,
|
||||
DAY = 86400,
|
||||
WEEK = 604800,
|
||||
MONTH = 2592000,
|
||||
YEAR = 31536000;
|
||||
|
||||
const secsToDuration = (secs: number) => {
|
||||
let hours = 0;
|
||||
let minutes = 0;
|
||||
let seconds = 0;
|
||||
|
||||
hours = Math.floor(secs / HOUR);
|
||||
secs -= hours * HOUR;
|
||||
minutes = Math.floor(secs / MINUTE);
|
||||
secs -= minutes * MINUTE;
|
||||
seconds = Math.floor(secs / SECOND);
|
||||
secs -= seconds * SECOND;
|
||||
|
||||
let duration = "";
|
||||
|
||||
if (hours > 0) {
|
||||
duration += `${hours.toString().padStart(2, "0")}:`;
|
||||
}
|
||||
|
||||
duration += `${minutes.toString().padStart(2, "0")}:`;
|
||||
duration += `${seconds.toString().padStart(2, "0")}`;
|
||||
|
||||
return duration;
|
||||
};
|
||||
|
||||
// Shits out a timestring, to be used for autofills
|
||||
const secsToTimestring = (secs: number) => {
|
||||
let years = 0;
|
||||
let months = 0;
|
||||
let weeks = 0;
|
||||
let days = 0;
|
||||
let hours = 0;
|
||||
let minutes = 0;
|
||||
let seconds = 0;
|
||||
|
||||
years = Math.floor(secs / YEAR);
|
||||
secs -= years * YEAR;
|
||||
months = Math.floor(secs / MONTH);
|
||||
secs -= months * MONTH;
|
||||
weeks = Math.floor(secs / WEEK);
|
||||
secs -= weeks * WEEK;
|
||||
days = Math.floor(secs / DAY);
|
||||
secs -= days * DAY;
|
||||
hours = Math.floor(secs / HOUR);
|
||||
secs -= hours * HOUR;
|
||||
minutes = Math.floor(secs / MINUTE);
|
||||
secs -= minutes * MINUTE;
|
||||
seconds = Math.floor(secs / SECOND);
|
||||
secs -= seconds * SECOND;
|
||||
|
||||
const segments = [];
|
||||
|
||||
if (years > 0) segments.push(`${years}y`);
|
||||
if (months > 0) segments.push(`${months}mon`);
|
||||
if (weeks > 0) segments.push(`${weeks}w`);
|
||||
if (days > 0) segments.push(`${days}d`);
|
||||
if (hours > 0) segments.push(`${hours}h`);
|
||||
if (minutes > 0) segments.push(`${minutes}m`);
|
||||
if (seconds > 0) segments.push(`${seconds}s`);
|
||||
|
||||
return segments.join(" ");
|
||||
};
|
||||
|
||||
const timestringToSecs = (time: string) => {
|
||||
try {
|
||||
// Horrible but consistent with the inverse and good enough for input
|
||||
return timestring(time, undefined, {
|
||||
weeksPerMonth: 30 / 7,
|
||||
monthsPerYear: 73 / 6,
|
||||
daysPerYear: 365,
|
||||
});
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export { secsToDuration, secsToTimestring, timestringToSecs };
|
||||
38
src/util.ts
38
src/util.ts
@@ -2,7 +2,6 @@ import { Board, Post } from "./db/schema";
|
||||
import { createHash } from "crypto";
|
||||
import ipaddr, { IPv6 } from "ipaddr.js";
|
||||
import { parse } from "path";
|
||||
import timestring from "timestring";
|
||||
|
||||
const encodeCapcode = (capcode: {
|
||||
text: string;
|
||||
@@ -118,35 +117,15 @@ const bytesToSize = (bytes: number) => {
|
||||
return `${(bytes / Math.pow(1024, e)).toFixed(2)} ${" KMGTPEZY".charAt(e)}B`;
|
||||
};
|
||||
|
||||
// TODO: MAKE THIS LESS RETARDED
|
||||
// For user IDs
|
||||
|
||||
const secsToDuration = (secs: number) => {
|
||||
let date = new Date(0);
|
||||
const contrastingColor = (color: string) => {
|
||||
const r = parseInt(color.slice(0, 2), 16);
|
||||
const g = parseInt(color.slice(2, 4), 16);
|
||||
const b = parseInt(color.slice(4, 6), 16);
|
||||
const luma = r * 0.2126 + g * 0.7152 + b * 0.0722;
|
||||
|
||||
date.setUTCSeconds(secs);
|
||||
|
||||
const hours = date.getUTCHours();
|
||||
const minutes = date.getUTCMinutes();
|
||||
const seconds = date.getUTCSeconds();
|
||||
|
||||
let duration = "";
|
||||
|
||||
if (hours > 0) {
|
||||
duration += `${hours.toString().padStart(2, "0")}:`;
|
||||
}
|
||||
|
||||
duration += `${minutes.toString().padStart(2, "0")}:`;
|
||||
duration += `${seconds.toString().padStart(2, "0")}`;
|
||||
|
||||
return duration;
|
||||
};
|
||||
|
||||
const timestringToSecs = (time: string) => {
|
||||
try {
|
||||
return timestring(time);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
return luma >= 165 ? "000000" : "ffffff";
|
||||
};
|
||||
|
||||
// Truncate
|
||||
@@ -180,8 +159,7 @@ export {
|
||||
postDeadCrossQuote,
|
||||
password,
|
||||
bytesToSize,
|
||||
secsToDuration,
|
||||
timestringToSecs,
|
||||
contrastingColor,
|
||||
truncate,
|
||||
truncateFilename,
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { timestringToSecs } from "../../time";
|
||||
import z from "zod";
|
||||
|
||||
const zBlockPolicy = z.enum(
|
||||
@@ -15,10 +16,10 @@ const zLimit = z.coerce
|
||||
.int("Limit musí být celé číslo.")
|
||||
.positive("Limit musí být kladný.");
|
||||
|
||||
const zInterval = z.coerce
|
||||
.number("Interval musí být číslo.")
|
||||
.int("Interval musí být celé číslo.")
|
||||
.positive("Interval musí být kladný.");
|
||||
const zInterval = z
|
||||
.string()
|
||||
.transform((time) => timestringToSecs(time))
|
||||
.refine((time) => time !== null, "Interval musí být platný čas");
|
||||
|
||||
const zTheme = z
|
||||
.string()
|
||||
|
||||
@@ -175,6 +175,10 @@ footer {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.user-id {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.post-body {
|
||||
margin: 8px 16px;
|
||||
}
|
||||
|
||||
@@ -70,6 +70,10 @@ header {
|
||||
color: $trip;
|
||||
}
|
||||
|
||||
.user-id {
|
||||
border: 1px solid $box-border;
|
||||
}
|
||||
|
||||
.thumb-video {
|
||||
border: 2px solid $video-border;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user