Compare commits

...

10 Commits

25 changed files with 1026 additions and 367 deletions

View File

@ -35,9 +35,11 @@ func AniListQuery(body interface{}, login bool) (json.RawMessage, string) {
return returnedBody, ""
}
func (a *App) GetAniListItem(aniId int) any {
func (a *App) GetAniListItem(aniId int) AniListGetSingleAnime {
var user = a.GetAniListLoggedInUserId()
type Variables struct {
ID int `json:"id"`
UserId int `json:"userId"`
MediaId int `json:"mediaId"`
ListType string `json:"listType"`
}
body := struct {
@ -45,43 +47,78 @@ func (a *App) GetAniListItem(aniId int) any {
Variables Variables `json:"variables"`
}{
Query: `
query ($id: Int!, $listType: MediaType) {
Media (id: $id, type: $listType) {
query($userId: Int, $mediaId: Int, $listType: MediaType) {
MediaList(mediaId: $mediaId, userId: $userId, type: $listType) {
id
idMal
title {
mediaId
userId
media {
id
idMal
title {
romaji
english
}
description
coverImage {
medium
native
}
description
coverImage {
large
extraLarge
color
}
season
seasonYear
status
episodes
nextAiringEpisode {
airingAt
timeUntilAiring
episode
}
}
tags {
id
name
description
category
rank
isGeneralSpoiler
isMediaSpoiler
isAdult
status
startedAt{
year
month
day
}
completedAt{
year
month
day
}
notes
progress
score
repeat
user {
id
name
avatar {
large
medium
}
statistics {
anime {
count
statuses {
status
count
}
}
}
}
}
}
}
`,
Variables: Variables{
ID: aniId,
MediaId: aniId,
UserId: user.Data.Viewer.ID,
ListType: "ANIME",
},
}
returnedBody, _ := AniListQuery(body, false)
var post interface{}
var post AniListGetSingleAnime
err := json.Unmarshal(returnedBody, &post)
if err != nil {
log.Printf("Failed at unmarshal, %s\n", err)
@ -110,18 +147,28 @@ func (a *App) AniListSearch(query string) any {
perPage
}
media (search: $search, type: $listType) {
id
idMal
title {
romaji
english
}
coverImage {
extraLarge
color
}
id
idMal
title {
romaji
english
native
}
description
coverImage {
large
}
season
seasonYear
status
episodes
nextAiringEpisode{
airingAt
timeUntilAiring
episode
}
}
}
}
}
`,
Variables: Variables{
@ -198,6 +245,16 @@ func (a *App) GetAniListUserWatchingList(page int, perPage int, sort string) Ani
}
}
status
startedAt {
year
month
day
}
completedAt {
year
month
day
}
notes
progress
score
@ -257,3 +314,111 @@ func (a *App) GetAniListUserWatchingList(page int, perPage int, sort string) Ani
return post
}
func (a *App) AniListUpdateEntry(
mediaId int,
progress string,
status string,
score float64,
repeat int,
notes string,
startYear int,
startMonth int,
startDay int,
completeYear int,
completeMonth int,
completeDay int,
) interface{} {
type StartedAt struct {
Year int `json:"year"`
Month int `json:"month"`
Day int `json:"day"`
}
type CompletedAt struct {
Year int `json:"year"`
Month int `json:"month"`
Day int `json:"day"`
}
type Variables struct {
MediaId int `json:"mediaId"`
Progress string `json:"progress"`
Status string `json:"status"`
Score float64 `json:"score"`
Repeat int `json:"repeat"`
Notes string `json:"notes"`
StartedAt StartedAt `json:"startedAt"`
CompletedAt CompletedAt `json:"completedAt"`
}
body := struct {
Mutation string `json:"mutation"`
Variables Variables `json:"variables"`
}{
Mutation: `
mutation(
$mediaId:Int,
$progress:Int,
$status:MediaListStatus,
$score:Float,
$repeat:Int,
$notes:String,
$startedAt:FuzzyDateInput,
$completedAt:FuzzyDateInput,
){
SaveMediaListEntry(
mediaId:$mediaId,
progress:$progress,
status:$status,
score:$score,
repeat:$repeat,
notes:$notes,
startedAt:$startedAt
completedAt:$completedAt
){
mediaId
progress
status
score
repeat
notes
startedAt{
year
month
day
}
completedAt{
year
month
day
}
}
}
`,
Variables: Variables{
MediaId: mediaId,
Progress: progress,
Status: status,
Score: score,
Repeat: repeat,
Notes: notes,
StartedAt: StartedAt{
Year: startYear,
Month: startMonth,
Day: startDay,
},
CompletedAt: CompletedAt{
Year: completeYear,
Month: completeMonth,
Day: completeDay,
},
}}
returnedBody, _ := AniListQuery(body, true)
var post interface{}
err := json.Unmarshal(returnedBody, &post)
if err != nil {
log.Printf("Failed at unmarshal, %s\n", err)
}
return post
}

View File

@ -26,59 +26,67 @@ type AniListCurrentUserWatchList struct {
LastPage int `json:"lastPage"`
HasNextPage bool `json:"hasNextPage"`
} `json:"pageInfo"`
MediaList []struct {
ID int `json:"id"`
MediaID int `json:"mediaId"`
UserID int `json:"userId"`
Media 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"`
} `json:"media"`
Status string `json:"status"`
Notes string `json:"notes"`
Progress int `json:"progress"`
Score int `json:"score"`
Repeat int `json:"repeat"`
User struct {
ID int `json:"id"`
Name string `json:"name"`
Avatar struct {
Large string `json:"large"`
Medium string `json:"medium"`
} `json:"avatar"`
Statistics struct {
Anime struct {
Count int `json:"count"`
Statuses []struct {
Status string `json:"status"`
Count int `json:"count"`
} `json:"statuses"`
} `json:"anime"`
} `json:"statistics"`
} `json:"user"`
} `json:"mediaList"`
MediaList []MediaList `json:"mediaList"`
} `json:"Page"`
} `json:"data"`
}
type AniListGetSingleAnime struct {
Data struct {
MediaList MediaList `json:"MediaList"`
} `json:"data"`
}
type MediaList struct {
ID int `json:"id"`
MediaID int `json:"mediaId"`
UserID int `json:"userId"`
Media 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"`
} `json:"media"`
Status string `json:"status"`
Notes string `json:"notes"`
Progress int `json:"progress"`
Score int `json:"score"`
Repeat int `json:"repeat"`
User struct {
ID int `json:"id"`
Name string `json:"name"`
Avatar struct {
Large string `json:"large"`
Medium string `json:"medium"`
} `json:"avatar"`
Statistics struct {
Anime struct {
Count int `json:"count"`
Statuses []struct {
Status string `json:"status"`
Count int `json:"count"`
} `json:"statuses"`
} `json:"anime"`
} `json:"statistics"`
} `json:"user"`
}
var MediaListSort = struct {
MediaId string
MediaIdDesc string

View File

@ -1,58 +0,0 @@
meta {
name: AniList Item
type: graphql
seq: 2
}
post {
url: https://graphql.anilist.co
body: graphql
auth: none
}
headers {
Content-Type: "application/json"
Accept: "application/json"
}
body:graphql {
query ($id: Int, $listType: MediaType) {
Media(id: $id, type: $listType) {
id
idMal
title {
romaji
english
native
}
description
coverImage {
large
}
}
}
}
body:graphql:vars {
{
"id": 157371,
"listType": "ANIME"
}
}
docs {
Title
Image
Description
Episodes
Status
Season
External & Streaming Links
}

View File

@ -1,28 +0,0 @@
meta {
name: AniList MediaList User
type: http
seq: 5
}
post {
url: https://graphql.anilist.co
body: none
auth: none
}
docs {
Title
Image
Description
Episodes
Status
Season
External & Streaming Links
User Stuff Per Item
Status
Score
Episode Progress
Total Rewatches
Notes
}

View File

@ -0,0 +1,105 @@
meta {
name: AniList Item
type: graphql
seq: 1
}
post {
url: https://graphql.anilist.co
body: graphql
auth: none
}
headers {
Content-Type: "application/json"
Accept: "application/json"
}
body:graphql {
query($userId: Int, $mediaId: Int, $listType: MediaType) {
MediaList(mediaId: $mediaId, userId: $userId, type: $listType) {
id
mediaId
userId
media {
id
idMal
title {
romaji
english
native
}
description
coverImage {
large
}
season
seasonYear
status
episodes
nextAiringEpisode {
airingAt
timeUntilAiring
episode
}
}
status
startedAt{
year
month
day
}
completedAt{
year
month
day
}
notes
progress
score
repeat
user {
id
name
avatar {
large
medium
}
statistics {
anime {
count
statuses {
status
count
}
}
}
}
}
}
}
body:graphql:vars {
{
"userId": 413504,
"mediaId": 157371,
"listType": "ANIME"
}
}
docs {
Title
Image
Description
Episodes
Status
Season
External & Streaming Links
}

View File

@ -1,7 +1,7 @@
meta {
name: AniList MediaList User Query
type: graphql
seq: 6
seq: 4
}
post {

View File

@ -0,0 +1,75 @@
meta {
name: AniList Search
type: graphql
seq: 2
}
post {
url: https://graphql.anilist.co
body: graphql
auth: none
}
headers {
Content-Type: "application/json"
Accept: "application/json"
}
body:graphql {
query ($search: String!, $listType: MediaType) {
Page (page: 1, perPage: 100) {
pageInfo {
total
currentPage
lastPage
hasNextPage
perPage
}
media (search: $search, type: $listType) {
id
idMal
title {
romaji
english
native
}
description
coverImage {
large
}
season
seasonYear
status
episodes
nextAiringEpisode{
airingAt
timeUntilAiring
episode
}
}
}
}
}
body:graphql:vars {
{
"search": "frieren",
"listType": "ANIME"
}
}
docs {
Title
Image
Description
Episodes
Status
Season
External & Streaming Links
}

View File

@ -0,0 +1,125 @@
meta {
name: GetAniListUserWatchingList
type: graphql
seq: 3
}
post {
url: https://graphql.anilist.co
body: graphql
auth: none
}
headers {
Content-Type: "application/json"
Accept: "application/json"
}
body:graphql {
# Write your query or mutation here
query (
$page: Int
$perPage: Int
$userId: Int
$listType: MediaType
$status: MediaListStatus
$sort: [MediaListSort]
) {
Page(page: $page, perPage: $perPage) {
pageInfo {
total
perPage
currentPage
lastPage
hasNextPage
}
mediaList(userId: $userId, type: $listType, status: $status, sort: $sort) {
id
mediaId
userId
media {
id
idMal
title {
romaji
english
native
}
description
coverImage {
large
}
season
seasonYear
status
episodes
nextAiringEpisode {
airingAt
timeUntilAiring
episode
}
}
status
startedAt {
year
month
day
}
completedAt {
year
month
day
}
notes
progress
score
repeat
user {
id
name
avatar {
large
medium
}
statistics {
anime {
count
statuses {
status
count
}
}
}
}
}
}
}
}
body:graphql:vars {
{
"page": 1,
"perPage": 20,
"userId": 413504,
"listType": "ANIME",
"status": "CURRENT",
"sort": "UPDATED_TIME_DESC"
}
}
docs {
Title
Image
Description
Episodes
Status
Season
External & Streaming Links
}

View File

@ -1,7 +1,7 @@
meta {
name: GetAuthorizationToken
type: http
seq: 4
seq: 2
}
post {

View File

@ -1,7 +1,7 @@
meta {
name: Load AniList Oauth Page
type: http
seq: 3
seq: 1
}
get {

View File

@ -0,0 +1,48 @@
meta {
name: AniList Change Episode Watched
type: graphql
seq: 1
}
post {
url: https://graphql.anilist.co
body: graphql
auth: none
}
headers {
Authorization: Bearer {{process.env.ANILIST_ACCESS_TOKEN}}
Content-Type: "application/json"
Accept: "application/json"
}
body:graphql {
mutation($mediaId:Int, $progress:Int, $status:MediaListStatus){
SaveMediaListEntry(mediaId:$mediaId, progress:$progress, status:$status){
mediaId
progress
status
score
repeat
notes
startedAt{
year
month
day
}
completedAt{
year
month
day
}
}
}
}
body:graphql:vars {
{
"mediaId": 1,
"progress": 26,
"status":"COMPLETED"
}
}

View File

@ -0,0 +1,33 @@
meta {
name: AniList Change Status
type: graphql
seq: 2
}
post {
url: https://graphql.anilist.co
body: graphql
auth: none
}
headers {
Authorization: Bearer {{process.env.ANILIST_ACCESS_TOKEN}}
Content-Type: "application/json"
Accept: "application/json"
}
body:graphql {
mutation($mediaId:Int, $status:MediaListStatus){
SaveMediaListEntry(mediaId:$mediaId, status:$status){
id
status
}
}
}
body:graphql:vars {
{
"mediaId": 1,
"status": "CURRENT"
}
}

View File

@ -17,12 +17,15 @@
"svelte": "^3.49.0",
"svelte-check": "^2.8.0",
"svelte-preprocess": "^4.10.7",
"tailwind-merge": "^2.4.0",
"tailwindcss": "^3.4.6",
"tslib": "^2.4.0",
"typescript": "^4.6.4",
"vite": "^3.0.7"
},
"dependencies": {
"@tailwindcss/aspect-ratio": "^0.4.2"
"flowbite": "^2.4.1",
"flowbite-svelte": "^0.46.15",
"@popperjs/core": "^2.11.8"
}
}

View File

@ -1,28 +1,32 @@
<script lang="ts">
import {
AniListSearch,
GetAniListItem,
GetAniListLoggedInUserId,
GetAniListUserWatchingList
} from "../wailsjs/go/main/App";
import {type AniListItem, type AniSearchList, MediaListSort} from "./anilist/types/AniListTypes";
import {type AniSearchList, MediaListSort} from "./anilist/types/AniListTypes";
import type {AniListCurrentUserWatchList} from "./anilist/types/AniListCurrentUserWatchListType"
import Header from "./Header.svelte";
import {Button, Modal} from "flowbite-svelte";
import type {AniListGetSingleAnime} from "./anilist/types/AniListCurrentUserWatchListType.js";
import {GetAniListItem} from "../wailsjs/go/main/App";
import ChangeDataDialogue from "./ChangeDataDialogue.svelte";
let aniId = "157371"
let aniSearch = ""
let aniListItem: AniListItem
let aniListSearch: AniSearchList
let aniListSearchActive = false
let aniListLoggedIn = false
let aniListWatchlist: AniListCurrentUserWatchList
let page = 1
let perPage = 20
function getAniListitem(): void {
GetAniListItem(Number(aniId)).then(result => aniListItem = result)
}
let anilistModal = false;
let anime: AniListGetSingleAnime
let title: string
function runAniListSearch(): void {
AniListSearch(aniSearch).then(result => aniListSearch = result)
AniListSearch(aniSearch).then(result => {
aniListSearch = result
aniListSearchActive = true
})
}
function anilistGetUserWatchlist(): void {
@ -31,14 +35,50 @@
aniListLoggedIn = true
})
}
function GetAniListSingleItemAndOpenModal(aniId: number): void {
GetAniListItem(aniId).then(result => {
anime = result
title = anime.data.MediaList.media.title.english === "" ?
anime.data.MediaList.media.title.romaji :
anime.data.MediaList.media.title.english
anilistModal = true
})
}
</script>
<Header />
<main>
<div class="input-box" id="aniSearch">
<input autocomplete="off" bind:value={aniSearch} class="input" id="aniSearchInput" type="text"/>
<button class="btn" on:click={runAniListSearch}>Search AniList</button>
<input autocomplete="off" bind:value={aniSearch} class="input text-black" id="aniSearchInput" type="text"/>
<button class="text-gray-800 dark:text-white hover:bg-gray-50 focus:ring-4 focus:ring-gray-300 font-medium rounded-lg text-sm px-4 lg:px-5 py-2 lg:py-2.5 mr-2 dark:hover:bg-gray-700 focus:outline-none dark:focus:ring-gray-800" on:click={runAniListSearch}>Search AniList</button>
</div>
{#if aniListSearchActive}
<div class="mx-auto max-w-2xl px-4 py-16 sm:px-6 sm:py-24 lg:max-w-7xl lg:px-8">
<h1>Your Search Results</h1>
<div class="grid grid-cols-1 gap-x-6 gap-y-10 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 xl:gap-x-8">
{#each aniListSearch.data.Page.media as media}
<button on:click={() => GetAniListSingleItemAndOpenModal(media.id)} class="group">
<!-- <div class="aspect-h-1 aspect-w-1 w-full overflow-hidden rounded-lg bg-gray-200 xl:aspect-h-8 xl:aspect-w-7">-->
<div class="justify-center rounded-lg bg-gray-200">
<img src={media.coverImage.large} alt="anime cover"/>
</div>
<h3 class="mt-4 text-sm text-white-700">{
media.title.english === "" ?
media.title.romaji :
media.title.english
}</h3>
</button>
{/each}
</div>
</div>
{/if}
<button class="btn" on:click={anilistGetUserWatchlist}>Login to AniList</button>
{#if aniListLoggedIn}
<div>You are logged in {aniListWatchlist.data.Page.mediaList[0].user.name}!</div>
@ -50,7 +90,7 @@
<div class="grid grid-cols-1 gap-x-6 gap-y-10 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 xl:gap-x-8">
{#each aniListWatchlist.data.Page.mediaList as media}
<a href="#" class="group">
<button on:click={() => GetAniListSingleItemAndOpenModal(media.media.id)} class="group">
<!-- <div class="aspect-h-1 aspect-w-1 w-full overflow-hidden rounded-lg bg-gray-200 xl:aspect-h-8 xl:aspect-w-7">-->
<div class="justify-center rounded-lg bg-gray-200">
<img src={media.media.coverImage.large} alt="anime cover"/>
@ -67,68 +107,13 @@
<p class="mt-1 text-lg font-medium text-white-900">Total
Episodes: {media.media.episodes}</p>
{/if}
</a>
</button>
<!-- <Button on:click={() => (anilistModal = true)}>Default modal</Button>-->
{/each}
</div>
</div>
{/if}
<Modal title={title} bind:open={anilistModal} autoclose>
<ChangeDataDialogue anime={anime} />
</Modal>
</main>
<style>
#logo {
display: block;
width: 50%;
height: 50%;
margin: auto;
padding: 10% 0 0;
background-position: center;
background-repeat: no-repeat;
background-size: 100% 100%;
background-origin: content-box;
}
.result {
height: 20px;
line-height: 20px;
margin: 1.5rem auto;
}
.input-box .btn {
/*width: 60px;*/
height: 30px;
line-height: 30px;
border-radius: 3px;
border: none;
margin: 0 0 0 20px;
padding: 0 8px;
cursor: pointer;
}
.input-box .btn:hover {
background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%);
color: #333333;
}
.input-box .input {
border: none;
border-radius: 3px;
outline: none;
height: 30px;
line-height: 30px;
padding: 0 10px;
background-color: rgba(240, 240, 240, 1);
-webkit-font-smoothing: antialiased;
}
.input-box .input:hover {
border: none;
background-color: rgba(255, 255, 255, 1);
}
.input-box .input:focus {
border: none;
background-color: rgba(255, 255, 255, 1);
}
</style>

View File

@ -0,0 +1,32 @@
<script lang="ts">
import type {AniListGetSingleAnime} from "./anilist/types/AniListCurrentUserWatchListType";
import {Button} from "flowbite-svelte";
export let anime: AniListGetSingleAnime
</script>
<div>
<div>
<form class="max-w-sm mx-auto">
<label for="countries" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Select your
country</label>
<select id="countries"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<option>United States</option>
<option>Canada</option>
<option>France</option>
<option>Germany</option>
</select>
</form>
</div>
<footer class="bg-white rounded-lg shadow m-4 dark:bg-gray-800">
<div class="w-full mx-auto max-w-screen-xl p-4 md:flex md:items-center md:justify-end">
<Button class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800" on:click={() => alert('Handle "success"')}>Sync Changes</Button>
<Button class="text-gray-900 bg-white border border-gray-300 focus:outline-none hover:bg-gray-100 focus:ring-4 focus:ring-gray-100 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-gray-800 dark:text-white dark:border-gray-600 dark:hover:bg-gray-700 dark:hover:border-gray-600 dark:focus:ring-gray-700">Cancel</Button>
</div>
</footer>
</div>

View File

@ -0,0 +1,74 @@
<script lang="ts">
import logo from "./assets/images/AniTrackLogo.svg"
import type {AniSearchList} from "./anilist/types/AniListTypes.js";
import {AniListSearch} from "../wailsjs/go/main/App.js";
let aniSearch = ""
let aniListSearch: AniSearchList
let aniListSearchActive = false
function runAniListSearch(): void {
AniListSearch(aniSearch).then(result => {
aniListSearch = result
aniListSearchActive = true
})
}
</script>
<header class="mb-2">
<nav class="bg-white border-gray-200 px-4 lg:px-6 py-2.5 dark:bg-gray-800">
<div class="flex flex-wrap justify-between items-center mx-auto max-w-screen-xl">
<a href="#" class="flex items-center">
<img src={logo} class="mr-3 h-6 sm:h-9" alt="AniTrack Logo" />
<!-- <span class="self-center text-xl font-semibold whitespace-nowrap dark:text-white">AniTrack</span>-->
</a>
<div class="flex items-center lg:order-2">
<form class="max-w-md mx-auto">
<div class="flex">
<label for="anime-search" class="mb-2 text-sm font-medium text-gray-900 sr-only dark:text-white">Find Anime</label>
<div class="relative w-full">
<input type="search" id="anime-search" class="rounded-s-lg block p-2.5 w-full z-20 text-sm text-gray-900 bg-gray-50 rounded-e-lg border-s-gray-50 border-s-2 border border-gray-300 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-s-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:border-blue-500" placeholder="Search for Anime" required />
<button type="submit" class="absolute top-0 end-0 h-full p-2.5 text-sm font-medium text-white bg-blue-700 rounded-e-lg border border-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
<svg class="w-4 h-4" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"/>
</svg>
<span class="sr-only">Search</span>
</button>
</div>
</div>
</form>
<a href="#" class="text-gray-800 dark:text-white hover:bg-gray-50 focus:ring-4 focus:ring-gray-300 font-medium rounded-lg text-sm px-4 lg:px-5 py-2 lg:py-2.5 mr-2 dark:hover:bg-gray-700 focus:outline-none dark:focus:ring-gray-800">Log in AniList</a>
<!-- <a href="#" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-4 lg:px-5 py-2 lg:py-2.5 mr-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">Get started</a>-->
<button data-collapse-toggle="mobile-menu-2" type="button" class="inline-flex items-center p-2 ml-1 text-sm text-gray-500 rounded-lg lg:hidden hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600" aria-controls="mobile-menu-2" aria-expanded="false">
<span class="sr-only">Open main menu</span>
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" clip-rule="evenodd"></path></svg>
<svg class="hidden w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
</button>
</div>
<div class="hidden justify-between items-center w-full lg:flex lg:w-auto lg:order-1" id="mobile-menu-2">
<ul class="flex flex-col mt-4 font-medium lg:flex-row lg:space-x-8 lg:mt-0">
<li>
<a href="#" class="block py-2 pr-4 pl-3 text-white rounded bg-blue-700 lg:bg-transparent lg:text-blue-700 lg:p-0 dark:text-white" aria-current="page">Home</a>
</li>
<!-- <li>-->
<!-- <a href="#" class="block py-2 pr-4 pl-3 text-gray-700 border-b border-gray-100 hover:bg-gray-50 lg:hover:bg-transparent lg:border-0 lg:hover:text-blue-700 lg:p-0 dark:text-gray-400 lg:dark:hover:text-white dark:hover:bg-gray-700 dark:hover:text-white lg:dark:hover:bg-transparent dark:border-gray-700">Company</a>-->
<!-- </li>-->
<!-- <li>-->
<!-- <a href="#" class="block py-2 pr-4 pl-3 text-gray-700 border-b border-gray-100 hover:bg-gray-50 lg:hover:bg-transparent lg:border-0 lg:hover:text-blue-700 lg:p-0 dark:text-gray-400 lg:dark:hover:text-white dark:hover:bg-gray-700 dark:hover:text-white lg:dark:hover:bg-transparent dark:border-gray-700">Marketplace</a>-->
<!-- </li>-->
<!-- <li>-->
<!-- <a href="#" class="block py-2 pr-4 pl-3 text-gray-700 border-b border-gray-100 hover:bg-gray-50 lg:hover:bg-transparent lg:border-0 lg:hover:text-blue-700 lg:p-0 dark:text-gray-400 lg:dark:hover:text-white dark:hover:bg-gray-700 dark:hover:text-white lg:dark:hover:bg-transparent dark:border-gray-700">Features</a>-->
<!-- </li>-->
<!-- <li>-->
<!-- <a href="#" class="block py-2 pr-4 pl-3 text-gray-700 border-b border-gray-100 hover:bg-gray-50 lg:hover:bg-transparent lg:border-0 lg:hover:text-blue-700 lg:p-0 dark:text-gray-400 lg:dark:hover:text-white dark:hover:bg-gray-700 dark:hover:text-white lg:dark:hover:bg-transparent dark:border-gray-700">Team</a>-->
<!-- </li>-->
<!-- <li>-->
<!-- <a href="#" class="block py-2 pr-4 pl-3 text-gray-700 border-b border-gray-100 hover:bg-gray-50 lg:hover:bg-transparent lg:border-0 lg:hover:text-blue-700 lg:p-0 dark:text-gray-400 lg:dark:hover:text-white dark:hover:bg-gray-700 dark:hover:text-white lg:dark:hover:bg-transparent dark:border-gray-700">Contact</a>-->
<!-- </li>-->
</ul>
</div>
</div>
</nav>
</header>

View File

@ -1,88 +1,70 @@
export interface AniListCurrentUserWatchList {
data: Data
data: {
Page: {
pageInfo: {
total: number
perPage: number
currentPage: number
lastPage: number
hasNextPage: boolean
},
mediaList: MediaList[]
}
}
}
export interface Data {
Page: Page
}
export interface Page {
pageInfo: PageInfo
mediaList: MediaList[]
}
export interface PageInfo {
total: number
perPage: number
currentPage: number
lastPage: number
hasNextPage: boolean
export interface AniListGetSingleAnime {
data: {
MediaList: MediaList
}
}
export interface MediaList {
id: number
mediaId: number
userId: number
media: Media
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
notes?: string
progress: number
score: number
repeat: number
user: User
}
export interface Media {
id: number
idMal: number
title: Title
description: string
coverImage: CoverImage
season: string
seasonYear: number
status: string
episodes?: number
nextAiringEpisode?: NextAiringEpisode
}
export interface Title {
romaji: string
english?: string
native: string
}
export interface CoverImage {
large: string
}
export interface NextAiringEpisode {
airingAt: number
timeUntilAiring: number
episode: number
}
export interface User {
id: number
name: string
avatar: Avatar
statistics: Statistics
}
export interface Avatar {
large: string
medium: string
}
export interface Statistics {
anime: Anime
}
export interface Anime {
count: number
statuses: Status[]
}
export interface Status {
status: string
count: number
}
user: {
id: number
name: string
avatar: {
large: string
medium: string
}
statistics: {
anime: {
count: number
statuses: [{
status: string
count: number
}]
}
}
}
}

View File

@ -1,33 +1,3 @@
export interface AniListItem {
data: {
Media: {
id: number,
idMal: number,
title: {
romaji: string,
english: string,
},
description: string,
coverImage: {
medium: string,
large: string,
extraLarge: string,
color: string,
},
tags: [{
id: number,
name: string,
description: string,
category: string,
rank: number,
isGeneralSpoiler: boolean,
isMediaSpoiler: boolean,
isAdult: boolean
}]
}
}
}
export interface AniSearchList {
data: {
Page: {
@ -39,17 +9,26 @@ export interface AniSearchList {
perPage: number
},
media: [{
id: number,
idMal: number,
id: number
idMal: number
title: {
romaji: string,
english: string,
native: string,
},
romaji: string
english?: string
native: string
}
description: string
coverImage: {
extraLarge: string,
color: string,
},
large: string
}
season: string
seasonYear: number
status: string
episodes?: number
nextAiringEpisode?: {
airingAt: number
timeUntilAiring: number
episode: number
}
}],
},
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

View File

@ -3,12 +3,22 @@ export default {
content: [
"./index.html",
"./src/**/*.{svelte,js,ts,jsx,tsx}",
"./node_modules/flowbite/**/*.{html,js,svelte,ts}",
"./node_modules/flowbite-svelte/**/*.{html,js,svelte,ts}",
],
theme: {
extend: {},
},
plugins: [
require('@tailwindcss/aspect-ratio'),
require('flowbite/plugin')
],
darkMode: 'media',
theme: {
extend: {
colors: {
// flowbite-svelte
primary: { 50: '#FFF5F2', 100: '#FFF1EE', 200: '#FFE4DE', 300: '#FFD5CC', 400: '#FFBCAD', 500: '#FE795D', 600: '#EF562F', 700: '#EB4F27', 800: '#CC4522', 900: '#A5371B'},
}
}
}
}

View File

@ -6,7 +6,9 @@ export function AniListLogin():Promise<void>;
export function AniListSearch(arg1:string):Promise<any>;
export function GetAniListItem(arg1:number):Promise<any>;
export function AniListUpdateEntry(arg1:number,arg2:string,arg3:string,arg4:number,arg5:number,arg6:string,arg7:number,arg8:number,arg9:number,arg10:number,arg11:number,arg12:number):Promise<any>;
export function GetAniListItem(arg1:number):Promise<main.AniListGetSingleAnime>;
export function GetAniListLoggedInUserId():Promise<main.AniListUser>;

View File

@ -10,6 +10,10 @@ export function AniListSearch(arg1) {
return window['go']['main']['App']['AniListSearch'](arg1);
}
export function AniListUpdateEntry(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12) {
return window['go']['main']['App']['AniListUpdateEntry'](arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12);
}
export function GetAniListItem(arg1) {
return window['go']['main']['App']['GetAniListItem'](arg1);
}

View File

@ -1,8 +1,7 @@
export namespace main {
export class AniListCurrentUserWatchList {
// Go type: struct { Page struct { PageInfo struct { Total int "json:\"total\""; PerPage int "json:\"perPage\""; CurrentPage int "json:\"currentPage\""; LastPage int "json:\"lastPage\""; HasNextPage bool "json:\"hasNextPage\"" } "json:\"pageInfo\""; MediaList []struct { ID int "json:\"id\""; MediaID int "json:\"mediaId\""; UserID int "json:\"userId\""; Media 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\"" } "json:\"media\""; Status string "json:\"status\""; Notes string "json:\"notes\""; Progress int "json:\"progress\""; Score int "json:\"score\""; Repeat int "json:\"repeat\""; User struct { ID int "json:\"id\""; Name string "json:\"name\""; Avatar struct { Large string "json:\"large\""; Medium string "json:\"medium\"" } "json:\"avatar\""; Statistics struct { Anime struct { Count int "json:\"count\""; Statuses []struct { Status string "json:\"status\""; Count int "json:\"count\"" } "json:\"statuses\"" } "json:\"anime\"" } "json:\"statistics\"" } "json:\"user\"" } "json:\"mediaList\"" } "json:\"Page\"" }
data: any;
data: struct { Page struct { PageInfo struct { Total int "json:\"total\""; PerPage int "json:\"perPage\""; CurrentPage int "json:\"currentPage\""; LastPage int "json:\"lastPage\""; HasNextPage bool "json:\"hasNextPage\"" } "json:\"pageInfo\""; MediaList []main.;
static createFrom(source: any = {}) {
return new AniListCurrentUserWatchList(source);
@ -31,6 +30,36 @@ export namespace main {
return a;
}
}
export class AniListGetSingleAnime {
data: struct { MediaList main.;
static createFrom(source: any = {}) {
return new AniListGetSingleAnime(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.data = this.convertValues(source["data"], Object);
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice && a.map) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
export class AniListUser {
// Go type: struct { Viewer struct { ID int "json:\"id\""; Name string "json:\"name\"" } "json:\"Viewer\"" }
data: any;
@ -62,6 +91,91 @@ export namespace main {
return a;
}
}
export class MediaList {
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\"" }
media: any;
status: string;
notes: string;
progress: number;
score: number;
repeat: number;
// Go type: struct { ID int "json:\"id\""; Name string "json:\"name\""; Avatar struct { Large string "json:\"large\""; Medium string "json:\"medium\"" } "json:\"avatar\""; Statistics struct { Anime struct { Count int "json:\"count\""; Statuses []struct { Status string "json:\"status\""; Count int "json:\"count\"" } "json:\"statuses\"" } "json:\"anime\"" } "json:\"statistics\"" }
user: any;
static createFrom(source: any = {}) {
return new MediaList(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
this.mediaId = source["mediaId"];
this.userId = source["userId"];
this.media = this.convertValues(source["media"], Object);
this.status = source["status"];
this.notes = source["notes"];
this.progress = source["progress"];
this.score = source["score"];
this.repeat = source["repeat"];
this.user = this.convertValues(source["user"], Object);
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice && a.map) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
}
export namespace struct { MediaList main {
export class {
MediaList: main.MediaList;
static createFrom(source: any = {}) {
return new (source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.MediaList = this.convertValues(source["MediaList"], main.MediaList);
}
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;
}
}
}