diff --git a/bff/apollo-gateway/src/app/content/content.resolver.ts b/bff/apollo-gateway/src/app/content/content.resolver.ts index 044d7b22..30176bef 100644 --- a/bff/apollo-gateway/src/app/content/content.resolver.ts +++ b/bff/apollo-gateway/src/app/content/content.resolver.ts @@ -9,7 +9,9 @@ import { ArticleOGP, ArticlesInput, FeedConnection, + Feed, FeedsInput, + FeedInput, } from '../../graphql/types/graphql'; import { SupabaseAuthGuard } from '../auth/auth.guard'; @@ -47,4 +49,14 @@ export class ContentResolver { const user = context.req.user; return await this.feedService.getFeeds(user.id, feedsInput); } + + @Query(() => Feed) + @UseGuards(SupabaseAuthGuard) + async feed( + @Args('feedInput') feedInput: FeedInput, + @Context() context: GraphQLContext, + ): Promise { + const user = context.req.user; + return await this.feedService.getFeed(user.id, feedInput); + } } diff --git a/bff/apollo-gateway/src/app/content/feed/feed.service.ts b/bff/apollo-gateway/src/app/content/feed/feed.service.ts index 5c65730e..5f9f160b 100644 --- a/bff/apollo-gateway/src/app/content/feed/feed.service.ts +++ b/bff/apollo-gateway/src/app/content/feed/feed.service.ts @@ -3,7 +3,12 @@ import { Int64Value, StringValue, } from 'google-protobuf/google/protobuf/wrappers_pb'; -import { FeedConnection, FeedsInput } from 'src/graphql/types/graphql'; +import { + FeedConnection, + FeedsInput, + Feed, + FeedInput, +} from 'src/graphql/types/graphql'; import { GetFeedsRequest } from 'src/grpc/content/content_pb'; import { convertTimestampToInt } from '../../../utils/timestamp'; @@ -91,4 +96,40 @@ export class FeedService { }); }); } + + async getFeed(userId: string, input: FeedInput): Promise { + console.log('getFeed', userId, input); + return new Promise((resolve) => { + resolve({ + apiQueryParam: '', + category: { + createdAt: 0, + id: '', + name: '', + type: 0, + updatedAt: 0, + }, + createdAt: 0, + description: '', + id: '', + myFeedIds: [], + name: '', + platform: { + createdAt: 0, + faviconUrl: '', + id: '', + isEng: false, + name: '', + platformSiteType: 0, + siteUrl: '', + updatedAt: 0, + }, + rssUrl: '', + siteUrl: '', + thumbnailUrl: '', + trendPlatformType: 0, + updatedAt: 0, + }); + }); + } } diff --git a/bff/apollo-gateway/src/graphql/types/graphql.ts b/bff/apollo-gateway/src/graphql/types/graphql.ts index 4634b3b6..2c71179d 100644 --- a/bff/apollo-gateway/src/graphql/types/graphql.ts +++ b/bff/apollo-gateway/src/graphql/types/graphql.ts @@ -156,6 +156,10 @@ export class FeedsInput { before?: Nullable; } +export class FeedInput { + id: string; +} + export interface Node { id: string; } @@ -176,6 +180,8 @@ export abstract class IQuery { abstract favoriteAllFolderArticles(input?: Nullable): FavoriteAllFolderArticleConnection | Promise; abstract feeds(feedsInput: FeedsInput): FeedConnection | Promise; + + abstract feed(feedInput?: Nullable): Feed | Promise; } export class Article implements Node { diff --git a/bff/apollo-gateway/src/schema/feed/query.graphql b/bff/apollo-gateway/src/schema/feed/query.graphql index 644091e6..d09b364c 100644 --- a/bff/apollo-gateway/src/schema/feed/query.graphql +++ b/bff/apollo-gateway/src/schema/feed/query.graphql @@ -3,6 +3,7 @@ type Query { Get feeds """ feeds(feedsInput: FeedsInput!): FeedConnection! + feed(feedInput: FeedInput): Feed! } input FeedsInput { @@ -13,4 +14,8 @@ input FeedsInput { after: String last: Int before: String +} + +input FeedInput { + id: ID! } \ No newline at end of file diff --git a/micro-service/content-service/internal/adapter/persistence_adapter/article.go b/micro-service/content-service/internal/adapter/persistence_adapter/article.go index d96e5b67..5aa7f094 100644 --- a/micro-service/content-service/internal/adapter/persistence_adapter/article.go +++ b/micro-service/content-service/internal/adapter/persistence_adapter/article.go @@ -14,7 +14,7 @@ import ( ) type ArticlePersistenceAdapter interface { - GetArticles(ctx context.Context, req *cpb.GetArticlesRequest) (entity.ArticleSlice, error) + GetArticles(ctx context.Context, req *cpb.GetArticlesRequest, limit int) (entity.ArticleSlice, error) GetArticlesByArticleURLAndPlatformURL(ctx context.Context, articleURL, platformURL string) (entity.ArticleSlice, error) GetPrivateArticlesByArticleURL(ctx context.Context, articleURL string) (entity.ArticleSlice, error) GetArticleRelationPlatform(ctx context.Context, articleID string) (entity.Article, error) @@ -31,12 +31,7 @@ func NewArticlePersistenceAdapter(ar repository.ArticleRepository) ArticlePersis } } -func (apa *articlePersistenceAdapter) GetArticles(ctx context.Context, req *cpb.GetArticlesRequest) (entity.ArticleSlice, error) { - limit := 20 - if req.GetLimit() != 0 { - limit = int(req.GetLimit()) - } - +func (apa *articlePersistenceAdapter) GetArticles(ctx context.Context, req *cpb.GetArticlesRequest, limit int) (entity.ArticleSlice, error) { q := []qm.QueryMod{ qm.InnerJoin("platforms ON articles.platform_id = platforms.id"), qm.LeftOuterJoin("feed_article_relations ON articles.id = feed_article_relations.article_id"), diff --git a/micro-service/content-service/internal/application/usecase/article.go b/micro-service/content-service/internal/application/usecase/article.go index f508f115..dc665606 100644 --- a/micro-service/content-service/internal/application/usecase/article.go +++ b/micro-service/content-service/internal/application/usecase/article.go @@ -13,7 +13,12 @@ import ( ) func (cu *contentUseCase) GetArticles(ctx context.Context, req *cpb.GetArticlesRequest) (*cpb.GetArticlesResponse, error) { - articles, err := cu.articlePersistenceAdapter.GetArticles(ctx, req) + limit := 20 + if req.GetLimit() != 0 { + limit = int(req.GetLimit()) + } + + articles, err := cu.articlePersistenceAdapter.GetArticles(ctx, req, limit) if err != nil { return nil, err } @@ -83,7 +88,7 @@ func (cu *contentUseCase) GetArticles(ctx context.Context, req *cpb.GetArticlesR return &cpb.GetArticlesResponse{ ArticlesEdge: edges, PageInfo: &cpb.PageInfo{ - HasNextPage: len(edges) == 20, + HasNextPage: len(edges) == limit, EndCursor: edges[len(edges)-1].Cursor, }, }, nil diff --git a/web/client-v2/src/app/feed/[id]/page.tsx b/web/client-v2/src/app/feed/[id]/page.tsx index af0d4ba5..7d9b1585 100644 --- a/web/client-v2/src/app/feed/[id]/page.tsx +++ b/web/client-v2/src/app/feed/[id]/page.tsx @@ -1,6 +1,7 @@ import { redirect } from "next/navigation"; import { getUser } from "@/features/auth/actions/user"; +import { FeedArticleListTemplate } from "@/features/feeds/components/Template"; type FeedByIdPageProps = { params: { id: string }; @@ -16,9 +17,10 @@ export default async function FeedByIdPage({ redirect("/login"); } const { id } = params; - return ( -
-

{`feed ${id}`}

-
- ); + const keyword = + typeof searchParams["keyword"] === "string" + ? searchParams["keyword"] + : undefined; + + return ; } diff --git a/web/client-v2/src/features/articles/components/Card/ArticleCardItem/ArticleCardItem.tsx b/web/client-v2/src/features/articles/components/Card/ArticleCardItem/ArticleCardItem.tsx index 80a2ff78..69d52811 100644 --- a/web/client-v2/src/features/articles/components/Card/ArticleCardItem/ArticleCardItem.tsx +++ b/web/client-v2/src/features/articles/components/Card/ArticleCardItem/ArticleCardItem.tsx @@ -1,6 +1,5 @@ "use client"; -import { User } from "@supabase/supabase-js"; import { clsx } from "clsx"; import { FragmentOf, readFragment } from "gql.tada"; import NextLink from "next/link"; @@ -9,18 +8,13 @@ import { FC } from "react"; import { ZoomableImage } from "@/components/ui/image"; import { Link } from "@/components/ui/link"; - import { showDiffDateToCurrentDate } from "@/lib/date"; -import { ArticleTabType } from "@/types/article"; - import styles from "./ArticleCardItem.module.css"; import { ArticleCardItemFragment } from "./ArticleCardItemFragment"; type ArticleCardItemProps = { data: FragmentOf; - user: User; - tab: ArticleTabType; }; export const ArticleCardItem: FC = ({ data }) => { diff --git a/web/client-v2/src/features/articles/components/Card/ArticleCardWrapper/ArticleCardWrapper.tsx b/web/client-v2/src/features/articles/components/Card/ArticleCardWrapper/ArticleCardWrapper.tsx index b5d928d8..8176dc00 100644 --- a/web/client-v2/src/features/articles/components/Card/ArticleCardWrapper/ArticleCardWrapper.tsx +++ b/web/client-v2/src/features/articles/components/Card/ArticleCardWrapper/ArticleCardWrapper.tsx @@ -1,6 +1,5 @@ "use client"; -import { User } from "@supabase/supabase-js"; import { clsx } from "clsx"; import { FragmentOf, readFragment } from "gql.tada"; import { FC } from "react"; @@ -27,7 +26,6 @@ type ArticleCardWrapperProps = { favoriteArticleFolders: FragmentOf< typeof FavoriteFolderArticleCardWrapperFragment >; - user: User; tab: ArticleTabType; }; @@ -36,7 +34,6 @@ const TREND_TAB = "trend"; export const ArticleCardWrapper: FC = ({ data, favoriteArticleFolders, - user, tab, }: ArticleCardWrapperProps) => { const fragment = readFragment(ArticleCardWrapperFragment, data); @@ -133,7 +130,7 @@ export const ArticleCardWrapper: FC = ({
- +
diff --git a/web/client-v2/src/features/articles/components/List/ArticleList/ArticleList.tsx b/web/client-v2/src/features/articles/components/List/ArticleList/ArticleList.tsx index 5311a35d..f73978af 100644 --- a/web/client-v2/src/features/articles/components/List/ArticleList/ArticleList.tsx +++ b/web/client-v2/src/features/articles/components/List/ArticleList/ArticleList.tsx @@ -154,7 +154,6 @@ export function ArticleList({ user, languageStatus, tab }: ArticleListProps) { key={`${i}-${edge.node.id}`} data={edge.node} favoriteArticleFolders={resSuspenseData.favoriteArticleFolders} - user={user} tab={tab} /> ))} diff --git a/web/client-v2/src/features/feeds/components/List/FeedArticleList/FeedArticleList.tsx b/web/client-v2/src/features/feeds/components/List/FeedArticleList/FeedArticleList.tsx new file mode 100644 index 00000000..81733e7c --- /dev/null +++ b/web/client-v2/src/features/feeds/components/List/FeedArticleList/FeedArticleList.tsx @@ -0,0 +1,164 @@ +"use client"; + +import { useSuspenseQuery, useQuery } from "@apollo/client"; +import { useCallback, useRef, useState, useEffect } from "react"; + +import { ArticleCardWrapper } from "@/features/articles/components/Card/ArticleCardWrapper/ArticleCardWrapper"; + +import { NotFoundList } from "@/components/layout/NotFoundList"; +import { Loader } from "@/components/ui/loader"; + +import { FeedArticleListQuery } from "./FeedArticleListQuery"; +import { FeedArticleListTemplateQuery } from "../../Template/FeedArticleListTemplate/FeedArticleListTemplateQuery"; + +type FeedArticleListProps = { + id: string; + keyword?: string; +}; + +export function FeedArticleList({ id, keyword }: FeedArticleListProps) { + const observerTarget = useRef(null); + + const { data: resSuspenseData, error } = useSuspenseQuery( + FeedArticleListTemplateQuery, + { + variables: { + input: { + first: 20, + after: null, + feedIds: [id], + }, + favoriteArticleFoldersInput: { + isAllFetch: true, + isFolderOnly: true, + }, + }, + } + ); + + const { + data: res, + fetchMore, + error: onlyFetchArticlesError, + } = useQuery(FeedArticleListQuery, { + variables: { + input: { + first: 20, + after: null, + feedIds: [id], + }, + }, + fetchPolicy: "cache-first", + nextFetchPolicy: "network-only", + }); + + const [hashMore, setHashMore] = useState(true); + const [offset, setOffset] = useState(1); + const [endCursor, setEndCursor] = useState( + res?.articles.pageInfo?.endCursor || null + ); + const [isNextPage, setIsNextPage] = useState(true); + + const loadMore = useCallback(async () => { + if (!isNextPage) return; + + const { data: resData, error: resError } = await fetchMore({ + variables: { + input: { + first: 20, + after: endCursor, + feedIds: [id], + }, + }, + updateQuery: (prev, { fetchMoreResult }) => { + if (!fetchMoreResult) return prev; + return { + ...prev, + articles: { + ...prev.articles, + edges: [...prev.articles.edges, ...fetchMoreResult.articles.edges], + pageInfo: fetchMoreResult.articles.pageInfo, + }, + }; + }, + }); + + if (resError) return; + + if (resData.articles.pageInfo.hasNextPage) { + const endCursor = resData.articles.pageInfo?.endCursor || null; + setEndCursor(endCursor); + } + setIsNextPage(resData.articles.pageInfo.hasNextPage); + + setHashMore(resData.articles.edges.length > 0); + }, [id, endCursor, isNextPage, fetchMore]); + + useEffect(() => { + const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting && hashMore) { + setOffset((prev) => prev + 1); + } + }); + }, + { threshold: 1 } + ); + + let observerRefValue: null = null; + + if (observerTarget.current) { + observer.observe(observerTarget.current); + observerRefValue = observerTarget.current; + } + + return () => { + if (observerRefValue) { + observer.unobserve(observerRefValue); + } + }; + }, [hashMore]); + + useEffect(() => { + if (offset > 1) { + loadMore(); + } + }, [offset, hashMore]); // eslint-disable-line + + if (error) { + return
{error.message}
; + } + + if (onlyFetchArticlesError) { + return
{onlyFetchArticlesError.message}
; + } + + return ( + <> + {res?.articles?.edges.length === 0 ? ( +
+ +
+ ) : ( +
+ {res?.articles?.edges?.map((edge, i) => ( + + ))} +
+ {hashMore && isNextPage && ( +
+ +
+ )} +
+
+ )} + + ); +} diff --git a/web/client-v2/src/features/feeds/components/List/FeedArticleList/FeedArticleListQuery.ts b/web/client-v2/src/features/feeds/components/List/FeedArticleList/FeedArticleListQuery.ts new file mode 100644 index 00000000..0df4d8a4 --- /dev/null +++ b/web/client-v2/src/features/feeds/components/List/FeedArticleList/FeedArticleListQuery.ts @@ -0,0 +1,25 @@ +import { graphql } from "gql.tada"; + +import { ArticleCardWrapperFragment } from "@/features/articles/components/Card"; + +export const FeedArticleListQuery = graphql( + ` + query FeedArticleListQuery($input: ArticlesInput!) { + articles(articlesInput: $input) { + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + edges { + node { + id + ...ArticleCardWrapperFragment + } + } + } + } + `, + [ArticleCardWrapperFragment] +); diff --git a/web/client-v2/src/features/feeds/components/List/FeedArticleList/index.ts b/web/client-v2/src/features/feeds/components/List/FeedArticleList/index.ts new file mode 100644 index 00000000..b182d0b6 --- /dev/null +++ b/web/client-v2/src/features/feeds/components/List/FeedArticleList/index.ts @@ -0,0 +1 @@ +export * from "./FeedArticleList"; diff --git a/web/client-v2/src/features/feeds/components/List/index.ts b/web/client-v2/src/features/feeds/components/List/index.ts index 4d1b0259..993c9dbe 100644 --- a/web/client-v2/src/features/feeds/components/List/index.ts +++ b/web/client-v2/src/features/feeds/components/List/index.ts @@ -1 +1,2 @@ export * from "./FeedList"; +export * from "./FeedArticleList"; diff --git a/web/client-v2/src/features/feeds/components/Template/FeedArticleListTemplate/FeedArticleListTemplate.tsx b/web/client-v2/src/features/feeds/components/Template/FeedArticleListTemplate/FeedArticleListTemplate.tsx new file mode 100644 index 00000000..f445fac2 --- /dev/null +++ b/web/client-v2/src/features/feeds/components/Template/FeedArticleListTemplate/FeedArticleListTemplate.tsx @@ -0,0 +1,75 @@ +import { FC, Suspense } from "react"; + +import { ScreenLoader } from "@/components/layout/ScreenLoader"; +import { BreadCrumbType, PageBreadcrumb } from "@/components/ui/breadcrumb"; + +import { PreloadQuery } from "@/lib/apollo/client"; + +import { FeedArticleListTemplateQuery } from "./FeedArticleListTemplateQuery"; +import { FeedArticleList } from "../../List"; + +type FeedArticleListTemplateProps = { + id: string; + keyword?: string; +}; + +export const FeedArticleListTemplate: FC = ({ + id, + keyword, +}) => { + const breadcrumbs: BreadCrumbType[] = [ + { + title: "Home", + href: "/", + }, + { + title: "Feeds", + href: "/feed", + }, + { + title: "", + href: `/feed/${id}`, + }, + ]; + + return ( + <> +
+
+ +
+
+ {/* {resFeed.data.feed && ( + + )} */} +
+
+ +
+ + + }> + + + + + ); +}; diff --git a/web/client-v2/src/features/feeds/components/Template/FeedArticleListTemplate/FeedArticleListTemplateQuery.ts b/web/client-v2/src/features/feeds/components/Template/FeedArticleListTemplate/FeedArticleListTemplateQuery.ts new file mode 100644 index 00000000..8c262ec0 --- /dev/null +++ b/web/client-v2/src/features/feeds/components/Template/FeedArticleListTemplate/FeedArticleListTemplateQuery.ts @@ -0,0 +1,34 @@ +import { graphql } from "gql.tada"; + +import { + ArticleCardWrapperFragment, + FavoriteFolderArticleCardWrapperFragment, +} from "@/features/articles/components/Card"; + +export const FeedArticleListTemplateQuery = graphql( + ` + query FeedArticleListTemplateQuery( + $input: ArticlesInput! + $favoriteArticleFoldersInput: FavoriteArticleFoldersInput! + ) { + articles(articlesInput: $input) { + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + edges { + node { + id + ...ArticleCardWrapperFragment + } + } + } + favoriteArticleFolders(input: $favoriteArticleFoldersInput) { + ...FavoriteFolderArticleCardWrapperFragment + } + } + `, + [ArticleCardWrapperFragment, FavoriteFolderArticleCardWrapperFragment] +); diff --git a/web/client-v2/src/features/feeds/components/Template/FeedArticleListTemplate/actGetFeedArticleListTemplateQuery.ts b/web/client-v2/src/features/feeds/components/Template/FeedArticleListTemplate/actGetFeedArticleListTemplateQuery.ts new file mode 100644 index 00000000..908fe79e --- /dev/null +++ b/web/client-v2/src/features/feeds/components/Template/FeedArticleListTemplate/actGetFeedArticleListTemplateQuery.ts @@ -0,0 +1 @@ +"use server"; diff --git a/web/client-v2/src/features/feeds/components/Template/FeedArticleListTemplate/index.ts b/web/client-v2/src/features/feeds/components/Template/FeedArticleListTemplate/index.ts new file mode 100644 index 00000000..974b5b13 --- /dev/null +++ b/web/client-v2/src/features/feeds/components/Template/FeedArticleListTemplate/index.ts @@ -0,0 +1 @@ +export * from "./FeedArticleListTemplate"; diff --git a/web/client-v2/src/features/feeds/components/Template/index.ts b/web/client-v2/src/features/feeds/components/Template/index.ts index 51c222c5..599e9622 100644 --- a/web/client-v2/src/features/feeds/components/Template/index.ts +++ b/web/client-v2/src/features/feeds/components/Template/index.ts @@ -1 +1,2 @@ export * from "./FeedListTemplate"; +export * from "./FeedArticleListTemplate"; diff --git a/web/client-v2/src/features/trends/components/List/TrendArticleList/TrendArticleList.tsx b/web/client-v2/src/features/trends/components/List/TrendArticleList/TrendArticleList.tsx index aa4849eb..e669c5ac 100644 --- a/web/client-v2/src/features/trends/components/List/TrendArticleList/TrendArticleList.tsx +++ b/web/client-v2/src/features/trends/components/List/TrendArticleList/TrendArticleList.tsx @@ -157,7 +157,6 @@ export function TrendArticleList({
diff --git a/web/client-v2/src/graphql/graphql-env.d.ts b/web/client-v2/src/graphql/graphql-env.d.ts index de01582d..74a808e9 100644 --- a/web/client-v2/src/graphql/graphql-env.d.ts +++ b/web/client-v2/src/graphql/graphql-env.d.ts @@ -37,6 +37,7 @@ export type introspection_types = { 'Feed': { kind: 'OBJECT'; name: 'Feed'; fields: { 'apiQueryParam': { name: 'apiQueryParam'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'category': { name: 'category'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Category'; ofType: null; }; } }; 'createdAt': { name: 'createdAt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'deletedAt': { name: 'deletedAt'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'description': { name: 'description'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'myFeedIds': { name: 'myFeedIds'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; } }; 'name': { name: 'name'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'platform': { name: 'platform'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Platform'; ofType: null; }; } }; 'rssUrl': { name: 'rssUrl'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'siteUrl': { name: 'siteUrl'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'thumbnailUrl': { name: 'thumbnailUrl'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'trendPlatformType': { name: 'trendPlatformType'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'updatedAt': { name: 'updatedAt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; }; }; 'FeedConnection': { kind: 'OBJECT'; name: 'FeedConnection'; fields: { 'edges': { name: 'edges'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'FeedEdge'; ofType: null; }; }; }; } }; 'pageInfo': { name: 'pageInfo'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'PageInfo'; ofType: null; }; } }; }; }; 'FeedEdge': { kind: 'OBJECT'; name: 'FeedEdge'; fields: { 'cursor': { name: 'cursor'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'node': { name: 'node'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Feed'; ofType: null; }; } }; }; }; + 'FeedInput': { kind: 'INPUT_OBJECT'; name: 'FeedInput'; isOneOf: false; inputFields: [{ name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }]; }; 'FeedsInput': { kind: 'INPUT_OBJECT'; name: 'FeedsInput'; isOneOf: false; inputFields: [{ name: 'platformSiteType'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; defaultValue: null }, { name: 'platformId'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'keyword'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'first'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; defaultValue: null }, { name: 'after'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'last'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; defaultValue: null }, { name: 'before'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }]; }; 'ID': unknown; 'Int': unknown; @@ -46,7 +47,7 @@ export type introspection_types = { 'PageInfo': { kind: 'OBJECT'; name: 'PageInfo'; fields: { 'endCursor': { name: 'endCursor'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'hasNextPage': { name: 'hasNextPage'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'hasPreviousPage': { name: 'hasPreviousPage'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'startCursor': { name: 'startCursor'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; }; }; 'Platform': { kind: 'OBJECT'; name: 'Platform'; fields: { 'createdAt': { name: 'createdAt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'deletedAt': { name: 'deletedAt'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'faviconUrl': { name: 'faviconUrl'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'isEng': { name: 'isEng'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'name': { name: 'name'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'platformSiteType': { name: 'platformSiteType'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'siteUrl': { name: 'siteUrl'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'updatedAt': { name: 'updatedAt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; }; }; 'Profile': { kind: 'OBJECT'; name: 'Profile'; fields: { 'createdAt': { name: 'createdAt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'deletedAt': { name: 'deletedAt'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'email': { name: 'email'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'emailVerified': { name: 'emailVerified'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'image': { name: 'image'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'isSuperAdmin': { name: 'isSuperAdmin'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'name': { name: 'name'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'updatedAt': { name: 'updatedAt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; }; }; - 'Query': { kind: 'OBJECT'; name: 'Query'; fields: { 'articleOpg': { name: 'articleOpg'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ArticleOGP'; ofType: null; }; } }; 'articles': { name: 'articles'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ArticleConnection'; ofType: null; }; } }; 'bookmarks': { name: 'bookmarks'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'BookmarkConnection'; ofType: null; }; } }; 'favoriteAllFolderArticles': { name: 'favoriteAllFolderArticles'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'FavoriteAllFolderArticleConnection'; ofType: null; }; } }; 'favoriteArticleFolder': { name: 'favoriteArticleFolder'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'FavoriteArticleFolder'; ofType: null; }; } }; 'favoriteArticleFolders': { name: 'favoriteArticleFolders'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'FavoriteArticleFolderConnection'; ofType: null; }; } }; 'favoriteArticles': { name: 'favoriteArticles'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'FavoriteArticleConnection'; ofType: null; }; } }; 'feeds': { name: 'feeds'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'FeedConnection'; ofType: null; }; } }; }; }; + 'Query': { kind: 'OBJECT'; name: 'Query'; fields: { 'articleOpg': { name: 'articleOpg'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ArticleOGP'; ofType: null; }; } }; 'articles': { name: 'articles'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ArticleConnection'; ofType: null; }; } }; 'bookmarks': { name: 'bookmarks'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'BookmarkConnection'; ofType: null; }; } }; 'favoriteAllFolderArticles': { name: 'favoriteAllFolderArticles'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'FavoriteAllFolderArticleConnection'; ofType: null; }; } }; 'favoriteArticleFolder': { name: 'favoriteArticleFolder'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'FavoriteArticleFolder'; ofType: null; }; } }; 'favoriteArticleFolders': { name: 'favoriteArticleFolders'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'FavoriteArticleFolderConnection'; ofType: null; }; } }; 'favoriteArticles': { name: 'favoriteArticles'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'FavoriteArticleConnection'; ofType: null; }; } }; 'feed': { name: 'feed'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Feed'; ofType: null; }; } }; 'feeds': { name: 'feeds'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'FeedConnection'; ofType: null; }; } }; }; }; 'String': unknown; 'UpdateFavoriteArticleFolderInput': { kind: 'INPUT_OBJECT'; name: 'UpdateFavoriteArticleFolderInput'; isOneOf: false; inputFields: [{ name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }, { name: 'title'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'description'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }]; }; }; diff --git a/web/client-v2/src/graphql/type.ts b/web/client-v2/src/graphql/type.ts index c47fade2..297140b0 100644 --- a/web/client-v2/src/graphql/type.ts +++ b/web/client-v2/src/graphql/type.ts @@ -347,6 +347,10 @@ export type FeedEdge = { node: Feed; }; +export type FeedInput = { + id: Scalars['ID']['input']; +}; + export type FeedsInput = { after?: InputMaybe; before?: InputMaybe; @@ -484,6 +488,7 @@ export type Query = { favoriteArticleFolder: FavoriteArticleFolder; favoriteArticleFolders: FavoriteArticleFolderConnection; favoriteArticles: FavoriteArticleConnection; + feed: Feed; /** Get feeds */ feeds: FeedConnection; }; @@ -524,6 +529,11 @@ export type QueryFavoriteArticlesArgs = { }; +export type QueryFeedArgs = { + feedInput?: InputMaybe; +}; + + export type QueryFeedsArgs = { feedsInput: FeedsInput; }; @@ -757,6 +767,13 @@ export type FeedCardItemFragmentFragment = { __typename?: 'Feed', id: string, na export type FeedCardWrapperFragmentFragment = { __typename?: 'Feed', id: string, myFeedIds?: Array | null, name: string, description: string, siteUrl: string, thumbnailUrl: string, platform: { __typename?: 'Platform', id: string, faviconUrl: string } }; +export type FeedArticleListQueryQueryVariables = Exact<{ + input: ArticlesInput; +}>; + + +export type FeedArticleListQueryQuery = { __typename?: 'Query', articles: { __typename?: 'ArticleConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null, endCursor?: string | null }, edges: Array<{ __typename?: 'ArticleEdge', node: { __typename: 'Article', id: string, title: string, description: string, articleUrl: string, publishedAt?: number | null, authorName?: string | null, tags?: string | null, thumbnailUrl: string, isEng: boolean, isPrivate: boolean, isBookmarked: boolean, bookmarkId?: string | null, likeCount?: number | null, isFollowing: boolean, favoriteArticleFolderIds: Array, platform?: { __typename?: 'Platform', id: string, name: string, siteUrl: string, faviconUrl: string } | null, feeds?: Array<{ __typename?: 'Feed', id: string, name: string }> | null } }> } }; + export type FeedListQueryQueryVariables = Exact<{ input: FeedsInput; }>; @@ -764,6 +781,14 @@ export type FeedListQueryQueryVariables = Exact<{ export type FeedListQueryQuery = { __typename?: 'Query', feeds: { __typename?: 'FeedConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean, endCursor?: string | null }, edges: Array<{ __typename?: 'FeedEdge', node: { __typename?: 'Feed', id: string, myFeedIds?: Array | null, name: string, description: string, siteUrl: string, thumbnailUrl: string, platform: { __typename?: 'Platform', id: string, faviconUrl: string } } }> } }; +export type FeedArticleListTemplateQueryQueryVariables = Exact<{ + input: ArticlesInput; + favoriteArticleFoldersInput: FavoriteArticleFoldersInput; +}>; + + +export type FeedArticleListTemplateQueryQuery = { __typename?: 'Query', articles: { __typename?: 'ArticleConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null, endCursor?: string | null }, edges: Array<{ __typename?: 'ArticleEdge', node: { __typename: 'Article', id: string, title: string, description: string, articleUrl: string, publishedAt?: number | null, authorName?: string | null, tags?: string | null, thumbnailUrl: string, isEng: boolean, isPrivate: boolean, isBookmarked: boolean, bookmarkId?: string | null, likeCount?: number | null, isFollowing: boolean, favoriteArticleFolderIds: Array, platform?: { __typename?: 'Platform', id: string, name: string, siteUrl: string, faviconUrl: string } | null, feeds?: Array<{ __typename?: 'Feed', id: string, name: string }> | null } }> }, favoriteArticleFolders: { __typename?: 'FavoriteArticleFolderConnection', edges: Array<{ __typename?: 'FavoriteArticleFolderEdge', node: { __typename?: 'FavoriteArticleFolder', id: string, title: string } }> } }; + export type FeedListTemplateQueryQueryVariables = Exact<{ input: FeedsInput; }>; @@ -2226,6 +2251,57 @@ export function useUpdateFavoriteArticleFolderMutationMutation(baseOptions?: Apo export type UpdateFavoriteArticleFolderMutationMutationHookResult = ReturnType; export type UpdateFavoriteArticleFolderMutationMutationResult = Apollo.MutationResult; export type UpdateFavoriteArticleFolderMutationMutationOptions = Apollo.BaseMutationOptions; +export const FeedArticleListQueryDocument = gql` + query FeedArticleListQuery($input: ArticlesInput!) { + articles(articlesInput: $input) { + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + edges { + node { + id + ...ArticleCardWrapperFragment + } + } + } +} + ${ArticleCardWrapperFragmentFragmentDoc}`; + +/** + * __useFeedArticleListQueryQuery__ + * + * To run a query within a React component, call `useFeedArticleListQueryQuery` and pass it any options that fit your needs. + * When your component renders, `useFeedArticleListQueryQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useFeedArticleListQueryQuery({ + * variables: { + * input: // value for 'input' + * }, + * }); + */ +export function useFeedArticleListQueryQuery(baseOptions: Apollo.QueryHookOptions & ({ variables: FeedArticleListQueryQueryVariables; skip?: boolean; } | { skip: boolean; }) ) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(FeedArticleListQueryDocument, options); + } +export function useFeedArticleListQueryLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(FeedArticleListQueryDocument, options); + } +export function useFeedArticleListQuerySuspenseQuery(baseOptions?: Apollo.SuspenseQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useSuspenseQuery(FeedArticleListQueryDocument, options); + } +export type FeedArticleListQueryQueryHookResult = ReturnType; +export type FeedArticleListQueryLazyQueryHookResult = ReturnType; +export type FeedArticleListQuerySuspenseQueryHookResult = ReturnType; +export type FeedArticleListQueryQueryResult = Apollo.QueryResult; export const FeedListQueryDocument = gql` query FeedListQuery($input: FeedsInput!) { feeds(feedsInput: $input) { @@ -2275,6 +2351,62 @@ export type FeedListQueryQueryHookResult = ReturnType; export type FeedListQuerySuspenseQueryHookResult = ReturnType; export type FeedListQueryQueryResult = Apollo.QueryResult; +export const FeedArticleListTemplateQueryDocument = gql` + query FeedArticleListTemplateQuery($input: ArticlesInput!, $favoriteArticleFoldersInput: FavoriteArticleFoldersInput!) { + articles(articlesInput: $input) { + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + edges { + node { + id + ...ArticleCardWrapperFragment + } + } + } + favoriteArticleFolders(input: $favoriteArticleFoldersInput) { + ...FavoriteFolderArticleCardWrapperFragment + } +} + ${ArticleCardWrapperFragmentFragmentDoc} +${FavoriteFolderArticleCardWrapperFragmentFragmentDoc}`; + +/** + * __useFeedArticleListTemplateQueryQuery__ + * + * To run a query within a React component, call `useFeedArticleListTemplateQueryQuery` and pass it any options that fit your needs. + * When your component renders, `useFeedArticleListTemplateQueryQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useFeedArticleListTemplateQueryQuery({ + * variables: { + * input: // value for 'input' + * favoriteArticleFoldersInput: // value for 'favoriteArticleFoldersInput' + * }, + * }); + */ +export function useFeedArticleListTemplateQueryQuery(baseOptions: Apollo.QueryHookOptions & ({ variables: FeedArticleListTemplateQueryQueryVariables; skip?: boolean; } | { skip: boolean; }) ) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(FeedArticleListTemplateQueryDocument, options); + } +export function useFeedArticleListTemplateQueryLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(FeedArticleListTemplateQueryDocument, options); + } +export function useFeedArticleListTemplateQuerySuspenseQuery(baseOptions?: Apollo.SuspenseQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useSuspenseQuery(FeedArticleListTemplateQueryDocument, options); + } +export type FeedArticleListTemplateQueryQueryHookResult = ReturnType; +export type FeedArticleListTemplateQueryLazyQueryHookResult = ReturnType; +export type FeedArticleListTemplateQuerySuspenseQueryHookResult = ReturnType; +export type FeedArticleListTemplateQueryQueryResult = Apollo.QueryResult; export const FeedListTemplateQueryDocument = gql` query FeedListTemplateQuery($input: FeedsInput!) { feeds(feedsInput: $input) {