Skip to content

Commit

Permalink
feat: Connect Superfeed API (#217)
Browse files Browse the repository at this point in the history
  • Loading branch information
Xavier-Charles authored Feb 5, 2024
1 parent 4ead60a commit f73cdcf
Show file tree
Hide file tree
Showing 22 changed files with 493 additions and 598 deletions.
58 changes: 58 additions & 0 deletions packages/frontend/src/api/services/superfeed/superfeedEndpoints.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import queryString from "query-string";
import { Logger } from "src/api/utils/logging";
import CONFIG from "../../../config/config";
import { alphadayApi } from "../alphadayApi";
import {
TGetSuperfeedItemsRawResponse,
TGetSuperfeedItemsResponse,
TGetSuperfeedItemsRequest,
} from "./types";

const { SUPERFEED } = CONFIG.API.DEFAULT.ROUTES;

export const superfeedApi = alphadayApi.injectEndpoints({
endpoints: (builder) => ({
getSuperfeedList: builder.query<
TGetSuperfeedItemsResponse,
TGetSuperfeedItemsRequest
>({
query: (req) => {
const { ...reqParams } = req;
const params: string = queryString.stringify(reqParams);
const path = `${String(SUPERFEED.BASE)}?${params}`;
Logger.debug("getSuperfeedList: querying", path);
return path;
},
transformResponse: (
r: TGetSuperfeedItemsRawResponse
): TGetSuperfeedItemsResponse => ({
...r,
results: r.results.map((i) => ({
id: i.id,
type: i.content_type,
itemId: i.item_id,
title: i.title,
date: i.item_date,
trendiness: i.trendiness,
url: i.url,
image: i.image,
shortDescription: i.short_description,
duration: i.duration,
fileUrl: i.file_url,
startsAt: i.starts_at,
endsAt: i.ends_at,
sourceIcon: i.source.icon,
sourceSlug: i.source.slug,
sourceName: i.source.name,
tags: i.tags || [],
likes: i.likes,
comments: i.comments,
})),
}),
keepUnusedDataFor: 0,
}),
}),
overrideExisting: false,
});

export const { useGetSuperfeedListQuery } = superfeedApi;
41 changes: 41 additions & 0 deletions packages/frontend/src/api/services/superfeed/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { EFeedItemType, TSuperfeedItem } from "src/api/types";
import { TPagination } from "../baseTypes";

export type TRemoteSuperfeedItem = {
id: number;
content_type: EFeedItemType;
item_id: number;
title: string;
item_date: string;
trendiness: number;
url: string;
image: string;
short_description: string;
file_url: string | null;
duration: number | null;
starts_at: string | null;
ends_at: string | null;
source: {
icon: string;
name: string;
slug: string;
};
tags: {
name: string;
slug: string;
}[];
likes: number;
comments: number;
};

export type TGetSuperfeedItemsRequest = {
tags?: string;
page?: number;
limit?: number;
};
export type TGetSuperfeedItemsRawResponse = TPagination & {
results: TRemoteSuperfeedItem[];
};
export type TGetSuperfeedItemsResponse = TPagination & {
results: TSuperfeedItem[];
};
1 change: 1 addition & 0 deletions packages/frontend/src/api/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ export * from "./tutorial";
export * from "./user";
export * from "./views";
export * from "./video";
export * from "./superfeed";
28 changes: 28 additions & 0 deletions packages/frontend/src/api/types/superfeed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { TBaseItem } from "./primitives";

export enum EFeedItemType {
NEWS = "newsitem",
EVENT = "eventitem",
VIDEO = "videoitem",
PODCAST = "podcastitem",
IMAGE = "imageitem",
BLOG = "blogitem",
FORUM = "forumitem",
PERSON = "personitem",
REDDIT = "reddititem",
DISCORD = "discorditem",
PRICE = "priceitem",
TVL = "tvlitem",
}

export type TSuperfeedItem = Omit<TBaseItem, "bookmarked"> & {
type: EFeedItemType;
date: string;
startsAt: string | null;
endsAt: string | null;
image: string | null;
shortDescription?: string;
tags: { name: string; slug: string }[];
likes: number;
comments: number;
};
74 changes: 74 additions & 0 deletions packages/frontend/src/components/superfeed/SuperfeedModule.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { useRef, FC, FormEvent, useCallback } from "react";
import { twMerge, FeedCard, ModuleLoader, ScrollBar } from "@alphaday/ui-kit";
import { Link } from "react-router-dom";
import { useOnScreen } from "src/api/hooks";
import { TSuperfeedItem } from "src/api/types";
import { shouldFetchMoreItems } from "src/api/utils/itemUtils";
import { ReactComponent as SettingsSVG } from "src/assets/icons/settings.svg";
import { ReactComponent as Settings2SVG } from "src/assets/icons/settings3.svg";

interface ISuperfeedModule {
isLoading: boolean;
feed: TSuperfeedItem[] | undefined;
handlePaginate: (type: "next" | "previous") => void;
}

const FiltersButton = () => {
const element1: React.Ref<HTMLAnchorElement> = useRef(null);
const element2: React.Ref<HTMLAnchorElement> = useRef(null);
const element1Visible = useOnScreen(element1);

return (
<>
<Link
ref={element1}
className="flex justify-between mt-2 mb-3 px-4 py-2 border border-accentVariant100 rounded-lg"
to="/filters"
>
<p className="m-0 pr-2 fontGroup-highlight self-center">
Craft your superfeed with personalized filters
</p>
<SettingsSVG className="w-6 text-accentVariant100 mt-[3px]" />
</Link>
<Link
ref={element2}
to="/filters"
className={twMerge(
"absolute bg-accentVariant100 rounded-lg p-4 bottom-24 right-5 z-10 delay-300",
element1Visible && "hidden delay-0"
)}
>
<Settings2SVG className="w-6 text-primary" />
</Link>
</>
);
};

const SuperfeedModule: FC<ISuperfeedModule> = ({
isLoading,
feed,
handlePaginate,
}) => {
const handleScrollEvent = useCallback(
({ currentTarget }: FormEvent<HTMLElement>) => {
if (shouldFetchMoreItems(currentTarget)) {
handlePaginate("next");
}
},
[handlePaginate]
);
if (isLoading || feed === undefined) {
return <ModuleLoader $height="100%" />;
}

return (
<ScrollBar onScroll={handleScrollEvent} className="w-full px-5 pt-4">
<FiltersButton />
{feed.map((item) => (
<FeedCard key={item.id} item={item} />
))}
</ScrollBar>
);
};

export default SuperfeedModule;
3 changes: 3 additions & 0 deletions packages/frontend/src/config/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ const API_V0 = {
HISTORY: "/history/",
BY_ID: (id: number): string => `/${id}/`,
},
SUPERFEED: {
BASE: "superfeed",
},
STATUS: {
BASE: "status",
DEFAULT: "/",
Expand Down
80 changes: 80 additions & 0 deletions packages/frontend/src/containers/superfeed/SuperfeedContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { useEffect, useRef, useState } from "react";
import { usePagination } from "src/api/hooks";
import { useGetSuperfeedListQuery } from "src/api/services/superfeed/superfeedEndpoints";
import { TSuperfeedItem } from "src/api/types";
// import { filteringListToStr } from "src/api/utils/filterUtils";
import SuperfeedModule from "src/components/superfeed/SuperfeedModule";

// Todo(xavier-charles) move this to config
const MAX_PAGE_NUMBER = 1000;

const SuperfeedContainer = () => {
const [currentPage, setCurrentPage] = useState<number | undefined>();

const {
currentData: feedDataResponse,
isLoading,
isSuccess,
} = useGetSuperfeedListQuery({
page: currentPage,
// tags: currentTags ? filteringListToStr(currentTags) : undefined,
});
const prevFeedDataResponseRef = useRef<TSuperfeedItem[]>();

const {
nextPage,
handleNextPage,
// reset: resetPagination,
} = usePagination(feedDataResponse?.links, MAX_PAGE_NUMBER, isSuccess);

const feedDataForCurrentPage = [...(feedDataResponse?.results ?? [])];

const [feedData, setfeedData] = useState<TSuperfeedItem[]>([]);

// Todo(xavier-charles) Will use this later
// const reset = () => {
// if (feedData.length !== 0) setfeedData([]);
// if (currentPage !== undefined) setCurrentPage(undefined);
// resetPagination();
// };

// Todo(xavier-charles) Will use this later
// If the current response changes, it means the request parameters changed
// This happens 1. when user scrolled to the bottom or 2. tags changed.
// Case 2 is handled separately.
if (
feedDataResponse?.results !== undefined &&
prevFeedDataResponseRef.current !== feedDataResponse?.results
) {
setfeedData((prevState) => [...prevState, ...feedDataForCurrentPage]);
prevFeedDataResponseRef.current = feedDataResponse?.results;
}

// Todo(xavier-charles) Will use this lateradd
// if (tags && !itemListsAreEqual(currentTags || [], tags)) {
// reset();
// setCurrentTags(tags);
// }

// set current page 350ms after next page is set.
// RTK should cache requests, so we don't need to be too careful about rerenders.
useEffect(() => {
if (nextPage === undefined) return () => null;
const timeout = setTimeout(() => {
setCurrentPage(nextPage);
}, 350);
return () => {
clearTimeout(timeout);
};
}, [nextPage]);

return (
<SuperfeedModule
isLoading={isLoading}
feed={feedData}
handlePaginate={handleNextPage}
/>
);
};

export default SuperfeedContainer;
6 changes: 3 additions & 3 deletions packages/frontend/src/layout/MobileLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { FC, ReactNode } from "react";
import { NavBottom, NavHeader, ScrollBar } from "@alphaday/ui-kit";
import { NavBottom, NavHeader } from "@alphaday/ui-kit";

const MobileLayout: FC<{
children: ReactNode;
onScroll?: () => void;
}> = ({ children, onScroll }) => {
return (
<ScrollBar className="h-screen" onScroll={onScroll}>
<div className="h-screen" onScroll={onScroll}>
<NavHeader avatar={undefined} />
{children}
<NavBottom />
</ScrollBar>
</div>
);
};

Expand Down
Loading

0 comments on commit f73cdcf

Please sign in to comment.