-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #76 from JimTheCat/CU-86971g2py_Create-design-for-…
…Post-card_Patryk-Kosiski feature: Create post design
- Loading branch information
Showing
6 changed files
with
247 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './Post'; |
26 changes: 26 additions & 0 deletions
26
frontend/src/Components/ImageWithSkeleton/ImageWithSkeleton.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"} | ||
/> | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export type PostDTO = { | ||
content: string; | ||
ownerLogin: string; | ||
createdAt: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |