diff options
| author | nsfisis <nsfisis@gmail.com> | 2025-07-12 08:59:01 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2025-07-12 10:18:01 +0900 |
| commit | 9da56e3023af305ba7c5fd49caab60ac8bb57100 (patch) | |
| tree | ee6ada3798ffafac1742dbcdff2fbb6326640498 /frontend | |
| parent | 1e6d528f54744a7fd76ba5cac4e63d8b5abd5fd6 (diff) | |
| download | feedaka-9da56e3023af305ba7c5fd49caab60ac8bb57100.tar.gz feedaka-9da56e3023af305ba7c5fd49caab60ac8bb57100.tar.zst feedaka-9da56e3023af305ba7c5fd49caab60ac8bb57100.zip | |
feat(frontend): setup GraphQL code generation
Diffstat (limited to 'frontend')
| -rw-r--r-- | frontend/biome.json | 4 | ||||
| -rw-r--r-- | frontend/graphql-codegen.ts | 12 | ||||
| -rw-r--r-- | frontend/package-lock.json | 46 | ||||
| -rw-r--r-- | frontend/package.json | 1 | ||||
| -rw-r--r-- | frontend/src/graphql/generated/fragment-masking.ts | 87 | ||||
| -rw-r--r-- | frontend/src/graphql/generated/gql.ts | 52 | ||||
| -rw-r--r-- | frontend/src/graphql/generated/graphql.ts | 212 | ||||
| -rw-r--r-- | frontend/src/graphql/generated/index.ts | 2 | ||||
| -rw-r--r-- | frontend/src/graphql/mutations.graphql | 52 | ||||
| -rw-r--r-- | frontend/src/graphql/queries.graphql | 73 |
10 files changed, 539 insertions, 2 deletions
diff --git a/frontend/biome.json b/frontend/biome.json index 5500f2c..d589e28 100644 --- a/frontend/biome.json +++ b/frontend/biome.json @@ -4,5 +4,9 @@ "enabled": true, "clientKind": "git", "useIgnoreFile": true + }, + "files": { + "includes": ["**", "!src/graphql/generated/*.ts"], + "ignoreUnknown": true } } diff --git a/frontend/graphql-codegen.ts b/frontend/graphql-codegen.ts index ddba3b1..af62625 100644 --- a/frontend/graphql-codegen.ts +++ b/frontend/graphql-codegen.ts @@ -3,11 +3,19 @@ import type { CodegenConfig } from "@graphql-codegen/cli"; const config: CodegenConfig = { overwrite: true, schema: "../common/graphql/schema.graphql", - documents: "src/**/*.tsx", + documents: ["src/**/*.tsx", "src/**/*.ts", "src/**/*.graphql"], generates: { - "src/graphql": { + "src/graphql/generated/": { preset: "client", plugins: [], + config: { + enumsAsTypes: true, + skipTypename: true, + useTypeImports: true, + scalars: { + DateTime: "string", + }, + }, }, }, }; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index cbf5ced..bcc0bdc 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -13,11 +13,13 @@ "react": "^19.1.0", "react-dom": "^19.1.0", "tailwindcss": "^4.1.11", + "urql": "^4.2.2", "wouter": "^3.7.1" }, "devDependencies": { "@biomejs/biome": "^2.1.1", "@graphql-codegen/cli": "^5.0.7", + "@graphql-codegen/client-preset": "^4.8.3", "@types/react": "^19.1.8", "@types/react-dom": "^19.1.6", "@vitejs/plugin-react": "^4.6.0", @@ -26,6 +28,20 @@ "vite": "^7.0.3" } }, + "node_modules/@0no-co/graphql.web": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.1.2.tgz", + "integrity": "sha512-N2NGsU5FLBhT8NZ+3l2YrzZSHITjNXNuDhC4iDiikv0IujaJ0Xc6xIxQZ/Ek3Cb+rgPjnLHYyJm11tInuJn+cw==", + "license": "MIT", + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" + }, + "peerDependenciesMeta": { + "graphql": { + "optional": true + } + } + }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -2605,6 +2621,16 @@ "@types/node": "*" } }, + "node_modules/@urql/core": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@urql/core/-/core-5.2.0.tgz", + "integrity": "sha512-/n0ieD0mvvDnVAXEQgX/7qJiVcvYvNkOHeBvkwtylfjydar123caCXcl58PXFY11oU1oquJocVXHxLAbtv4x1A==", + "license": "MIT", + "dependencies": { + "@0no-co/graphql.web": "^1.0.13", + "wonka": "^6.3.2" + } + }, "node_modules/@vitejs/plugin-react": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.6.0.tgz", @@ -5970,6 +5996,20 @@ "dev": true, "license": "MIT" }, + "node_modules/urql": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/urql/-/urql-4.2.2.tgz", + "integrity": "sha512-3GgqNa6iF7bC4hY/ImJKN4REQILcSU9VKcKL8gfELZM8mM5BnLH1BsCc8kBdnVGD1LIFOs4W3O2idNHhON1r0w==", + "license": "MIT", + "dependencies": { + "@urql/core": "^5.1.1", + "wonka": "^6.3.2" + }, + "peerDependencies": { + "@urql/core": "^5.0.0", + "react": ">= 16.8.0" + } + }, "node_modules/use-sync-external-store": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", @@ -6134,6 +6174,12 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/wonka": { + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/wonka/-/wonka-6.3.5.tgz", + "integrity": "sha512-SSil+ecw6B4/Dm7Pf2sAshKQ5hWFvfyGlfPbEd6A14dOH6VDjrmbY86u6nZvy9omGwwIPFR8V41+of1EezgoUw==", + "license": "MIT" + }, "node_modules/wouter": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/wouter/-/wouter-3.7.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 9d809f0..6e8d3ad 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -17,6 +17,7 @@ "react": "^19.1.0", "react-dom": "^19.1.0", "tailwindcss": "^4.1.11", + "urql": "^4.2.2", "wouter": "^3.7.1" }, "devDependencies": { diff --git a/frontend/src/graphql/generated/fragment-masking.ts b/frontend/src/graphql/generated/fragment-masking.ts new file mode 100644 index 0000000..743a364 --- /dev/null +++ b/frontend/src/graphql/generated/fragment-masking.ts @@ -0,0 +1,87 @@ +/* eslint-disable */ +import type { ResultOf, DocumentTypeDecoration, TypedDocumentNode } from '@graphql-typed-document-node/core'; +import type { FragmentDefinitionNode } from 'graphql'; +import type { Incremental } from './graphql'; + + +export type FragmentType<TDocumentType extends DocumentTypeDecoration<any, any>> = TDocumentType extends DocumentTypeDecoration< + infer TType, + any +> + ? [TType] extends [{ ' $fragmentName'?: infer TKey }] + ? TKey extends string + ? { ' $fragmentRefs'?: { [key in TKey]: TType } } + : never + : never + : never; + +// return non-nullable if `fragmentType` is non-nullable +export function useFragment<TType>( + _documentNode: DocumentTypeDecoration<TType, any>, + fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> +): TType; +// return nullable if `fragmentType` is undefined +export function useFragment<TType>( + _documentNode: DocumentTypeDecoration<TType, any>, + fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | undefined +): TType | undefined; +// return nullable if `fragmentType` is nullable +export function useFragment<TType>( + _documentNode: DocumentTypeDecoration<TType, any>, + fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | null +): TType | null; +// return nullable if `fragmentType` is nullable or undefined +export function useFragment<TType>( + _documentNode: DocumentTypeDecoration<TType, any>, + fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | null | undefined +): TType | null | undefined; +// return array of non-nullable if `fragmentType` is array of non-nullable +export function useFragment<TType>( + _documentNode: DocumentTypeDecoration<TType, any>, + fragmentType: Array<FragmentType<DocumentTypeDecoration<TType, any>>> +): Array<TType>; +// return array of nullable if `fragmentType` is array of nullable +export function useFragment<TType>( + _documentNode: DocumentTypeDecoration<TType, any>, + fragmentType: Array<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined +): Array<TType> | null | undefined; +// return readonly array of non-nullable if `fragmentType` is array of non-nullable +export function useFragment<TType>( + _documentNode: DocumentTypeDecoration<TType, any>, + fragmentType: ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>> +): ReadonlyArray<TType>; +// return readonly array of nullable if `fragmentType` is array of nullable +export function useFragment<TType>( + _documentNode: DocumentTypeDecoration<TType, any>, + fragmentType: ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined +): ReadonlyArray<TType> | null | undefined; +export function useFragment<TType>( + _documentNode: DocumentTypeDecoration<TType, any>, + fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | Array<FragmentType<DocumentTypeDecoration<TType, any>>> | ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined +): TType | Array<TType> | ReadonlyArray<TType> | null | undefined { + return fragmentType as any; +} + + +export function makeFragmentData< + F extends DocumentTypeDecoration<any, any>, + FT extends ResultOf<F> +>(data: FT, _fragment: F): FragmentType<F> { + return data as FragmentType<F>; +} +export function isFragmentReady<TQuery, TFrag>( + queryNode: DocumentTypeDecoration<TQuery, any>, + fragmentNode: TypedDocumentNode<TFrag>, + data: FragmentType<TypedDocumentNode<Incremental<TFrag>, any>> | null | undefined +): data is FragmentType<typeof fragmentNode> { + const deferredFields = (queryNode as { __meta__?: { deferredFields: Record<string, (keyof TFrag)[]> } }).__meta__ + ?.deferredFields; + + if (!deferredFields) return true; + + const fragDef = fragmentNode.definitions[0] as FragmentDefinitionNode | undefined; + const fragName = fragDef?.name?.value; + + const fields = (fragName && deferredFields[fragName]) || []; + return fields.length > 0 && fields.every(field => data && field in data); +} diff --git a/frontend/src/graphql/generated/gql.ts b/frontend/src/graphql/generated/gql.ts new file mode 100644 index 0000000..ae8c1e1 --- /dev/null +++ b/frontend/src/graphql/generated/gql.ts @@ -0,0 +1,52 @@ +/* eslint-disable */ +import * as types from './graphql'; +import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; + +/** + * Map of all GraphQL operations in the project. + * + * This map has several performance disadvantages: + * 1. It is not tree-shakeable, so it will include all operations in the project. + * 2. It is not minifiable, so the string of a GraphQL query will be multiple times inside the bundle. + * 3. It does not support dead code elimination, so it will add unused operations. + * + * Therefore it is highly recommended to use the babel or swc plugin for production. + * Learn more about it here: https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#reducing-bundle-size + */ +type Documents = { + "mutation AddFeed($url: String!) {\n addFeed(url: $url) {\n id\n url\n title\n fetchedAt\n }\n}\n\nmutation RemoveFeed($id: ID!) {\n removeFeed(id: $id)\n}\n\nmutation MarkArticleRead($id: ID!) {\n markArticleRead(id: $id) {\n id\n feedId\n guid\n title\n url\n isRead\n }\n}\n\nmutation MarkArticleUnread($id: ID!) {\n markArticleUnread(id: $id) {\n id\n feedId\n guid\n title\n url\n isRead\n }\n}\n\nmutation MarkFeedRead($id: ID!) {\n markFeedRead(id: $id) {\n id\n url\n title\n fetchedAt\n }\n}\n\nmutation MarkFeedUnread($id: ID!) {\n markFeedUnread(id: $id) {\n id\n url\n title\n fetchedAt\n }\n}": typeof types.AddFeedDocument, + "query GetFeeds {\n feeds {\n id\n url\n title\n fetchedAt\n articles {\n id\n isRead\n }\n }\n}\n\nquery GetUnreadArticles {\n unreadArticles {\n id\n feedId\n guid\n title\n url\n isRead\n feed {\n id\n title\n }\n }\n}\n\nquery GetReadArticles {\n readArticles {\n id\n feedId\n guid\n title\n url\n isRead\n feed {\n id\n title\n }\n }\n}\n\nquery GetFeed($id: ID!) {\n feed(id: $id) {\n id\n url\n title\n fetchedAt\n articles {\n id\n guid\n title\n url\n isRead\n }\n }\n}\n\nquery GetArticle($id: ID!) {\n article(id: $id) {\n id\n feedId\n guid\n title\n url\n isRead\n feed {\n id\n title\n }\n }\n}": typeof types.GetFeedsDocument, +}; +const documents: Documents = { + "mutation AddFeed($url: String!) {\n addFeed(url: $url) {\n id\n url\n title\n fetchedAt\n }\n}\n\nmutation RemoveFeed($id: ID!) {\n removeFeed(id: $id)\n}\n\nmutation MarkArticleRead($id: ID!) {\n markArticleRead(id: $id) {\n id\n feedId\n guid\n title\n url\n isRead\n }\n}\n\nmutation MarkArticleUnread($id: ID!) {\n markArticleUnread(id: $id) {\n id\n feedId\n guid\n title\n url\n isRead\n }\n}\n\nmutation MarkFeedRead($id: ID!) {\n markFeedRead(id: $id) {\n id\n url\n title\n fetchedAt\n }\n}\n\nmutation MarkFeedUnread($id: ID!) {\n markFeedUnread(id: $id) {\n id\n url\n title\n fetchedAt\n }\n}": types.AddFeedDocument, + "query GetFeeds {\n feeds {\n id\n url\n title\n fetchedAt\n articles {\n id\n isRead\n }\n }\n}\n\nquery GetUnreadArticles {\n unreadArticles {\n id\n feedId\n guid\n title\n url\n isRead\n feed {\n id\n title\n }\n }\n}\n\nquery GetReadArticles {\n readArticles {\n id\n feedId\n guid\n title\n url\n isRead\n feed {\n id\n title\n }\n }\n}\n\nquery GetFeed($id: ID!) {\n feed(id: $id) {\n id\n url\n title\n fetchedAt\n articles {\n id\n guid\n title\n url\n isRead\n }\n }\n}\n\nquery GetArticle($id: ID!) {\n article(id: $id) {\n id\n feedId\n guid\n title\n url\n isRead\n feed {\n id\n title\n }\n }\n}": types.GetFeedsDocument, +}; + +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + * + * + * @example + * ```ts + * const query = graphql(`query GetUser($id: ID!) { user(id: $id) { name } }`); + * ``` + * + * The query argument is unknown! + * Please regenerate the types. + */ +export function graphql(source: string): unknown; + +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "mutation AddFeed($url: String!) {\n addFeed(url: $url) {\n id\n url\n title\n fetchedAt\n }\n}\n\nmutation RemoveFeed($id: ID!) {\n removeFeed(id: $id)\n}\n\nmutation MarkArticleRead($id: ID!) {\n markArticleRead(id: $id) {\n id\n feedId\n guid\n title\n url\n isRead\n }\n}\n\nmutation MarkArticleUnread($id: ID!) {\n markArticleUnread(id: $id) {\n id\n feedId\n guid\n title\n url\n isRead\n }\n}\n\nmutation MarkFeedRead($id: ID!) {\n markFeedRead(id: $id) {\n id\n url\n title\n fetchedAt\n }\n}\n\nmutation MarkFeedUnread($id: ID!) {\n markFeedUnread(id: $id) {\n id\n url\n title\n fetchedAt\n }\n}"): (typeof documents)["mutation AddFeed($url: String!) {\n addFeed(url: $url) {\n id\n url\n title\n fetchedAt\n }\n}\n\nmutation RemoveFeed($id: ID!) {\n removeFeed(id: $id)\n}\n\nmutation MarkArticleRead($id: ID!) {\n markArticleRead(id: $id) {\n id\n feedId\n guid\n title\n url\n isRead\n }\n}\n\nmutation MarkArticleUnread($id: ID!) {\n markArticleUnread(id: $id) {\n id\n feedId\n guid\n title\n url\n isRead\n }\n}\n\nmutation MarkFeedRead($id: ID!) {\n markFeedRead(id: $id) {\n id\n url\n title\n fetchedAt\n }\n}\n\nmutation MarkFeedUnread($id: ID!) {\n markFeedUnread(id: $id) {\n id\n url\n title\n fetchedAt\n }\n}"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "query GetFeeds {\n feeds {\n id\n url\n title\n fetchedAt\n articles {\n id\n isRead\n }\n }\n}\n\nquery GetUnreadArticles {\n unreadArticles {\n id\n feedId\n guid\n title\n url\n isRead\n feed {\n id\n title\n }\n }\n}\n\nquery GetReadArticles {\n readArticles {\n id\n feedId\n guid\n title\n url\n isRead\n feed {\n id\n title\n }\n }\n}\n\nquery GetFeed($id: ID!) {\n feed(id: $id) {\n id\n url\n title\n fetchedAt\n articles {\n id\n guid\n title\n url\n isRead\n }\n }\n}\n\nquery GetArticle($id: ID!) {\n article(id: $id) {\n id\n feedId\n guid\n title\n url\n isRead\n feed {\n id\n title\n }\n }\n}"): (typeof documents)["query GetFeeds {\n feeds {\n id\n url\n title\n fetchedAt\n articles {\n id\n isRead\n }\n }\n}\n\nquery GetUnreadArticles {\n unreadArticles {\n id\n feedId\n guid\n title\n url\n isRead\n feed {\n id\n title\n }\n }\n}\n\nquery GetReadArticles {\n readArticles {\n id\n feedId\n guid\n title\n url\n isRead\n feed {\n id\n title\n }\n }\n}\n\nquery GetFeed($id: ID!) {\n feed(id: $id) {\n id\n url\n title\n fetchedAt\n articles {\n id\n guid\n title\n url\n isRead\n }\n }\n}\n\nquery GetArticle($id: ID!) {\n article(id: $id) {\n id\n feedId\n guid\n title\n url\n isRead\n feed {\n id\n title\n }\n }\n}"]; + +export function graphql(source: string) { + return (documents as any)[source] ?? {}; +} + +export type DocumentType<TDocumentNode extends DocumentNode<any, any>> = TDocumentNode extends DocumentNode< infer TType, any> ? TType : never;
\ No newline at end of file diff --git a/frontend/src/graphql/generated/graphql.ts b/frontend/src/graphql/generated/graphql.ts new file mode 100644 index 0000000..a54f1b6 --- /dev/null +++ b/frontend/src/graphql/generated/graphql.ts @@ -0,0 +1,212 @@ +/* eslint-disable */ +import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; +export type Maybe<T> = T | null; +export type InputMaybe<T> = Maybe<T>; +export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] }; +export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> }; +export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> }; +export type MakeEmpty<T extends { [key: string]: unknown }, K extends keyof T> = { [_ in K]?: never }; +export type Incremental<T> = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never }; +/** All built-in and custom scalars, mapped to their actual values */ +export type Scalars = { + ID: { input: string; output: string; } + String: { input: string; output: string; } + Boolean: { input: boolean; output: boolean; } + Int: { input: number; output: number; } + Float: { input: number; output: number; } + DateTime: { input: string; output: string; } +}; + +/** Represents an individual article/post from a feed */ +export type Article = { + /** The feed this article belongs to */ + feed: Feed; + /** ID of the feed this article belongs to */ + feedId: Scalars['ID']['output']; + /** GUID from the RSS/Atom feed (unique identifier from feed) */ + guid: Scalars['String']['output']; + /** Unique identifier for the article */ + id: Scalars['ID']['output']; + /** Whether the article has been marked as read */ + isRead: Scalars['Boolean']['output']; + /** Title of the article */ + title: Scalars['String']['output']; + /** URL/link to the original article */ + url: Scalars['String']['output']; +}; + +/** Represents a feed subscription in the system */ +export type Feed = { + /** Articles belonging to this feed */ + articles: Array<Article>; + /** Timestamp when the feed was last fetched */ + fetchedAt: Scalars['DateTime']['output']; + /** Unique identifier for the feed */ + id: Scalars['ID']['output']; + /** Title of the feed (extracted from feed metadata) */ + title: Scalars['String']['output']; + /** URL of the RSS/Atom feed */ + url: Scalars['String']['output']; +}; + +/** Root mutation type for modifying data */ +export type Mutation = { + /** Add a new feed subscription */ + addFeed: Feed; + /** Mark an article as read */ + markArticleRead: Article; + /** Mark an article as unread */ + markArticleUnread: Article; + /** Mark all articles in a feed as read */ + markFeedRead: Feed; + /** Mark all articles in a feed as unread */ + markFeedUnread: Feed; + /** Remove a feed subscription and all its articles */ + removeFeed: Scalars['Boolean']['output']; +}; + + +/** Root mutation type for modifying data */ +export type MutationAddFeedArgs = { + url: Scalars['String']['input']; +}; + + +/** Root mutation type for modifying data */ +export type MutationMarkArticleReadArgs = { + id: Scalars['ID']['input']; +}; + + +/** Root mutation type for modifying data */ +export type MutationMarkArticleUnreadArgs = { + id: Scalars['ID']['input']; +}; + + +/** Root mutation type for modifying data */ +export type MutationMarkFeedReadArgs = { + id: Scalars['ID']['input']; +}; + + +/** Root mutation type for modifying data */ +export type MutationMarkFeedUnreadArgs = { + id: Scalars['ID']['input']; +}; + + +/** Root mutation type for modifying data */ +export type MutationRemoveFeedArgs = { + id: Scalars['ID']['input']; +}; + +/** Root query type for reading data */ +export type Query = { + /** Get a specific article by ID */ + article?: Maybe<Article>; + /** Get a specific feed by ID */ + feed?: Maybe<Feed>; + /** Get all feeds with their metadata */ + feeds: Array<Feed>; + /** Get all read articles across all feeds */ + readArticles: Array<Article>; + /** Get all unread articles across all feeds */ + unreadArticles: Array<Article>; +}; + + +/** Root query type for reading data */ +export type QueryArticleArgs = { + id: Scalars['ID']['input']; +}; + + +/** Root query type for reading data */ +export type QueryFeedArgs = { + id: Scalars['ID']['input']; +}; + +export type AddFeedMutationVariables = Exact<{ + url: Scalars['String']['input']; +}>; + + +export type AddFeedMutation = { addFeed: { id: string, url: string, title: string, fetchedAt: string } }; + +export type RemoveFeedMutationVariables = Exact<{ + id: Scalars['ID']['input']; +}>; + + +export type RemoveFeedMutation = { removeFeed: boolean }; + +export type MarkArticleReadMutationVariables = Exact<{ + id: Scalars['ID']['input']; +}>; + + +export type MarkArticleReadMutation = { markArticleRead: { id: string, feedId: string, guid: string, title: string, url: string, isRead: boolean } }; + +export type MarkArticleUnreadMutationVariables = Exact<{ + id: Scalars['ID']['input']; +}>; + + +export type MarkArticleUnreadMutation = { markArticleUnread: { id: string, feedId: string, guid: string, title: string, url: string, isRead: boolean } }; + +export type MarkFeedReadMutationVariables = Exact<{ + id: Scalars['ID']['input']; +}>; + + +export type MarkFeedReadMutation = { markFeedRead: { id: string, url: string, title: string, fetchedAt: string } }; + +export type MarkFeedUnreadMutationVariables = Exact<{ + id: Scalars['ID']['input']; +}>; + + +export type MarkFeedUnreadMutation = { markFeedUnread: { id: string, url: string, title: string, fetchedAt: string } }; + +export type GetFeedsQueryVariables = Exact<{ [key: string]: never; }>; + + +export type GetFeedsQuery = { feeds: Array<{ id: string, url: string, title: string, fetchedAt: string, articles: Array<{ id: string, isRead: boolean }> }> }; + +export type GetUnreadArticlesQueryVariables = Exact<{ [key: string]: never; }>; + + +export type GetUnreadArticlesQuery = { unreadArticles: Array<{ id: string, feedId: string, guid: string, title: string, url: string, isRead: boolean, feed: { id: string, title: string } }> }; + +export type GetReadArticlesQueryVariables = Exact<{ [key: string]: never; }>; + + +export type GetReadArticlesQuery = { readArticles: Array<{ id: string, feedId: string, guid: string, title: string, url: string, isRead: boolean, feed: { id: string, title: string } }> }; + +export type GetFeedQueryVariables = Exact<{ + id: Scalars['ID']['input']; +}>; + + +export type GetFeedQuery = { feed?: { id: string, url: string, title: string, fetchedAt: string, articles: Array<{ id: string, guid: string, title: string, url: string, isRead: boolean }> } | null }; + +export type GetArticleQueryVariables = Exact<{ + id: Scalars['ID']['input']; +}>; + + +export type GetArticleQuery = { article?: { id: string, feedId: string, guid: string, title: string, url: string, isRead: boolean, feed: { id: string, title: string } } | null }; + + +export const AddFeedDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"AddFeed"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"url"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"addFeed"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"url"},"value":{"kind":"Variable","name":{"kind":"Name","value":"url"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"fetchedAt"}}]}}]}}]} as unknown as DocumentNode<AddFeedMutation, AddFeedMutationVariables>; +export const RemoveFeedDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RemoveFeed"},"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":"removeFeed"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}]}]}}]} as unknown as DocumentNode<RemoveFeedMutation, RemoveFeedMutationVariables>; +export const MarkArticleReadDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"MarkArticleRead"},"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":"markArticleRead"},"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":"feedId"}},{"kind":"Field","name":{"kind":"Name","value":"guid"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"isRead"}}]}}]}}]} as unknown as DocumentNode<MarkArticleReadMutation, MarkArticleReadMutationVariables>; +export const MarkArticleUnreadDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"MarkArticleUnread"},"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":"markArticleUnread"},"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":"feedId"}},{"kind":"Field","name":{"kind":"Name","value":"guid"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"isRead"}}]}}]}}]} as unknown as DocumentNode<MarkArticleUnreadMutation, MarkArticleUnreadMutationVariables>; +export const MarkFeedReadDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"MarkFeedRead"},"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":"markFeedRead"},"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":"url"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"fetchedAt"}}]}}]}}]} as unknown as DocumentNode<MarkFeedReadMutation, MarkFeedReadMutationVariables>; +export const MarkFeedUnreadDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"MarkFeedUnread"},"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":"markFeedUnread"},"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":"url"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"fetchedAt"}}]}}]}}]} as unknown as DocumentNode<MarkFeedUnreadMutation, MarkFeedUnreadMutationVariables>; +export const GetFeedsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetFeeds"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"feeds"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"fetchedAt"}},{"kind":"Field","name":{"kind":"Name","value":"articles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"isRead"}}]}}]}}]}}]} as unknown as DocumentNode<GetFeedsQuery, GetFeedsQueryVariables>; +export const GetUnreadArticlesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetUnreadArticles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"unreadArticles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"feedId"}},{"kind":"Field","name":{"kind":"Name","value":"guid"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"isRead"}},{"kind":"Field","name":{"kind":"Name","value":"feed"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}}]}}]}}]}}]} as unknown as DocumentNode<GetUnreadArticlesQuery, GetUnreadArticlesQueryVariables>; +export const GetReadArticlesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetReadArticles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"readArticles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"feedId"}},{"kind":"Field","name":{"kind":"Name","value":"guid"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"isRead"}},{"kind":"Field","name":{"kind":"Name","value":"feed"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}}]}}]}}]}}]} as unknown as DocumentNode<GetReadArticlesQuery, GetReadArticlesQueryVariables>; +export const GetFeedDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetFeed"},"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":"feed"},"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":"url"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"fetchedAt"}},{"kind":"Field","name":{"kind":"Name","value":"articles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"guid"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"isRead"}}]}}]}}]}}]} as unknown as DocumentNode<GetFeedQuery, GetFeedQueryVariables>; +export const GetArticleDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetArticle"},"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":"article"},"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":"feedId"}},{"kind":"Field","name":{"kind":"Name","value":"guid"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"isRead"}},{"kind":"Field","name":{"kind":"Name","value":"feed"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}}]}}]}}]}}]} as unknown as DocumentNode<GetArticleQuery, GetArticleQueryVariables>;
\ No newline at end of file diff --git a/frontend/src/graphql/generated/index.ts b/frontend/src/graphql/generated/index.ts new file mode 100644 index 0000000..f515991 --- /dev/null +++ b/frontend/src/graphql/generated/index.ts @@ -0,0 +1,2 @@ +export * from "./fragment-masking"; +export * from "./gql";
\ No newline at end of file diff --git a/frontend/src/graphql/mutations.graphql b/frontend/src/graphql/mutations.graphql new file mode 100644 index 0000000..8bcbcec --- /dev/null +++ b/frontend/src/graphql/mutations.graphql @@ -0,0 +1,52 @@ +mutation AddFeed($url: String!) { + addFeed(url: $url) { + id + url + title + fetchedAt + } +} + +mutation RemoveFeed($id: ID!) { + removeFeed(id: $id) +} + +mutation MarkArticleRead($id: ID!) { + markArticleRead(id: $id) { + id + feedId + guid + title + url + isRead + } +} + +mutation MarkArticleUnread($id: ID!) { + markArticleUnread(id: $id) { + id + feedId + guid + title + url + isRead + } +} + +mutation MarkFeedRead($id: ID!) { + markFeedRead(id: $id) { + id + url + title + fetchedAt + } +} + +mutation MarkFeedUnread($id: ID!) { + markFeedUnread(id: $id) { + id + url + title + fetchedAt + } +} diff --git a/frontend/src/graphql/queries.graphql b/frontend/src/graphql/queries.graphql new file mode 100644 index 0000000..af4ac01 --- /dev/null +++ b/frontend/src/graphql/queries.graphql @@ -0,0 +1,73 @@ +query GetFeeds { + feeds { + id + url + title + fetchedAt + articles { + id + isRead + } + } +} + +query GetUnreadArticles { + unreadArticles { + id + feedId + guid + title + url + isRead + feed { + id + title + } + } +} + +query GetReadArticles { + readArticles { + id + feedId + guid + title + url + isRead + feed { + id + title + } + } +} + +query GetFeed($id: ID!) { + feed(id: $id) { + id + url + title + fetchedAt + articles { + id + guid + title + url + isRead + } + } +} + +query GetArticle($id: ID!) { + article(id: $id) { + id + feedId + guid + title + url + isRead + feed { + id + title + } + } +} |
