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";
|
2026-03-14 21:17:41 +00:00
|
|
|
import { ProfileVerification } from "./ProfileVerification";
|
2026-03-05 19:46:44 +00:00
|
|
|
|
|
|
|
|
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-14 21:17:41 +00:00
|
|
|
const {
|
|
|
|
|
has_doms,
|
|
|
|
|
mastodon_attn_list,
|
|
|
|
|
mastodon_post_public,
|
2026-03-30 21:18:30 +00:00
|
|
|
verify_mastodon_alt_text,
|
2026-03-14 21:17:41 +00:00
|
|
|
verify_mastodon_favorite,
|
|
|
|
|
verify_delay,
|
|
|
|
|
} = useLoaderData<UserProfile>();
|
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>
|
2026-03-14 21:17:41 +00:00
|
|
|
{!has_doms ? (
|
|
|
|
|
<ProfileVerification
|
|
|
|
|
username={username}
|
2026-03-30 21:18:30 +00:00
|
|
|
verify_mastodon_alt_text={verify_mastodon_alt_text}
|
2026-03-14 21:17:41 +00:00
|
|
|
verify_mastodon_favorite={verify_mastodon_favorite}
|
|
|
|
|
verify_delay={verify_delay}
|
|
|
|
|
/>
|
|
|
|
|
) : null}
|
2026-03-05 17:55:33 +00:00
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
};
|