Compare commits
15 Commits
53b7368daf
...
aaf0f421f2
Author | SHA1 | Date | |
---|---|---|---|
aaf0f421f2 | |||
49e461d0ff | |||
6c4fdabb79 | |||
77289052e9 | |||
1dcf7ba5bd | |||
46e2a9156f | |||
1e90f68e70 | |||
8fc2b8b508 | |||
ce52ae20f5 | |||
94c0f97e95 | |||
79ed79c25e | |||
091ca82f69 | |||
eeb4a62f26 | |||
a0b00de615 | |||
6e9c2cf46e |
@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
@ -90,6 +91,15 @@ func (a *App) GetAniListItem(aniId int, login bool) AniListGetSingleAnime {
|
||||
timeUntilAiring
|
||||
episode
|
||||
}
|
||||
tags{
|
||||
id
|
||||
name
|
||||
description
|
||||
rank
|
||||
isMediaSpoiler
|
||||
isAdult
|
||||
}
|
||||
isAdult
|
||||
}
|
||||
status
|
||||
startedAt{
|
||||
@ -180,6 +190,15 @@ func (a *App) AniListSearch(query string) any {
|
||||
timeUntilAiring
|
||||
episode
|
||||
}
|
||||
tags{
|
||||
id
|
||||
name
|
||||
description
|
||||
rank
|
||||
isMediaSpoiler
|
||||
isAdult
|
||||
}
|
||||
isAdult
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -256,6 +275,15 @@ func (a *App) GetAniListUserWatchingList(page int, perPage int, sort string) Ani
|
||||
timeUntilAiring
|
||||
episode
|
||||
}
|
||||
tags{
|
||||
id
|
||||
name
|
||||
description
|
||||
rank
|
||||
isMediaSpoiler
|
||||
isAdult
|
||||
}
|
||||
isAdult
|
||||
}
|
||||
status
|
||||
startedAt {
|
||||
@ -330,7 +358,7 @@ func (a *App) GetAniListUserWatchingList(page int, perPage int, sort string) Ani
|
||||
|
||||
func (a *App) AniListUpdateEntry(
|
||||
mediaId int,
|
||||
progress string,
|
||||
progress int,
|
||||
status string,
|
||||
score float64,
|
||||
repeat int,
|
||||
@ -354,7 +382,7 @@ func (a *App) AniListUpdateEntry(
|
||||
}
|
||||
type Variables struct {
|
||||
MediaId int `json:"mediaId"`
|
||||
Progress string `json:"progress"`
|
||||
Progress int `json:"progress"`
|
||||
Status string `json:"status"`
|
||||
Score float64 `json:"score"`
|
||||
Repeat int `json:"repeat"`
|
||||
@ -363,10 +391,10 @@ func (a *App) AniListUpdateEntry(
|
||||
CompletedAt CompletedAt `json:"completedAt"`
|
||||
}
|
||||
body := struct {
|
||||
Mutation string `json:"mutation"`
|
||||
Query string `json:"query"`
|
||||
Variables Variables `json:"variables"`
|
||||
}{
|
||||
Mutation: `
|
||||
Query: `
|
||||
mutation(
|
||||
$mediaId:Int,
|
||||
$progress:Int,
|
||||
@ -427,6 +455,8 @@ func (a *App) AniListUpdateEntry(
|
||||
|
||||
returnedBody, _ := AniListQuery(body, true)
|
||||
|
||||
fmt.Println(string(returnedBody))
|
||||
|
||||
var post interface{}
|
||||
err := json.Unmarshal(returnedBody, &post)
|
||||
if err != nil {
|
||||
|
137
AniListTypes.go
137
AniListTypes.go
@ -68,6 +68,15 @@ type MediaList struct {
|
||||
TimeUntilAiring int `json:"timeUntilAiring"`
|
||||
Episode int `json:"episode"`
|
||||
} `json:"nextAiringEpisode"`
|
||||
Tags []struct {
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Rank int `json:"rank"`
|
||||
IsMediaSpoiler bool `json:"isMediaSpoiler"`
|
||||
IsAdult bool `json:"isAdult"`
|
||||
} `json:"tags"`
|
||||
IsAdult bool `json:"isAdult"`
|
||||
} `json:"media"`
|
||||
Status string `json:"status"`
|
||||
StartedAt struct {
|
||||
@ -82,7 +91,7 @@ type MediaList struct {
|
||||
} `json:"completedAt"`
|
||||
Notes string `json:"notes"`
|
||||
Progress int `json:"progress"`
|
||||
Score int `json:"score"`
|
||||
Score float64 `json:"score"`
|
||||
Repeat int `json:"repeat"`
|
||||
User struct {
|
||||
ID int `json:"id"`
|
||||
@ -103,66 +112,66 @@ type MediaList struct {
|
||||
} `json:"user"`
|
||||
}
|
||||
|
||||
var MediaListSort = struct {
|
||||
MediaId string
|
||||
MediaIdDesc string
|
||||
Score string
|
||||
ScoreDesc string
|
||||
Status string
|
||||
StatusDesc string
|
||||
Progress string
|
||||
ProgressDesc string
|
||||
ProgressVolumes string
|
||||
ProgressVolumesDesc string
|
||||
Repeat string
|
||||
RepeatDesc string
|
||||
Priority string
|
||||
PriorityDesc string
|
||||
StartedOn string
|
||||
StartedOnDesc string
|
||||
FinishedOn string
|
||||
FinishedOnDesc string
|
||||
AddedTime string
|
||||
AddedTimeDesc string
|
||||
UpdatedTime string
|
||||
UpdatedTimeDesc string
|
||||
MediaTitleRomaji string
|
||||
MediaTitleRomajiDesc string
|
||||
MediaTitleEnglish string
|
||||
MediaTitleEnglishDesc string
|
||||
MediaTitleNative string
|
||||
MediaTitleNativeDesc string
|
||||
MediaPopularity string
|
||||
MediaPopularityDesc string
|
||||
}{
|
||||
MediaId: "MEDIA_ID",
|
||||
MediaIdDesc: "MEDIA_ID_DESC",
|
||||
Score: "SCORE",
|
||||
ScoreDesc: "SCORE_DESC",
|
||||
Status: "STATUS",
|
||||
StatusDesc: "STATUS_DESC",
|
||||
Progress: "PROGRESS",
|
||||
ProgressDesc: "PROGRESS_DESC",
|
||||
ProgressVolumes: "PROGRESS_VOLUMES",
|
||||
ProgressVolumesDesc: "PROGRESS_VOLUMES_DESC",
|
||||
Repeat: "REPEAT",
|
||||
RepeatDesc: "REPEAT_DESC",
|
||||
Priority: "PRIORITY",
|
||||
PriorityDesc: "PRIORITY_DESC",
|
||||
StartedOn: "STARTED_ON",
|
||||
StartedOnDesc: "STARTED_ON_DESC",
|
||||
FinishedOn: "FINISHED_ON",
|
||||
FinishedOnDesc: "FINISHED_ON_DESC",
|
||||
AddedTime: "ADDED_TIME",
|
||||
AddedTimeDesc: "ADDED_TIME_DESC",
|
||||
UpdatedTime: "UPDATED_TIME",
|
||||
UpdatedTimeDesc: "UPDATED_TIME_DESC",
|
||||
MediaTitleRomaji: "MEDIA_TITLE_ROMAJI",
|
||||
MediaTitleRomajiDesc: "MEDIA_TITLE_ROMAJI_DESC",
|
||||
MediaTitleEnglish: "MEDIA_TITLE_ENGLISH",
|
||||
MediaTitleEnglishDesc: "MEDIA_TITLE_ENGLISH_DESC",
|
||||
MediaTitleNative: "MEDIA_TITLE_NATIVE",
|
||||
MediaTitleNativeDesc: "MEDIA_TITLE_NATIVE_DESC",
|
||||
MediaPopularity: "MEDIA_POPULARITY",
|
||||
MediaPopularityDesc: "MEDIA_POPULARITY_DESC",
|
||||
}
|
||||
//var MediaListSort = struct {
|
||||
// MediaId string
|
||||
// MediaIdDesc string
|
||||
// Score string
|
||||
// ScoreDesc string
|
||||
// Status string
|
||||
// StatusDesc string
|
||||
// Progress string
|
||||
// ProgressDesc string
|
||||
// ProgressVolumes string
|
||||
// ProgressVolumesDesc string
|
||||
// Repeat string
|
||||
// RepeatDesc string
|
||||
// Priority string
|
||||
// PriorityDesc string
|
||||
// StartedOn string
|
||||
// StartedOnDesc string
|
||||
// FinishedOn string
|
||||
// FinishedOnDesc string
|
||||
// AddedTime string
|
||||
// AddedTimeDesc string
|
||||
// UpdatedTime string
|
||||
// UpdatedTimeDesc string
|
||||
// MediaTitleRomaji string
|
||||
// MediaTitleRomajiDesc string
|
||||
// MediaTitleEnglish string
|
||||
// MediaTitleEnglishDesc string
|
||||
// MediaTitleNative string
|
||||
// MediaTitleNativeDesc string
|
||||
// MediaPopularity string
|
||||
// MediaPopularityDesc string
|
||||
//}{
|
||||
// MediaId: "MEDIA_ID",
|
||||
// MediaIdDesc: "MEDIA_ID_DESC",
|
||||
// Score: "SCORE",
|
||||
// ScoreDesc: "SCORE_DESC",
|
||||
// Status: "STATUS",
|
||||
// StatusDesc: "STATUS_DESC",
|
||||
// Progress: "PROGRESS",
|
||||
// ProgressDesc: "PROGRESS_DESC",
|
||||
// ProgressVolumes: "PROGRESS_VOLUMES",
|
||||
// ProgressVolumesDesc: "PROGRESS_VOLUMES_DESC",
|
||||
// Repeat: "REPEAT",
|
||||
// RepeatDesc: "REPEAT_DESC",
|
||||
// Priority: "PRIORITY",
|
||||
// PriorityDesc: "PRIORITY_DESC",
|
||||
// StartedOn: "STARTED_ON",
|
||||
// StartedOnDesc: "STARTED_ON_DESC",
|
||||
// FinishedOn: "FINISHED_ON",
|
||||
// FinishedOnDesc: "FINISHED_ON_DESC",
|
||||
// AddedTime: "ADDED_TIME",
|
||||
// AddedTimeDesc: "ADDED_TIME_DESC",
|
||||
// UpdatedTime: "UPDATED_TIME",
|
||||
// UpdatedTimeDesc: "UPDATED_TIME_DESC",
|
||||
// MediaTitleRomaji: "MEDIA_TITLE_ROMAJI",
|
||||
// MediaTitleRomajiDesc: "MEDIA_TITLE_ROMAJI_DESC",
|
||||
// MediaTitleEnglish: "MEDIA_TITLE_ENGLISH",
|
||||
// MediaTitleEnglishDesc: "MEDIA_TITLE_ENGLISH_DESC",
|
||||
// MediaTitleNative: "MEDIA_TITLE_NATIVE",
|
||||
// MediaTitleNativeDesc: "MEDIA_TITLE_NATIVE_DESC",
|
||||
// MediaPopularity: "MEDIA_POPULARITY",
|
||||
// MediaPopularityDesc: "MEDIA_POPULARITY_DESC",
|
||||
//}
|
||||
|
@ -30,7 +30,7 @@ type SimklWatchList struct {
|
||||
Anime []struct {
|
||||
LastWatchedAt string `json:"last_watched_at" ts_type:"last_watched_at"`
|
||||
Status string `json:"status" ts_type:"status"`
|
||||
UserRating int `json:"user_rating" ts_type:"user_rating"`
|
||||
UserRating float64 `json:"user_rating" ts_type:"user_rating"`
|
||||
LastWatched string `json:"last_watched" ts_type:"last_watched"`
|
||||
NextToWatch string `json:"next_to_watch" ts_type:"next_to_watch"`
|
||||
WatchedEpisodesCount int `json:"watched_episodes_count" ts_type:"watched_episodes_count"`
|
||||
|
@ -157,8 +157,6 @@ func getSimklAuthorizationToken(content string) SimklJWT {
|
||||
func (a *App) GetSimklLoggedInUser() SimklUser {
|
||||
a.SimklLogin()
|
||||
|
||||
fmt.Println("Token: ", simklJwt.AccessToken)
|
||||
|
||||
client := &http.Client{}
|
||||
|
||||
req, _ := http.NewRequest("POST", "https://api.simkl.com/users/settings", nil)
|
||||
|
@ -24,6 +24,14 @@ body:graphql {
|
||||
media {
|
||||
id
|
||||
idMal
|
||||
tags{
|
||||
id
|
||||
name
|
||||
description
|
||||
rank
|
||||
isMediaSpoiler
|
||||
isAdult
|
||||
}
|
||||
title {
|
||||
romaji
|
||||
english
|
||||
@ -42,6 +50,7 @@ body:graphql {
|
||||
timeUntilAiring
|
||||
episode
|
||||
}
|
||||
isAdult
|
||||
}
|
||||
status
|
||||
startedAt {
|
||||
|
@ -6,5 +6,6 @@ vars {
|
||||
}
|
||||
vars:secret [
|
||||
code,
|
||||
SIMKL_AUTH_TOKEN
|
||||
SIMKL_AUTH_TOKEN,
|
||||
ANILIST_ACCESS_TOKEN
|
||||
]
|
||||
|
@ -27,6 +27,7 @@
|
||||
"@ernane/svelte-star-rating": "^1.1.7",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"flowbite": "^2.4.1",
|
||||
"flowbite-svelte": "^0.46.15"
|
||||
"flowbite-svelte": "^0.46.15",
|
||||
"moment": "^2.30.1"
|
||||
}
|
||||
}
|
||||
|
@ -1,53 +1,39 @@
|
||||
<script lang="ts">
|
||||
import {anilistModal, GetAniListSingleItemAndOpenModal, title} from "./GetAniListSingleItemAndOpenModal.svelte";
|
||||
import {
|
||||
anilistModal,
|
||||
GetAniListSingleItemAndOpenModal,
|
||||
title,
|
||||
simklWatchList,
|
||||
aniListLoggedIn,
|
||||
simklLoggedIn
|
||||
} from "./GlobalVariablesAndHelperFunctions.svelte";
|
||||
import {
|
||||
CheckIfAniListLoggedIn,
|
||||
CheckIfSimklLoggedIn,
|
||||
GetAniListLoggedInUser,
|
||||
GetAniListUserWatchingList,
|
||||
GetSimklLoggedInUser, SimklGetUserWatchlist,
|
||||
GetSimklLoggedInUser,
|
||||
SimklGetUserWatchlist,
|
||||
} from "../wailsjs/go/main/App";
|
||||
import {type AniListUser, MediaListSort} from "./anilist/types/AniListTypes";
|
||||
import type {AniListCurrentUserWatchList} from "./anilist/types/AniListCurrentUserWatchListType"
|
||||
import Header from "./Header.svelte";
|
||||
import StarRatting from '@ernane/svelte-star-rating'
|
||||
import {Button, Modal} from "flowbite-svelte";
|
||||
|
||||
import {Button, Rating} from "flowbite-svelte";
|
||||
import {default as Modal} from "./modal/Modal.svelte"
|
||||
import ChangeDataDialogue from "./ChangeDataDialogue.svelte";
|
||||
import {onMount} from "svelte";
|
||||
import type {SimklUser, SimklWatchList} from "./simkl/types/simklTypes";
|
||||
import {writable} from "svelte/store";
|
||||
import type {SimklUser} from "./simkl/types/simklTypes";
|
||||
|
||||
const config = {
|
||||
readOnly: false,
|
||||
countStars: 5,
|
||||
range: {
|
||||
min: 0,
|
||||
max: 5,
|
||||
step: 0.5
|
||||
},
|
||||
score: 0.0,
|
||||
showScore: true,
|
||||
scoreFormat: function () {
|
||||
return `(${this.score.toFixed(1)}/${this.countStars})`
|
||||
},
|
||||
name: "",
|
||||
starConfig: {
|
||||
size: 20,
|
||||
fillColor: '#F9ED4F',
|
||||
strokeColor: "#e2c714",
|
||||
unfilledColor: '#FFF',
|
||||
strokeUnfilledColor: '#000'
|
||||
}
|
||||
}
|
||||
|
||||
let aniListLoggedIn = false
|
||||
let aniListPrimary = true
|
||||
let simklUser: SimklUser
|
||||
let simklLoggedIn = false
|
||||
let aniListUser: AniListUser
|
||||
let aniListWatchlist: AniListCurrentUserWatchList
|
||||
export let simklWatchList = writable({} as SimklWatchList)
|
||||
let isAniListLoggedIn: boolean
|
||||
let isSimklLoggedIn: boolean
|
||||
|
||||
aniListLoggedIn.subscribe((value) => isAniListLoggedIn = value)
|
||||
simklLoggedIn.subscribe((value) => isSimklLoggedIn = value)
|
||||
|
||||
let page = 1
|
||||
let perPage = 20
|
||||
const size = "xl"
|
||||
@ -60,10 +46,10 @@
|
||||
if (aniListPrimary) {
|
||||
GetAniListUserWatchingList(page, perPage, MediaListSort.UpdatedTimeDesc).then((result) => {
|
||||
aniListWatchlist = result
|
||||
aniListLoggedIn = true
|
||||
aniListLoggedIn.set(true)
|
||||
})
|
||||
} else {
|
||||
aniListLoggedIn = result
|
||||
aniListLoggedIn.set(result)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -75,7 +61,7 @@
|
||||
simklUser = result
|
||||
SimklGetUserWatchlist().then(result => {
|
||||
simklWatchList.set(result)
|
||||
simklLoggedIn = result
|
||||
simklLoggedIn.set(result)
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -85,38 +71,38 @@
|
||||
function loginToSimkl(): void {
|
||||
GetSimklLoggedInUser().then(result => {
|
||||
simklUser = result
|
||||
simklLoggedIn = true
|
||||
simklLoggedIn.set(true)
|
||||
})
|
||||
}
|
||||
|
||||
function loginToAniList(): void {
|
||||
GetAniListLoggedInUser().then(result => {
|
||||
aniListUser = result
|
||||
aniListLoggedIn = true
|
||||
aniListLoggedIn.set(true)
|
||||
if (aniListPrimary) {
|
||||
GetAniListUserWatchingList(page, perPage, MediaListSort.UpdatedTimeDesc).then(result => aniListWatchlist = result)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function anilistGetUserWatchlist(): void {
|
||||
GetAniListUserWatchingList(page, perPage, MediaListSort.UpdatedTimeDesc).then((result) => {
|
||||
aniListWatchlist = result
|
||||
aniListLoggedIn = true
|
||||
})
|
||||
}
|
||||
|
||||
let count = 1;
|
||||
|
||||
const decrement = () => {
|
||||
if (count > 0) {
|
||||
count--
|
||||
}
|
||||
}
|
||||
|
||||
const increment = () => {
|
||||
count++
|
||||
}
|
||||
// function anilistGetUserWatchlist(): void {
|
||||
// GetAniListUserWatchingList(page, perPage, MediaListSort.UpdatedTimeDesc).then((result) => {
|
||||
// aniListWatchlist = result
|
||||
// aniListLoggedIn.set(true)
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// let count = 1;
|
||||
//
|
||||
// const decrement = () => {
|
||||
// if (count > 0) {
|
||||
// count--
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// const increment = () => {
|
||||
// count++
|
||||
// }
|
||||
|
||||
|
||||
</script>
|
||||
@ -124,39 +110,42 @@
|
||||
<Header/>
|
||||
|
||||
<main>
|
||||
{#if aniListLoggedIn}
|
||||
<div>You are logged into AniList, {aniListWatchlist.data.Page.mediaList[0].user.name}!</div>
|
||||
{#if isAniListLoggedIn}
|
||||
<div>You are logged into AniList, {aniListUser.data.Viewer.name}!</div>
|
||||
{:else}
|
||||
<button class="btn" on:click={loginToAniList}>Login to AniList</button>
|
||||
{/if}
|
||||
|
||||
{#if simklLoggedIn}
|
||||
{#if isSimklLoggedIn}
|
||||
<div>You are logged into Simkl, {simklUser.user.name}</div>
|
||||
{:else}
|
||||
<Button class="btn" on:click={loginToSimkl}>Login to Simkl</Button>
|
||||
{/if}
|
||||
|
||||
{#if aniListLoggedIn}
|
||||
<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 Watching List</h1>
|
||||
{#if isAniListLoggedIn}
|
||||
<div class="mx-auto max-w-2xl p-4 sm:p-6 lg:max-w-7xl lg:px-8">
|
||||
<h1 class="text-left text-xl font-bold mb-4">Your Watching List</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 aniListWatchlist.data.Page.mediaList as media}
|
||||
<div class="aspect-h-1 aspect-w-1 w-full overflow-hidden rounded-lg xl:aspect-h-8 xl:aspect-w-7">
|
||||
<button on:click={() => GetAniListSingleItemAndOpenModal(media.media.id, true)} class="group">
|
||||
<div class="flex flex-col items-center">
|
||||
<div class="flex flex-col items-center group">
|
||||
<button on:click={() => GetAniListSingleItemAndOpenModal(media.media.id, true)}>
|
||||
<img class="rounded-lg" src={media.media.coverImage.large} alt={
|
||||
media.media.title.english === "" ?
|
||||
media.media.title.romaji :
|
||||
media.media.title.english
|
||||
}/>
|
||||
{config.score = media.score / 2.0}
|
||||
<StarRatting {config}/>
|
||||
<h3 class="mt-4 text-sm text-white-700">{
|
||||
</button>
|
||||
<Rating id="anime-rating" total={5} size={35} rating={media.score/2.0} />
|
||||
<button class="mt-4 text-md font-semibold text-white-700"
|
||||
on:click={() => GetAniListSingleItemAndOpenModal(media.media.id, true)}>
|
||||
{
|
||||
media.media.title.english === "" ?
|
||||
media.media.title.romaji :
|
||||
media.media.title.english
|
||||
}</h3>
|
||||
}
|
||||
</button>
|
||||
<p class="mt-1 text-lg font-medium text-white-900">{media.progress}
|
||||
/ {media.media.nextAiringEpisode.episode !== 0 ?
|
||||
media.media.nextAiringEpisode.episode - 1 : media.media.episodes}</p>
|
||||
@ -165,47 +154,12 @@
|
||||
Episodes: {media.media.episodes}</p>
|
||||
{/if}
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="flex items-center justify-center">
|
||||
<button on:click={decrement}
|
||||
class="flex justify-center items-center w-10 h-10 rounded-full text-white focus:outline-none bg-gray-400 hover:bg-gray-500">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 12H4"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<input bind:value={count} class="text-2xl font-bold mx-4"/>
|
||||
<button on:click={increment}
|
||||
class="flex justify-center items-center w-10 h-10 rounded-full text-white focus:outline-none bg-indigo-500 hover:bg-indigo-600">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v12M6 12h12"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<!-- <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-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"/>-->
|
||||
<!-- </svg>-->
|
||||
<!-- </div>-->
|
||||
<!-- <input-->
|
||||
<!-- datepicker-->
|
||||
<!-- datepicker-buttons-->
|
||||
<!-- datepicker-autoselect-today-->
|
||||
<!-- datepicker-autohide-->
|
||||
<!-- datepicker-title="Started At"-->
|
||||
<!-- id="startedAt"-->
|
||||
<!-- type="text"-->
|
||||
<!-- 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 ps-10 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"-->
|
||||
<!-- placeholder="Select date">-->
|
||||
<!-- </div>-->
|
||||
<Modal title={$title} bind:open={$anilistModal} {size} autoclose={false}>
|
||||
<ChangeDataDialogue/>
|
||||
</Modal>
|
||||
|
@ -1,7 +1,90 @@
|
||||
<script lang="ts">
|
||||
import {anime} from "./GetAniListSingleItemAndOpenModal.svelte";
|
||||
import {
|
||||
anilistModal,
|
||||
simklWatchList,
|
||||
aniListLoggedIn,
|
||||
simklLoggedIn
|
||||
} from "./GlobalVariablesAndHelperFunctions.svelte";
|
||||
import {aniListAnime} from "./GlobalVariablesAndHelperFunctions.svelte";
|
||||
import {Button} from "flowbite-svelte";
|
||||
// @ts-ignore
|
||||
import StarRatting from "@ernane/svelte-star-rating"
|
||||
import moment from 'moment'
|
||||
import { Table, TableBody, TableBodyCell, TableBodyRow, TableHead, TableHeadCell } from 'flowbite-svelte';
|
||||
import { writable } from 'svelte/store';
|
||||
import type {SimklAnime} from "./simkl/types/simklTypes";
|
||||
import { get } from 'svelte/store';
|
||||
import {AniListUpdateEntry} from "../wailsjs/go/main/App";
|
||||
|
||||
const simklWatch = get(simklWatchList);
|
||||
let isAniListLoggedIn: boolean
|
||||
let isSimklLoggedIn: boolean
|
||||
|
||||
aniListLoggedIn.subscribe((value) => isAniListLoggedIn = value)
|
||||
simklLoggedIn.subscribe((value) => isSimklLoggedIn = value)
|
||||
|
||||
const simklAnime: SimklAnime | undefined = simklWatch.anime.filter((x) => Number(x.show.ids.mal) === aniListAnime.data.MediaList.media.idMal)[0]
|
||||
|
||||
let items = [];
|
||||
|
||||
if(isAniListLoggedIn) {
|
||||
items.push({
|
||||
id: aniListAnime.data.MediaList.id,
|
||||
service: "AniList",
|
||||
progress: aniListAnime.data.MediaList.progress,
|
||||
status: aniListAnime.data.MediaList.status,
|
||||
startedAt: `${aniListAnime.data.MediaList.startedAt.month}-${aniListAnime.data.MediaList.startedAt.day}-${aniListAnime.data.MediaList.startedAt.year}`,
|
||||
completedAt: `${aniListAnime.data.MediaList.completedAt.month}-${aniListAnime.data.MediaList.completedAt.day}-${aniListAnime.data.MediaList.completedAt.year}`,
|
||||
score: aniListAnime.data.MediaList.score,
|
||||
repeat: aniListAnime.data.MediaList.repeat,
|
||||
notes: aniListAnime.data.MediaList.notes
|
||||
})
|
||||
}
|
||||
|
||||
if(isSimklLoggedIn && simklAnime !== undefined) {
|
||||
items.push({
|
||||
id: simklAnime.show.ids.simkl,
|
||||
service: "Simkl",
|
||||
progress: simklAnime.watched_episodes_count,
|
||||
status: simklAnime.status,
|
||||
startedAt: "",
|
||||
completedAt: "",
|
||||
score: simklAnime.user_rating,
|
||||
repeat: 0,
|
||||
notes: ""
|
||||
})
|
||||
}
|
||||
|
||||
const sortKey = writable('service'); // default sort key
|
||||
const sortDirection = writable(1); // default sort direction (ascending)
|
||||
const sortItems = writable(items.slice()); // make a copy of the items array
|
||||
|
||||
// Define a function to sort the items
|
||||
const sortTable = (key: any) => {
|
||||
// If the same key is clicked, reverse the sort direction
|
||||
if ($sortKey === key) {
|
||||
sortDirection.update((val) => -val);
|
||||
} else {
|
||||
sortKey.set(key);
|
||||
sortDirection.set(1);
|
||||
}
|
||||
};
|
||||
|
||||
$: {
|
||||
const key = $sortKey;
|
||||
const direction = $sortDirection;
|
||||
const sorted = [...$sortItems].sort((a, b) => {
|
||||
const aVal = a[key];
|
||||
const bVal = b[key];
|
||||
if (aVal < bVal) {
|
||||
return -direction;
|
||||
} else if (aVal > bVal) {
|
||||
return direction;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
sortItems.set(sorted);
|
||||
}
|
||||
|
||||
const ratingInWords = {
|
||||
0: "Not Reviewed",
|
||||
@ -17,9 +100,14 @@
|
||||
10: "Masterpiece",
|
||||
}
|
||||
|
||||
const title = anime.data.MediaList.media.title.english !== "" ?
|
||||
anime.data.MediaList.media.title.english :
|
||||
anime.data.MediaList.media.title.romaji
|
||||
const hide = (e) => {
|
||||
e.preventDefault();
|
||||
anilistModal.set(false)
|
||||
};
|
||||
|
||||
const title = aniListAnime.data.MediaList.media.title.english !== "" ?
|
||||
aniListAnime.data.MediaList.media.title.english :
|
||||
aniListAnime.data.MediaList.media.title.romaji
|
||||
|
||||
let config = {
|
||||
readOnly: false,
|
||||
@ -29,7 +117,7 @@
|
||||
max: 5,
|
||||
step: 0.5
|
||||
},
|
||||
score: anime.data.MediaList.score / 2,
|
||||
score: aniListAnime.data.MediaList.score / 2,
|
||||
showScore: false,
|
||||
scoreFormat: function () {
|
||||
return `(${this.score.toFixed(1)}/${this.countStars})`
|
||||
@ -45,41 +133,36 @@
|
||||
}
|
||||
|
||||
let values = {
|
||||
progress: anime.data.MediaList.progress,
|
||||
status: anime.data.MediaList.status,
|
||||
progress: aniListAnime.data.MediaList.progress,
|
||||
status: aniListAnime.data.MediaList.status,
|
||||
startedAt: {
|
||||
year: anime.data.MediaList.startedAt.year,
|
||||
month: anime.data.MediaList.startedAt.month,
|
||||
day: anime.data.MediaList.startedAt.day
|
||||
year: aniListAnime.data.MediaList.startedAt.year,
|
||||
month: aniListAnime.data.MediaList.startedAt.month,
|
||||
day: aniListAnime.data.MediaList.startedAt.day
|
||||
},
|
||||
completedAt: {
|
||||
year: anime.data.MediaList.completedAt.year,
|
||||
month: anime.data.MediaList.completedAt.month,
|
||||
day: anime.data.MediaList.completedAt.day
|
||||
year: aniListAnime.data.MediaList.completedAt.year,
|
||||
month: aniListAnime.data.MediaList.completedAt.month,
|
||||
day: aniListAnime.data.MediaList.completedAt.day
|
||||
},
|
||||
repeat: anime.data.MediaList.repeat,
|
||||
score: anime.data.MediaList.score,
|
||||
notes: anime.data.MediaList.notes
|
||||
repeat: aniListAnime.data.MediaList.repeat,
|
||||
score: aniListAnime.data.MediaList.score,
|
||||
notes: aniListAnime.data.MediaList.notes
|
||||
}
|
||||
let startedAtDate: string
|
||||
let completedAtDate: string
|
||||
|
||||
const changeRating = (e) => {
|
||||
config.score = e.target.valueAsNumber
|
||||
values.score = e.target.valueAsNumber * 2
|
||||
}
|
||||
|
||||
let count = 1;
|
||||
|
||||
const decrement = () => {
|
||||
if (count > 0) {
|
||||
count--
|
||||
if (values.startedAt.year > 0) {
|
||||
let startedAtISODate = new Date(values.startedAt.year, values.startedAt.month - 1, values.startedAt.day)
|
||||
let startedAtMoment = moment(startedAtISODate)
|
||||
startedAtDate = startedAtMoment.format('YYYY-MM-DD')
|
||||
}
|
||||
}
|
||||
|
||||
const increment = () => {
|
||||
count++
|
||||
}
|
||||
|
||||
let startedAtDate = `${values.startedAt.year}-${values.startedAt.month}-${values.startedAt.day}`
|
||||
|
||||
const transformStartedAtDate = (e) => {
|
||||
const re = /^([0-9]{4})-([0-9]{2})-([0-9]{2})/
|
||||
@ -89,31 +172,62 @@
|
||||
values.startedAt.day = Number(date[3])
|
||||
}
|
||||
|
||||
console.log(startedAtDate)
|
||||
if (values.completedAt.year > 0) {
|
||||
let completedAtISODate = new Date(values.completedAt.year, values.completedAt.month - 1, values.completedAt.day)
|
||||
let completedAtMoment = moment(completedAtISODate)
|
||||
completedAtDate = completedAtMoment.format('YYYY-MM-DD')
|
||||
}
|
||||
|
||||
const transformCompletedAtDate = (e) => {
|
||||
const re = /^([0-9]{4})-([0-9]{2})-([0-9]{2})/
|
||||
const date = re.exec(e.target.value)
|
||||
values.completedAt.year = Number(date[1])
|
||||
values.completedAt.month = Number(date[2])
|
||||
values.completedAt.day = Number(date[3])
|
||||
}
|
||||
|
||||
const submitData = async () => {
|
||||
await AniListUpdateEntry(
|
||||
aniListAnime.data.MediaList.mediaId,
|
||||
values.progress,
|
||||
values.status,
|
||||
values.score,
|
||||
values.repeat,
|
||||
values.notes,
|
||||
values.startedAt.year,
|
||||
values.startedAt.month,
|
||||
values.startedAt.day,
|
||||
values.completedAt.year,
|
||||
values.completedAt.month,
|
||||
values.completedAt.day
|
||||
).then((value) => {
|
||||
console.log(value)
|
||||
})
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div class="grid grid-rows-2 grid-cols-10 grid-flow-col gap-4 mt-10">
|
||||
<div class="row-span-2 col-span-2 space-y-3">
|
||||
<img class="rounded-lg" src={anime.data.MediaList.media.coverImage.large} alt="{title} Cover Image">
|
||||
<div id="inapp-data">
|
||||
<div class="grid grid-cols-1 md:grid-cols-10 grid-flow-col gap-4">
|
||||
<div class="md:col-span-2 space-y-3">
|
||||
<img class="rounded-lg" src={aniListAnime.data.MediaList.media.coverImage.large} alt="{title} Cover Image">
|
||||
<StarRatting bind:config on:change={changeRating}/>
|
||||
<p>Rating: {config.score * 2}</p>
|
||||
<p>{ratingInWords[config.score * 2]}</p>
|
||||
</div>
|
||||
|
||||
<div class="col-span-8 z-30">
|
||||
<div class="flex flex-row p-10 justify-center gap-x-56">
|
||||
|
||||
|
||||
<div class="mr-4">
|
||||
<label for="episodes" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Episode Progress</label>
|
||||
<div class="md:col-span-8 ">
|
||||
<div class="flex flex-col md:flex-row md:pl-10 md:pr-10 pt-5 pb-5 justify-center md:gap-x-24 lg:gap-x-56">
|
||||
<div>
|
||||
<label for="episodes" class="text-left block mb-2 text-sm font-medium text-gray-900 dark:text-white">Episode
|
||||
Progress</label>
|
||||
<input
|
||||
type="number"
|
||||
name="episodes"
|
||||
min="0"
|
||||
id="episodes"
|
||||
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600
|
||||
focus:border-primary-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400
|
||||
focus:border-primary-600 block w-24 p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400
|
||||
dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500"
|
||||
bind:value={values.progress}
|
||||
required>
|
||||
@ -121,13 +235,13 @@
|
||||
|
||||
|
||||
<div>
|
||||
<label for="status" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Select
|
||||
your
|
||||
country</label>
|
||||
<label for="status"
|
||||
class="text-left block mb-2 text-sm font-medium text-gray-900 dark:text-white">Status</label>
|
||||
<select id="status"
|
||||
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500
|
||||
focus:border-blue-500 block 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"
|
||||
bind:value={values.status}
|
||||
>
|
||||
|
||||
<option>CURRENT</option>
|
||||
@ -139,58 +253,141 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-row p-10 justify-center">
|
||||
<div class="flex flex-col md:flex-row md:pl-10 md:pr-10 pt-5 pb-5 justify-center md:gap-x-16 lg:gap-x-36">
|
||||
|
||||
<div class="relative z-40 max-w-sm">
|
||||
<div>
|
||||
|
||||
<label for="startedAt" class="text-left block mb-2 text-sm font-medium text-gray-900 dark: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-500 dark:text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
||||
<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"/>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- <input-->
|
||||
<!-- datepicker-->
|
||||
<!-- datepicker-buttons-->
|
||||
<!-- datepicker-autoselect-today-->
|
||||
<!-- datepicker-autohide-->
|
||||
<!-- datepicker-title="Started At"-->
|
||||
<!-- id="startedAt"-->
|
||||
<!-- type="text"-->
|
||||
<!-- name="startedAt"-->
|
||||
<!-- class="z-50 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 ps-10 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"-->
|
||||
<!-- value={`${values.startedAt.year}-${values.startedAt.month}-${values.startedAt.day}`}-->
|
||||
<!-- on:change={transformStartedAtDate}-->
|
||||
<!-- >-->
|
||||
<input
|
||||
id="startedAt"
|
||||
type="date"
|
||||
name="startedAt"
|
||||
class="z-50 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500
|
||||
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 ps-10 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"
|
||||
value={startedAtDate}
|
||||
placeholder="Date Started"
|
||||
on:change={transformStartedAtDate}
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
<label for="completedAt" class="text-left block mb-2 text-sm font-medium text-gray-900 dark: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-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"/>
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
id="completedAt"
|
||||
type="date"
|
||||
name="completedAt"
|
||||
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 ps-10 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"
|
||||
value={completedAtDate}
|
||||
placeholder="Date Completed"
|
||||
on:change={transformCompletedAtDate}
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="repeat"
|
||||
class="text-left block mb-2 text-sm font-medium text-gray-900 dark:text-white">Rewatched</label>
|
||||
<input
|
||||
type="number"
|
||||
name="repeat"
|
||||
min="0"
|
||||
id="repeat"
|
||||
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600
|
||||
focus:border-primary-600 block w-24 p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400
|
||||
dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500"
|
||||
bind:value={values.repeat}
|
||||
required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col md:flex-row md:pl-10 md:pr-10 pt-5 pb-5 justify-center">
|
||||
<div class="w-full">
|
||||
<label for="notes" class="text-left block mb-2 text-sm font-medium text-gray-900 dark:text-white">Your
|
||||
notes</label>
|
||||
<textarea id="notes" rows="3"
|
||||
class="block p-2.5 w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500 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"
|
||||
placeholder="Write your thoughts here..."
|
||||
bind:value={values.notes}></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="bg-white rounded-lg shadow m-4 dark:bg-gray-800">
|
||||
</div>
|
||||
<div id="external-data">
|
||||
<div id="anilist-data" class="flex flex-col md:flex-row md:pl-10 md:pr-10 pt-5 pb-5 justify-center md:gap-x-16 lg:gap-x-36 group">
|
||||
<h2 class="text-left mb-1 text-base font-semibold text-gray-900 dark:text-white">AniList</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Table hoverable={true}>
|
||||
<TableHead>
|
||||
<TableHeadCell class="cursor-pointer" on:click={() => sortTable('id')}>ID</TableHeadCell>
|
||||
<TableHeadCell class="cursor-pointer" on:click={() => sortTable('service')}>Service</TableHeadCell>
|
||||
<TableHeadCell class="cursor-pointer" on:click={() => sortTable('progress')}>Episode Progress</TableHeadCell>
|
||||
<TableHeadCell class="cursor-pointer" on:click={() => sortTable('status')}>Status</TableHeadCell>
|
||||
<TableHeadCell class="cursor-pointer" on:click={() => sortTable('startedAt')}>Date Started</TableHeadCell>
|
||||
<TableHeadCell class="cursor-pointer" on:click={() => sortTable('completedAt')}>Date Completed</TableHeadCell>
|
||||
<TableHeadCell class="cursor-pointer" on:click={() => sortTable('score')}>Rating</TableHeadCell>
|
||||
<TableHeadCell class="cursor-pointer" on:click={() => sortTable('repeat')}>Rewatches</TableHeadCell>
|
||||
<TableHeadCell>Notes</TableHeadCell>
|
||||
</TableHead>
|
||||
<TableBody tableBodyClass="divide-y">
|
||||
{#each $sortItems as item}
|
||||
<TableBodyRow>
|
||||
<TableBodyCell class="overflow-x-auto">{item.id}</TableBodyCell>
|
||||
<TableBodyCell class="overflow-x-auto">{item.service}</TableBodyCell>
|
||||
<TableBodyCell class="overflow-x-auto">{item.progress}</TableBodyCell>
|
||||
<TableBodyCell class="overflow-x-auto">{item.status}</TableBodyCell>
|
||||
<TableBodyCell class="overflow-x-auto">{item.startedAt}</TableBodyCell>
|
||||
<TableBodyCell class="overflow-x-auto">{item.completedAt}</TableBodyCell>
|
||||
<TableBodyCell class="overflow-x-auto">{item.score}</TableBodyCell>
|
||||
<TableBodyCell class="overflow-x-auto">{item.repeat}</TableBodyCell>
|
||||
<TableBodyCell class="overflow-x-auto">{item.notes}</TableBodyCell>
|
||||
</TableBodyRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
|
||||
<div class="bg-white rounded-lg shadow max-w-4-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
|
||||
on:click={submitData}>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">
|
||||
dark:border-gray-600 dark:hover:bg-gray-700 dark:hover:border-gray-600 dark:focus:ring-gray-700"
|
||||
on:click={hide}>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="text-2xl">
|
||||
Summary
|
||||
</h3>
|
||||
<p>{@html aniListAnime.data.MediaList.media.description}</p>
|
||||
</div>
|
||||
</div>
|
@ -2,17 +2,21 @@
|
||||
import {GetAniListItem} from "../wailsjs/go/main/App";
|
||||
import type {AniListGetSingleAnime} from "./anilist/types/AniListCurrentUserWatchListType.js";
|
||||
import {writable} from 'svelte/store'
|
||||
import type {SimklWatchList} from "./simkl/types/simklTypes";
|
||||
|
||||
export let anime: AniListGetSingleAnime
|
||||
export let aniListAnime: AniListGetSingleAnime
|
||||
export let title = writable("")
|
||||
export let anilistModal = writable(false);
|
||||
export let aniListLoggedIn = writable(false)
|
||||
export let simklLoggedIn = writable(false)
|
||||
export let simklWatchList = writable({} as SimklWatchList)
|
||||
|
||||
export function GetAniListSingleItemAndOpenModal(aniId: number, login: boolean): void {
|
||||
GetAniListItem(aniId, login).then(result => {
|
||||
anime = result
|
||||
title.set(anime.data.MediaList.media.title.english === "" ?
|
||||
anime.data.MediaList.media.title.romaji :
|
||||
anime.data.MediaList.media.title.english)
|
||||
aniListAnime = result
|
||||
title.set(aniListAnime.data.MediaList.media.title.english === "" ?
|
||||
aniListAnime.data.MediaList.media.title.romaji :
|
||||
aniListAnime.data.MediaList.media.title.english)
|
||||
anilistModal.set(true)
|
||||
})
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
|
||||
import {AniListSearch} from "../wailsjs/go/main/App";
|
||||
import type {AniSearchList} from "./anilist/types/AniListTypes";
|
||||
import {GetAniListSingleItemAndOpenModal} from "./GetAniListSingleItemAndOpenModal.svelte";
|
||||
import {GetAniListSingleItemAndOpenModal} from "./GlobalVariablesAndHelperFunctions.svelte";
|
||||
|
||||
let aniSearch = ""
|
||||
let aniListSearch: AniSearchList
|
||||
@ -51,14 +51,18 @@
|
||||
aria-labelledby="aniListSearchButton">
|
||||
{#each aniListSearch.data.Page.media as media}
|
||||
<li class="w-full">
|
||||
<div class="flex w-full items-start p-1 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white rounded-lg">
|
||||
<button on:click={() => {
|
||||
GetAniListSingleItemAndOpenModal(media.id, false)
|
||||
}}
|
||||
class="flex w-full items-start p-1 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white rounded-lg">
|
||||
>
|
||||
<img class="rounded-bl-lg rounded-tl-lg max-w-24 max-h-24" src={media.coverImage.large}
|
||||
alt="{media.title.english === '' || media.title.english === null ? media.title.romaji : media.title.english} Cover">
|
||||
<p class="bg-gray-800 text-left w-full h-24 p-2 rounded-tr-lg rounded-br-lg">{media.title.english === '' || media.title.english === null ? media.title.romaji : media.title.english }</p>
|
||||
</button>
|
||||
<button class="rounded-bl-lg rounded-tl-lg w-full h-24" on:click={() => {
|
||||
GetAniListSingleItemAndOpenModal(media.id, false)
|
||||
}} >{media.title.english === '' || media.title.english === null ? media.title.romaji : media.title.english }</button>
|
||||
</div>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
155
frontend/src/modal/Modal.svelte
Normal file
155
frontend/src/modal/Modal.svelte
Normal file
@ -0,0 +1,155 @@
|
||||
<script>import { twMerge } from "tailwind-merge";
|
||||
import { Frame } from "flowbite-svelte";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import {CloseButton} from "flowbite-svelte";
|
||||
import focusTrap from "../../node_modules/flowbite-svelte/dist/utils/focusTrap.js";
|
||||
export let open = false;
|
||||
export let title = "";
|
||||
export let size = "md";
|
||||
export let color = "default";
|
||||
export let placement = "center";
|
||||
export let autoclose = false;
|
||||
export let outsideclose = false;
|
||||
export let dismissable = true;
|
||||
export let backdropClass = "fixed inset-0 z-20 bg-gray-900 bg-opacity-50 dark:bg-opacity-80";
|
||||
export let classBackdrop = void 0;
|
||||
export let dialogClass = "fixed top-0 start-0 end-0 h-modal md:inset-0 md:h-full z-30 w-full p-4 flex";
|
||||
export let classDialog = void 0;
|
||||
export let defaultClass = "relative flex flex-col mx-auto";
|
||||
export let headerClass = "flex justify-between items-center p-4 md:p-5 rounded-t-lg";
|
||||
export let classHeader = void 0;
|
||||
export let bodyClass = "p-4 md:p-5 space-y-4 flex-1 overflow-y-auto overscroll-contain";
|
||||
export let classBody = void 0;
|
||||
export let footerClass = "flex items-center p-4 md:p-5 space-x-3 rtl:space-x-reverse rounded-b-lg";
|
||||
export let classFooter = void 0;
|
||||
const dispatch = createEventDispatcher();
|
||||
$: dispatch(open ? "open" : "close");
|
||||
function prepareFocus(node) {
|
||||
const walker = document.createTreeWalker(node, NodeFilter.SHOW_ELEMENT);
|
||||
let n;
|
||||
while (n = walker.nextNode()) {
|
||||
if (n instanceof HTMLElement) {
|
||||
const el = n;
|
||||
const [x, y] = isScrollable(el);
|
||||
if (x || y) el.tabIndex = 0;
|
||||
}
|
||||
}
|
||||
node.focus();
|
||||
}
|
||||
const getPlacementClasses = (placement2) => {
|
||||
switch (placement2) {
|
||||
case "top-left":
|
||||
return ["justify-start", "items-start"];
|
||||
case "top-center":
|
||||
return ["justify-center", "items-start"];
|
||||
case "top-right":
|
||||
return ["justify-end", "items-start"];
|
||||
case "center-left":
|
||||
return ["justify-start", "items-center"];
|
||||
case "center":
|
||||
return ["justify-center", "items-center"];
|
||||
case "center-right":
|
||||
return ["justify-end", "items-center"];
|
||||
case "bottom-left":
|
||||
return ["justify-start", "items-end"];
|
||||
case "bottom-center":
|
||||
return ["justify-center", "items-end"];
|
||||
case "bottom-right":
|
||||
return ["justify-end", "items-end"];
|
||||
default:
|
||||
return ["justify-center", "items-center"];
|
||||
}
|
||||
};
|
||||
const sizes = {
|
||||
xs: "max-w-md",
|
||||
sm: "max-w-lg",
|
||||
md: "max-w-2xl",
|
||||
lg: "max-w-4xl",
|
||||
xl: "max-w-6xl"
|
||||
};
|
||||
const onAutoClose = (e) => {
|
||||
const target = e.target;
|
||||
if (autoclose && target?.tagName === "BUTTON") hide(e);
|
||||
};
|
||||
const onOutsideClose = (e) => {
|
||||
const target = e.target;
|
||||
if (outsideclose && target === e.currentTarget) hide(e);
|
||||
};
|
||||
const hide = (e) => {
|
||||
e.preventDefault();
|
||||
open = false;
|
||||
};
|
||||
const isScrollable = (e) => [e.scrollWidth > e.clientWidth && ["scroll", "auto"].indexOf(getComputedStyle(e).overflowX) >= 0, e.scrollHeight > e.clientHeight && ["scroll", "auto"].indexOf(getComputedStyle(e).overflowY) >= 0];
|
||||
function handleKeys(e) {
|
||||
if (e.key === "Escape" && dismissable) return hide(e);
|
||||
}
|
||||
$: backdropCls = twMerge(backdropClass, classBackdrop);
|
||||
$: dialogCls = twMerge(dialogClass, classDialog, getPlacementClasses(placement));
|
||||
$: frameCls = twMerge(defaultClass, "w-full divide-y", $$props.class);
|
||||
$: headerCls = twMerge(headerClass, classHeader);
|
||||
$: bodyCls = twMerge(bodyClass, classBody);
|
||||
$: footerCls = twMerge(footerClass, classFooter);
|
||||
</script>
|
||||
|
||||
{#if open}
|
||||
<!-- backdrop -->
|
||||
<div class={backdropCls}></div>
|
||||
<!-- dialog -->
|
||||
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
|
||||
<div on:keydown={handleKeys} on:wheel|preventDefault|nonpassive use:prepareFocus use:focusTrap on:click={onAutoClose} on:mousedown={onOutsideClose} class={dialogCls} tabindex="-1" aria-modal="true" role="dialog">
|
||||
<div class="flex relative {sizes[size]} w-full max-h-full">
|
||||
<!-- Modal content -->
|
||||
<Frame rounded shadow {...$$restProps} class={frameCls} {color}>
|
||||
<!-- Modal header -->
|
||||
{#if $$slots.header || title}
|
||||
<Frame class={headerCls} {color}>
|
||||
<slot name="header">
|
||||
<h3 class="text-xl font-semibold {color === 'default' ? '' : 'text-gray-900 dark:text-white'} p-0">
|
||||
{title}
|
||||
</h3>
|
||||
</slot>
|
||||
{#if dismissable}<CloseButton name="Close modal" {color} on:click={hide} />{/if}
|
||||
</Frame>
|
||||
{/if}
|
||||
<!-- Modal body -->
|
||||
<div class={bodyCls} role="document" on:keydown|stopPropagation={handleKeys} on:wheel|stopPropagation|passive>
|
||||
{#if dismissable && !$$slots.header && !title}
|
||||
<CloseButton name="Close modal" class="absolute top-3 end-2.5" {color} on:click={hide} />
|
||||
{/if}
|
||||
<slot></slot>
|
||||
</div>
|
||||
<!-- Modal footer -->
|
||||
{#if $$slots.footer}
|
||||
<Frame class={footerCls} {color}>
|
||||
<slot name="footer"></slot>
|
||||
</Frame>
|
||||
{/if}
|
||||
</Frame>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!--
|
||||
@component
|
||||
[Go to docs](https://flowbite-svelte.com/)
|
||||
## Props
|
||||
@prop export let open: boolean = false;
|
||||
@prop export let title: string = '';
|
||||
@prop export let size: SizeType = 'md';
|
||||
@prop export let color: ComponentProps<Frame>['color'] = 'default';
|
||||
@prop export let placement: ModalPlacementType = 'center';
|
||||
@prop export let autoclose: boolean = false;
|
||||
@prop export let outsideclose: boolean = false;
|
||||
@prop export let dismissable: boolean = true;
|
||||
@prop export let backdropClass: string = 'fixed inset-0 z-40 bg-gray-900 bg-opacity-50 dark:bg-opacity-80';
|
||||
@prop export let classBackdrop: string | undefined = undefined;
|
||||
@prop export let dialogClass: string = 'fixed top-0 start-0 end-0 h-modal md:inset-0 md:h-full z-50 w-full p-4 flex';
|
||||
@prop export let classDialog: string | undefined = undefined;
|
||||
@prop export let defaultClass: string = 'relative flex flex-col mx-auto';
|
||||
@prop export let headerClass: string = 'flex justify-between items-center p-4 md:p-5 rounded-t-lg';
|
||||
@prop export let classHeader: string | undefined = undefined;
|
||||
@prop export let bodyClass: string = 'p-4 md:p-5 space-y-4 flex-1 overflow-y-auto overscroll-contain';
|
||||
@prop export let classBody: string | undefined = undefined;
|
||||
@prop export let footerClass: string = 'flex items-center p-4 md:p-5 space-x-3 rtl:space-x-reverse rounded-b-lg';
|
||||
@prop export let classFooter: string | undefined = undefined;
|
||||
-->
|
77
frontend/src/modal/Modal.svelte.d.ts
vendored
Normal file
77
frontend/src/modal/Modal.svelte.d.ts
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
import { SvelteComponentTyped } from "svelte";
|
||||
import type { Dismissable, SizeType } from '../types';
|
||||
import type { ModalPlacementType } from '../types';
|
||||
declare const __propDef: {
|
||||
props: import("svelte/elements").HTMLAnchorAttributes & {
|
||||
tag?: string;
|
||||
color?: import("../utils/Frame.svelte").FrameColor;
|
||||
rounded?: boolean;
|
||||
border?: boolean;
|
||||
shadow?: boolean;
|
||||
node?: HTMLElement | undefined;
|
||||
use?: import("svelte/action").Action<HTMLElement, any>;
|
||||
options?: object;
|
||||
class?: string;
|
||||
role?: string;
|
||||
open?: boolean;
|
||||
transition?: (node: HTMLElement, params: any) => import("svelte/transition").TransitionConfig;
|
||||
params?: any;
|
||||
} & Dismissable & {
|
||||
open?: boolean;
|
||||
title?: string;
|
||||
size?: SizeType;
|
||||
placement?: ModalPlacementType;
|
||||
autoclose?: boolean;
|
||||
outsideclose?: boolean;
|
||||
backdropClass?: string;
|
||||
classBackdrop?: string;
|
||||
dialogClass?: string;
|
||||
classDialog?: string;
|
||||
defaultClass?: string;
|
||||
headerClass?: string;
|
||||
classHeader?: string;
|
||||
bodyClass?: string;
|
||||
classBody?: string;
|
||||
footerClass?: string;
|
||||
classFooter?: string;
|
||||
};
|
||||
events: {
|
||||
wheel: WheelEvent;
|
||||
} & {
|
||||
[evt: string]: CustomEvent<any>;
|
||||
};
|
||||
slots: {
|
||||
header: {};
|
||||
default: {};
|
||||
footer: {};
|
||||
};
|
||||
};
|
||||
export type ModalProps = typeof __propDef.props;
|
||||
export type ModalEvents = typeof __propDef.events;
|
||||
export type ModalSlots = typeof __propDef.slots;
|
||||
/**
|
||||
* [Go to docs](https://flowbite-svelte.com/)
|
||||
* ## Props
|
||||
* @prop export let open: boolean = false;
|
||||
* @prop export let title: string = '';
|
||||
* @prop export let size: SizeType = 'md';
|
||||
* @prop export let color: ComponentProps<Frame>['color'] = 'default';
|
||||
* @prop export let placement: ModalPlacementType = 'center';
|
||||
* @prop export let autoclose: boolean = false;
|
||||
* @prop export let outsideclose: boolean = false;
|
||||
* @prop export let dismissable: boolean = true;
|
||||
* @prop export let backdropClass: string = 'fixed inset-0 z-40 bg-gray-900 bg-opacity-50 dark:bg-opacity-80';
|
||||
* @prop export let classBackdrop: string | undefined = undefined;
|
||||
* @prop export let dialogClass: string = 'fixed top-0 start-0 end-0 h-modal md:inset-0 md:h-full z-50 w-full p-4 flex';
|
||||
* @prop export let classDialog: string | undefined = undefined;
|
||||
* @prop export let defaultClass: string = 'relative flex flex-col mx-auto';
|
||||
* @prop export let headerClass: string = 'flex justify-between items-center p-4 md:p-5 rounded-t-lg';
|
||||
* @prop export let classHeader: string | undefined = undefined;
|
||||
* @prop export let bodyClass: string = 'p-4 md:p-5 space-y-4 flex-1 overflow-y-auto overscroll-contain';
|
||||
* @prop export let classBody: string | undefined = undefined;
|
||||
* @prop export let footerClass: string = 'flex items-center p-4 md:p-5 space-x-3 rtl:space-x-reverse rounded-b-lg';
|
||||
* @prop export let classFooter: string | undefined = undefined;
|
||||
*/
|
||||
export default class Modal extends SvelteComponentTyped<ModalProps, ModalEvents, ModalSlots> {
|
||||
}
|
||||
export {};
|
@ -19,7 +19,10 @@ export type SimklUser = {
|
||||
}
|
||||
|
||||
export type SimklWatchList = {
|
||||
anime: [{
|
||||
anime: [SimklAnime]
|
||||
}
|
||||
|
||||
export type SimklAnime = {
|
||||
last_watched_at: string,
|
||||
status: string
|
||||
user_rating: number,
|
||||
@ -55,5 +58,4 @@ export type SimklWatchList = {
|
||||
}
|
||||
},
|
||||
anime_type: string
|
||||
}]
|
||||
}
|
2
frontend/wailsjs/go/main/App.d.ts
vendored
2
frontend/wailsjs/go/main/App.d.ts
vendored
@ -6,7 +6,7 @@ export function AniListLogin():Promise<void>;
|
||||
|
||||
export function AniListSearch(arg1:string):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 AniListUpdateEntry(arg1:number,arg2:number,arg3:string,arg4:number,arg5:number,arg6:string,arg7:number,arg8:number,arg9:number,arg10:number,arg11:number,arg12:number):Promise<any>;
|
||||
|
||||
export function CheckIfAniListLoggedIn():Promise<boolean>;
|
||||
|
||||
|
@ -95,7 +95,7 @@ export namespace main {
|
||||
id: number;
|
||||
mediaId: number;
|
||||
userId: number;
|
||||
// Go type: struct { ID int "json:\"id\""; IDMal int "json:\"idMal\""; Title struct { Romaji string "json:\"romaji\""; English string "json:\"english\""; Native string "json:\"native\"" } "json:\"title\""; Description string "json:\"description\""; CoverImage struct { Large string "json:\"large\"" } "json:\"coverImage\""; Season string "json:\"season\""; SeasonYear int "json:\"seasonYear\""; Status string "json:\"status\""; Episodes int "json:\"episodes\""; NextAiringEpisode struct { AiringAt int "json:\"airingAt\""; TimeUntilAiring int "json:\"timeUntilAiring\""; Episode int "json:\"episode\"" } "json:\"nextAiringEpisode\"" }
|
||||
// Go type: struct { ID int "json:\"id\""; IDMal int "json:\"idMal\""; Title struct { Romaji string "json:\"romaji\""; English string "json:\"english\""; Native string "json:\"native\"" } "json:\"title\""; Description string "json:\"description\""; CoverImage struct { Large string "json:\"large\"" } "json:\"coverImage\""; Season string "json:\"season\""; SeasonYear int "json:\"seasonYear\""; Status string "json:\"status\""; Episodes int "json:\"episodes\""; NextAiringEpisode struct { AiringAt int "json:\"airingAt\""; TimeUntilAiring int "json:\"timeUntilAiring\""; Episode int "json:\"episode\"" } "json:\"nextAiringEpisode\""; Tags []struct { Id int "json:\"id\""; Name string "json:\"name\""; Description string "json:\"description\""; Rank int "json:\"rank\""; IsMediaSpoiler bool "json:\"isMediaSpoiler\""; IsAdult bool "json:\"isAdult\"" } "json:\"tags\""; IsAdult bool "json:\"isAdult\"" }
|
||||
media: any;
|
||||
status: string;
|
||||
// Go type: struct { Year int "json:\"year\""; Month int "json:\"month\""; Day int "json:\"day\"" }
|
||||
|
Loading…
Reference in New Issue
Block a user