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
|
return post, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) AniListUpdateEntry(updateBody AniListUpdateVariables) AniListGetSingleAnime {
|
func (a *App) AniListUpdateEntry(updateBody AniListUpdateVariables) (AniListGetSingleAnime, error) {
|
||||||
body := struct {
|
body := struct {
|
||||||
Query string `json:"query"`
|
Query string `json:"query"`
|
||||||
Variables AniListUpdateVariables `json:"variables"`
|
Variables AniListUpdateVariables `json:"variables"`
|
||||||
@@ -584,84 +584,87 @@ func (a *App) AniListUpdateEntry(updateBody AniListUpdateVariables) AniListGetSi
|
|||||||
startedAt: $startedAt
|
startedAt: $startedAt
|
||||||
completedAt: $completedAt
|
completedAt: $completedAt
|
||||||
) {
|
) {
|
||||||
id
|
id
|
||||||
idMal
|
mediaId
|
||||||
title {
|
userId
|
||||||
userPreferred
|
media {
|
||||||
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
|
id
|
||||||
|
idMal
|
||||||
title {
|
title {
|
||||||
userPreferred
|
userPreferred
|
||||||
romaji
|
romaji
|
||||||
english
|
english
|
||||||
native
|
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
|
status
|
||||||
startedAt {
|
startedAt {
|
||||||
year
|
year
|
||||||
@@ -700,22 +703,51 @@ func (a *App) AniListUpdateEntry(updateBody AniListUpdateVariables) AniListGetSi
|
|||||||
Variables: updateBody,
|
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
|
var returnedJson AniListUpdateReturn
|
||||||
err := json.Unmarshal(returnedBody, &returnedJson)
|
err := json.Unmarshal(returnedBody, &returnedJson)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed at unmarshal, %s\n", err)
|
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
|
var post AniListGetSingleAnime
|
||||||
|
|
||||||
post.Data.MediaList = returnedJson.Data.SaveMediaListEntry
|
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 {
|
type Variables = struct {
|
||||||
Id int `json:"id"`
|
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
|
var post DeleteAniListReturn
|
||||||
err := json.Unmarshal(returnedBody, &post)
|
err := json.Unmarshal(returnedBody, &post)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed at unmarshal, %s\n", err)
|
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(
|
func (a *App) AniListBrowse(
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
aniListLoggedIn,
|
aniListLoggedIn,
|
||||||
malAnime,
|
malAnime,
|
||||||
malLoggedIn,
|
malLoggedIn,
|
||||||
|
setApiError,
|
||||||
simklAnime,
|
simklAnime,
|
||||||
simklLoggedIn,
|
simklLoggedIn,
|
||||||
watchlistNeedsRefresh,
|
watchlistNeedsRefresh,
|
||||||
@@ -207,6 +208,15 @@
|
|||||||
completedAt: convertDateToAniList(completedAtDate),
|
completedAt: convertDateToAniList(completedAtDate),
|
||||||
};
|
};
|
||||||
await AniListUpdateEntry(body).then((value: AniListGetSingleAnime) => {
|
await AniListUpdateEntry(body).then((value: AniListGetSingleAnime) => {
|
||||||
|
if (!value.data?.MediaList || value.data.MediaList.mediaId === 0) {
|
||||||
|
setApiError(
|
||||||
|
"anilist",
|
||||||
|
"AniList update failed: no saved entry was returned",
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
value.data.MediaList.media.tags =
|
value.data.MediaList.media.tags =
|
||||||
currentAniListAnime.data.MediaList.media.tags;
|
currentAniListAnime.data.MediaList.media.tags;
|
||||||
value.data.MediaList.media.genres =
|
value.data.MediaList.media.genres =
|
||||||
|
|||||||
+1
-1
@@ -12,6 +12,6 @@
|
|||||||
},
|
},
|
||||||
"info": {
|
"info": {
|
||||||
"productName": "AniTrack",
|
"productName": "AniTrack",
|
||||||
"productVersion": "1.5.5"
|
"productVersion": "1.6.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user