Add live account appeal updates and restricted status settings

This commit is contained in:
2026-10-13 14:22:12 +05:00
parent cfe5820802
commit 8c3de302e7
21 changed files with 508 additions and 52 deletions

View File

@@ -8,6 +8,7 @@ import type {
AccountAppealRecord,
} from "@/shared/account-controls";
import styles from "@/app/ban/appeal/page.module.css";
import { useAppealRealtime } from "./useAppealRealtime";
export function AccountControls({
administrator,
@@ -28,6 +29,7 @@ export function AccountControls({
const [message, setMessage] = useState("");
const [busy, setBusy] = useState(false);
const lock = useRef(false);
const appealReads = useRef(0);
const [selected, setSelected] = useState<{
account: ManagedAccount;
action: "deactivate" | "ban" | "restore" | "delete";
@@ -37,19 +39,38 @@ export function AccountControls({
const [reviewReasons, setReviewReasons] = useState<Record<string, string>>(
{},
);
const realtime = useAppealRealtime({
enabled: showAppeals,
refresh: async () => {
const version = ++appealReads.current;
const result = await api<{ appeals: AccountAppealRecord[] }>(
"moderation/account-appeals",
);
if (version === appealReads.current) {
setAppeals(result.appeals);
setLoading(false);
}
},
onError: (error) => {
setError((error as Error).message);
setLoading(false);
},
});
useEffect(() => {
if (!showAppeals) return;
let active = true;
const version = ++appealReads.current;
api<{ appeals: AccountAppealRecord[] }>("moderation/account-appeals")
.then((data) => {
if (active) setAppeals(data.appeals);
if (active && version === appealReads.current) setAppeals(data.appeals);
})
.catch((e) => {
if (active) setError((e as Error).message);
if (active && version === appealReads.current)
setError((e as Error).message);
})
.finally(() => {
if (active) setLoading(false);
if (active && version === appealReads.current) setLoading(false);
});
return () => {
active = false;
@@ -81,16 +102,14 @@ export function AccountControls({
async function refreshAppeals() {
if (!showAppeals) return;
setLoading(true);
const version = ++appealReads.current;
try {
setAppeals(
(
await api<{ appeals: AccountAppealRecord[] }>(
"moderation/account-appeals",
)
).appeals,
const result = await api<{ appeals: AccountAppealRecord[] }>(
"moderation/account-appeals",
);
if (version === appealReads.current) setAppeals(result.appeals);
} finally {
setLoading(false);
if (version === appealReads.current) setLoading(false);
}
}
async function save(event: React.FormEvent) {
@@ -320,14 +339,29 @@ export function AccountControls({
"Модераторы и администраторы могут рассматривать апелляции. Одобрение восстанавливает доступ после этой блокировки. Деактивация и удаление доступны только администраторам.",
)}
</p>
<button
className="button"
disabled={busy || loading}
onClick={() => void run(refreshAppeals)}
>
<RefreshCw size={16} aria-hidden="true" />
{l("Refresh appeals", "Обновить апелляции")}
</button>
<div className={styles.refreshActions}>
<button
className="button"
disabled={busy || loading}
onClick={() => void run(refreshAppeals)}
>
<RefreshCw size={16} aria-hidden="true" />
{l("Refresh appeals", "Обновить апелляции")}
</button>
<span className="muted">
{realtime === "live"
? l(
"Live updates connected",
"Обновления в реальном времени подключены",
)
: realtime === "offline"
? l(
"Reconnecting… Refresh remains available.",
"Переподключение… Можно обновить вручную.",
)
: l("Connecting live updates…", "Подключение обновлений…")}
</span>
</div>
{loading && (
<p role="status">{l("Loading appeals…", "Загрузка апелляций…")}</p>
)}