Skip to content

Commit

Permalink
Merge pull request #76 from JimTheCat/CU-86971g2py_Create-design-for-…
Browse files Browse the repository at this point in the history
…Post-card_Patryk-Kosiski

feature: Create post design
  • Loading branch information
JimTheCat authored Dec 14, 2024
2 parents ec3d26c + a5a927e commit 54c5288
Show file tree
Hide file tree
Showing 6 changed files with 247 additions and 0 deletions.
182 changes: 182 additions & 0 deletions frontend/src/Components/Cards/Post/Post.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import {
ActionIcon,
Avatar,
BackgroundImage,
Box,
Button,
Card,
Divider,
Group,
SimpleGrid,
Stack,
Text
} from "@mantine/core";
import {User} from "../../../Services/DTOs/User.tsx";
import {InnerHtmlHandler} from "../../InnerHtmlHandler";
import {DateFormatter} from "../../../Services/Utils/DateFormatter.tsx";
import {IconDots, IconMessage, IconPaw, IconShare3} from "@tabler/icons-react";
import {useAuthStore} from "../../../Services/authStore.ts";
import {useNavigate} from "react-router-dom";
import {ImageWithSkeleton} from "../../ImageWithSkeleton/ImageWithSkeleton.tsx";

type PostProps = {
userId: string;
contentHtml: string;
photosUrls?: string[];
createdAt: string;
}

export const Post = (props: PostProps) => {

const auth = useAuthStore();
const isOwner = auth.user?.login === props.userId;
const navigate = useNavigate();

/*Render this element each time when number of photos will change*/
const renderPhotos = (urls: string[]) => {
const uniqueKey = `photo-grid-${urls.join("-")}`;
const maxHeight = '30vh';

if (urls.length === 1) {
return <ImageWithSkeleton src={urls[0]} alt="" radius="md" key={uniqueKey} style={{maxHeight: maxHeight}}/>;
}

if (urls.length === 2 || urls.length === 4) {
return (
<SimpleGrid cols={2} spacing="xs" key={uniqueKey}>
{urls.map((url) => (
<ImageWithSkeleton src={url} alt="" radius="md" key={url} style={{maxHeight: maxHeight}}/>
))}
</SimpleGrid>
);
}

if (urls.length === 3) {
const spacing = 8;
return (
<Group wrap="nowrap" align="stretch" style={{width: '100%', height: '300px'}}>
<ImageWithSkeleton
src={urls[0]}
alt=""
radius="md"
style={{
flex: 1,
height: `calc(100% + ${spacing / 2}px)`,
objectFit: 'cover',
}}
/>

<Stack gap={spacing + 4} style={{flex: 1, height: '100%'}}>
<ImageWithSkeleton
src={urls[1]}
alt=""
radius="md"
style={{height: `calc(50% - ${spacing / 2}px)`, objectFit: 'cover'}}
/>
<ImageWithSkeleton
src={urls[2]}
alt=""
radius="md"
style={{height: `calc(50% - ${spacing / 2}px)`, objectFit: 'cover'}}
/>
</Stack>
</Group>
);
}


return (
<SimpleGrid cols={2} spacing="xs" key={uniqueKey}>
{urls.slice(0, 3).map((url, idx) => (
<ImageWithSkeleton src={url} alt="" radius="md" key={`${url}-${idx}`} style={{maxHeight: maxHeight}}/>
))}
<BackgroundImage
src={urls[3]}
radius="md"
style={{
position: "relative",
display: "flex",
alignItems: "center",
justifyContent: "center",
maxHeight: maxHeight
}}
>
<Text
size="xl"
w={700}
c="white"
ta="center"
style={{
zIndex: 1,
textShadow: "0 0 5px rgba(0, 0, 0, 0.5)",
}}
>
+{urls.length - 4}
</Text>
<Box
style={{
position: "absolute",
top: 0,
left: 0,
width: "100%",
height: "100%",
backgroundColor: "rgba(0, 0, 0, 0.4)",
borderRadius: "inherit",
}}
/>
</BackgroundImage>
</SimpleGrid>
);
};

const user = {
name: "John",
surname: "Doe",
login: "johndoe",
} as User;

return (
<Card w={"30vw"} radius={"md"} p={"lg"} my={'lg'}>
<Stack>
<Group justify="space-between">
<Group onClick={() => navigate(`/profile/${auth.user?.tag}`)} style={{cursor: "pointer"}}>
<Avatar src={null} size={"lg"} radius={180}/>
<Stack gap={0}>
<Text>{user.name} {user.surname}</Text>
<Text c="dimmed">{DateFormatter(props.createdAt)}</Text>
</Stack>
</Group>
{isOwner &&
<ActionIcon size="lg" color="gray" radius={"xl"} variant={"subtle"} onClick={() => {
}}>
<IconDots stroke={1.5}/>
</ActionIcon>
}
</Group>

<InnerHtmlHandler innerHtml={props.contentHtml}/>

{/*// Show photos if they exist*/}
{props.photosUrls && props.photosUrls.length > 0 && (
<Card.Section mt="sm">
{renderPhotos(props.photosUrls)}
</Card.Section>
)}

<Divider my={"xs"}/>

<Group grow preventGrowOverflow={false}>
<Button variant={"subtle"} color={"gray"} leftSection={<IconPaw stroke={1.5}/>}>
Reaction
</Button>
<Button variant={"subtle"} color={"gray"} leftSection={<IconMessage stroke={1.5}/>}>
Comment
</Button>
<Button variant={"subtle"} color={"gray"} leftSection={<IconShare3 stroke={1.5}/>}>
Share
</Button>
</Group>
</Stack>
</Card>
);
}
1 change: 1 addition & 0 deletions frontend/src/Components/Cards/Post/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './Post';
26 changes: 26 additions & 0 deletions frontend/src/Components/ImageWithSkeleton/ImageWithSkeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {Image, MantineRadius, MantineStyleProp, Skeleton} from "@mantine/core";
import {useState} from "react";

export const ImageWithSkeleton = (props: {
src: string;
alt: string,
radius: number | MantineRadius,
style?: MantineStyleProp
}) => {

const [isImageLoaded, setIsImageLoaded] = useState(false);

return (
<>
{!isImageLoaded && <Skeleton height={400} radius={props.radius}/>}
<Image
src={props.src}
alt={props.alt}
radius={props.radius}
onLoad={() => setIsImageLoaded(true)}
style={props.style}
display={isImageLoaded ? "block" : "none"}
/>
</>
);
}
25 changes: 25 additions & 0 deletions frontend/src/Pages/MainPage/MainPage.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,34 @@
import {Box, Title} from "@mantine/core";
import {useEffect, useState} from "react";
import api from "../../Services/api.ts";
import {Post} from "../../Components/Cards/Post";
import {PostDTO} from "../../Services/DTOs/Post.tsx";

export const MainPage = () => {

const [posts, setPosts] = useState<PostDTO[] | []>([]);

useEffect(() => {
api.get<PostDTO[]>("/api/posts/get-all").then((response) => {
setPosts(response.data);
});
}, []);

return (
<Box p={"md"}>
<Title order={1}>Main page</Title>

{/*Posts*/}
{posts.map((post) => (
<Post key={post.ownerLogin} userId={post.ownerLogin} contentHtml={post.content} createdAt={post.createdAt}
photosUrls={
// generate 100 random photos
Array.from({length: 100}, () => {
return "https://picsum.photos/seed/" + Math.random() + "/800/2200";
})
}
/>
))}
</Box>
)
}
5 changes: 5 additions & 0 deletions frontend/src/Services/DTOs/Post.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type PostDTO = {
content: string;
ownerLogin: string;
createdAt: string;
}
8 changes: 8 additions & 0 deletions frontend/src/Services/DTOs/User.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export type User = {
name?: string;
surname?: string;
login?: string;
email?: string;
birthDate?: string;
gender?: string;
}

0 comments on commit 54c5288

Please sign in to comment.