15 Commits
0.1.8 ... 0.5.0

Author SHA1 Message Date
2e5a4a4493 finally fixed being able to reload the anime page when searching or changing the url 2025-12-21 13:01:51 -05:00
874b3952ee upped version to 0.4 due to big change 2025-12-21 01:15:39 -05:00
60eac10545 user dropdown now closes when certain actions are taken or clicked outside of dialog 2025-12-21 01:14:46 -05:00
6d66d711ff added a showversion button with popup to the interface 2025-12-21 01:14:05 -05:00
cd62e6c658 fixed MAL not auto logging in when app starts 2025-12-21 00:25:48 -05:00
063c5016f3 fixed MAL not auto logging in when app starts 2025-12-21 00:24:25 -05:00
5c4caf68e6 added tooltip and version change 2025-06-22 21:08:56 -04:00
c10e853564 updated minor version for tag fix 2025-06-06 23:25:24 -04:00
cd043d093f fixed issue where tags would not reload after submit 2025-06-06 23:25:08 -04:00
6db01f7f9f added tags to frontend anime item 2025-05-14 14:31:43 -04:00
8460d56d55 created simple linux install script for AniTrack 2025-05-07 10:03:58 -04:00
8fedbe4607 created xdg icon set instead of one big icon 2025-05-07 10:03:37 -04:00
e069c47242 automated adding anime by moving progress from 0 to 1 2025-03-30 10:03:05 -04:00
aba0f2d1d5 removed console.log 2025-03-30 10:02:38 -04:00
af6cb7f08a removed unecessary button dependency and neovim reformatted 2025-03-30 09:52:46 -04:00
16 changed files with 248 additions and 102 deletions

View File

@@ -308,7 +308,16 @@ func refreshMyAnimeListAuthorizationToken() {
func (a *App) GetMyAnimeListLoggedInUser() MyAnimeListUser { func (a *App) GetMyAnimeListLoggedInUser() MyAnimeListUser {
a.MyAnimeListLogin() a.MyAnimeListLogin()
user := createUser()
if user.Name == "" {
refreshMyAnimeListAuthorizationToken()
user = createUser()
}
return user
}
func createUser() MyAnimeListUser {
client := &http.Client{} client := &http.Client{}
req, _ := http.NewRequest("GET", "https://api.myanimelist.net/v2/users/@me?fields=anime_statistics", nil) req, _ := http.NewRequest("GET", "https://api.myanimelist.net/v2/users/@me?fields=anime_statistics", nil)
@@ -335,6 +344,7 @@ func (a *App) GetMyAnimeListLoggedInUser() MyAnimeListUser {
} }
return user return user
} }
func (a *App) LogoutMyAnimeList() string { func (a *App) LogoutMyAnimeList() string {

15
app.go
View File

@@ -3,9 +3,11 @@ package main
import ( import (
"context" "context"
_ "embed" _ "embed"
"log"
"strings"
"github.com/wailsapp/wails/v2/pkg/options" "github.com/wailsapp/wails/v2/pkg/options"
"github.com/wailsapp/wails/v2/pkg/runtime" "github.com/wailsapp/wails/v2/pkg/runtime"
"strings"
"github.com/tidwall/gjson" "github.com/tidwall/gjson"
) )
@@ -43,3 +45,14 @@ func (a *App) onSecondInstanceLaunch(secondInstanceData options.SecondInstanceDa
runtime.Show(*wailsContext) runtime.Show(*wailsContext)
go runtime.EventsEmit(*wailsContext, "launchArgs", secondInstanceArgs) 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)
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

26
build/install_linux.sh Executable file
View 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."

View File

@@ -1,7 +1,10 @@
<script lang="ts"> <script lang="ts">
import { import {
aniListAnime, aniListAnime,
aniListLoggedIn,
GetAnimeSingleItem, GetAnimeSingleItem,
malLoggedIn,
simklLoggedIn,
} from "./helperModules/GlobalVariablesAndHelperFunctions.svelte"; } from "./helperModules/GlobalVariablesAndHelperFunctions.svelte";
import {onMount} from "svelte"; import {onMount} from "svelte";
import Router from "svelte-spa-router" import Router from "svelte-spa-router"
@@ -16,9 +19,17 @@
import {AniListGetSingleAnimeDefaultData} from "./helperDefaults/AniListGetSingleAnime"; import {AniListGetSingleAnimeDefaultData} from "./helperDefaults/AniListGetSingleAnime";
onMount(async () => { onMount(async () => {
await CheckIfAniListLoggedInAndLoadWatchList() let isAniListLoggedIn: boolean
await CheckIfMALLoggedInAndSetUser() let isMALLoggedIn: boolean
await CheckIfSimklLoggedInAndSetUser() 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> </script>
@@ -27,17 +38,7 @@
'/': Home, '/': Home,
'/anime/:id': wrap({ '/anime/:id': wrap({
asyncComponent: () => import('./routes/AnimeRoutePage.svelte'), asyncComponent: () => import('./routes/AnimeRoutePage.svelte'),
conditions: [ conditions: [async () => await CheckIfAniListLoggedIn()],
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
},
],
loadingComponent: Spinner loadingComponent: Spinner
}), }),
// '*': "Not Found" // '*': "Not Found"

View File

@@ -1,80 +1,93 @@
export interface AniListCurrentUserWatchList { export interface AniListCurrentUserWatchList {
data: { data: {
Page: { Page: {
pageInfo: { pageInfo: {
total: number total: number;
perPage: number perPage: number;
currentPage: number currentPage: number;
lastPage: number lastPage: number;
hasNextPage: boolean hasNextPage: boolean;
}, };
mediaList: MediaList[] mediaList: MediaList[];
} };
} };
} }
export interface AniListGetSingleAnime { export interface AniListGetSingleAnime {
data: { data: {
MediaList: MediaList MediaList: MediaList;
} };
} }
export interface MediaList { export interface MediaList {
id: number id: number;
mediaId: number mediaId: number;
userId: number userId: number;
media: { media: {
id: number id: number;
idMal: number idMal: number;
title: { title: {
romaji: string romaji: string;
english?: string english?: string;
native: string native: string;
} };
description: string description: string;
coverImage: { coverImage: {
large: string large: string;
} };
season: string season: string;
seasonYear: number seasonYear: number;
status: string status: string;
episodes?: number episodes?: number;
nextAiringEpisode?: { nextAiringEpisode?: {
airingAt: number airingAt: number;
timeUntilAiring: number timeUntilAiring: number;
episode: number episode: number;
} };
} tags: [
status: string {
startedAt: { id: number;
year: number name: string;
month: number description: string;
day: number rank: number;
} isMediaSpoiler: boolean;
completedAt: { isAdult: boolean;
year?: number },
month?: number ];
day?: number isAdult: boolean;
} };
notes?: string status: string;
progress: number startedAt: {
score: number year: number;
repeat: number month: number;
user: { day: number;
id: number };
name: string completedAt: {
avatar: { year?: number;
large: string month?: number;
medium: string day?: number;
} };
statistics: { notes?: string;
anime: { progress: number;
count: number score: number;
statuses: [{ repeat: number;
status: string user: {
count: number id: number;
}] name: string;
} avatar: {
} large: string;
} medium: string;
};
statistics: {
anime: {
count: number;
statuses: [
{
status: string;
count: number;
},
];
};
};
};
} }

View File

@@ -8,7 +8,6 @@
simklLoggedIn, simklLoggedIn,
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte"; } from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
import { push } from "svelte-spa-router"; import { push } from "svelte-spa-router";
import { Button } from "flowbite-svelte";
import type { AniListGetSingleAnime } from "../anilist/types/AniListCurrentUserWatchListType"; import type { AniListGetSingleAnime } from "../anilist/types/AniListCurrentUserWatchListType";
import Rating from "./Rating.svelte"; import Rating from "./Rating.svelte";
import { import {
@@ -42,6 +41,7 @@
import { AddAnimeServiceToTable } from "../helperModules/AddAnimeServiceToTable.svelte"; import { AddAnimeServiceToTable } from "../helperModules/AddAnimeServiceToTable.svelte";
import { CheckIfAniListLoggedInAndLoadWatchList } from "../helperModules/CheckIfAniListLoggedInAndLoadWatchList.svelte"; import { CheckIfAniListLoggedInAndLoadWatchList } from "../helperModules/CheckIfAniListLoggedInAndLoadWatchList.svelte";
import Datepicker from "./Datepicker.svelte"; import Datepicker from "./Datepicker.svelte";
import {Badge, Tooltip} from "flowbite-svelte";
const re = /^([0-9]{4})-([0-9]{2})-([0-9]{2})/; const re = /^([0-9]{4})-([0-9]{2})-([0-9]{2})/;
let isAniListLoggedIn: boolean; let isAniListLoggedIn: boolean;
@@ -213,9 +213,8 @@
}; };
await AniListUpdateEntry(body).then( await AniListUpdateEntry(body).then(
(value: AniListGetSingleAnime) => { (value: AniListGetSingleAnime) => {
/* TODO in future when you inevitably add tags to typescript, until Anilist fixes the api bug value.data.MediaList.media.tags =
where tags break the SaveMediaListEntry return, you'll want to use this delete line currentAniListAnime.data.MediaList.media.tags;
delete value.data.MediaList.media.tags */
aniListAnime.update((newValue) => { aniListAnime.update((newValue) => {
newValue = value; newValue = value;
return newValue; return newValue;
@@ -448,7 +447,6 @@
</script> </script>
<form on:submit|preventDefault={handleSubmit} class="container pt-3 pb-10"> <form on:submit|preventDefault={handleSubmit} class="container pt-3 pb-10">
{console.log(startingAnilistStatusOption)}
<h1 class="text-white font-bold text-left text-xl pb-3"> <h1 class="text-white font-bold text-left text-xl pb-3">
{title} {title}
</h1> </h1>
@@ -553,6 +551,17 @@
statusOptions[2]; statusOptions[2];
completedAtDate = new Date(); completedAtDate = new Date();
} }
if (
currentAniListAnime.data.MediaList
.progress -
1 ===
0
) {
startingAnilistStatusOption =
statusOptions[0];
if (startedAtDate === null)
startedAtDate = new Date();
}
}} }}
class="bg-gray-700 hover:bg-gray-600 border-gray-600 border rounded-e-lg p-3 h-11 focus:ring-gray-700 focus:ring-2 focus:outline-none" class="bg-gray-700 hover:bg-gray-600 border-gray-600 border rounded-e-lg p-3 h-11 focus:ring-gray-700 focus:ring-2 focus:outline-none"
> >
@@ -816,8 +825,29 @@
</div> </div>
</div> </div>
<div> <div class="flex m-5">
<h3 class="text-2xl">Summary</h3> <div>
<p>{@html currentAniListAnime.data.MediaList.media.description}</p> <h3 class="text-2xl">Tags</h3>
<div class="mt-2">
{#each currentAniListAnime.data.MediaList.media.tags as tag}
<div>
<Badge large border color="blue" class="m-1 w-52">
<div>
{tag.name} -
<span class="text-xs">{tag.rank}%</span>
</div>
</Badge>
<Tooltip>{tag.description}</Tooltip>
</div>
{/each}
</div>
</div>
<div class="ml-5">
<h3 class="text-2xl">Summary</h3>
<p class="rounded border border-gray-700 p-2 mt-2">
{@html currentAniListAnime.data.MediaList.media.description}
</p>
</div>
</div> </div>
</form> </form>

View File

@@ -13,11 +13,12 @@
loginToSimkl, loginToSimkl,
logoutOfAniList, logoutOfAniList,
logoutOfMAL, logoutOfMAL,
logoutOfSimkl, logoutOfSimkl
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte"; } from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
import * as runtime from "../../wailsjs/runtime"; import * as runtime from "../../wailsjs/runtime";
import type {MyAnimeListUser} from "../mal/types/MALTypes"; import type {MyAnimeListUser} from "../mal/types/MALTypes";
import type {SimklUser} from "../simkl/types/simklTypes"; import type {SimklUser} from "../simkl/types/simklTypes";
import { ShowVersion } from "../../wailsjs/go/main/App";
let currentAniListUser: AniListUser; let currentAniListUser: AniListUser;
let currentMALUser: MyAnimeListUser; let currentMALUser: MyAnimeListUser;
@@ -36,6 +37,20 @@
function dropdownUser(): void { function dropdownUser(): void {
let dropdown = document.querySelector("#userDropdown"); let dropdown = document.querySelector("#userDropdown");
dropdown.classList.toggle("hidden"); dropdown.classList.toggle("hidden");
if (!dropdown.classList.contains("hidden")) {
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> </script>
@@ -77,7 +92,10 @@
</li> </li>
{:else} {:else}
<li> <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"> 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 <span class="maple-font text-lg mr-4">A</span>Login to AniList
</button> </button>
@@ -94,7 +112,10 @@
</li> </li>
{:else} {:else}
<li> <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"> 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 <span class="maple-font text-lg mr-4">M</span>Login to MyAnimeList
</button> </button>
@@ -111,7 +132,10 @@
</li> </li>
{:else} {:else}
<li> <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"> 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 <span class="maple-font text-lg mr-4">S</span>Login to Simkl
</button> </button>
@@ -119,6 +143,15 @@
{/if} {/if}
</ul> </ul>
<div class="py-2"> <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 <button
on:click={() => runtime.Quit()} on:click={() => runtime.Quit()}
class="block px-4 py-2 w-full text-sm hover:bg-gray-600 text-gray-200 over:text-white" class="block px-4 py-2 w-full text-sm hover:bg-gray-600 text-gray-200 over:text-white"

View File

@@ -1,9 +1,23 @@
<script lang="ts"> <script lang="ts">
import { aniListAnime, GetAnimeSingleItem } from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
import Anime from "../helperComponents/Anime.svelte" import Anime from "../helperComponents/Anime.svelte"
import { AniListGetSingleAnimeDefaultData } from "../helperDefaults/AniListGetSingleAnime";
import Spinner from "../helperComponents/Spinner.svelte";
export let params: Record<string, string> 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> </script>
{#key params.id} {#key params.id}
<Anime /> {#await loadPromise}
<Spinner />
{:then _}
<Anime />
{/await}
{/key} {/key}

View File

@@ -42,6 +42,8 @@ export function MyAnimeListLogin():Promise<void>;
export function MyAnimeListUpdate(arg1:main.MALAnime,arg2:main.MALUploadStatus):Promise<main.MalListStatus>; 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 SimklGetUserWatchlist():Promise<main.SimklWatchListType>;
export function SimklLogin():Promise<void>; export function SimklLogin():Promise<void>;

View File

@@ -82,6 +82,10 @@ export function MyAnimeListUpdate(arg1, arg2) {
return window['go']['main']['App']['MyAnimeListUpdate'](arg1, arg2); return window['go']['main']['App']['MyAnimeListUpdate'](arg1, arg2);
} }
export function ShowVersion() {
return window['go']['main']['App']['ShowVersion']();
}
export function SimklGetUserWatchlist() { export function SimklGetUserWatchlist() {
return window['go']['main']['App']['SimklGetUserWatchlist'](); return window['go']['main']['App']['SimklGetUserWatchlist']();
} }

View File

@@ -12,6 +12,6 @@
}, },
"info": { "info": {
"productName": "AniTrack", "productName": "AniTrack",
"productVersion": "0.1.8" "productVersion": "0.5.0"
} }
} }