diff options
| author | Armand Philippot <git@armandphilippot.com> | 2023-11-24 20:00:08 +0100 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2023-11-27 14:47:51 +0100 |
| commit | f111685c5886f3e77edfd3621c98d8ac1b9bcce4 (patch) | |
| tree | 62a541fe3afeb64bf745443706fbfb02e96c5230 /src/types | |
| parent | bee515641cb144be9a855ff2cac258d2fedab21d (diff) | |
refactor(services, types): reorganize GraphQL fetchers and data types
The Typescript mapped types was useful for autocompletion in fetchers
but their are harder to maintain. I think it's better to keep each
query close to its fetcher to have a better understanding of the
fetched data. So I:
* colocate queries with their own fetcher
* colocate mutations with their own mutator
* remove Typescript mapped types for queries and mutations
* move data convertors inside graphql services
* rename most of data types and fetchers
Diffstat (limited to 'src/types')
| -rw-r--r-- | src/types/app.ts | 102 | ||||
| -rw-r--r-- | src/types/data.ts | 289 | ||||
| -rw-r--r-- | src/types/gql.ts | 73 | ||||
| -rw-r--r-- | src/types/graphql/generics.ts | 25 | ||||
| -rw-r--r-- | src/types/graphql/index.ts | 3 | ||||
| -rw-r--r-- | src/types/graphql/mutations.ts | 60 | ||||
| -rw-r--r-- | src/types/graphql/queries.ts | 143 | ||||
| -rw-r--r-- | src/types/index.ts | 5 | ||||
| -rw-r--r-- | src/types/mdx.ts | 22 | ||||
| -rw-r--r-- | src/types/raw-data.ts | 111 |
10 files changed, 364 insertions, 469 deletions
diff --git a/src/types/app.ts b/src/types/app.ts index b613e6e..218d63b 100644 --- a/src/types/app.ts +++ b/src/types/app.ts @@ -28,108 +28,6 @@ export type AppPropsWithLayout = AppProps<CustomPageProps> & { Component: NextPageWithLayout; }; -export type ContentKind = - | 'article' - | 'comment' - | 'page' - | 'project' - | 'thematic' - | 'topic'; - -export type Author<T extends ContentKind> = { - avatar?: Image; - description?: T extends 'comment' ? never : string; - name: string; - website?: string; -}; - -export type CommentMeta = { - author: Author<'comment'>; - date: string; -}; - -export type SingleComment = { - approved: boolean; - content: string; - id: number; - meta: CommentMeta; - parentId?: number; - replies: SingleComment[]; -}; - -export type Dates = { - publication: string; - update?: string; -}; - -export type Image = { - alt: string; - height: number; - src: string; - title?: string; - width: number; -}; - -export type Repos = { - github?: string; - gitlab?: string; -}; - -export type SEO = { - description: string; - title: string; -}; - -export type PageKind = Exclude<ContentKind, 'comment'>; - -export type Meta<T extends PageKind> = { - articles?: T extends 'thematic' | 'topic' ? Article[] : never; - author?: T extends 'article' | 'page' ? Author<T> : never; - commentsCount?: T extends 'article' ? number : never; - cover?: Image; - dates: Dates; - license?: T extends 'project' ? string : never; - repos?: T extends 'project' ? Repos : never; - seo: SEO; - tagline?: T extends 'project' ? string : never; - technologies?: T extends 'project' ? string[] : never; - thematics?: T extends 'article' | 'topic' ? PageLink[] : never; - topics?: T extends 'article' | 'thematic' ? PageLink[] : never; - website?: T extends 'topic' ? string : never; - wordsCount: number; -}; - -export type Page<T extends PageKind> = { - content: string; - id: number | string; - intro: string; - meta: Meta<T>; - slug: string; - title: string; -}; - -export type PageLink = { - id: number; - logo?: Image; - name: string; - url: string; -}; - -export type Article = Page<'article'>; -export type ArticleCard = Pick<Article, 'id' | 'slug' | 'title'> & - Pick<Meta<'article'>, 'cover' | 'dates'>; -export type Project = Page<'project'>; -export type ProjectPreview = Omit<Page<'project'>, 'content'>; -export type ProjectCard = Pick<Page<'project'>, 'id' | 'slug' | 'title'> & { - meta: Pick<Meta<'project'>, 'cover' | 'dates' | 'tagline' | 'technologies'>; -}; -export type Thematic = Page<'thematic'>; -export type Topic = Page<'topic'>; - -export type Slug = { - slug: string; -}; - export type Position = 'bottom' | 'center' | 'left' | 'right' | 'top'; /** Spacing keys defined has CSS variables */ diff --git a/src/types/data.ts b/src/types/data.ts new file mode 100644 index 0000000..9a6d674 --- /dev/null +++ b/src/types/data.ts @@ -0,0 +1,289 @@ +import type { StaticImageData } from 'next/image'; +import type { Nullable } from './generics'; +import type { GraphQLNode } from './gql'; + +export type SlugNode = { + slug: string; +}; + +//=========================================================================== +// Data from WordPress +//=========================================================================== + +type WPSeo = { + metaDesc: string; + title: string; +}; + +type WPCommentAuthorAvatar = { + height: number; + url: string; + width: number; +}; + +type WPCommentAuthor = { + avatar: Nullable<WPCommentAuthorAvatar>; + name: string; + url: Nullable<string>; +}; + +export type WPCommentStatus = 'APPROVE' | 'HOLD' | 'SPAM' | 'TRASH'; + +export type WPComment = { + approved: boolean; + author: GraphQLNode<WPCommentAuthor>; + content: string; + databaseId: number; + date: string; + parentDatabaseId: number; + status: WPCommentStatus; +}; + +type WPContentParts = { + afterMore: string; + beforeMore: string; +}; + +export type WPImage = { + altText: Nullable<string>; + mediaDetails: { + height: number; + width: number; + }; + sourceUrl: string; + title: Nullable<string>; +}; + +type WPInfo = { wordsCount: number }; + +type WPContent = { + date: string; + featuredImage: Nullable<GraphQLNode<WPImage>>; + modified: string; + seo: WPSeo; + slug: string; + title: string; +}; + +export type WPPage = WPContent & { + contentParts: WPContentParts; + info: WPInfo; +}; + +type WPPostAuthor = { name: string }; + +type WPAcfPosts = { + postsInThematic: Nullable<WPThematicPreview[]>; + postsInTopic: Nullable<WPTopicPreview[]>; +}; + +export type WPPost = WPContent & { + acfPosts: Nullable<Partial<WPAcfPosts>>; + author: GraphQLNode<WPPostAuthor>; + commentCount: Nullable<number>; + contentParts: WPContentParts; + databaseId: number; + info: WPInfo; +}; + +export type WPPostPreview = Pick< + WPPost, + | 'commentCount' + | 'databaseId' + | 'date' + | 'featuredImage' + | 'info' + | 'modified' + | 'slug' + | 'title' +> & { + acfPosts: + | Nullable<Pick<WPAcfPosts, 'postsInThematic'>> + | Nullable<Pick<WPAcfPosts, 'postsInTopic'>>; + contentParts: Pick<WPContentParts, 'beforeMore'>; +}; + +export type RecentWPPost = Pick< + WPPost, + 'date' | 'featuredImage' | 'slug' | 'title' +> & { + databaseId: number; +}; + +type WPAcfThematics = { + postsInThematic: Nullable<WPPostPreview[]>; +}; + +export type WPThematic = WPContent & { + acfThematics: Nullable<WPAcfThematics>; +}; + +export type WPThematicPreview = Pick<WPThematic, 'slug' | 'title'> & { + databaseId: number; +}; + +type WPAcfTopics = { + officialWebsite: string; + postsInTopic: Nullable<WPPostPreview[]>; +}; + +export type WPTopic = WPContent & { + acfTopics: Nullable<WPAcfTopics>; +}; + +export type WPTopicPreview = Pick< + WPTopic, + 'featuredImage' | 'slug' | 'title' +> & { + databaseId: number; +}; + +//=========================================================================== +// Data from MDX files +//=========================================================================== + +export type MDXData = { + file: string; + image: MDXImage; +}; + +export type MDXImage = StaticImageData & { + alt: string; + title?: string; +}; + +export type MDXPageMeta = Omit<PageMeta, 'wordsCount'> & { + intro: string; + title: string; +}; + +export type MDXProjectMeta = Omit<ProjectMeta, 'wordsCount'> & { + intro: string; + title: string; +}; + +//=========================================================================== +// Data used in this application +//=========================================================================== + +export type Dates = { + publication: string; + update?: string; +}; + +export type SEO = { + description: string; + title: string; +}; + +export type Img = { + alt: string; + height: number; + src: string; + title?: string; + width: number; +}; + +export type CommentAuthor = { + avatar?: Omit<Img, 'title'>; + name: string; + website?: string; +}; + +export type CommentMeta = { + author: CommentAuthor; + date: string; +}; + +export type SingleComment = { + content: string; + id: number; + isApproved: boolean; + meta: CommentMeta; + parentId?: number; + replies: SingleComment[]; +}; + +export type PageMeta = { + cover?: Img; + dates: Dates; + seo: SEO; + wordsCount: number; +}; + +export type Page = { + content: string; + intro: string; + slug: string; + title: string; +}; + +export type PageLink = { + id: number; + logo?: Img; + name: string; + url: string; +}; + +type ArticleMeta = PageMeta & { + author?: string; + commentsCount?: number; + thematics?: PageLink[]; + topics?: PageLink[]; +}; + +export type Article = Page & { + id: number; + meta: ArticleMeta; +}; + +export type ArticlePreview = Pick<Article, 'intro' | 'slug' | 'title'> & { + id: number; + meta: Omit<ArticleMeta, 'author' | 'seo' | 'topics'>; +}; + +export type RecentArticle = Pick<Article, 'slug' | 'title'> & + Pick<ArticleMeta, 'cover'> & { + id: number; + publicationDate: string; + }; + +export type Repos = { + github?: string; + gitlab?: string; +}; + +export type ProjectMeta = Omit<PageMeta, 'wordsCount'> & { + license?: string; + repos?: Repos; + tagline?: string; + technologies?: string[]; +}; + +export type Project = Omit<Page, 'content'> & { + id: string; + meta: ProjectMeta; +}; + +export type ProjectPreview = Omit<Project, 'meta'> & { + meta: Omit<ProjectMeta, 'license' | 'repos'>; +}; + +export type ThematicMeta = PageMeta & { + articles?: ArticlePreview[]; + topics?: PageLink[]; +}; + +export type Thematic = Page & { + meta: ThematicMeta; +}; + +export type TopicMeta = PageMeta & { + articles?: ArticlePreview[]; + thematics?: PageLink[]; + website?: string; +}; + +export type Topic = Page & { + meta: TopicMeta; +}; diff --git a/src/types/gql.ts b/src/types/gql.ts new file mode 100644 index 0000000..cec66c6 --- /dev/null +++ b/src/types/gql.ts @@ -0,0 +1,73 @@ +import type { WPCommentStatus } from './data'; +import type { Nullable } from './generics'; + +export type GraphQLNode<T> = { + node: T; +}; + +export type GraphQLNodes<T> = { + nodes: T[]; +}; + +export type GraphQLPageInfo = { + endCursor: Nullable<string>; + hasNextPage: boolean; + hasPreviousPage: boolean; + startCursor: Nullable<string>; + total: number; +}; + +export type GraphQLEdge<T> = GraphQLNode<T> & { + cursor: string; +}; + +export type GraphQLConnection<T> = { + edges: GraphQLEdge<T>[]; + pageInfo: GraphQLPageInfo; +}; + +export type GraphQLEdgesInput = { + after?: Nullable<string>; + before?: Nullable<string>; + first?: number; + last?: number; +}; + +export type GraphQLOrder = 'ASC' | 'DESC'; + +export type GraphQLCommentWhere = { + contentId?: number; + contentName?: string; + status?: WPCommentStatus; +}; + +type GraphQLPostFieldOrder = + | 'AUTHOR' + | 'COMMENT_COUNT' + | 'DATE' + | 'MODIFIED' + | 'SLUG' + | 'TITLE'; + +export type GraphQLPostOrderBy = { + field: GraphQLPostFieldOrder; + order: GraphQLOrder; +}; + +export type GraphQLPostWhere = { + authorName?: string; + search?: string; + title?: string; +}; + +export type GraphQLTaxonomyFieldOrder = 'DATE' | 'MODIFIED' | 'SLUG' | 'TITLE'; + +export type GraphQLTaxonomyOrderBy = { + field: GraphQLTaxonomyFieldOrder; + order: GraphQLOrder; +}; + +export type GraphQLTaxonomyWhere = { + search?: string; + title?: string; +}; diff --git a/src/types/graphql/generics.ts b/src/types/graphql/generics.ts deleted file mode 100644 index dec5f10..0000000 --- a/src/types/graphql/generics.ts +++ /dev/null @@ -1,25 +0,0 @@ -export type GraphQLPageInfo = { - endCursor: string; - hasNextPage: boolean; - total: number; -}; - -export type GraphQLEdges<T> = { - cursor: string; - node: T; -}; - -export type GraphQLEdgesInput = { - after?: string; - before?: string; - first?: number; - last?: number; -}; - -export type GraphQLNode<T> = { - node: T; -}; - -export type GraphQLNodes<T> = { - nodes: T[]; -}; diff --git a/src/types/graphql/index.ts b/src/types/graphql/index.ts deleted file mode 100644 index 79eb05e..0000000 --- a/src/types/graphql/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './generics'; -export * from './mutations'; -export * from './queries'; diff --git a/src/types/graphql/mutations.ts b/src/types/graphql/mutations.ts deleted file mode 100644 index 6ff066c..0000000 --- a/src/types/graphql/mutations.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { sendCommentMutation, sendMailMutation } from '../../services/graphql'; - -//=========================================================================== -// Existing mutations list -//=========================================================================== - -export type Mutations = typeof sendMailMutation | typeof sendCommentMutation; - -//=========================================================================== -// Mutations response types -//=========================================================================== - -export type SendCommentResponse<T> = { - createComment: T; -}; - -export type SendMailResponse<T> = { - sendEmail: T; -}; - -export type MutationsResponseMap<T> = { - [sendCommentMutation]: SendCommentResponse<T>; - [sendMailMutation]: SendMailResponse<T>; -}; - -export type Approved = { - approved: boolean; -}; - -export type SentComment = { - clientMutationId: string; - success: boolean; - comment: Approved | null; -}; - -//=========================================================================== -// Mutations input types -//=========================================================================== - -export type SendCommentInput = { - author: string; - authorEmail: string; - authorUrl: string; - clientMutationId: string; - commentOn: number; - content: string; - parent?: number; -}; - -export type SendMailInput = { - body: string; - clientMutationId: string; - replyTo: string; - subject: string; -}; - -export type MutationsInputMap = { - [sendCommentMutation]: SendCommentInput; - [sendMailMutation]: SendMailInput; -}; diff --git a/src/types/graphql/queries.ts b/src/types/graphql/queries.ts deleted file mode 100644 index 83e9c67..0000000 --- a/src/types/graphql/queries.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { - articleBySlugQuery, - articlesCardQuery, - articlesEndCursorQuery, - articlesQuery, - articlesSlugQuery, - commentsQuery, - thematicBySlugQuery, - thematicsListQuery, - thematicsSlugQuery, - topicBySlugQuery, - topicsListQuery, - topicsSlugQuery, - totalArticlesQuery, - totalThematicsQuery, - totalTopicsQuery, -} from '../../services/graphql'; -import { Slug } from '../app'; -import { RawComment } from '../raw-data'; -import { - GraphQLEdges, - GraphQLEdgesInput, - GraphQLNodes, - GraphQLPageInfo, -} from './generics'; - -//=========================================================================== -// Existing queries list -//=========================================================================== - -export type Queries = - | typeof articlesQuery - | typeof articleBySlugQuery - | typeof articlesCardQuery - | typeof articlesEndCursorQuery - | typeof articlesSlugQuery - | typeof commentsQuery - | typeof thematicBySlugQuery - | typeof thematicsListQuery - | typeof thematicsSlugQuery - | typeof topicBySlugQuery - | typeof topicsListQuery - | typeof topicsSlugQuery - | typeof totalArticlesQuery - | typeof totalThematicsQuery - | typeof totalTopicsQuery; - -//=========================================================================== -// Queries response types -//=========================================================================== - -export type ArticleResponse<T> = { - post: T; -}; - -export type ArticlesResponse<T> = { - posts: T; -}; - -export type CommentsResponse<T> = { - comments: T; -}; - -export type ThematicResponse<T> = { - thematic: T; -}; - -export type ThematicsResponse<T> = { - thematics: T; -}; - -export type TopicResponse<T> = { - topic: T; -}; - -export type TopicsResponse<T> = { - topics: T; -}; - -export type EdgesResponse<T> = { - edges: GraphQLEdges<T>[]; - pageInfo: GraphQLPageInfo; -}; - -export type EndCursorResponse = { - pageInfo: Pick<GraphQLPageInfo, 'endCursor'>; -}; - -export type QueriesResponseMap<T> = { - [articleBySlugQuery]: ArticleResponse<T>; - [articlesCardQuery]: ArticlesResponse<GraphQLNodes<T>>; - [articlesEndCursorQuery]: ArticlesResponse<EndCursorResponse>; - [articlesQuery]: ArticlesResponse<EdgesResponse<T>>; - [articlesSlugQuery]: ArticlesResponse<EdgesResponse<T>>; - [commentsQuery]: CommentsResponse<EdgesResponse<T>>; - [thematicBySlugQuery]: ThematicResponse<T>; - [thematicsListQuery]: ThematicsResponse<EdgesResponse<T>>; - [thematicsSlugQuery]: ThematicsResponse<EdgesResponse<T>>; - [topicBySlugQuery]: TopicResponse<T>; - [topicsListQuery]: TopicsResponse<EdgesResponse<T>>; - [topicsSlugQuery]: TopicsResponse<EdgesResponse<T>>; - [totalArticlesQuery]: ArticlesResponse<T>; - [totalThematicsQuery]: ThematicsResponse<T>; - [totalTopicsQuery]: TopicsResponse<T>; -}; - -//=========================================================================== -// Queries input types -//=========================================================================== - -export type QueryEdges = Pick<GraphQLEdgesInput, 'after' | 'first'>; - -export type ContentId = { - contentId: number; -}; - -export type Search = { - search?: string; -}; - -export type QueriesInputMap = { - [articleBySlugQuery]: Slug; - [articlesCardQuery]: QueryEdges & Search; - [articlesEndCursorQuery]: QueryEdges & Search; - [articlesQuery]: QueryEdges & Search; - [articlesSlugQuery]: QueryEdges & Search; - [commentsQuery]: ContentId & QueryEdges; - [thematicBySlugQuery]: Slug; - [thematicsListQuery]: QueryEdges & Search; - [thematicsSlugQuery]: QueryEdges & Search; - [topicBySlugQuery]: Slug; - [topicsListQuery]: QueryEdges & Search; - [topicsSlugQuery]: QueryEdges & Search; - [totalArticlesQuery]: Search; - [totalThematicsQuery]: null; - [totalTopicsQuery]: null; -}; - -export type CommentPage = { - comments: RawComment[]; - hasNextPage: boolean; - endCursor: string; -}; diff --git a/src/types/index.ts b/src/types/index.ts index e2f0f55..d6e4a6a 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,6 +1,5 @@ export * from './app'; +export * from './data'; export * from './generics'; -export * from './graphql'; -export * from './mdx'; -export * from './raw-data'; +export * from './gql'; export * from './swr'; diff --git a/src/types/mdx.ts b/src/types/mdx.ts deleted file mode 100644 index 7645ce6..0000000 --- a/src/types/mdx.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { StaticImageData } from 'next/image'; -import { Meta } from './app'; - -export type MDXData = { - file: string; - image: MDXImage; -}; - -export type MDXImage = StaticImageData & { - alt: string; - title?: string; -}; - -export type MDXPageMeta = Pick<Meta<'page'>, 'cover' | 'dates' | 'seo'> & { - intro: string; - title: string; -}; - -export type MDXProjectMeta = Exclude<Meta<'project'>, 'wordsCount'> & { - intro: string; - title: string; -}; diff --git a/src/types/raw-data.ts b/src/types/raw-data.ts deleted file mode 100644 index 022016e..0000000 --- a/src/types/raw-data.ts +++ /dev/null @@ -1,111 +0,0 @@ -/** - * Types for raw data coming from GraphQL API. - */ - -import { ContentKind } from './app'; -import { GraphQLNode, GraphQLPageInfo } from './graphql/generics'; - -export type ACFPosts = { - postsInThematic?: RawThematicPreview[]; - postsInTopic?: RawTopicPreview[]; -}; - -export type ACFThematics = { - postsInThematic: RawArticle[]; -}; - -export type ACFTopics = { - officialWebsite: string; - postsInTopic: RawArticle[]; -}; - -export type ContentParts = { - afterMore: string; - beforeMore: string; -}; - -export type Info = { - wordsCount: number; -}; - -export type RawAuthor<T extends ContentKind> = { - description?: T extends 'comment' ? never : string; - gravatarUrl?: string; - name: string; - url?: string; -}; - -export type RawComment = { - approved: boolean; - author: GraphQLNode<RawAuthor<'comment'>>; - content: string; - databaseId: number; - date: string; - parentDatabaseId: number; -}; - -export type RawCommentsPage = { - comments: RawComment[]; - hasNextPage: boolean; - endCursor: string; -}; - -export type RawCover = { - altText: string; - mediaDetails: { - width: number; - height: number; - }; - sourceUrl: string; - title?: string; -}; - -export type RawArticle = RawPage & { - acfPosts: ACFPosts; - commentCount: number | null; -}; - -export type RawArticlePreview = Pick< - RawArticle, - 'databaseId' | 'date' | 'featuredImage' | 'slug' | 'title' ->; - -export type RawPage = { - author?: GraphQLNode<RawAuthor<'page'>>; - contentParts: ContentParts; - databaseId: number; - date: string; - featuredImage: GraphQLNode<RawCover> | null; - info: Info; - modified: string; - seo?: RawSEO; - slug: string; - title: string; -}; - -export type RawSEO = { - metaDesc: string; - title: string; -}; - -export type RawThematic = RawPage & { - acfThematics: ACFThematics; -}; - -export type RawThematicPreview = Pick< - RawThematic, - 'databaseId' | 'featuredImage' | 'slug' | 'title' ->; - -export type RawTopic = RawPage & { - acfTopics: ACFTopics; -}; - -export type RawTopicPreview = Pick< - RawTopic, - 'databaseId' | 'featuredImage' | 'slug' | 'title' ->; - -export type TotalItems = { - pageInfo: Pick<GraphQLPageInfo, 'total'>; -}; |
