diff --git a/frontend/src/components/listings/ListingGrid.tsx b/frontend/src/components/listings/ListingGrid.tsx index 7c0af9cb..dfab100e 100644 --- a/frontend/src/components/listings/ListingGrid.tsx +++ b/frontend/src/components/listings/ListingGrid.tsx @@ -1,4 +1,6 @@ import { useEffect, useState } from "react"; +import Masonry from "react-masonry-css"; +import { Link } from "react-router-dom"; import { paths } from "gen/api"; import { useAlertQueue } from "hooks/useAlertQueue"; @@ -44,20 +46,35 @@ const ListingGrid = (props: ListingGridProps) => { } }, [listingIds]); + const breakpointColumnsObj = { + default: 4, + 1536: 3, + 1280: 3, + 1024: 2, + 768: 2, + 640: 1, + }; + return listingIds === null ? (
) : ( -
+ {listingIds.map((listingId) => ( - l.id === listingId)} - /> + + l.id === listingId)} + showDescription={true} + /> + ))} -
+ ); }; diff --git a/frontend/src/components/listings/ListingGridCard.tsx b/frontend/src/components/listings/ListingGridCard.tsx index 94abba4f..01da63ff 100644 --- a/frontend/src/components/listings/ListingGridCard.tsx +++ b/frontend/src/components/listings/ListingGridCard.tsx @@ -1,94 +1,56 @@ -import { useState } from "react"; -import { FaEye } from "react-icons/fa"; -import { useNavigate } from "react-router-dom"; - -import clsx from "clsx"; import { paths } from "gen/api"; -import { formatNumber } from "utils/formatNumber"; -import { formatTimeSince } from "utils/formatTimeSince"; - -import ImagePlaceholder from "components/ImagePlaceholder"; -import ListingVoteButtons from "components/listing/ListingVoteButtons"; -import { Card, CardFooter, CardHeader, CardTitle } from "components/ui/Card"; type ListingInfo = paths["/listings/batch"]["get"]["responses"][200]["content"]["application/json"]["listings"][0]; -interface Props { +interface ListingGridCardProps { listingId: string; - listing: ListingInfo | undefined; + listing?: ListingInfo; + showDescription?: boolean; } -const ListingGridCard = ({ listingId, listing }: Props) => { - const navigate = useNavigate(); - const [hovering, setHovering] = useState(false); +const ListingGridCard = ({ + listing, + showDescription, +}: ListingGridCardProps) => { + const getFirstLine = (text: string | null) => { + if (!text) return null; + const firstLine = text.split("\n")[0].trim(); + return firstLine.length > 100 ? `${firstLine.slice(0, 97)}...` : firstLine; + }; return ( - setHovering(true)} - onMouseLeave={() => setHovering(false)} - onClick={() => navigate(`/item/${listingId}`)} - > - {/* Hover overlay */} -
- - {listing?.image_url ? ( -
- {listing.name} -
- ) : ( - - )} -
- - - {listing ? ( - listing.name - ) : ( -
- )} -
-
- - {listing && ( - <> -
- - {formatNumber(listing.views || 0)} -
-
- {formatTimeSince(new Date(listing.created_at * 1000))} -
- +
+ {listing ? ( + <> + {listing.image_url && ( +
+ {listing.name} +
)} - -
- {listing && ( -
- +
+

+ {listing.name} +

+ {showDescription && listing.description !== null && ( +

+ {getFirstLine(listing.description)} +

+ )} +
+ + ) : ( +
+
+
+
)} - +
); }; diff --git a/store/app/crud/users.py b/store/app/crud/users.py index 97147f9e..9d7e56ac 100644 --- a/store/app/crud/users.py +++ b/store/app/crud/users.py @@ -47,23 +47,17 @@ async def get_user(self, id: str, throw_if_missing: bool = False) -> User | None async def get_user(self, id: str, throw_if_missing: bool = False) -> User | None: return await self._get_item(id, User, throw_if_missing=throw_if_missing) - """For safely retrieving public user data for display on profile pages""" - async def get_user_public(self, id: str, throw_if_missing: bool = False) -> UserPublic | None: user = await self.get_user(id, throw_if_missing=throw_if_missing) if user is None: return None return UserPublic(**user.model_dump()) - """Standard sign up with email and password, leaves oauth providers empty""" - async def _create_user_from_email(self, email: str, password: str) -> User: user = User.create(email=email, password=password) await self._add_item(user, unique_fields=["email"]) return user - """OAuth sign up, creates user and links OAuthKey""" - async def _create_user_from_oauth(self, email: str, provider: str, user_token: str) -> User: user = await self.get_user_from_email(email) if user is None: @@ -195,9 +189,7 @@ async def update_user(self, user_id: str, updates: dict[str, Any]) -> User: async def test_adhoc() -> None: async with UserCrud() as crud: await crud._create_user_from_email(email="ben@kscale.dev", password="examplepas$w0rd") - await crud.get_user_from_github_token(token="gh_token_example", email="oauth_github@kscale.dev") - await crud.get_user_from_google_token(email="oauth_google@kscale.dev") diff --git a/store/app/routers/listings.py b/store/app/routers/listings.py index cb9e5234..9bb1fa27 100644 --- a/store/app/routers/listings.py +++ b/store/app/routers/listings.py @@ -230,7 +230,7 @@ class GetListingResponse(BaseModel): views: int score: int user_vote: bool | None - creator_id: str # Add this line + creator_id: str creator_name: str | None diff --git a/store/app/routers/users.py b/store/app/routers/users.py index 67627edb..b67eda6c 100644 --- a/store/app/routers/users.py +++ b/store/app/routers/users.py @@ -341,7 +341,7 @@ async def update_profile( crud: Annotated[Crud, Depends(Crud.get)], ) -> UserPublic: try: - update_dict = updates.dict(exclude_unset=True, exclude_none=True) + update_dict = updates.model_dump(exclude_unset=True, exclude_none=True) updated_user = await crud.update_user(user.id, update_dict) return UserPublic(**updated_user.model_dump()) except ValueError as e: