v3: added localization, use global player for uploaded audio/video files

This commit is contained in:
khannurien
2026-04-03 15:29:33 +00:00
parent 378b3ffa46
commit 0ce80398a4
64 changed files with 4248 additions and 941 deletions

View File

@@ -1,6 +1,8 @@
import { useEffect, useState } from "react";
import type { SubmitEvent } from "react";
import { Link, useNavigate, useSearchParams } from "react-router";
import { t } from "@lingui/core/macro"
import { Trans } from "@lingui/react/macro";
import { API_URL, VALIDATION } from "../config/api.ts";
import {
@@ -30,16 +32,19 @@ export function UserRegister() {
const [searchParams] = useSearchParams();
const token = searchParams.get("token") ?? "";
const [tokenState, setTokenState] = useState<TokenState>({
status: "checking",
});
const [tokenState, setTokenState] = useState<TokenState>(() =>
token ? { status: "checking" } : { status: "invalid" }
);
const [formState, setFormState] = useState<FormState>({ status: "idle" });
const [prevToken, setPrevToken] = useState(token);
if (prevToken !== token) {
setPrevToken(token);
setTokenState(token ? { status: "checking" } : { status: "invalid" });
}
useEffect(() => {
if (!token) {
setTokenState({ status: "invalid" });
return;
}
if (!token) return;
fetch(`${API_URL}/api/invites/${encodeURIComponent(token)}`)
.then((r) => {
setTokenState(r.ok ? { status: "valid" } : { status: "invalid" });
@@ -86,7 +91,7 @@ export function UserRegister() {
if (tokenState.status === "checking") {
return (
<PageShell centered>
<p className="page-loading">Checking invite</p>
<p className="page-loading"><Trans>Checking invite</Trans></p>
</PageShell>
);
}
@@ -96,8 +101,8 @@ export function UserRegister() {
<PageShell centered>
<div className="page-error-wrap">
<ErrorCard
title="Invalid invite"
message="This invite link is missing, expired, or already used."
title={t`Invalid invite`}
message={t`This invite link is missing, expired, or already used.`}
/>
</div>
</PageShell>
@@ -107,34 +112,34 @@ export function UserRegister() {
return (
<PageShell centered>
<div className="auth-card">
<h1 className="auth-card-title">Register</h1>
<h1 className="auth-card-title"><Trans>Register</Trans></h1>
{formState.status === "error" && (
<ErrorCard title="Registration failed" message={formState.error} />
<ErrorCard title={t`Registration failed`} message={formState.error} />
)}
<form onSubmit={handleSubmit} className="auth-form">
<input
name="username"
type="text"
placeholder="Username"
placeholder={t`Username`}
required
pattern={`[a-zA-Z0-9_]{${VALIDATION.USERNAME_MIN},${VALIDATION.USERNAME_MAX}}`}
title={`${VALIDATION.USERNAME_MIN}${VALIDATION.USERNAME_MAX} characters: letters, numbers, or underscores`}
title={t`${VALIDATION.USERNAME_MIN}${VALIDATION.USERNAME_MAX} characters: letters, numbers, or underscores`}
disabled={formState.status === "submitting"}
autoFocus
/>
<input
name="email"
type="email"
placeholder="Email address"
placeholder={t`Email address`}
required
disabled={formState.status === "submitting"}
/>
<input
name="password"
type="password"
placeholder={`Password (min. ${VALIDATION.PASSWORD_MIN} characters)`}
placeholder={t`Password (min. ${VALIDATION.PASSWORD_MIN} characters)`}
required
minLength={VALIDATION.PASSWORD_MIN}
maxLength={VALIDATION.PASSWORD_MAX}
@@ -145,12 +150,14 @@ export function UserRegister() {
className="btn-primary"
disabled={formState.status === "submitting"}
>
{formState.status === "submitting" ? "Registering…" : "Register"}
{formState.status === "submitting"
? <Trans>Registering</Trans>
: <Trans>Register</Trans>}
</button>
</form>
<p className="auth-card-footer">
Already have an account? <Link to="/login">Log in</Link>
<Trans>Already have an account? <Link to="/login">Log in</Link></Trans>
</p>
</div>
</PageShell>