"use client";
import { useState } from "react";
import { Button } from "../../ui/button";
import { useToast } from "../../ui/use-toast";
import type { Account } from "@/lib/db";
import { User } from "revolt-api";
import {
cancelAccountDeletion,
changeAccountEmail,
deleteMFARecoveryCodes,
disableAccount,
disableMFA,
queueAccountDeletion,
restoreAccount,
verifyAccountEmail,
} from "@/lib/actions";
import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "../../ui/alert-dialog";
import { AlertDialogDescription } from "@radix-ui/react-alert-dialog";
import { Input } from "@/components/ui/input";
dayjs.extend(relativeTime);
export function AccountActions({
account,
user,
}: {
account: Account;
user?: User;
}) {
const { toast } = useToast();
const [accountDraft, setAccountDraft] = useState(account);
const [emailDraft, setEmailDraft] = useState("");
return (
Update account email
setEmailDraft(e.currentTarget.value)}
value={emailDraft}
/>
Cancel
{
try {
await changeAccountEmail(account._id, emailDraft);
setEmailDraft("");
toast({ title: "Updated email" });
window.location.reload();
} catch (err) {
toast({
title: "Failed to execute action",
description: String(err),
variant: "destructive",
});
}
}}
>
Change
Mark Email as verified
Verification status is currently {accountDraft.verification.status}.
Cancel
{
try {
await verifyAccountEmail(account._id);
toast({ title: "Verified email" });
setAccountDraft({ ...accountDraft, verification: { status: "Verified" } });
} catch(e) {
toast({
title: "Failed to verify",
description: String(e),
variant: "destructive",
})
}
}}
>
Mark verified
{
navigator.clipboard.writeText(`https://app.revolt.chat/login/verify/${(accountDraft.verification as any).token}`);
toast({ title: "Copied verification link" })
}}
>
Copy link
Manage MFA
MFA is currently {
accountDraft.mfa?.totp_token?.status == "Pending"
? "pending setup"
: (accountDraft.mfa?.totp_token?.status.toLowerCase() || "disabled")
}.
The account has {accountDraft.mfa?.recovery_codes ?? "no"} recovery codes.
{
try {
await deleteMFARecoveryCodes(account._id);
toast({
title: "MFA recovery codes cleared",
});
accountDraft.mfa!.recovery_codes = undefined;
} catch(e) {
toast({
title: "Failed to clear recovery codes",
description: String(e),
variant: "destructive",
})
}
}}
>
Clear recovery codes
{
try {
await disableMFA(account._id);
toast({
title: "MFA disabled",
});
accountDraft.mfa!.totp_token = undefined;
} catch(e) {
toast({
title: " Failed to disable MFA",
description: String(e),
variant: "destructive",
})
}
}}
>
Disable MFA
Close
Are you sure you want to{" "}
{accountDraft.disabled ? "restore" : "disable"} this account?
Cancel
{
try {
if (accountDraft.disabled) {
await restoreAccount(account._id);
setAccountDraft((account) => ({
...account!,
disabled: false,
}));
toast({
title: "Restored account",
});
} else {
await disableAccount(account._id);
setAccountDraft((account) => ({
...account!,
disabled: true,
}));
toast({
title: "Disabled account",
});
}
} catch (err) {
toast({
title: "Failed to execute action",
description: String(err),
variant: "destructive",
});
}
}}
>
{accountDraft.disabled ? "Restore" : "Disable"}
Are you sure you want to{" "}
{accountDraft.deletion?.status === "Scheduled"
? "cancel deletion of"
: "queue deletion for"}{" "}
this account?
Cancel
{
try {
if (accountDraft.deletion?.status === "Scheduled") {
await cancelAccountDeletion(account._id);
setAccountDraft((account) => ({
...account,
deletion: null,
}));
toast({
title: "Cancelled account deletion",
});
} else {
const $set = await queueAccountDeletion(account._id);
setAccountDraft((account) => ({ ...account!, ...$set }));
toast({
title: "Queued account for deletion",
});
}
} catch (err) {
toast({
title: "Failed to execute action",
description: String(err),
variant: "destructive",
});
}
}}
>
{accountDraft.deletion?.status === "Scheduled"
? "Unqueue"
: "Queue"}
);
}