fix(anilist): repair SaveMediaListEntry mutation and surface update failures
The AniListUpdateEntry mutation was mangled in 54c109a when the
standardized media field block was pasted in without the media { }
wrapper: media-level fields (idMal, title, ...) sat directly on
SaveMediaListEntry (which returns MediaList), the MediaList fields
(status, startedAt, ..., user) ended up at the Mutation root, and a
stray closing brace made the document a guaranteed 400. Because the
response status was discarded, the frontend received a zeroed
AniListGetSingleAnime and blanked the anime page after every submit,
making AniList appear logged out (and the change was never saved).
- Restore the mutation to match the tested bruno request: id/mediaId/
userId, standard media block inside media { }, MediaList fields
inside the selection, balanced braces.
- AniListUpdateEntry and AniListDeleteEntry now return an error on
403/non-200/unparseable responses (mirroring
GetAniListUserWatchingList) instead of silently returning zero
values.
- Anime.svelte guards against replacing the page data with an empty
response and raises the API error modal instead.
Bump productVersion to 1.6.1.
This commit is contained in:
+133
-76
@@ -558,7 +558,7 @@ func (a *App) GetAniListUserWatchingList(page int, perPage int, sort string) (An
|
||||
return post, nil
|
||||
}
|
||||
|
||||
func (a *App) AniListUpdateEntry(updateBody AniListUpdateVariables) AniListGetSingleAnime {
|
||||
func (a *App) AniListUpdateEntry(updateBody AniListUpdateVariables) (AniListGetSingleAnime, error) {
|
||||
body := struct {
|
||||
Query string `json:"query"`
|
||||
Variables AniListUpdateVariables `json:"variables"`
|
||||
@@ -584,84 +584,87 @@ func (a *App) AniListUpdateEntry(updateBody AniListUpdateVariables) AniListGetSi
|
||||
startedAt: $startedAt
|
||||
completedAt: $completedAt
|
||||
) {
|
||||
id
|
||||
idMal
|
||||
title {
|
||||
userPreferred
|
||||
romaji
|
||||
english
|
||||
native
|
||||
}
|
||||
description
|
||||
coverImage {
|
||||
extraLarge
|
||||
large
|
||||
medium
|
||||
color
|
||||
}
|
||||
bannerImage
|
||||
format
|
||||
season
|
||||
seasonYear
|
||||
status
|
||||
episodes
|
||||
duration
|
||||
countryOfOrigin
|
||||
source
|
||||
synonyms
|
||||
averageScore
|
||||
meanScore
|
||||
popularity
|
||||
trending
|
||||
favourites
|
||||
isFavourite
|
||||
relations {
|
||||
nodes {
|
||||
id
|
||||
mediaId
|
||||
userId
|
||||
media {
|
||||
id
|
||||
idMal
|
||||
title {
|
||||
userPreferred
|
||||
romaji
|
||||
english
|
||||
native
|
||||
}
|
||||
description
|
||||
coverImage {
|
||||
extraLarge
|
||||
large
|
||||
medium
|
||||
color
|
||||
}
|
||||
bannerImage
|
||||
format
|
||||
season
|
||||
seasonYear
|
||||
status
|
||||
episodes
|
||||
duration
|
||||
countryOfOrigin
|
||||
source
|
||||
synonyms
|
||||
averageScore
|
||||
meanScore
|
||||
popularity
|
||||
trending
|
||||
favourites
|
||||
isFavourite
|
||||
relations {
|
||||
nodes {
|
||||
id
|
||||
title {
|
||||
userPreferred
|
||||
romaji
|
||||
english
|
||||
native
|
||||
}
|
||||
}
|
||||
}
|
||||
startDate {
|
||||
year
|
||||
month
|
||||
day
|
||||
}
|
||||
endDate {
|
||||
year
|
||||
month
|
||||
day
|
||||
}
|
||||
nextAiringEpisode {
|
||||
airingAt
|
||||
timeUntilAiring
|
||||
episode
|
||||
}
|
||||
airingSchedule {
|
||||
nodes {
|
||||
id
|
||||
airingAt
|
||||
timeUntilAiring
|
||||
episode
|
||||
mediaId
|
||||
}
|
||||
}
|
||||
genres
|
||||
tags {
|
||||
id
|
||||
name
|
||||
description
|
||||
rank
|
||||
isMediaSpoiler
|
||||
isAdult
|
||||
}
|
||||
isAdult
|
||||
}
|
||||
}
|
||||
startDate {
|
||||
year
|
||||
month
|
||||
day
|
||||
}
|
||||
endDate {
|
||||
year
|
||||
month
|
||||
day
|
||||
}
|
||||
nextAiringEpisode {
|
||||
airingAt
|
||||
timeUntilAiring
|
||||
episode
|
||||
}
|
||||
airingSchedule {
|
||||
nodes {
|
||||
id
|
||||
airingAt
|
||||
timeUntilAiring
|
||||
episode
|
||||
mediaId
|
||||
}
|
||||
}
|
||||
genres
|
||||
tags {
|
||||
id
|
||||
name
|
||||
description
|
||||
rank
|
||||
isMediaSpoiler
|
||||
isAdult
|
||||
}
|
||||
isAdult
|
||||
}
|
||||
|
||||
status
|
||||
startedAt {
|
||||
year
|
||||
@@ -700,22 +703,51 @@ func (a *App) AniListUpdateEntry(updateBody AniListUpdateVariables) AniListGetSi
|
||||
Variables: updateBody,
|
||||
}
|
||||
|
||||
returnedBody, _ := AniListQuery(body, true)
|
||||
returnedBody, status := AniListQuery(body, true)
|
||||
|
||||
var badPost struct {
|
||||
Errors []struct {
|
||||
Message string `json:"message"`
|
||||
Status int `json:"status"`
|
||||
Locations []struct {
|
||||
Line int `json:"line"`
|
||||
Column int `json:"column"`
|
||||
} `json:"locations"`
|
||||
} `json:"errors"`
|
||||
Data any `json:"data"`
|
||||
}
|
||||
|
||||
if status == "403 Forbidden" {
|
||||
err := json.Unmarshal(returnedBody, &badPost)
|
||||
if err != nil {
|
||||
log.Printf("Failed at unmarshal, %s\n", err)
|
||||
return AniListGetSingleAnime{}, fmt.Errorf("API authentication error")
|
||||
}
|
||||
return AniListGetSingleAnime{}, fmt.Errorf("AniList API error: %s", badPost.Errors[0].Message)
|
||||
}
|
||||
if status != "200 OK" {
|
||||
return AniListGetSingleAnime{}, fmt.Errorf("API request failed with status: %s", status)
|
||||
}
|
||||
|
||||
var returnedJson AniListUpdateReturn
|
||||
err := json.Unmarshal(returnedBody, &returnedJson)
|
||||
if err != nil {
|
||||
log.Printf("Failed at unmarshal, %s\n", err)
|
||||
return AniListGetSingleAnime{}, fmt.Errorf("failed to parse AniList update response")
|
||||
}
|
||||
|
||||
if returnedJson.Data.SaveMediaListEntry.MediaID == 0 {
|
||||
return AniListGetSingleAnime{}, fmt.Errorf("AniList returned no saved entry")
|
||||
}
|
||||
|
||||
var post AniListGetSingleAnime
|
||||
|
||||
post.Data.MediaList = returnedJson.Data.SaveMediaListEntry
|
||||
|
||||
return post
|
||||
return post, nil
|
||||
}
|
||||
|
||||
func (a *App) AniListDeleteEntry(mediaListId int) DeleteAniListReturn {
|
||||
func (a *App) AniListDeleteEntry(mediaListId int) (DeleteAniListReturn, error) {
|
||||
type Variables = struct {
|
||||
Id int `json:"id"`
|
||||
}
|
||||
@@ -740,15 +772,40 @@ func (a *App) AniListDeleteEntry(mediaListId int) DeleteAniListReturn {
|
||||
},
|
||||
}
|
||||
|
||||
returnedBody, _ := AniListQuery(body, true)
|
||||
returnedBody, status := AniListQuery(body, true)
|
||||
|
||||
var badPost struct {
|
||||
Errors []struct {
|
||||
Message string `json:"message"`
|
||||
Status int `json:"status"`
|
||||
Locations []struct {
|
||||
Line int `json:"line"`
|
||||
Column int `json:"column"`
|
||||
} `json:"locations"`
|
||||
} `json:"errors"`
|
||||
Data any `json:"data"`
|
||||
}
|
||||
|
||||
if status == "403 Forbidden" {
|
||||
err := json.Unmarshal(returnedBody, &badPost)
|
||||
if err != nil {
|
||||
log.Printf("Failed at unmarshal, %s\n", err)
|
||||
return DeleteAniListReturn{}, fmt.Errorf("API authentication error")
|
||||
}
|
||||
return DeleteAniListReturn{}, fmt.Errorf("AniList API error: %s", badPost.Errors[0].Message)
|
||||
}
|
||||
if status != "200 OK" {
|
||||
return DeleteAniListReturn{}, fmt.Errorf("API request failed with status: %s", status)
|
||||
}
|
||||
|
||||
var post DeleteAniListReturn
|
||||
err := json.Unmarshal(returnedBody, &post)
|
||||
if err != nil {
|
||||
log.Printf("Failed at unmarshal, %s\n", err)
|
||||
return DeleteAniListReturn{}, fmt.Errorf("failed to parse AniList delete response")
|
||||
}
|
||||
|
||||
return post
|
||||
return post, nil
|
||||
}
|
||||
|
||||
func (a *App) AniListBrowse(
|
||||
|
||||
Reference in New Issue
Block a user