From 169cb0a12e2c28bc33647bbf810aba101be340cf Mon Sep 17 00:00:00 2001 From: hoangvu12 Date: Sun, 8 Sep 2024 17:55:25 +0700 Subject: [PATCH] feat: random anime button --- src/gql/gql.ts | 8 ++ src/gql/graphql.ts | 83 ++++++++++++++++++ src/screens/anime/components/random-anime.tsx | 86 +++++++++++++++++++ src/screens/anime/components/your-list.tsx | 2 +- src/screens/anime/screen.tsx | 7 +- 5 files changed, 184 insertions(+), 2 deletions(-) create mode 100644 src/screens/anime/components/random-anime.tsx diff --git a/src/gql/gql.ts b/src/gql/gql.ts index 146b496..bea037e 100644 --- a/src/gql/gql.ts +++ b/src/gql/gql.ts @@ -31,6 +31,8 @@ const documents = { types.AiringScheduleDocument, '\n query PopularThisSeason($season: MediaSeason, $seasonYear: Int) {\n Page(page: 1, perPage: 10) {\n media(\n type: ANIME\n sort: [POPULARITY_DESC]\n season: $season\n seasonYear: $seasonYear\n isAdult: false\n countryOfOrigin: "JP"\n ) {\n ...CardMedia\n }\n }\n }\n': types.PopularThisSeasonDocument, + '\n query GetMedia($page: Int!) {\n Page(page: $page, perPage: 1) {\n media(type: ANIME, isAdult: false) {\n id\n }\n }\n }\n': + types.GetMediaDocument, '\n query UpcomingNextSeason($season: MediaSeason, $seasonYear: Int) {\n Page(page: 1, perPage: 10) {\n media(\n type: ANIME\n sort: [POPULARITY_DESC]\n season: $season\n seasonYear: $seasonYear\n isAdult: false\n countryOfOrigin: "JP"\n ) {\n ...CardMedia\n }\n }\n }\n': types.UpcomingNextSeasonDocument, '\n fragment WatchCard on Media {\n id\n title {\n userPreferred\n }\n coverImage {\n large\n }\n bannerImage\n ...MediaUnitStatsMedia\n }\n': @@ -163,6 +165,12 @@ export function graphql( export function graphql( source: '\n query PopularThisSeason($season: MediaSeason, $seasonYear: Int) {\n Page(page: 1, perPage: 10) {\n media(\n type: ANIME\n sort: [POPULARITY_DESC]\n season: $season\n seasonYear: $seasonYear\n isAdult: false\n countryOfOrigin: "JP"\n ) {\n ...CardMedia\n }\n }\n }\n' ): (typeof documents)['\n query PopularThisSeason($season: MediaSeason, $seasonYear: Int) {\n Page(page: 1, perPage: 10) {\n media(\n type: ANIME\n sort: [POPULARITY_DESC]\n season: $season\n seasonYear: $seasonYear\n isAdult: false\n countryOfOrigin: "JP"\n ) {\n ...CardMedia\n }\n }\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: '\n query GetMedia($page: Int!) {\n Page(page: $page, perPage: 1) {\n media(type: ANIME, isAdult: false) {\n id\n }\n }\n }\n' +): (typeof documents)['\n query GetMedia($page: Int!) {\n Page(page: $page, perPage: 1) {\n media(type: ANIME, isAdult: false) {\n id\n }\n }\n }\n']; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/src/gql/graphql.ts b/src/gql/graphql.ts index 3ffd504..3b02e41 100644 --- a/src/gql/graphql.ts +++ b/src/gql/graphql.ts @@ -4787,6 +4787,18 @@ export type PopularThisSeasonQuery = { } | null; }; +export type GetMediaQueryVariables = Exact<{ + page: Scalars['Int']['input']; +}>; + +export type GetMediaQuery = { + __typename?: 'Query'; + Page?: { + __typename?: 'Page'; + media?: Array<{ __typename?: 'Media'; id: number } | null> | null; + } | null; +}; + export type UpcomingNextSeasonQueryVariables = Exact<{ season?: InputMaybe; seasonYear?: InputMaybe; @@ -9623,6 +9635,77 @@ export const PopularThisSeasonDocument = { PopularThisSeasonQuery, PopularThisSeasonQueryVariables >; +export const GetMediaDocument = { + kind: 'Document', + definitions: [ + { + kind: 'OperationDefinition', + operation: 'query', + name: { kind: 'Name', value: 'GetMedia' }, + variableDefinitions: [ + { + kind: 'VariableDefinition', + variable: { kind: 'Variable', name: { kind: 'Name', value: 'page' } }, + type: { + kind: 'NonNullType', + type: { kind: 'NamedType', name: { kind: 'Name', value: 'Int' } }, + }, + }, + ], + selectionSet: { + kind: 'SelectionSet', + selections: [ + { + kind: 'Field', + name: { kind: 'Name', value: 'Page' }, + arguments: [ + { + kind: 'Argument', + name: { kind: 'Name', value: 'page' }, + value: { + kind: 'Variable', + name: { kind: 'Name', value: 'page' }, + }, + }, + { + kind: 'Argument', + name: { kind: 'Name', value: 'perPage' }, + value: { kind: 'IntValue', value: '1' }, + }, + ], + selectionSet: { + kind: 'SelectionSet', + selections: [ + { + kind: 'Field', + name: { kind: 'Name', value: 'media' }, + arguments: [ + { + kind: 'Argument', + name: { kind: 'Name', value: 'type' }, + value: { kind: 'EnumValue', value: 'ANIME' }, + }, + { + kind: 'Argument', + name: { kind: 'Name', value: 'isAdult' }, + value: { kind: 'BooleanValue', value: false }, + }, + ], + selectionSet: { + kind: 'SelectionSet', + selections: [ + { kind: 'Field', name: { kind: 'Name', value: 'id' } }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + ], +} as unknown as DocumentNode; export const UpcomingNextSeasonDocument = { kind: 'Document', definitions: [ diff --git a/src/screens/anime/components/random-anime.tsx b/src/screens/anime/components/random-anime.tsx new file mode 100644 index 0000000..1e2ec79 --- /dev/null +++ b/src/screens/anime/components/random-anime.tsx @@ -0,0 +1,86 @@ +import { useFocusEffect, useNavigation } from '@react-navigation/native'; +import { ShuffleIcon } from 'lucide-react-native'; +import React, { useState } from 'react'; +import { ToastAndroid } from 'react-native'; + +import { graphql } from '@/gql'; +import anilistClient from '@/services/anilist'; +import { ActivityIndicator, Button, Text } from '@/ui'; + +const randomAnimeDocument = graphql(` + query GetMedia($page: Int!) { + Page(page: $page, perPage: 1) { + media(type: ANIME, isAdult: false) { + id + } + } + } +`); + +// Last updated: 2024-09-08 +const totalAnimeCount = 17151; + +const RandomAnime = () => { + const navigation = useNavigation(); + + const [isLoading, setIsLoading] = useState(false); + + const [shouldShowLuckyText, setShouldShowLuckyText] = useState(false); + + useFocusEffect(() => { + // 20% chance to show the lucky text + if (Math.random() < 0.2) { + setShouldShowLuckyText(true); + } + }); + + const handlePress = async () => { + setIsLoading(true); + + const randomPage = Math.ceil(Math.random() * totalAnimeCount); + + const randomAnimeResponse = await anilistClient.request( + randomAnimeDocument, + { + page: randomPage, + } + ); + + const id = randomAnimeResponse?.Page?.media?.[0]?.id; + + setIsLoading(false); + + if (!id) { + ToastAndroid.show( + 'Cannot find any anime, you are unlucky!', + ToastAndroid.SHORT + ); + + return; + } + + navigation.navigate('AnimeDetails', { + mediaId: id, + }); + }; + + return ( + + ); +}; + +export default RandomAnime; diff --git a/src/screens/anime/components/your-list.tsx b/src/screens/anime/components/your-list.tsx index cbf6656..609666a 100644 --- a/src/screens/anime/components/your-list.tsx +++ b/src/screens/anime/components/your-list.tsx @@ -12,7 +12,7 @@ const YourList = () => { navigation.navigate('AnimeList'); }} variant="defaults" - className="mt-4 bg-thunder-800" + className="mr-2 grow bg-thunder-800" > Your Anime List diff --git a/src/screens/anime/screen.tsx b/src/screens/anime/screen.tsx index 94cf814..d81602e 100644 --- a/src/screens/anime/screen.tsx +++ b/src/screens/anime/screen.tsx @@ -17,6 +17,7 @@ import { BannerList } from '@/ui/banner-card'; import AiringTodayList from './components/airing-today-list'; import GenreList from './components/genre-list'; import PopularThisSeason from './components/popular-this-season'; +import RandomAnime from './components/random-anime'; import UpcomingNextSeason from './components/upcoming-next-season'; import WatchedList from './components/watched-list'; import YourList from './components/your-list'; @@ -37,7 +38,11 @@ export const AnimeHomeScreen = () => { - + + + + + Genres