-
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 #78 from game-node-app/dev
Activities work
- Loading branch information
Showing
30 changed files
with
635 additions
and
112 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 |
---|---|---|
@@ -1,35 +1,93 @@ | ||
import React, { useMemo } from "react"; | ||
import React, { useCallback, useEffect, useMemo } from "react"; | ||
import { useInfiniteActivities } from "@/components/activity/hooks/useInfiniteActivities"; | ||
import { Stack } from "@mantine/core"; | ||
import { Skeleton, 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"; | ||
import { useIntersection } from "@mantine/hooks"; | ||
import ActivityList from "@/components/activity/ActivityList"; | ||
|
||
interface Props { | ||
criteria: "following" | "all"; | ||
} | ||
|
||
const ActivityFeed = ({ criteria }: Props) => { | ||
const { ref, entry } = useIntersection({ | ||
threshold: 1, | ||
}); | ||
const activityQuery = useInfiniteActivities({ | ||
criteria, | ||
limit: 10, | ||
}); | ||
|
||
const items = useMemo(() => { | ||
if (!activityQuery.data) return undefined; | ||
return activityQuery.data.pages.flatMap((page) => { | ||
return page.data.map((activity) => { | ||
if (activity.type === type.REVIEW) { | ||
return ( | ||
<ReviewActivityItem | ||
key={activity.id} | ||
activity={activity} | ||
/> | ||
); | ||
} | ||
}); | ||
}); | ||
return activityQuery.data.pages?.flatMap((page) => page.data); | ||
}, [activityQuery.data]); | ||
return <Stack className={"w-full h-full"}>{items}</Stack>; | ||
|
||
const isLoading = activityQuery.isLoading; | ||
const isError = activityQuery.isError; | ||
const isSuccess = activityQuery.isSuccess; | ||
const isFetching = activityQuery.isFetching; | ||
|
||
const isEmpty = | ||
activityQuery.data != undefined && | ||
activityQuery.data?.pages.some((page) => { | ||
return page.pagination.totalItems === 0; | ||
}); | ||
|
||
const buildSkeletons = useCallback(() => { | ||
return new Array(4).fill(0).map((_, i) => { | ||
return <Skeleton key={i} className={"w-full h-[140px]"} />; | ||
}); | ||
}, []); | ||
|
||
useEffect(() => { | ||
const lastElement = | ||
activityQuery.data?.pages[activityQuery.data?.pages.length - 1]; | ||
const hasNextPage = | ||
lastElement != undefined && | ||
lastElement.data.length > 0 && | ||
lastElement.pagination.hasNextPage; | ||
|
||
const canFetchNextPage = !isFetching && !isLoading && hasNextPage; | ||
|
||
// Minimum amount of time (ms) since document creation for | ||
// intersection to be considered valid | ||
const minimumIntersectionTime = 3000; | ||
|
||
if ( | ||
canFetchNextPage && | ||
entry?.isIntersecting && | ||
entry.time > minimumIntersectionTime | ||
) { | ||
activityQuery.fetchNextPage({ cancelRefetch: false }); | ||
} | ||
}, [activityQuery, entry, isFetching, isLoading]); | ||
|
||
return ( | ||
<Stack className={"w-full h-full"}> | ||
{activityQuery.isLoading && buildSkeletons()} | ||
{!isLoading && isEmpty && ( | ||
<CenteredErrorMessage | ||
message={"No activities to show. Try a different filter."} | ||
/> | ||
)} | ||
{isError && ( | ||
<CenteredErrorMessage | ||
message={ | ||
"Error while fetching activities. Please try again or contact support." | ||
} | ||
/> | ||
)} | ||
<ActivityList items={items} /> | ||
<div ref={ref} id={"last-element-tracker"}></div> | ||
</Stack> | ||
); | ||
}; | ||
|
||
export default ActivityFeed; |
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
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,38 @@ | ||
import React from "react"; | ||
import { Activity } from "@/wrapper/server"; | ||
import ReviewActivityItem from "@/components/activity/item/ReviewActivityItem"; | ||
import CollectionEntryActivityItem from "@/components/activity/item/CollectionEntryActivityItem"; | ||
import UserFollowActivityItem from "@/components/activity/item/UserFollowActivityItem"; | ||
import type = Activity.type; | ||
|
||
interface Props { | ||
items: Activity[] | undefined; | ||
} | ||
|
||
const ActivityList = ({ items }: Props) => { | ||
if (!items) return null; | ||
return items.map((activity) => { | ||
switch (activity.type) { | ||
case type.REVIEW: | ||
return ( | ||
<ReviewActivityItem key={activity.id} activity={activity} /> | ||
); | ||
case type.COLLECTION_ENTRY: | ||
return ( | ||
<CollectionEntryActivityItem | ||
key={activity.id} | ||
activity={activity} | ||
/> | ||
); | ||
case type.FOLLOW: | ||
return ( | ||
<UserFollowActivityItem | ||
key={activity.id} | ||
activity={activity} | ||
/> | ||
); | ||
} | ||
}); | ||
}; | ||
|
||
export default ActivityList; |
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,58 @@ | ||
import React, { useMemo } from "react"; | ||
import useUserProfile from "@/components/profile/hooks/useUserProfile"; | ||
import { useLatestActivities } from "@/components/activity/hooks/useLatestActivities"; | ||
import ReviewActivityItem from "@/components/activity/item/ReviewActivityItem"; | ||
import CollectionEntryActivityItem from "@/components/activity/item/CollectionEntryActivityItem"; | ||
import UserFollowActivityItem from "@/components/activity/item/UserFollowActivityItem"; | ||
import { Stack } from "@mantine/core"; | ||
import CenteredLoading from "@/components/general/CenteredLoading"; | ||
import { Activity } from "@/wrapper/server"; | ||
import type = Activity.type; | ||
|
||
interface Props { | ||
userId?: string; | ||
limit?: number; | ||
offset?: number; | ||
} | ||
|
||
const RecentActivitiesList = ({ userId, offset = 0, limit = 5 }: Props) => { | ||
const activitiesQuery = useLatestActivities(userId, offset, limit); | ||
|
||
const items = useMemo(() => { | ||
if (!activitiesQuery.data) return null; | ||
return activitiesQuery.data.data.map((activity) => { | ||
switch (activity.type) { | ||
case type.REVIEW: | ||
return ( | ||
<ReviewActivityItem | ||
key={activity.id} | ||
activity={activity} | ||
/> | ||
); | ||
case type.COLLECTION_ENTRY: | ||
return ( | ||
<CollectionEntryActivityItem | ||
key={activity.id} | ||
activity={activity} | ||
/> | ||
); | ||
case type.FOLLOW: | ||
return ( | ||
<UserFollowActivityItem | ||
key={activity.id} | ||
activity={activity} | ||
/> | ||
); | ||
} | ||
}); | ||
}, [activitiesQuery.data]); | ||
|
||
return ( | ||
<Stack className={"w-full h-full"}> | ||
{activitiesQuery.isLoading && <CenteredLoading />} | ||
{items} | ||
</Stack> | ||
); | ||
}; | ||
|
||
export default RecentActivitiesList; |
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,20 @@ | ||
import { useQuery } from "@tanstack/react-query"; | ||
import { ActivitiesService } from "@/wrapper/server"; | ||
|
||
export function useLatestActivities( | ||
userId: string | undefined, | ||
offset = 0, | ||
limit = 10, | ||
) { | ||
return useQuery({ | ||
queryKey: ["activities", "latest", userId, offset, limit], | ||
queryFn: async () => { | ||
return ActivitiesService.activitiesRepositoryControllerFindLatest( | ||
userId, | ||
offset, | ||
limit, | ||
); | ||
}, | ||
retry: 1, | ||
}); | ||
} |
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
Oops, something went wrong.