Post card improvement
This commit is contained in:
parent
d1474bce35
commit
1659e007e4
@ -17,6 +17,7 @@ export type Post = {
|
||||
post_id: string;
|
||||
title: string;
|
||||
user_id: string;
|
||||
content: string;
|
||||
};
|
||||
|
||||
export type SelectedPostsResponse = {
|
||||
|
||||
@ -8,8 +8,8 @@ export default function LoginButton() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Link to={"/login"}>
|
||||
<Flex className="justify-between gap-2">
|
||||
<Link to={"/login"} className="size-full">
|
||||
<Flex className="items-center justify-start h-full gap-2">
|
||||
<Icon>
|
||||
<EnterIcon />
|
||||
</Icon>
|
||||
|
||||
@ -36,7 +36,7 @@ export default function LogoutButton() {
|
||||
|
||||
return (
|
||||
<Flex
|
||||
className="justify-between gap-2"
|
||||
className="items-center justify-start gap-2 cursor-pointer size-full"
|
||||
onClick={() => logoutMutation.mutate()}
|
||||
>
|
||||
<Icon>
|
||||
|
||||
@ -50,7 +50,7 @@ export default function UserCard(props: TUserCard) {
|
||||
};
|
||||
|
||||
const getUsername = (): string => {
|
||||
if (!user || props.username) return props.username || "Nothing";
|
||||
if (!user || props.username) return props.username || "";
|
||||
return user.username;
|
||||
};
|
||||
|
||||
@ -76,7 +76,7 @@ export default function UserCard(props: TUserCard) {
|
||||
)}
|
||||
|
||||
<Text size={"1"} color={"gray"}>
|
||||
{`@${getUsername()}`}
|
||||
{`@${data?.user_info.username}`}
|
||||
</Text>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
@ -1,33 +1,60 @@
|
||||
import { Skeleton, Text } from "@radix-ui/themes";
|
||||
import { HoverCard, Link, Skeleton, Text } from "@radix-ui/themes";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Link } from "react-router-dom";
|
||||
import { lazy, Suspense } from "react";
|
||||
import { axiosLocalhost } from "../../api/axios/axios";
|
||||
|
||||
type TUserNicknameLink = {
|
||||
userId: string;
|
||||
userId: string;
|
||||
};
|
||||
|
||||
const UserCard = lazy(() => import("../UserCard/UserCard"));
|
||||
|
||||
export default function UserNicknameLink(props: TUserNicknameLink) {
|
||||
const { data, isPending } = useQuery({
|
||||
queryKey: [`userLink${props.userId}`],
|
||||
queryFn: async () => {
|
||||
const response = await axiosLocalhost.get(
|
||||
`/user/${props.userId || 0}`
|
||||
);
|
||||
return response.data as string;
|
||||
},
|
||||
});
|
||||
const { data, isPending } = useQuery({
|
||||
queryKey: [`userLink${props.userId}`],
|
||||
queryFn: async () => {
|
||||
const response = await axiosLocalhost.get(
|
||||
`/user/${props.userId || 0}`
|
||||
);
|
||||
return response.data as string;
|
||||
},
|
||||
});
|
||||
|
||||
if (isPending)
|
||||
return (
|
||||
<Skeleton>
|
||||
<Text>@Nickname</Text>
|
||||
</Skeleton>
|
||||
);
|
||||
if (isPending)
|
||||
return (
|
||||
<Skeleton>
|
||||
<Text
|
||||
size={{
|
||||
sm: "4",
|
||||
md: "5",
|
||||
lg: "6",
|
||||
}}
|
||||
>
|
||||
@Nickname
|
||||
</Text>
|
||||
</Skeleton>
|
||||
);
|
||||
|
||||
return (
|
||||
<Link to={`/users/${data}`}>
|
||||
<Text>@{data}</Text>
|
||||
</Link>
|
||||
);
|
||||
return (
|
||||
<HoverCard.Root>
|
||||
<HoverCard.Trigger>
|
||||
<Link href={`/users/${data}`}>
|
||||
<Text
|
||||
size={{
|
||||
sm: "3",
|
||||
md: "4",
|
||||
lg: "5",
|
||||
}}
|
||||
>
|
||||
@{data}
|
||||
</Text>
|
||||
</Link>
|
||||
</HoverCard.Trigger>
|
||||
<HoverCard.Content className="p-0" maxWidth={'220px'}>
|
||||
<Suspense fallback={<Skeleton />}>
|
||||
<UserCard userId={props.userId} />
|
||||
</Suspense>
|
||||
</HoverCard.Content>
|
||||
</HoverCard.Root>
|
||||
);
|
||||
}
|
||||
|
||||
@ -0,0 +1,50 @@
|
||||
import { Inset } from "@radix-ui/themes";
|
||||
import { useMemo } from "react";
|
||||
|
||||
type TInsetImageProps = {
|
||||
isHovered: boolean;
|
||||
ref_: React.RefObject<HTMLDivElement>;
|
||||
windowWidth: number;
|
||||
};
|
||||
|
||||
export default function InsetImage(props: TInsetImageProps) {
|
||||
const seed = useMemo(() => {
|
||||
return Math.floor(Math.random() * (1 + Math.random()) * 100000);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Inset
|
||||
side={"left"}
|
||||
clip={"padding-box"}
|
||||
className={`max-w-[${
|
||||
props.isHovered ? "100%" : "225px"
|
||||
}] transition-[flex] duration-[250ms]
|
||||
${props.isHovered ? "flex-1" : "flex-[0.5]"}
|
||||
relative overflow-hidden h-72`}
|
||||
>
|
||||
<img
|
||||
style={{
|
||||
minWidth: `${
|
||||
Math.min(
|
||||
props.windowWidth,
|
||||
props.ref_.current?.clientWidth || 0
|
||||
) / 2
|
||||
}px`,
|
||||
transform: `${
|
||||
props.isHovered
|
||||
? "translateX(0)"
|
||||
: `translateX(calc(-50% + ${
|
||||
Math.min(
|
||||
props.windowWidth,
|
||||
props.ref_.current?.clientWidth || 0
|
||||
) / 6
|
||||
}px))`
|
||||
}`,
|
||||
}}
|
||||
className={`h-72 transition-all duration-[250ms]`}
|
||||
src={`https://picsum.photos/seed/${seed}/1000/600?grayscale`}
|
||||
alt="Bold typography"
|
||||
/>
|
||||
</Inset>
|
||||
);
|
||||
}
|
||||
@ -1,24 +1,20 @@
|
||||
import { CalendarIcon } from "@radix-ui/react-icons";
|
||||
import {
|
||||
Card,
|
||||
Flex,
|
||||
Heading,
|
||||
Inset,
|
||||
Text,
|
||||
Tooltip
|
||||
} from "@radix-ui/themes";
|
||||
import { Box, Card, Flex, Heading, Text, Tooltip } from "@radix-ui/themes";
|
||||
import dayjs from "dayjs";
|
||||
import "dayjs/locale/ru";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { Interweave } from "interweave";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Post } from "../../../@types/PostTypes";
|
||||
import UserNicknameLink from "../../../Components/UserNicknameLink/UserNicknameLink";
|
||||
import InsetImage from "./InsetImage/InsetImage";
|
||||
|
||||
|
||||
export default function PostCard(props: Post) {
|
||||
const navigate = useNavigate();
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
const [windowWidth, setWindowWidth] = useState<number>(window.innerWidth);
|
||||
const [isImageLoaded, setIsImageLoaded] = useState(false);
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
const parsedDate = dayjs(props.created_at)
|
||||
@ -29,79 +25,95 @@ export default function PostCard(props: Post) {
|
||||
navigate(`/posts/${props.post_id.toString()}`);
|
||||
};
|
||||
|
||||
const seed = useMemo(() => {
|
||||
return Math.floor(Math.random() * (1 + Math.random()) * 100000);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const f = () => {
|
||||
setWindowWidth(window.innerWidth);
|
||||
console.log(`Window width: ${window.innerWidth} px`);
|
||||
};
|
||||
|
||||
}
|
||||
f();
|
||||
|
||||
f()
|
||||
|
||||
window.addEventListener('resize', f);
|
||||
return () => window.removeEventListener('resize', f);
|
||||
window.addEventListener("resize", f);
|
||||
return () => window.removeEventListener("resize", f);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Card
|
||||
ref={ref}
|
||||
className="flex w-full cursor-pointer"
|
||||
className="flex w-full cursor-pointer max-h-72"
|
||||
onClick={clickHandler}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
>
|
||||
<Inset
|
||||
side={"left"}
|
||||
clip={"padding-box"}
|
||||
className={`max-w-[${
|
||||
isHovered ? "100%" : "225px"
|
||||
}] transition-all duration-[250ms]
|
||||
${
|
||||
isHovered ? "flex-1" : "flex-[0.5]"
|
||||
}
|
||||
relative overflow-hidden h-72`}
|
||||
>
|
||||
<img
|
||||
style={{
|
||||
minWidth: `${Math.min(windowWidth, ref.current?.clientWidth || 0) / 2}px`,
|
||||
transform: `${
|
||||
isHovered ? "translateX(0)" : `translateX(calc(-50% + ${Math.min(windowWidth, ref.current?.clientWidth || 0) / 6}px))`
|
||||
}`
|
||||
}}
|
||||
className={`h-72 transition-all duration-[250ms]`}
|
||||
src={`https://picsum.photos/seed/${seed}/1000/600?grayscale`}
|
||||
alt="Bold typography"
|
||||
onLoadedData={() => setIsImageLoaded(true)}
|
||||
/>
|
||||
</Inset>
|
||||
<InsetImage
|
||||
isHovered={isHovered}
|
||||
ref_={ref}
|
||||
windowWidth={windowWidth}
|
||||
/>
|
||||
|
||||
<Flex
|
||||
direction={"column"}
|
||||
className="justify-between flex-1 w-full px-4 pt-2"
|
||||
className="justify-between flex-1 w-full gap-4 px-4 pt-2"
|
||||
>
|
||||
<Heading>{props.title}</Heading>
|
||||
<Heading
|
||||
size={{
|
||||
sm: "4",
|
||||
md: "5",
|
||||
lg: "6",
|
||||
}}
|
||||
className="flex items-center h-fit"
|
||||
>
|
||||
{props.title}
|
||||
</Heading>
|
||||
|
||||
<Flex gap={"2"} className="items-end justify-start flex-1">
|
||||
<Tooltip content={`Written at`}>
|
||||
<Flex className="items-center gap-2">
|
||||
<CalendarIcon className="size-6" />
|
||||
<Flex
|
||||
direction={"column"}
|
||||
justify={"between"}
|
||||
className="h-full overflow-hidden"
|
||||
>
|
||||
<Box className="overflow-y-hidden">
|
||||
<Text
|
||||
size={{
|
||||
sm: "4",
|
||||
md: "5",
|
||||
lg: "6",
|
||||
}}
|
||||
>
|
||||
<Interweave content={props.content} />
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Flex className="gap-2 lg:gap-4">
|
||||
<Tooltip content={`Written at`}>
|
||||
<Flex className="items-center gap-2 h-fit">
|
||||
<CalendarIcon className="size-6" />
|
||||
<Text
|
||||
size={{
|
||||
sm: "3",
|
||||
md: "4",
|
||||
lg: "5",
|
||||
}}
|
||||
weight={"medium"}
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
{`${parsedDate}`}
|
||||
</Text>
|
||||
</Flex>
|
||||
</Tooltip>
|
||||
|
||||
<Flex className="items-center gap-2 h-fit">
|
||||
<Text
|
||||
size={{
|
||||
sm: "4",
|
||||
md: "5",
|
||||
lg: "6",
|
||||
sm: "3",
|
||||
md: "4",
|
||||
lg: "5",
|
||||
}}
|
||||
weight={"medium"}
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
{`${parsedDate}`}
|
||||
Author:{" "}
|
||||
</Text>
|
||||
<UserNicknameLink userId={props.user_id} />
|
||||
</Flex>
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Card>
|
||||
|
||||
@ -43,7 +43,7 @@ export default function RandomPostsPage() {
|
||||
direction={"column"}
|
||||
className="w-full overflow-hidden sm:mx-auto max-w-pc-width "
|
||||
>
|
||||
<Flex direction={"column"} gap={"4"}>
|
||||
<Flex direction={"column"} gap={"4"} className="mx-4 xl:mx-0">
|
||||
{data?.pages.map((post, i) => {
|
||||
return (
|
||||
<Flex
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
}
|
||||
|
||||
.radix-themes {
|
||||
--default-font-family: "Pochaevsk", sans-serif;
|
||||
--default-font-family:
|
||||
--heading-font-family: "Edu AU VIC WA NT Pre", cursive;
|
||||
/* Your custom font for <Heading> components */
|
||||
--code-font-family:
|
||||
|
||||
@ -66,7 +66,7 @@ export default function MainPage() {
|
||||
<NavBar />
|
||||
<Box
|
||||
flexGrow={"1"}
|
||||
className="flex flex-col mx-4 overflow-hidden"
|
||||
className="flex flex-col overflow-hidden"
|
||||
>
|
||||
<Outlet />
|
||||
</Box>
|
||||
|
||||
@ -160,7 +160,7 @@ filtered_posts AS (
|
||||
json_build_object(
|
||||
'post_id', post_id::text, 'blog_id', blog_id::text,
|
||||
'user_id', user_id::text, 'title', title,
|
||||
'created_at', created_at
|
||||
'created_at', created_at, 'content', SUBSTRING(content FROM 1 FOR 300)
|
||||
)
|
||||
FROM
|
||||
public.posts
|
||||
|
||||
@ -49,7 +49,7 @@ filtered_posts AS (
|
||||
json_build_object(
|
||||
'post_id', post_id::text, 'blog_id', blog_id::text,
|
||||
'user_id', user_id::text, 'title', title,
|
||||
'created_at', created_at
|
||||
'created_at', created_at, 'content', SUBSTRING(content FROM 1 FOR 300)
|
||||
)
|
||||
FROM
|
||||
public.posts
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user