16 Commits
0.1.3 ... 0.1.6

20 changed files with 887 additions and 321 deletions

View File

@ -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{}
@ -141,11 +144,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 +157,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
@ -249,7 +252,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"`

View File

@ -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{}
}

View File

@ -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"`

View File

@ -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,12 +301,9 @@ 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 {
@ -311,7 +318,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{}
@ -333,13 +339,12 @@ func (a *App) GetMyAnimeListLoggedInUser() MyAnimeListUser {
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{}
}

View File

@ -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,
@ -300,7 +296,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 +345,4 @@ func WatchListUpdate(anime SimklAnime) {
if !updated {
SimklWatchList.Anime = append(SimklWatchList.Anime, anime)
}
return
}

View File

@ -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{}
}

11
build/AniTrack.desktop Executable file
View 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=

View File

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View File

@ -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>

View File

@ -11,7 +11,10 @@
import { Button } from "flowbite-svelte";
import type { AniListGetSingleAnime } from "../anilist/types/AniListCurrentUserWatchListType";
import Rating from "./Rating.svelte";
import convertAniListDateToString from "../helperFunctions/convertAniListDateToString";
import {
convertAniListDateToString,
convertAniListDateToDate,
} from "../helperFunctions/convertAniListDateIn";
import AnimeTable from "./AnimeTable.svelte";
import type {
MALAnime,
@ -25,7 +28,10 @@
StatusOptions,
} from "../helperTypes/StatusTypes";
import type { AniListUpdateVariables } from "../anilist/types/AniListTypes";
import convertDateStringToAniList from "../helperFunctions/convertDateStringToAniList";
import {
convertDateStringToAniList,
convertDateToAniList,
} from "../helperFunctions/convertDateToAniList";
import {
AniListDeleteEntry,
AniListUpdateEntry,
@ -38,6 +44,8 @@
} from "../../wailsjs/go/main/App";
import { AddAnimeServiceToTable } from "../helperModules/AddAnimeServiceToTable.svelte";
import { CheckIfAniListLoggedInAndLoadWatchList } from "../helperModules/CheckIfAniListLoggedInAndLoadWatchList.svelte";
import Datepicker from "./Datepicker.svelte";
const re = /^([0-9]{4})-([0-9]{2})-([0-9]{2})/;
let isAniListLoggedIn: boolean;
let isMalLoggedIn: boolean;
@ -78,10 +86,10 @@
(option) =>
currentAniListAnime.data.MediaList.status === option.aniList,
)[0];
const startedAtDate = convertAniListDateToString(
let startedAtDate: Date | null = convertAniListDateToDate(
currentAniListAnime.data.MediaList.startedAt,
);
const completedAtDate = convertAniListDateToString(
let completedAtDate: Date | null = convertAniListDateToDate(
currentAniListAnime.data.MediaList.completedAt,
);
@ -103,19 +111,34 @@
notes: currentAniListAnime.data.MediaList.notes,
});
if (isMalLoggedIn)
if (isMalLoggedIn) {
let startDate = "";
let finishDate = "";
if (currentMalAnime.my_list_status.start_date !== "") {
const startArray = re.exec(
currentMalAnime.my_list_status.start_date,
);
startDate = `${startArray[2]}-${startArray[3]}-${startArray[1]}`;
}
if (currentMalAnime.my_list_status.finish_date !== "") {
const finishArray = re.exec(
currentMalAnime.my_list_status.finish_date,
);
finishDate = `${finishArray[2]}-${finishArray[3]}-${finishArray[1]}`;
}
AddAnimeServiceToTable({
id: `m-${currentMalAnime.id}`,
title: currentMalAnime.title,
service: "MyAnimeList",
progress: currentMalAnime.my_list_status.num_episodes_watched,
status: currentMalAnime.my_list_status.status,
startedAt: currentMalAnime.my_list_status.start_date,
completedAt: currentMalAnime.my_list_status.finish_date,
startedAt: startDate,
completedAt: finishDate,
score: currentMalAnime.my_list_status.score,
repeat: currentMalAnime.my_list_status.num_times_rewatched,
notes: currentMalAnime.my_list_status.comments,
});
}
if (isSimklLoggedIn && Object.keys(currentSimklAnime).length > 0)
AddAnimeServiceToTable({
@ -137,8 +160,8 @@
rating: number;
episodes: number;
status: StatusOption;
startedAt: string;
completedAt: string;
startedAt: Date | null;
completedAt: Date | null;
repeat: number;
notes: string;
} = {
@ -150,8 +173,8 @@
mal: "",
simkl: "",
},
startedAt: "",
completedAt: "",
startedAt: null,
completedAt: null,
repeat: 0,
notes: "",
};
@ -188,8 +211,8 @@
score: submitData.rating,
repeat: submitData.repeat,
notes: submitData.notes,
startedAt: convertDateStringToAniList(submitData.startedAt),
completedAt: convertDateStringToAniList(submitData.completedAt),
startedAt: convertDateToAniList(startedAtDate),
completedAt: convertDateToAniList(completedAtDate),
};
await AniListUpdateEntry(body).then(
(value: AniListGetSingleAnime) => {
@ -244,6 +267,20 @@
value.my_list_status.comments = malAnimeReturn.comments;
return value;
});
let startDate = "";
let finishDate = "";
if (currentMalAnime.my_list_status.start_date !== "") {
const startArray = re.exec(
currentMalAnime.my_list_status.start_date,
);
startDate = `${startArray[2]}-${startArray[3]}-${startArray[1]}`;
}
if (currentMalAnime.my_list_status.finish_date !== "") {
const finishArray = re.exec(
currentMalAnime.my_list_status.finish_date,
);
finishDate = `${finishArray[2]}-${finishArray[3]}-${finishArray[1]}`;
}
AddAnimeServiceToTable({
id: `m-${currentMalAnime.id}`,
title: currentMalAnime.title,
@ -251,8 +288,8 @@
progress:
currentMalAnime.my_list_status.num_episodes_watched,
status: currentMalAnime.my_list_status.status,
startedAt: currentMalAnime.my_list_status.start_date,
completedAt: currentMalAnime.my_list_status.finish_date,
startedAt: startDate,
completedAt: finishDate,
score: currentMalAnime.my_list_status.score,
repeat: currentMalAnime.my_list_status
.num_times_rewatched,
@ -398,15 +435,19 @@
setTimeout(() => submitSuccess.set(false), 2000);
};
let max = 999
let max = 999;
if (currentAniListAnime.data.MediaList.media.episodes !== 0) {
max = currentAniListAnime.data.MediaList.media.episodes
max = currentAniListAnime.data.MediaList.media.episodes;
}
if (
currentAniListAnime.data.MediaList.media.episodes === 0 &&
currentAniListAnime.data.MediaList.media.nextAiringEpisode.episode !== 0
) {
max =
currentAniListAnime.data.MediaList.media.nextAiringEpisode.episode -
1;
}
if (currentAniListAnime.data.MediaList.media.episodes === 0 &&
currentAniListAnime.data.MediaList.media.nextAiringEpisode.episode !== 0) {
max = currentAniListAnime.data.MediaList.media.nextAiringEpisode.episode - 1
}
</script>
<form on:submit|preventDefault={handleSubmit} class="container pt-3 pb-10">
@ -433,24 +474,82 @@
class="text-left block mb-2 text-sm font-medium text-white"
>Episode Progress</label
>
<input
type="number"
name="episodes"
min="0"
max={max}
id="episodes"
class="border {currentAniListAnime.data.MediaList
.progress < 0 ||
(currentAniListAnime.data.MediaList.media.episodes >
0 &&
currentAniListAnime.data.MediaList.progress >
currentAniListAnime.data.MediaList.media
.episodes)
? 'border-red-500 border-[2px] text-rose-300 focus:ring-red-500 focus:border-red-500'
: 'border-gray-500 text-white focus:ring-blue-500 focus:border-blue-500'} text-sm rounded-lg block w-24 p-2.5 bg-gray-600 placeholder-gray-400"
bind:value={currentAniListAnime.data.MediaList.progress}
required
/>
<div class="relative flex items-center max-w-[8rem]">
<button
type="button"
id="decrement-button"
data-input-counter-decrement="quantity-input"
on:click={() =>
(currentAniListAnime.data.MediaList.progress -= 1)}
class="bg-gray-100 dark:bg-gray-700 dark:hover:bg-gray-600 dark:border-gray-600 hover:bg-gray-200 border border-gray-300 rounded-s-lg p-3 h-11 focus:ring-gray-100 dark:focus:ring-gray-700 focus:ring-2 focus:outline-none"
>
<svg
class="w-3 h-3 text-gray-900 dark:text-white"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 18 2"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M1 1h16"
/>
</svg>
</button>
<input
type="number"
name="episodes"
min="0"
{max}
id="episodes"
class="border border-x-0 p-2.5 h-11 text-center text-sm block w-full placeholder-gray-400 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none
{currentAniListAnime.data.MediaList.progress < 0 ||
(currentAniListAnime.data.MediaList.media.episodes >
0 &&
currentAniListAnime.data.MediaList.progress >
currentAniListAnime.data.MediaList.media
.episodes) ||
(currentAniListAnime.data.MediaList.media
.nextAiringEpisode.episode > 0 &&
currentAniListAnime.data.MediaList.progress >
currentAniListAnime.data.MediaList.media
.nextAiringEpisode.episode -
1)
? 'border-red-500 border-[2px] text-rose-300 focus:ring-red-500 focus:border-red-500'
: 'bg-gray-700 hover:bg-gray-600 border-gray-600 text-white focus:ring-blue-500 focus:border-blue-500'} w-24"
bind:value={
currentAniListAnime.data.MediaList.progress
}
required
/>
<button
type="button"
id="increment-button"
data-input-counter-increment="quantity-input"
on:click={() =>
(currentAniListAnime.data.MediaList.progress += 1)}
class="bg-gray-100 dark:bg-gray-700 dark:hover:bg-gray-600 dark:border-gray-600 hover:bg-gray-200 border border-gray-300 rounded-e-lg p-3 h-11 focus:ring-gray-100 dark:focus:ring-gray-700 focus:ring-2 focus:outline-none"
>
<svg
class="w-3 h-3 text-gray-900 dark:text-white"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 18 18"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 1v16M1 9h16"
/>
</svg>
</button>
</div>
<div>
/ {currentAniListAnime.data.MediaList.media
.nextAiringEpisode.episode !== 0
@ -459,9 +558,10 @@
: currentAniListAnime.data.MediaList.media.episodes}
</div>
{#if currentAniListAnime.data.MediaList.media.nextAiringEpisode.episode !== 0}
<div>
of {currentAniListAnime.data.MediaList.media.episodes}
</div>
<div>
of {currentAniListAnime.data.MediaList.media
.episodes}
</div>
{/if}
</div>
@ -494,33 +594,16 @@
class="text-left block mb-2 text-sm font-medium text-white"
>Date Started</label
>
<div class="relative max-w-sm">
<div
class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none"
>
<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"
/>
</svg>
</div>
<input
id="startedAt"
type="date"
name="startedAt"
class="border text-sm rounded-lg
focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 bg-gray-700 border-gray-600
placeholder-gray-400 text-white"
value={startedAtDate}
placeholder="Date Started"
/>
</div>
<Datepicker
bind:value={startedAtDate}
color="slate"
dateFormat={{
year: "numeric",
month: "2-digit",
day: "2-digit",
}}
showActionButtons
/>
</div>
<div>
<label
@ -528,33 +611,16 @@
class="text-left block mb-2 text-sm font-medium text-white"
>Date Completed</label
>
<div class="relative max-w-sm">
<div
class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none"
>
<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"
/>
</svg>
</div>
<input
id="completedAt"
type="date"
name="completedAt"
class="border text-sm rounded-lg
block w-full ps-10 p-2.5 bg-gray-700 border-gray-600
placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500"
value={completedAtDate}
placeholder="Date Completed"
/>
</div>
<Datepicker
bind:value={completedAtDate}
color="slate"
dateFormat={{
year: "numeric",
month: "2-digit",
day: "2-digit",
}}
showActionButtons
/>
</div>
<div>
<label

View 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-500 dark:focus:ring-primary-400";
case "blue":
return "focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400";
case "red":
return "focus:ring-2 focus:ring-red-500 dark:focus:ring-red-400";
case "green":
return "focus:ring-2 focus:ring-green-500 dark:focus:ring-green-400";
case "yellow":
return "focus:ring-2 focus:ring-yellow-500 dark:focus:ring-yellow-400";
case "purple":
return "focus:ring-2 focus:ring-purple-500 dark:focus:ring-purple-400";
case "slate":
return "focus:ring-2 focus:ring-slate-500 dark:focus:ring-slate-400";
default:
return "";
}
}
function getRangeBackgroundClass(color: Button["color"]): string {
switch (color) {
case "primary":
return "bg-primary-100 dark:bg-primary-900";
case "blue":
return "bg-blue-100 dark:bg-blue-900";
case "red":
return "bg-red-100 dark:bg-red-900";
case "green":
return "bg-green-100 dark:bg-green-900";
case "yellow":
return "bg-yellow-100 dark:bg-yellow-900";
case "purple":
return "bg-purple-100 dark:bg-purple-900";
case "slate":
return "bg-slate-100 dark: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 dark:bg-gray-700 dark:text-white dark: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-500 dark: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-500 dark: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-white dark: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 dark: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 dark: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 dark: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 dark: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-500 dark: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-300 dark: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 = '';
-->

View 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 };

View File

@ -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')
}

View File

@ -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])
}
}

View 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 };

View File

@ -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;
@ -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;
}
}
}

18
go.mod
View File

@ -12,20 +12,20 @@ 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
@ -37,13 +37,13 @@ require (
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.32.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/term v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
)
// replace github.com/wailsapp/wails/v2 v2.9.1 => /home/nymusicman/go/pkg/mod

42
go.sum
View File

@ -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=
@ -25,8 +25,8 @@ github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEE
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/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 +42,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=
@ -64,8 +63,8 @@ github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc=
github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
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 +78,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=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
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.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
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.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
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.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
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=

View File

@ -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",
},
})

View File

@ -12,6 +12,6 @@
},
"info": {
"productName": "AniTrack",
"productVersion": "0.1.3"
"productVersion": "0.1.6"
}
}