diff --git a/generated/gql/fragment-masking.ts b/generated/gql/fragment-masking.ts index 1f96e5a..1183007 100644 --- a/generated/gql/fragment-masking.ts +++ b/generated/gql/fragment-masking.ts @@ -1,11 +1,13 @@ -import type { ResultOf, DocumentTypeDecoration, } from '@graphql-typed-document-node/core'; +import type { ResultOf, DocumentTypeDecoration, TypedDocumentNode } from '@graphql-typed-document-node/core'; +import type { FragmentDefinitionNode } from 'graphql'; +import type { Incremental } from './graphql.js'; export type FragmentType> = TDocumentType extends DocumentTypeDecoration< infer TType, any > - ? TType extends { ' $fragmentName'?: infer TKey } + ? [TType] extends [{ ' $fragmentName'?: infer TKey }] ? TKey extends string ? { ' $fragmentRefs'?: { [key in TKey]: TType } } : never @@ -45,4 +47,20 @@ export function makeFragmentData< FT extends ResultOf >(data: FT, _fragment: F): FragmentType { return data as FragmentType; -} \ No newline at end of file +} +export function isFragmentReady( + queryNode: DocumentTypeDecoration, + fragmentNode: TypedDocumentNode, + data: FragmentType, any>> | null | undefined +): data is FragmentType { + const deferredFields = (queryNode as { __meta__?: { deferredFields: Record } }).__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/generated/gql/gql.ts b/generated/gql/gql.ts index d35ec8a..3bc1f04 100644 --- a/generated/gql/gql.ts +++ b/generated/gql/gql.ts @@ -13,7 +13,7 @@ import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document- * Therefore it is highly recommended to use the babel or swc plugin for production. */ const documents = { - "\n query Me {\n books: kindleBooks {\n title\n author\n asin\n coverUrl\n progress\n readAt\n }\n\n likedSongs: spotifyLikedSongs {\n likedAt\n song {\n title\n artist\n coverUrl\n spotifyUrl\n previewUrl\n durationMs\n }\n }\n\n tv {\n simklId\n title\n lastWatchedAt\n episode\n nextEpisode\n simklLink\n }\n }\n": types.MeDocument, + "\n query Me($filter: KindleFilterType) {\n books: kindleBooks(filter: $filter) {\n title\n author\n asin\n coverUrl\n progress\n readAt\n }\n\n likedSongs: spotifyLikedSongs {\n likedAt\n song {\n title\n artist\n coverUrl\n spotifyUrl\n previewUrl\n durationMs\n }\n }\n\n tv {\n simklId\n title\n lastWatchedAt\n episode\n nextEpisode\n simklLink\n }\n }\n": types.MeDocument, }; /** @@ -33,7 +33,7 @@ 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: "\n query Me {\n books: kindleBooks {\n title\n author\n asin\n coverUrl\n progress\n readAt\n }\n\n likedSongs: spotifyLikedSongs {\n likedAt\n song {\n title\n artist\n coverUrl\n spotifyUrl\n previewUrl\n durationMs\n }\n }\n\n tv {\n simklId\n title\n lastWatchedAt\n episode\n nextEpisode\n simklLink\n }\n }\n"): (typeof documents)["\n query Me {\n books: kindleBooks {\n title\n author\n asin\n coverUrl\n progress\n readAt\n }\n\n likedSongs: spotifyLikedSongs {\n likedAt\n song {\n title\n artist\n coverUrl\n spotifyUrl\n previewUrl\n durationMs\n }\n }\n\n tv {\n simklId\n title\n lastWatchedAt\n episode\n nextEpisode\n simklLink\n }\n }\n"]; +export function graphql(source: "\n query Me($filter: KindleFilterType) {\n books: kindleBooks(filter: $filter) {\n title\n author\n asin\n coverUrl\n progress\n readAt\n }\n\n likedSongs: spotifyLikedSongs {\n likedAt\n song {\n title\n artist\n coverUrl\n spotifyUrl\n previewUrl\n durationMs\n }\n }\n\n tv {\n simklId\n title\n lastWatchedAt\n episode\n nextEpisode\n simklLink\n }\n }\n"): (typeof documents)["\n query Me($filter: KindleFilterType) {\n books: kindleBooks(filter: $filter) {\n title\n author\n asin\n coverUrl\n progress\n readAt\n }\n\n likedSongs: spotifyLikedSongs {\n likedAt\n song {\n title\n artist\n coverUrl\n spotifyUrl\n previewUrl\n durationMs\n }\n }\n\n tv {\n simklId\n title\n lastWatchedAt\n episode\n nextEpisode\n simklLink\n }\n }\n"]; export function graphql(source: string) { return (documents as any)[source] ?? {}; diff --git a/generated/gql/graphql.ts b/generated/gql/graphql.ts index 15e1cf7..d97788e 100644 --- a/generated/gql/graphql.ts +++ b/generated/gql/graphql.ts @@ -5,34 +5,38 @@ export type InputMaybe = Maybe; export type Exact = { [K in keyof T]: T[K] }; export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; +export type MakeEmpty = { [_ in K]?: never }; +export type Incremental = 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: string; - String: string; - Boolean: boolean; - Int: number; - Float: number; + 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; } /** A date-time string at UTC, such as 2007-12-03T10:15:30Z, compliant with the `date-time` format outlined in section 5.6 of the RFC 3339 profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar. */ - Date: any; + Date: { input: any; output: any; } }; export type Book = { __typename?: 'Book'; /** Amazon Standard Identification Number */ - asin: Scalars['String']; + asin: Scalars['String']['output']; /** Author of the book */ - author: Scalars['String']; + author: Scalars['String']['output']; /** URL to the cover image */ - coverUrl: Scalars['String']; - device: Scalars['String']; + coverUrl: Scalars['String']['output']; + device?: Maybe; /** The first time this book was seen on the Kindle API */ - firstSeenAt: Scalars['Date']; + firstSeenAt: Scalars['Date']['output']; + /** Whether this book was purchased */ + isPurchased: Scalars['Boolean']['output']; /** Percentage of the book read. Books with 0 percentage reads are not shown. */ - progress: Scalars['Float']; + progress: Scalars['Float']['output']; /** The last date this book was read */ - readAt?: Maybe; + readAt?: Maybe; /** Title of the book */ - title: Scalars['String']; + title: Scalars['String']['output']; }; export enum ImageSize { @@ -44,10 +48,17 @@ export enum ImageSize { Small = 'SMALL' } +export enum KindleFilterType { + /** Include all read books seen in the user's library */ + All = 'ALL', + /** Include only books purchased by the user, not samples */ + Purchased = 'PURCHASED' +} + export type LikedSong = { __typename?: 'LikedSong'; /** The date the song was liked */ - likedAt?: Maybe; + likedAt?: Maybe; song: Song; }; @@ -58,23 +69,24 @@ export type Query = { tv: Array; }; + +export type QueryKindleBooksArgs = { + filter?: InputMaybe; +}; + export type Song = { __typename?: 'Song'; - /** Album of the song */ - album: Scalars['String']; - /** Artist of the song */ - artist: Scalars['String']; + album: Scalars['String']['output']; + artist: Scalars['String']['output']; /** Album art of the song */ - coverUrl: Scalars['String']; - /** Duration of the song in milliseconds */ - durationMs: Scalars['Int']; - id: Scalars['ID']; + coverUrl: Scalars['String']['output']; + durationMs: Scalars['Int']['output']; + id: Scalars['ID']['output']; /** A 30 second preview of the song */ - previewUrl?: Maybe; + previewUrl?: Maybe; /** Spotify URL of the song */ - spotifyUrl?: Maybe; - /** Title of the song */ - title: Scalars['String']; + spotifyUrl?: Maybe; + title: Scalars['String']['output']; }; @@ -84,21 +96,23 @@ export type SongCoverUrlArgs = { export type Tv = { __typename?: 'TV'; - coverUrl?: Maybe; - createdAt: Scalars['Date']; - episode: Scalars['String']; - lastWatchedAt?: Maybe; - nextEpisode?: Maybe; - simklId: Scalars['String']; - simklLink?: Maybe; - title: Scalars['String']; - updatedAt: Scalars['Date']; + coverUrl?: Maybe; + createdAt: Scalars['Date']['output']; + episode: Scalars['String']['output']; + lastWatchedAt?: Maybe; + nextEpisode?: Maybe; + simklId: Scalars['String']['output']; + simklLink?: Maybe; + title: Scalars['String']['output']; + updatedAt: Scalars['Date']['output']; }; -export type MeQueryVariables = Exact<{ [key: string]: never; }>; +export type MeQueryVariables = Exact<{ + filter?: InputMaybe; +}>; export type MeQuery = { __typename?: 'Query', books: Array<{ __typename?: 'Book', title: string, author: string, asin: string, coverUrl: string, progress: number, readAt?: any | null }>, likedSongs: Array<{ __typename?: 'LikedSong', likedAt?: any | null, song: { __typename?: 'Song', title: string, artist: string, coverUrl: string, spotifyUrl?: string | null, previewUrl?: string | null, durationMs: number } }>, tv: Array<{ __typename?: 'TV', simklId: string, title: string, lastWatchedAt?: any | null, episode: string, nextEpisode?: string | null, simklLink?: string | null }> }; -export const MeDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Me"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"books"},"name":{"kind":"Name","value":"kindleBooks"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"author"}},{"kind":"Field","name":{"kind":"Name","value":"asin"}},{"kind":"Field","name":{"kind":"Name","value":"coverUrl"}},{"kind":"Field","name":{"kind":"Name","value":"progress"}},{"kind":"Field","name":{"kind":"Name","value":"readAt"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"likedSongs"},"name":{"kind":"Name","value":"spotifyLikedSongs"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"likedAt"}},{"kind":"Field","name":{"kind":"Name","value":"song"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"artist"}},{"kind":"Field","name":{"kind":"Name","value":"coverUrl"}},{"kind":"Field","name":{"kind":"Name","value":"spotifyUrl"}},{"kind":"Field","name":{"kind":"Name","value":"previewUrl"}},{"kind":"Field","name":{"kind":"Name","value":"durationMs"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"tv"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"simklId"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"lastWatchedAt"}},{"kind":"Field","name":{"kind":"Name","value":"episode"}},{"kind":"Field","name":{"kind":"Name","value":"nextEpisode"}},{"kind":"Field","name":{"kind":"Name","value":"simklLink"}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file +export const MeDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Me"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"KindleFilterType"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"books"},"name":{"kind":"Name","value":"kindleBooks"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"author"}},{"kind":"Field","name":{"kind":"Name","value":"asin"}},{"kind":"Field","name":{"kind":"Name","value":"coverUrl"}},{"kind":"Field","name":{"kind":"Name","value":"progress"}},{"kind":"Field","name":{"kind":"Name","value":"readAt"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"likedSongs"},"name":{"kind":"Name","value":"spotifyLikedSongs"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"likedAt"}},{"kind":"Field","name":{"kind":"Name","value":"song"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"artist"}},{"kind":"Field","name":{"kind":"Name","value":"coverUrl"}},{"kind":"Field","name":{"kind":"Name","value":"spotifyUrl"}},{"kind":"Field","name":{"kind":"Name","value":"previewUrl"}},{"kind":"Field","name":{"kind":"Name","value":"durationMs"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"tv"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"simklId"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"lastWatchedAt"}},{"kind":"Field","name":{"kind":"Name","value":"episode"}},{"kind":"Field","name":{"kind":"Name","value":"nextEpisode"}},{"kind":"Field","name":{"kind":"Name","value":"simklLink"}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/src/components/kindle/BookGrid.astro b/src/components/kindle/BookGrid.astro index 6ee8333..6266667 100644 --- a/src/components/kindle/BookGrid.astro +++ b/src/components/kindle/BookGrid.astro @@ -5,6 +5,10 @@ import SectionHeader from "🧱/SectionHeader.astro"; import Book from "./Book.astro"; import { meQuery } from "🛠️/me"; +const booksMyGfWasReadingOnMyKindleThatIDontWantShowingOnMySite = [ + "Tall Tales & Witchy Fails (A Witch on the Rocks Cozy Mystery Book 1)", +]; + interface Props { class?: string; } @@ -14,7 +18,14 @@ function skewer(word: string) { } const { books } = await meQuery; const SHOW_COUNT = 10; -const showing = books.slice(0, SHOW_COUNT) ?? []; +const showing = + books + .filter((book) => + booksMyGfWasReadingOnMyKindleThatIDontWantShowingOnMySite.every( + (title) => title !== book.title, + ), + ) + .slice(0, SHOW_COUNT) ?? []; const className = "book-item rounded"; --- diff --git a/src/scripts/me.ts b/src/scripts/me.ts index e48ae96..8d59bda 100644 --- a/src/scripts/me.ts +++ b/src/scripts/me.ts @@ -4,8 +4,8 @@ import { GRAPHQL_URL, executeOperation } from "./graphql"; import type { MeQuery } from "generated/gql/graphql"; const query = graphql(` - query Me { - books: kindleBooks { + query Me($filter: KindleFilterType) { + books: kindleBooks(filter: $filter) { title author asin @@ -37,7 +37,9 @@ const query = graphql(` } `); -export const meQuery = executeOperation(GRAPHQL_URL, query).then((r) => { +export const meQuery = executeOperation(GRAPHQL_URL, query, { + filter: "PURCHASED", +}).then((r) => { if (r.errors) { console.log(inspect(r.errors, { depth: Infinity, colors: true })); throw new Error("Failed to execute GraphQL query");