From 59a170cf62b2d22abee847e5541c29a18fb41035 Mon Sep 17 00:00:00 2001 From: Lamarcke Date: Thu, 18 Apr 2024 21:40:32 -0300 Subject: [PATCH 1/7] Improved loading state for GameInfoPlaytimeItem --- .../info/playtime/GameInfoPlaytimeItem.tsx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/components/game/info/playtime/GameInfoPlaytimeItem.tsx b/src/components/game/info/playtime/GameInfoPlaytimeItem.tsx index 37ee851..ccb15da 100644 --- a/src/components/game/info/playtime/GameInfoPlaytimeItem.tsx +++ b/src/components/game/info/playtime/GameInfoPlaytimeItem.tsx @@ -20,14 +20,17 @@ const GameInfoPlaytimeItem = ({ name, value, isLoading }: Props) => { > {name} - - {valueHours} Hours - - {isLoading && } + {isLoading ? ( + + ) : ( + + {valueHours} Hours + + )} ); }; From e9d096b2f65e5f054bdc04f995ddd40d931b402a Mon Sep 17 00:00:00 2001 From: Lamarcke Date: Thu, 18 Apr 2024 21:44:05 -0300 Subject: [PATCH 2/7] Improved loading state for GameInfoPlaytimeItem --- src/components/game/info/playtime/GameInfoPlaytime.tsx | 1 - src/components/game/info/playtime/GameInfoPlaytimeItem.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/game/info/playtime/GameInfoPlaytime.tsx b/src/components/game/info/playtime/GameInfoPlaytime.tsx index 0848750..523ded3 100644 --- a/src/components/game/info/playtime/GameInfoPlaytime.tsx +++ b/src/components/game/info/playtime/GameInfoPlaytime.tsx @@ -47,7 +47,6 @@ const GameInfoPlaytime = ({ gameId }: Props) => { isLoading={playtimeQuery.isLoading} value={playtime?.time100} /> - {playtimeQuery.isLoading && } Data provided by HLTB diff --git a/src/components/game/info/playtime/GameInfoPlaytimeItem.tsx b/src/components/game/info/playtime/GameInfoPlaytimeItem.tsx index ccb15da..8165cf6 100644 --- a/src/components/game/info/playtime/GameInfoPlaytimeItem.tsx +++ b/src/components/game/info/playtime/GameInfoPlaytimeItem.tsx @@ -28,7 +28,7 @@ const GameInfoPlaytimeItem = ({ name, value, isLoading }: Props) => { "items-center font-bold w-1/2 h-10 justify-center bg-[#F15025] border-[#2E2E2E] border-[1px] rounded-r" } > - {valueHours} Hours + {valueHours === 0 ? "Not Available" : `${valueHours} Hours`} )} From 6232d39833faf47b0a441abf9e15884aaa57dedf Mon Sep 17 00:00:00 2001 From: Lamarcke Date: Fri, 19 Apr 2024 00:25:41 -0300 Subject: [PATCH 3/7] - Activities work --- src/components/activity/ActivityFeed.tsx | 64 +++++++++-- .../activity/ActivityFeedLayout.tsx | 2 +- .../activity/input/ActivityItemLikes.tsx | 35 +++--- .../item/CollectionEntryActivityItem.tsx | 108 ++++++++++++++++++ .../{ => item}/ReviewActivityItem.tsx | 24 ++-- .../activity/item/UserFollowActivityItem.tsx | 62 ++++++++++ .../hooks/useCollectionEntry.ts | 13 +++ src/components/follow/hooks/useUserFollow.ts | 15 +++ .../general/input/UserAvatarWithUsername.tsx | 6 +- .../view/sidebar/LibraryViewSidebar.tsx | 5 +- src/pages/activity/all.tsx | 12 +- src/pages/activity/following.tsx | 13 ++- src/wrapper/input/server_swagger.json | 2 +- 13 files changed, 312 insertions(+), 49 deletions(-) create mode 100644 src/components/activity/item/CollectionEntryActivityItem.tsx rename src/components/activity/{ => item}/ReviewActivityItem.tsx (80%) create mode 100644 src/components/activity/item/UserFollowActivityItem.tsx create mode 100644 src/components/collection/collection-entry/hooks/useCollectionEntry.ts create mode 100644 src/components/follow/hooks/useUserFollow.ts diff --git a/src/components/activity/ActivityFeed.tsx b/src/components/activity/ActivityFeed.tsx index 4d9ec34..987048e 100644 --- a/src/components/activity/ActivityFeed.tsx +++ b/src/components/activity/ActivityFeed.tsx @@ -3,7 +3,11 @@ import { useInfiniteActivities } from "@/components/activity/hooks/useInfiniteAc import { Stack } from "@mantine/core"; import { Activity } from "@/wrapper/server"; import type = Activity.type; -import ReviewActivityItem from "@/components/activity/ReviewActivityItem"; +import ReviewActivityItem from "@/components/activity/item/ReviewActivityItem"; +import CenteredLoading from "@/components/general/CenteredLoading"; +import CollectionEntryActivityItem from "@/components/activity/item/CollectionEntryActivityItem"; +import CenteredErrorMessage from "@/components/general/CenteredErrorMessage"; +import UserFollowActivityItem from "@/components/activity/item/UserFollowActivityItem"; interface Props { criteria: "following" | "all"; @@ -18,18 +22,60 @@ const ActivityFeed = ({ criteria }: Props) => { if (!activityQuery.data) return undefined; return activityQuery.data.pages.flatMap((page) => { return page.data.map((activity) => { - if (activity.type === type.REVIEW) { - return ( - - ); + switch (activity.type) { + case type.REVIEW: + return ( + + ); + case type.COLLECTION_ENTRY: + return ( + + ); + case type.FOLLOW: + return ( + + ); } }); }); }, [activityQuery.data]); - return {items}; + + const isLoading = activityQuery.isLoading; + const isError = activityQuery.isError; + const isSucess = activityQuery.isSuccess; + const isEmpty = + activityQuery.data != undefined && + activityQuery.data?.pages.some((page) => { + return page.pagination.totalItems === 0; + }); + + return ( + + {activityQuery.isLoading && } + {!isLoading && isEmpty && ( + + )} + {isError && ( + + )} + {items} + + ); }; export default ActivityFeed; diff --git a/src/components/activity/ActivityFeedLayout.tsx b/src/components/activity/ActivityFeedLayout.tsx index 1ad7f00..02a8cfb 100644 --- a/src/components/activity/ActivityFeedLayout.tsx +++ b/src/components/activity/ActivityFeedLayout.tsx @@ -26,7 +26,7 @@ const ActivityFeedLayout = ({ children, currentTab }: Props) => { - {children} + {children} ); }; diff --git a/src/components/activity/input/ActivityItemLikes.tsx b/src/components/activity/input/ActivityItemLikes.tsx index a43c739..7c62dab 100644 --- a/src/components/activity/input/ActivityItemLikes.tsx +++ b/src/components/activity/input/ActivityItemLikes.tsx @@ -6,6 +6,7 @@ import sourceType = StatisticsActionDto.sourceType; import { ActionIcon, Group, Text } from "@mantine/core"; import { redirectToAuth } from "supertokens-auth-react"; import { IconThumbUp } from "@tabler/icons-react"; +import useOnMobile from "@/components/general/hooks/useOnMobile"; interface Props { activityId: string; @@ -19,24 +20,22 @@ const ActivityItemLikes = ({ activityId }: Props) => { sourceType: sourceType.ACTIVITY, }); return ( - - { - if (!userId) { - redirectToAuth(); - return; - } - toggleLike(); - }} - variant={isLiked ? "filled" : "subtle"} - size={"xl"} - color={isLiked ? "brand" : "white"} - data-disabled={!userId} - > - - {likesCount} - - + { + if (!userId) { + await redirectToAuth(); + return; + } + toggleLike(); + }} + variant={isLiked ? "filled" : "default"} + size={"lg"} + color={isLiked ? "brand" : "white"} + data-disabled={!userId} + > + + {likesCount} + ); }; diff --git a/src/components/activity/item/CollectionEntryActivityItem.tsx b/src/components/activity/item/CollectionEntryActivityItem.tsx new file mode 100644 index 0000000..c98a3eb --- /dev/null +++ b/src/components/activity/item/CollectionEntryActivityItem.tsx @@ -0,0 +1,108 @@ +import React from "react"; +import { Activity } from "@/wrapper/server"; +import useOnMobile from "@/components/general/hooks/useOnMobile"; +import { useReview } from "@/components/review/hooks/useReview"; +import { useGame } from "@/components/game/hooks/useGame"; +import { + getSizedImageUrl, + ImageSize, +} from "@/components/game/util/getSizedImageUrl"; +import { Box, Group, Overlay, Stack, Text, Title } from "@mantine/core"; +import UserAvatarWithUsername from "@/components/general/input/UserAvatarWithUsername"; +import GameRating from "@/components/general/input/GameRating"; +import ActivityItemLikes from "@/components/activity/input/ActivityItemLikes"; +import { useCollectionEntry } from "@/components/collection/collection-entry/hooks/useCollectionEntry"; +import { useCollection } from "@/components/collection/hooks/useCollection"; +import Link from "next/link"; +import TitleLink from "@/components/general/TitleLink"; +import TextLink from "@/components/general/TextLink"; + +interface Props { + activity: Activity; +} + +const CollectionEntryActivityItem = ({ activity }: Props) => { + const onMobile = useOnMobile(); + const collectionEntryQuery = useCollectionEntry( + activity.collectionEntryId!, + ); + const collectionQuery = useCollection(activity.collectionId!); + const gameId = collectionEntryQuery.data?.gameId; + const gameQuery = useGame(gameId, { + relations: { + cover: true, + }, + }); + const imageUrl = getSizedImageUrl( + gameQuery.data?.cover?.url, + onMobile ? ImageSize.SCREENSHOT_MED : ImageSize.SCREENSHOT_BIG, + ); + const isError = collectionQuery.isError || collectionEntryQuery.isError; + if (isError) { + return null; + } + + const collectionEntryCreateDate = collectionEntryQuery.data + ? new Date(collectionEntryQuery.data.createdAt) + : new Date(); + return ( + + + + + + + + + + + {gameQuery.data?.name} + + + + Added at{" "} + {collectionEntryCreateDate.toLocaleDateString()} + + + + + + + + + {collectionQuery.data?.name} + + + + + + + + + + + + ); +}; + +export default CollectionEntryActivityItem; diff --git a/src/components/activity/ReviewActivityItem.tsx b/src/components/activity/item/ReviewActivityItem.tsx similarity index 80% rename from src/components/activity/ReviewActivityItem.tsx rename to src/components/activity/item/ReviewActivityItem.tsx index d4f1463..ea8e4df 100644 --- a/src/components/activity/ReviewActivityItem.tsx +++ b/src/components/activity/item/ReviewActivityItem.tsx @@ -13,6 +13,7 @@ import { IconThumbUp } from "@tabler/icons-react"; import ActivityItemLikes from "@/components/activity/input/ActivityItemLikes"; import GameRating from "@/components/general/input/GameRating"; import UserAvatarWithUsername from "@/components/general/input/UserAvatarWithUsername"; +import Link from "next/link"; interface Props { activity: Activity; @@ -31,6 +32,10 @@ const ReviewActivityItem = ({ activity }: Props) => { gameQuery.data?.cover?.url, onMobile ? ImageSize.SCREENSHOT_MED : ImageSize.SCREENSHOT_BIG, ); + const reviewCreateDate = reviewQuery.data + ? new Date(reviewQuery.data.createdAt) + : new Date(); + return ( { /> - - - {gameQuery.data?.name} - - - Reviewed + + + + {gameQuery.data?.name} + + + + + Reviewed at {reviewCreateDate.toLocaleDateString()} @@ -68,12 +76,12 @@ const ReviewActivityItem = ({ activity }: Props) => { "w-full h-full items-end justify-end lg:pe-5" } > - + - + diff --git a/src/components/activity/item/UserFollowActivityItem.tsx b/src/components/activity/item/UserFollowActivityItem.tsx new file mode 100644 index 0000000..8fa14e5 --- /dev/null +++ b/src/components/activity/item/UserFollowActivityItem.tsx @@ -0,0 +1,62 @@ +import React from "react"; +import { Activity } from "@/wrapper/server"; +import { useUserFollow } from "@/components/follow/hooks/useUserFollow"; +import useUserProfile from "@/components/profile/hooks/useUserProfile"; +import { Box, Group, Paper, Text, Title } from "@mantine/core"; +import UserAvatarWithUsername from "@/components/general/input/UserAvatarWithUsername"; +import useOnMobile from "@/components/general/hooks/useOnMobile"; +import Link from "next/link"; +import TextLink from "@/components/general/TextLink"; + +interface Props { + activity: Activity; +} + +const UserFollowActivityItem = ({ activity }: Props) => { + const onMobile = useOnMobile(); + const userFollowQuery = useUserFollow(activity.userFollowId!); + + const followerUserId = userFollowQuery.data?.followerUserId; + const followedUserId = userFollowQuery.data?.followedUserId; + + const followerUserProfile = useUserProfile(followerUserId); + const followedUserProfile = useUserProfile(followedUserId); + + if (!followerUserId || !followedUserId) return null; + + return ( + + + + + + + + + {followerUserProfile.data?.username} + {" "} + started following{" "} + + {followedUserProfile.data?.username} + + + + + + + + + ); +}; + +export default UserFollowActivityItem; diff --git a/src/components/collection/collection-entry/hooks/useCollectionEntry.ts b/src/components/collection/collection-entry/hooks/useCollectionEntry.ts new file mode 100644 index 0000000..dc3db0d --- /dev/null +++ b/src/components/collection/collection-entry/hooks/useCollectionEntry.ts @@ -0,0 +1,13 @@ +import { useQuery } from "@tanstack/react-query"; +import { CollectionsEntriesService } from "@/wrapper/server"; + +export function useCollectionEntry(collectionEntryId: string) { + return useQuery({ + queryKey: ["collection-entries", collectionEntryId], + queryFn: async () => { + return CollectionsEntriesService.collectionsEntriesControllerFindEntryById( + collectionEntryId, + ); + }, + }); +} diff --git a/src/components/follow/hooks/useUserFollow.ts b/src/components/follow/hooks/useUserFollow.ts new file mode 100644 index 0000000..2088b26 --- /dev/null +++ b/src/components/follow/hooks/useUserFollow.ts @@ -0,0 +1,15 @@ +import { useQuery } from "@tanstack/react-query"; +import { FollowService } from "@/wrapper/server"; + +/** + * Returns a UserFollow entity using it's id. + * @param id + */ +export function useUserFollow(id: number) { + return useQuery({ + queryKey: ["follow", "entity", id], + queryFn: async () => { + return FollowService.followControllerGetUserFollowById(id); + }, + }); +} diff --git a/src/components/general/input/UserAvatarWithUsername.tsx b/src/components/general/input/UserAvatarWithUsername.tsx index 09473e3..45405b7 100644 --- a/src/components/general/input/UserAvatarWithUsername.tsx +++ b/src/components/general/input/UserAvatarWithUsername.tsx @@ -51,16 +51,16 @@ const UserAvatarWithUsername = ({ userId, ...avatarProps }: Props) => { "linear-gradient(180deg, rgba(30,30,30,0.7973390039609594) 0%, rgba(30,30,30,0.772128919927346) 100%)" } backgroundOpacity={0.6} - className={"z-10"} + className={"z-10 rounded"} /> )} {overlayVisible && ( - + {profileQuery.isLoading && ( )} {username && ( - + {username} )} diff --git a/src/components/library/view/sidebar/LibraryViewSidebar.tsx b/src/components/library/view/sidebar/LibraryViewSidebar.tsx index 6dca2e1..4638b04 100644 --- a/src/components/library/view/sidebar/LibraryViewSidebar.tsx +++ b/src/components/library/view/sidebar/LibraryViewSidebar.tsx @@ -6,6 +6,7 @@ import { Box, Group, Space, Text, TextInput } from "@mantine/core"; import Link from "next/link"; import { Collection } from "@/wrapper/server"; import LibraryViewSidebarCollections from "@/components/library/view/sidebar/LibraryViewSidebarCollections"; +import useUserProfile from "@/components/profile/hooks/useUserProfile"; interface ILibraryViewSidebarProps { userId: string | undefined; @@ -20,12 +21,14 @@ const buildCollectionsItems = (collections: Collection[]) => { const LibraryViewSidebar = ({ userId }: ILibraryViewSidebarProps) => { const userLibraryQuery = useUserLibrary(userId); const userLibrary = userLibraryQuery.data; + const userProfileQuery = useUserProfile(userId); + const username = userProfileQuery.data?.username; return (