├── .eslintrc.json ├── .gitignore ├── README.md ├── clients └── api.ts ├── codegen.ts ├── components └── FeedCard │ ├── Layout │ └── TwitterLayout.tsx │ └── index.tsx ├── gql ├── fragment-masking.ts ├── gql.ts ├── graphql.ts └── index.ts ├── graphql.schema.json ├── graphql ├── mutation │ ├── tweet.ts │ └── user.ts └── query │ ├── tweet.ts │ └── user.ts ├── hooks ├── tweet.ts └── user.ts ├── next.config.js ├── package.json ├── pages ├── [id].tsx ├── _app.tsx ├── _document.tsx ├── api │ └── hello.ts └── index.tsx ├── postcss.config.js ├── public ├── favicon.ico ├── next.svg └── vercel.svg ├── styles └── globals.css ├── tailwind.config.js ├── tsconfig.json └── yarn.lock /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | 30 | # vercel 31 | .vercel 32 | 33 | # typescript 34 | *.tsbuildinfo 35 | next-env.d.ts 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | ``` 14 | 15 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 16 | 17 | You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file. 18 | 19 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`. 20 | 21 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. 22 | 23 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 24 | 25 | ## Learn More 26 | 27 | To learn more about Next.js, take a look at the following resources: 28 | 29 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 30 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 31 | 32 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 33 | 34 | ## Deploy on Vercel 35 | 36 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 37 | 38 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 39 | -------------------------------------------------------------------------------- /clients/api.ts: -------------------------------------------------------------------------------- 1 | import { GraphQLClient } from "graphql-request"; 2 | 3 | const isClient = typeof window !== "undefined"; 4 | 5 | export const graphqlClient = new GraphQLClient( 6 | process.env.NEXT_PUBLIC_API_URL as string, 7 | { 8 | headers: () => ({ 9 | Authorization: isClient 10 | ? `Bearer ${window.localStorage.getItem("__twitter_token")}` 11 | : "", 12 | }), 13 | } 14 | ); 15 | -------------------------------------------------------------------------------- /codegen.ts: -------------------------------------------------------------------------------- 1 | import type { CodegenConfig } from "@graphql-codegen/cli"; 2 | 3 | const config: CodegenConfig = { 4 | overwrite: true, 5 | schema: "https://d170eyh2z0r1e0.cloudfront.net/graphql", 6 | documents: "**/*.{tsx,ts}", 7 | generates: { 8 | "gql/": { 9 | preset: "client", 10 | plugins: [], 11 | }, 12 | "./graphql.schema.json": { 13 | plugins: ["introspection"], 14 | }, 15 | }, 16 | }; 17 | 18 | export default config; 19 | -------------------------------------------------------------------------------- /components/FeedCard/Layout/TwitterLayout.tsx: -------------------------------------------------------------------------------- 1 | import { useCurrentUser } from "@/hooks/user"; 2 | import React, { useCallback, useMemo } from "react"; 3 | import Image from "next/image"; 4 | import { BiHash, BiHomeCircle, BiMoney, BiUser } from "react-icons/bi"; 5 | import { BsBell, BsBookmark, BsEnvelope, BsTwitter } from "react-icons/bs"; 6 | import { SlOptions } from "react-icons/sl"; 7 | import { CredentialResponse, GoogleLogin } from "@react-oauth/google"; 8 | import toast from "react-hot-toast"; 9 | import { graphqlClient } from "@/clients/api"; 10 | import { verifyUserGoogleTokenQuery } from "@/graphql/query/user"; 11 | import { useQueryClient } from "@tanstack/react-query"; 12 | import Link from "next/link"; 13 | 14 | interface TwitterSidebarButton { 15 | title: string; 16 | icon: React.ReactNode; 17 | link: string; 18 | } 19 | 20 | interface TwitterlayoutProps { 21 | children: React.ReactNode; 22 | } 23 | 24 | const Twitterlayout: React.FC = (props) => { 25 | const { user } = useCurrentUser(); 26 | const queryClient = useQueryClient(); 27 | 28 | const sidebarMenuItems: TwitterSidebarButton[] = useMemo( 29 | () => [ 30 | { 31 | title: "Home", 32 | icon: , 33 | link: "/", 34 | }, 35 | { 36 | title: "Explore", 37 | icon: , 38 | link: "/", 39 | }, 40 | { 41 | title: "Notifications", 42 | icon: , 43 | link: "/", 44 | }, 45 | { 46 | title: "Messages", 47 | icon: , 48 | link: "/", 49 | }, 50 | { 51 | title: "Bookmarks", 52 | icon: , 53 | link: "/", 54 | }, 55 | { 56 | title: "Twitter Blue", 57 | icon: , 58 | link: "/", 59 | }, 60 | { 61 | title: "Profile", 62 | icon: , 63 | link: `/${user?.id}`, 64 | }, 65 | { 66 | title: "More Options", 67 | icon: , 68 | link: "/", 69 | }, 70 | ], 71 | [user?.id] 72 | ); 73 | 74 | const handleLoginWithGoogle = useCallback( 75 | async (cred: CredentialResponse) => { 76 | const googleToken = cred.credential; 77 | if (!googleToken) return toast.error(`Google token not found`); 78 | 79 | const { verifyGoogleToken } = await graphqlClient.request( 80 | verifyUserGoogleTokenQuery, 81 | { token: googleToken } 82 | ); 83 | 84 | toast.success("Verified Success"); 85 | console.log(verifyGoogleToken); 86 | 87 | if (verifyGoogleToken) 88 | window.localStorage.setItem("__twitter_token", verifyGoogleToken); 89 | 90 | await queryClient.invalidateQueries(["curent-user"]); 91 | }, 92 | [queryClient] 93 | ); 94 | 95 | return ( 96 |
97 |
98 |
99 |
100 |
101 | 102 |
103 |
104 |
    105 | {sidebarMenuItems.map((item) => ( 106 |
  • 107 | 111 | {item.icon} 112 | {item.title} 113 | 114 |
  • 115 | ))} 116 |
117 |
118 | 121 | 124 |
125 |
126 |
127 | {user && ( 128 |
129 | {user && user.profileImageURL && ( 130 | user-image 137 | )} 138 |
139 |

140 | {user.firstName} {user.lastName} 141 |

142 |
143 |
144 | )} 145 |
146 |
147 | {props.children} 148 |
149 |
150 | {!user ? ( 151 |
152 |

New to Twitter?

153 | 154 |
155 | ) : ( 156 |
157 |

Users you may know

158 | {user?.recommendedUsers?.map((el) => ( 159 |
160 | {el?.profileImageURL && ( 161 | user-image 168 | )} 169 |
170 |
171 | {el?.firstName} {el?.lastName} 172 |
173 | 177 | View 178 | 179 |
180 |
181 | ))} 182 |
183 | )} 184 |
185 |
186 |
187 | ); 188 | }; 189 | 190 | export default Twitterlayout; 191 | -------------------------------------------------------------------------------- /components/FeedCard/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Image from "next/image"; 3 | import { BiMessageRounded, BiUpload } from "react-icons/bi"; 4 | import { FaRetweet } from "react-icons/fa"; 5 | import { AiOutlineHeart } from "react-icons/ai"; 6 | import { Tweet } from "@/gql/graphql"; 7 | import Link from "next/link"; 8 | 9 | interface FeedCardProps { 10 | data: Tweet; 11 | } 12 | 13 | const FeedCard: React.FC = (props) => { 14 | const { data } = props; 15 | 16 | return ( 17 |
18 |
19 |
20 | {data.author?.profileImageURL && ( 21 | user-image 28 | )} 29 |
30 |
31 |
32 | 33 | {data.author?.firstName} {data.author?.lastName} 34 | 35 |
36 |

{data.content}

37 | {data.imageURL && ( 38 | image 39 | )} 40 |
41 |
42 | 43 |
44 |
45 | 46 |
47 |
48 | 49 |
50 |
51 | 52 |
53 |
54 |
55 |
56 |
57 | ); 58 | }; 59 | 60 | export default FeedCard; 61 | -------------------------------------------------------------------------------- /gql/fragment-masking.ts: -------------------------------------------------------------------------------- 1 | import { ResultOf, DocumentTypeDecoration, } from '@graphql-typed-document-node/core'; 2 | 3 | 4 | export type FragmentType> = TDocumentType extends DocumentTypeDecoration< 5 | infer TType, 6 | any 7 | > 8 | ? TType extends { ' $fragmentName'?: infer TKey } 9 | ? TKey extends string 10 | ? { ' $fragmentRefs'?: { [key in TKey]: TType } } 11 | : never 12 | : never 13 | : never; 14 | 15 | // return non-nullable if `fragmentType` is non-nullable 16 | export function useFragment( 17 | _documentNode: DocumentTypeDecoration, 18 | fragmentType: FragmentType> 19 | ): TType; 20 | // return nullable if `fragmentType` is nullable 21 | export function useFragment( 22 | _documentNode: DocumentTypeDecoration, 23 | fragmentType: FragmentType> | null | undefined 24 | ): TType | null | undefined; 25 | // return array of non-nullable if `fragmentType` is array of non-nullable 26 | export function useFragment( 27 | _documentNode: DocumentTypeDecoration, 28 | fragmentType: ReadonlyArray>> 29 | ): ReadonlyArray; 30 | // return array of nullable if `fragmentType` is array of nullable 31 | export function useFragment( 32 | _documentNode: DocumentTypeDecoration, 33 | fragmentType: ReadonlyArray>> | null | undefined 34 | ): ReadonlyArray | null | undefined; 35 | export function useFragment( 36 | _documentNode: DocumentTypeDecoration, 37 | fragmentType: FragmentType> | ReadonlyArray>> | null | undefined 38 | ): TType | ReadonlyArray | null | undefined { 39 | return fragmentType as any; 40 | } 41 | 42 | 43 | export function makeFragmentData< 44 | F extends DocumentTypeDecoration, 45 | FT extends ResultOf 46 | >(data: FT, _fragment: F): FragmentType { 47 | return data as FragmentType; 48 | } -------------------------------------------------------------------------------- /gql/gql.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import * as types from './graphql'; 3 | import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; 4 | 5 | /** 6 | * Map of all GraphQL operations in the project. 7 | * 8 | * This map has several performance disadvantages: 9 | * 1. It is not tree-shakeable, so it will include all operations in the project. 10 | * 2. It is not minifiable, so the string of a GraphQL query will be multiple times inside the bundle. 11 | * 3. It does not support dead code elimination, so it will add unused operations. 12 | * 13 | * Therefore it is highly recommended to use the babel or swc plugin for production. 14 | */ 15 | const documents = { 16 | "\n #graphql\n mutation CreateTweet($payload: CreateTweetData!) {\n createTweet(payload: $payload) {\n id\n }\n }\n": types.CreateTweetDocument, 17 | "\n #graphql\n mutation FollowUser($to: ID!) {\n followUser(to: $to)\n }\n": types.FollowUserDocument, 18 | "\n #graphql\n mutation UnfollowUser($to: ID!) {\n unfollowUser(to: $to)\n }\n": types.UnfollowUserDocument, 19 | "\n #graphql\n\n query GetAllTweets {\n getAllTweets {\n id\n content\n imageURL\n author {\n id\n firstName\n lastName\n profileImageURL\n }\n }\n }\n": types.GetAllTweetsDocument, 20 | "\n query GetSignedURL($imageName: String!, $imageType: String!) {\n getSignedURLForTweet(imageName: $imageName, imageType: $imageType)\n }\n": types.GetSignedUrlDocument, 21 | "\n #graphql\n query VerifyUserGoogleToken($token: String!) {\n verifyGoogleToken(token: $token)\n }\n": types.VerifyUserGoogleTokenDocument, 22 | "\n query GetCurrentUser {\n getCurrentUser {\n id\n profileImageURL\n email\n firstName\n lastName\n recommendedUsers {\n id\n firstName\n lastName\n profileImageURL\n }\n followers {\n id\n firstName\n lastName\n profileImageURL\n }\n following {\n id\n firstName\n lastName\n profileImageURL\n }\n tweets {\n id\n content\n author {\n id\n firstName\n lastName\n profileImageURL\n }\n }\n }\n }\n": types.GetCurrentUserDocument, 23 | "\n #graphql\n query GetuserById($id: ID!) {\n getUserById(id: $id) {\n id\n firstName\n lastName\n profileImageURL\n\n followers {\n id\n firstName\n lastName\n profileImageURL\n }\n following {\n id\n firstName\n lastName\n profileImageURL\n }\n tweets {\n content\n id\n author {\n id\n firstName\n lastName\n profileImageURL\n }\n }\n }\n }\n": types.GetuserByIdDocument, 24 | }; 25 | 26 | /** 27 | * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. 28 | * 29 | * 30 | * @example 31 | * ```ts 32 | * const query = graphql(`query GetUser($id: ID!) { user(id: $id) { name } }`); 33 | * ``` 34 | * 35 | * The query argument is unknown! 36 | * Please regenerate the types. 37 | */ 38 | export function graphql(source: string): unknown; 39 | 40 | /** 41 | * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. 42 | */ 43 | export function graphql(source: "\n #graphql\n mutation CreateTweet($payload: CreateTweetData!) {\n createTweet(payload: $payload) {\n id\n }\n }\n"): (typeof documents)["\n #graphql\n mutation CreateTweet($payload: CreateTweetData!) {\n createTweet(payload: $payload) {\n id\n }\n }\n"]; 44 | /** 45 | * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. 46 | */ 47 | export function graphql(source: "\n #graphql\n mutation FollowUser($to: ID!) {\n followUser(to: $to)\n }\n"): (typeof documents)["\n #graphql\n mutation FollowUser($to: ID!) {\n followUser(to: $to)\n }\n"]; 48 | /** 49 | * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. 50 | */ 51 | export function graphql(source: "\n #graphql\n mutation UnfollowUser($to: ID!) {\n unfollowUser(to: $to)\n }\n"): (typeof documents)["\n #graphql\n mutation UnfollowUser($to: ID!) {\n unfollowUser(to: $to)\n }\n"]; 52 | /** 53 | * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. 54 | */ 55 | export function graphql(source: "\n #graphql\n\n query GetAllTweets {\n getAllTweets {\n id\n content\n imageURL\n author {\n id\n firstName\n lastName\n profileImageURL\n }\n }\n }\n"): (typeof documents)["\n #graphql\n\n query GetAllTweets {\n getAllTweets {\n id\n content\n imageURL\n author {\n id\n firstName\n lastName\n profileImageURL\n }\n }\n }\n"]; 56 | /** 57 | * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. 58 | */ 59 | export function graphql(source: "\n query GetSignedURL($imageName: String!, $imageType: String!) {\n getSignedURLForTweet(imageName: $imageName, imageType: $imageType)\n }\n"): (typeof documents)["\n query GetSignedURL($imageName: String!, $imageType: String!) {\n getSignedURLForTweet(imageName: $imageName, imageType: $imageType)\n }\n"]; 60 | /** 61 | * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. 62 | */ 63 | export function graphql(source: "\n #graphql\n query VerifyUserGoogleToken($token: String!) {\n verifyGoogleToken(token: $token)\n }\n"): (typeof documents)["\n #graphql\n query VerifyUserGoogleToken($token: String!) {\n verifyGoogleToken(token: $token)\n }\n"]; 64 | /** 65 | * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. 66 | */ 67 | export function graphql(source: "\n query GetCurrentUser {\n getCurrentUser {\n id\n profileImageURL\n email\n firstName\n lastName\n recommendedUsers {\n id\n firstName\n lastName\n profileImageURL\n }\n followers {\n id\n firstName\n lastName\n profileImageURL\n }\n following {\n id\n firstName\n lastName\n profileImageURL\n }\n tweets {\n id\n content\n author {\n id\n firstName\n lastName\n profileImageURL\n }\n }\n }\n }\n"): (typeof documents)["\n query GetCurrentUser {\n getCurrentUser {\n id\n profileImageURL\n email\n firstName\n lastName\n recommendedUsers {\n id\n firstName\n lastName\n profileImageURL\n }\n followers {\n id\n firstName\n lastName\n profileImageURL\n }\n following {\n id\n firstName\n lastName\n profileImageURL\n }\n tweets {\n id\n content\n author {\n id\n firstName\n lastName\n profileImageURL\n }\n }\n }\n }\n"]; 68 | /** 69 | * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. 70 | */ 71 | export function graphql(source: "\n #graphql\n query GetuserById($id: ID!) {\n getUserById(id: $id) {\n id\n firstName\n lastName\n profileImageURL\n\n followers {\n id\n firstName\n lastName\n profileImageURL\n }\n following {\n id\n firstName\n lastName\n profileImageURL\n }\n tweets {\n content\n id\n author {\n id\n firstName\n lastName\n profileImageURL\n }\n }\n }\n }\n"): (typeof documents)["\n #graphql\n query GetuserById($id: ID!) {\n getUserById(id: $id) {\n id\n firstName\n lastName\n profileImageURL\n\n followers {\n id\n firstName\n lastName\n profileImageURL\n }\n following {\n id\n firstName\n lastName\n profileImageURL\n }\n tweets {\n content\n id\n author {\n id\n firstName\n lastName\n profileImageURL\n }\n }\n }\n }\n"]; 72 | 73 | export function graphql(source: string) { 74 | return (documents as any)[source] ?? {}; 75 | } 76 | 77 | export type DocumentType> = TDocumentNode extends DocumentNode< infer TType, any> ? TType : never; -------------------------------------------------------------------------------- /gql/graphql.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; 3 | export type Maybe = T | null; 4 | export type InputMaybe = Maybe; 5 | export type Exact = { [K in keyof T]: T[K] }; 6 | export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; 7 | export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; 8 | /** All built-in and custom scalars, mapped to their actual values */ 9 | export type Scalars = { 10 | ID: string; 11 | String: string; 12 | Boolean: boolean; 13 | Int: number; 14 | Float: number; 15 | }; 16 | 17 | export type CreateTweetData = { 18 | content: Scalars['String']; 19 | imageURL?: InputMaybe; 20 | }; 21 | 22 | export type Mutation = { 23 | __typename?: 'Mutation'; 24 | createTweet?: Maybe; 25 | followUser?: Maybe; 26 | unfollowUser?: Maybe; 27 | }; 28 | 29 | 30 | export type MutationCreateTweetArgs = { 31 | payload: CreateTweetData; 32 | }; 33 | 34 | 35 | export type MutationFollowUserArgs = { 36 | to: Scalars['ID']; 37 | }; 38 | 39 | 40 | export type MutationUnfollowUserArgs = { 41 | to: Scalars['ID']; 42 | }; 43 | 44 | export type Query = { 45 | __typename?: 'Query'; 46 | getAllTweets?: Maybe>>; 47 | getCurrentUser?: Maybe; 48 | getSignedURLForTweet?: Maybe; 49 | getUserById?: Maybe; 50 | verifyGoogleToken?: Maybe; 51 | }; 52 | 53 | 54 | export type QueryGetSignedUrlForTweetArgs = { 55 | imageName: Scalars['String']; 56 | imageType: Scalars['String']; 57 | }; 58 | 59 | 60 | export type QueryGetUserByIdArgs = { 61 | id: Scalars['ID']; 62 | }; 63 | 64 | 65 | export type QueryVerifyGoogleTokenArgs = { 66 | token: Scalars['String']; 67 | }; 68 | 69 | export type Tweet = { 70 | __typename?: 'Tweet'; 71 | author?: Maybe; 72 | content: Scalars['String']; 73 | id: Scalars['ID']; 74 | imageURL?: Maybe; 75 | }; 76 | 77 | export type User = { 78 | __typename?: 'User'; 79 | email: Scalars['String']; 80 | firstName: Scalars['String']; 81 | followers?: Maybe>>; 82 | following?: Maybe>>; 83 | id: Scalars['ID']; 84 | lastName?: Maybe; 85 | profileImageURL?: Maybe; 86 | recommendedUsers?: Maybe>>; 87 | tweets?: Maybe>>; 88 | }; 89 | 90 | export type CreateTweetMutationVariables = Exact<{ 91 | payload: CreateTweetData; 92 | }>; 93 | 94 | 95 | export type CreateTweetMutation = { __typename?: 'Mutation', createTweet?: { __typename?: 'Tweet', id: string } | null }; 96 | 97 | export type FollowUserMutationVariables = Exact<{ 98 | to: Scalars['ID']; 99 | }>; 100 | 101 | 102 | export type FollowUserMutation = { __typename?: 'Mutation', followUser?: boolean | null }; 103 | 104 | export type UnfollowUserMutationVariables = Exact<{ 105 | to: Scalars['ID']; 106 | }>; 107 | 108 | 109 | export type UnfollowUserMutation = { __typename?: 'Mutation', unfollowUser?: boolean | null }; 110 | 111 | export type GetAllTweetsQueryVariables = Exact<{ [key: string]: never; }>; 112 | 113 | 114 | export type GetAllTweetsQuery = { __typename?: 'Query', getAllTweets?: Array<{ __typename?: 'Tweet', id: string, content: string, imageURL?: string | null, author?: { __typename?: 'User', id: string, firstName: string, lastName?: string | null, profileImageURL?: string | null } | null } | null> | null }; 115 | 116 | export type GetSignedUrlQueryVariables = Exact<{ 117 | imageName: Scalars['String']; 118 | imageType: Scalars['String']; 119 | }>; 120 | 121 | 122 | export type GetSignedUrlQuery = { __typename?: 'Query', getSignedURLForTweet?: string | null }; 123 | 124 | export type VerifyUserGoogleTokenQueryVariables = Exact<{ 125 | token: Scalars['String']; 126 | }>; 127 | 128 | 129 | export type VerifyUserGoogleTokenQuery = { __typename?: 'Query', verifyGoogleToken?: string | null }; 130 | 131 | export type GetCurrentUserQueryVariables = Exact<{ [key: string]: never; }>; 132 | 133 | 134 | export type GetCurrentUserQuery = { __typename?: 'Query', getCurrentUser?: { __typename?: 'User', id: string, profileImageURL?: string | null, email: string, firstName: string, lastName?: string | null, recommendedUsers?: Array<{ __typename?: 'User', id: string, firstName: string, lastName?: string | null, profileImageURL?: string | null } | null> | null, followers?: Array<{ __typename?: 'User', id: string, firstName: string, lastName?: string | null, profileImageURL?: string | null } | null> | null, following?: Array<{ __typename?: 'User', id: string, firstName: string, lastName?: string | null, profileImageURL?: string | null } | null> | null, tweets?: Array<{ __typename?: 'Tweet', id: string, content: string, author?: { __typename?: 'User', id: string, firstName: string, lastName?: string | null, profileImageURL?: string | null } | null } | null> | null } | null }; 135 | 136 | export type GetuserByIdQueryVariables = Exact<{ 137 | id: Scalars['ID']; 138 | }>; 139 | 140 | 141 | export type GetuserByIdQuery = { __typename?: 'Query', getUserById?: { __typename?: 'User', id: string, firstName: string, lastName?: string | null, profileImageURL?: string | null, followers?: Array<{ __typename?: 'User', id: string, firstName: string, lastName?: string | null, profileImageURL?: string | null } | null> | null, following?: Array<{ __typename?: 'User', id: string, firstName: string, lastName?: string | null, profileImageURL?: string | null } | null> | null, tweets?: Array<{ __typename?: 'Tweet', content: string, id: string, author?: { __typename?: 'User', id: string, firstName: string, lastName?: string | null, profileImageURL?: string | null } | null } | null> | null } | null }; 142 | 143 | 144 | export const CreateTweetDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateTweet"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"payload"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateTweetData"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createTweet"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"payload"},"value":{"kind":"Variable","name":{"kind":"Name","value":"payload"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode; 145 | export const FollowUserDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"FollowUser"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"to"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"followUser"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"to"},"value":{"kind":"Variable","name":{"kind":"Name","value":"to"}}}]}]}}]} as unknown as DocumentNode; 146 | export const UnfollowUserDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UnfollowUser"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"to"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"unfollowUser"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"to"},"value":{"kind":"Variable","name":{"kind":"Name","value":"to"}}}]}]}}]} as unknown as DocumentNode; 147 | export const GetAllTweetsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAllTweets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getAllTweets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"content"}},{"kind":"Field","name":{"kind":"Name","value":"imageURL"}},{"kind":"Field","name":{"kind":"Name","value":"author"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"profileImageURL"}}]}}]}}]}}]} as unknown as DocumentNode; 148 | export const GetSignedUrlDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSignedURL"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"imageName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"imageType"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getSignedURLForTweet"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"imageName"},"value":{"kind":"Variable","name":{"kind":"Name","value":"imageName"}}},{"kind":"Argument","name":{"kind":"Name","value":"imageType"},"value":{"kind":"Variable","name":{"kind":"Name","value":"imageType"}}}]}]}}]} as unknown as DocumentNode; 149 | export const VerifyUserGoogleTokenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"VerifyUserGoogleToken"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"token"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"verifyGoogleToken"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"token"},"value":{"kind":"Variable","name":{"kind":"Name","value":"token"}}}]}]}}]} as unknown as DocumentNode; 150 | export const GetCurrentUserDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetCurrentUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getCurrentUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"profileImageURL"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"recommendedUsers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"profileImageURL"}}]}},{"kind":"Field","name":{"kind":"Name","value":"followers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"profileImageURL"}}]}},{"kind":"Field","name":{"kind":"Name","value":"following"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"profileImageURL"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tweets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"content"}},{"kind":"Field","name":{"kind":"Name","value":"author"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"profileImageURL"}}]}}]}}]}}]}}]} as unknown as DocumentNode; 151 | export const GetuserByIdDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetuserById"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getUserById"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"profileImageURL"}},{"kind":"Field","name":{"kind":"Name","value":"followers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"profileImageURL"}}]}},{"kind":"Field","name":{"kind":"Name","value":"following"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"profileImageURL"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tweets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"content"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"author"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"profileImageURL"}}]}}]}}]}}]}}]} as unknown as DocumentNode; -------------------------------------------------------------------------------- /gql/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./fragment-masking"; 2 | export * from "./gql"; -------------------------------------------------------------------------------- /graphql.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "__schema": { 3 | "queryType": { 4 | "name": "Query" 5 | }, 6 | "mutationType": { 7 | "name": "Mutation" 8 | }, 9 | "subscriptionType": null, 10 | "types": [ 11 | { 12 | "kind": "SCALAR", 13 | "name": "Boolean", 14 | "description": "The `Boolean` scalar type represents `true` or `false`.", 15 | "fields": null, 16 | "inputFields": null, 17 | "interfaces": null, 18 | "enumValues": null, 19 | "possibleTypes": null 20 | }, 21 | { 22 | "kind": "INPUT_OBJECT", 23 | "name": "CreateTweetData", 24 | "description": null, 25 | "fields": null, 26 | "inputFields": [ 27 | { 28 | "name": "content", 29 | "description": null, 30 | "type": { 31 | "kind": "NON_NULL", 32 | "name": null, 33 | "ofType": { 34 | "kind": "SCALAR", 35 | "name": "String", 36 | "ofType": null 37 | } 38 | }, 39 | "defaultValue": null, 40 | "isDeprecated": false, 41 | "deprecationReason": null 42 | }, 43 | { 44 | "name": "imageURL", 45 | "description": null, 46 | "type": { 47 | "kind": "SCALAR", 48 | "name": "String", 49 | "ofType": null 50 | }, 51 | "defaultValue": null, 52 | "isDeprecated": false, 53 | "deprecationReason": null 54 | } 55 | ], 56 | "interfaces": null, 57 | "enumValues": null, 58 | "possibleTypes": null 59 | }, 60 | { 61 | "kind": "SCALAR", 62 | "name": "ID", 63 | "description": "The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\"4\"`) or integer (such as `4`) input value will be accepted as an ID.", 64 | "fields": null, 65 | "inputFields": null, 66 | "interfaces": null, 67 | "enumValues": null, 68 | "possibleTypes": null 69 | }, 70 | { 71 | "kind": "OBJECT", 72 | "name": "Mutation", 73 | "description": null, 74 | "fields": [ 75 | { 76 | "name": "createTweet", 77 | "description": null, 78 | "args": [ 79 | { 80 | "name": "payload", 81 | "description": null, 82 | "type": { 83 | "kind": "NON_NULL", 84 | "name": null, 85 | "ofType": { 86 | "kind": "INPUT_OBJECT", 87 | "name": "CreateTweetData", 88 | "ofType": null 89 | } 90 | }, 91 | "defaultValue": null, 92 | "isDeprecated": false, 93 | "deprecationReason": null 94 | } 95 | ], 96 | "type": { 97 | "kind": "OBJECT", 98 | "name": "Tweet", 99 | "ofType": null 100 | }, 101 | "isDeprecated": false, 102 | "deprecationReason": null 103 | }, 104 | { 105 | "name": "followUser", 106 | "description": null, 107 | "args": [ 108 | { 109 | "name": "to", 110 | "description": null, 111 | "type": { 112 | "kind": "NON_NULL", 113 | "name": null, 114 | "ofType": { 115 | "kind": "SCALAR", 116 | "name": "ID", 117 | "ofType": null 118 | } 119 | }, 120 | "defaultValue": null, 121 | "isDeprecated": false, 122 | "deprecationReason": null 123 | } 124 | ], 125 | "type": { 126 | "kind": "SCALAR", 127 | "name": "Boolean", 128 | "ofType": null 129 | }, 130 | "isDeprecated": false, 131 | "deprecationReason": null 132 | }, 133 | { 134 | "name": "unfollowUser", 135 | "description": null, 136 | "args": [ 137 | { 138 | "name": "to", 139 | "description": null, 140 | "type": { 141 | "kind": "NON_NULL", 142 | "name": null, 143 | "ofType": { 144 | "kind": "SCALAR", 145 | "name": "ID", 146 | "ofType": null 147 | } 148 | }, 149 | "defaultValue": null, 150 | "isDeprecated": false, 151 | "deprecationReason": null 152 | } 153 | ], 154 | "type": { 155 | "kind": "SCALAR", 156 | "name": "Boolean", 157 | "ofType": null 158 | }, 159 | "isDeprecated": false, 160 | "deprecationReason": null 161 | } 162 | ], 163 | "inputFields": null, 164 | "interfaces": [], 165 | "enumValues": null, 166 | "possibleTypes": null 167 | }, 168 | { 169 | "kind": "OBJECT", 170 | "name": "Query", 171 | "description": null, 172 | "fields": [ 173 | { 174 | "name": "getAllTweets", 175 | "description": null, 176 | "args": [], 177 | "type": { 178 | "kind": "LIST", 179 | "name": null, 180 | "ofType": { 181 | "kind": "OBJECT", 182 | "name": "Tweet", 183 | "ofType": null 184 | } 185 | }, 186 | "isDeprecated": false, 187 | "deprecationReason": null 188 | }, 189 | { 190 | "name": "getCurrentUser", 191 | "description": null, 192 | "args": [], 193 | "type": { 194 | "kind": "OBJECT", 195 | "name": "User", 196 | "ofType": null 197 | }, 198 | "isDeprecated": false, 199 | "deprecationReason": null 200 | }, 201 | { 202 | "name": "getSignedURLForTweet", 203 | "description": null, 204 | "args": [ 205 | { 206 | "name": "imageName", 207 | "description": null, 208 | "type": { 209 | "kind": "NON_NULL", 210 | "name": null, 211 | "ofType": { 212 | "kind": "SCALAR", 213 | "name": "String", 214 | "ofType": null 215 | } 216 | }, 217 | "defaultValue": null, 218 | "isDeprecated": false, 219 | "deprecationReason": null 220 | }, 221 | { 222 | "name": "imageType", 223 | "description": null, 224 | "type": { 225 | "kind": "NON_NULL", 226 | "name": null, 227 | "ofType": { 228 | "kind": "SCALAR", 229 | "name": "String", 230 | "ofType": null 231 | } 232 | }, 233 | "defaultValue": null, 234 | "isDeprecated": false, 235 | "deprecationReason": null 236 | } 237 | ], 238 | "type": { 239 | "kind": "SCALAR", 240 | "name": "String", 241 | "ofType": null 242 | }, 243 | "isDeprecated": false, 244 | "deprecationReason": null 245 | }, 246 | { 247 | "name": "getUserById", 248 | "description": null, 249 | "args": [ 250 | { 251 | "name": "id", 252 | "description": null, 253 | "type": { 254 | "kind": "NON_NULL", 255 | "name": null, 256 | "ofType": { 257 | "kind": "SCALAR", 258 | "name": "ID", 259 | "ofType": null 260 | } 261 | }, 262 | "defaultValue": null, 263 | "isDeprecated": false, 264 | "deprecationReason": null 265 | } 266 | ], 267 | "type": { 268 | "kind": "OBJECT", 269 | "name": "User", 270 | "ofType": null 271 | }, 272 | "isDeprecated": false, 273 | "deprecationReason": null 274 | }, 275 | { 276 | "name": "verifyGoogleToken", 277 | "description": null, 278 | "args": [ 279 | { 280 | "name": "token", 281 | "description": null, 282 | "type": { 283 | "kind": "NON_NULL", 284 | "name": null, 285 | "ofType": { 286 | "kind": "SCALAR", 287 | "name": "String", 288 | "ofType": null 289 | } 290 | }, 291 | "defaultValue": null, 292 | "isDeprecated": false, 293 | "deprecationReason": null 294 | } 295 | ], 296 | "type": { 297 | "kind": "SCALAR", 298 | "name": "String", 299 | "ofType": null 300 | }, 301 | "isDeprecated": false, 302 | "deprecationReason": null 303 | } 304 | ], 305 | "inputFields": null, 306 | "interfaces": [], 307 | "enumValues": null, 308 | "possibleTypes": null 309 | }, 310 | { 311 | "kind": "SCALAR", 312 | "name": "String", 313 | "description": "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.", 314 | "fields": null, 315 | "inputFields": null, 316 | "interfaces": null, 317 | "enumValues": null, 318 | "possibleTypes": null 319 | }, 320 | { 321 | "kind": "OBJECT", 322 | "name": "Tweet", 323 | "description": null, 324 | "fields": [ 325 | { 326 | "name": "author", 327 | "description": null, 328 | "args": [], 329 | "type": { 330 | "kind": "OBJECT", 331 | "name": "User", 332 | "ofType": null 333 | }, 334 | "isDeprecated": false, 335 | "deprecationReason": null 336 | }, 337 | { 338 | "name": "content", 339 | "description": null, 340 | "args": [], 341 | "type": { 342 | "kind": "NON_NULL", 343 | "name": null, 344 | "ofType": { 345 | "kind": "SCALAR", 346 | "name": "String", 347 | "ofType": null 348 | } 349 | }, 350 | "isDeprecated": false, 351 | "deprecationReason": null 352 | }, 353 | { 354 | "name": "id", 355 | "description": null, 356 | "args": [], 357 | "type": { 358 | "kind": "NON_NULL", 359 | "name": null, 360 | "ofType": { 361 | "kind": "SCALAR", 362 | "name": "ID", 363 | "ofType": null 364 | } 365 | }, 366 | "isDeprecated": false, 367 | "deprecationReason": null 368 | }, 369 | { 370 | "name": "imageURL", 371 | "description": null, 372 | "args": [], 373 | "type": { 374 | "kind": "SCALAR", 375 | "name": "String", 376 | "ofType": null 377 | }, 378 | "isDeprecated": false, 379 | "deprecationReason": null 380 | } 381 | ], 382 | "inputFields": null, 383 | "interfaces": [], 384 | "enumValues": null, 385 | "possibleTypes": null 386 | }, 387 | { 388 | "kind": "OBJECT", 389 | "name": "User", 390 | "description": null, 391 | "fields": [ 392 | { 393 | "name": "email", 394 | "description": null, 395 | "args": [], 396 | "type": { 397 | "kind": "NON_NULL", 398 | "name": null, 399 | "ofType": { 400 | "kind": "SCALAR", 401 | "name": "String", 402 | "ofType": null 403 | } 404 | }, 405 | "isDeprecated": false, 406 | "deprecationReason": null 407 | }, 408 | { 409 | "name": "firstName", 410 | "description": null, 411 | "args": [], 412 | "type": { 413 | "kind": "NON_NULL", 414 | "name": null, 415 | "ofType": { 416 | "kind": "SCALAR", 417 | "name": "String", 418 | "ofType": null 419 | } 420 | }, 421 | "isDeprecated": false, 422 | "deprecationReason": null 423 | }, 424 | { 425 | "name": "followers", 426 | "description": null, 427 | "args": [], 428 | "type": { 429 | "kind": "LIST", 430 | "name": null, 431 | "ofType": { 432 | "kind": "OBJECT", 433 | "name": "User", 434 | "ofType": null 435 | } 436 | }, 437 | "isDeprecated": false, 438 | "deprecationReason": null 439 | }, 440 | { 441 | "name": "following", 442 | "description": null, 443 | "args": [], 444 | "type": { 445 | "kind": "LIST", 446 | "name": null, 447 | "ofType": { 448 | "kind": "OBJECT", 449 | "name": "User", 450 | "ofType": null 451 | } 452 | }, 453 | "isDeprecated": false, 454 | "deprecationReason": null 455 | }, 456 | { 457 | "name": "id", 458 | "description": null, 459 | "args": [], 460 | "type": { 461 | "kind": "NON_NULL", 462 | "name": null, 463 | "ofType": { 464 | "kind": "SCALAR", 465 | "name": "ID", 466 | "ofType": null 467 | } 468 | }, 469 | "isDeprecated": false, 470 | "deprecationReason": null 471 | }, 472 | { 473 | "name": "lastName", 474 | "description": null, 475 | "args": [], 476 | "type": { 477 | "kind": "SCALAR", 478 | "name": "String", 479 | "ofType": null 480 | }, 481 | "isDeprecated": false, 482 | "deprecationReason": null 483 | }, 484 | { 485 | "name": "profileImageURL", 486 | "description": null, 487 | "args": [], 488 | "type": { 489 | "kind": "SCALAR", 490 | "name": "String", 491 | "ofType": null 492 | }, 493 | "isDeprecated": false, 494 | "deprecationReason": null 495 | }, 496 | { 497 | "name": "recommendedUsers", 498 | "description": null, 499 | "args": [], 500 | "type": { 501 | "kind": "LIST", 502 | "name": null, 503 | "ofType": { 504 | "kind": "OBJECT", 505 | "name": "User", 506 | "ofType": null 507 | } 508 | }, 509 | "isDeprecated": false, 510 | "deprecationReason": null 511 | }, 512 | { 513 | "name": "tweets", 514 | "description": null, 515 | "args": [], 516 | "type": { 517 | "kind": "LIST", 518 | "name": null, 519 | "ofType": { 520 | "kind": "OBJECT", 521 | "name": "Tweet", 522 | "ofType": null 523 | } 524 | }, 525 | "isDeprecated": false, 526 | "deprecationReason": null 527 | } 528 | ], 529 | "inputFields": null, 530 | "interfaces": [], 531 | "enumValues": null, 532 | "possibleTypes": null 533 | }, 534 | { 535 | "kind": "OBJECT", 536 | "name": "__Directive", 537 | "description": "A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.\n\nIn some cases, you need to provide options to alter GraphQL's execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor.", 538 | "fields": [ 539 | { 540 | "name": "name", 541 | "description": null, 542 | "args": [], 543 | "type": { 544 | "kind": "NON_NULL", 545 | "name": null, 546 | "ofType": { 547 | "kind": "SCALAR", 548 | "name": "String", 549 | "ofType": null 550 | } 551 | }, 552 | "isDeprecated": false, 553 | "deprecationReason": null 554 | }, 555 | { 556 | "name": "description", 557 | "description": null, 558 | "args": [], 559 | "type": { 560 | "kind": "SCALAR", 561 | "name": "String", 562 | "ofType": null 563 | }, 564 | "isDeprecated": false, 565 | "deprecationReason": null 566 | }, 567 | { 568 | "name": "isRepeatable", 569 | "description": null, 570 | "args": [], 571 | "type": { 572 | "kind": "NON_NULL", 573 | "name": null, 574 | "ofType": { 575 | "kind": "SCALAR", 576 | "name": "Boolean", 577 | "ofType": null 578 | } 579 | }, 580 | "isDeprecated": false, 581 | "deprecationReason": null 582 | }, 583 | { 584 | "name": "locations", 585 | "description": null, 586 | "args": [], 587 | "type": { 588 | "kind": "NON_NULL", 589 | "name": null, 590 | "ofType": { 591 | "kind": "LIST", 592 | "name": null, 593 | "ofType": { 594 | "kind": "NON_NULL", 595 | "name": null, 596 | "ofType": { 597 | "kind": "ENUM", 598 | "name": "__DirectiveLocation", 599 | "ofType": null 600 | } 601 | } 602 | } 603 | }, 604 | "isDeprecated": false, 605 | "deprecationReason": null 606 | }, 607 | { 608 | "name": "args", 609 | "description": null, 610 | "args": [ 611 | { 612 | "name": "includeDeprecated", 613 | "description": null, 614 | "type": { 615 | "kind": "SCALAR", 616 | "name": "Boolean", 617 | "ofType": null 618 | }, 619 | "defaultValue": "false", 620 | "isDeprecated": false, 621 | "deprecationReason": null 622 | } 623 | ], 624 | "type": { 625 | "kind": "NON_NULL", 626 | "name": null, 627 | "ofType": { 628 | "kind": "LIST", 629 | "name": null, 630 | "ofType": { 631 | "kind": "NON_NULL", 632 | "name": null, 633 | "ofType": { 634 | "kind": "OBJECT", 635 | "name": "__InputValue", 636 | "ofType": null 637 | } 638 | } 639 | } 640 | }, 641 | "isDeprecated": false, 642 | "deprecationReason": null 643 | } 644 | ], 645 | "inputFields": null, 646 | "interfaces": [], 647 | "enumValues": null, 648 | "possibleTypes": null 649 | }, 650 | { 651 | "kind": "ENUM", 652 | "name": "__DirectiveLocation", 653 | "description": "A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation describes one such possible adjacencies.", 654 | "fields": null, 655 | "inputFields": null, 656 | "interfaces": null, 657 | "enumValues": [ 658 | { 659 | "name": "QUERY", 660 | "description": "Location adjacent to a query operation.", 661 | "isDeprecated": false, 662 | "deprecationReason": null 663 | }, 664 | { 665 | "name": "MUTATION", 666 | "description": "Location adjacent to a mutation operation.", 667 | "isDeprecated": false, 668 | "deprecationReason": null 669 | }, 670 | { 671 | "name": "SUBSCRIPTION", 672 | "description": "Location adjacent to a subscription operation.", 673 | "isDeprecated": false, 674 | "deprecationReason": null 675 | }, 676 | { 677 | "name": "FIELD", 678 | "description": "Location adjacent to a field.", 679 | "isDeprecated": false, 680 | "deprecationReason": null 681 | }, 682 | { 683 | "name": "FRAGMENT_DEFINITION", 684 | "description": "Location adjacent to a fragment definition.", 685 | "isDeprecated": false, 686 | "deprecationReason": null 687 | }, 688 | { 689 | "name": "FRAGMENT_SPREAD", 690 | "description": "Location adjacent to a fragment spread.", 691 | "isDeprecated": false, 692 | "deprecationReason": null 693 | }, 694 | { 695 | "name": "INLINE_FRAGMENT", 696 | "description": "Location adjacent to an inline fragment.", 697 | "isDeprecated": false, 698 | "deprecationReason": null 699 | }, 700 | { 701 | "name": "VARIABLE_DEFINITION", 702 | "description": "Location adjacent to a variable definition.", 703 | "isDeprecated": false, 704 | "deprecationReason": null 705 | }, 706 | { 707 | "name": "SCHEMA", 708 | "description": "Location adjacent to a schema definition.", 709 | "isDeprecated": false, 710 | "deprecationReason": null 711 | }, 712 | { 713 | "name": "SCALAR", 714 | "description": "Location adjacent to a scalar definition.", 715 | "isDeprecated": false, 716 | "deprecationReason": null 717 | }, 718 | { 719 | "name": "OBJECT", 720 | "description": "Location adjacent to an object type definition.", 721 | "isDeprecated": false, 722 | "deprecationReason": null 723 | }, 724 | { 725 | "name": "FIELD_DEFINITION", 726 | "description": "Location adjacent to a field definition.", 727 | "isDeprecated": false, 728 | "deprecationReason": null 729 | }, 730 | { 731 | "name": "ARGUMENT_DEFINITION", 732 | "description": "Location adjacent to an argument definition.", 733 | "isDeprecated": false, 734 | "deprecationReason": null 735 | }, 736 | { 737 | "name": "INTERFACE", 738 | "description": "Location adjacent to an interface definition.", 739 | "isDeprecated": false, 740 | "deprecationReason": null 741 | }, 742 | { 743 | "name": "UNION", 744 | "description": "Location adjacent to a union definition.", 745 | "isDeprecated": false, 746 | "deprecationReason": null 747 | }, 748 | { 749 | "name": "ENUM", 750 | "description": "Location adjacent to an enum definition.", 751 | "isDeprecated": false, 752 | "deprecationReason": null 753 | }, 754 | { 755 | "name": "ENUM_VALUE", 756 | "description": "Location adjacent to an enum value definition.", 757 | "isDeprecated": false, 758 | "deprecationReason": null 759 | }, 760 | { 761 | "name": "INPUT_OBJECT", 762 | "description": "Location adjacent to an input object type definition.", 763 | "isDeprecated": false, 764 | "deprecationReason": null 765 | }, 766 | { 767 | "name": "INPUT_FIELD_DEFINITION", 768 | "description": "Location adjacent to an input object field definition.", 769 | "isDeprecated": false, 770 | "deprecationReason": null 771 | } 772 | ], 773 | "possibleTypes": null 774 | }, 775 | { 776 | "kind": "OBJECT", 777 | "name": "__EnumValue", 778 | "description": "One possible value for a given Enum. Enum values are unique values, not a placeholder for a string or numeric value. However an Enum value is returned in a JSON response as a string.", 779 | "fields": [ 780 | { 781 | "name": "name", 782 | "description": null, 783 | "args": [], 784 | "type": { 785 | "kind": "NON_NULL", 786 | "name": null, 787 | "ofType": { 788 | "kind": "SCALAR", 789 | "name": "String", 790 | "ofType": null 791 | } 792 | }, 793 | "isDeprecated": false, 794 | "deprecationReason": null 795 | }, 796 | { 797 | "name": "description", 798 | "description": null, 799 | "args": [], 800 | "type": { 801 | "kind": "SCALAR", 802 | "name": "String", 803 | "ofType": null 804 | }, 805 | "isDeprecated": false, 806 | "deprecationReason": null 807 | }, 808 | { 809 | "name": "isDeprecated", 810 | "description": null, 811 | "args": [], 812 | "type": { 813 | "kind": "NON_NULL", 814 | "name": null, 815 | "ofType": { 816 | "kind": "SCALAR", 817 | "name": "Boolean", 818 | "ofType": null 819 | } 820 | }, 821 | "isDeprecated": false, 822 | "deprecationReason": null 823 | }, 824 | { 825 | "name": "deprecationReason", 826 | "description": null, 827 | "args": [], 828 | "type": { 829 | "kind": "SCALAR", 830 | "name": "String", 831 | "ofType": null 832 | }, 833 | "isDeprecated": false, 834 | "deprecationReason": null 835 | } 836 | ], 837 | "inputFields": null, 838 | "interfaces": [], 839 | "enumValues": null, 840 | "possibleTypes": null 841 | }, 842 | { 843 | "kind": "OBJECT", 844 | "name": "__Field", 845 | "description": "Object and Interface types are described by a list of Fields, each of which has a name, potentially a list of arguments, and a return type.", 846 | "fields": [ 847 | { 848 | "name": "name", 849 | "description": null, 850 | "args": [], 851 | "type": { 852 | "kind": "NON_NULL", 853 | "name": null, 854 | "ofType": { 855 | "kind": "SCALAR", 856 | "name": "String", 857 | "ofType": null 858 | } 859 | }, 860 | "isDeprecated": false, 861 | "deprecationReason": null 862 | }, 863 | { 864 | "name": "description", 865 | "description": null, 866 | "args": [], 867 | "type": { 868 | "kind": "SCALAR", 869 | "name": "String", 870 | "ofType": null 871 | }, 872 | "isDeprecated": false, 873 | "deprecationReason": null 874 | }, 875 | { 876 | "name": "args", 877 | "description": null, 878 | "args": [ 879 | { 880 | "name": "includeDeprecated", 881 | "description": null, 882 | "type": { 883 | "kind": "SCALAR", 884 | "name": "Boolean", 885 | "ofType": null 886 | }, 887 | "defaultValue": "false", 888 | "isDeprecated": false, 889 | "deprecationReason": null 890 | } 891 | ], 892 | "type": { 893 | "kind": "NON_NULL", 894 | "name": null, 895 | "ofType": { 896 | "kind": "LIST", 897 | "name": null, 898 | "ofType": { 899 | "kind": "NON_NULL", 900 | "name": null, 901 | "ofType": { 902 | "kind": "OBJECT", 903 | "name": "__InputValue", 904 | "ofType": null 905 | } 906 | } 907 | } 908 | }, 909 | "isDeprecated": false, 910 | "deprecationReason": null 911 | }, 912 | { 913 | "name": "type", 914 | "description": null, 915 | "args": [], 916 | "type": { 917 | "kind": "NON_NULL", 918 | "name": null, 919 | "ofType": { 920 | "kind": "OBJECT", 921 | "name": "__Type", 922 | "ofType": null 923 | } 924 | }, 925 | "isDeprecated": false, 926 | "deprecationReason": null 927 | }, 928 | { 929 | "name": "isDeprecated", 930 | "description": null, 931 | "args": [], 932 | "type": { 933 | "kind": "NON_NULL", 934 | "name": null, 935 | "ofType": { 936 | "kind": "SCALAR", 937 | "name": "Boolean", 938 | "ofType": null 939 | } 940 | }, 941 | "isDeprecated": false, 942 | "deprecationReason": null 943 | }, 944 | { 945 | "name": "deprecationReason", 946 | "description": null, 947 | "args": [], 948 | "type": { 949 | "kind": "SCALAR", 950 | "name": "String", 951 | "ofType": null 952 | }, 953 | "isDeprecated": false, 954 | "deprecationReason": null 955 | } 956 | ], 957 | "inputFields": null, 958 | "interfaces": [], 959 | "enumValues": null, 960 | "possibleTypes": null 961 | }, 962 | { 963 | "kind": "OBJECT", 964 | "name": "__InputValue", 965 | "description": "Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value.", 966 | "fields": [ 967 | { 968 | "name": "name", 969 | "description": null, 970 | "args": [], 971 | "type": { 972 | "kind": "NON_NULL", 973 | "name": null, 974 | "ofType": { 975 | "kind": "SCALAR", 976 | "name": "String", 977 | "ofType": null 978 | } 979 | }, 980 | "isDeprecated": false, 981 | "deprecationReason": null 982 | }, 983 | { 984 | "name": "description", 985 | "description": null, 986 | "args": [], 987 | "type": { 988 | "kind": "SCALAR", 989 | "name": "String", 990 | "ofType": null 991 | }, 992 | "isDeprecated": false, 993 | "deprecationReason": null 994 | }, 995 | { 996 | "name": "type", 997 | "description": null, 998 | "args": [], 999 | "type": { 1000 | "kind": "NON_NULL", 1001 | "name": null, 1002 | "ofType": { 1003 | "kind": "OBJECT", 1004 | "name": "__Type", 1005 | "ofType": null 1006 | } 1007 | }, 1008 | "isDeprecated": false, 1009 | "deprecationReason": null 1010 | }, 1011 | { 1012 | "name": "defaultValue", 1013 | "description": "A GraphQL-formatted string representing the default value for this input value.", 1014 | "args": [], 1015 | "type": { 1016 | "kind": "SCALAR", 1017 | "name": "String", 1018 | "ofType": null 1019 | }, 1020 | "isDeprecated": false, 1021 | "deprecationReason": null 1022 | }, 1023 | { 1024 | "name": "isDeprecated", 1025 | "description": null, 1026 | "args": [], 1027 | "type": { 1028 | "kind": "NON_NULL", 1029 | "name": null, 1030 | "ofType": { 1031 | "kind": "SCALAR", 1032 | "name": "Boolean", 1033 | "ofType": null 1034 | } 1035 | }, 1036 | "isDeprecated": false, 1037 | "deprecationReason": null 1038 | }, 1039 | { 1040 | "name": "deprecationReason", 1041 | "description": null, 1042 | "args": [], 1043 | "type": { 1044 | "kind": "SCALAR", 1045 | "name": "String", 1046 | "ofType": null 1047 | }, 1048 | "isDeprecated": false, 1049 | "deprecationReason": null 1050 | } 1051 | ], 1052 | "inputFields": null, 1053 | "interfaces": [], 1054 | "enumValues": null, 1055 | "possibleTypes": null 1056 | }, 1057 | { 1058 | "kind": "OBJECT", 1059 | "name": "__Schema", 1060 | "description": "A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations.", 1061 | "fields": [ 1062 | { 1063 | "name": "description", 1064 | "description": null, 1065 | "args": [], 1066 | "type": { 1067 | "kind": "SCALAR", 1068 | "name": "String", 1069 | "ofType": null 1070 | }, 1071 | "isDeprecated": false, 1072 | "deprecationReason": null 1073 | }, 1074 | { 1075 | "name": "types", 1076 | "description": "A list of all types supported by this server.", 1077 | "args": [], 1078 | "type": { 1079 | "kind": "NON_NULL", 1080 | "name": null, 1081 | "ofType": { 1082 | "kind": "LIST", 1083 | "name": null, 1084 | "ofType": { 1085 | "kind": "NON_NULL", 1086 | "name": null, 1087 | "ofType": { 1088 | "kind": "OBJECT", 1089 | "name": "__Type", 1090 | "ofType": null 1091 | } 1092 | } 1093 | } 1094 | }, 1095 | "isDeprecated": false, 1096 | "deprecationReason": null 1097 | }, 1098 | { 1099 | "name": "queryType", 1100 | "description": "The type that query operations will be rooted at.", 1101 | "args": [], 1102 | "type": { 1103 | "kind": "NON_NULL", 1104 | "name": null, 1105 | "ofType": { 1106 | "kind": "OBJECT", 1107 | "name": "__Type", 1108 | "ofType": null 1109 | } 1110 | }, 1111 | "isDeprecated": false, 1112 | "deprecationReason": null 1113 | }, 1114 | { 1115 | "name": "mutationType", 1116 | "description": "If this server supports mutation, the type that mutation operations will be rooted at.", 1117 | "args": [], 1118 | "type": { 1119 | "kind": "OBJECT", 1120 | "name": "__Type", 1121 | "ofType": null 1122 | }, 1123 | "isDeprecated": false, 1124 | "deprecationReason": null 1125 | }, 1126 | { 1127 | "name": "subscriptionType", 1128 | "description": "If this server support subscription, the type that subscription operations will be rooted at.", 1129 | "args": [], 1130 | "type": { 1131 | "kind": "OBJECT", 1132 | "name": "__Type", 1133 | "ofType": null 1134 | }, 1135 | "isDeprecated": false, 1136 | "deprecationReason": null 1137 | }, 1138 | { 1139 | "name": "directives", 1140 | "description": "A list of all directives supported by this server.", 1141 | "args": [], 1142 | "type": { 1143 | "kind": "NON_NULL", 1144 | "name": null, 1145 | "ofType": { 1146 | "kind": "LIST", 1147 | "name": null, 1148 | "ofType": { 1149 | "kind": "NON_NULL", 1150 | "name": null, 1151 | "ofType": { 1152 | "kind": "OBJECT", 1153 | "name": "__Directive", 1154 | "ofType": null 1155 | } 1156 | } 1157 | } 1158 | }, 1159 | "isDeprecated": false, 1160 | "deprecationReason": null 1161 | } 1162 | ], 1163 | "inputFields": null, 1164 | "interfaces": [], 1165 | "enumValues": null, 1166 | "possibleTypes": null 1167 | }, 1168 | { 1169 | "kind": "OBJECT", 1170 | "name": "__Type", 1171 | "description": "The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum.\n\nDepending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name, description and optional `specifiedByURL`, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types.", 1172 | "fields": [ 1173 | { 1174 | "name": "kind", 1175 | "description": null, 1176 | "args": [], 1177 | "type": { 1178 | "kind": "NON_NULL", 1179 | "name": null, 1180 | "ofType": { 1181 | "kind": "ENUM", 1182 | "name": "__TypeKind", 1183 | "ofType": null 1184 | } 1185 | }, 1186 | "isDeprecated": false, 1187 | "deprecationReason": null 1188 | }, 1189 | { 1190 | "name": "name", 1191 | "description": null, 1192 | "args": [], 1193 | "type": { 1194 | "kind": "SCALAR", 1195 | "name": "String", 1196 | "ofType": null 1197 | }, 1198 | "isDeprecated": false, 1199 | "deprecationReason": null 1200 | }, 1201 | { 1202 | "name": "description", 1203 | "description": null, 1204 | "args": [], 1205 | "type": { 1206 | "kind": "SCALAR", 1207 | "name": "String", 1208 | "ofType": null 1209 | }, 1210 | "isDeprecated": false, 1211 | "deprecationReason": null 1212 | }, 1213 | { 1214 | "name": "specifiedByURL", 1215 | "description": null, 1216 | "args": [], 1217 | "type": { 1218 | "kind": "SCALAR", 1219 | "name": "String", 1220 | "ofType": null 1221 | }, 1222 | "isDeprecated": false, 1223 | "deprecationReason": null 1224 | }, 1225 | { 1226 | "name": "fields", 1227 | "description": null, 1228 | "args": [ 1229 | { 1230 | "name": "includeDeprecated", 1231 | "description": null, 1232 | "type": { 1233 | "kind": "SCALAR", 1234 | "name": "Boolean", 1235 | "ofType": null 1236 | }, 1237 | "defaultValue": "false", 1238 | "isDeprecated": false, 1239 | "deprecationReason": null 1240 | } 1241 | ], 1242 | "type": { 1243 | "kind": "LIST", 1244 | "name": null, 1245 | "ofType": { 1246 | "kind": "NON_NULL", 1247 | "name": null, 1248 | "ofType": { 1249 | "kind": "OBJECT", 1250 | "name": "__Field", 1251 | "ofType": null 1252 | } 1253 | } 1254 | }, 1255 | "isDeprecated": false, 1256 | "deprecationReason": null 1257 | }, 1258 | { 1259 | "name": "interfaces", 1260 | "description": null, 1261 | "args": [], 1262 | "type": { 1263 | "kind": "LIST", 1264 | "name": null, 1265 | "ofType": { 1266 | "kind": "NON_NULL", 1267 | "name": null, 1268 | "ofType": { 1269 | "kind": "OBJECT", 1270 | "name": "__Type", 1271 | "ofType": null 1272 | } 1273 | } 1274 | }, 1275 | "isDeprecated": false, 1276 | "deprecationReason": null 1277 | }, 1278 | { 1279 | "name": "possibleTypes", 1280 | "description": null, 1281 | "args": [], 1282 | "type": { 1283 | "kind": "LIST", 1284 | "name": null, 1285 | "ofType": { 1286 | "kind": "NON_NULL", 1287 | "name": null, 1288 | "ofType": { 1289 | "kind": "OBJECT", 1290 | "name": "__Type", 1291 | "ofType": null 1292 | } 1293 | } 1294 | }, 1295 | "isDeprecated": false, 1296 | "deprecationReason": null 1297 | }, 1298 | { 1299 | "name": "enumValues", 1300 | "description": null, 1301 | "args": [ 1302 | { 1303 | "name": "includeDeprecated", 1304 | "description": null, 1305 | "type": { 1306 | "kind": "SCALAR", 1307 | "name": "Boolean", 1308 | "ofType": null 1309 | }, 1310 | "defaultValue": "false", 1311 | "isDeprecated": false, 1312 | "deprecationReason": null 1313 | } 1314 | ], 1315 | "type": { 1316 | "kind": "LIST", 1317 | "name": null, 1318 | "ofType": { 1319 | "kind": "NON_NULL", 1320 | "name": null, 1321 | "ofType": { 1322 | "kind": "OBJECT", 1323 | "name": "__EnumValue", 1324 | "ofType": null 1325 | } 1326 | } 1327 | }, 1328 | "isDeprecated": false, 1329 | "deprecationReason": null 1330 | }, 1331 | { 1332 | "name": "inputFields", 1333 | "description": null, 1334 | "args": [ 1335 | { 1336 | "name": "includeDeprecated", 1337 | "description": null, 1338 | "type": { 1339 | "kind": "SCALAR", 1340 | "name": "Boolean", 1341 | "ofType": null 1342 | }, 1343 | "defaultValue": "false", 1344 | "isDeprecated": false, 1345 | "deprecationReason": null 1346 | } 1347 | ], 1348 | "type": { 1349 | "kind": "LIST", 1350 | "name": null, 1351 | "ofType": { 1352 | "kind": "NON_NULL", 1353 | "name": null, 1354 | "ofType": { 1355 | "kind": "OBJECT", 1356 | "name": "__InputValue", 1357 | "ofType": null 1358 | } 1359 | } 1360 | }, 1361 | "isDeprecated": false, 1362 | "deprecationReason": null 1363 | }, 1364 | { 1365 | "name": "ofType", 1366 | "description": null, 1367 | "args": [], 1368 | "type": { 1369 | "kind": "OBJECT", 1370 | "name": "__Type", 1371 | "ofType": null 1372 | }, 1373 | "isDeprecated": false, 1374 | "deprecationReason": null 1375 | } 1376 | ], 1377 | "inputFields": null, 1378 | "interfaces": [], 1379 | "enumValues": null, 1380 | "possibleTypes": null 1381 | }, 1382 | { 1383 | "kind": "ENUM", 1384 | "name": "__TypeKind", 1385 | "description": "An enum describing what kind of type a given `__Type` is.", 1386 | "fields": null, 1387 | "inputFields": null, 1388 | "interfaces": null, 1389 | "enumValues": [ 1390 | { 1391 | "name": "SCALAR", 1392 | "description": "Indicates this type is a scalar.", 1393 | "isDeprecated": false, 1394 | "deprecationReason": null 1395 | }, 1396 | { 1397 | "name": "OBJECT", 1398 | "description": "Indicates this type is an object. `fields` and `interfaces` are valid fields.", 1399 | "isDeprecated": false, 1400 | "deprecationReason": null 1401 | }, 1402 | { 1403 | "name": "INTERFACE", 1404 | "description": "Indicates this type is an interface. `fields`, `interfaces`, and `possibleTypes` are valid fields.", 1405 | "isDeprecated": false, 1406 | "deprecationReason": null 1407 | }, 1408 | { 1409 | "name": "UNION", 1410 | "description": "Indicates this type is a union. `possibleTypes` is a valid field.", 1411 | "isDeprecated": false, 1412 | "deprecationReason": null 1413 | }, 1414 | { 1415 | "name": "ENUM", 1416 | "description": "Indicates this type is an enum. `enumValues` is a valid field.", 1417 | "isDeprecated": false, 1418 | "deprecationReason": null 1419 | }, 1420 | { 1421 | "name": "INPUT_OBJECT", 1422 | "description": "Indicates this type is an input object. `inputFields` is a valid field.", 1423 | "isDeprecated": false, 1424 | "deprecationReason": null 1425 | }, 1426 | { 1427 | "name": "LIST", 1428 | "description": "Indicates this type is a list. `ofType` is a valid field.", 1429 | "isDeprecated": false, 1430 | "deprecationReason": null 1431 | }, 1432 | { 1433 | "name": "NON_NULL", 1434 | "description": "Indicates this type is a non-null. `ofType` is a valid field.", 1435 | "isDeprecated": false, 1436 | "deprecationReason": null 1437 | } 1438 | ], 1439 | "possibleTypes": null 1440 | } 1441 | ], 1442 | "directives": [ 1443 | { 1444 | "name": "deprecated", 1445 | "description": "Marks an element of a GraphQL schema as no longer supported.", 1446 | "isRepeatable": false, 1447 | "locations": [ 1448 | "ARGUMENT_DEFINITION", 1449 | "ENUM_VALUE", 1450 | "FIELD_DEFINITION", 1451 | "INPUT_FIELD_DEFINITION" 1452 | ], 1453 | "args": [ 1454 | { 1455 | "name": "reason", 1456 | "description": "Explains why this element was deprecated, usually also including a suggestion for how to access supported similar data. Formatted using the Markdown syntax, as specified by [CommonMark](https://commonmark.org/).", 1457 | "type": { 1458 | "kind": "SCALAR", 1459 | "name": "String", 1460 | "ofType": null 1461 | }, 1462 | "defaultValue": "\"No longer supported\"", 1463 | "isDeprecated": false, 1464 | "deprecationReason": null 1465 | } 1466 | ] 1467 | }, 1468 | { 1469 | "name": "include", 1470 | "description": "Directs the executor to include this field or fragment only when the `if` argument is true.", 1471 | "isRepeatable": false, 1472 | "locations": [ 1473 | "FIELD", 1474 | "FRAGMENT_SPREAD", 1475 | "INLINE_FRAGMENT" 1476 | ], 1477 | "args": [ 1478 | { 1479 | "name": "if", 1480 | "description": "Included when true.", 1481 | "type": { 1482 | "kind": "NON_NULL", 1483 | "name": null, 1484 | "ofType": { 1485 | "kind": "SCALAR", 1486 | "name": "Boolean", 1487 | "ofType": null 1488 | } 1489 | }, 1490 | "defaultValue": null, 1491 | "isDeprecated": false, 1492 | "deprecationReason": null 1493 | } 1494 | ] 1495 | }, 1496 | { 1497 | "name": "skip", 1498 | "description": "Directs the executor to skip this field or fragment when the `if` argument is true.", 1499 | "isRepeatable": false, 1500 | "locations": [ 1501 | "FIELD", 1502 | "FRAGMENT_SPREAD", 1503 | "INLINE_FRAGMENT" 1504 | ], 1505 | "args": [ 1506 | { 1507 | "name": "if", 1508 | "description": "Skipped when true.", 1509 | "type": { 1510 | "kind": "NON_NULL", 1511 | "name": null, 1512 | "ofType": { 1513 | "kind": "SCALAR", 1514 | "name": "Boolean", 1515 | "ofType": null 1516 | } 1517 | }, 1518 | "defaultValue": null, 1519 | "isDeprecated": false, 1520 | "deprecationReason": null 1521 | } 1522 | ] 1523 | }, 1524 | { 1525 | "name": "specifiedBy", 1526 | "description": "Exposes a URL that specifies the behavior of this scalar.", 1527 | "isRepeatable": false, 1528 | "locations": [ 1529 | "SCALAR" 1530 | ], 1531 | "args": [ 1532 | { 1533 | "name": "url", 1534 | "description": "The URL that specifies the behavior of this scalar.", 1535 | "type": { 1536 | "kind": "NON_NULL", 1537 | "name": null, 1538 | "ofType": { 1539 | "kind": "SCALAR", 1540 | "name": "String", 1541 | "ofType": null 1542 | } 1543 | }, 1544 | "defaultValue": null, 1545 | "isDeprecated": false, 1546 | "deprecationReason": null 1547 | } 1548 | ] 1549 | } 1550 | ] 1551 | } 1552 | } -------------------------------------------------------------------------------- /graphql/mutation/tweet.ts: -------------------------------------------------------------------------------- 1 | import { graphql } from "@/gql"; 2 | 3 | export const createTweetMutation = graphql(` 4 | #graphql 5 | mutation CreateTweet($payload: CreateTweetData!) { 6 | createTweet(payload: $payload) { 7 | id 8 | } 9 | } 10 | `); 11 | -------------------------------------------------------------------------------- /graphql/mutation/user.ts: -------------------------------------------------------------------------------- 1 | import { graphql } from "@/gql"; 2 | 3 | export const followUserMutation = graphql(` 4 | #graphql 5 | mutation FollowUser($to: ID!) { 6 | followUser(to: $to) 7 | } 8 | `); 9 | 10 | export const unfollowUserMutation = graphql(` 11 | #graphql 12 | mutation UnfollowUser($to: ID!) { 13 | unfollowUser(to: $to) 14 | } 15 | `); 16 | -------------------------------------------------------------------------------- /graphql/query/tweet.ts: -------------------------------------------------------------------------------- 1 | import { graphql } from "@/gql"; 2 | 3 | export const getAllTweetsQuery = graphql(` 4 | #graphql 5 | 6 | query GetAllTweets { 7 | getAllTweets { 8 | id 9 | content 10 | imageURL 11 | author { 12 | id 13 | firstName 14 | lastName 15 | profileImageURL 16 | } 17 | } 18 | } 19 | `); 20 | 21 | export const getSignedURLForTweetQuery = graphql(` 22 | query GetSignedURL($imageName: String!, $imageType: String!) { 23 | getSignedURLForTweet(imageName: $imageName, imageType: $imageType) 24 | } 25 | `); 26 | -------------------------------------------------------------------------------- /graphql/query/user.ts: -------------------------------------------------------------------------------- 1 | import { graphql } from "../../gql"; 2 | 3 | export const verifyUserGoogleTokenQuery = graphql(` 4 | #graphql 5 | query VerifyUserGoogleToken($token: String!) { 6 | verifyGoogleToken(token: $token) 7 | } 8 | `); 9 | 10 | export const getCurrentUserQuery = graphql(` 11 | query GetCurrentUser { 12 | getCurrentUser { 13 | id 14 | profileImageURL 15 | email 16 | firstName 17 | lastName 18 | recommendedUsers { 19 | id 20 | firstName 21 | lastName 22 | profileImageURL 23 | } 24 | followers { 25 | id 26 | firstName 27 | lastName 28 | profileImageURL 29 | } 30 | following { 31 | id 32 | firstName 33 | lastName 34 | profileImageURL 35 | } 36 | tweets { 37 | id 38 | content 39 | author { 40 | id 41 | firstName 42 | lastName 43 | profileImageURL 44 | } 45 | } 46 | } 47 | } 48 | `); 49 | 50 | export const getUserByIdQuery = graphql(` 51 | #graphql 52 | query GetuserById($id: ID!) { 53 | getUserById(id: $id) { 54 | id 55 | firstName 56 | lastName 57 | profileImageURL 58 | 59 | followers { 60 | id 61 | firstName 62 | lastName 63 | profileImageURL 64 | } 65 | following { 66 | id 67 | firstName 68 | lastName 69 | profileImageURL 70 | } 71 | tweets { 72 | content 73 | id 74 | author { 75 | id 76 | firstName 77 | lastName 78 | profileImageURL 79 | } 80 | } 81 | } 82 | } 83 | `); 84 | -------------------------------------------------------------------------------- /hooks/tweet.ts: -------------------------------------------------------------------------------- 1 | import { graphqlClient } from "@/clients/api"; 2 | import { CreateTweetData } from "@/gql/graphql"; 3 | import { createTweetMutation } from "@/graphql/mutation/tweet"; 4 | import { getAllTweetsQuery } from "@/graphql/query/tweet"; 5 | import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; 6 | import { toast } from "react-hot-toast"; 7 | 8 | export const useCreateTweet = () => { 9 | const queryClient = useQueryClient(); 10 | 11 | const mutation = useMutation({ 12 | mutationFn: (payload: CreateTweetData) => 13 | graphqlClient.request(createTweetMutation, { payload }), 14 | onMutate: (payload) => toast.loading("Creating Tweet", { id: "1" }), 15 | onSuccess: async (payload) => { 16 | await queryClient.invalidateQueries(["all-tweets"]); 17 | toast.success("Created Success", { id: "1" }); 18 | }, 19 | }); 20 | 21 | return mutation; 22 | }; 23 | 24 | export const useGetAllTweets = () => { 25 | const query = useQuery({ 26 | queryKey: ["all-tweets"], 27 | queryFn: () => graphqlClient.request(getAllTweetsQuery), 28 | }); 29 | return { ...query, tweets: query.data?.getAllTweets }; 30 | }; 31 | -------------------------------------------------------------------------------- /hooks/user.ts: -------------------------------------------------------------------------------- 1 | import { graphqlClient } from "@/clients/api"; 2 | import { getCurrentUserQuery } from "@/graphql/query/user"; 3 | import { useQuery } from "@tanstack/react-query"; 4 | 5 | export const useCurrentUser = () => { 6 | const query = useQuery({ 7 | queryKey: ["curent-user"], 8 | queryFn: () => graphqlClient.request(getCurrentUserQuery), 9 | }); 10 | 11 | return { ...query, user: query.data?.getCurrentUser }; 12 | }; 13 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | images: { 5 | domains: [ 6 | "avatars.githubusercontent.com", 7 | "lh3.googleusercontent.com", 8 | "piyush-twitter-new.s3.ap-south-1.amazonaws.com", 9 | "i.imgur.com", 10 | ], 11 | }, 12 | }; 13 | 14 | module.exports = nextConfig; 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "twitter-client", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "concurrently \"yarn run codegen\" next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint", 10 | "codegen": "graphql-codegen --config codegen.ts --watch" 11 | }, 12 | "dependencies": { 13 | "@react-oauth/google": "^0.11.0", 14 | "@tanstack/react-query": "^4.29.5", 15 | "@tanstack/react-query-devtools": "^4.29.6", 16 | "@types/node": "18.16.3", 17 | "@types/react": "18.2.0", 18 | "@types/react-dom": "18.2.1", 19 | "autoprefixer": "10.4.14", 20 | "axios": "^1.4.0", 21 | "eslint": "8.39.0", 22 | "eslint-config-next": "13.3.2", 23 | "graphql": "^16.6.0", 24 | "graphql-request": "^6.0.0", 25 | "next": "13.3.2", 26 | "postcss": "8.4.23", 27 | "react": "18.2.0", 28 | "react-dom": "18.2.0", 29 | "react-hot-toast": "^2.4.1", 30 | "react-icons": "^4.8.0", 31 | "tailwindcss": "3.3.2", 32 | "typescript": "5.0.4" 33 | }, 34 | "devDependencies": { 35 | "@graphql-codegen/cli": "3.3.1", 36 | "@graphql-codegen/client-preset": "^3.0.1", 37 | "@graphql-codegen/introspection": "3.0.1", 38 | "concurrently": "^8.0.1" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /pages/[id].tsx: -------------------------------------------------------------------------------- 1 | import { useRouter } from "next/router"; 2 | import Twitterlayout from "@/components/FeedCard/Layout/TwitterLayout"; 3 | import Image from "next/image"; 4 | import type { GetServerSideProps, NextPage } from "next"; 5 | import { BsArrowLeftShort } from "react-icons/bs"; 6 | import { useCurrentUser } from "@/hooks/user"; 7 | import FeedCard from "@/components/FeedCard"; 8 | import { Tweet, User } from "@/gql/graphql"; 9 | import { graphqlClient } from "@/clients/api"; 10 | import { getUserByIdQuery } from "@/graphql/query/user"; 11 | import { useCallback, useMemo } from "react"; 12 | import { 13 | followUserMutation, 14 | unfollowUserMutation, 15 | } from "@/graphql/mutation/user"; 16 | import { useQueryClient } from "@tanstack/react-query"; 17 | 18 | interface ServerProps { 19 | userInfo?: User; 20 | } 21 | 22 | const UserProfilePage: NextPage = (props) => { 23 | const router = useRouter(); 24 | const { user: currentUser } = useCurrentUser(); 25 | const queryClient = useQueryClient(); 26 | 27 | const amIFollowing = useMemo(() => { 28 | if (!props.userInfo) return false; 29 | return ( 30 | (currentUser?.following?.findIndex( 31 | (el) => el?.id === props.userInfo?.id 32 | ) ?? -1) >= 0 33 | ); 34 | }, [currentUser?.following, props.userInfo]); 35 | 36 | const handleFollowUser = useCallback(async () => { 37 | if (!props.userInfo?.id) return; 38 | 39 | await graphqlClient.request(followUserMutation, { to: props.userInfo?.id }); 40 | await queryClient.invalidateQueries(["curent-user"]); 41 | }, [props.userInfo?.id, queryClient]); 42 | 43 | const handleUnfollowUser = useCallback(async () => { 44 | if (!props.userInfo?.id) return; 45 | 46 | await graphqlClient.request(unfollowUserMutation, { 47 | to: props.userInfo?.id, 48 | }); 49 | await queryClient.invalidateQueries(["curent-user"]); 50 | }, [props.userInfo?.id, queryClient]); 51 | 52 | return ( 53 |
54 | 55 |
56 | 67 |
68 | {props.userInfo?.profileImageURL && ( 69 | user-image 76 | )} 77 |

78 | {props.userInfo?.firstName} {props.userInfo?.lastName} 79 |

80 |
81 |
82 | {props.userInfo?.followers?.length} followers 83 | {props.userInfo?.following?.length} following 84 |
85 | {currentUser?.id !== props.userInfo?.id && ( 86 | <> 87 | {amIFollowing ? ( 88 | 94 | ) : ( 95 | 101 | )} 102 | 103 | )} 104 |
105 |
106 |
107 | {props.userInfo?.tweets?.map((tweet) => ( 108 | 109 | ))} 110 |
111 |
112 |
113 |
114 | ); 115 | }; 116 | 117 | export const getServerSideProps: GetServerSideProps = async ( 118 | context 119 | ) => { 120 | const id = context.query.id as string | undefined; 121 | 122 | if (!id) return { notFound: true, props: { userInfo: undefined } }; 123 | 124 | const userInfo = await graphqlClient.request(getUserByIdQuery, { id }); 125 | 126 | if (!userInfo?.getUserById) return { notFound: true }; 127 | 128 | return { 129 | props: { 130 | userInfo: userInfo.getUserById as User, 131 | }, 132 | }; 133 | }; 134 | 135 | export default UserProfilePage; 136 | -------------------------------------------------------------------------------- /pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import "@/styles/globals.css"; 2 | import { Inter, Quicksand } from "next/font/google"; 3 | import { GoogleOAuthProvider } from "@react-oauth/google"; 4 | import { QueryClientProvider, QueryClient } from "@tanstack/react-query"; 5 | import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; 6 | import type { AppProps } from "next/app"; 7 | import { Toaster } from "react-hot-toast"; 8 | 9 | const inter = Inter({ subsets: ["latin"] }); 10 | const quickSand = Quicksand({ subsets: ["latin"] }); 11 | 12 | const queryClient = new QueryClient(); 13 | 14 | export default function App({ Component, pageProps }: AppProps) { 15 | return ( 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import { Html, Head, Main, NextScript } from 'next/document' 2 | 3 | export default function Document() { 4 | return ( 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /pages/api/hello.ts: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | import type { NextApiRequest, NextApiResponse } from 'next' 3 | 4 | type Data = { 5 | name: string 6 | } 7 | 8 | export default function handler( 9 | req: NextApiRequest, 10 | res: NextApiResponse 11 | ) { 12 | res.status(200).json({ name: 'John Doe' }) 13 | } 14 | -------------------------------------------------------------------------------- /pages/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useCallback, useEffect, useState } from "react"; 2 | import Image from "next/image"; 3 | import { BiImageAlt } from "react-icons/bi"; 4 | import FeedCard from "@/components/FeedCard"; 5 | import { useCurrentUser } from "@/hooks/user"; 6 | import { useCreateTweet, useGetAllTweets } from "@/hooks/tweet"; 7 | import { Tweet } from "@/gql/graphql"; 8 | import Twitterlayout from "@/components/FeedCard/Layout/TwitterLayout"; 9 | import { GetServerSideProps } from "next"; 10 | import { graphqlClient } from "@/clients/api"; 11 | import { 12 | getAllTweetsQuery, 13 | getSignedURLForTweetQuery, 14 | } from "@/graphql/query/tweet"; 15 | import axios from "axios"; 16 | import { toast } from "react-hot-toast"; 17 | 18 | interface HomeProps { 19 | tweets?: Tweet[]; 20 | } 21 | 22 | export default function Home(props: HomeProps) { 23 | const { user } = useCurrentUser(); 24 | const { tweets = props.tweets as Tweet[] } = useGetAllTweets(); 25 | const { mutateAsync } = useCreateTweet(); 26 | 27 | const [content, setContent] = useState(""); 28 | const [imageURL, setImageURL] = useState(""); 29 | 30 | const handleInputChangeFile = useCallback((input: HTMLInputElement) => { 31 | return async (event: Event) => { 32 | event.preventDefault(); 33 | const file: File | null | undefined = input.files?.item(0); 34 | if (!file) return; 35 | 36 | const { getSignedURLForTweet } = await graphqlClient.request( 37 | getSignedURLForTweetQuery, 38 | { 39 | imageName: file.name, 40 | imageType: file.type, 41 | } 42 | ); 43 | 44 | if (getSignedURLForTweet) { 45 | toast.loading("Uploading...", { id: "2" }); 46 | await axios.put(getSignedURLForTweet, file, { 47 | headers: { 48 | "Content-Type": file.type, 49 | }, 50 | }); 51 | toast.success("Upload Completed", { id: "2" }); 52 | const url = new URL(getSignedURLForTweet); 53 | const myFilePath = `${url.origin}${url.pathname}`; 54 | setImageURL(myFilePath); 55 | } 56 | }; 57 | }, []); 58 | 59 | const handleSelectImage = useCallback(() => { 60 | const input = document.createElement("input"); 61 | input.setAttribute("type", "file"); 62 | input.setAttribute("accept", "image/*"); 63 | 64 | const handlerFn = handleInputChangeFile(input); 65 | 66 | input.addEventListener("change", handlerFn); 67 | 68 | input.click(); 69 | }, [handleInputChangeFile]); 70 | 71 | const handleCreateTweet = useCallback(async () => { 72 | await mutateAsync({ 73 | content, 74 | imageURL, 75 | }); 76 | setContent(""); 77 | setImageURL(""); 78 | }, [mutateAsync, content, imageURL]); 79 | 80 | return ( 81 |
82 | 83 |
84 |
85 |
86 |
87 | {user?.profileImageURL && ( 88 | user-image 95 | )} 96 |
97 |
98 | 105 | {imageURL && ( 106 | tweet-image 112 | )} 113 |
114 | 115 | 121 |
122 |
123 |
124 |
125 |
126 | {tweets?.map((tweet) => 127 | tweet ? : null 128 | )} 129 |
130 |
131 | ); 132 | } 133 | 134 | export const getServerSideProps: GetServerSideProps = async ( 135 | context 136 | ) => { 137 | const allTweets = await graphqlClient.request(getAllTweetsQuery); 138 | return { 139 | props: { 140 | tweets: allTweets.getAllTweets as Tweet[], 141 | }, 142 | }; 143 | }; 144 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piyushgarg-dev/twitter-clone/5f8ffb42999bc59ffe1a433df11b6e509f2c22c4/public/favicon.ico -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /styles/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | --foreground-rgb: 0, 0, 0; 7 | --background-start-rgb: 214, 219, 220; 8 | --background-end-rgb: 255, 255, 255; 9 | } 10 | 11 | @media (prefers-color-scheme: dark) { 12 | :root { 13 | --foreground-rgb: 255, 255, 255; 14 | --background-start-rgb: 0, 0, 0; 15 | --background-end-rgb: 0, 0, 0; 16 | } 17 | } 18 | 19 | body { 20 | color: rgb(var(--foreground-rgb)); 21 | background: linear-gradient( 22 | to bottom, 23 | transparent, 24 | rgb(var(--background-end-rgb)) 25 | ) 26 | rgb(var(--background-start-rgb)); 27 | } 28 | 29 | *:focus { 30 | outline: none; 31 | } 32 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | './pages/**/*.{js,ts,jsx,tsx,mdx}', 5 | './components/**/*.{js,ts,jsx,tsx,mdx}', 6 | './app/**/*.{js,ts,jsx,tsx,mdx}', 7 | ], 8 | theme: { 9 | extend: { 10 | backgroundImage: { 11 | 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', 12 | 'gradient-conic': 13 | 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', 14 | }, 15 | }, 16 | }, 17 | plugins: [], 18 | } 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true, 17 | "paths": { 18 | "@/*": ["./*"] 19 | } 20 | }, 21 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 22 | "exclude": ["node_modules"] 23 | } 24 | --------------------------------------------------------------------------------