From ba6ed8967cebf204a8b6c7420f549134226d0590 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 30 Jul 2021 09:36:25 -0400 Subject: [PATCH 001/106] Update Kitsu GraphQL schema --- src/AnimeClient/API/Kitsu/schema.graphql | 500 ++++++++++++++++++++++- 1 file changed, 485 insertions(+), 15 deletions(-) diff --git a/src/AnimeClient/API/Kitsu/schema.graphql b/src/AnimeClient/API/Kitsu/schema.graphql index 49c62187..81806a10 100644 --- a/src/AnimeClient/API/Kitsu/schema.graphql +++ b/src/AnimeClient/API/Kitsu/schema.graphql @@ -115,6 +115,18 @@ interface Media { "Returns the last _n_ elements from the list." last: Int ): MappingConnection! + "A list of your wiki submissions for this media" + myWikiSubmissions( + "Returns the elements in the list that come after the specified cursor." + after: String, + "Returns the elements in the list that come before the specified cursor." + before: String, + "Returns the first _n_ elements from the list." + first: Int, + "Returns the last _n_ elements from the list." + last: Int, + sort: [WikiSubmissionSortOption] + ): WikiSubmissionConnection! "The time of the next release of this media" nextRelease: ISO8601DateTime "The country in which the media was primarily produced" @@ -219,6 +231,9 @@ union FavoriteItemUnion = Anime | Character | Manga | Person "Objects which are Mappable" union MappingItemUnion = Anime | Category | Character | Episode | Manga | Person | Producer +"Objects which are Reportable" +union ReportItemUnion = Comment | MediaReaction | Post | Review + "A user account on Kitsu" type Account implements WithTimestamps { "The country this user resides in" @@ -252,6 +267,19 @@ type Account implements WithTimestamps { updatedAt: ISO8601DateTime! } +type AccountMutations { + "Send a password reset email" + resetPassword( + "The email address to reset the password for" + email: String! + ): AccountResetPasswordPayload +} + +"Autogenerated return type of AccountResetPassword" +type AccountResetPasswordPayload { + email: String! +} + type Anime implements Episodic & Media & WithTimestamps { "The recommended minimum age group for this media" ageRating: AgeRatingEnum @@ -318,6 +346,18 @@ type Anime implements Episodic & Media & WithTimestamps { "Returns the last _n_ elements from the list." last: Int ): MappingConnection! + "A list of your wiki submissions for this media" + myWikiSubmissions( + "Returns the elements in the list that come after the specified cursor." + after: String, + "Returns the elements in the list that come before the specified cursor." + before: String, + "Returns the first _n_ elements from the list." + first: Int, + "Returns the last _n_ elements from the list." + last: Int, + sort: [WikiSubmissionSortOption] + ): WikiSubmissionConnection! "The time of the next release of this media" nextRelease: ISO8601DateTime "The country in which the media was primarily produced" @@ -647,9 +687,9 @@ type Comment implements WithTimestamps { "The user who created this comment for the parent post." author: Profile! "Unmodified content." - content: String! + content: String "Html formatted content." - contentFormatted: String! + contentFormatted: String createdAt: ISO8601DateTime! id: ID! "Users who liked this comment" @@ -811,6 +851,47 @@ type FavoriteEdge { node: Favorite } +"Related media grouped together" +type Franchise implements WithTimestamps { + createdAt: ISO8601DateTime! + id: ID! + "All media related to a franchise" + installments( + "Returns the elements in the list that come after the specified cursor." + after: String, + "Returns the elements in the list that come before the specified cursor." + before: String, + "Returns the first _n_ elements from the list." + first: Int, + "Returns the last _n_ elements from the list." + last: Int, + sort: [InstallmentSortOption] + ): InstallmentConnection + "The name of this franchise in various languages" + titles: TitlesList! + updatedAt: ISO8601DateTime! +} + +"The connection type for Franchise." +type FranchiseConnection { + "A list of edges." + edges: [FranchiseEdge] + "A list of nodes." + nodes: [Franchise] + "Information to aid in pagination." + pageInfo: PageInfo! + "The total amount of nodes." + totalCount: Int! +} + +"An edge in a connection." +type FranchiseEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: Franchise +} + type Generic implements Error { "The error code." code: String @@ -833,19 +914,54 @@ type Image { views(names: [String!]): [ImageView!]! } -type ImageView implements WithTimestamps { - createdAt: ISO8601DateTime! +type ImageView { "The height of the image" height: Int "The name of this view of the image" name: String! - updatedAt: ISO8601DateTime! "The URL of this view of the image" url: String! "The width of the image" width: Int } +"Individual media that belongs to a franchise" +type Installment implements WithTimestamps { + "Order based chronologically" + alternativeOrder: Int + createdAt: ISO8601DateTime! + "The franchise related to this installment" + franchise: Franchise! + id: ID! + "The media related to this installment" + media: Media! + "Order based by date released" + releaseOrder: Int + "Further explains the media relationship corresponding to a franchise" + tag: InstallmentTagEnum + updatedAt: ISO8601DateTime! +} + +"The connection type for Installment." +type InstallmentConnection { + "A list of edges." + edges: [InstallmentEdge] + "A list of nodes." + nodes: [Installment] + "Information to aid in pagination." + pageInfo: PageInfo! + "The total amount of nodes." + totalCount: Int! +} + +"An edge in a connection." +type InstallmentEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: Installment +} + "The user library filterable by media_type and status" type Library { "All Library Entries for a specific Media" @@ -1201,6 +1317,18 @@ type Manga implements Media & WithTimestamps { "Returns the last _n_ elements from the list." last: Int ): MappingConnection! + "A list of your wiki submissions for this media" + myWikiSubmissions( + "Returns the elements in the list that come after the specified cursor." + after: String, + "Returns the elements in the list that come before the specified cursor." + before: String, + "Returns the first _n_ elements from the list." + first: Int, + "Returns the last _n_ elements from the list." + last: Int, + sort: [WikiSubmissionSortOption] + ): WikiSubmissionConnection! "The time of the next release of this media" nextRelease: ISO8601DateTime "The country in which the media was primarily produced" @@ -1567,12 +1695,14 @@ type MediaStaffEdge { } type Mutation { + account: AccountMutations! anime: AnimeMutations! episode: EpisodeMutations! libraryEntry: LibraryEntryMutations! mapping: MappingMutations! post: PostMutations! pro: ProMutations! + wikiSubmission: WikiSubmissionMutations! } "Information about pagination in a connection." @@ -1645,9 +1775,9 @@ type Post implements WithTimestamps { sort: [CommentSortOption] ): CommentConnection! "Unmodified content." - content: String! + content: String "Html formatted content." - contentFormatted: String! + contentFormatted: String createdAt: ISO8601DateTime! "Users that are watching this post" follows( @@ -1718,12 +1848,12 @@ type PostMutations { "Lock a Post." lock( "Lock a Post." - input: LockInput! + input: PostLockInput! ): PostLockPayload "Unlock a Post." unlock( "Unlock a Post." - input: UnlockInput! + input: PostUnlockInput! ): PostUnlockPayload } @@ -1854,9 +1984,11 @@ type Profile implements WithTimestamps { before: String, "Returns the first _n_ elements from the list." first: Int, - kind: [LibraryEventKindEnum!], + "Will return all if not supplied" + kind: [LibraryEventKindEnum!] = [PROGRESSED, UPDATED, REACTED, RATED, ANNOTATED], "Returns the last _n_ elements from the list." - last: Int + last: Int, + sort: [LibraryEventSortOption] ): LibraryEventConnection! "The user's general location" location: String @@ -1891,6 +2023,18 @@ type Profile implements WithTimestamps { proMessage: String "The PRO level the user currently has" proTier: ProTierEnum + "Reviews created by this user" + reviews( + "Returns the elements in the list that come after the specified cursor." + after: String, + "Returns the elements in the list that come before the specified cursor." + before: String, + "Returns the first _n_ elements from the list." + first: Int, + "Returns the last _n_ elements from the list." + last: Int, + sort: [WikiSubmissionSortOption] + ): ReviewConnection "Links to the user on other (social media) sites." siteLinks( "Returns the elements in the list that come after the specified cursor." @@ -1913,6 +2057,20 @@ type Profile implements WithTimestamps { waifu: Character "The properly-gendered term for the user's waifu. This should normally only be 'Waifu' or 'Husbando' but some people are jerks, including the person who wrote this..." waifuOrHusbando: String + "Wiki submissions created by this user" + wikiSubmissions( + "Returns the elements in the list that come after the specified cursor." + after: String, + "Returns the elements in the list that come before the specified cursor." + before: String, + "Returns the first _n_ elements from the list." + first: Int, + "Returns the last _n_ elements from the list." + last: Int, + sort: [WikiSubmissionSortOption], + "Will return all if not supplied" + statuses: [WikiSubmissionStatusEnum!] = [DRAFT, PENDING, APPROVED, REJECTED] + ): WikiSubmissionConnection! } "The connection type for Profile." @@ -2014,6 +2172,21 @@ type Query { findProfileById(id: ID!): Profile "Find a single User by Slug" findProfileBySlug(slug: String!): Profile + "Find a single Report by ID" + findReportById(id: ID!): Report + "Find a single Wiki Submission by ID" + findWikiSubmissionById(id: ID!): WikiSubmission + "All Franchise in the Kitsu database" + franchises( + "Returns the elements in the list that come after the specified cursor." + after: String, + "Returns the elements in the list that come before the specified cursor." + before: String, + "Returns the first _n_ elements from the list." + first: Int, + "Returns the last _n_ elements from the list." + last: Int + ): FranchiseConnection "List trending media on Kitsu" globalTrending( "Returns the elements in the list that come after the specified cursor." @@ -2101,6 +2274,30 @@ type Query { ): ProfileConnection! "Random anime or manga" randomMedia(ageRatings: [AgeRatingEnum!]!, mediaType: MediaTypeEnum!): Media! + "All Reports in the Kitsu database" + reports( + "Returns the elements in the list that come after the specified cursor." + after: String, + "Returns the elements in the list that come before the specified cursor." + before: String, + "Returns the first _n_ elements from the list." + first: Int, + "Returns the last _n_ elements from the list." + last: Int + ): ReportConnection + "Select all Reports that match with a supplied status." + reportsByStatus( + "Returns the elements in the list that come after the specified cursor." + after: String, + "Returns the elements in the list that come before the specified cursor." + before: String, + "Returns the first _n_ elements from the list." + first: Int, + "Returns the last _n_ elements from the list." + last: Int, + "Will return all if not supplied" + statuses: [ReportStatusEnum!] = [REPORTED, RESOLVED, DECLINED] + ): ReportConnection "Search for Anime by title using Algolia. The most relevant results will be at the top." searchAnimeByTitle( "Returns the elements in the list that come after the specified cursor." @@ -2153,6 +2350,20 @@ type Query { ): ProfileConnection "Get your current session info" session: Session! + "Select all Wiki Submissions that match with a supplied status." + wikiSubmissionsByStatuses( + "Returns the elements in the list that come after the specified cursor." + after: String, + "Returns the elements in the list that come before the specified cursor." + before: String, + "Returns the first _n_ elements from the list." + first: Int, + "Returns the last _n_ elements from the list." + last: Int, + sort: [WikiSubmissionSortOption], + "Will return all if not supplied" + statuses: [WikiSubmissionStatusEnum!] = [DRAFT, PENDING, APPROVED, REJECTED] + ): WikiSubmissionConnection } "A quote from a media" @@ -2228,6 +2439,101 @@ type QuoteLineEdge { node: QuoteLine } +"A report made by a user" +type Report implements WithTimestamps { + createdAt: ISO8601DateTime! + "Additional information related to why the report was made" + explanation: String + id: ID! + "The moderator who responded to this report" + moderator: Profile + "The entity that the report is related to" + naughty: ReportItemUnion! + "The reason for why the report was made" + reason: ReportReasonEnum! + "The user who made this report" + reporter: Profile! + "The resolution status for this report" + status: ReportStatusEnum! + updatedAt: ISO8601DateTime! +} + +"The connection type for Report." +type ReportConnection { + "A list of edges." + edges: [ReportEdge] + "A list of nodes." + nodes: [Report] + "Information to aid in pagination." + pageInfo: PageInfo! + "The total amount of nodes." + totalCount: Int! +} + +"An edge in a connection." +type ReportEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: Report +} + +"A media review made by a user" +type Review implements WithTimestamps { + "The author who wrote this review." + author: Profile! + "The review data" + content: String! + createdAt: ISO8601DateTime! + "The review data formatted" + formattedContent: String! + id: ID! + "Does this review contain spoilers from the media" + isSpoiler: Boolean! + "The library entry related to this review." + libraryEntry: LibraryEntry! + "Users who liked this review" + likes( + "Returns the elements in the list that come after the specified cursor." + after: String, + "Returns the elements in the list that come before the specified cursor." + before: String, + "Returns the first _n_ elements from the list." + first: Int, + "Returns the last _n_ elements from the list." + last: Int + ): ProfileConnection! + "The media related to this review." + media: Media! + "When this review was written based on media progress." + progress: Int! + "The user rating for this media" + rating: Int! + "Potentially migrated over from hummingbird." + source: String! + updatedAt: ISO8601DateTime! +} + +"The connection type for Review." +type ReviewConnection { + "A list of edges." + edges: [ReviewEdge] + "A list of nodes." + nodes: [Review] + "Information to aid in pagination." + pageInfo: PageInfo! + "The total amount of nodes." + totalCount: Int! +} + +"An edge in a connection." +type ReviewEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: Review +} + "Information about a user session" type Session { "The account associated with this session" @@ -2417,6 +2723,79 @@ type Volume implements WithTimestamps { updatedAt: ISO8601DateTime! } +"A Wiki Submission is used to either create or edit existing data in our database. This will allow a simple and convient way for users to submit issues/corrections without all the work being left to the mods." +type WikiSubmission implements WithTimestamps { + "The user who created this draft" + author: Profile! + createdAt: ISO8601DateTime! + "The full object that holds all the details for any modifications/additions/deletions made to the entity you are editing. This will be validated using JSON Schema." + data: JSON + id: ID! + "Any additional information that may need to be provided related to the Wiki Submission" + notes: String + "The status of the Wiki Submission" + status: WikiSubmissionStatusEnum! + "The title given to the Wiki Submission. This will default to the title of what is being edited." + title: String + updatedAt: ISO8601DateTime! +} + +"The connection type for WikiSubmission." +type WikiSubmissionConnection { + "A list of edges." + edges: [WikiSubmissionEdge] + "A list of nodes." + nodes: [WikiSubmission] + "Information to aid in pagination." + pageInfo: PageInfo! + "The total amount of nodes." + totalCount: Int! +} + +"Autogenerated return type of WikiSubmissionCreateDraft" +type WikiSubmissionCreateDraftPayload { + errors: [Error!] + wikiSubmission: WikiSubmission +} + +"An edge in a connection." +type WikiSubmissionEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: WikiSubmission +} + +type WikiSubmissionMutations { + "Create a wiki submission draft" + createDraft( + "Create a wiki submission draft." + input: WikiSubmissionCreateDraftInput! + ): WikiSubmissionCreateDraftPayload + "Submit a wiki submission draft" + submitDraft( + "Submit a wiki submission draft. This will change the status to pending." + input: WikiSubmissionSubmitDraftInput! + ): WikiSubmissionSubmitDraftPayload + "Update a wiki submission draft" + updateDraft( + "Update a wiki submission draft." + input: WikiSubmissionUpdateDraftInput! + ): WikiSubmissionUpdateDraftPayload +} + +"Autogenerated return type of WikiSubmissionSubmitDraft" +type WikiSubmissionSubmitDraftPayload { + errors: [Error!] + wikiSubmission: WikiSubmission +} + +"Autogenerated return type of WikiSubmissionUpdateDraft" +type WikiSubmissionUpdateDraftPayload { + errors: [Error!] + wikiSubmission: WikiSubmission +} + enum AgeRatingEnum { "Acceptable for all ages" G @@ -2468,6 +2847,26 @@ enum FollowSortEnum { FOLLOWING_FOLLOWER } +enum InstallmentSortEnum { + ALTERNATIVE_ORDER + RELEASE_ORDER +} + +enum InstallmentTagEnum { + "Same universe/world/reality/timeline, completely different characters." + ALTERNATE_SETTING + "Same setting, same characters, story is told differently." + ALTERNATE_VERSION + "Characters from different media meet in the same story." + CROSSOVER + "The main story." + MAIN_STORY + "Takes place sometime during the main storyline." + SIDE_STORY + "Uses characters of a different series, but is not an alternate setting or story." + SPINOFF +} + enum LibraryEntryStatusEnum { "The user completed this media." COMPLETED @@ -2494,6 +2893,11 @@ enum LibraryEventKindEnum { UPDATED } +enum LibraryEventSortEnum { + CREATED_AT + UPDATED_AT +} + enum LockedReasonEnum { CLOSED SPAM @@ -2622,6 +3026,23 @@ enum ReleaseStatusEnum { UPCOMING } +enum ReportReasonEnum { + "No bulli!" + BULLYING + "Not Safe For Work" + NSFW + OFFENSIVE + OTHER + SPAM + SPOILER +} + +enum ReportStatusEnum { + DECLINED + REPORTED + RESOLVED +} + enum SitePermissionEnum { "Administrator/staff member of Kitsu" ADMIN @@ -2645,6 +3066,18 @@ enum TitleLanguagePreferenceEnum { ROMANIZED } +enum WikiSubmissionSortEnum { + CREATED_AT + UPDATED_AT +} + +enum WikiSubmissionStatusEnum { + APPROVED + DRAFT + PENDING + REJECTED +} + input AnimeCreateInput { ageRating: AgeRatingEnum ageRatingGuide: String @@ -2716,6 +3149,11 @@ input GenericDeleteInput { id: ID! } +input InstallmentSortOption { + direction: SortDirection! + on: InstallmentSortEnum! +} + input LibraryEntryCreateInput { finishedAt: ISO8601DateTime mediaId: ID! @@ -2781,9 +3219,9 @@ input LibraryEntryUpdateStatusByMediaInput { status: LibraryEntryStatusEnum! } -input LockInput { - id: ID! - lockedReason: LockedReasonEnum! +input LibraryEventSortOption { + direction: SortDirection! + on: LibraryEventSortEnum! } input MappingCreateInput { @@ -2806,11 +3244,20 @@ input PostLikeSortOption { on: PostLikeSortEnum! } +input PostLockInput { + id: ID! + lockedReason: LockedReasonEnum! +} + input PostSortOption { direction: SortDirection! on: PostSortEnum! } +input PostUnlockInput { + id: ID! +} + input TitlesListInput { alternatives: [String!] canonical: String @@ -2818,8 +3265,28 @@ input TitlesListInput { localized: Map } -input UnlockInput { +input WikiSubmissionCreateDraftInput { + data: JSON! + notes: String + title: String +} + +input WikiSubmissionSortOption { + direction: SortDirection! + on: WikiSubmissionSortEnum! +} + +input WikiSubmissionSubmitDraftInput { + data: JSON! id: ID! + notes: String + title: String +} + +input WikiSubmissionUpdateDraftInput { + data: JSON! + id: ID! + notes: String } @@ -2832,6 +3299,9 @@ scalar ISO8601Date "An ISO 8601-encoded datetime" scalar ISO8601DateTime +"Represents untyped JSON" +scalar JSON + "A loose key-value map in GraphQL" scalar Map From 1abac0ac0e8d08aced7e1ab909e239074a7bafb2 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 30 Jul 2021 09:37:06 -0400 Subject: [PATCH 002/106] Sort libraryEvents so watch history is in correct order --- src/AnimeClient/API/Kitsu/Queries/GetUserHistory.graphql | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/AnimeClient/API/Kitsu/Queries/GetUserHistory.graphql b/src/AnimeClient/API/Kitsu/Queries/GetUserHistory.graphql index eb569d6d..4dd35bc2 100644 --- a/src/AnimeClient/API/Kitsu/Queries/GetUserHistory.graphql +++ b/src/AnimeClient/API/Kitsu/Queries/GetUserHistory.graphql @@ -1,6 +1,9 @@ query ($slug: String!) { findProfileBySlug(slug: $slug) { - libraryEvents(first: 100, kind: [PROGRESSED, UPDATED]) { + libraryEvents(first: 100, kind: [PROGRESSED, UPDATED], sort: [{ + direction: DESCENDING, + on: UPDATED_AT, + }]) { nodes { id changedData From 2dacca5d067a9825136d7e4f3af8a703379020da Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Thu, 7 Oct 2021 21:45:17 -0400 Subject: [PATCH 003/106] Make some anime detail pages more robust --- src/AnimeClient/API/Kitsu/Transformer/AnimeTransformer.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/AnimeClient/API/Kitsu/Transformer/AnimeTransformer.php b/src/AnimeClient/API/Kitsu/Transformer/AnimeTransformer.php index 1a2caf00..6da23ae1 100644 --- a/src/AnimeClient/API/Kitsu/Transformer/AnimeTransformer.php +++ b/src/AnimeClient/API/Kitsu/Transformer/AnimeTransformer.php @@ -88,6 +88,13 @@ final class AnimeTransformer extends AbstractTransformer { $role = $staffing['role']; $name = $person['names']['localized'][$person['names']['canonical']]; + // If this person object is so broken as to not have a proper image object, + // just skip it. No point in showing a role with nothing in it. + if ($person === null || $person['id'] === null || $person['image'] === null || $person['image']['original'] === null) + { + continue; + } + if ( ! array_key_exists($role, $staff)) { $staff[$role] = []; From 69db87e305d981f22da81dcec72383d1fa73b3a4 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Thu, 7 Oct 2021 21:51:29 -0400 Subject: [PATCH 004/106] Attempt to fix Jenkins build, again --- Jenkinsfile | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index d873dd84..7edf8bdd 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -36,16 +36,6 @@ pipeline { sh 'php ./vendor/bin/phpunit --colors=never' } } - stage('Code Cleanliness') { - agent any - steps { - sh "php ./vendor/bin/phpstan analyse -c phpstan.neon -n --no-progress --no-ansi --error-format=checkstyle | awk '{\$1=\$1;print}' > build/logs/phpstan.log" - recordIssues( - failOnError: false, - tools: [phpStan(reportEncoding: 'UTF-8', pattern: 'build/logs/phpstan.log')] - ) - } - } stage('Coverage') { agent any steps { From 6e4e065b75edfbe92e48c290e225d801f374b4dc Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 8 Oct 2021 12:06:08 -0400 Subject: [PATCH 005/106] Check user library when searching for new media --- src/AnimeClient/API/Kitsu/Model.php | 1 + src/AnimeClient/API/Kitsu/Queries/SearchAnime.graphql | 4 ++++ src/AnimeClient/API/Kitsu/Queries/SearchManga.graphql | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/src/AnimeClient/API/Kitsu/Model.php b/src/AnimeClient/API/Kitsu/Model.php index 60123ab5..ca7befa8 100644 --- a/src/AnimeClient/API/Kitsu/Model.php +++ b/src/AnimeClient/API/Kitsu/Model.php @@ -535,6 +535,7 @@ final class Model { 'slug' => $item['slug'], 'canonicalTitle' => $item['titles']['canonical'], 'titles' => array_values(K::getTitles($item['titles'])), + 'libraryEntry' => $item['myLibraryEntry'], ]; // Search for MAL mapping diff --git a/src/AnimeClient/API/Kitsu/Queries/SearchAnime.graphql b/src/AnimeClient/API/Kitsu/Queries/SearchAnime.graphql index fbe03936..b72aad30 100644 --- a/src/AnimeClient/API/Kitsu/Queries/SearchAnime.graphql +++ b/src/AnimeClient/API/Kitsu/Queries/SearchAnime.graphql @@ -14,6 +14,10 @@ query ($query: String!) { localized alternatives } + myLibraryEntry { + id + status + } } } } \ No newline at end of file diff --git a/src/AnimeClient/API/Kitsu/Queries/SearchManga.graphql b/src/AnimeClient/API/Kitsu/Queries/SearchManga.graphql index 88b9430d..8ad9a6e8 100644 --- a/src/AnimeClient/API/Kitsu/Queries/SearchManga.graphql +++ b/src/AnimeClient/API/Kitsu/Queries/SearchManga.graphql @@ -14,6 +14,10 @@ query ($query: String!) { localized alternatives } + myLibraryEntry { + id + status + } } } } \ No newline at end of file From 489ec276026656141ef8c948fe70e2fca1b1b2dd Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 8 Oct 2021 12:15:34 -0400 Subject: [PATCH 006/106] Refactor media search rendering to be less redundant --- frontEndSrc/css/src/general.css | 8 +++ frontEndSrc/js/anime.js | 4 +- frontEndSrc/js/manga.js | 4 +- frontEndSrc/js/template-helpers.js | 80 ++++++++++++++++-------------- 4 files changed, 54 insertions(+), 42 deletions(-) diff --git a/frontEndSrc/css/src/general.css b/frontEndSrc/css/src/general.css index 29efb1f5..9d00cff3 100644 --- a/frontEndSrc/css/src/general.css +++ b/frontEndSrc/css/src/general.css @@ -628,6 +628,14 @@ picture.cover { background-size: contain; background-repeat: no-repeat; } +/* There are two .name elements, just darken them both in this case! */ +.media.search.disabled .name { + background-color: #000; + background-color: rgba(0, 0, 0, 0.75); + background-size: cover; + background-size: contain; + background-repeat: no-repeat; +} .media.search > .row { z-index: 6; diff --git a/frontEndSrc/js/anime.js b/frontEndSrc/js/anime.js index 77ec194b..f82a496a 100644 --- a/frontEndSrc/js/anime.js +++ b/frontEndSrc/js/anime.js @@ -1,5 +1,5 @@ import _ from './anime-client.js' -import { renderAnimeSearchResults } from './template-helpers.js' +import { renderSearchResults } from './template-helpers.js' const search = (query) => { // Show the loader @@ -13,7 +13,7 @@ const search = (query) => { _.hide('.cssload-loader'); // Show the results - _.$('#series-list')[ 0 ].innerHTML = renderAnimeSearchResults(searchResults); + _.$('#series-list')[ 0 ].innerHTML = renderSearchResults('anime', searchResults); }); }; diff --git a/frontEndSrc/js/manga.js b/frontEndSrc/js/manga.js index 21992d26..d07907bb 100644 --- a/frontEndSrc/js/manga.js +++ b/frontEndSrc/js/manga.js @@ -1,12 +1,12 @@ import _ from './anime-client.js' -import { renderMangaSearchResults } from './template-helpers.js' +import { renderSearchResults } from './template-helpers.js' const search = (query) => { _.show('.cssload-loader'); return _.get(_.url('/manga/search'), { query }, (searchResults, status) => { searchResults = JSON.parse(searchResults); _.hide('.cssload-loader'); - _.$('#series-list')[ 0 ].innerHTML = renderMangaSearchResults(searchResults); + _.$('#series-list')[ 0 ].innerHTML = renderSearchResults('manga', searchResults); }); }; diff --git a/frontEndSrc/js/template-helpers.js b/frontEndSrc/js/template-helpers.js index 33bdeb5c..55c33aa8 100644 --- a/frontEndSrc/js/template-helpers.js +++ b/frontEndSrc/js/template-helpers.js @@ -7,52 +7,55 @@ _.on('main', 'change', '.big-check', (e) => { document.getElementById(`mal_${id}`).checked = true; }); -export function renderAnimeSearchResults (data) { - return data.map(item => { - const titles = item.titles.join('
'); +/** + * On search results with an existing library entry, this shows that fact, with an edit link for the existing + * library entry + * + * @param {'anime'|'manga'} type + * @param {Object} item + * @returns {String} + */ +function renderEditLink (type, item) { + if (item.libraryEntry === null) { + return ''; + } - return ` - - `; - }).join(''); + return ` +
+ [ Already in List ] +
+
+ + Edit + +
+
 
+ ` } -export function renderMangaSearchResults (data) { +/** + * Show the search results for a media item + * + * @param {'anime'|'manga'} type + * @param {Object} data + * @returns {String} + */ +export function renderSearchResults (type, data) { return data.map(item => { const titles = item.titles.join('
'); + const disabled = item.libraryEntry !== null ? 'disabled' : ''; + const editLink = renderEditLink(type, item); + return ` - \ No newline at end of file diff --git a/app/templates/manga-cover.php b/app/templates/manga-cover.php index 4b3f58ef..f1372c2e 100644 --- a/app/templates/manga-cover.php +++ b/app/templates/manga-cover.php @@ -1,25 +1,25 @@
- isAuthenticated()): ?> + isAuthenticated()): ?> - img($item['manga']['image'], ['width' => 220, 'loading' => 'lazy']) ?> + h->img($item['manga']['image'], ['width' => 220, 'loading' => 'lazy']) ?>
- isAuthenticated()): ?> + isAuthenticated()): ?>
urlFromRoute('edit', [ 'controller' => 'manga', 'id' => $item['id'], 'status' => $name diff --git a/app/views/anime/add.php b/app/views/anime/add.php index d378f00a..7d52f8ae 100644 --- a/app/views/anime/add.php +++ b/app/views/anime/add.php @@ -1,4 +1,4 @@ -isAuthenticated()): ?> +isAuthenticated()): ?>

Add Anime to your List

diff --git a/app/views/anime/cover.php b/app/views/anime/cover.php index 96cdf359..ca1e049a 100644 --- a/app/views/anime/cover.php +++ b/app/views/anime/cover.php @@ -1,6 +1,6 @@
-isAuthenticated()): ?> -Add Item +isAuthenticated()): ?> +Add Item

There's nothing here!

@@ -11,16 +11,16 @@ $items): ?>
-

html($name) ?>

+

escape->html($name) ?>

There's nothing here!

-

html($name) ?>

+

escape->html($name) ?>

- isAuthenticated()) continue; ?> - animeCover($item) ?> + isAuthenticated()) continue; ?> + component->animeCover($item) ?>
diff --git a/app/views/anime/details.php b/app/views/anime/details.php index 99e89f4f..754d78b7 100644 --- a/app/views/anime/details.php +++ b/app/views/anime/details.php @@ -4,9 +4,9 @@ use function Aviat\AnimeClient\friendlyTime; ?>
-
+