Compare commits
71 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ca8c8beaf3 | |||
| 3e7f7d1c95 | |||
| b0ca864dfe | |||
| 5ed6dedeab | |||
| 3271af445a | |||
| e7e9e5b826 | |||
| 5337758dee | |||
| 426793e56a | |||
| a794b77654 | |||
| c510c2a138 | |||
| 8cbf5cb20c | |||
| 54a8924384 | |||
| d70153064f | |||
| 7960f8e26d | |||
| 2e5a4a4493 | |||
| 874b3952ee | |||
| 60eac10545 | |||
| 6d66d711ff | |||
| cd62e6c658 | |||
| 063c5016f3 | |||
| 5c4caf68e6 | |||
| c10e853564 | |||
| cd043d093f | |||
| 6db01f7f9f | |||
| 8460d56d55 | |||
| 8fedbe4607 | |||
| e069c47242 | |||
| aba0f2d1d5 | |||
| af6cb7f08a | |||
| 487e5ee5a8 | |||
| 631bd8b885 | |||
| b35be6926a | |||
| 5a9f4391dc | |||
| 72004c98b4 | |||
| 3db25bc33a | |||
| f3a1536953 | |||
| dc01aa314c | |||
| baed9a4a67 | |||
| 0e00120778 | |||
| 697692c277 | |||
| 3e82677c2c | |||
| b81549e5a6 | |||
| fd806df0a9 | |||
| 26f85dd412 | |||
| 4d9c54a116 | |||
| 068e568ec6 | |||
| 4e6f910e74 | |||
| b39f19f03a | |||
| 1a083deb54 | |||
| 61f8f5dd84 | |||
| 4509e479bc | |||
| ee8dd2e866 | |||
| 0c6a8a40c3 | |||
| 45845c2a69 | |||
| 3ec5eb1a03 | |||
| a2aa90edec | |||
| d08283bd2d | |||
| 73d349ee1a | |||
| c9c6650829 | |||
| 896c6640e2 | |||
| 18c744c1cf | |||
| 0475d39c6c | |||
| dde5d20537 | |||
| 314646e7f5 | |||
| 72dfbf4a03 | |||
| d4ad4bc430 | |||
| 49681f3ffb | |||
| d98d0e77c1 | |||
| 8e57b4b259 | |||
| 1e1c891173 | |||
| 5ee9c42352 |
8
.gitignore
vendored
8
.gitignore
vendored
@@ -29,4 +29,10 @@ package.json.md5
|
||||
package-lock.json
|
||||
.idea
|
||||
.env
|
||||
environment.go
|
||||
environment.go
|
||||
|
||||
# REST (http files)
|
||||
http-client.private.env.json
|
||||
|
||||
# Build artifacts
|
||||
build/*.tar.gz
|
||||
|
||||
@@ -17,26 +17,29 @@ func AniListQuery(body interface{}, login bool) (json.RawMessage, string) {
|
||||
if login && (AniListJWT{}) != aniListJwt {
|
||||
response.Header.Add("Authorization", "Bearer "+aniListJwt.AccessToken)
|
||||
} else if login {
|
||||
return nil, "Please login to anilist to make this request"
|
||||
return nil, "Please login to AniList to make this request"
|
||||
}
|
||||
response.Header.Add("Content-Type", "application/json")
|
||||
response.Header.Add("Accept", "application/json")
|
||||
|
||||
client := &http.Client{}
|
||||
res, reserr := client.Do(response)
|
||||
if reserr != nil {
|
||||
res, resErr := client.Do(response)
|
||||
if resErr != nil {
|
||||
log.Printf("Failed at res, %s\n", err)
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
returnedBody, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, "Could not read the returned body."
|
||||
}
|
||||
|
||||
return returnedBody, res.Status
|
||||
}
|
||||
|
||||
func (a *App) GetAniListItem(aniId int, login bool) AniListGetSingleAnime {
|
||||
var user = a.GetAniListLoggedInUser()
|
||||
user := a.GetAniListLoggedInUser()
|
||||
|
||||
var neededVariables interface{}
|
||||
|
||||
@@ -90,6 +93,7 @@ func (a *App) GetAniListItem(aniId int, login bool) AniListGetSingleAnime {
|
||||
timeUntilAiring
|
||||
episode
|
||||
}
|
||||
genres
|
||||
tags{
|
||||
id
|
||||
name
|
||||
@@ -141,11 +145,11 @@ func (a *App) GetAniListItem(aniId int, login bool) AniListGetSingleAnime {
|
||||
returnedBody, status := AniListQuery(body, login)
|
||||
var post AniListGetSingleAnime
|
||||
|
||||
if status == "404 Not Found" && login == false {
|
||||
if status == "404 Not Found" && !login {
|
||||
return post
|
||||
}
|
||||
|
||||
if status == "404 Not Found" && login {
|
||||
if status == "404 Not Found" {
|
||||
post = a.GetAniListItem(aniId, false)
|
||||
}
|
||||
|
||||
@@ -154,7 +158,7 @@ func (a *App) GetAniListItem(aniId int, login bool) AniListGetSingleAnime {
|
||||
log.Printf("Failed at unmarshal, %s\n", err)
|
||||
}
|
||||
|
||||
if login == false {
|
||||
if !login {
|
||||
post.Data.MediaList.UserID = user.Data.Viewer.ID
|
||||
post.Data.MediaList.Status = ""
|
||||
post.Data.MediaList.StartedAt.Year = 0
|
||||
@@ -199,7 +203,7 @@ func (a *App) AniListSearch(query string) any {
|
||||
perPage
|
||||
}
|
||||
media (search: $search, type: $listType) {
|
||||
id
|
||||
id
|
||||
idMal
|
||||
title {
|
||||
romaji
|
||||
@@ -219,6 +223,7 @@ func (a *App) AniListSearch(query string) any {
|
||||
timeUntilAiring
|
||||
episode
|
||||
}
|
||||
genres
|
||||
tags{
|
||||
id
|
||||
name
|
||||
@@ -249,7 +254,7 @@ func (a *App) AniListSearch(query string) any {
|
||||
}
|
||||
|
||||
func (a *App) GetAniListUserWatchingList(page int, perPage int, sort string) AniListCurrentUserWatchList {
|
||||
var user = a.GetAniListLoggedInUser()
|
||||
user := a.GetAniListLoggedInUser()
|
||||
type Variables struct {
|
||||
Page int `json:"page"`
|
||||
PerPage int `json:"perPage"`
|
||||
@@ -304,6 +309,7 @@ func (a *App) GetAniListUserWatchingList(page int, perPage int, sort string) Ani
|
||||
timeUntilAiring
|
||||
episode
|
||||
}
|
||||
genres
|
||||
tags{
|
||||
id
|
||||
name
|
||||
@@ -412,8 +418,8 @@ func (a *App) AniListUpdateEntry(updateBody AniListUpdateVariables) AniListGetSi
|
||||
}{
|
||||
Query: `
|
||||
mutation(
|
||||
$mediaId:Int,
|
||||
$progress:Int,
|
||||
$mediaId:Int,
|
||||
$progress:Int,
|
||||
$status:MediaListStatus,
|
||||
$score:Float,
|
||||
$repeat:Int,
|
||||
@@ -422,15 +428,15 @@ func (a *App) AniListUpdateEntry(updateBody AniListUpdateVariables) AniListGetSi
|
||||
$completedAt:FuzzyDateInput,
|
||||
){
|
||||
SaveMediaListEntry(
|
||||
mediaId:$mediaId,
|
||||
progress:$progress,
|
||||
mediaId:$mediaId,
|
||||
progress:$progress,
|
||||
status:$status,
|
||||
score:$score,
|
||||
repeat:$repeat,
|
||||
notes:$notes,
|
||||
startedAt:$startedAt
|
||||
completedAt:$completedAt
|
||||
){
|
||||
){
|
||||
id
|
||||
mediaId
|
||||
userId
|
||||
@@ -525,7 +531,7 @@ func (a *App) AniListDeleteEntry(mediaListId int) DeleteAniListReturn {
|
||||
){
|
||||
DeleteMediaListEntry(
|
||||
id:$id,
|
||||
){
|
||||
){
|
||||
deleted
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,8 @@ type MediaList struct {
|
||||
TimeUntilAiring int `json:"timeUntilAiring"`
|
||||
Episode int `json:"episode"`
|
||||
} `json:"nextAiringEpisode"`
|
||||
Tags []struct {
|
||||
Genres []string `json:"genres"`
|
||||
Tags []struct {
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
|
||||
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
@@ -30,11 +31,11 @@ var aniCtxShutdown, aniCancel = context.WithCancel(context.Background())
|
||||
|
||||
func (a *App) CheckIfAniListLoggedIn() bool {
|
||||
if (AniListJWT{} == aniListJwt) {
|
||||
tokenType, err := aniRing.Get("anilistTokenType")
|
||||
expiresIn, err := aniRing.Get("anilistTokenExpiresIn")
|
||||
accessToken, err := aniRing.Get("anilistAccessToken")
|
||||
refreshToken, err := aniRing.Get("anilistRefreshToken")
|
||||
if err != nil || len(accessToken.Data) == 0 {
|
||||
tokenType, tokenErr := aniRing.Get("anilistTokenType")
|
||||
expiresIn, expiresInErr := aniRing.Get("anilistTokenExpiresIn")
|
||||
refreshToken, refreshTokenErr := aniRing.Get("anilistRefreshToken")
|
||||
accessToken, accessTokenErr := aniRing.Get("anilistAccessToken")
|
||||
if (tokenErr != nil || expiresInErr != nil || refreshTokenErr != nil || accessTokenErr != nil) || len(accessToken.Data) == 0 {
|
||||
return false
|
||||
} else {
|
||||
aniListJwt.TokenType = string(tokenType.Data)
|
||||
@@ -50,11 +51,11 @@ func (a *App) CheckIfAniListLoggedIn() bool {
|
||||
|
||||
func (a *App) AniListLogin() {
|
||||
if (AniListJWT{} == aniListJwt) {
|
||||
tokenType, err := aniRing.Get("anilistTokenType")
|
||||
expiresIn, err := aniRing.Get("anilistTokenExpiresIn")
|
||||
accessToken, err := aniRing.Get("anilistAccessToken")
|
||||
refreshToken, err := aniRing.Get("anilistRefreshToken")
|
||||
if err != nil || len(accessToken.Data) == 0 {
|
||||
tokenType, tokenErr := aniRing.Get("anilistTokenType")
|
||||
expiresIn, expiresInErr := aniRing.Get("anilistTokenExpiresIn")
|
||||
refreshToken, refreshTokenErr := aniRing.Get("anilistRefreshToken")
|
||||
accessToken, accessTokenErr := aniRing.Get("anilistAccessToken")
|
||||
if (tokenErr != nil || expiresInErr != nil || refreshTokenErr != nil || accessTokenErr != nil) || len(accessToken.Data) == 0 {
|
||||
getAniListCodeUrl := "https://anilist.co/api/v2/oauth/authorize?client_id=" + Environment.ANILIST_APP_ID + "&redirect_uri=" + Environment.ANILIST_CALLBACK_URI + "&response_type=code"
|
||||
runtime.BrowserOpenURL(*wailsContext, getAniListCodeUrl)
|
||||
|
||||
@@ -123,7 +124,7 @@ func (a *App) handleAniListCallback(wg *sync.WaitGroup) {
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
log.Fatalf("listen: %s\n", err)
|
||||
}
|
||||
fmt.Println("Shutting down...")
|
||||
@@ -152,14 +153,17 @@ func getAniListAuthorizationToken(content string) AniListJWT {
|
||||
response.Header.Add("Accept", "application/json")
|
||||
|
||||
client := &http.Client{}
|
||||
res, reserr := client.Do(response)
|
||||
if reserr != nil {
|
||||
res, resErr := client.Do(response)
|
||||
if resErr != nil {
|
||||
log.Printf("Failed at res, %s\n", err)
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
returnedBody, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
log.Printf("Could not read returned body, %s\n.", err)
|
||||
}
|
||||
|
||||
var post AniListJWT
|
||||
err = json.Unmarshal(returnedBody, &post)
|
||||
@@ -204,13 +208,12 @@ func (a *App) GetAniListLoggedInUser() AniListUser {
|
||||
|
||||
func (a *App) LogoutAniList() string {
|
||||
if (AniListJWT{} != aniListJwt) {
|
||||
err := aniRing.Remove("anilistTokenType")
|
||||
err = aniRing.Remove("anilistTokenExpiresIn")
|
||||
err = aniRing.Remove("anilistAccessToken")
|
||||
err = aniRing.Remove("anilistRefreshToken")
|
||||
|
||||
if err != nil {
|
||||
fmt.Println("AniList Logout Failed", err)
|
||||
typeErr := aniRing.Remove("anilistTokenType")
|
||||
expiresInErr := aniRing.Remove("anilistTokenExpiresIn")
|
||||
accessTokenErr := aniRing.Remove("anilistAccessToken")
|
||||
refreshTokenErr := aniRing.Remove("anilistRefreshToken")
|
||||
if typeErr != nil || expiresInErr != nil || accessTokenErr != nil || refreshTokenErr != nil {
|
||||
fmt.Println("AniList Logout Failed")
|
||||
}
|
||||
aniListJwt = AniListJWT{}
|
||||
}
|
||||
|
||||
16
MALTypes.go
16
MALTypes.go
@@ -46,10 +46,10 @@ type MALWatchlist struct {
|
||||
Id int `json:"id" ts_type:"id"`
|
||||
Title string `json:"title" ts_type:"title"`
|
||||
MainPicture struct {
|
||||
Medium string `json:"medium" json:"medium"`
|
||||
Large string `json:"large" json:"large"`
|
||||
} `json:"main_picture" json:"mainPicture"`
|
||||
} `json:"node" json:"node"`
|
||||
Medium string `json:"medium" ts_type:"medium"`
|
||||
Large string `json:"large" ts_type:"large"`
|
||||
} `json:"main_picture" ts_type:"mainPicture"`
|
||||
} `json:"node" ts_type:"node"`
|
||||
ListStatus struct {
|
||||
Status string `json:"status" ts_type:"status"`
|
||||
Score int `json:"score" ts_type:"score"`
|
||||
@@ -59,7 +59,7 @@ type MALWatchlist struct {
|
||||
StartDate string `json:"start_date" ts_type:"startDate"`
|
||||
FinishDate string `json:"finish_date" ts_type:"finishDate"`
|
||||
} `json:"list_status" ts_type:"listStatus"`
|
||||
} `json:"data" json:"data"`
|
||||
} `json:"data" ts_type:"data"`
|
||||
Paging struct {
|
||||
Previous string `json:"previous" ts_type:"previous"`
|
||||
Next string `json:"next" ts_type:"next"`
|
||||
@@ -70,9 +70,9 @@ type MALAnime struct {
|
||||
Id int `json:"id" ts_type:"id"`
|
||||
Title string `json:"title" ts_type:"title"`
|
||||
MainPicture struct {
|
||||
Large string `json:"large" json:"large"`
|
||||
Medium string `json:"medium" json:"medium"`
|
||||
} `json:"main_picture" json:"mainPicture"`
|
||||
Large string `json:"large" ts_type:"large"`
|
||||
Medium string `json:"medium" ts_type:"medium"`
|
||||
} `json:"main_picture" ts_type:"mainPicture"`
|
||||
AlternativeTitles struct {
|
||||
Synonyms []string `json:"synonyms" ts_type:"synonyms"`
|
||||
En string `json:"en" ts_type:"en"`
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
@@ -50,7 +51,7 @@ func base64URLEncode(str []byte) string {
|
||||
|
||||
func verifier() (*CodeVerifier, error) {
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
b := make([]byte, length, length)
|
||||
b := make([]byte, length)
|
||||
for i := 0; i < length; i++ {
|
||||
b[i] = byte(r.Intn(255))
|
||||
}
|
||||
@@ -71,16 +72,17 @@ func (v *CodeVerifier) CodeChallengeS256() string {
|
||||
|
||||
func (a *App) CheckIfMyAnimeListLoggedIn() bool {
|
||||
if (MyAnimeListJWT{} == myAnimeListJwt) {
|
||||
tokenType, err := myAnimeListRing.Get("MyAnimeListTokenType")
|
||||
expiresIn, err := myAnimeListRing.Get("MyAnimeListExpiresIn")
|
||||
accessToken, err := myAnimeListRing.Get("MyAnimeListAccessToken")
|
||||
refreshToken, err := myAnimeListRing.Get("MyAnimeListRefreshToken")
|
||||
if err != nil || len(accessToken.Data) == 0 {
|
||||
tokenType, tokenErr := myAnimeListRing.Get("MyAnimeListTokenType")
|
||||
expiresIn, expiresInErr := myAnimeListRing.Get("MyAnimeListExpiresIn")
|
||||
refreshToken, refreshTokenErr := myAnimeListRing.Get("MyAnimeListAccessToken")
|
||||
accessToken, accessTokenErr := myAnimeListRing.Get("MyAnimeListRefreshToken")
|
||||
if (tokenErr != nil || expiresInErr != nil || refreshTokenErr != nil || accessTokenErr != nil) || len(accessToken.Data) == 0 {
|
||||
return false
|
||||
} else {
|
||||
var expiresInConvertErr error
|
||||
myAnimeListJwt.TokenType = string(tokenType.Data)
|
||||
myAnimeListJwt.ExpiresIn, err = strconv.Atoi(string(expiresIn.Data))
|
||||
if err != nil {
|
||||
myAnimeListJwt.ExpiresIn, expiresInConvertErr = strconv.Atoi(string(expiresIn.Data))
|
||||
if expiresInConvertErr != nil {
|
||||
fmt.Println("unable to convert string to int")
|
||||
}
|
||||
myAnimeListJwt.AccessToken = string(accessToken.Data)
|
||||
@@ -93,12 +95,13 @@ func (a *App) CheckIfMyAnimeListLoggedIn() bool {
|
||||
}
|
||||
|
||||
func (a *App) MyAnimeListLogin() {
|
||||
if a.CheckIfMyAnimeListLoggedIn() == false {
|
||||
tokenType, err := myAnimeListRing.Get("MyAnimeListTokenType")
|
||||
expiresIn, err := myAnimeListRing.Get("MyAnimeListExpiresIn")
|
||||
accessToken, err := myAnimeListRing.Get("MyAnimeListAccessToken")
|
||||
refreshToken, err := myAnimeListRing.Get("MyAnimeListRefreshToken")
|
||||
if err != nil || len(accessToken.Data) == 0 {
|
||||
if !a.CheckIfMyAnimeListLoggedIn() {
|
||||
fmt.Println("check logged in function failed")
|
||||
tokenType, tokenErr := myAnimeListRing.Get("MyAnimeListTokenType")
|
||||
expiresIn, expiresInErr := myAnimeListRing.Get("MyAnimeListExpiresIn")
|
||||
refreshToken, refreshTokenErr := myAnimeListRing.Get("MyAnimeListAccessToken")
|
||||
accessToken, accessTokenErr := myAnimeListRing.Get("MyAnimeListRefreshToken")
|
||||
if (tokenErr != nil || expiresInErr != nil || refreshTokenErr != nil || accessTokenErr != nil) || len(accessToken.Data) == 0 {
|
||||
verifier, _ := verifier()
|
||||
getMyAnimeListCodeUrl := "https://myanimelist.net/v1/oauth2/authorize?response_type=code&client_id=" + Environment.MAL_CLIENT_ID + "&redirect_uri=" + Environment.MAL_CALLBACK_URI + "&code_challenge=" + verifier.Value + "&code_challenge_method=plain"
|
||||
runtime.BrowserOpenURL(*wailsContext, getMyAnimeListCodeUrl)
|
||||
@@ -107,9 +110,10 @@ func (a *App) MyAnimeListLogin() {
|
||||
a.handleMyAnimeListCallback(serverDone, verifier)
|
||||
serverDone.Wait()
|
||||
} else {
|
||||
var expiresInConvertErr error
|
||||
myAnimeListJwt.TokenType = string(tokenType.Data)
|
||||
myAnimeListJwt.ExpiresIn, err = strconv.Atoi(string(expiresIn.Data))
|
||||
if err != nil {
|
||||
myAnimeListJwt.ExpiresIn, expiresInConvertErr = strconv.Atoi(string(expiresIn.Data))
|
||||
if expiresInConvertErr != nil {
|
||||
fmt.Println("unable to convert string to int in Login function")
|
||||
}
|
||||
myAnimeListJwt.AccessToken = string(accessToken.Data)
|
||||
@@ -171,7 +175,7 @@ func (a *App) handleMyAnimeListCallback(wg *sync.WaitGroup, verifier *CodeVerifi
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
log.Fatalf("listen: %s\n", err)
|
||||
}
|
||||
fmt.Println("Shutting down...")
|
||||
@@ -210,14 +214,17 @@ func getMyAnimeListAuthorizationToken(content string, verifier *CodeVerifier) My
|
||||
response.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
client := &http.Client{}
|
||||
res, reserr := client.Do(response)
|
||||
if reserr != nil {
|
||||
res, resErr := client.Do(response)
|
||||
if resErr != nil {
|
||||
log.Printf("Failed at res, %s\n", err)
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
returnedBody, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
log.Printf("Could not read returned body, %s\n", err)
|
||||
}
|
||||
|
||||
var post MyAnimeListJWT
|
||||
err = json.Unmarshal(returnedBody, &post)
|
||||
@@ -257,14 +264,17 @@ func refreshMyAnimeListAuthorizationToken() {
|
||||
response.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
client := &http.Client{}
|
||||
res, reserr := client.Do(response)
|
||||
if reserr != nil {
|
||||
res, resErr := client.Do(response)
|
||||
if resErr != nil {
|
||||
log.Printf("Failed at res, %s\n", err)
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
returnedBody, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
log.Printf("Could not read returned body, %s\n", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(returnedBody, &myAnimeListJwt)
|
||||
if err != nil {
|
||||
@@ -291,17 +301,23 @@ func refreshMyAnimeListAuthorizationToken() {
|
||||
Title: "MyAnimeList Authorization",
|
||||
Message: "It is now safe to close your browser tab",
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (a *App) GetMyAnimeListLoggedInUser() MyAnimeListUser {
|
||||
a.MyAnimeListLogin()
|
||||
user := createUser()
|
||||
if user.Name == "" {
|
||||
refreshMyAnimeListAuthorizationToken()
|
||||
user = createUser()
|
||||
}
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
func createUser() MyAnimeListUser {
|
||||
client := &http.Client{}
|
||||
|
||||
req, _ := http.NewRequest("GET", "https://api.myanimelist.net/v2/users/@me?fields=anime_statistics", nil)
|
||||
@@ -311,7 +327,6 @@ func (a *App) GetMyAnimeListLoggedInUser() MyAnimeListUser {
|
||||
req.Header.Add("myAnimeList-api-key", Environment.MAL_CLIENT_ID)
|
||||
|
||||
response, err := client.Do(req)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Failed at request, %s\n", err)
|
||||
return MyAnimeListUser{}
|
||||
@@ -329,17 +344,17 @@ func (a *App) GetMyAnimeListLoggedInUser() MyAnimeListUser {
|
||||
}
|
||||
|
||||
return user
|
||||
|
||||
}
|
||||
|
||||
func (a *App) LogoutMyAnimeList() string {
|
||||
if (MyAnimeListJWT{} != myAnimeListJwt) {
|
||||
err := myAnimeListRing.Remove("MyAnimeListTokenType")
|
||||
err = myAnimeListRing.Remove("MyAnimeListExpiresIn")
|
||||
err = myAnimeListRing.Remove("MyAnimeListAccessToken")
|
||||
err = myAnimeListRing.Remove("MyAnimeListRefreshToken")
|
||||
|
||||
if err != nil {
|
||||
fmt.Println("MAL Logout Failed", err)
|
||||
typeErr := myAnimeListRing.Remove("MyAnimeListTokenType")
|
||||
expiresInErr := myAnimeListRing.Remove("MyAnimeListExpiresIn")
|
||||
accessTokenErr := myAnimeListRing.Remove("MyAnimeListAccessToken")
|
||||
refreshTokenErr := myAnimeListRing.Remove("MyAnimeListRefreshToken")
|
||||
if typeErr != nil || expiresInErr != nil || accessTokenErr != nil || refreshTokenErr != nil {
|
||||
fmt.Println("MAL Logout Failed")
|
||||
}
|
||||
myAnimeListJwt = MyAnimeListJWT{}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ func SimklHelper(method string, url string, body interface{}) json.RawMessage {
|
||||
req.Header.Add("simkl-api-key", Environment.SIMKL_CLIENT_ID)
|
||||
|
||||
resp, err := client.Do(req)
|
||||
|
||||
if err != nil {
|
||||
fmt.Println("Errored when sending request to the server")
|
||||
message, _ := json.Marshal(struct {
|
||||
@@ -47,7 +46,6 @@ func SimklHelper(method string, url string, body interface{}) json.RawMessage {
|
||||
respBody, _ := io.ReadAll(resp.Body)
|
||||
|
||||
return respBody
|
||||
|
||||
}
|
||||
|
||||
func (a *App) SimklGetUserWatchlist() SimklWatchListType {
|
||||
@@ -62,7 +60,6 @@ func (a *App) SimklGetUserWatchlist() SimklWatchListType {
|
||||
}
|
||||
|
||||
err := json.Unmarshal(respBody, &errCheck)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Failed at unmarshal, %s\n", err)
|
||||
}
|
||||
@@ -85,7 +82,6 @@ func (a *App) SimklGetUserWatchlist() SimklWatchListType {
|
||||
}
|
||||
|
||||
func (a *App) SimklSyncEpisodes(anime SimklAnime, progress int) SimklAnime {
|
||||
|
||||
var episodes []Episode
|
||||
var url string
|
||||
var shows []SimklPostShow
|
||||
@@ -140,7 +136,7 @@ func (a *App) SimklSyncEpisodes(anime SimklAnime, progress int) SimklAnime {
|
||||
|
||||
func (a *App) SimklSyncRating(anime SimklAnime, rating int) SimklAnime {
|
||||
var url string
|
||||
var showWithRating = ShowWithRating{
|
||||
showWithRating := ShowWithRating{
|
||||
Title: anime.Show.Title,
|
||||
Ids: Ids{
|
||||
Simkl: anime.Show.Ids.Simkl,
|
||||
@@ -150,7 +146,7 @@ func (a *App) SimklSyncRating(anime SimklAnime, rating int) SimklAnime {
|
||||
Rating: rating,
|
||||
}
|
||||
|
||||
var showWithoutRating = ShowWithoutRating{
|
||||
showWithoutRating := ShowWithoutRating{
|
||||
Title: anime.Show.Title,
|
||||
Ids: Ids{
|
||||
Simkl: anime.Show.Ids.Simkl,
|
||||
@@ -197,7 +193,7 @@ func (a *App) SimklSyncRating(anime SimklAnime, rating int) SimklAnime {
|
||||
|
||||
func (a *App) SimklSyncStatus(anime SimklAnime, status string) SimklAnime {
|
||||
url := "https://api.simkl.com/sync/add-to-list"
|
||||
var show = SimklShowStatus{
|
||||
show := SimklShowStatus{
|
||||
Title: anime.Show.Title,
|
||||
Ids: Ids{
|
||||
Simkl: anime.Show.Ids.Simkl,
|
||||
@@ -266,7 +262,6 @@ func (a *App) SimklSearch(aniListAnime MediaList) SimklAnime {
|
||||
if len(anime) == 0 {
|
||||
url = "https://api.simkl.com/search/id?mal=" + strconv.Itoa(aniListAnime.Media.IDMal)
|
||||
respBody = SimklHelper("GET", url, nil)
|
||||
fmt.Println(string(respBody))
|
||||
err = json.Unmarshal(respBody, &anime)
|
||||
}
|
||||
|
||||
@@ -300,7 +295,7 @@ func (a *App) SimklSyncRemove(anime SimklAnime) bool {
|
||||
url := "https://api.simkl.com/sync/history/remove"
|
||||
var showArray []SimklShowStatus
|
||||
|
||||
var singleShow = SimklShowStatus{
|
||||
singleShow := SimklShowStatus{
|
||||
Title: anime.Show.Title,
|
||||
Ids: Ids{
|
||||
Simkl: anime.Show.Ids.Simkl,
|
||||
@@ -349,5 +344,4 @@ func WatchListUpdate(anime SimklAnime) {
|
||||
if !updated {
|
||||
SimklWatchList.Anime = append(SimklWatchList.Anime, anime)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
@@ -28,10 +29,10 @@ var simklCtxShutdown, simklCancel = context.WithCancel(context.Background())
|
||||
|
||||
func (a *App) CheckIfSimklLoggedIn() bool {
|
||||
if (SimklJWT{} == simklJwt) {
|
||||
tokenType, err := simklRing.Get("SimklTokenType")
|
||||
accessToken, err := simklRing.Get("SimklAccessToken")
|
||||
scope, err := simklRing.Get("SimklScope")
|
||||
if err != nil || len(accessToken.Data) == 0 {
|
||||
tokenType, tokenTypeErr := simklRing.Get("SimklTokenType")
|
||||
accessToken, accessTokenErr := simklRing.Get("SimklAccessToken")
|
||||
scope, scopeErr := simklRing.Get("SimklScope")
|
||||
if (tokenTypeErr != nil || accessTokenErr != nil || scopeErr != nil) || len(accessToken.Data) == 0 {
|
||||
return false
|
||||
} else {
|
||||
simklJwt.TokenType = string(tokenType.Data)
|
||||
@@ -45,11 +46,11 @@ func (a *App) CheckIfSimklLoggedIn() bool {
|
||||
}
|
||||
|
||||
func (a *App) SimklLogin() {
|
||||
if a.CheckIfSimklLoggedIn() == false {
|
||||
tokenType, err := simklRing.Get("SimklTokenType")
|
||||
accessToken, err := simklRing.Get("SimklAccessToken")
|
||||
scope, err := simklRing.Get("SimklScope")
|
||||
if err != nil || len(accessToken.Data) == 0 {
|
||||
if !a.CheckIfSimklLoggedIn() {
|
||||
tokenType, tokenTypeErr := simklRing.Get("SimklTokenType")
|
||||
accessToken, accessTokenErr := simklRing.Get("SimklAccessToken")
|
||||
scope, scopeErr := simklRing.Get("SimklScope")
|
||||
if (tokenTypeErr != nil || accessTokenErr != nil || scopeErr != nil) || len(accessToken.Data) == 0 {
|
||||
getSimklCodeUrl := "https://simkl.com/oauth/authorize?response_type=code&client_id=" + Environment.SIMKL_CLIENT_ID + "&redirect_uri=" + Environment.SIMKL_CALLBACK_URI
|
||||
runtime.BrowserOpenURL(*wailsContext, getSimklCodeUrl)
|
||||
|
||||
@@ -114,7 +115,7 @@ func (a *App) handleSimklCallback(wg *sync.WaitGroup) {
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
log.Fatalf("listen: %s\n", err)
|
||||
}
|
||||
fmt.Println("Shutting down...")
|
||||
@@ -147,14 +148,17 @@ func getSimklAuthorizationToken(content string) SimklJWT {
|
||||
response.Header.Add("Content-Type", "application/json")
|
||||
|
||||
client := &http.Client{}
|
||||
res, reserr := client.Do(response)
|
||||
if reserr != nil {
|
||||
res, resErr := client.Do(response)
|
||||
if resErr != nil {
|
||||
log.Printf("Failed at res, %s\n", err)
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
returnedBody, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
log.Printf("Could not read returned body, %s\n.", err)
|
||||
}
|
||||
|
||||
var post SimklJWT
|
||||
err = json.Unmarshal(returnedBody, &post)
|
||||
@@ -177,7 +181,6 @@ func (a *App) GetSimklLoggedInUser() SimklUser {
|
||||
req.Header.Add("simkl-api-key", Environment.SIMKL_CLIENT_ID)
|
||||
|
||||
response, err := client.Do(req)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Failed at request, %s\n", err)
|
||||
return SimklUser{}
|
||||
@@ -193,7 +196,6 @@ func (a *App) GetSimklLoggedInUser() SimklUser {
|
||||
}
|
||||
|
||||
err = json.Unmarshal(respBody, &errCheck)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Failed at unmarshal, %s\n", err)
|
||||
}
|
||||
@@ -215,12 +217,12 @@ func (a *App) GetSimklLoggedInUser() SimklUser {
|
||||
|
||||
func (a *App) LogoutSimkl() string {
|
||||
if (SimklJWT{} != simklJwt) {
|
||||
err := simklRing.Remove("SimklTokenType")
|
||||
err = simklRing.Remove("SimklAccessToken")
|
||||
err = simklRing.Remove("SimklScope")
|
||||
tokenTypeErr := simklRing.Remove("SimklTokenType")
|
||||
accessTokenErr := simklRing.Remove("SimklAccessToken")
|
||||
scopeErr := simklRing.Remove("SimklScope")
|
||||
|
||||
if err != nil {
|
||||
fmt.Println("Simkl Logout Failed", err)
|
||||
if tokenTypeErr != nil || accessTokenErr != nil || scopeErr != nil {
|
||||
fmt.Println("Simkl Logout Failed")
|
||||
}
|
||||
simklJwt = SimklJWT{}
|
||||
}
|
||||
|
||||
15
app.go
15
app.go
@@ -3,9 +3,11 @@ package main
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
@@ -43,3 +45,14 @@ func (a *App) onSecondInstanceLaunch(secondInstanceData options.SecondInstanceDa
|
||||
runtime.Show(*wailsContext)
|
||||
go runtime.EventsEmit(*wailsContext, "launchArgs", secondInstanceArgs)
|
||||
}
|
||||
|
||||
func (a *App) ShowVersion() {
|
||||
version := gjson.Get(wailsJSON, "info.productVersion")
|
||||
_, err := runtime.MessageDialog(*wailsContext, runtime.MessageDialogOptions{
|
||||
Title: "Version",
|
||||
Message: "AniTrack Version: " + version.String(),
|
||||
})
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ body:graphql {
|
||||
media {
|
||||
id
|
||||
idMal
|
||||
genres
|
||||
tags {
|
||||
id
|
||||
name
|
||||
|
||||
@@ -33,6 +33,7 @@ body:graphql {
|
||||
english
|
||||
native
|
||||
}
|
||||
genre
|
||||
description
|
||||
coverImage {
|
||||
large
|
||||
|
||||
@@ -12,7 +12,7 @@ post {
|
||||
|
||||
headers {
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
}
|
||||
|
||||
body:form-urlencoded {
|
||||
@@ -20,7 +20,7 @@ body:form-urlencoded {
|
||||
client_id: {{ANILIST_APP_ID}}
|
||||
client_secret: {{ANILIST_SECRET_TOKEN}}
|
||||
redirect_uri: http://localhost:6734/callback
|
||||
code: {{code}}
|
||||
code: {{ANILIST_CODE}}
|
||||
}
|
||||
|
||||
body:multipart-form {
|
||||
|
||||
@@ -9,7 +9,7 @@ vars {
|
||||
}
|
||||
vars:secret [
|
||||
ANILIST_ACCESS_TOKEN,
|
||||
code,
|
||||
ANILIST_CODE,
|
||||
SIMKL_AUTH_TOKEN,
|
||||
MAL_CODE,
|
||||
MAL_VERIFIER,
|
||||
|
||||
11
build/AniTrack.desktop
Executable file
11
build/AniTrack.desktop
Executable file
@@ -0,0 +1,11 @@
|
||||
[Desktop Entry]
|
||||
Name=AniTrack
|
||||
Comment=A manual synchronizer for various Anime trackers.
|
||||
Exec=/home/nymusicman/Applications/AniTrack
|
||||
Icon=AniTrack
|
||||
Terminal=false
|
||||
Type=Application
|
||||
StartupNotify=true
|
||||
Categories=Internet
|
||||
Keywords=anitrack;anilist;simkl;mal;myanimelist;anime;sync
|
||||
Path=
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 53 KiB |
BIN
build/icon/128/AniTrack.png
Normal file
BIN
build/icon/128/AniTrack.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.9 KiB |
BIN
build/icon/32/AniTrack.png
Normal file
BIN
build/icon/32/AniTrack.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
BIN
build/icon/48/AniTrack.png
Normal file
BIN
build/icon/48/AniTrack.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
BIN
build/icon/64/AniTrack.png
Normal file
BIN
build/icon/64/AniTrack.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
26
build/install_linux.sh
Executable file
26
build/install_linux.sh
Executable file
@@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
|
||||
# copy desktop file
|
||||
if [ -e "~/.local/share/applications/AniTrack.desktop" ]; then
|
||||
if [ -d "~/.local/share/applications/" ]; then
|
||||
cp ./AniTrack.desktop ~/.local/share/applications/
|
||||
else
|
||||
mkdir -p ~/.local/share/applications/
|
||||
cp ./AniTrack.desktop ~/.local/share/applications/
|
||||
fi
|
||||
fi
|
||||
|
||||
# copy icons to xdg folders
|
||||
for size in 32 48 64 128; do
|
||||
xdg-icon-resource install --novendor --context apps --size $size ./icon/$size/AniTrack.png AniTrack
|
||||
done
|
||||
|
||||
# copy AniTrack Binary to $HOME/Applications/
|
||||
if ! [ -d "~/Applications" ]; then
|
||||
mkdir -p ~/Applications
|
||||
cp ./bin/AniTrack ~/Applications/
|
||||
elif ! [[ -e ~/Applications/AniTrack ]]; then
|
||||
cp ./bin/AniTrack ~/Applications/
|
||||
fi
|
||||
|
||||
echo "AniTrack has been successfully installed."
|
||||
@@ -1,13 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>AniTrack</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="./src/main.ts" type="module"></script>
|
||||
<script src="./node_modules/flowbite/dist/flowbite.js"></script>
|
||||
</body>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
|
||||
<title>AniTrack</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="./src/main.ts" type="module"></script>
|
||||
<script
|
||||
src="./node_modules/flowbite/dist/flowbite.js"
|
||||
type="module"
|
||||
></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"postcss": "^8.4.45",
|
||||
"svelte": "^4.0.0",
|
||||
"svelte-check": "^3.4.3",
|
||||
"svelte-headless-table": "^0.18.2",
|
||||
"svelte-headless-table": "^0.18.3",
|
||||
"svelte-preprocess": "^5.0.3",
|
||||
"svelte-spa-router": "^4.0.1",
|
||||
"tailwind-merge": "^2.5.2",
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<script lang="ts">
|
||||
import {
|
||||
aniListAnime,
|
||||
GetAnimeSingleItem,
|
||||
aniListLoggedIn,
|
||||
malLoggedIn,
|
||||
simklLoggedIn,
|
||||
} from "./helperModules/GlobalVariablesAndHelperFunctions.svelte";
|
||||
import {onMount} from "svelte";
|
||||
import Router from "svelte-spa-router"
|
||||
@@ -13,12 +14,19 @@
|
||||
import { CheckIfMALLoggedInAndSetUser } from "./helperModules/CheckIfMyAnimeListLoggedIn.svelte";
|
||||
import {CheckIfSimklLoggedInAndSetUser} from "./helperModules/CheckIsSimklLoggedIn.svelte"
|
||||
import {CheckIfAniListLoggedIn} from "../wailsjs/go/main/App";
|
||||
import {AniListGetSingleAnimeDefaultData} from "./helperDefaults/AniListGetSingleAnime";
|
||||
|
||||
onMount(async () => {
|
||||
await CheckIfAniListLoggedInAndLoadWatchList()
|
||||
await CheckIfMALLoggedInAndSetUser()
|
||||
await CheckIfSimklLoggedInAndSetUser()
|
||||
let isAniListLoggedIn: boolean
|
||||
let isMALLoggedIn: boolean
|
||||
let isSimklLoggedIn: boolean
|
||||
aniListLoggedIn.subscribe((value) => isAniListLoggedIn = value)
|
||||
malLoggedIn.subscribe((value) => isMALLoggedIn = value)
|
||||
simklLoggedIn.subscribe((value) => isSimklLoggedIn = value)
|
||||
|
||||
console.log(isAniListLoggedIn)
|
||||
!isAniListLoggedIn && await CheckIfAniListLoggedInAndLoadWatchList()
|
||||
!isMALLoggedIn && await CheckIfMALLoggedInAndSetUser()
|
||||
!isSimklLoggedIn && await CheckIfSimklLoggedInAndSetUser()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -27,17 +35,7 @@
|
||||
'/': Home,
|
||||
'/anime/:id': wrap({
|
||||
asyncComponent: () => import('./routes/AnimeRoutePage.svelte'),
|
||||
conditions: [
|
||||
async () => await CheckIfAniListLoggedIn(),
|
||||
async (detail) => {
|
||||
aniListAnime.update(value => {
|
||||
value = AniListGetSingleAnimeDefaultData
|
||||
return value
|
||||
})
|
||||
await GetAnimeSingleItem(Number(detail.params.id), true)
|
||||
return Object.keys($aniListAnime).length!==0
|
||||
},
|
||||
],
|
||||
conditions: [async () => await CheckIfAniListLoggedIn()],
|
||||
loadingComponent: Spinner
|
||||
}),
|
||||
// '*': "Not Found"
|
||||
|
||||
@@ -1,80 +1,94 @@
|
||||
export interface AniListCurrentUserWatchList {
|
||||
data: {
|
||||
Page: {
|
||||
pageInfo: {
|
||||
total: number
|
||||
perPage: number
|
||||
currentPage: number
|
||||
lastPage: number
|
||||
hasNextPage: boolean
|
||||
},
|
||||
mediaList: MediaList[]
|
||||
}
|
||||
}
|
||||
data: {
|
||||
Page: {
|
||||
pageInfo: {
|
||||
total: number;
|
||||
perPage: number;
|
||||
currentPage: number;
|
||||
lastPage: number;
|
||||
hasNextPage: boolean;
|
||||
};
|
||||
mediaList: MediaList[];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface AniListGetSingleAnime {
|
||||
data: {
|
||||
MediaList: MediaList
|
||||
}
|
||||
data: {
|
||||
MediaList: MediaList;
|
||||
};
|
||||
}
|
||||
|
||||
export interface MediaList {
|
||||
id: number
|
||||
mediaId: number
|
||||
userId: number
|
||||
media: {
|
||||
id: number
|
||||
idMal: number
|
||||
title: {
|
||||
romaji: string
|
||||
english?: string
|
||||
native: string
|
||||
}
|
||||
description: string
|
||||
coverImage: {
|
||||
large: string
|
||||
}
|
||||
season: string
|
||||
seasonYear: number
|
||||
status: string
|
||||
episodes?: number
|
||||
nextAiringEpisode?: {
|
||||
airingAt: number
|
||||
timeUntilAiring: number
|
||||
episode: number
|
||||
}
|
||||
}
|
||||
status: string
|
||||
startedAt: {
|
||||
year: number
|
||||
month: number
|
||||
day: number
|
||||
}
|
||||
completedAt: {
|
||||
year?: number
|
||||
month?: number
|
||||
day?: number
|
||||
}
|
||||
notes?: string
|
||||
progress: number
|
||||
score: number
|
||||
repeat: number
|
||||
user: {
|
||||
id: number
|
||||
name: string
|
||||
avatar: {
|
||||
large: string
|
||||
medium: string
|
||||
}
|
||||
statistics: {
|
||||
anime: {
|
||||
count: number
|
||||
statuses: [{
|
||||
status: string
|
||||
count: number
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
id: number;
|
||||
mediaId: number;
|
||||
userId: number;
|
||||
media: {
|
||||
id: number;
|
||||
idMal: number;
|
||||
title: {
|
||||
romaji: string;
|
||||
english?: string;
|
||||
native: string;
|
||||
};
|
||||
description: string;
|
||||
coverImage: {
|
||||
large: string;
|
||||
};
|
||||
season: string;
|
||||
seasonYear: number;
|
||||
status: string;
|
||||
episodes?: number;
|
||||
nextAiringEpisode?: {
|
||||
airingAt: number;
|
||||
timeUntilAiring: number;
|
||||
episode: number;
|
||||
};
|
||||
genres: string[];
|
||||
tags: [
|
||||
{
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
rank: number;
|
||||
isMediaSpoiler: boolean;
|
||||
isAdult: boolean;
|
||||
},
|
||||
];
|
||||
isAdult: boolean;
|
||||
};
|
||||
status: string;
|
||||
startedAt: {
|
||||
year: number;
|
||||
month: number;
|
||||
day: number;
|
||||
};
|
||||
completedAt: {
|
||||
year?: number;
|
||||
month?: number;
|
||||
day?: number;
|
||||
};
|
||||
notes?: string;
|
||||
progress: number;
|
||||
score: number;
|
||||
repeat: number;
|
||||
user: {
|
||||
id: number;
|
||||
name: string;
|
||||
avatar: {
|
||||
large: string;
|
||||
medium: string;
|
||||
};
|
||||
statistics: {
|
||||
anime: {
|
||||
count: number;
|
||||
statuses: [
|
||||
{
|
||||
status: string;
|
||||
count: number;
|
||||
},
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,19 +4,19 @@
|
||||
createTable,
|
||||
Render,
|
||||
Subscribe,
|
||||
} from "svelte-headless-table"
|
||||
} from "svelte-headless-table";
|
||||
// @ts-ignore
|
||||
import { addSortBy } from "svelte-headless-table/plugins"
|
||||
import { tableItems } from "../helperModules/GlobalVariablesAndHelperFunctions.svelte"
|
||||
import WebsiteLink from "./WebsiteLink.svelte"
|
||||
import { addSortBy } from "svelte-headless-table/plugins";
|
||||
import { tableItems } from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
|
||||
import WebsiteLink from "./WebsiteLink.svelte";
|
||||
|
||||
//when adding sort here is code { sort: addSortBy() }
|
||||
const table = createTable(tableItems, { sort: addSortBy() })
|
||||
const table = createTable(tableItems, { sort: addSortBy() });
|
||||
|
||||
const columns = table.createColumns([
|
||||
table.column({
|
||||
header: "Service Id",
|
||||
cell: ({ value }) => createRender(WebsiteLink, {id: value}),
|
||||
cell: ({ value }) => createRender(WebsiteLink, { id: value }),
|
||||
accessor: "id",
|
||||
}),
|
||||
table.column({
|
||||
@@ -55,11 +55,11 @@
|
||||
header: "Notes",
|
||||
accessor: "notes",
|
||||
}),
|
||||
])
|
||||
]);
|
||||
|
||||
//add pluginStates when add sort back
|
||||
const { headerRows, rows, tableAttrs, tableBodyAttrs } =
|
||||
table.createViewModel(columns)
|
||||
table.createViewModel(columns);
|
||||
</script>
|
||||
|
||||
<div class="relative overflow-x-auto rounded-lg mb-5">
|
||||
|
||||
@@ -13,11 +13,12 @@
|
||||
loginToSimkl,
|
||||
logoutOfAniList,
|
||||
logoutOfMAL,
|
||||
logoutOfSimkl,
|
||||
logoutOfSimkl
|
||||
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
|
||||
import * as runtime from "../../wailsjs/runtime";
|
||||
import type {MyAnimeListUser} from "../mal/types/MALTypes";
|
||||
import type {SimklUser} from "../simkl/types/simklTypes";
|
||||
import { ShowVersion } from "../../wailsjs/go/main/App";
|
||||
|
||||
let currentAniListUser: AniListUser;
|
||||
let currentMALUser: MyAnimeListUser;
|
||||
@@ -36,6 +37,20 @@
|
||||
function dropdownUser(): void {
|
||||
let dropdown = document.querySelector("#userDropdown");
|
||||
dropdown.classList.toggle("hidden");
|
||||
|
||||
if (!dropdown.classList.contains("hidden")) {
|
||||
document.addEventListener("click", clickOutside)
|
||||
}
|
||||
}
|
||||
|
||||
function clickOutside(event: Event): void {
|
||||
let dropdown = document.querySelector("#userDropdown")
|
||||
let toggleBtn = document.querySelector("#userDropdownButton")
|
||||
|
||||
if (!dropdown.contains(event.target as Node) && !toggleBtn.contains(event.target as Node)) {
|
||||
dropdown.classList.add("hidden")
|
||||
document.removeEventListener("click", clickOutside)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -77,7 +92,10 @@
|
||||
</li>
|
||||
{:else}
|
||||
<li>
|
||||
<button on:click={loginToAniList}
|
||||
<button on:click={() => {
|
||||
dropdownUser()
|
||||
loginToAniList()
|
||||
}}
|
||||
class="block px-4 py-2 w-full hover:bg-gray-600 truncate hover:text-white">
|
||||
<span class="maple-font text-lg mr-4">A</span>Login to AniList
|
||||
</button>
|
||||
@@ -94,7 +112,10 @@
|
||||
</li>
|
||||
{:else}
|
||||
<li>
|
||||
<button on:click={loginToMAL}
|
||||
<button on:click={() => {
|
||||
dropdownUser()
|
||||
loginToMAL()
|
||||
}}
|
||||
class="block px-4 py-2 w-full hover:bg-gray-600 truncate hover:text-white">
|
||||
<span class="maple-font text-lg mr-4">M</span>Login to MyAnimeList
|
||||
</button>
|
||||
@@ -111,7 +132,10 @@
|
||||
</li>
|
||||
{:else}
|
||||
<li>
|
||||
<button on:click={loginToSimkl}
|
||||
<button on:click={() => {
|
||||
dropdownUser()
|
||||
loginToSimkl()
|
||||
}}
|
||||
class="block px-4 py-2 w-full hover:bg-gray-600 truncate hover:text-white">
|
||||
<span class="maple-font text-lg mr-4">S</span>Login to Simkl
|
||||
</button>
|
||||
@@ -119,6 +143,15 @@
|
||||
{/if}
|
||||
</ul>
|
||||
<div class="py-2">
|
||||
<button
|
||||
on:click={() => {
|
||||
dropdownUser()
|
||||
ShowVersion()
|
||||
}}
|
||||
class="block px-4 py-2 w-full text-sm hover:bg-gray-600 text-gray-200 over:text-white"
|
||||
>
|
||||
Version
|
||||
</button>
|
||||
<button
|
||||
on:click={() => runtime.Quit()}
|
||||
class="block px-4 py-2 w-full text-sm hover:bg-gray-600 text-gray-200 over:text-white"
|
||||
|
||||
481
frontend/src/helperComponents/Datepicker.svelte
Normal file
481
frontend/src/helperComponents/Datepicker.svelte
Normal file
@@ -0,0 +1,481 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
import { fade } from "svelte/transition";
|
||||
import { Button } from "flowbite-svelte";
|
||||
|
||||
export let value: Date | null = null;
|
||||
export let defaultDate: Date | null = null;
|
||||
export let range: boolean = false;
|
||||
export let rangeFrom: Date | null = null;
|
||||
export let rangeTo: Date | null = null;
|
||||
export let locale: string = "default";
|
||||
export let firstDayOfWeek: number = 0; // 0 = Monday, 6 = Sunday
|
||||
export let dateFormat: Intl.DateTimeFormatOptions = {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
};
|
||||
export let placeholder: string = "Select date";
|
||||
export let disabled: boolean = false;
|
||||
export let required: boolean = false;
|
||||
export let inputClass: string = "";
|
||||
export let color: Button["color"] = "primary";
|
||||
export let inline: boolean = false;
|
||||
export let autohide: boolean = true;
|
||||
export let showActionButtons: boolean = false;
|
||||
export let title: string = "";
|
||||
|
||||
// Internal state
|
||||
const dispatch = createEventDispatcher();
|
||||
let isOpen: boolean = inline;
|
||||
let inputElement: HTMLInputElement;
|
||||
let datepickerContainerElement: HTMLDivElement;
|
||||
let currentMonth: Date = value || defaultDate || new Date();
|
||||
let focusedDate: Date | null = null;
|
||||
let calendarRef: HTMLDivElement;
|
||||
|
||||
$: daysInMonth = getDaysInMonth(currentMonth);
|
||||
$: weekdays = getWeekdays();
|
||||
|
||||
onMount(() => {
|
||||
if (!inline) {
|
||||
document.addEventListener("click", handleClickOutside);
|
||||
return () => {
|
||||
document.removeEventListener("click", handleClickOutside);
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// Color handling functions
|
||||
function getFocusRingClass(color: Button["color"]): string {
|
||||
switch (color) {
|
||||
case "primary":
|
||||
return "focus:ring-2 focus:ring-primary-400";
|
||||
case "blue":
|
||||
return "focus:ring-2 focus:ring-blue-400";
|
||||
case "red":
|
||||
return "focus:ring-2 focus:ring-red-400";
|
||||
case "green":
|
||||
return "focus:ring-2 focus:ring-green-400";
|
||||
case "yellow":
|
||||
return "focus:ring-2 focus:ring-yellow-400";
|
||||
case "purple":
|
||||
return "focus:ring-2 focus:ring-purple-400";
|
||||
case "slate":
|
||||
return "focus:ring-2 focus:ring-slate-400";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function getRangeBackgroundClass(color: Button["color"]): string {
|
||||
switch (color) {
|
||||
case "primary":
|
||||
return "bg-primary-900";
|
||||
case "blue":
|
||||
return "bg-blue-900";
|
||||
case "red":
|
||||
return "bg-red-900";
|
||||
case "green":
|
||||
return "bg-green-900";
|
||||
case "yellow":
|
||||
return "bg-yellow-900";
|
||||
case "purple":
|
||||
return "bg-purple-900";
|
||||
case "slate":
|
||||
return "bg-slate-900";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function getDaysInMonth(date: Date): Date[] {
|
||||
const year = date.getFullYear();
|
||||
const month = date.getMonth();
|
||||
const firstDay = new Date(year, month, 0);
|
||||
const lastDay = new Date(year, month + 1, 0);
|
||||
const daysArray: Date[] = [];
|
||||
|
||||
// Add days from previous month to fill the first week
|
||||
let start = firstDay.getDay() - firstDayOfWeek;
|
||||
if (start < 0) start += 7;
|
||||
for (let i = 0; i < start; i++) {
|
||||
daysArray.unshift(new Date(year, month, -i));
|
||||
}
|
||||
|
||||
// Add days of the current month
|
||||
for (let i = 1; i <= lastDay.getDate(); i++) {
|
||||
daysArray.push(new Date(year, month, i));
|
||||
}
|
||||
|
||||
// Add days from next month to fill the last week
|
||||
const remainingDays = 7 - (daysArray.length % 7);
|
||||
if (remainingDays < 7) {
|
||||
for (let i = 1; i <= remainingDays; i++) {
|
||||
daysArray.push(new Date(year, month + 1, i));
|
||||
}
|
||||
}
|
||||
|
||||
return daysArray;
|
||||
}
|
||||
|
||||
function getWeekdays(): string[] {
|
||||
const weekdays = [];
|
||||
for (let i = 0; i < 7; i++) {
|
||||
const day = new Date(2021, 5, i + firstDayOfWeek);
|
||||
weekdays.push(day.toLocaleString(locale, { weekday: "short" }));
|
||||
}
|
||||
return weekdays;
|
||||
}
|
||||
|
||||
function changeMonth(increment: number) {
|
||||
currentMonth = new Date(
|
||||
currentMonth.getFullYear(),
|
||||
currentMonth.getMonth() + increment,
|
||||
1,
|
||||
);
|
||||
}
|
||||
|
||||
function handleDaySelect(day: Date) {
|
||||
if (range) {
|
||||
if (!rangeFrom || (rangeFrom && rangeTo)) {
|
||||
rangeFrom = day;
|
||||
rangeTo = null;
|
||||
} else if (day < rangeFrom) {
|
||||
rangeTo = rangeFrom;
|
||||
rangeFrom = day;
|
||||
} else {
|
||||
rangeTo = day;
|
||||
}
|
||||
dispatch("select", { from: rangeFrom, to: rangeTo });
|
||||
} else {
|
||||
value = day;
|
||||
dispatch("select", value);
|
||||
if (autohide && !inline) isOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleInputChange() {
|
||||
const date = new Date(inputElement.value);
|
||||
if (!isNaN(date.getTime())) {
|
||||
handleDaySelect(date);
|
||||
}
|
||||
}
|
||||
|
||||
function handleClickOutside(event: MouseEvent) {
|
||||
if (
|
||||
isOpen &&
|
||||
datepickerContainerElement &&
|
||||
!datepickerContainerElement.contains(event.target as Node)
|
||||
) {
|
||||
isOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
function formatDate(date: Date | null): string {
|
||||
if (!date) return "";
|
||||
return date.toLocaleDateString(locale, dateFormat);
|
||||
}
|
||||
|
||||
function isSameDate(date1: Date | null, date2: Date | null): boolean {
|
||||
if (!date1 || !date2) return false;
|
||||
return date1.toDateString() === date2.toDateString();
|
||||
}
|
||||
|
||||
$: isSelected = (day: Date): boolean => {
|
||||
if (range) {
|
||||
return isSameDate(day, rangeFrom) || isSameDate(day, rangeTo);
|
||||
}
|
||||
return isSameDate(day, value);
|
||||
};
|
||||
|
||||
function isInRange(day: Date): boolean {
|
||||
if (!range || !rangeFrom || !rangeTo) return false;
|
||||
return day > rangeFrom && day < rangeTo;
|
||||
}
|
||||
|
||||
function isToday(day: Date): boolean {
|
||||
const today = new Date();
|
||||
return day.toDateString() === today.toDateString();
|
||||
}
|
||||
|
||||
function handleCalendarKeydown(event: KeyboardEvent) {
|
||||
if (!isOpen) return;
|
||||
|
||||
if (!focusedDate) {
|
||||
focusedDate = value || new Date();
|
||||
}
|
||||
|
||||
switch (event.key) {
|
||||
case "ArrowLeft":
|
||||
focusedDate = new Date(
|
||||
focusedDate.getFullYear(),
|
||||
focusedDate.getMonth(),
|
||||
focusedDate.getDate() - 1,
|
||||
);
|
||||
break;
|
||||
case "ArrowRight":
|
||||
focusedDate = new Date(
|
||||
focusedDate.getFullYear(),
|
||||
focusedDate.getMonth(),
|
||||
focusedDate.getDate() + 1,
|
||||
);
|
||||
break;
|
||||
case "ArrowUp":
|
||||
focusedDate = new Date(
|
||||
focusedDate.getFullYear(),
|
||||
focusedDate.getMonth(),
|
||||
focusedDate.getDate() - 7,
|
||||
);
|
||||
break;
|
||||
case "ArrowDown":
|
||||
focusedDate = new Date(
|
||||
focusedDate.getFullYear(),
|
||||
focusedDate.getMonth(),
|
||||
focusedDate.getDate() + 7,
|
||||
);
|
||||
break;
|
||||
case "Enter":
|
||||
handleDaySelect(focusedDate);
|
||||
break;
|
||||
case "Escape":
|
||||
isOpen = false;
|
||||
inputElement.focus();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
if (focusedDate.getMonth() !== currentMonth.getMonth()) {
|
||||
currentMonth = new Date(
|
||||
focusedDate.getFullYear(),
|
||||
focusedDate.getMonth(),
|
||||
1,
|
||||
);
|
||||
}
|
||||
|
||||
// Focus the button for the focused date
|
||||
setTimeout(() => {
|
||||
const focusedButton = calendarRef.querySelector(
|
||||
`button[aria-label="${focusedDate!.toLocaleDateString(locale, { weekday: "long", year: "numeric", month: "long", day: "numeric" })}"]`,
|
||||
) as HTMLButtonElement | null;
|
||||
focusedButton?.focus();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function handleInputKeydown(event: KeyboardEvent) {
|
||||
if (event.key === "Enter" || event.key === " ") {
|
||||
event.preventDefault();
|
||||
isOpen = !isOpen;
|
||||
}
|
||||
}
|
||||
|
||||
function handleToday() {
|
||||
handleDaySelect(new Date());
|
||||
}
|
||||
|
||||
function handleClear() {
|
||||
value = null;
|
||||
rangeFrom = null;
|
||||
rangeTo = null;
|
||||
dispatch("clear");
|
||||
}
|
||||
|
||||
function handleApply() {
|
||||
dispatch("apply", range ? { from: rangeFrom, to: rangeTo } : value);
|
||||
if (!inline) isOpen = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={datepickerContainerElement}
|
||||
class="relative {inline ? 'inline-block' : ''}"
|
||||
>
|
||||
{#if !inline}
|
||||
<div class="relative">
|
||||
<input
|
||||
bind:this={inputElement}
|
||||
type="text"
|
||||
class="w-full px-4 py-3 text-sm border rounded-md focus:outline-none bg-gray-700 text-white border-gray-600 {getFocusRingClass(
|
||||
color,
|
||||
)} {inputClass}"
|
||||
{placeholder}
|
||||
value={range
|
||||
? `${formatDate(rangeFrom)} - ${formatDate(rangeTo)}`
|
||||
: formatDate(value)}
|
||||
on:focus={() => (isOpen = true)}
|
||||
on:input={handleInputChange}
|
||||
on:keydown={handleInputKeydown}
|
||||
{disabled}
|
||||
{required}
|
||||
aria-haspopup="dialog"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="absolute inset-y-0 right-0 flex items-center px-3 text-gray-400 focus:outline-none"
|
||||
on:click={() => (isOpen = !isOpen)}
|
||||
{disabled}
|
||||
aria-label={isOpen ? "Close date picker" : "Open date picker"}
|
||||
>
|
||||
<svg
|
||||
class="w-4 h-4 text-gray-400"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
d="M20 4a2 2 0 0 0-2-2h-2V1a1 1 0 0 0-2 0v1h-3V1a1 1 0 0 0-2 0v1H6V1a1 1 0 0 0-2 0v1H2a2 2 0 0 0-2 2v2h20V4ZM0 18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8H0v10Zm5-8h10a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2Z"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if isOpen || inline}
|
||||
<div
|
||||
bind:this={calendarRef}
|
||||
id="datepicker-dropdown"
|
||||
class="
|
||||
{inline ? '' : 'absolute z-10 mt-1'}
|
||||
bg-gray-800 rounded-md shadow-lg"
|
||||
transition:fade={{ duration: 100 }}
|
||||
role="dialog"
|
||||
aria-label="Calendar"
|
||||
>
|
||||
<div class="p-4" role="application">
|
||||
{#if title}
|
||||
<h2 class="text-lg font-semibold mb-4 text-white">
|
||||
{title}
|
||||
</h2>
|
||||
{/if}
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<Button
|
||||
on:click={() => changeMonth(-1)}
|
||||
{color}
|
||||
size="sm"
|
||||
aria-label="Previous month"
|
||||
>
|
||||
<svg
|
||||
class="w-3 h-3 rtl:rotate-180 text-white"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 14 10"
|
||||
><path
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M13 5H1m0 0 4 4M1 5l4-4"
|
||||
></path></svg
|
||||
>
|
||||
</Button>
|
||||
<h3
|
||||
class="text-lg font-semibold text-white"
|
||||
aria-live="polite"
|
||||
>
|
||||
{currentMonth.toLocaleString(locale, {
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
})}
|
||||
</h3>
|
||||
<Button
|
||||
on:click={() => changeMonth(1)}
|
||||
{color}
|
||||
size="sm"
|
||||
aria-label="Next month"
|
||||
>
|
||||
<svg
|
||||
class="w-3 h-3 rtl:rotate-180 text-white"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 14 10"
|
||||
><path
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M1 5h12m0 0L9 1m4 4L9 9"
|
||||
></path></svg
|
||||
>
|
||||
</Button>
|
||||
</div>
|
||||
<div class="grid grid-cols-7 gap-1" role="grid">
|
||||
{#each weekdays as day}
|
||||
<div
|
||||
class="text-center text-sm font-medium text-gray-400"
|
||||
role="columnheader"
|
||||
>
|
||||
{day}
|
||||
</div>
|
||||
{/each}
|
||||
{#each daysInMonth as day}
|
||||
<Button
|
||||
color={isSelected(day) ? color : "alternative"}
|
||||
size="sm"
|
||||
class="w-full h-8 {day.getMonth() !==
|
||||
currentMonth.getMonth()
|
||||
? 'text-gray-600'
|
||||
: ''} {isToday(day)
|
||||
? 'font-bold'
|
||||
: ''} {isInRange(day)
|
||||
? getRangeBackgroundClass(color)
|
||||
: ''}"
|
||||
on:click={() => handleDaySelect(day)}
|
||||
on:keydown={handleCalendarKeydown}
|
||||
aria-label={day.toLocaleDateString(locale, {
|
||||
weekday: "long",
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})}
|
||||
aria-selected={isSelected(day)}
|
||||
role="gridcell"
|
||||
>
|
||||
{day.getDate()}
|
||||
</Button>
|
||||
{/each}
|
||||
</div>
|
||||
{#if showActionButtons}
|
||||
<div class="mt-4 flex justify-between">
|
||||
<Button on:click={handleToday} {color} size="sm"
|
||||
>Today</Button
|
||||
>
|
||||
<Button on:click={handleClear} color="red" size="sm"
|
||||
>Clear</Button
|
||||
>
|
||||
<Button on:click={handleApply} {color} size="sm"
|
||||
>Apply</Button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!--
|
||||
@component
|
||||
[Go to docs](https://flowbite-svelte.com/)
|
||||
## Props
|
||||
@prop export let value: Date | null = null;
|
||||
@prop export let defaultDate: Date | null = null;
|
||||
@prop export let range: boolean = false;
|
||||
@prop export let rangeFrom: Date | null = null;
|
||||
@prop export let rangeTo: Date | null = null;
|
||||
@prop export let locale: string = 'default';
|
||||
@prop export let firstDayOfWeek: number = 0;
|
||||
@prop export let dateFormat: Intl.DateTimeFormatOptions = { year: 'numeric', month: 'long', day: 'numeric' };
|
||||
@prop export let placeholder: string = 'Select date';
|
||||
@prop export let disabled: boolean = false;
|
||||
@prop export let required: boolean = false;
|
||||
@prop export let inputClass: string = '';
|
||||
@prop export let color: Button['color'] = 'primary';
|
||||
@prop export let inline: boolean = false;
|
||||
@prop export let autohide: boolean = true;
|
||||
@prop export let showActionButtons: boolean = false;
|
||||
@prop export let title: string = '';
|
||||
-->
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import Search from "./Search.svelte"
|
||||
import Search from "./Search.svelte";
|
||||
import {
|
||||
aniListLoggedIn,
|
||||
loginToAniList,
|
||||
@@ -7,55 +7,81 @@
|
||||
loginToSimkl,
|
||||
malLoggedIn,
|
||||
simklLoggedIn,
|
||||
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte"
|
||||
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
|
||||
import AvatarMenu from "./AvatarMenu.svelte";
|
||||
import logo from "../assets/images/AniTrackLogo.svg"
|
||||
import logo from "../assets/images/AniTrackLogo.svg";
|
||||
import { link } from "svelte-spa-router";
|
||||
|
||||
let isAniListLoggedIn: boolean
|
||||
let isSimklLoggedIn: boolean
|
||||
let isMALLoggedIn: boolean
|
||||
let isAniListLoggedIn: boolean;
|
||||
let isSimklLoggedIn: boolean;
|
||||
let isMALLoggedIn: boolean;
|
||||
|
||||
aniListLoggedIn.subscribe((value) => isAniListLoggedIn = value)
|
||||
simklLoggedIn.subscribe((value) => isSimklLoggedIn = value)
|
||||
malLoggedIn.subscribe((value) => isMALLoggedIn = value)
|
||||
aniListLoggedIn.subscribe((value) => (isAniListLoggedIn = value));
|
||||
simklLoggedIn.subscribe((value) => (isSimklLoggedIn = value));
|
||||
malLoggedIn.subscribe((value) => (isMALLoggedIn = value));
|
||||
</script>
|
||||
|
||||
<nav class="border-gray-200 bg-gray-900">
|
||||
<div class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4">
|
||||
<div
|
||||
class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4"
|
||||
>
|
||||
<div class="flex items-center space-x-3 rtl:space-x-reverse">
|
||||
<a href="/"><img src={logo} class="h-8" alt="AniTrack Logo"/></a>
|
||||
<a href="/" use:link
|
||||
><img src={logo} class="h-8" alt="AniTrack Logo" /></a
|
||||
>
|
||||
</div>
|
||||
<div class="flex items-center min-[950px]:order-2 space-x-3 min-[950px]:space-x-0 rtl:space-x-reverse">
|
||||
<div
|
||||
class="flex items-center min-[950px]:order-2 space-x-3 min-[950px]:space-x-0 rtl:space-x-reverse"
|
||||
>
|
||||
<div class="min-[950px]:block min-[950px]:mr-4">
|
||||
<Search />
|
||||
</div>
|
||||
<AvatarMenu/>
|
||||
<button on:click={() => {
|
||||
let menu = document.querySelector("#navbar-user")
|
||||
menu.classList.toggle("hidden")
|
||||
}} type="button"
|
||||
class="inline-flex items-center p-2 w-10 h-10 justify-center text-sm rounded-lg min-[950px]:hidden focus:outline-none focus:ring-2 text-gray-400 hover:bg-gray-700 focus:ring-gray-600"
|
||||
aria-controls="navbar-user" aria-expanded="false">
|
||||
<AvatarMenu />
|
||||
<button
|
||||
on:click={() => {
|
||||
let menu = document.querySelector("#navbar-user");
|
||||
menu.classList.toggle("hidden");
|
||||
}}
|
||||
type="button"
|
||||
class="inline-flex items-center p-2 w-10 h-10 justify-center text-sm rounded-lg min-[950px]:hidden focus:outline-none focus:ring-2 text-gray-400 hover:bg-gray-700 focus:ring-gray-600"
|
||||
aria-controls="navbar-user"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<span class="sr-only">Open main menu</span>
|
||||
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
|
||||
viewBox="0 0 17 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M1 1h15M1 7h15M1 13h15"/>
|
||||
<svg
|
||||
class="w-5 h-5"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 17 14"
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M1 1h15M1 7h15M1 13h15"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="hidden items-center justify-between w-full pb-4 min-[950px]:pb-0 min-[950px]:flex min-[950px]:w-auto min-[950px]:order-1 border border-gray-700 min-[950px]:border-0 bg-gray-800 min-[950px]:bg-transparent rounded-lg" id="navbar-user">
|
||||
<ul class="flex flex-col font-medium pb-6 min-[950px]:p-0 mt-4 min-[950px]:space-x-8 rtl:space-x-reverse min-[950px]:flex-row min-[950px]:mt-0">
|
||||
<div
|
||||
class="hidden items-center justify-between w-full pb-4 min-[950px]:pb-0 min-[950px]:flex min-[950px]:w-auto min-[950px]:order-1 border border-gray-700 min-[950px]:border-0 bg-gray-800 min-[950px]:bg-transparent rounded-lg"
|
||||
id="navbar-user"
|
||||
>
|
||||
<ul
|
||||
class="flex flex-col font-medium pb-6 min-[950px]:p-0 mt-4 min-[950px]:space-x-8 rtl:space-x-reverse min-[950px]:flex-row min-[950px]:mt-0"
|
||||
>
|
||||
<li>
|
||||
{#if !isAniListLoggedIn}
|
||||
<button on:click={loginToAniList}>
|
||||
<!-- class="block py-2 px-3 w-full min-[950px]:w-auto rounded text-gray-300 min-[950px]:hover:text-blue-500 hover:bg-gray-700 hover:text-white min-[950px]:hover:bg-transparent border-gray-700">-->
|
||||
<!-- class="block py-2 px-3 w-full min-[950px]:w-auto rounded text-gray-300 min-[950px]:hover:text-blue-500 hover:bg-gray-700 hover:text-white min-[950px]:hover:bg-transparent border-gray-700">-->
|
||||
AniList Login
|
||||
</button>
|
||||
{/if}
|
||||
{#if !isMALLoggedIn}
|
||||
<button on:click={loginToMAL}>
|
||||
<!-- class="block py-2 px-3 w-full min-[950px]:w-auto rounded min-[950px]:p-0 text-gray-300 min-[950px]:hover:text-blue-500 hover:bg-gray-700 hover:text-white min-[950px]:hover:bg-transparent border-gray-700">-->
|
||||
<!-- class="block py-2 px-3 w-full min-[950px]:w-auto rounded min-[950px]:p-0 text-gray-300 min-[950px]:hover:text-blue-500 hover:bg-gray-700 hover:text-white min-[950px]:hover:bg-transparent border-gray-700">-->
|
||||
MyAnimeList Login
|
||||
</button>
|
||||
{/if}
|
||||
@@ -63,14 +89,14 @@
|
||||
<li>
|
||||
{#if !isSimklLoggedIn}
|
||||
<button on:click={loginToSimkl}>
|
||||
<!-- class="block py-2 px-3 w-full min-[950px]:w-auto rounded min-[950px]:p-0 text-gray-300 min-[950px]:hover:text-blue-500 hover:bg-gray-700 hover:text-white min-[950px]:hover:bg-transparent border-gray-700">-->
|
||||
<!-- class="block py-2 px-3 w-full min-[950px]:w-auto rounded min-[950px]:p-0 text-gray-300 min-[950px]:hover:text-blue-500 hover:bg-gray-700 hover:text-white min-[950px]:hover:bg-transparent border-gray-700">-->
|
||||
Simkl Login
|
||||
</button>
|
||||
{/if}
|
||||
</li>
|
||||
</ul>
|
||||
<div class="flex justify-center min-[950px]:hidden">
|
||||
<Search/>
|
||||
<Search />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,28 +1,32 @@
|
||||
<script lang="ts">
|
||||
import {BrowserOpenURL} from "../../wailsjs/runtime"
|
||||
import { BrowserOpenURL } from "../../wailsjs/runtime";
|
||||
|
||||
export let id: string
|
||||
let url = ""
|
||||
let isAniList = false
|
||||
let isMAL = false
|
||||
let isSimkl = false
|
||||
let newId = id
|
||||
let re = /[ams]?-?(.*)/
|
||||
if (id !== undefined && id.length > 0) {
|
||||
isAniList = id.includes("a-")
|
||||
isMAL = id.includes("m-")
|
||||
isSimkl = id.includes("s-")
|
||||
newId = id.match(re)[1]
|
||||
}
|
||||
export let id: string;
|
||||
export let url = "";
|
||||
let isAniList = false;
|
||||
let isMAL = false;
|
||||
let isSimkl = false;
|
||||
let newId = id;
|
||||
let re = /[ams]?-?(.*)/;
|
||||
if (id !== undefined && id.length > 0) {
|
||||
isAniList = id.includes("a-");
|
||||
isMAL = id.includes("m-");
|
||||
isSimkl = id.includes("s-");
|
||||
if (isAniList || isMAL || isSimkl) newId = id.match(re)[1];
|
||||
else newId = id;
|
||||
}
|
||||
|
||||
|
||||
if (isAniList) url = `https://anilist.co/anime/${newId}`
|
||||
if (isMAL) url = `https://myanimelist.net/anime/${newId}`
|
||||
if (isSimkl) url = `https://simkl.com/anime/${newId}`
|
||||
if (isAniList) url = `https://anilist.co/anime/${newId}`;
|
||||
if (isMAL) url = `https://myanimelist.net/anime/${newId}`;
|
||||
if (isSimkl) url = `https://simkl.com/anime/${newId}`;
|
||||
</script>
|
||||
|
||||
{#if url.length > 0}
|
||||
<button class="underline underline-offset-2 px-4 py-1" on:click={() => BrowserOpenURL(url)}>{newId}</button>
|
||||
<button
|
||||
type="button"
|
||||
class="underline underline-offset-2 px-4 py-1"
|
||||
on:click={() => BrowserOpenURL(url)}>{newId}</button
|
||||
>
|
||||
{:else}
|
||||
{id}
|
||||
{id}
|
||||
{/if}
|
||||
|
||||
37
frontend/src/helperFunctions/convertAniListDateIn.ts
Normal file
37
frontend/src/helperFunctions/convertAniListDateIn.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import moment from "moment";
|
||||
|
||||
const convertAniListDateToString = (date: {
|
||||
year?: number;
|
||||
month?: number;
|
||||
day?: number;
|
||||
}): string => {
|
||||
if (
|
||||
date.year === undefined ||
|
||||
(date.year === 0 && date.month === undefined) ||
|
||||
(date.month === 0 && date.day === undefined) ||
|
||||
date.day === 0
|
||||
) {
|
||||
return "";
|
||||
}
|
||||
const newISODate = new Date(date.year, date.month - 1, date.day);
|
||||
const newMoment = moment(newISODate);
|
||||
return newMoment.format("MM-DD-YYYY");
|
||||
};
|
||||
|
||||
const convertAniListDateToDate = (date: {
|
||||
year?: number;
|
||||
month?: number;
|
||||
day?: number;
|
||||
}): Date | null => {
|
||||
if (
|
||||
date.year === undefined ||
|
||||
(date.year === 0 && date.month === undefined) ||
|
||||
(date.month === 0 && date.day === undefined) ||
|
||||
date.day === 0
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return new Date(date.year, date.month - 1, date.day);
|
||||
};
|
||||
|
||||
export { convertAniListDateToString, convertAniListDateToDate };
|
||||
@@ -1,17 +0,0 @@
|
||||
import moment from "moment";
|
||||
|
||||
export default (date: {
|
||||
year?: number,
|
||||
month?: number,
|
||||
day?: number,
|
||||
}): string => {
|
||||
if (date.year === undefined || date.year === 0
|
||||
&& date.month === undefined || date.month === 0
|
||||
&& date.day === undefined || date.day === 0
|
||||
) {
|
||||
return ""
|
||||
}
|
||||
const newISODate = new Date(date.year, date.month - 1, date.day)
|
||||
const newMoment = moment(newISODate)
|
||||
return newMoment.format('YYYY-MM-DD')
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
type Date = {
|
||||
year: number,
|
||||
month: number,
|
||||
day: number,
|
||||
}
|
||||
|
||||
export default (date: string): Date => {
|
||||
if (date === "") {
|
||||
return {
|
||||
year: 0,
|
||||
month: 0,
|
||||
day: 0,
|
||||
}
|
||||
}
|
||||
const re = /^([0-9]{4})-([0-9]{2})-([0-9]{2})/
|
||||
const newDate = re.exec(date)
|
||||
return {
|
||||
year: Number(newDate[1]),
|
||||
month: Number(newDate[2]),
|
||||
day: Number(newDate[3])
|
||||
}
|
||||
}
|
||||
39
frontend/src/helperFunctions/convertDateToAniList.ts
Normal file
39
frontend/src/helperFunctions/convertDateToAniList.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
type AnilistDate = {
|
||||
year: number;
|
||||
month: number;
|
||||
day: number;
|
||||
};
|
||||
|
||||
const convertDateStringToAniList = (date: string): AnilistDate => {
|
||||
if (date === "") {
|
||||
return {
|
||||
year: 0,
|
||||
month: 0,
|
||||
day: 0,
|
||||
};
|
||||
}
|
||||
const re = /^([0-9]{4})-([0-9]{2})-([0-9]{2})/;
|
||||
const newDate = re.exec(date);
|
||||
return {
|
||||
year: Number(newDate[1]),
|
||||
month: Number(newDate[2]),
|
||||
day: Number(newDate[3]),
|
||||
};
|
||||
};
|
||||
|
||||
const convertDateToAniList = (date: Date | null): AnilistDate => {
|
||||
if (date === null) {
|
||||
return {
|
||||
year: 0,
|
||||
month: 0,
|
||||
day: 0,
|
||||
};
|
||||
}
|
||||
return {
|
||||
year: Number(date.getFullYear()),
|
||||
month: Number(date.getMonth()) + 1,
|
||||
day: Number(date.getDate()),
|
||||
};
|
||||
};
|
||||
|
||||
export { convertDateStringToAniList, convertDateToAniList };
|
||||
@@ -1,9 +1,23 @@
|
||||
<script lang="ts">
|
||||
import { aniListAnime, GetAnimeSingleItem } from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
|
||||
import Anime from "../helperComponents/Anime.svelte"
|
||||
import { AniListGetSingleAnimeDefaultData } from "../helperDefaults/AniListGetSingleAnime";
|
||||
import Spinner from "../helperComponents/Spinner.svelte";
|
||||
|
||||
export let params: Record<string, string>
|
||||
let loadPromise = load(params.id)
|
||||
$: loadPromise = load(params.id)
|
||||
|
||||
async function load(id: string) {
|
||||
aniListAnime.update(() => AniListGetSingleAnimeDefaultData)
|
||||
await GetAnimeSingleItem(Number(id), true)
|
||||
}
|
||||
</script>
|
||||
|
||||
{#key params.id}
|
||||
<Anime />
|
||||
{#await loadPromise}
|
||||
<Spinner />
|
||||
{:then _}
|
||||
<Anime />
|
||||
{/await}
|
||||
{/key}
|
||||
2
frontend/wailsjs/go/main/App.d.ts
vendored
2
frontend/wailsjs/go/main/App.d.ts
vendored
@@ -42,6 +42,8 @@ export function MyAnimeListLogin():Promise<void>;
|
||||
|
||||
export function MyAnimeListUpdate(arg1:main.MALAnime,arg2:main.MALUploadStatus):Promise<main.MalListStatus>;
|
||||
|
||||
export function ShowVersion():Promise<void>;
|
||||
|
||||
export function SimklGetUserWatchlist():Promise<main.SimklWatchListType>;
|
||||
|
||||
export function SimklLogin():Promise<void>;
|
||||
|
||||
@@ -82,6 +82,10 @@ export function MyAnimeListUpdate(arg1, arg2) {
|
||||
return window['go']['main']['App']['MyAnimeListUpdate'](arg1, arg2);
|
||||
}
|
||||
|
||||
export function ShowVersion() {
|
||||
return window['go']['main']['App']['ShowVersion']();
|
||||
}
|
||||
|
||||
export function SimklGetUserWatchlist() {
|
||||
return window['go']['main']['App']['SimklGetUserWatchlist']();
|
||||
}
|
||||
|
||||
@@ -202,8 +202,7 @@ export namespace main {
|
||||
export class MALAnime {
|
||||
id: id;
|
||||
title: title;
|
||||
// Go type: struct { Large string "json:\"large\" json:\"large\""; Medium string "json:\"medium\" json:\"medium\"" }
|
||||
main_picture: any;
|
||||
main_picture: mainPicture;
|
||||
alternative_titles: alternativeTitles;
|
||||
start_date: startDate;
|
||||
end_date: endDate;
|
||||
@@ -242,7 +241,7 @@ export namespace main {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.id = source["id"];
|
||||
this.title = source["title"];
|
||||
this.main_picture = this.convertValues(source["main_picture"], Object);
|
||||
this.main_picture = source["main_picture"];
|
||||
this.alternative_titles = source["alternative_titles"];
|
||||
this.start_date = source["start_date"];
|
||||
this.end_date = source["end_date"];
|
||||
@@ -314,7 +313,7 @@ export namespace main {
|
||||
}
|
||||
}
|
||||
export class MALWatchlist {
|
||||
data: struct { Node struct { Id int "json:\"id\" ts_type:\"id\""; Title string "json:\"title\" ts_type:\"title\""; MainPicture struct { Medium string "json:\"medium\" json:\"medium\""; Large string "json:\"large\" json:\"large\"" } "json:\"main_picture\" json:\"mainPicture\"" } "json:\"node\" json:\"node\""; ListStatus 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.Time "json:\"updated_at\" ts_type:\"updatedAt\""; StartDate string "json:\"start_date\" ts_type:\"startDate\""; FinishDate string "json:\"finish_date\" ts_type:\"finishDate\"" } "json:\"list_status\" ts_type:\"listStatus\"" }[];
|
||||
data: data;
|
||||
paging: paging;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
@@ -323,27 +322,9 @@ export namespace main {
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.data = this.convertValues(source["data"], struct { Node struct { Id int "json:\"id\" ts_type:\"id\""; Title string "json:\"title\" ts_type:\"title\""; MainPicture struct { Medium string "json:\"medium\" json:\"medium\""; Large string "json:\"large\" json:\"large\"" } "json:\"main_picture\" json:\"mainPicture\"" } "json:\"node\" json:\"node\""; ListStatus 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.Time "json:\"updated_at\" ts_type:\"updatedAt\""; StartDate string "json:\"start_date\" ts_type:\"startDate\""; FinishDate string "json:\"finish_date\" ts_type:\"finishDate\"" } "json:\"list_status\" ts_type:\"listStatus\"" });
|
||||
this.data = source["data"];
|
||||
this.paging = source["paging"];
|
||||
}
|
||||
|
||||
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 class MalListStatus {
|
||||
status: status;
|
||||
@@ -383,7 +364,7 @@ export namespace main {
|
||||
id: number;
|
||||
mediaId: number;
|
||||
userId: number;
|
||||
// Go type: struct { ID int "json:\"id\""; IDMal int "json:\"idMal\""; Title struct { Romaji string "json:\"romaji\""; English string "json:\"english\""; Native string "json:\"native\"" } "json:\"title\""; Description string "json:\"description\""; CoverImage struct { Large string "json:\"large\"" } "json:\"coverImage\""; Season string "json:\"season\""; SeasonYear int "json:\"seasonYear\""; Status string "json:\"status\""; Episodes int "json:\"episodes\""; NextAiringEpisode struct { AiringAt int "json:\"airingAt\""; TimeUntilAiring int "json:\"timeUntilAiring\""; Episode int "json:\"episode\"" } "json:\"nextAiringEpisode\""; Tags []struct { Id int "json:\"id\""; Name string "json:\"name\""; Description string "json:\"description\""; Rank int "json:\"rank\""; IsMediaSpoiler bool "json:\"isMediaSpoiler\""; IsAdult bool "json:\"isAdult\"" } "json:\"tags\""; IsAdult bool "json:\"isAdult\"" }
|
||||
// Go type: struct { ID int "json:\"id\""; IDMal int "json:\"idMal\""; Title struct { Romaji string "json:\"romaji\""; English string "json:\"english\""; Native string "json:\"native\"" } "json:\"title\""; Description string "json:\"description\""; CoverImage struct { Large string "json:\"large\"" } "json:\"coverImage\""; Season string "json:\"season\""; SeasonYear int "json:\"seasonYear\""; Status string "json:\"status\""; Episodes int "json:\"episodes\""; NextAiringEpisode struct { AiringAt int "json:\"airingAt\""; TimeUntilAiring int "json:\"timeUntilAiring\""; Episode int "json:\"episode\"" } "json:\"nextAiringEpisode\""; Genres []string "json:\"genres\""; Tags []struct { Id int "json:\"id\""; Name string "json:\"name\""; Description string "json:\"description\""; Rank int "json:\"rank\""; IsMediaSpoiler bool "json:\"isMediaSpoiler\""; IsAdult bool "json:\"isAdult\"" } "json:\"tags\""; IsAdult bool "json:\"isAdult\"" }
|
||||
media: any;
|
||||
status: string;
|
||||
// Go type: struct { Year int "json:\"year\""; Month int "json:\"month\""; Day int "json:\"day\"" }
|
||||
@@ -624,11 +605,10 @@ export namespace struct { Node main {
|
||||
|
||||
}
|
||||
|
||||
export namespace struct { Node struct { Id int "json:\"id\" ts_type:\"id\""; Title string "json:\"title\" ts_type:\"title\""; MainPicture struct { Medium string "json:\"medium\" json:\"medium\""; Large string "json:\"large\" json:\"large\"" } "json:\"main_picture\" json:\"mainPicture\"" } "json:\"node\" json:\"node\""; ListStatus 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 { Node struct { Id int "json:\"id\" ts_type:\"id\""; Title string "json:\"title\" ts_type:\"title\""; MainPicture struct { Medium string "json:\"medium\" ts_type:\"medium\""; Large string "json:\"large\" ts_type:\"large\"" } "json:\"main_picture\" ts_type:\"mainPicture\"" } "json:\"node\" ts_type:\"node\""; ListStatus 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 {
|
||||
// Go type: struct { Id int "json:\"id\" ts_type:\"id\""; Title string "json:\"title\" ts_type:\"title\""; MainPicture struct { Medium string "json:\"medium\" json:\"medium\""; Large string "json:\"large\" json:\"large\"" } "json:\"main_picture\" json:\"mainPicture\"" }
|
||||
node: any;
|
||||
node: node;
|
||||
list_status: listStatus;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
@@ -637,27 +617,9 @@ export namespace struct { Node struct { Id int "json:\"id\" ts_type:\"id\""; Tit
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.node = this.convertValues(source["node"], Object);
|
||||
this.node = source["node"];
|
||||
this.list_status = source["list_status"];
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
2
frontend/wailsjs/runtime/runtime.d.ts
vendored
2
frontend/wailsjs/runtime/runtime.d.ts
vendored
@@ -134,7 +134,7 @@ export function WindowIsFullscreen(): Promise<boolean>;
|
||||
|
||||
// [WindowSetSize](https://wails.io/docs/reference/runtime/window#windowsetsize)
|
||||
// Sets the width and height of the window.
|
||||
export function WindowSetSize(width: number, height: number): Promise<Size>;
|
||||
export function WindowSetSize(width: number, height: number): void;
|
||||
|
||||
// [WindowGetSize](https://wails.io/docs/reference/runtime/window#windowgetsize)
|
||||
// Gets the width and height of the window.
|
||||
|
||||
24
go.mod
24
go.mod
@@ -1,49 +1,49 @@
|
||||
module AniTrack
|
||||
|
||||
go 1.23
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/99designs/keyring v1.2.2
|
||||
github.com/tidwall/gjson v1.18.0
|
||||
github.com/wailsapp/wails/v2 v2.9.2
|
||||
github.com/wailsapp/wails/v2 v2.10.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
|
||||
github.com/bep/debounce v1.2.1 // indirect
|
||||
github.com/danieljoos/wincred v1.2.2 // indirect
|
||||
github.com/dvsekhvalnov/jose2go v1.7.0 // indirect
|
||||
github.com/dvsekhvalnov/jose2go v1.8.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/v5 v5.1.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
|
||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
|
||||
github.com/labstack/echo/v4 v4.12.0 // indirect
|
||||
github.com/labstack/echo/v4 v4.13.3 // indirect
|
||||
github.com/labstack/gommon v0.4.2 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.6.1 // indirect
|
||||
github.com/leaanthony/gosod v1.0.4 // indirect
|
||||
github.com/leaanthony/slicer v1.6.0 // indirect
|
||||
github.com/leaanthony/u v1.1.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mtibben/percent v0.2.1 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/samber/lo v1.47.0 // indirect
|
||||
github.com/samber/lo v1.49.1 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tkrajina/go-reflector v0.5.8 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
github.com/wailsapp/go-webview2 v1.0.16 // indirect
|
||||
github.com/wailsapp/go-webview2 v1.0.19 // indirect
|
||||
github.com/wailsapp/mimetype v1.4.1 // indirect
|
||||
golang.org/x/crypto v0.28.0 // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/sys v0.26.0 // indirect
|
||||
golang.org/x/term v0.25.0 // indirect
|
||||
golang.org/x/text v0.19.0 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/term 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
|
||||
|
||||
53
go.sum
53
go.sum
@@ -8,8 +8,8 @@ github.com/danieljoos/wincred v1.2.2 h1:774zMFJrqaeYCK2W57BgAem/MLi6mtSE47MB6BOJ
|
||||
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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dvsekhvalnov/jose2go v1.7.0 h1:bnQc8+GMnidJZA8zc6lLEAb4xNrIqHwO+9TzqvtQZPo=
|
||||
github.com/dvsekhvalnov/jose2go v1.7.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU=
|
||||
github.com/dvsekhvalnov/jose2go v1.8.0 h1:LqkkVKAlHFfH9LOEl5fe4p/zL02OhWE7pCufMBG2jLA=
|
||||
github.com/dvsekhvalnov/jose2go v1.8.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU=
|
||||
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/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0=
|
||||
@@ -23,10 +23,11 @@ github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NM
|
||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
|
||||
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/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0=
|
||||
github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY=
|
||||
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=
|
||||
@@ -42,9 +43,8 @@ github.com/leaanthony/u v1.1.1/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQc
|
||||
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
||||
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/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
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-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
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=
|
||||
@@ -60,12 +60,12 @@ 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.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc=
|
||||
github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
|
||||
github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
|
||||
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/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
@@ -79,31 +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/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/wailsapp/go-webview2 v1.0.16 h1:wffnvnkkLvhRex/aOrA3R7FP7rkvOqL/bir1br7BekU=
|
||||
github.com/wailsapp/go-webview2 v1.0.16/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo=
|
||||
github.com/wailsapp/go-webview2 v1.0.19 h1:7U3QcDj1PrBPaxJNCui2k1SkWml+Q5kvFUFyTImA6NU=
|
||||
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/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
|
||||
github.com/wailsapp/wails/v2 v2.9.2 h1:Xb5YRTos1w5N7DTMyYegWaGukCP2fIaX9WF21kPPF2k=
|
||||
github.com/wailsapp/wails/v2 v2.9.2/go.mod h1:uehvlCwJSFcBq7rMCGfk4rxca67QQGsbg5Nm4m9UnBs=
|
||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||
github.com/wailsapp/wails/v2 v2.10.1 h1:QWHvWMXII2nI/nXz77gpPG8P3ehl6zKe+u4su5BWIns=
|
||||
github.com/wailsapp/wails/v2 v2.10.1/go.mod h1:zrebnFV6MQf9kx8HI4iAv63vsR5v67oS7GTEZ7Pz1TY=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
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.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
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-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-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
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.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
|
||||
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
|
||||
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||
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.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
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=
|
||||
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=
|
||||
|
||||
4
main.go
4
main.go
@@ -34,9 +34,9 @@ func main() {
|
||||
app,
|
||||
},
|
||||
Linux: &linux.Options{
|
||||
Icon: []byte("./build/appicon.png"),
|
||||
Icon: []byte("./build/AniTrack.png"),
|
||||
WindowIsTranslucent: false,
|
||||
WebviewGpuPolicy: linux.WebviewGpuPolicyAlways,
|
||||
WebviewGpuPolicy: linux.WebviewGpuPolicyNever,
|
||||
ProgramName: "AniTrack",
|
||||
},
|
||||
})
|
||||
|
||||
@@ -12,6 +12,6 @@
|
||||
},
|
||||
"info": {
|
||||
"productName": "AniTrack",
|
||||
"productVersion": "0.1.4"
|
||||
"productVersion": "0.5.3"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user