187 lines
5.4 KiB
TypeScript
187 lines
5.4 KiB
TypeScript
import { gql } from '@apollo/client';
|
|
import { Avatar } from '@app/client/components/ui/avatar';
|
|
import { userFragment } from '@app/client/fragments';
|
|
import { toFormattedDate } from '@app/client/helpers';
|
|
import { useIdentity } from '@app/client/hooks/identity';
|
|
import { useToastMutation } from '@app/client/hooks/useToastMutation';
|
|
import { useUserOneQuery } from '@app/client/hooks/useUserOneQuery';
|
|
import {
|
|
Button,
|
|
Card,
|
|
Heading,
|
|
HStack,
|
|
Icon,
|
|
SkeletonCircle,
|
|
SkeletonText,
|
|
Stack,
|
|
Text,
|
|
Textarea
|
|
} from '@chakra-ui/react';
|
|
import { UserProfileUpdateMutation } from '@generated/types';
|
|
import { createFileRoute } from '@tanstack/react-router';
|
|
import { useState } from 'react';
|
|
|
|
export const Route = createFileRoute('/profile/$username')({
|
|
component: RouteComponent
|
|
});
|
|
|
|
function RouteComponent() {
|
|
const { username } = Route.useParams();
|
|
|
|
const { data } = useUserOneQuery({
|
|
input: {
|
|
username
|
|
}
|
|
});
|
|
|
|
if (data == null) return <UserOverviewSkeleton />;
|
|
|
|
return (
|
|
<Stack width="100%" justifyContent="center" alignItems="center" padding={3}>
|
|
<UserOverview user={{ ...data.userOne }} />
|
|
</Stack>
|
|
);
|
|
}
|
|
|
|
function UserOverview({
|
|
user
|
|
}: {
|
|
readonly user: {
|
|
readonly id: string;
|
|
readonly username: string;
|
|
readonly description?: string | null;
|
|
readonly createdAt: string;
|
|
};
|
|
}) {
|
|
const identity = useIdentity();
|
|
|
|
const { update: userUpdate } = useToastMutation<UserProfileUpdateMutation>({
|
|
mutation: userUpdateMutation,
|
|
successMsg: 'Profile updated successfully!'
|
|
});
|
|
|
|
const [isEditMode, setIsEditMode] = useState<boolean>(false);
|
|
const [description, setDescription] = useState<string>(user.description ?? '');
|
|
|
|
return (
|
|
<Stack width={{ base: '100%', md: '80%' }} maxWidth="840px" gap={3}>
|
|
<Stack direction="row" alignItems="start">
|
|
<Avatar size="2xl" fallback={user.username[0]} />
|
|
<Stack width="100%">
|
|
<Heading color="colorPalette.400" size="2xl" overflow="auto">
|
|
{user.username}
|
|
</Heading>
|
|
|
|
<Stack direction="row" justifyContent="space-between" alignItems="center">
|
|
<Stack direction="row">
|
|
<Text fontWeight="bold">joined:</Text>
|
|
<Text color="fg.muted">{toFormattedDate({ value: user.createdAt })}</Text>
|
|
</Stack>
|
|
{identity != null && isEditMode && (
|
|
<Stack>
|
|
<Stack direction="row" alignItems="center" justifyContent="end" gap={1}>
|
|
<Button
|
|
size="sm"
|
|
height={6}
|
|
bg="green.500"
|
|
padding={0}
|
|
onClick={async () => {
|
|
await userUpdate({
|
|
variables: {
|
|
input: {
|
|
id: user.id,
|
|
description
|
|
}
|
|
}
|
|
});
|
|
identity.refetch();
|
|
setIsEditMode(false);
|
|
}}
|
|
>
|
|
<Icon>
|
|
<i className="nf nf-fa-check" />
|
|
</Icon>
|
|
</Button>
|
|
<Button
|
|
size="sm"
|
|
height={6}
|
|
bg="red.500"
|
|
padding={0}
|
|
onClick={async () => {
|
|
setDescription(user.description ?? '');
|
|
setIsEditMode(false);
|
|
}}
|
|
>
|
|
<Icon>
|
|
<i className="nf nf-fa-close" />
|
|
</Icon>
|
|
</Button>
|
|
</Stack>
|
|
</Stack>
|
|
)}
|
|
|
|
{!isEditMode && identity?.data.id === user.id && (
|
|
<Button
|
|
size="sm"
|
|
height={6}
|
|
padding={0}
|
|
onClick={() => {
|
|
setIsEditMode(true);
|
|
}}
|
|
>
|
|
<Icon>
|
|
<i className="nf nf-fa-edit" />
|
|
</Icon>
|
|
</Button>
|
|
)}
|
|
</Stack>
|
|
</Stack>
|
|
</Stack>
|
|
{isEditMode ? (
|
|
<Stack width="100%" borderRadius="md" position="relative">
|
|
<Textarea
|
|
variant="subtle"
|
|
placeholder="Tell us about yourself!"
|
|
resize="none"
|
|
maxLength={700}
|
|
maxLines={10}
|
|
fontSize="md"
|
|
autoresize
|
|
value={description}
|
|
onChange={event => setDescription(event.target.value)}
|
|
/>
|
|
</Stack>
|
|
) : (
|
|
<Card.Root variant="subtle" width="100%">
|
|
<Card.Body paddingX="3" paddingY="1">
|
|
<Text>{user.description ?? 'No user description =('}</Text>
|
|
</Card.Body>
|
|
</Card.Root>
|
|
)}
|
|
</Stack>
|
|
);
|
|
}
|
|
|
|
function UserOverviewSkeleton() {
|
|
return (
|
|
<Stack width="100%" justifyContent="center" alignItems="center" padding={3}>
|
|
<Stack width={{ base: '100%', md: '80%' }} maxWidth="840px">
|
|
<SkeletonCircle width={70} height={70} />
|
|
<Stack gap="6" maxWidth="100%">
|
|
<HStack width="full">
|
|
<SkeletonText noOfLines={5} />
|
|
</HStack>
|
|
</Stack>
|
|
</Stack>
|
|
</Stack>
|
|
);
|
|
}
|
|
|
|
const userUpdateMutation = gql`
|
|
mutation UserProfileUpdate($input: UserUpdateInput!) {
|
|
userUpdate(input: $input) {
|
|
...User
|
|
}
|
|
}
|
|
${userFragment}
|
|
`;
|