changed from modal to client-side router

This commit is contained in:
John O'Keefe 2024-09-04 12:05:41 -04:00
parent 4fa38b5252
commit f5001cff04
21 changed files with 327 additions and 379 deletions

View File

@ -5,7 +5,7 @@ meta {
}
get {
url: https://api.simkl.com/anime/40398?extended=full
url: https://api.simkl.com/anime/862523?extended=full
body: none
auth: none
}

View File

@ -17,6 +17,7 @@
"svelte": "^3.49.0",
"svelte-check": "^2.8.0",
"svelte-preprocess": "^4.10.7",
"svelte-spa-router": "^4.0.1",
"tailwind-merge": "^2.4.0",
"tailwindcss": "^3.4.6",
"tslib": "^2.4.0",
@ -24,7 +25,6 @@
"vite": "^3.0.7"
},
"dependencies": {
"@ernane/svelte-star-rating": "^1.1.7",
"@popperjs/core": "^2.11.8",
"flowbite": "^2.4.1",
"flowbite-svelte": "^0.46.15",

View File

@ -1,12 +1,10 @@
<script lang="ts">
import {
aniListLoggedIn,
anilistModal,
aniListPrimary,
aniListUser,
aniListWatchlist,
animePerPage,
GetAniListSingleItemAndOpenModal,
malLoggedIn,
malPrimary,
malUser,
@ -14,10 +12,11 @@
simklLoggedIn,
simklUser,
simklWatchList,
title,
watchListPage,
simklPrimary,
} from "./GlobalVariablesAndHelperFunctions.svelte";
aniListAnime,
GetAniListSingleItem,
} from "./helperComponents/GlobalVariablesAndHelperFunctions.svelte";
import {
CheckIfAniListLoggedIn,
CheckIfMyAnimeListLoggedIn,
@ -30,24 +29,20 @@
SimklGetUserWatchlist,
} from "../wailsjs/go/main/App";
import {MediaListSort} from "./anilist/types/AniListTypes";
import type {AniListCurrentUserWatchList} from "./anilist/types/AniListCurrentUserWatchListType"
import Header from "./Header.svelte";
import {Rating} from "flowbite-svelte";
import {default as Modal} from "./modal/Modal.svelte"
import ChangeDataDialogue from "./ChangeDataDialogue.svelte";
import {onMount} from "svelte";
import Pagination from "./Pagination.svelte";
import Router from "svelte-spa-router"
import Home from "./routes/Home.svelte";
import {wrap} from "svelte-spa-router/wrap";
import Spinner from "./helperComponents/Spinner.svelte";
import Anime from "./routes/Anime.svelte";
import Header from "./helperComponents/Header.svelte";
let isAniListLoggedIn: boolean
let isAniListPrimary: boolean
let isMalPrimary: boolean
let isSimklPrimary: boolean
let aniListWatchListLoaded: AniListCurrentUserWatchList
aniListLoggedIn.subscribe((value) => isAniListLoggedIn = value)
aniListPrimary.subscribe((value) => isAniListPrimary = value)
aniListWatchlist.subscribe((value) => aniListWatchListLoaded = value)
malPrimary.subscribe((value) => isMalPrimary = value)
simklPrimary.subscribe(value => isSimklPrimary = value)
@ -56,7 +51,6 @@
let perPage: number
watchListPage.subscribe(value => page = value)
animePerPage.subscribe(value => perPage = value)
const size = "xl"
onMount(async () => {
await CheckIfAniListLoggedIn().then(loggedIn => {
@ -113,67 +107,18 @@
})
</script>
<Header/>
<main>
{#if isAniListLoggedIn}
<div class="mx-auto max-w-2xl p-4 sm:p-6 lg:max-w-7xl lg:px-8 relative items-center">
<div id="spinner" role="status" class="fixed hidden -translate-x-1/2 -translate-y-1/2 top-2/4 left-1/2">
<svg aria-hidden="true"
class="inline w-16 h-16 text-gray-200 animate-spin dark:text-gray-600 fill-blue-600"
viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
fill="currentColor"/>
<path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
fill="currentFill"/>
</svg>
<span class="sr-only">Loading...</span>
</div>
<h1 class="text-left text-xl font-bold mb-4">Your AniList WatchList</h1>
<Pagination/>
<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 aniListWatchListLoaded.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">
<div class="flex flex-col items-center group">
<button on:click={() => {
let spinner = document.querySelector("#spinner")
spinner.classList.toggle("hidden", false)
GetAniListSingleItemAndOpenModal(media.media.id, true).then(() => spinner.classList.toggle("hidden", true))
}}>
<img class="rounded-lg" src={media.media.coverImage.large} alt={
media.media.title.english === "" ?
media.media.title.romaji :
media.media.title.english
}/>
</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
}
</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>
{#if media.media.episodes > 0}
<p class="mt-1 text-lg font-medium text-white-900">Total
Episodes: {media.media.episodes}</p>
{/if}
</div>
</div>
{/each}
</div>
<Pagination/>
</div>
{/if}
<Modal title={$title} bind:open={$anilistModal} {size} autoclose={false}>
<ChangeDataDialogue/>
</Modal>
</main>
<Header />
<Router routes={{
'/': Home,
'/anime/:id': wrap({
component: Anime,
conditions: [
async (detail) => {
await GetAniListSingleItem(Number(detail.params.id), true)
return Object.keys($aniListAnime).length!==0
},
],
loadingComponent: Spinner
}),
// '*': "Not Found"
}} />

View File

@ -1,9 +1,9 @@
<script lang="ts">
import {Avatar} from "flowbite-svelte";
import type {AniListUser} from "./anilist/types/AniListTypes";
import type {AniListUser} from "../anilist/types/AniListTypes";
import {aniListLoggedIn, aniListUser, malLoggedIn, simklLoggedIn, logoutOfAniList, logoutOfMAL, logoutOfSimkl} from "./GlobalVariablesAndHelperFunctions.svelte"
import * as runtime from "../wailsjs/runtime";
import * as runtime from "../../wailsjs/runtime";
let currentAniListUser: AniListUser
let isAniListLoggedIn: boolean

View File

@ -11,19 +11,18 @@
LogoutSimkl,
SimklGetUserWatchlist,
SimklSearch
} from "../wailsjs/go/main/App";
} from "../../wailsjs/go/main/App";
import type {
AniListCurrentUserWatchList,
AniListGetSingleAnime
} from "./anilist/types/AniListCurrentUserWatchListType.js";
} from "../anilist/types/AniListCurrentUserWatchListType.js";
import {writable} from 'svelte/store'
import type {SimklAnime, SimklUser, SimklWatchList} from "./simkl/types/simklTypes";
import {type AniListUser, MediaListSort} from "./anilist/types/AniListTypes";
import type {MALAnime, MALWatchlist, MyAnimeListUser} from "./mal/types/MALTypes";
import type {SimklAnime, SimklUser, SimklWatchList} from "../simkl/types/simklTypes";
import {type AniListUser, MediaListSort} from "../anilist/types/AniListTypes";
import type {MALAnime, MALWatchlist, MyAnimeListUser} from "../mal/types/MALTypes";
export let aniListAnime = writable({} as AniListGetSingleAnime)
export let title = writable("")
export let anilistModal = writable(false);
export let aniListLoggedIn = writable(false)
export let simklLoggedIn = writable(false)
export let malLoggedIn = writable(false)
@ -38,6 +37,7 @@
export let malWatchList = writable({} as MALWatchlist)
export let malAnime = writable({} as MALAnime)
export let simklAnime = writable({} as SimklAnime)
export let loading = writable(false)
export let watchListPage = writable(1)
export let animePerPage = writable(20)
@ -60,7 +60,7 @@
aniListAnime.subscribe(value => currentAniListAnime = value)
export async function GetAniListSingleItemAndOpenModal(aniId: number, login: boolean): Promise<""> {
export async function GetAniListSingleItem(aniId: number, login: boolean): Promise<""> {
await GetAniListItem(aniId, login).then(aniListResult => {
let finalResult: AniListGetSingleAnime
finalResult = aniListResult
@ -92,7 +92,6 @@
simklAnime.set(value)
})
}
anilistModal.set(true)
return ""
}

View File

@ -9,13 +9,14 @@
malLoggedIn,
malUser,
simklLoggedIn,
simklUser
simklUser,
} from "./GlobalVariablesAndHelperFunctions.svelte"
import type {AniListUser} from "./anilist/types/AniListTypes";
import type {SimklUser} from "./simkl/types/simklTypes";
import type {MyAnimeListUser} from "./mal/types/MALTypes";
import type {AniListUser} from "../anilist/types/AniListTypes";
import type {SimklUser} from "../simkl/types/simklTypes";
import type {MyAnimeListUser} from "../mal/types/MALTypes";
import AvatarMenu from "./AvatarMenu.svelte";
import logo from "./assets/images/AniTrackLogo.svg"
import logo from "../assets/images/AniTrackLogo.svg"
import {location, pop} from "svelte-spa-router";
let isAniListLoggedIn: boolean
let isSimklLoggedIn: boolean
@ -30,12 +31,20 @@
aniListUser.subscribe((value) => currentAniListUser = value)
simklUser.subscribe((value) => currentSimklUser = value)
malUser.subscribe((value) => currentMALUser = value)
let currentLocation: any
location.subscribe(value => currentLocation = value)
console.log(currentLocation)
</script>
<nav class="bg-white border-gray-200 dark:bg-gray-900">
<div class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4">
<div class="flex items-center space-x-3 rtl:space-x-reverse">
<img src={logo} class="h-8" alt="AniTrack Logo"/>
{#if currentLocation === "/"}
<a href="/"><img src={logo} class="h-8" alt="AniTrack Logo"/></a>
{:else}
<button on:click={() => pop()}><img src={logo} class="h-8" alt="AniTrack Logo"/></button>
{/if}
</div>
<div class="flex items-center min-[950px]:order-2 space-x-3 min-[950px]:space-x-0 rtl:space-x-reverse">
<div class="min-[950px]:block min-[950px]:mr-4">

View File

@ -6,9 +6,9 @@
watchListPage,
} from "./GlobalVariablesAndHelperFunctions.svelte";
import type {AniListCurrentUserWatchList} from "./anilist/types/AniListCurrentUserWatchListType"
import {GetAniListUserWatchingList} from "../wailsjs/go/main/App";
import {MediaListSort} from "./anilist/types/AniListTypes";
import type {AniListCurrentUserWatchList} from "../anilist/types/AniListCurrentUserWatchListType"
import {GetAniListUserWatchingList} from "../../wailsjs/go/main/App";
import {MediaListSort} from "../anilist/types/AniListTypes";
let aniListWatchListLoaded: AniListCurrentUserWatchList
let page: number

View File

@ -1,8 +1,8 @@
<script lang="ts">
import {AniListSearch} from "../wailsjs/go/main/App";
import type {AniSearchList} from "./anilist/types/AniListTypes";
import {GetAniListSingleItemAndOpenModal} from "./GlobalVariablesAndHelperFunctions.svelte";
import {AniListSearch} from "../../wailsjs/go/main/App";
import type {AniSearchList} from "../anilist/types/AniListTypes";
import {GetAniListSingleItem, loading} from "./GlobalVariablesAndHelperFunctions.svelte";
let aniSearch = ""
let aniListSearch: AniSearchList
@ -55,18 +55,22 @@
<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={() => {
let spinner = document.querySelector("#spinner")
spinner.classList.toggle("hidden", false)
GetAniListSingleItemAndOpenModal(media.id, false).then(() => spinner.classList.toggle("hidden", true))
loading.set(true)
GetAniListSingleItem(media.id, false).then(() => {
loading.set(false)
searchDropdown()
})
}}
>
<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">
</button>
<button class="rounded-bl-lg rounded-tl-lg w-full h-24" on:click={() => {
let spinner = document.querySelector("#spinner")
spinner.classList.toggle("hidden", false)
GetAniListSingleItemAndOpenModal(media.id, false).then(() => spinner.classList.toggle("hidden", true))
loading.set(true)
GetAniListSingleItem(media.id, false).then(() => {
loading.set(false)
searchDropdown()
})
}} >{media.title.english === '' || media.title.english === null ? media.title.romaji : media.title.english }</button>
</div>
</li>

View File

@ -0,0 +1,11 @@
<div id="spinner" role="status" class="fixed -translate-x-1/2 -translate-y-1/2 top-2/4 left-1/2">
<svg aria-hidden="true"
class="inline w-16 h-16 text-gray-200 animate-spin dark:text-gray-600 fill-blue-600"
viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
fill="currentColor"/>
<path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
fill="currentFill"/>
</svg>
<span class="sr-only">Loading...</span>
</div>

View File

@ -0,0 +1,68 @@
<script lang="ts">
import {
aniListLoggedIn,
aniListWatchlist,
GetAniListSingleItem,
loading,
} from "./GlobalVariablesAndHelperFunctions.svelte";
import {push} from "svelte-spa-router";
import type {AniListCurrentUserWatchList} from "../anilist/types/AniListCurrentUserWatchListType"
import {Rating} from "flowbite-svelte";
import loader from '../helperFunctions/loader'
let isAniListLoggedIn: boolean
let aniListWatchListLoaded: AniListCurrentUserWatchList
aniListLoggedIn.subscribe((value) => isAniListLoggedIn = value)
aniListWatchlist.subscribe((value) => aniListWatchListLoaded = value)
</script>
<div>
{#if isAniListLoggedIn}
<div class="mx-auto max-w-2xl p-4 sm:p-6 lg:max-w-7xl lg:px-8 relative items-center">
<h1 class="text-left text-xl font-bold mb-4">Your AniList WatchList</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 aniListWatchListLoaded.data.Page.mediaList as media}
<div use:loader={loading} class="aspect-h-1 aspect-w-1 w-full overflow-hidden rounded-lg xl:aspect-h-8 xl:aspect-w-7">
<div class="flex flex-col items-center group">
<button on:click={() => {
push(`#/anime/${media.media.id}`)
// loading.set(true)
// GetAniListSingleItem(media.media.id, true).then(() => {
// loading.set(false)
//
// })
}}
>
<img class="rounded-lg" src={media.media.coverImage.large} alt={
media.media.title.english === "" ?
media.media.title.romaji :
media.media.title.english
}/>
</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={() => GetAniListSingleItem(media.media.id, true)}>
{
media.media.title.english === "" ?
media.media.title.romaji :
media.media.title.english
}
</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>
{#if media.media.episodes > 0}
<p class="mt-1 text-lg font-medium text-white-900">Total
Episodes: {media.media.episodes}</p>
{/if}
</div>
</div>
{/each}
</div>
</div>
{/if}
</div>

View File

@ -0,0 +1,18 @@
import Spinner from '../helperComponents/Spinner.svelte';
export default (node: any, loading: any) => {
let Spin: any
loading.subscribe((loading: any) => {
if(loading){
Spin = new Spinner({
target: node,
intro: true
})
} else {
if(Spin){
Spin?.$destroy?.()
Spin = undefined;
}
}
})
}

View File

@ -1,155 +0,0 @@
<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;
-->

View File

@ -1,77 +0,0 @@
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 {};

View File

@ -1,29 +1,29 @@
<script lang="ts">
import {
anilistModal,
aniListLoggedIn,
simklLoggedIn,
malLoggedIn,
malAnime,
simklAnime,
aniListAnime,
} from "./GlobalVariablesAndHelperFunctions.svelte";
} from "../helperComponents/GlobalVariablesAndHelperFunctions.svelte";
import {Button} from "flowbite-svelte";
// @ts-ignore
import StarRatting from "@ernane/svelte-star-rating"
import StarRatting from "../star-rating/Stars.svelte"
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 type {SimklAnime} from "../simkl/types/simklTypes";
import {
AniListUpdateEntry, MyAnimeListUpdate,
SimklSyncEpisodes,
SimklSyncRating,
SimklSyncStatus
} from "../wailsjs/go/main/App";
import type {MALAnime, MALUploadStatus, MyListStatus} from "./mal/types/MALTypes";
import type {AniListUpdateVariables} from "./anilist/types/AniListTypes";
import type {AniListGetSingleAnime} from "./anilist/types/AniListCurrentUserWatchListType";
} from "../../wailsjs/go/main/App";
import type {MALAnime, MALUploadStatus, MyListStatus} from "../mal/types/MALTypes";
import type {AniListUpdateVariables} from "../anilist/types/AniListTypes";
import type {AniListGetSingleAnime} from "../anilist/types/AniListCurrentUserWatchListType";
import {pop} from "svelte-spa-router";
let isAniListLoggedIn: boolean
let isMalLoggedIn: boolean
@ -134,7 +134,6 @@
const sortKey = writable('service'); // default sort key
const sortDirection = writable(1); // default sort direction (ascending)
const sortItems = writable(tableItems.slice()); // make a copy of the items array
console.log(tableItems[0])
// Define a function to sort the items
const sortTable = (key: any) => {
@ -177,10 +176,6 @@
10: "Masterpiece",
}
const hide = () => {
anilistModal.set(false)
};
const title = currentAniListAnime.data.MediaList.media.title.english !== "" ?
currentAniListAnime.data.MediaList.media.title.english :
currentAniListAnime.data.MediaList.media.title.romaji
@ -195,7 +190,8 @@
},
score: currentAniListAnime.data.MediaList.score / 2,
showScore: false,
name: "",
name: "rating",
scoreFormat: function(){ return `(${this.score.toFixed(0)}/${this.countStars})` },
starConfig: {
size: 32,
fillColor: '#F9ED4F',
@ -225,7 +221,7 @@
let startedAtDate: string
let completedAtDate: string
const changeRating = (e) => {
const changeRating = (e: any) => {
config.score = e.target.valueAsNumber
values.score = e.target.valueAsNumber * 2
}
@ -237,7 +233,7 @@
startedAtDate = startedAtMoment.format('YYYY-MM-DD')
}
const transformStartedAtDate = (e) => {
const transformStartedAtDate = (e: any) => {
const re = /^([0-9]{4})-([0-9]{2})-([0-9]{2})/
const date = re.exec(e.target.value)
values.startedAt.year = Number(date[1])
@ -251,7 +247,7 @@
completedAtDate = completedAtMoment.format('YYYY-MM-DD')
}
const transformCompletedAtDate = (e) => {
const transformCompletedAtDate = (e: any) => {
const re = /^([0-9]{4})-([0-9]{2})-([0-9]{2})/
const date = re.exec(e.target.value)
values.completedAt.year = Number(date[1])
@ -410,9 +406,9 @@
isSubmitSuccess = true
setTimeout(() => isSubmitSuccess = false, 2000)
}
</script>
<div id="inapp-data">
</script>
<div class="container py-10">
<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={currentAniListAnime.data.MediaList.media.coverImage.large} alt="{title} Cover Image">
@ -591,8 +587,8 @@
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"
on:click={hide}>
Cancel
on:click={() => pop()}>
Back
</Button>
</div>
</div>

View File

@ -0,0 +1,25 @@
<script lang="ts">
import Pagination from "../helperComponents/Pagination.svelte";
import WatchList from "../helperComponents/WatchList.svelte";
import {
aniListLoggedIn,
aniListPrimary,
loading,
} from "../helperComponents/GlobalVariablesAndHelperFunctions.svelte";
import loader from '../helperFunctions/loader'
let isAniListPrimary: boolean
let isAniListLoggedIn: boolean
aniListPrimary.subscribe((value) => isAniListPrimary = value)
aniListLoggedIn.subscribe((value) => isAniListLoggedIn = value)
</script>
{#if isAniListLoggedIn && isAniListPrimary}
<div class="container py-10">
<Pagination />
<WatchList />
<Pagination />
</div>
{:else}
<div use:loader={loading}></div>
{/if}

View File

@ -0,0 +1,63 @@
<!-- Originally from @ernane/svelte-star-rating. Wanted to give credit but could not use from the library without causing program crash. -->
<script>
import Star from './components/Star.svelte';
export let config = {
readOnly: false,
countStars: 5,
range: { min: 0, max: 5, step: 0.001 },
score: 0.0,
showScore: true,
name: "stars",
scoreFormat: function(){ return `(${this.score.toFixed(0)}/${this.countStars})` },
starConfig: {
size: 30,
fillColor: '#F9ED4F',
strokeColor: "#BB8511",
unfilledColor: '#FFF',
strokeUnfilledColor: '#000'
}
}
</script>
<section class="stars-container">
<div class="range-stars">
<div class="stars">
{#each Array(config.countStars) as star, id}
{#if Math.floor(config.score) === id}
<Star id={config.name + id} readOnly={config.readOnly} starConfig={config.starConfig} fillPercentage={config.score - Math.floor(config.score)}/>
{:else if Math.floor(config.score) > id}
<Star id={config.name + id} readOnly={config.readOnly} starConfig={config.starConfig} fillPercentage={1}/>
{:else}
<Star id={config.name + id} readOnly={config.readOnly} starConfig={config.starConfig} fillPercentage={0}/>
{/if}
{/each}
</div>
<input name={config.name}
class="slider"
type="range"
min={config.readOnly ? config.score : config.range.min}
max={config.readOnly ? config.score : config.range.max}
step="{config.range.step}" bind:value={config.score}
on:change
on:click
>
</div>
{#if config.showScore}
<span class="show-score" style="font-size: {config.starConfig.size/2}px;">
{#if config.scoreFormat}
{config.scoreFormat()}
{:else}
({((config.score/config.countStars)*100).toFixed(2)}%)
{/if}
</span>
{/if}
</section>
<style>
.stars-container{ position: relative; display: flex; align-items: center; justify-content: center; gap: .5rem; }
.range-stars{ position: relative; }
.stars{ display: flex; align-items: center; justify-content: center; gap: .5rem; }
.slider{ opacity: 0; cursor: pointer; position: absolute; top: 0; left: 0; right: 0; height: 100%; }
.show-score{ user-select: none; color: #888 }
</style>

View File

@ -0,0 +1,21 @@
<script>
export let id, readOnly, fillPercentage, starConfig;
</script>
<svg xmlns="http://www.w3.org/2000/svg" width="{starConfig.size}" viewBox="0 -10 187.673 179.503" height="{starConfig.size}" >
{#if fillPercentage < 1 && fillPercentage > 0}
<defs>
<linearGradient id="linear-gradient-{id}" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:{starConfig.fillColor};stop-opacity:1" />
<stop offset="{fillPercentage * 100}%" style="stop-color:{starConfig.fillColor};stop-opacity:1"/>
<stop offset="{fillPercentage * 100}%" style="stop-color:{starConfig.unfilledColor};stop-opacity:1" />
</linearGradient>
</defs>
{/if}
<path
opacity="{readOnly ? .7 : 1}"
stroke={fillPercentage > 0 ? starConfig.strokeColor : starConfig.strokeUnfilledColor}
fill={fillPercentage === 1 ? starConfig.fillColor : fillPercentage === 0 ? starConfig.unfilledColor : `url(#linear-gradient-${id})`}
d="M187.183 57.47a9.955 9.955 0 00-8.587-6.86l-54.167-4.918-21.42-50.134a9.978 9.978 0 00-9.172-6.052 9.972 9.972 0 00-9.172 6.061l-21.42 50.125L9.07 50.611a9.973 9.973 0 00-8.578 6.858 9.964 9.964 0 002.917 10.596l40.944 35.908-12.073 53.184a9.97 9.97 0 003.878 10.298A9.953 9.953 0 0042 169.357a9.937 9.937 0 005.114-1.424l46.724-27.925 46.707 27.925a9.936 9.936 0 0010.964-.478 9.979 9.979 0 003.88-10.298l-12.074-53.184 40.944-35.9a9.98 9.98 0 002.925-10.604zm0 0"
/>
</svg>

View File

@ -1,24 +1,45 @@
/** @type {import('tailwindcss').Config} */
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}",
],
plugins: [
require('flowbite/plugin')
],
content: [
"./index.html",
"./src/**/*.{svelte,js,ts,jsx,tsx}",
"./node_modules/flowbite/**/*.{html,js,svelte,ts}",
"./node_modules/flowbite-svelte/**/*.{html,js,svelte,ts}",
],
plugins: [
require('flowbite/plugin')
],
darkMode: 'media',
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'},
}
theme: {
container: {
center: true,
padding: {
DEFAULT: '1rem',
sm: '2rem',
lg: '4rem',
xl: '5rem',
'2xl': '6rem',
},
},
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'
},
}
}
}
}
}

0
frontend/wailsjs/runtime/package.json Executable file → Normal file
View File

0
frontend/wailsjs/runtime/runtime.d.ts vendored Executable file → Normal file
View File

0
frontend/wailsjs/runtime/runtime.js Executable file → Normal file
View File