gear-orders/web/vite/src/Profile.tsx

161 lines
4.4 KiB
TypeScript
Raw Normal View History

2026-03-05 17:55:33 +00:00
import React from "react";
import { useUserContext } from "./UserContext";
import {
Avatar,
Box,
Button,
2026-03-05 19:46:44 +00:00
Checkbox,
Fieldset,
2026-03-05 17:55:33 +00:00
Flex,
Modal,
Paper,
TextInput,
Title,
} from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
2026-03-05 19:46:44 +00:00
import { Link, useLoaderData } from "react-router";
import { useForm } from "@mantine/form";
import { notifications } from "@mantine/notifications";
import { fetchHeaders } from "./fetch";
const RE_MASTODON_ACCOUNTS = /^(@(\w+)(@([\w\.]+))? )*(@\w+)(@[\w\.]+)?$/;
2026-03-05 17:55:33 +00:00
export const AuthorizeMastodonModal: React.FC<{
opened: boolean;
onClose: () => void;
}> = ({ opened, onClose }) => {
const [serverName, setServerName] = React.useState("rubber.social");
const [loading, setLoading] = React.useState(false);
const handleClick = React.useCallback(() => {
setLoading(true);
fetch(`/api/mastodon_oauth?server=${encodeURI(serverName)}`)
.then((response) => response.json())
.then((json) => {
if (json["url"]) {
window.location.href = json["url"];
}
});
}, []);
return (
<Modal opened={opened} onClose={onClose} title="Authorize with Mastodon">
<TextInput
label="Server Domain"
value={serverName}
onChange={(event) => setServerName(event.currentTarget.value)}
/>
<Button loading={loading} onClick={handleClick}>
Authorize
</Button>
</Modal>
);
};
2026-03-05 19:46:44 +00:00
type MastodonPreferencesForm = {
attn_list?: string;
post_public?: boolean;
};
export const profileLoader = async () =>
fetch(`/api/me`).then((response) => response.json());
2026-03-05 17:55:33 +00:00
export const Profile: React.FC = () => {
const { username, telegram_photo_url, mastodon_server, mastodon_username } =
useUserContext();
2026-03-05 19:46:44 +00:00
const { mastodon_attn_list, mastodon_post_public } = useLoaderData<{
mastodon_attn_list?: string;
mastodon_post_public?: boolean;
}>();
2026-03-05 17:55:33 +00:00
const [opened, { open, close }] = useDisclosure(false);
const mastodon_account = React.useMemo(
2026-03-07 17:00:49 +00:00
() =>
mastodon_username && mastodon_server
? `@${mastodon_username}@${mastodon_server}`
: null,
2026-03-05 17:55:33 +00:00
[mastodon_server, mastodon_username],
);
2026-03-05 19:46:44 +00:00
const [loading, setLoading] = React.useState(false);
const form = useForm<MastodonPreferencesForm>({
mode: "uncontrolled",
initialValues: {
attn_list: mastodon_attn_list,
post_public: mastodon_post_public,
},
validate: {
attn_list: (value: string) =>
!value || RE_MASTODON_ACCOUNTS.test(value)
? null
: "Please enter a valid list of accounts",
},
});
const handleSubmit = React.useCallback(
form.onSubmit((values) => {
setLoading(true);
fetch(`/api/profile`, {
method: "POST",
headers: fetchHeaders(),
body: JSON.stringify(values),
})
.then((response) => {
if (response.ok) {
notifications.show({
title: "Success",
message: "Your preferences have been saved",
color: "green",
});
} else {
notifications.show({
title: "Error",
message: "There was a problem saving your preferences",
color: "red",
});
}
})
.finally(() => {
setLoading(false);
});
}),
[],
);
2026-03-05 17:55:33 +00:00
return (
<>
<Flex align="center" gap="md" mb="xl">
<Avatar src={telegram_photo_url} size="150" />
<Title>{username}</Title>
</Flex>
<Box mb="md">
<Link to={`/dashboard`}>Return to dashboard</Link>
</Box>
<Paper bg="gray.1">
<Title order={4}>Mastodon</Title>
<TextInput label="Account" w="50%" value={mastodon_account} />
<Button onClick={open}>Authorize with Mastodon</Button>
<AuthorizeMastodonModal opened={opened} onClose={close} />
2026-03-05 19:46:44 +00:00
<Fieldset legend="Preferences" my="lg">
<form onSubmit={handleSubmit}>
<Checkbox
{...form.getInputProps("post_public", { type: "checkbox" })}
label="Post orders publically"
mb="md"
/>
<TextInput
{...form.getInputProps("attn_list")}
label="Tag Accounts"
w="50%"
description="Additional accounts to tag with your orders"
/>
<Button type="submit" loading={loading} mt="md">
Save
</Button>
</form>
</Fieldset>
2026-03-05 17:55:33 +00:00
</Paper>
</>
);
};