2 Commits

Author SHA1 Message Date
john-okeefe b934276342 chore(version): bump version to 0.5.1
Increment version number from 0.5.0 to 0.5.1 for the upcoming release.
2026-03-20 10:56:39 -04:00
john-okeefe 6860a5541a style(frontend): format Header.svelte with consistent code style
Apply consistent formatting to Header.svelte:
- Add semicolons to all statements
- Improve JSX/HTML attribute formatting for better readability
- Add link import from svelte-spa-router for SPA navigation
- Format multi-line attributes with proper indentation

These changes improve code consistency and maintainability without
altering any functionality.
2026-03-20 10:56:37 -04:00
26 changed files with 1544 additions and 2161 deletions
-4
View File
@@ -33,7 +33,3 @@ environment.go
# REST (http files) # REST (http files)
http-client.private.env.json http-client.private.env.json
# Build artifacts
build/*.tar.gz
AniTrack
+8 -15
View File
@@ -3,7 +3,6 @@ package main
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt"
"io" "io"
"log" "log"
"net/http" "net/http"
@@ -184,7 +183,7 @@ func (a *App) GetAniListItem(aniId int, login bool) AniListGetSingleAnime {
return post return post
} }
func (a *App) AniListSearch(query string) (interface{}, error) { func (a *App) AniListSearch(query string) any {
type Variables struct { type Variables struct {
Search string `json:"search"` Search string `json:"search"`
ListType string `json:"listType"` ListType string `json:"listType"`
@@ -243,20 +242,18 @@ func (a *App) AniListSearch(query string) (interface{}, error) {
ListType: "ANIME", ListType: "ANIME",
}, },
} }
returnedBody, status := AniListQuery(body, false) returnedBody, _ := AniListQuery(body, false)
if status != "200 OK" {
return nil, fmt.Errorf("API search failed with status: %s", status)
}
var post interface{} var post interface{}
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 nil, fmt.Errorf("Failed to parse search results")
} }
return post, nil
return post
} }
func (a *App) GetAniListUserWatchingList(page int, perPage int, sort string) (AniListCurrentUserWatchList, error) { func (a *App) GetAniListUserWatchingList(page int, perPage int, sort string) AniListCurrentUserWatchList {
user := a.GetAniListLoggedInUser() user := a.GetAniListLoggedInUser()
type Variables struct { type Variables struct {
Page int `json:"page"` Page int `json:"page"`
@@ -407,15 +404,11 @@ func (a *App) GetAniListUserWatchingList(page int, perPage int, sort string) (An
err := json.Unmarshal(returnedBody, &badPost) err := json.Unmarshal(returnedBody, &badPost)
if err != nil { if err != nil {
log.Printf("Failed at unmarshal, %s\n", err) log.Printf("Failed at unmarshal, %s\n", err)
return post, fmt.Errorf("API authentication error")
} }
return post, fmt.Errorf("AniList API error: %s", badPost.Errors[0].Message) log.Fatal(badPost.Errors[0].Message)
}
if status != "200 OK" {
return post, fmt.Errorf("API request failed with status: %s", status)
} }
return post, nil return post
} }
func (a *App) AniListUpdateEntry(updateBody AniListUpdateVariables) AniListGetSingleAnime { func (a *App) AniListUpdateEntry(updateBody AniListUpdateVariables) AniListGetSingleAnime {
+25 -72
View File
@@ -11,34 +11,10 @@ import (
"strings" "strings"
) )
// Unmarshalling accidental numbers received from MAL to strings func MALHelper(method string, malUrl string, body url.Values) (json.RawMessage, string) {
func (f *FlexString) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err == nil {
*f = FlexString(s)
return nil
}
var n json.Number
if err := json.Unmarshal(data, &n); err == nil {
*f = FlexString(string(n))
return nil
}
return fmt.Errorf("FlexString: invalid value")
}
func MALHelper(method string, malUrl string, body url.Values) (json.RawMessage, string, error) {
client := &http.Client{} client := &http.Client{}
req, err := http.NewRequest(method, malUrl, strings.NewReader(body.Encode())) req, _ := http.NewRequest(method, malUrl, strings.NewReader(body.Encode()))
if err != nil {
message, _ := json.Marshal(struct {
Message string `json:"message"`
}{
Message: "Failed to create request: " + err.Error(),
})
return message, "", fmt.Errorf("failed to create request: %w", err)
}
req.Header.Add("Content-Type", "application/x-www-form-urlencoded") req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Authorization", "Bearer "+myAnimeListJwt.AccessToken) req.Header.Add("Authorization", "Bearer "+myAnimeListJwt.AccessToken)
@@ -46,57 +22,47 @@ func MALHelper(method string, malUrl string, body url.Values) (json.RawMessage,
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
fmt.Println("Errored when sending request to the server")
message, _ := json.Marshal(struct { message, _ := json.Marshal(struct {
Message string `json:"message"` Message string `json:"message" ts_type:"message"`
}{ }{
Message: "Network error: " + err.Error(), Message: "Errored when sending request to the server" + err.Error(),
}) })
return message, "", fmt.Errorf("network error: %w", err)
return message, resp.Status
} }
defer resp.Body.Close() defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body) respBody, _ := io.ReadAll(resp.Body)
if err != nil {
return nil, resp.Status, fmt.Errorf("failed to read response: %w", err)
}
if resp.Status == "401 Unauthorized" { if resp.Status == "401 Unauthorized" {
refreshMyAnimeListAuthorizationToken() refreshMyAnimeListAuthorizationToken()
return MALHelper(method, malUrl, body) MALHelper(method, malUrl, body)
} }
if resp.Status != "200 OK" && resp.Status != "201 Created" && resp.Status != "202 Accepted" { return respBody, resp.Status
return respBody, resp.Status, fmt.Errorf("API returned status: %s", resp.Status)
}
return respBody, resp.Status, nil
} }
func (a *App) GetMyAnimeList(count int) (MALWatchlist, error) { func (a *App) GetMyAnimeList(count int) MALWatchlist {
limit := strconv.Itoa(count) limit := strconv.Itoa(count)
user := a.GetMyAnimeListLoggedInUser() user := a.GetMyAnimeListLoggedInUser()
malUrl := "https://api.myanimelist.net/v2/users/" + user.Name + "/animelist?fields=list_status&status=watching&limit=" + limit malUrl := "https://api.myanimelist.net/v2/users/" + user.Name + "/animelist?fields=list_status&status=watching&limit=" + limit
var malList MALWatchlist var malList MALWatchlist
respBody, resStatus, err := MALHelper("GET", malUrl, nil) respBody, resStatus := MALHelper("GET", malUrl, nil)
if err != nil {
return malList, fmt.Errorf("failed to get MAL watchlist: %w", err)
}
if resStatus == "200 OK" { if resStatus == "200 OK" {
err := json.Unmarshal(respBody, &malList) err := json.Unmarshal(respBody, &malList)
if err != nil { if err != nil {
log.Printf("Failed to unmarshal json response, %s\n", err) log.Printf("Failed to unmarshal json response, %s\n", err)
return malList, fmt.Errorf("failed to parse response: %w", err)
} }
} }
return malList, nil return malList
} }
func (a *App) MyAnimeListUpdate(anime MALAnime, update MALUploadStatus) (MalListStatus, error) { func (a *App) MyAnimeListUpdate(anime MALAnime, update MALUploadStatus) MalListStatus {
if update.NumTimesRewatched >= 1 { if update.NumTimesRewatched >= 1 {
update.IsRewatching = true update.IsRewatching = true
} else { } else {
@@ -112,52 +78,39 @@ func (a *App) MyAnimeListUpdate(anime MALAnime, update MALUploadStatus) (MalList
malUrl := "https://api.myanimelist.net/v2/anime/" + strconv.Itoa(anime.Id) + "/my_list_status" malUrl := "https://api.myanimelist.net/v2/anime/" + strconv.Itoa(anime.Id) + "/my_list_status"
var status MalListStatus var status MalListStatus
respBody, respStatus, err := MALHelper("PATCH", malUrl, body) respBody, respStatus := MALHelper("PATCH", malUrl, body)
if err != nil {
return status, fmt.Errorf("failed to update MAL entry: %w", err)
}
if respStatus == "200 OK" { if respStatus == "200 OK" {
err := json.Unmarshal(respBody, &status) err := json.Unmarshal(respBody, &status)
if err != nil { if err != nil {
log.Printf("Failed to unmarshal json response, %s\n", err) log.Printf("Failed to unmarshal json response, %s\n", err)
return status, fmt.Errorf("failed to parse response: %w", err)
} }
} }
return status, nil
return status
} }
func (a *App) GetMyAnimeListAnime(id int) (MALAnime, error) { func (a *App) GetMyAnimeListAnime(id int) MALAnime {
malUrl := "https://api.myanimelist.net/v2/anime/" + strconv.Itoa(id) + "?fields=id,title,main_picture,alternative_titles,start_date,end_date,synopsis,mean,rank,popularity,num_list_users,num_scoring_users,nsfw,genres,created_at,updated_at,media_type,status,my_list_status,num_episodes,start_season,broadcast,source,average_episode_duration,rating,pictures,background,related_anime,recommendations,studios,statistics" malUrl := "https://api.myanimelist.net/v2/anime/" + strconv.Itoa(id) + "?fields=id,title,main_picture,alternative_titles,start_date,end_date,synopsis,mean,rank,popularity,num_list_users,num_scoring_users,nsfw,genres,created_at,updated_at,media_type,status,my_list_status,num_episodes,start_season,broadcast,source,average_episode_duration,rating,pictures,background,related_anime,recommendations,studios,statistics"
respBody, respStatus := MALHelper("GET", malUrl, nil)
var malAnime MALAnime var malAnime MALAnime
respBody, respStatus, err := MALHelper("GET", malUrl, nil)
if err != nil {
return malAnime, fmt.Errorf("failed to get MAL anime: %w", err)
}
if respStatus == "200 OK" { if respStatus == "200 OK" {
err := json.Unmarshal(respBody, &malAnime) err := json.Unmarshal(respBody, &malAnime)
if err != nil { if err != nil {
log.Printf("Failed to unmarshal json response, %s\n", err) log.Printf("Failed to unmarshal json response, %s\n", err)
return malAnime, fmt.Errorf("failed to parse response: %w", err)
} }
} }
return malAnime, nil
return malAnime
} }
func (a *App) DeleteMyAnimeListEntry(id int) (bool, error) { func (a *App) DeleteMyAnimeListEntry(id int) bool {
malUrl := "https://api.myanimelist.net/v2/anime/" + strconv.Itoa(id) + "/my_list_status" malUrl := "https://api.myanimelist.net/v2/anime/" + strconv.Itoa(id) + "/my_list_status"
_, respStatus, err := MALHelper("DELETE", malUrl, nil) _, respStatus := MALHelper("DELETE", malUrl, nil)
if err != nil {
return false, fmt.Errorf("failed to delete MAL entry: %w", err)
}
if respStatus == "200 OK" { if respStatus == "200 OK" {
return true, nil return true
} else { } else {
return false, fmt.Errorf("delete failed with status: %s", respStatus) return false
} }
} }
+6 -10
View File
@@ -1,10 +1,6 @@
package main package main
import ( import "time"
"time"
)
type FlexString string
type MyAnimeListJWT struct { type MyAnimeListJWT struct {
TokenType string `json:"token_type"` TokenType string `json:"token_type"`
@@ -133,11 +129,11 @@ type MALAnime struct {
Statistics struct { Statistics struct {
NumListUsers int `json:"num_list_users" ts_type:"numListUsers"` NumListUsers int `json:"num_list_users" ts_type:"numListUsers"`
Status struct { Status struct {
Watching FlexString `json:"watching" ts_type:"string"` Watching string `json:"watching" ts_type:"watching"`
Completed FlexString `json:"completed" ts_type:"string"` Completed string `json:"completed" ts_type:"completed"`
OnHold FlexString `json:"on_hold" ts_type:"string"` OnHold string `json:"on_hold" ts_type:"onHold"`
Dropped FlexString `json:"dropped" ts_type:"string"` Dropped string `json:"dropped" ts_type:"dropped"`
PlanToWatch FlexString `json:"plan_to_watch" ts_type:"string"` PlanToWatch string `json:"plan_to_watch" ts_type:"planToWatch"`
} }
} }
} }
+1 -1
View File
@@ -176,7 +176,7 @@ func (a *App) handleMyAnimeListCallback(wg *sync.WaitGroup, verifier *CodeVerifi
go func() { go func() {
defer wg.Done() defer wg.Done()
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Printf("Server error: %s\n", err) log.Fatalf("listen: %s\n", err)
} }
fmt.Println("Shutting down...") fmt.Println("Shutting down...")
}() }()
-12
View File
@@ -1,12 +0,0 @@
TAGS := webkit2_41
.PHONY: dev build clean
dev:
wails dev -tags $(TAGS)
build:
wails build -tags $(TAGS)
clean:
rm -rf build/bin/*
+49 -92
View File
@@ -14,24 +14,16 @@ import (
var SimklWatchList SimklWatchListType var SimklWatchList SimklWatchListType
func SimklHelper(method string, url string, body interface{}) (json.RawMessage, error) { func SimklHelper(method string, url string, body interface{}) json.RawMessage {
reader, err := json.Marshal(body) reader, _ := json.Marshal(body)
if err != nil {
return nil, fmt.Errorf("failed to marshal body: %w", err)
}
var req *http.Request var req *http.Request
client := &http.Client{} client := &http.Client{}
if body != nil { if body != nil {
req, err = http.NewRequest(method, url, bytes.NewBuffer(reader)) req, _ = http.NewRequest(method, url, bytes.NewBuffer(reader))
} else { } else {
req, err = http.NewRequest(method, url, nil) req, _ = http.NewRequest(method, url, nil)
}
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
} }
req.Header.Add("Content-Type", "application/json") req.Header.Add("Content-Type", "application/json")
@@ -40,45 +32,41 @@ func SimklHelper(method string, url string, body interface{}) (json.RawMessage,
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return nil, fmt.Errorf("network error: %w", err) fmt.Println("Errored when sending request to the server")
message, _ := json.Marshal(struct {
Message string `json:"message"`
}{
Message: "Errored when sending request to the server" + err.Error(),
})
return message
} }
defer resp.Body.Close() defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body) respBody, _ := io.ReadAll(resp.Body)
if err != nil { return respBody
return nil, fmt.Errorf("failed to read response: %w", err)
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return respBody, fmt.Errorf("API returned status: %d", resp.StatusCode)
}
return respBody, nil
} }
func (a *App) SimklGetUserWatchlist() (SimklWatchListType, error) { func (a *App) SimklGetUserWatchlist() SimklWatchListType {
method := "GET" method := "GET"
url := "https://api.simkl.com/sync/all-items/anime" url := "https://api.simkl.com/sync/all-items/anime"
respBody, err := SimklHelper(method, url, nil) respBody := SimklHelper(method, url, nil)
if err != nil {
return SimklWatchListType{}, fmt.Errorf("failed to get Simkl watchlist: %w", err)
}
var errCheck struct { var errCheck struct {
Error string `json:"error"` Error string `json:"error"`
Message string `json:"message"` Message string `json:"message"`
} }
err = json.Unmarshal(respBody, &errCheck) err := json.Unmarshal(respBody, &errCheck)
if err != nil { if err != nil {
log.Printf("Failed at unmarshal, %s\n", err) log.Printf("Failed at unmarshal, %s\n", err)
return SimklWatchListType{}, fmt.Errorf("failed to parse error response: %w", err)
} }
if errCheck.Error != "" { if errCheck.Error != "" {
a.LogoutSimkl() a.LogoutSimkl()
return SimklWatchListType{}, fmt.Errorf("Simkl API error: %s", errCheck.Message) return SimklWatchListType{}
} }
var watchlist SimklWatchListType var watchlist SimklWatchListType
@@ -86,15 +74,14 @@ func (a *App) SimklGetUserWatchlist() (SimklWatchListType, error) {
err = json.Unmarshal(respBody, &watchlist) err = json.Unmarshal(respBody, &watchlist)
if err != nil { if err != nil {
log.Printf("Failed at unmarshal, %s\n", err) log.Printf("Failed at unmarshal, %s\n", err)
return SimklWatchListType{}, fmt.Errorf("failed to parse watchlist: %w", err)
} }
SimklWatchList = watchlist SimklWatchList = watchlist
return watchlist, nil return watchlist
} }
func (a *App) SimklSyncEpisodes(anime SimklAnime, progress int) (SimklAnime, error) { func (a *App) SimklSyncEpisodes(anime SimklAnime, progress int) SimklAnime {
var episodes []Episode var episodes []Episode
var url string var url string
var shows []SimklPostShow var shows []SimklPostShow
@@ -125,19 +112,13 @@ func (a *App) SimklSyncEpisodes(anime SimklAnime, progress int) (SimklAnime, err
simklSync := SimklSyncHistoryType{shows} simklSync := SimklSyncHistoryType{shows}
respBody, err := SimklHelper("POST", url, simklSync) respBody := SimklHelper("POST", url, simklSync)
if err != nil {
log.Printf("Failed to sync episodes: %s\n", err)
return anime, fmt.Errorf("failed to sync episodes: %w", err)
}
var success interface{} var success interface{}
err = json.Unmarshal(respBody, &success) err := json.Unmarshal(respBody, &success)
if err != nil { if err != nil {
log.Printf("Failed at unmarshal, %s\n", err) log.Printf("Failed at unmarshal, %s\n", err)
return anime, fmt.Errorf("failed to parse response: %w", err)
} }
for i, simklAnime := range SimklWatchList.Anime { for i, simklAnime := range SimklWatchList.Anime {
@@ -150,10 +131,10 @@ func (a *App) SimklSyncEpisodes(anime SimklAnime, progress int) (SimklAnime, err
WatchListUpdate(anime) WatchListUpdate(anime)
return anime, nil return anime
} }
func (a *App) SimklSyncRating(anime SimklAnime, rating int) (SimklAnime, error) { func (a *App) SimklSyncRating(anime SimklAnime, rating int) SimklAnime {
var url string var url string
showWithRating := ShowWithRating{ showWithRating := ShowWithRating{
Title: anime.Show.Title, Title: anime.Show.Title,
@@ -188,17 +169,13 @@ func (a *App) SimklSyncRating(anime SimklAnime, rating int) (SimklAnime, error)
Shows []interface{} `json:"shows" ts_type:"shows"` Shows []interface{} `json:"shows" ts_type:"shows"`
}{shows} }{shows}
respBody, err := SimklHelper("POST", url, simklSync) respBody := SimklHelper("POST", url, simklSync)
if err != nil {
log.Printf("Failed to sync rating: %s\n", err)
return anime, fmt.Errorf("failed to sync rating: %w", err)
}
var success interface{} var success interface{}
err = json.Unmarshal(respBody, &success) err := json.Unmarshal(respBody, &success)
if err != nil { if err != nil {
log.Printf("Failed at unmarshal, %s\n", err) log.Printf("Failed at unmarshal, %s\n", err)
return anime, fmt.Errorf("failed to parse response: %w", err)
} }
for i, simklAnime := range SimklWatchList.Anime { for i, simklAnime := range SimklWatchList.Anime {
@@ -211,10 +188,10 @@ func (a *App) SimklSyncRating(anime SimklAnime, rating int) (SimklAnime, error)
WatchListUpdate(anime) WatchListUpdate(anime)
return anime, nil return anime
} }
func (a *App) SimklSyncStatus(anime SimklAnime, status string) (SimklAnime, error) { func (a *App) SimklSyncStatus(anime SimklAnime, status string) SimklAnime {
url := "https://api.simkl.com/sync/add-to-list" url := "https://api.simkl.com/sync/add-to-list"
show := SimklShowStatus{ show := SimklShowStatus{
Title: anime.Show.Title, Title: anime.Show.Title,
@@ -234,17 +211,13 @@ func (a *App) SimklSyncStatus(anime SimklAnime, status string) (SimklAnime, erro
Shows []SimklShowStatus `json:"shows" ts_type:"shows"` Shows []SimklShowStatus `json:"shows" ts_type:"shows"`
}{shows} }{shows}
respBody, err := SimklHelper("POST", url, simklSync) respBody := SimklHelper("POST", url, simklSync)
if err != nil {
log.Printf("Failed to sync status: %s\n", err)
return anime, fmt.Errorf("failed to sync status: %w", err)
}
var success interface{} var success interface{}
err = json.Unmarshal(respBody, &success) err := json.Unmarshal(respBody, &success)
if err != nil { if err != nil {
log.Printf("Failed at unmarshal, %s\n", err) log.Printf("Failed at unmarshal, %s\n", err)
return anime, fmt.Errorf("failed to parse response: %w", err)
} }
for i, simklAnime := range SimklWatchList.Anime { for i, simklAnime := range SimklWatchList.Anime {
@@ -257,20 +230,15 @@ func (a *App) SimklSyncStatus(anime SimklAnime, status string) (SimklAnime, erro
WatchListUpdate(anime) WatchListUpdate(anime)
return anime, nil return anime
} }
func (a *App) SimklSearch(aniListAnime MediaList) (SimklAnime, error) { func (a *App) SimklSearch(aniListAnime MediaList) SimklAnime {
var result SimklAnime var result SimklAnime
if reflect.DeepEqual(SimklWatchList, SimklWatchListType{}) { if reflect.DeepEqual(SimklWatchList, SimklWatchListType{}) {
fmt.Println("Watchlist empty. Calling...") fmt.Println("Watchlist empty. Calling...")
watchlist, err := a.SimklGetUserWatchlist() SimklWatchList = a.SimklGetUserWatchlist()
if err != nil {
log.Printf("Failed to get watchlist: %s\n", err)
return result, fmt.Errorf("failed to load watchlist for search: %w", err)
}
SimklWatchList = watchlist
} }
for _, anime := range SimklWatchList.Anime { for _, anime := range SimklWatchList.Anime {
@@ -287,30 +255,22 @@ func (a *App) SimklSearch(aniListAnime MediaList) (SimklAnime, error) {
var anime SimklSearchType var anime SimklSearchType
url := "https://api.simkl.com/search/id?anilist=" + strconv.Itoa(aniListAnime.Media.ID) url := "https://api.simkl.com/search/id?anilist=" + strconv.Itoa(aniListAnime.Media.ID)
respBody, err := SimklHelper("GET", url, nil) respBody := SimklHelper("GET", url, nil)
if err != nil {
log.Printf("Failed to search Simkl: %s\n", err) err := json.Unmarshal(respBody, &anime)
return result, fmt.Errorf("failed to search Simkl by AniList ID: %w", err)
}
err = json.Unmarshal(respBody, &anime)
if len(anime) == 0 { if len(anime) == 0 {
url = "https://api.simkl.com/search/id?mal=" + strconv.Itoa(aniListAnime.Media.IDMal) url = "https://api.simkl.com/search/id?mal=" + strconv.Itoa(aniListAnime.Media.IDMal)
respBody, err = SimklHelper("GET", url, nil) respBody = SimklHelper("GET", url, nil)
if err != nil {
log.Printf("Failed to search Simkl by MAL ID: %s\n", err)
return result, fmt.Errorf("failed to search by MAL ID: %w", err)
}
err = json.Unmarshal(respBody, &anime) err = json.Unmarshal(respBody, &anime)
} }
if err != nil { if err != nil {
log.Printf("Failed at unmarshal, %s\n", err) log.Printf("Failed at unmarshal, %s\n", err)
return result, fmt.Errorf("failed to parse search results: %w", err)
} }
if len(anime) == 0 { if len(anime) == 0 {
return result, nil return result
} }
for _, watchListAnime := range SimklWatchList.Anime { for _, watchListAnime := range SimklWatchList.Anime {
@@ -328,10 +288,10 @@ func (a *App) SimklSearch(aniListAnime MediaList) (SimklAnime, error) {
} }
} }
return result, nil return result
} }
func (a *App) SimklSyncRemove(anime SimklAnime) (bool, error) { func (a *App) SimklSyncRemove(anime SimklAnime) bool {
url := "https://api.simkl.com/sync/history/remove" url := "https://api.simkl.com/sync/history/remove"
var showArray []SimklShowStatus var showArray []SimklShowStatus
@@ -352,28 +312,25 @@ func (a *App) SimklSyncRemove(anime SimklAnime) (bool, error) {
Shows: showArray, Shows: showArray,
} }
respBody, err := SimklHelper("POST", url, show) respBody := SimklHelper("POST", url, show)
if err != nil {
log.Printf("Failed to sync remove: %s\n", err)
return false, fmt.Errorf("failed to sync remove: %w", err)
}
var success SimklDeleteType var success SimklDeleteType
err = json.Unmarshal(respBody, &success)
err := json.Unmarshal(respBody, &success)
if err != nil { if err != nil {
log.Printf("Failed at unmarshal, %s\n", err) log.Printf("Failed at unmarshal, %s\n", err)
return false, fmt.Errorf("failed to parse response: %w", err)
} }
if success.Deleted.Shows >= 1 { if success.Deleted.Shows >= 1 {
for i, simklAnime := range SimklWatchList.Anime { for i, simklAnime := range SimklWatchList.Anime {
if simklAnime.Show.Ids.Simkl == anime.Show.Ids.Simkl { if simklAnime.Show.Ids.Simkl == anime.Show.Ids.Simkl {
SimklWatchList.Anime = slices.Delete(SimklWatchList.Anime, i, i+1) SimklWatchList.Anime = slices.Delete(SimklWatchList.Anime, i, i+1)
} }
} }
return true, nil return true
} else {
return false
} }
return false, fmt.Errorf("no shows were deleted")
} }
func WatchListUpdate(anime SimklAnime) { func WatchListUpdate(anime SimklAnime) {
+2 -3
View File
@@ -116,7 +116,7 @@ func (a *App) handleSimklCallback(wg *sync.WaitGroup) {
go func() { go func() {
defer wg.Done() defer wg.Done()
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Printf("Server error: %s\n", err) log.Fatalf("listen: %s\n", err)
} }
fmt.Println("Shutting down...") fmt.Println("Shutting down...")
}() }()
@@ -138,8 +138,7 @@ func getSimklAuthorizationToken(content string) SimklJWT {
} }
jsonData, err := json.Marshal(data) jsonData, err := json.Marshal(data)
if err != nil { if err != nil {
log.Printf("Failed to marshal data: %s\n", err) log.Fatal(err)
return SimklJWT{}
} }
response, err := http.NewRequest("POST", "https://api.simkl.com/oauth/token", bytes.NewBuffer(jsonData)) response, err := http.NewRequest("POST", "https://api.simkl.com/oauth/token", bytes.NewBuffer(jsonData))
+4 -17
View File
@@ -1,39 +1,26 @@
#!/bin/bash #!/bin/bash
# copy desktop file # copy desktop file
if [ ! -f "$HOME/.local/share/applications/AniTrack.desktop" ]; then if [ -e "~/.local/share/applications/AniTrack.desktop" ]; then
if [ -d "~/.local/share/applications/" ]; then if [ -d "~/.local/share/applications/" ]; then
echo "Copying desktop file..."
cp ./AniTrack.desktop ~/.local/share/applications/ cp ./AniTrack.desktop ~/.local/share/applications/
else else
mkdir -p ~/.local/share/applications/ mkdir -p ~/.local/share/applications/
echo "Copying desktop file..."
cp ./AniTrack.desktop ~/.local/share/applications/ cp ./AniTrack.desktop ~/.local/share/applications/
fi fi
else
echo "Desktop file already installed..."
fi fi
# copy icons to xdg folders # copy icons to xdg folders
for size in 32 48 64 128; do for size in 32 48 64 128; do
if [ ! -f $HOME/.local/share/icons/hicolor/${size}x${size}/apps/AniTrack.png ]; then xdg-icon-resource install --novendor --context apps --size $size ./icon/$size/AniTrack.png AniTrack
echo "Installing ${size} icon size..."
xdg-icon-resource install --novendor --context apps --size $size ./icon/$size/AniTrack.png AniTrack
else
echo "${size} icon size already exists..."
fi
done done
# copy AniTrack Binary to $HOME/Applications/ # copy AniTrack Binary to $HOME/Applications/
if ! [ -d "$HOME/Applications" ]; then if ! [ -d "~/Applications" ]; then
mkdir -p ~/Applications mkdir -p ~/Applications
echo "Installing app to ~/Applications..."
cp ./bin/AniTrack ~/Applications/ cp ./bin/AniTrack ~/Applications/
elif ! [[ -e $HOME/Applications/AniTrack ]]; then elif ! [[ -e ~/Applications/AniTrack ]]; then
echo "Installing app to ~/Applications"
cp ./bin/AniTrack ~/Applications/ cp ./bin/AniTrack ~/Applications/
else
echo "AniTrack already in Applications..."
fi fi
echo "AniTrack has been successfully installed." echo "AniTrack has been successfully installed."
+5
View File
@@ -0,0 +1,5 @@
{
"recommendations": [
"svelte.svelte-vscode"
]
}
+34 -64
View File
@@ -1,72 +1,42 @@
<script lang="ts"> <script lang="ts">
import { import {
aniListLoggedIn, aniListLoggedIn,
malLoggedIn, malLoggedIn,
simklLoggedIn, simklLoggedIn,
watchlistNeedsRefresh, } from "./helperModules/GlobalVariablesAndHelperFunctions.svelte";
aniListPrimary, import {onMount} from "svelte";
malPrimary, import Router from "svelte-spa-router"
simklPrimary, import Home from "./routes/Home.svelte";
malWatchList, import {wrap} from "svelte-spa-router/wrap";
simklWatchList, import Spinner from "./helperComponents/Spinner.svelte";
} from "./helperModules/GlobalVariablesAndHelperFunctions.svelte"; import Header from "./helperComponents/Header.svelte";
import { onMount } from "svelte"; import {CheckIfAniListLoggedInAndLoadWatchList} from "./helperModules/CheckIfAniListLoggedInAndLoadWatchList.svelte";
import Router from "svelte-spa-router"; import { CheckIfMALLoggedInAndSetUser } from "./helperModules/CheckIfMyAnimeListLoggedIn.svelte";
import Home from "./routes/Home.svelte"; import {CheckIfSimklLoggedInAndSetUser} from "./helperModules/CheckIsSimklLoggedIn.svelte"
import { wrap } from "svelte-spa-router/wrap"; import {CheckIfAniListLoggedIn} from "../wailsjs/go/main/App";
import Spinner from "./helperComponents/Spinner.svelte";
import Header from "./helperComponents/Header.svelte";
import { CheckIfAniListLoggedInAndLoadWatchList } from "./helperModules/CheckIfAniListLoggedInAndLoadWatchList.svelte";
import { CheckIfMALLoggedInAndSetUser } from "./helperModules/CheckIfMyAnimeListLoggedIn.svelte";
import { CheckIfSimklLoggedInAndSetUser } from "./helperModules/CheckIsSimklLoggedIn.svelte";
import {
CheckIfAniListLoggedIn,
GetMyAnimeList,
SimklGetUserWatchlist,
} from "../wailsjs/go/main/App";
import { loc } from "svelte-spa-router";
import ErrorModal from "./helperComponents/ErrorModal.svelte";
onMount(async () => { onMount(async () => {
let isAniListLoggedIn: boolean; let isAniListLoggedIn: boolean
let isMALLoggedIn: boolean; let isMALLoggedIn: boolean
let isSimklLoggedIn: boolean; let isSimklLoggedIn: boolean
aniListLoggedIn.subscribe((value) => (isAniListLoggedIn = value)); aniListLoggedIn.subscribe((value) => isAniListLoggedIn = value)
malLoggedIn.subscribe((value) => (isMALLoggedIn = value)); malLoggedIn.subscribe((value) => isMALLoggedIn = value)
simklLoggedIn.subscribe((value) => (isSimklLoggedIn = value)); simklLoggedIn.subscribe((value) => isSimklLoggedIn = value)
!isAniListLoggedIn && (await CheckIfAniListLoggedInAndLoadWatchList()); console.log(isAniListLoggedIn)
!isMALLoggedIn && (await CheckIfMALLoggedInAndSetUser()); !isAniListLoggedIn && await CheckIfAniListLoggedInAndLoadWatchList()
!isSimklLoggedIn && (await CheckIfSimklLoggedInAndSetUser()); !isMALLoggedIn && await CheckIfMALLoggedInAndSetUser()
}); !isSimklLoggedIn && await CheckIfSimklLoggedInAndSetUser()
})
$: if ($loc?.location === "/" && $watchlistNeedsRefresh) {
(async () => {
if ($aniListLoggedIn && $aniListPrimary) {
await CheckIfAniListLoggedInAndLoadWatchList();
}
if ($malLoggedIn && $malPrimary) {
await GetMyAnimeList(1000).then((w) => malWatchList.set(w));
}
if ($simklLoggedIn && $simklPrimary) {
await SimklGetUserWatchlist().then((w) => simklWatchList.set(w));
}
watchlistNeedsRefresh.set(false);
})();
}
</script> </script>
<Header /> <Header />
<ErrorModal /> <Router routes={{
<Router '/': Home,
routes={{ '/anime/:id': wrap({
"/": Home, asyncComponent: () => import('./routes/AnimeRoutePage.svelte'),
"/anime/:id": wrap({ conditions: [async () => await CheckIfAniListLoggedIn()],
asyncComponent: () => import("./routes/AnimeRoutePage.svelte"), loadingComponent: Spinner
conditions: [async () => await CheckIfAniListLoggedIn()],
loadingComponent: Spinner,
}), }),
// '*': "Not Found" // '*': "Not Found"
}} }} />
/>
File diff suppressed because it is too large Load Diff
+149 -163
View File
@@ -1,177 +1,163 @@
<script lang="ts"> <script lang="ts">
import { Avatar } from "flowbite-svelte"; import { Avatar } from "flowbite-svelte";
import type { AniListUser } from "../anilist/types/AniListTypes"; import type { AniListUser } from "../anilist/types/AniListTypes";
import { import {
aniListLoggedIn, aniListLoggedIn,
aniListUser, aniListUser,
malUser, malUser,
simklUser, simklUser,
malLoggedIn, malLoggedIn,
simklLoggedIn, simklLoggedIn,
loginToAniList, loginToAniList,
loginToMAL, loginToMAL,
loginToSimkl, loginToSimkl,
logoutOfAniList, logoutOfAniList,
logoutOfMAL, logoutOfMAL,
logoutOfSimkl, logoutOfSimkl
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte"; } from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
import * as runtime from "../../wailsjs/runtime"; import * as runtime from "../../wailsjs/runtime";
import type { MyAnimeListUser } from "../mal/types/MALTypes"; import type {MyAnimeListUser} from "../mal/types/MALTypes";
import type { SimklUser } from "../simkl/types/simklTypes"; import type {SimklUser} from "../simkl/types/simklTypes";
import { ShowVersion } from "../../wailsjs/go/main/App"; import { ShowVersion } from "../../wailsjs/go/main/App";
let currentAniListUser: AniListUser; let currentAniListUser: AniListUser;
let currentMALUser: MyAnimeListUser; let currentMALUser: MyAnimeListUser;
let currentSimklUser: SimklUser; let currentSimklUser: SimklUser;
let isAniListLoggedIn: boolean; let isAniListLoggedIn: boolean;
let isSimklLoggedIn: boolean; let isSimklLoggedIn: boolean;
let isMALLoggedIn: boolean; let isMALLoggedIn: boolean;
aniListUser.subscribe((value) => (currentAniListUser = value)); aniListUser.subscribe((value) => (currentAniListUser = value));
malUser.subscribe((value) => (currentMALUser = value)); malUser.subscribe((value) => (currentMALUser = value))
simklUser.subscribe((value) => (currentSimklUser = value)); simklUser.subscribe(value => currentSimklUser = value)
aniListLoggedIn.subscribe((value) => (isAniListLoggedIn = value)); aniListLoggedIn.subscribe((value) => (isAniListLoggedIn = value));
simklLoggedIn.subscribe((value) => (isSimklLoggedIn = value)); simklLoggedIn.subscribe((value) => (isSimklLoggedIn = value));
malLoggedIn.subscribe((value) => (isMALLoggedIn = value)); malLoggedIn.subscribe((value) => (isMALLoggedIn = value));
function dropdownUser(): void { function dropdownUser(): void {
let dropdown = document.querySelector("#userDropdown"); let dropdown = document.querySelector("#userDropdown");
dropdown.classList.toggle("hidden"); dropdown.classList.toggle("hidden");
if (!dropdown.classList.contains("hidden")) { if (!dropdown.classList.contains("hidden")) {
document.addEventListener("click", clickOutside); document.addEventListener("click", clickOutside)
}
} }
}
function clickOutside(event: Event): void { function clickOutside(event: Event): void {
let dropdown = document.querySelector("#userDropdown"); let dropdown = document.querySelector("#userDropdown")
let toggleBtn = document.querySelector("#userDropdownButton"); let toggleBtn = document.querySelector("#userDropdownButton")
if ( if (!dropdown.contains(event.target as Node) && !toggleBtn.contains(event.target as Node)) {
!dropdown.contains(event.target as Node) && dropdown.classList.add("hidden")
!toggleBtn.contains(event.target as Node) document.removeEventListener("click", clickOutside)
) { }
dropdown.classList.add("hidden");
document.removeEventListener("click", clickOutside);
} }
}
</script> </script>
<div class="relative"> <div class="relative">
<button id="userDropdownButton" on:click={dropdownUser}> <button id="userDropdownButton" on:click={dropdownUser}>
{#if isAniListLoggedIn} {#if isAniListLoggedIn}
<Avatar <Avatar
src={currentAniListUser.data.Viewer.avatar.medium} src={currentAniListUser.data.Viewer.avatar.medium}
class="cursor-pointer" class="cursor-pointer"
dot={isAniListLoggedIn && isMALLoggedIn && isSimklLoggedIn dot={{ color: "green" }}
? { color: "green" } />
: { color: "yellow" }} {:else}
/> <Avatar class="cursor-pointer" dot={{ color: "red" }} />
{:else} {/if}
<Avatar class="cursor-pointer" dot={{ color: "red" }} /> </button>
{/if} <div
</button> id="userDropdown"
<div class="absolute hidden right-0 2xl:left-1/2 2xl:-translate-x-1/2 z-10 divide-y rounded-lg shadow w-44 bg-gray-700 divide-gray-600"
id="userDropdown"
class="absolute hidden right-0 2xl:left-1/2 2xl:-translate-x-1/2 z-10 divide-y rounded-lg shadow w-44 bg-gray-700 divide-gray-600"
>
<div class="px-4 py-3 text-sm text-white">
{#if isAniListLoggedIn}
<div>{currentAniListUser.data.Viewer.name}</div>
{:else}
<div>You are not logged into AniList</div>
{/if}
</div>
<ul
class="py-2 text-sm text-gray-200"
aria-labelledby="dropdownUserAvatarButton"
> >
{#if isAniListLoggedIn} <div class="px-4 py-3 text-sm text-white">
<li> {#if isAniListLoggedIn}
<button <div>{currentAniListUser.data.Viewer.name}</div>
on:click={logoutOfAniList} {:else}
class="block px-4 py-2 w-full hover:bg-gray-600 truncate bg-green-800 hover:text-white" <div>You are not logged into AniList</div>
> {/if}
<span class="maple-font text-lg text-green-200 mr-4">A</span>Logout {currentAniListUser </div>
.data.Viewer.name} <ul
</button> class="py-2 text-sm text-gray-200"
</li> aria-labelledby="dropdownUserAvatarButton"
{:else} >
<li> {#if isAniListLoggedIn}
<button <li>
on:click={() => { <button
dropdownUser(); on:click={logoutOfAniList}
loginToAniList(); class="block px-4 py-2 w-full hover:bg-gray-600 truncate bg-green-800 hover:text-white"
}} >
class="block px-4 py-2 w-full hover:bg-gray-600 truncate hover:text-white" <span class="maple-font text-lg text-green-200 mr-4">A</span>Logout {currentAniListUser.data.Viewer.name}
> </button>
<span class="maple-font text-lg mr-4">A</span>Login to AniList </li>
</button> {:else}
</li> <li>
{/if} <button on:click={() => {
{#if isMALLoggedIn} dropdownUser()
<li> loginToAniList()
<button }}
on:click={logoutOfMAL} class="block px-4 py-2 w-full hover:bg-gray-600 truncate hover:text-white">
class="block px-4 py-2 w-full hover:bg-gray-600 truncate bg-blue-800 hover:text-white" <span class="maple-font text-lg mr-4">A</span>Login to AniList
> </button>
<span class="maple-font text-lg text-blue-200 mr-4">M</span>Logout {currentMALUser.name} </li>
</button> {/if}
</li> {#if isMALLoggedIn}
{:else} <li>
<li> <button
<button on:click={logoutOfMAL}
on:click={() => { class="block px-4 py-2 w-full hover:bg-gray-600 truncate bg-blue-800 hover:text-white"
dropdownUser(); >
loginToMAL(); <span class="maple-font text-lg text-blue-200 mr-4">M</span>Logout {currentMALUser.name}
}} </button>
class="block px-4 py-2 w-full hover:bg-gray-600 truncate hover:text-white" </li>
> {:else}
<span class="maple-font text-lg mr-4">M</span>Login to MyAnimeList <li>
</button> <button on:click={() => {
</li> dropdownUser()
{/if} loginToMAL()
{#if isSimklLoggedIn} }}
<li> class="block px-4 py-2 w-full hover:bg-gray-600 truncate hover:text-white">
<button <span class="maple-font text-lg mr-4">M</span>Login to MyAnimeList
on:click={logoutOfSimkl} </button>
class="block px-4 py-2 w-full hover:bg-gray-600 truncate bg-indigo-800 hover:text-white" </li>
> {/if}
<span class="maple-font text-lg text-indigo-200 mr-4">S</span>Logout {currentSimklUser {#if isSimklLoggedIn}
.user.name} <li>
</button> <button
</li> on:click={logoutOfSimkl}
{:else} class="block px-4 py-2 w-full hover:bg-gray-600 truncate bg-indigo-800 hover:text-white"
<li> >
<button <span class="maple-font text-lg text-indigo-200 mr-4">S</span>Logout {currentSimklUser.user.name}
on:click={() => { </button>
dropdownUser(); </li>
loginToSimkl(); {:else}
}} <li>
class="block px-4 py-2 w-full hover:bg-gray-600 truncate hover:text-white" <button on:click={() => {
> dropdownUser()
<span class="maple-font text-lg mr-4">S</span>Login to Simkl loginToSimkl()
</button> }}
</li> class="block px-4 py-2 w-full hover:bg-gray-600 truncate hover:text-white">
{/if} <span class="maple-font text-lg mr-4">S</span>Login to Simkl
</ul> </button>
<div class="py-2"> </li>
<button {/if}
on:click={() => { </ul>
dropdownUser(); <div class="py-2">
ShowVersion(); <button
}} on:click={() => {
class="block px-4 py-2 w-full text-sm hover:bg-gray-600 text-gray-200 over:text-white" dropdownUser()
> ShowVersion()
Version }}
</button> class="block px-4 py-2 w-full text-sm hover:bg-gray-600 text-gray-200 over:text-white"
<button >
on:click={() => runtime.Quit()} Version
class="block px-4 py-2 w-full text-sm hover:bg-gray-600 text-gray-200 over:text-white" </button>
> <button
Exit Application on:click={() => runtime.Quit()}
</button> class="block px-4 py-2 w-full text-sm hover:bg-gray-600 text-gray-200 over:text-white"
>
Exit Application
</button>
</div>
</div> </div>
</div> </div>
</div>
@@ -1,73 +0,0 @@
<script lang="ts">
import {
apiError,
isApiDown,
clearApiError,
setApiError,
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
import { CheckIfAniListLoggedInAndLoadWatchList } from "../helperModules/CheckIfAniListLoggedInAndLoadWatchList.svelte";
import { CheckIfMALLoggedInAndSetUser } from "../helperModules/CheckIfMyAnimeListLoggedIn.svelte";
import { CheckIfSimklLoggedInAndSetUser } from "../helperModules/CheckIsSimklLoggedIn.svelte";
import { Modal, Button } from "flowbite-svelte";
let showModal = false;
$: if ($apiError) {
showModal = true;
}
async function handleRetry() {
const service = $apiError?.service;
clearApiError();
try {
if (service === "anilist") {
await CheckIfAniListLoggedInAndLoadWatchList();
} else if (service === "mal") {
await CheckIfMALLoggedInAndSetUser();
} else if (service === "simkl") {
await CheckIfSimklLoggedInAndSetUser();
}
} catch (err) {
const errorMsg = err instanceof Error ? err.message : String(err);
setApiError(
service || "unknown",
`Retry failed: ${errorMsg}`,
undefined,
true,
);
}
}
function handleDismiss() {
clearApiError();
showModal = false;
}
</script>
{#if showModal && $apiError}
<Modal
open={showModal}
title="{$apiError.service.toUpperCase()} API Error"
size="md"
>
<div class="space-y-4">
<div class="p-4 bg-red-50 border border-red-200 rounded-lg">
<p class="text-red-800 font-medium">{$apiError.message}</p>
{#if $apiError.statusCode}
<p class="text-red-600 text-sm mt-2">
Status: {$apiError.statusCode}
</p>
{/if}
</div>
<p class="text-gray-600">
The application will remain open. You can retry the connection or
dismiss this message to continue with limited functionality.
</p>
</div>
<div slot="footer" class="flex gap-3 justify-end">
{#if $apiError.canRetry}
<Button on:click={handleRetry} class="bg-blue-600 hover:bg-blue-700">
Retry Connection
</Button>
{/if}
<Button on:click={handleDismiss} color="alternative">Dismiss</Button>
</div>
</Modal>
{/if}
+128 -203
View File
@@ -1,220 +1,145 @@
<script lang="ts"> <script lang="ts">
import { import {
aniListLoggedIn, aniListLoggedIn,
aniListSort, aniListWatchlist,
aniListWatchlist, animePerPage,
animePerPage, watchListPage,
watchListPage, } from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
import type { AniListCurrentUserWatchList } from "../anilist/types/AniListCurrentUserWatchListType"; import type {AniListCurrentUserWatchList} from "../anilist/types/AniListCurrentUserWatchListType"
import { GetAniListUserWatchingList } from "../../wailsjs/go/main/App"; import {GetAniListUserWatchingList} from "../../wailsjs/go/main/App";
import {MediaListSort} from "../anilist/types/AniListTypes";
let aniListWatchListLoaded: AniListCurrentUserWatchList; let aniListWatchListLoaded: AniListCurrentUserWatchList
let page: number; let page: number
let perPage: number; let perPage: number
let sort: string;
watchListPage.subscribe((value) => (page = value)); watchListPage.subscribe(value => page = value)
animePerPage.subscribe((value) => (perPage = value)); animePerPage.subscribe(value => perPage = value)
aniListWatchlist.subscribe((value) => (aniListWatchListLoaded = value)); aniListWatchlist.subscribe((value) => aniListWatchListLoaded = value)
aniListSort.subscribe((value) => (sort = value));
const perPageOptions = [10, 20, 50]; const perPageOptions = [10, 20, 50]
function ChangeWatchListPage(newPage: number) { function ChangeWatchListPage(newPage: number) {
GetAniListUserWatchingList(newPage, perPage, sort).then((result) => { GetAniListUserWatchingList(newPage, perPage, MediaListSort.UpdatedTimeDesc).then((result) => {
watchListPage.set(newPage); watchListPage.set(newPage)
aniListWatchlist.set(result); aniListWatchlist.set(result)
aniListLoggedIn.set(true); aniListLoggedIn.set(true)
}); })
} }
function changePage( function changePage(e): void {
e: KeyboardEvent & { currentTarget: HTMLInputElement }, if ((e.key === "Enter" || e.key === "Tab") && Number(e.target.value) !== page) ChangeWatchListPage(Number(e.target.value))
): void { }
if (
(e.key === "Enter" || e.key === "Tab") && function changeCountPerPage(e): void {
Number(e.currentTarget.value) !== page GetAniListUserWatchingList(1, Number(e.target.value), MediaListSort.UpdatedTimeDesc).then((result) => {
) animePerPage.set(Number(e.target.value))
ChangeWatchListPage(Number(e.currentTarget.value)); watchListPage.set(1)
} aniListWatchlist.set(result)
aniListLoggedIn.set(true)
})
}
function changeCountPerPage(
e: Event & { currentTarget: HTMLSelectElement },
): void {
GetAniListUserWatchingList(1, Number(e.currentTarget.value), sort).then(
(result) => {
animePerPage.set(Number(e.currentTarget.value));
watchListPage.set(1);
aniListWatchlist.set(result);
aniListLoggedIn.set(true);
},
);
}
</script> </script>
<div class="mb-8"> <div class="mb-8">
{#if aniListWatchListLoaded.data.Page.pageInfo.lastPage <= 12} {#if aniListWatchListLoaded.data.Page.pageInfo.lastPage <= 12}
<nav aria-label="Page navigation" class="hidden md:block"> <nav aria-label="Page navigation" class="hidden md:block">
<ul class="inline-flex -space-x-px text-base h-10"> <ul class="inline-flex -space-x-px text-base h-10">
{#if page === 1} {#if page === 1}
<li> <li>
<button <button disabled
disabled class="flex items-center justify-center px-4 h-10 ms-0 leading-tight border border-e-0 rounded-s-lg border-gray-700 text-gray-400 cursor-default">
class="flex items-center justify-center px-4 h-10 ms-0 leading-tight border border-e-0 rounded-s-lg border-gray-700 text-gray-400 cursor-default" Previous
> </button>
Previous </li>
</button> {:else}
</li> <li>
{:else} <button on:click={() => ChangeWatchListPage(page-1)}
<li> class="flex items-center justify-center px-4 h-10 ms-0 leading-tight border border-e-0 rounded-s-lg border-gray-700 text-gray-400 hover:bg-gray-700 hover:text-white">
<button Previous
on:click={() => ChangeWatchListPage(page - 1)} </button>
class="flex items-center justify-center px-4 h-10 ms-0 leading-tight border border-e-0 rounded-s-lg border-gray-700 text-gray-400 hover:bg-gray-700 hover:text-white" </li>
> {/if}
Previous {#each {length: aniListWatchListLoaded.data.Page.pageInfo.lastPage} as _, i}
</button> {#if i + 1 === page}
</li> <li>
{/if} <button on:click={() => ChangeWatchListPage(i+1)}
{#each { length: aniListWatchListLoaded.data.Page.pageInfo.lastPage } as _, i} class="flex items-center justify-center px-4 h-10 leading-tight border bg-gray-100 border-gray-700 bg-gray-700 text-white">{i + 1}</button>
{#if i + 1 === page} </li>
<li> {:else}
<button <li>
on:click={() => ChangeWatchListPage(i + 1)} <button on:click={() => ChangeWatchListPage(i+1)}
class="flex items-center justify-center px-4 h-10 leading-tight border hover:bg-gray-100 border-gray-700 bg-gray-700 text-white" class="flex items-center justify-center px-4 h-10 leading-tight border dark border-gray-700 text-gray-400 hover:bg-gray-700 hover:text-white">{i + 1}</button>
>{i + 1}</button </li>
> {/if}
</li> {/each}
{:else} {#if page === aniListWatchListLoaded.data.Page.pageInfo.lastPage}
<li> <li>
<button <button disabled
on:click={() => ChangeWatchListPage(i + 1)} class="flex items-center justify-center px-4 h-10 leading-tight border rounded-e-lg dark border-gray-700 text-gray-400 cursor-default">
class="flex items-center justify-center px-4 h-10 leading-tight border dark border-gray-700 text-gray-400 hover:bg-gray-700 hover:text-white" Next
>{i + 1}</button </button>
> </li>
</li> {:else}
{/if} <li>
{/each} <button on:click={() => ChangeWatchListPage(page+1)}
{#if page === aniListWatchListLoaded.data.Page.pageInfo.lastPage} class="flex items-center justify-center px-4 h-10 leading-tight border rounded-e-lg dark border-gray-700 text-gray-400 hover:bg-gray-700 hover:text-white">
<li> Next
<button </button>
disabled </li>
class="flex items-center justify-center px-4 h-10 leading-tight border rounded-e-lg dark border-gray-700 text-gray-400 cursor-default" {/if}
> </ul>
Next </nav>
</button> {/if}
</li> <div class="flex mt-5">
{:else} <div class="w-20 mx-auto">
<li> <select bind:value={perPage} on:change={(e) => changeCountPerPage(e)} id="countPerPage"
<button class="border text-sm rounded-lg block w-full p-2.5 bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500">
on:click={() => ChangeWatchListPage(page + 1)} {#each perPageOptions as option}
class="flex items-center justify-center px-4 h-10 leading-tight border rounded-e-lg dark border-gray-700 text-gray-400 hover:bg-gray-700 hover:text-white" <option value={option}>
> {option}
Next </option>
</button> {/each}
</li> </select>
{/if}
</ul>
</nav>
{/if}
<div class="flex">
<div class="w-20 mx-auto">
<select
bind:value={perPage}
on:change={(e) => changeCountPerPage(e)}
id="countPerPage"
class="border text-sm rounded-lg block w-full p-2.5 bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500"
>
{#each perPageOptions as option}
<option value={option}>
{option}
</option>
{/each}
</select>
</div>
<div>
<div>Total Anime: {aniListWatchListLoaded.data.Page.pageInfo.total}</div>
{#if aniListWatchListLoaded.data.Page.pageInfo.lastPage <= 12}
<div class="md:hidden">
Page: {page} of {aniListWatchListLoaded.data.Page.pageInfo.lastPage}
</div> </div>
{:else}
<div> <div>
Page: {page} of {aniListWatchListLoaded.data.Page.pageInfo.lastPage} <div>Total Anime: {aniListWatchListLoaded.data.Page.pageInfo.total}</div>
{#if aniListWatchListLoaded.data.Page.pageInfo.lastPage <= 12}
<div class="md:hidden">Page: {page} of {aniListWatchListLoaded.data.Page.pageInfo.lastPage}</div>
{:else}
<div>Page: {page} of {aniListWatchListLoaded.data.Page.pageInfo.lastPage}</div>
{/if}
</div> </div>
{/if}
</div>
<div class="max-w-xs mx-auto"> <div class="max-w-xs mx-auto">
<div class="relative flex items-center max-w-[11rem]"> <div class="relative flex items-center max-w-[11rem]">
<button <button type="button" id="decrement-button" on:click={() => ChangeWatchListPage(page-1)}
type="button" class="bg-gray-700 hover:bg-gray-600 border-gray-600 border rounded-s-lg p-3 h-11 focus:ring-gray-700 focus:ring-2 focus:outline-none">
id="decrement-button" <svg class="w-3 h-3 text-white" aria-hidden="true"
on:click={() => ChangeWatchListPage(page - 1)} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 2">
class={page <= 1 <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
? "border-gray-600 border rounded-s-lg p-3 h-11 focus:ring-gray-700 focus:ring-2 focus:outline-none" d="M1 1h16"/>
: "bg-gray-700 hover:bg-gray-600 border-gray-600 border rounded-s-lg p-3 h-11 focus:ring-gray-700 focus:ring-2 focus:outline-none"} </svg>
disabled={page <= 1} </button>
> <input type="number" min="1" max="{aniListWatchListLoaded.data.Page.pageInfo.lastPage}"
<svg on:keydown={changePage} id="page-counter"
class="w-3 h-3 text-white" class="[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none border-x-0 h-11 font-medium text-center text-sm block w-full pb-6 bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500"
aria-hidden="true" value={page} required/>
xmlns="http://www.w3.org/2000/svg" <div class="absolute bottom-1 start-1/2 -translate-x-1/2 rtl:translate-x-1/2 flex items-center text-xs text-gray-400 space-x-1 rtl:space-x-reverse">
fill="none" <span>Page #</span>
viewBox="0 0 18 2" </div>
> <button type="button" id="increment-button" on:click={() => ChangeWatchListPage(page+1)}
<path class="hover:bg-gray-600 border-gray-600 border rounded-e-lg p-3 h-11 focus:ring-gray-700 focus:ring-2 focus:outline-none">
stroke="currentColor" <svg class="w-3 h-3 text-white" aria-hidden="true"
stroke-linecap="round" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 18">
stroke-linejoin="round" <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
stroke-width="2" d="M9 1v16M1 9h16"/>
d="M1 1h16" </svg>
/> </button>
</svg> </div>
</button>
<input
type="number"
min="1"
max={aniListWatchListLoaded.data.Page.pageInfo.lastPage}
on:keydown={changePage}
id="page-counter"
class="[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none border-x-0 h-11 font-medium text-center text-sm block w-full pb-6 bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500"
value={page}
required
/>
<div
class="absolute bottom-1 start-1/2 -translate-x-1/2 rtl:translate-x-1/2 flex items-center text-xs text-gray-400 space-x-1 rtl:space-x-reverse"
>
<span>Page #</span>
</div> </div>
<button
type="button"
id="increment-button"
on:click={() => ChangeWatchListPage(page + 1)}
class={page >= aniListWatchListLoaded.data.Page.pageInfo.lastPage
? "border-gray-600 border rounded-e-lg p-3 h-11 focus:ring-gray-700 focus:ring-2 focus:outline-none"
: "bg-gray-700 hover:bg-gray-600 border-gray-600 border rounded-e-lg p-3 h-11 focus:ring-gray-700 focus:ring-2 focus:outline-none"}
disabled={page >= aniListWatchListLoaded.data.Page.pageInfo.lastPage}
>
<svg
class="w-3 h-3 text-white"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 18 18"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 1v16M1 9h16"
/>
</svg>
</button>
</div>
</div> </div>
</div> </div>
</div>
@@ -1,18 +0,0 @@
<script lang="ts">
import { CheckIfAniListLoggedInAndLoadWatchList } from "../helperModules/CheckIfAniListLoggedInAndLoadWatchList.svelte";
import { loading } from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
</script>
<div class="flex justify-end">
<button
type="button"
class="py-2 px-4 mt-4 mr-4 bg-gray-700 rounded-lg"
on:click={async () => {
loading.set(true);
await CheckIfAniListLoggedInAndLoadWatchList();
loading.set(false);
}}
>
Refresh WatchList
</button>
</div>
-77
View File
@@ -1,77 +0,0 @@
<script lang="ts">
import {
aniListSort,
watchListPage,
animePerPage,
aniListWatchlist,
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
import { MediaListSort } from "../anilist/types/AniListTypes";
import { GetAniListUserWatchingList } from "../../wailsjs/go/main/App";
const sortTypes = [
{ value: MediaListSort.MediaId, name: "Media Id Asc" },
{ value: MediaListSort.MediaIdDesc, name: "Media Id Desc" },
{ value: MediaListSort.Score, name: "Score Asc" },
{ value: MediaListSort.ScoreDesc, name: "Score Desc" },
{ value: MediaListSort.Status, name: "Status Asc" },
{ value: MediaListSort.StatusDesc, name: "Status Desc" },
{ value: MediaListSort.Progress, name: "Progress Asc" },
{ value: MediaListSort.ProgressDesc, name: "Progress Desc" },
{ value: MediaListSort.ProgressVolumes, name: "Progress Valumes Asc" },
{ value: MediaListSort.ProgressVolumesDesc, name: "Progress Valumes Desc" },
{ value: MediaListSort.Repeat, name: "Repeat Asc" },
{ value: MediaListSort.RepeatDesc, name: "Repeat Desc" },
{ value: MediaListSort.Priority, name: "Priority Asc" },
{ value: MediaListSort.PriorityDesc, name: "Priority Desc" },
{ value: MediaListSort.StartedOn, name: "Started On Asc" },
{ value: MediaListSort.StartedOnDesc, name: "Started On Desc" },
{ value: MediaListSort.FinishedOn, name: "Finished On Asc" },
{ value: MediaListSort.FinishedOnDesc, name: "Finished On Desc" },
{ value: MediaListSort.AddedTime, name: "Added Time Asc" },
{ value: MediaListSort.AddedTimeDesc, name: "Added Time Desc" },
{ value: MediaListSort.UpdatedTime, name: "Updated Time Asc" },
{ value: MediaListSort.UpdatedTimeDesc, name: "Updated Time Desc" },
{ value: MediaListSort.MediaTitleRomaji, name: "Media Title Romaji Asc" },
{
value: MediaListSort.MediaTitleRomajiDesc,
name: "Media Title Romaji Desc",
},
{ value: MediaListSort.MediaTitleEnglish, name: "Media Title English Asc" },
{
value: MediaListSort.MediaTitleEnglishDesc,
name: "Media Title English Desc",
},
{ value: MediaListSort.MediaTitleNative, name: "Media Title Native Asc" },
{
value: MediaListSort.MediaTitleNativeDesc,
name: "Media Title Native Desc",
},
{ value: MediaListSort.MediaPopularity, name: "Media Popularity Asc" },
{ value: MediaListSort.MediaPopularityDesc, name: "Media Popularity Desc" },
];
let sort: string;
aniListSort.subscribe((value) => (sort = value));
console.log(sort);
async function changeWatchListSort() {
const result = await GetAniListUserWatchingList(
$watchListPage,
$animePerPage,
$aniListSort,
);
aniListWatchlist.set(result);
}
</script>
<select
id="sort"
class="border rounded-lg block p-1.5 bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500"
bind:value={$aniListSort}
on:change={() => changeWatchListSort()}
>
<option value="" disabled>Sort WatchList</option>
{#each sortTypes as sort}
<option value={sort.value}>{sort.name}</option>
{/each}
</select>
+59 -81
View File
@@ -1,90 +1,68 @@
<script lang="ts"> <script lang="ts">
import { import {
aniListLoggedIn, aniListLoggedIn,
aniListWatchlist, aniListWatchlist,
GetAnimeSingleItem, GetAnimeSingleItem,
loading, loading,
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte"; } from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
import { push } from "svelte-spa-router"; import {push} from "svelte-spa-router";
import type { AniListCurrentUserWatchList } from "../anilist/types/AniListCurrentUserWatchListType"; import type {AniListCurrentUserWatchList} from "../anilist/types/AniListCurrentUserWatchListType"
import { Rating } from "flowbite-svelte"; import {Rating} from "flowbite-svelte";
import loader from "../helperFunctions/loader"; import loader from '../helperFunctions/loader'
import { CheckIfAniListLoggedInAndLoadWatchList } from "../helperModules/CheckIfAniListLoggedInAndLoadWatchList.svelte";
import Sort from "../helperComponents/Sort.svelte";
let isAniListLoggedIn: boolean;
let aniListWatchListLoaded: AniListCurrentUserWatchList;
aniListLoggedIn.subscribe((value) => (isAniListLoggedIn = value)); let isAniListLoggedIn: boolean
aniListWatchlist.subscribe((value) => (aniListWatchListLoaded = value)); let aniListWatchListLoaded: AniListCurrentUserWatchList
aniListLoggedIn.subscribe((value) => isAniListLoggedIn = value)
aniListWatchlist.subscribe((value) => aniListWatchListLoaded = value)
</script> </script>
<div> <div>
{#if isAniListLoggedIn} {#if isAniListLoggedIn}
<div <div class="mx-auto max-w-2xl p-4 sm:p-6 lg:max-w-7xl lg:px-8 relative items-center">
class="mx-auto max-w-2xl p-4 sm:p-6 lg:max-w-7xl lg:px-8 relative items-center" <h1 class="text-left text-xl font-bold mb-4">Your AniList WatchList</h1>
>
<div class="flex justify-between items-center mb-4">
<h1 class="text-left text-xl font-bold">Your AniList WatchList</h1>
<Sort />
</div>
<div <div class="grid grid-cols-1 gap-x-6 gap-y-10 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 xl:gap-x-8">
class="grid grid-cols-1 gap-x-6 gap-y-10 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 xl:gap-x-8" {#each aniListWatchListLoaded.data.Page.mediaList as media}
> <div use:loader={loading} class="aspect-h-1 aspect-w-1 w-full overflow-hidden rounded-lg xl:aspect-h-8 xl:aspect-w-7">
{#each aniListWatchListLoaded.data.Page.mediaList as media} <div class="flex flex-col items-center group">
<div <button on:click={() => {
use:loader={loading} push(`#/anime/${media.media.id}`)
class="aspect-h-1 aspect-w-1 w-full overflow-hidden rounded-lg xl:aspect-h-8 xl:aspect-w-7" // loading.set(true)
> // GetAniListSingleItem(media.media.id, true).then(() => {
<div class="flex flex-col items-center group"> // loading.set(false)
<button //
on:click={() => { // })
push(`#/anime/${media.media.id}`); }}
// loading.set(true) >
// GetAniListSingleItem(media.media.id, true).then(() => { <img class="rounded-lg" src={media.media.coverImage.large} alt={
// loading.set(false) media.media.title.english === "" ?
// media.media.title.romaji :
// }) media.media.title.english
}} }/>
> </button>
<img <Rating id="anime-rating" total={5} size={35} rating={media.score/2.0}/>
class="rounded-lg w-[230px] h-[330px] object-cover" <button class="mt-4 text-md font-semibold text-white-700"
src={media.media.coverImage.large} on:click={() => GetAnimeSingleItem(media.media.id, true)}>
alt={media.media.title.english === "" {
? media.media.title.romaji media.media.title.english === "" ?
: media.media.title.english} media.media.title.romaji :
/> media.media.title.english
</button> }
<Rating </button>
id="anime-rating" <p class="mt-1 text-lg font-medium text-white-900">{media.progress}
total={5} / {media.media.nextAiringEpisode.episode !== 0 ?
size={35} media.media.nextAiringEpisode.episode - 1 : media.media.episodes}</p>
rating={media.score / 2.0} {#if media.media.episodes > 0}
/> <p class="mt-1 text-lg font-medium text-white-900">Total
<button Episodes: {media.media.episodes}</p>
class="mt-4 text-md font-semibold text-white-700" {/if}
on:click={() => GetAnimeSingleItem(media.media.id, true)} </div>
> </div>
{media.media.title.english === "" {/each}
? media.media.title.romaji
: media.media.title.english}
</button>
<p class="mt-1 text-lg font-medium text-white-900">
{media.progress}
/ {media.media.nextAiringEpisode.episode !== 0
? media.media.nextAiringEpisode.episode - 1
: media.media.episodes}
</p>
{#if media.media.episodes > 0}
<p class="mt-1 text-lg font-medium text-white-900">
Total Episodes: {media.media.episodes}
</p>
{/if}
</div> </div>
</div> </div>
{/each} {/if}
</div>
</div>
{/if}
</div> </div>
@@ -1,81 +1,34 @@
<script lang="ts" context="module"> <script lang="ts" context="module">
import { import {CheckIfAniListLoggedIn, GetAniListLoggedInUser, GetAniListUserWatchingList} from "../../wailsjs/go/main/App";
CheckIfAniListLoggedIn, import {MediaListSort} from "../anilist/types/AniListTypes";
GetAniListLoggedInUser, import { aniListUser, watchListPage, animePerPage, aniListPrimary, aniListLoggedIn, aniListWatchlist } from "./GlobalVariablesAndHelperFunctions.svelte"
GetAniListUserWatchingList,
} from "../../wailsjs/go/main/App";
import {
aniListUser,
watchListPage,
animePerPage,
aniListPrimary,
aniListLoggedIn,
aniListWatchlist,
aniListSort,
clearApiError,
setApiError,
} from "./GlobalVariablesAndHelperFunctions.svelte";
let isAniListPrimary: boolean; let isAniListPrimary: boolean
let page: number; let page: number
let perPage: number; let perPage: number
let sort: string;
aniListPrimary.subscribe((value) => (isAniListPrimary = value)); aniListPrimary.subscribe(value => isAniListPrimary = value)
watchListPage.subscribe((value) => (page = value)); watchListPage.subscribe(value => page = value)
animePerPage.subscribe((value) => (perPage = value)); animePerPage.subscribe(value => perPage = value)
aniListSort.subscribe((value) => (sort = value));
export const LoadAniListUser = async () => { export const LoadAniListUser = async () => {
try { await GetAniListLoggedInUser().then(user => {
await GetAniListLoggedInUser().then((user) => { aniListUser.set(user)
aniListUser.set(user); })
});
} catch (err) {
const errorMsg = err instanceof Error ? err.message : String(err);
setApiError(
"anilist",
`Failed to load user: ${errorMsg}`,
undefined,
true,
);
throw err;
} }
};
export const LoadAniListWatchList = async () => { export const LoadAniListWatchList = async () => {
try { await GetAniListUserWatchingList(page, perPage, MediaListSort.UpdatedTimeDesc).then((watchList) => {
const watchList = await GetAniListUserWatchingList(page, perPage, sort); aniListWatchlist.set(watchList)
aniListWatchlist.set(watchList); })
clearApiError();
} catch (err) {
const errorMsg = err instanceof Error ? err.message : String(err);
setApiError(
"anilist",
`Failed to load watch list: ${errorMsg}`,
undefined,
true,
);
throw err;
} }
};
export const CheckIfAniListLoggedInAndLoadWatchList = async () => { export const CheckIfAniListLoggedInAndLoadWatchList = async () => {
try { const loggedIn = await CheckIfAniListLoggedIn()
const loggedIn = await CheckIfAniListLoggedIn(); if (loggedIn) {
if (loggedIn) { await LoadAniListUser()
await LoadAniListUser(); if (isAniListPrimary) await LoadAniListWatchList()
if (isAniListPrimary) await LoadAniListWatchList(); }
} aniListLoggedIn.set(loggedIn)
aniListLoggedIn.set(loggedIn);
} catch (err) {
const errorMsg = err instanceof Error ? err.message : String(err);
setApiError(
"anilist",
`Authentication failed: ${errorMsg}`,
undefined,
true,
);
aniListLoggedIn.set(false);
} }
}; </script>
</script>
@@ -1,212 +1,162 @@
<script lang="ts" context="module"> <script lang="ts" context="module">
import { import {
GetAniListItem, GetAniListItem,
GetAniListLoggedInUser, GetAniListLoggedInUser,
GetAniListUserWatchingList, GetAniListUserWatchingList,
GetMyAnimeListAnime, GetMyAnimeListAnime,
GetMyAnimeListLoggedInUser, GetMyAnimeListLoggedInUser,
GetSimklLoggedInUser, GetSimklLoggedInUser,
LogoutAniList, LogoutAniList,
LogoutMyAnimeList, LogoutMyAnimeList,
LogoutSimkl, LogoutSimkl,
SimklGetUserWatchlist, SimklGetUserWatchlist,
SimklSearch, SimklSearch
} from "../../wailsjs/go/main/App"; } from "../../wailsjs/go/main/App";
import type { import type {
AniListCurrentUserWatchList, AniListCurrentUserWatchList,
AniListGetSingleAnime, AniListGetSingleAnime
} from "../anilist/types/AniListCurrentUserWatchListType.js"; } from "../anilist/types/AniListCurrentUserWatchListType.js";
import { writable } from "svelte/store"; import {writable} from 'svelte/store'
import type { import type {SimklAnime, SimklUser, SimklWatchList} from "../simkl/types/simklTypes";
SimklAnime, import {type AniListUser, MediaListSort} from "../anilist/types/AniListTypes";
SimklUser, import type {MALAnime, MALWatchlist, MyAnimeListUser} from "../mal/types/MALTypes";
SimklWatchList, import type {TableItems} from "../helperTypes/TableTypes";
} from "../simkl/types/simklTypes"; import {AniListGetSingleAnimeDefaultData} from "../helperDefaults/AniListGetSingleAnime";
import {
type AniListUser,
MediaListSort,
} from "../anilist/types/AniListTypes";
import type {
MALAnime,
MALWatchlist,
MyAnimeListUser,
} from "../mal/types/MALTypes";
import type { TableItems } from "../helperTypes/TableTypes";
import { AniListGetSingleAnimeDefaultData } from "../helperDefaults/AniListGetSingleAnime";
export let aniListAnime = writable(AniListGetSingleAnimeDefaultData); export let aniListAnime = writable(AniListGetSingleAnimeDefaultData)
export let title = writable(""); export let title = writable("")
export let aniListLoggedIn = writable(false); export let aniListLoggedIn = writable(false)
export let simklLoggedIn = writable(false); export let simklLoggedIn = writable(false)
export let malLoggedIn = writable(false); export let malLoggedIn = writable(false)
export let simklWatchList = writable({} as SimklWatchList); export let simklWatchList = writable({} as SimklWatchList)
export let aniListPrimary = writable(true); export let aniListPrimary = writable(true)
export let simklPrimary = writable(false); export let simklPrimary = writable(false)
export let malPrimary = writable(false); export let malPrimary = writable(false)
export let simklUser = writable({} as SimklUser); export let simklUser = writable({} as SimklUser)
export let aniListUser = writable({} as AniListUser); export let aniListUser = writable({} as AniListUser)
export let malUser = writable({} as MyAnimeListUser); export let malUser = writable({} as MyAnimeListUser)
export let aniListWatchlist = writable({} as AniListCurrentUserWatchList); export let aniListWatchlist = writable({} as AniListCurrentUserWatchList)
export let malWatchList = writable({} as MALWatchlist); export let malWatchList = writable({} as MALWatchlist)
export let malAnime = writable({} as MALAnime); export let malAnime = writable({} as MALAnime)
export let simklAnime = writable({} as SimklAnime); export let simklAnime = writable({} as SimklAnime)
export let loading = writable(false); export let loading = writable(false)
export let tableItems = writable([] as TableItems); export let tableItems = writable([] as TableItems)
export let watchlistNeedsRefresh = writable(false);
export let aniListSort = writable(MediaListSort.UpdatedTimeDesc);
export let watchListPage = writable(1); export let watchListPage = writable(1)
export let animePerPage = writable(20); export let animePerPage = writable(20)
let isAniListPrimary: boolean; let isAniListPrimary: boolean
let page: number; let page: number
let perPage: number; let perPage: number
let sort: string; let aniWatchlist: AniListCurrentUserWatchList
let aniWatchlist: AniListCurrentUserWatchList; let currentAniListAnime: AniListGetSingleAnime
let currentAniListAnime: AniListGetSingleAnime;
let isMalLoggedIn: boolean; let isMalLoggedIn: boolean
let isSimklLoggedIn: boolean; let isSimklLoggedIn: boolean
aniListPrimary.subscribe((value) => (isAniListPrimary = value)); aniListPrimary.subscribe(value => isAniListPrimary = value)
watchListPage.subscribe((value) => (page = value)); watchListPage.subscribe(value => page = value)
animePerPage.subscribe((value) => (perPage = value)); animePerPage.subscribe(value => perPage = value)
aniListWatchlist.subscribe((value) => (aniWatchlist = value)); aniListWatchlist.subscribe(value => aniWatchlist = value)
malLoggedIn.subscribe((value) => (isMalLoggedIn = value)); malLoggedIn.subscribe(value => isMalLoggedIn = value)
simklLoggedIn.subscribe((value) => (isSimklLoggedIn = value)); simklLoggedIn.subscribe(value => isSimklLoggedIn = value)
aniListAnime.subscribe((value) => (currentAniListAnime = value)); aniListAnime.subscribe(value => currentAniListAnime = value)
aniListSort.subscribe((value) => (sort = value));
export interface ApiError {
service: string;
message: string;
statusCode?: string;
canRetry: boolean;
}
export const apiError = writable<ApiError | null>(null);
export const isApiDown = writable(false);
export function setApiError(
service: string,
message: string,
statusCode?: string,
canRetry: boolean = true,
) {
apiError.set({
service,
message,
statusCode,
canRetry,
});
isApiDown.set(true);
}
export function clearApiError() {
apiError.set(null);
isApiDown.set(false);
}
export async function GetAnimeSingleItem( export async function GetAnimeSingleItem(aniId: number, login: boolean): Promise<""> {
aniId: number, await GetAniListItem(aniId, login).then(aniListResult => {
login: boolean, let finalResult: AniListGetSingleAnime
): Promise<""> { finalResult = aniListResult
await GetAniListItem(aniId, login).then((aniListResult) => { if (login === false) {
let finalResult: AniListGetSingleAnime; finalResult.data.MediaList.status = ""
finalResult = aniListResult; finalResult.data.MediaList.score = 0
if (login === false) { finalResult.data.MediaList.progress = 0
finalResult.data.MediaList.status = ""; finalResult.data.MediaList.notes = ""
finalResult.data.MediaList.score = 0; finalResult.data.MediaList.repeat = 0
finalResult.data.MediaList.progress = 0; finalResult.data.MediaList.startedAt.day = 0
finalResult.data.MediaList.notes = ""; finalResult.data.MediaList.startedAt.month = 0
finalResult.data.MediaList.repeat = 0; finalResult.data.MediaList.startedAt.year = 0
finalResult.data.MediaList.startedAt.day = 0; finalResult.data.MediaList.completedAt.day = 0
finalResult.data.MediaList.startedAt.month = 0; finalResult.data.MediaList.completedAt.month = 0
finalResult.data.MediaList.startedAt.year = 0; finalResult.data.MediaList.completedAt.year = 0
finalResult.data.MediaList.completedAt.day = 0; }
finalResult.data.MediaList.completedAt.month = 0; aniListAnime.set(finalResult)
finalResult.data.MediaList.completedAt.year = 0; title.set(currentAniListAnime.data.MediaList.media.title.english === "" ?
} currentAniListAnime.data.MediaList.media.title.romaji :
aniListAnime.set(finalResult); currentAniListAnime.data.MediaList.media.title.english)
title.set( })
currentAniListAnime.data.MediaList.media.title.english === "" if (isMalLoggedIn) {
? currentAniListAnime.data.MediaList.media.title.romaji await GetMyAnimeListAnime(currentAniListAnime.data.MediaList.media.idMal).then(malResult => {
: currentAniListAnime.data.MediaList.media.title.english, malAnime.set(malResult)
); })
}); }
if (isMalLoggedIn) { if (isSimklLoggedIn) {
await GetMyAnimeListAnime( await SimklSearch(currentAniListAnime.data.MediaList).then((value: SimklAnime) => {
currentAniListAnime.data.MediaList.media.idMal, simklAnime.set(value)
).then((malResult) => { })
malAnime.set(malResult); }
}); return ""
} }
if (isSimklLoggedIn) {
await SimklSearch(currentAniListAnime.data.MediaList).then( export function loginToSimkl(): void {
(value: SimklAnime) => { GetSimklLoggedInUser().then(user => {
simklAnime.set(value); if (Object.keys(user).length === 0) {
}, simklLoggedIn.set(false)
); } else {
simklUser.set(user)
SimklGetUserWatchlist().then(result => {
simklWatchList.set(result)
simklLoggedIn.set(true)
})
}
})
} }
return "";
}
export function loginToSimkl(): void { export function loginToAniList(): void {
GetSimklLoggedInUser().then((user) => { GetAniListLoggedInUser().then(result => {
if (Object.keys(user).length === 0) { aniListUser.set(result)
simklLoggedIn.set(false); if (isAniListPrimary) {
} else { GetAniListUserWatchingList(page, perPage, MediaListSort.UpdatedTimeDesc).then((result) => {
simklUser.set(user); aniListWatchlist.set(result)
SimklGetUserWatchlist().then((result) => { aniListLoggedIn.set(true)
simklWatchList.set(result); })
simklLoggedIn.set(true); } else {
}); aniListLoggedIn.set(true)
} }
}); })
} }
export function loginToAniList(): void { export function loginToMAL(): void {
GetAniListLoggedInUser().then((result) => { GetMyAnimeListLoggedInUser().then(result => {
aniListUser.set(result); malUser.set(result)
if (isAniListPrimary) { malLoggedIn.set(true)
GetAniListUserWatchingList(page, perPage, sort).then((result) => { })
aniListWatchlist.set(result); }
aniListLoggedIn.set(true);
});
} else {
aniListLoggedIn.set(true);
}
});
}
export function loginToMAL(): void { export function logoutOfAniList(): void {
GetMyAnimeListLoggedInUser().then((result) => { LogoutAniList().then(result => {
malUser.set(result); console.log(result)
malLoggedIn.set(true); if (Object.keys(aniWatchlist).length !== 0) {
}); aniListWatchlist.set({} as AniListCurrentUserWatchList)
} }
aniListUser.set({} as AniListUser)
aniListLoggedIn.set(false)
})
}
export function logoutOfAniList(): void { export function logoutOfMAL(): void {
LogoutAniList().then((result) => { LogoutMyAnimeList().then(result => {
console.log(result); console.log(result)
if (Object.keys(aniWatchlist).length !== 0) { malUser.set({} as MyAnimeListUser)
aniListWatchlist.set({} as AniListCurrentUserWatchList); malLoggedIn.set(false)
} })
aniListUser.set({} as AniListUser); }
aniListLoggedIn.set(false);
});
}
export function logoutOfMAL(): void { export function logoutOfSimkl(): void {
LogoutMyAnimeList().then((result) => { LogoutSimkl().then(result => {
console.log(result); console.log(result)
malUser.set({} as MyAnimeListUser); simklUser.set({} as SimklUser)
malLoggedIn.set(false); simklLoggedIn.set(false)
}); })
} }
</script>
export function logoutOfSimkl(): void {
LogoutSimkl().then((result) => {
console.log(result);
simklUser.set({} as SimklUser);
simklLoggedIn.set(false);
});
}
</script>
+14 -46
View File
@@ -1,57 +1,25 @@
<script lang="ts"> <script lang="ts">
import Pagination from "../helperComponents/Pagination.svelte"; import Pagination from "../helperComponents/Pagination.svelte";
import WatchList from "../helperComponents/WatchList.svelte"; import WatchList from "../helperComponents/WatchList.svelte";
import RefreshWatchListButton from "../helperComponents/RefreshWatchListButton.svelte"; import {
import {
aniListLoggedIn, aniListLoggedIn,
aniListPrimary, aniListPrimary,
loading, loading,
isApiDown, } from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
apiError, import loader from '../helperFunctions/loader'
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
import loader from "../helperFunctions/loader";
let isAniListPrimary: boolean; let isAniListPrimary: boolean
let isAniListLoggedIn: boolean; let isAniListLoggedIn: boolean
aniListPrimary.subscribe((value) => (isAniListPrimary = value)); aniListPrimary.subscribe((value) => isAniListPrimary = value)
aniListLoggedIn.subscribe((value) => (isAniListLoggedIn = value)); aniListLoggedIn.subscribe((value) => isAniListLoggedIn = value)
</script> </script>
{#if isAniListLoggedIn && isAniListPrimary}
{#if $isApiDown} <div class="container py-10">
<div class="container py-10">
<div
class="bg-yellow-50 border border-yellow-200 rounded-lg p-6 text-center"
>
<svg
class="mx-auto h-12 w-12 text-yellow-600 mb-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
/>
</svg>
<h2 class="text-xl font-semibold text-yellow-900 mb-2">
API Unavailable
</h2>
<p class="text-yellow-700 mb-4">
The {$apiError?.service || "service"} is currently unavailable. The app will
remain open, and you can retry when the service is back online.
</p>
</div>
</div>
{:else if isAniListLoggedIn && isAniListPrimary}
<RefreshWatchListButton />
<div class="container py-10">
<Pagination /> <Pagination />
<WatchList /> <WatchList />
<Pagination /> <Pagination />
</div> </div>
{:else} {:else}
<div use:loader={loading}></div> <div use:loader={loading}></div>
{/if} {/if}
@@ -0,0 +1,10 @@
// vite.config.ts
import { defineConfig } from "file:///home/nymusicman/Code/AniTrack/frontend/node_modules/vite/dist/node/index.js";
import { svelte } from "file:///home/nymusicman/Code/AniTrack/frontend/node_modules/@sveltejs/vite-plugin-svelte/src/index.js";
var vite_config_default = defineConfig({
plugins: [svelte()]
});
export {
vite_config_default as default
};
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCIvaG9tZS9ueW11c2ljbWFuL0NvZGUvQW5pVHJhY2svZnJvbnRlbmRcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIi9ob21lL255bXVzaWNtYW4vQ29kZS9BbmlUcmFjay9mcm9udGVuZC92aXRlLmNvbmZpZy50c1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vaG9tZS9ueW11c2ljbWFuL0NvZGUvQW5pVHJhY2svZnJvbnRlbmQvdml0ZS5jb25maWcudHNcIjtpbXBvcnQge2RlZmluZUNvbmZpZ30gZnJvbSAndml0ZSdcbmltcG9ydCB7c3ZlbHRlfSBmcm9tICdAc3ZlbHRlanMvdml0ZS1wbHVnaW4tc3ZlbHRlJ1xuXG4vLyBodHRwczovL3ZpdGVqcy5kZXYvY29uZmlnL1xuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKHtcbiAgcGx1Z2luczogW3N2ZWx0ZSgpXVxufSlcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFBdVMsU0FBUSxvQkFBbUI7QUFDbFUsU0FBUSxjQUFhO0FBR3JCLElBQU8sc0JBQVEsYUFBYTtBQUFBLEVBQzFCLFNBQVMsQ0FBQyxPQUFPLENBQUM7QUFDcEIsQ0FBQzsiLAogICJuYW1lcyI6IFtdCn0K
+2 -63
View File
@@ -230,7 +230,8 @@ export namespace main {
background: background; background: background;
related_anime: relatedAnime; related_anime: relatedAnime;
recommendations: recommendations; recommendations: recommendations;
Statistics: struct { NumListUsers int "json:\"num_list_users\" ts_type:\"numListUsers\""; Status struct { Watching main.; // Go type: struct { NumListUsers int "json:\"num_list_users\" ts_type:\"numListUsers\""; Status struct { Watching string "json:\"watching\" ts_type:\"watching\""; Completed string "json:\"completed\" ts_type:\"completed\""; OnHold string "json:\"on_hold\" ts_type:\"onHold\""; Dropped string "json:\"dropped\" ts_type:\"dropped\""; PlanToWatch string "json:\"plan_to_watch\" ts_type:\"planToWatch\"" } }
Statistics: any;
static createFrom(source: any = {}) { static createFrom(source: any = {}) {
return new MALAnime(source); return new MALAnime(source);
@@ -623,43 +624,6 @@ export namespace struct { Node struct { Id int "json:\"id\" ts_type:\"id\""; Tit
} }
export namespace struct { NumListUsers int "json:\"num_list_users\" ts_type:\"numListUsers\""; Status struct { Watching main {
export class {
num_list_users: numListUsers;
Status: struct { Watching main.;
static createFrom(source: any = {}) {
return new (source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.num_list_users = source["num_list_users"];
this.Status = this.convertValues(source["Status"], Object);
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice && a.map) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
}
export namespace struct { Status string "json:\"status\" ts_type:\"status\""; Score int "json:\"score\" ts_type:\"score\""; NumEpisodesWatched int "json:\"num_episodes_watched\" ts_type:\"numEpisodesWatched\""; IsRewatching bool "json:\"is_rewatching\" ts_type:\"isRewatching\""; UpdatedAt time { export namespace struct { Status string "json:\"status\" ts_type:\"status\""; Score int "json:\"score\" ts_type:\"score\""; NumEpisodesWatched int "json:\"num_episodes_watched\" ts_type:\"numEpisodesWatched\""; IsRewatching bool "json:\"is_rewatching\" ts_type:\"isRewatching\""; UpdatedAt time {
export class { export class {
@@ -689,28 +653,3 @@ export namespace struct { Status string "json:\"status\" ts_type:\"status\""; Sc
} }
export namespace struct { Watching main {
export class {
watching: string;
completed: string;
on_hold: string;
dropped: string;
plan_to_watch: string;
static createFrom(source: any = {}) {
return new (source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.watching = source["watching"];
this.completed = source["completed"];
this.on_hold = source["on_hold"];
this.dropped = source["dropped"];
this.plan_to_watch = source["plan_to_watch"];
}
}
}
+17 -19
View File
@@ -1,51 +1,49 @@
module AniTrack module AniTrack
go 1.25.0 go 1.24.0
require ( require (
github.com/99designs/keyring v1.2.2 github.com/99designs/keyring v1.2.2
github.com/tidwall/gjson v1.19.0 github.com/tidwall/gjson v1.18.0
github.com/wailsapp/wails/v2 v2.12.0 github.com/wailsapp/wails/v2 v2.10.1
) )
require ( require (
git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3 // indirect
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
github.com/bep/debounce v1.2.1 // indirect github.com/bep/debounce v1.2.1 // indirect
github.com/danieljoos/wincred v1.2.3 // indirect github.com/danieljoos/wincred v1.2.2 // indirect
github.com/dvsekhvalnov/jose2go v1.8.0 // indirect github.com/dvsekhvalnov/jose2go v1.8.0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
github.com/godbus/dbus/v5 v5.2.2 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 // indirect github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
github.com/labstack/echo/v4 v4.15.2 // indirect github.com/labstack/echo/v4 v4.13.3 // indirect
github.com/labstack/gommon v0.5.0 // indirect github.com/labstack/gommon v0.4.2 // indirect
github.com/leaanthony/go-ansi-parser v1.6.1 // indirect github.com/leaanthony/go-ansi-parser v1.6.1 // indirect
github.com/leaanthony/gosod v1.0.4 // indirect github.com/leaanthony/gosod v1.0.4 // indirect
github.com/leaanthony/slicer v1.6.0 // indirect github.com/leaanthony/slicer v1.6.0 // indirect
github.com/leaanthony/u v1.1.1 // indirect github.com/leaanthony/u v1.1.1 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.22 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mtibben/percent v0.2.1 // indirect github.com/mtibben/percent v0.2.1 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect github.com/rivo/uniseg v0.4.7 // indirect
github.com/samber/lo v1.53.0 // indirect github.com/samber/lo v1.49.1 // indirect
github.com/tidwall/match v1.2.0 // indirect github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect
github.com/tkrajina/go-reflector v0.5.8 // indirect github.com/tkrajina/go-reflector v0.5.8 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/wailsapp/go-webview2 v1.0.23 // indirect github.com/wailsapp/go-webview2 v1.0.19 // indirect
github.com/wailsapp/mimetype v1.4.1 // indirect github.com/wailsapp/mimetype v1.4.1 // indirect
golang.org/x/crypto v0.52.0 // indirect golang.org/x/crypto v0.45.0 // indirect
golang.org/x/net v0.55.0 // indirect golang.org/x/net v0.47.0 // indirect
golang.org/x/sys v0.45.0 // indirect golang.org/x/sys v0.38.0 // indirect
golang.org/x/term v0.43.0 // indirect golang.org/x/term v0.37.0 // indirect
golang.org/x/text v0.37.0 // indirect golang.org/x/text v0.31.0 // indirect
) )
// replace github.com/wailsapp/wails/v2 v2.9.1 => /home/nymusicman/go/pkg/mod // replace github.com/wailsapp/wails/v2 v2.9.1 => /home/nymusicman/go/pkg/mod
+38 -39
View File
@@ -1,13 +1,11 @@
git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3 h1:N3IGoHHp9pb6mj1cbXbuaSXV/UMKwmbKLf53nQmtqMA=
git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3/go.mod h1:QtOLZGz8olr4qH2vWK0QH0w0O4T9fEIjMuWpKUsH7nc=
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs=
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4=
github.com/99designs/keyring v1.2.2 h1:pZd3neh/EmUzWONb35LxQfvuY7kiSXAq3HQd97+XBn0= github.com/99designs/keyring v1.2.2 h1:pZd3neh/EmUzWONb35LxQfvuY7kiSXAq3HQd97+XBn0=
github.com/99designs/keyring v1.2.2/go.mod h1:wes/FrByc8j7lFOAGLGSNEg8f/PaI3cgTBqhFkHUrPk= github.com/99designs/keyring v1.2.2/go.mod h1:wes/FrByc8j7lFOAGLGSNEg8f/PaI3cgTBqhFkHUrPk=
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
github.com/danieljoos/wincred v1.2.3 h1:v7dZC2x32Ut3nEfRH+vhoZGvN72+dQ/snVXo/vMFLdQ= github.com/danieljoos/wincred v1.2.2 h1:774zMFJrqaeYCK2W57BgAem/MLi6mtSE47MB6BOJ0i0=
github.com/danieljoos/wincred v1.2.3/go.mod h1:6qqX0WNrS4RzPZ1tnroDzq9kY3fu1KwE7MRLQK4X0bs= github.com/danieljoos/wincred v1.2.2/go.mod h1:w7w4Utbrz8lqeMbDAK0lkNJUv5sAOkFi7nd/ogr0Uh8=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dvsekhvalnov/jose2go v1.8.0 h1:LqkkVKAlHFfH9LOEl5fe4p/zL02OhWE7pCufMBG2jLA= github.com/dvsekhvalnov/jose2go v1.8.0 h1:LqkkVKAlHFfH9LOEl5fe4p/zL02OhWE7pCufMBG2jLA=
@@ -16,23 +14,22 @@ github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0=
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU=
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0=
github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 h1:njuLRcjAuMKr7kI3D85AXWkw6/+v9PwtV6M6o11sWHQ= github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs= github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/labstack/echo/v4 v4.15.2 h1:nnh2sCzGCVYnU+wCisMPiYapEg/QVo/gcI9ePKg5/T4= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/labstack/echo/v4 v4.15.2/go.mod h1:Xzp1Ns1RA2c9fY7nSgUJkpkUZGNbEIVHZbtbOMPktBI= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labstack/gommon v0.5.0 h1:6VSQ2NOzsnEJ5W6+84E0RbcaDDmgB6NIAzWCczTEe6c= github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY=
github.com/labstack/gommon v0.5.0/go.mod h1:Rzlg7HHy1maLfzBYGg9NZcVuz1sA68HHhLjhcEllYE0= github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g=
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc= github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc=
github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA= github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA=
github.com/leaanthony/go-ansi-parser v1.6.1 h1:xd8bzARK3dErqkPFtoF9F3/HgN8UQk0ed1YDKpEz01A= github.com/leaanthony/go-ansi-parser v1.6.1 h1:xd8bzARK3dErqkPFtoF9F3/HgN8UQk0ed1YDKpEz01A=
@@ -48,8 +45,8 @@ github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs=
github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
@@ -63,16 +60,17 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/samber/lo v1.53.0 h1:t975lj2py4kJPQ6haz1QMgtId2gtmfktACxIXArw3HM= github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
github.com/samber/lo v1.53.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0= github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tidwall/gjson v1.19.0 h1:xwxm7n691Uf3u5OFjzngavjGTh55KX5q/9w9xHW88JU= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.19.0/go.mod h1:V37/opeE/JbLUOfH0QTXiNez2l0RUjYUhpT4szFQAfc= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tkrajina/go-reflector v0.5.8 h1:yPADHrwmUbMq4RGEyaOUpz2H90sRsETNVpjzo3DLVQQ= github.com/tkrajina/go-reflector v0.5.8 h1:yPADHrwmUbMq4RGEyaOUpz2H90sRsETNVpjzo3DLVQQ=
@@ -81,29 +79,30 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/wailsapp/go-webview2 v1.0.23 h1:jmv8qhz1lHibCc79bMM/a/FqOnnzOGEisLav+a0b9P0= github.com/wailsapp/go-webview2 v1.0.19 h1:7U3QcDj1PrBPaxJNCui2k1SkWml+Q5kvFUFyTImA6NU=
github.com/wailsapp/go-webview2 v1.0.23/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc= github.com/wailsapp/go-webview2 v1.0.19/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc=
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs= github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o= github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
github.com/wailsapp/wails/v2 v2.12.0 h1:BHO/kLNWFHYjCzucxbzAYZWUjub1Tvb4cSguQozHn5c= github.com/wailsapp/wails/v2 v2.10.1 h1:QWHvWMXII2nI/nXz77gpPG8P3ehl6zKe+u4su5BWIns=
github.com/wailsapp/wails/v2 v2.12.0/go.mod h1:mo1bzK1DEJrobt7YrBjgxvb5Sihb1mhAY09hppbibQg= github.com/wailsapp/wails/v2 v2.10.1/go.mod h1:zrebnFV6MQf9kx8HI4iAv63vsR5v67oS7GTEZ7Pz1TY=
golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+2 -2
View File
@@ -8,10 +8,10 @@
"frontend:dev:serverUrl": "auto", "frontend:dev:serverUrl": "auto",
"author": { "author": {
"name": "John O'Keefe", "name": "John O'Keefe",
"email": "admin@linuxhg.com" "email": "jokeefe@fastmail.com"
}, },
"info": { "info": {
"productName": "AniTrack", "productName": "AniTrack",
"productVersion": "1.5.5" "productVersion": "0.5.1"
} }
} }