aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lib/CommandPalette/syncActions.ts1
-rw-r--r--src/lib/List/Anime/CleanAnimeList.svelte56
-rw-r--r--src/lib/List/Anime/DueAnimeList.svelte14
-rw-r--r--src/lib/Locale/english.ts4
-rw-r--r--src/lib/Locale/japanese.ts4
-rw-r--r--src/lib/Locale/layout.ts4
-rw-r--r--src/lib/Media/Anime/cache.ts16
-rw-r--r--src/lib/Settings/Categories/Debug.svelte7
-rw-r--r--src/lib/Settings/Categories/SettingSync.svelte2
-rw-r--r--src/lib/Tooltip/LinkedTooltip.svelte1
-rw-r--r--src/lib/Tooltip/tooltip.ts2
-rw-r--r--src/stores/settings.ts3
-rw-r--r--src/styles/input.css1
13 files changed, 75 insertions, 40 deletions
diff --git a/src/lib/CommandPalette/syncActions.ts b/src/lib/CommandPalette/syncActions.ts
index 49859221..90c6a931 100644
--- a/src/lib/CommandPalette/syncActions.ts
+++ b/src/lib/CommandPalette/syncActions.ts
@@ -24,6 +24,7 @@ export const syncActions = (
fetch(root(`/api/configuration`), {
method: "PUT",
+ headers: { "Content-Type": "application/json" },
body: JSON.stringify(get(settings)),
})
.then((response) => {
diff --git a/src/lib/List/Anime/CleanAnimeList.svelte b/src/lib/List/Anime/CleanAnimeList.svelte
index 76701a93..42f4d933 100644
--- a/src/lib/List/Anime/CleanAnimeList.svelte
+++ b/src/lib/List/Anime/CleanAnimeList.svelte
@@ -9,6 +9,7 @@ import type { AniListAuthorisation } from "$lib/Data/AniList/identity";
import ListTitle from "../ListTitle.svelte";
import { onDestroy, onMount } from "svelte";
import AiringTime from "$lib/Media/Anime/Airing/AiringTime.svelte";
+import { hasDueEpisodes } from "$lib/Media/Anime/Airing/classify";
import { browser } from "$app/environment";
import identity from "$stores/identity";
import "../covers.css";
@@ -109,6 +110,9 @@ $: filteredMedia =
? media
: media.filter((m) => m.mediaListEntry?.customLists?.[selectedList]);
+$: if (browser && !dummy && media && previousAnimeList !== media)
+ previousAnimeList = media;
+
const updateSelectedList = (event: Event) => {
const nextSelectedList = (event.currentTarget as HTMLSelectElement).value;
@@ -169,7 +173,7 @@ const scheduleAiringRefresh = () => {
m.nextAiringEpisode?.airingAt && m.nextAiringEpisode.airingAt < now,
)
)
- animeLists = cleanCache(user, $identity);
+ $revalidateAnime = $revalidateAnime + 1;
scheduleAiringRefresh();
},
@@ -206,28 +210,30 @@ $: if (browser && !dummy) {
onDestroy(() => clearAiringRefreshTimeout());
const increment = (anime: Media, progress: number) => {
- if (!dummy && pendingUpdate !== anime.id) {
- $revalidateAnime = $revalidateAnime + 1;
- lastUpdatedMedia = anime.id;
- pendingUpdate = anime.id;
-
- incrementMediaProgress(
- anime.id,
- anime.mediaListEntry?.progress,
- user,
- () => {
- const mediaListEntry = media.find(
- (m) => m.id === anime.id,
- )?.mediaListEntry;
-
- if (mediaListEntry) mediaListEntry.progress = progress + 1;
-
- previousAnimeList = media;
- animeLists = cleanCache(user, $identity);
- pendingUpdate = null;
- },
- );
- }
+ if (dummy || pendingUpdate === anime.id) return;
+
+ pendingUpdate = anime.id;
+ lastUpdatedMedia = anime.id;
+
+ const target = media.find((m) => m.id === anime.id);
+
+ if (target?.mediaListEntry) target.mediaListEntry.progress = progress + 1;
+
+ media =
+ !completed && target && !hasDueEpisodes(target)
+ ? media.filter((m) => m.id !== anime.id)
+ : media;
+
+ incrementMediaProgress(
+ anime.id,
+ anime.mediaListEntry?.progress,
+ user,
+ (skipped) => {
+ pendingUpdate = null;
+
+ if (!skipped) $revalidateAnime = $revalidateAnime + 1;
+ },
+ );
};
</script>
@@ -279,7 +285,7 @@ const increment = (anime: Media, progress: number) => {
>
<div slot="title" let:title={anime} let:progress>
{#if !upcoming && !notYetReleased}
- {pendingUpdate === anime.id ? progress + 1 : progress}{@html totalEpisodes(anime)}
+ {progress}{@html totalEpisodes(anime)}
<button
class={`button-square button-action ${pendingUpdate === anime.id ? 'opaque' : ''}`}
style={pendingUpdate === anime.id ? 'pointer-events: none;' : ''}
@@ -312,7 +318,7 @@ const increment = (anime: Media, progress: number) => {
{/if}
{#if !upcoming || notYetReleased}
<!-- {anime.mediaListEntry?.progress || 0}{@html totalEpisodes(anime)} -->
- {pendingUpdate === anime.id ? progress + 1 : progress}{@html totalEpisodes(anime)}
+ {progress}{@html totalEpisodes(anime)}
<button
class={`button-square button-action ${pendingUpdate === anime.id ? 'opaque' : ''}`}
style={pendingUpdate === anime.id ? 'pointer-events: none;' : ''}
diff --git a/src/lib/List/Anime/DueAnimeList.svelte b/src/lib/List/Anime/DueAnimeList.svelte
index d2c47ebe..29af4a88 100644
--- a/src/lib/List/Anime/DueAnimeList.svelte
+++ b/src/lib/List/Anime/DueAnimeList.svelte
@@ -31,19 +31,7 @@ const restartKeyCacher = (cacheMinutes: number) => {
keyCacheMinutes = cacheMinutes;
keyCacher = setInterval(
() => {
- startTime = performance.now();
- endTime = -1;
- animeLists = mediaListCollection(
- user,
- $identity,
- Type.Anime,
- $anime,
- $lastPruneTimes.anime,
- {
- forcePrune: true,
- addNotification,
- },
- );
+ $revalidateAnime = $revalidateAnime + 1;
},
cacheMinutes * 1000 * 60,
);
diff --git a/src/lib/Locale/english.ts b/src/lib/Locale/english.ts
index 0cfc4035..f0195948 100644
--- a/src/lib/Locale/english.ts
+++ b/src/lib/Locale/english.ts
@@ -284,6 +284,10 @@ const English: Locale = {
"If you are having issues with data loading or logging out, it is recommended to clear your site data.",
},
dummyLists: "Use dummy media lists",
+ dryRunMutations: {
+ title: "Dry-run mutations",
+ hint: "Block outgoing list updates (e.g., the + button) and skip the post-mutation refresh so optimistic UI changes persist locally for testing. No data is sent to AniList.",
+ },
},
hololive: {
live: "LIVE",
diff --git a/src/lib/Locale/japanese.ts b/src/lib/Locale/japanese.ts
index af309a25..8e3744d4 100644
--- a/src/lib/Locale/japanese.ts
+++ b/src/lib/Locale/japanese.ts
@@ -284,6 +284,10 @@ const Japanese: Locale = {
"データの読み込みやログアウトに問題がある場合、サイトデータをクリアすることをお勧めします。",
},
dummyLists: "ダミーメディアリストを使用する",
+ dryRunMutations: {
+ title: "ドライランモード",
+ hint: "リストの更新リクエスト(+ボタンなど)の送信をブロックし、更新後の再取得もスキップします。楽観的UIの変更をローカルで保持してテストできます。AniListにはデータは送信されません。",
+ },
},
hololive: {
live: "ライブ",
diff --git a/src/lib/Locale/layout.ts b/src/lib/Locale/layout.ts
index f317ab7f..697404f2 100644
--- a/src/lib/Locale/layout.ts
+++ b/src/lib/Locale/layout.ts
@@ -282,6 +282,10 @@ export interface Locale {
hint2: LocaleValue;
};
dummyLists: LocaleValue;
+ dryRunMutations: {
+ title: LocaleValue;
+ hint: LocaleValue;
+ };
};
hololive: {
live: LocaleValue;
diff --git a/src/lib/Media/Anime/cache.ts b/src/lib/Media/Anime/cache.ts
index e988f255..e1a711ca 100644
--- a/src/lib/Media/Anime/cache.ts
+++ b/src/lib/Media/Anime/cache.ts
@@ -2,6 +2,7 @@ import { get } from "svelte/store";
import anime from "$stores/anime";
import { mediaListCollection, Type } from "../../Data/AniList/media";
import lastPruneTimes from "$stores/lastPruneTimes";
+import settings from "$stores/settings";
import type {
AniListAuthorisation,
UserIdentity,
@@ -26,8 +27,19 @@ export const incrementMediaProgress = (
id: number,
progress: number | undefined,
user: AniListAuthorisation,
- callback: () => void,
+ callback: (skipped?: boolean) => void,
) => {
+ if (get(settings).debugDryRunMutations) {
+ console.log(
+ `[dry-run] SaveMediaListEntry(mediaId: ${id}, progress: ${
+ (progress || 0) + 1
+ }) skipped`,
+ );
+ callback(true);
+
+ return;
+ }
+
fetch("https://graphql.anilist.co", {
method: "POST",
headers: {
@@ -40,5 +52,5 @@ export const incrementMediaProgress = (
(progress || 0) + 1
}) { id } }`,
}),
- }).then(callback);
+ }).then(() => callback(false));
};
diff --git a/src/lib/Settings/Categories/Debug.svelte b/src/lib/Settings/Categories/Debug.svelte
index ad388fee..6da4b6ae 100644
--- a/src/lib/Settings/Categories/Debug.svelte
+++ b/src/lib/Settings/Categories/Debug.svelte
@@ -15,6 +15,13 @@ import { invalidateListCaches } from "$lib/Media/invalidate";
setting="debugShowListTimings"
text={$locale().debug.showListTimings}
/>
+<SettingCheckboxToggle
+ setting="debugDryRunMutations"
+ text={$locale().debug.dryRunMutations.title}
+/>
+<SettingHint lineBreak>
+ {$locale().debug.dryRunMutations.hint}
+</SettingHint>
<br />
<button onclick={invalidateListCaches}>{$locale().debug.clearCaches}</button>
diff --git a/src/lib/Settings/Categories/SettingSync.svelte b/src/lib/Settings/Categories/SettingSync.svelte
index 0cfe9261..867b2b47 100644
--- a/src/lib/Settings/Categories/SettingSync.svelte
+++ b/src/lib/Settings/Categories/SettingSync.svelte
@@ -29,6 +29,7 @@ import settingsSyncTimes from "$stores/settingsSyncTimes";
} else {
fetch(root(`/api/configuration`), {
method: 'PUT',
+ headers: { 'Content-Type': 'application/json' },
body: JSON.stringify($settings)
}).then((response) => {
if (response.ok)
@@ -56,6 +57,7 @@ import settingsSyncTimes from "$stores/settingsSyncTimes";
fetch(root(`/api/configuration`), {
method: 'PUT',
+ headers: { 'Content-Type': 'application/json' },
body: JSON.stringify($settings)
}).then((response) => {
if (response.ok)
diff --git a/src/lib/Tooltip/LinkedTooltip.svelte b/src/lib/Tooltip/LinkedTooltip.svelte
index 2a7961f5..4776322d 100644
--- a/src/lib/Tooltip/LinkedTooltip.svelte
+++ b/src/lib/Tooltip/LinkedTooltip.svelte
@@ -217,6 +217,7 @@ const hideTooltip = () => {
const handleMouseEnter = (event: MouseEvent) => {
if (disable) return;
+ if (window.matchMedia("(pointer: coarse)").matches) return;
if (hideTimeout !== null) {
clearTimeout(hideTimeout);
diff --git a/src/lib/Tooltip/tooltip.ts b/src/lib/Tooltip/tooltip.ts
index 3024bbff..830ff47f 100644
--- a/src/lib/Tooltip/tooltip.ts
+++ b/src/lib/Tooltip/tooltip.ts
@@ -108,6 +108,8 @@ const tooltip = (element: HTMLElement) => {
};
const handleMouseEnter = (event: MouseEvent) => {
+ if (window.matchMedia("(pointer: coarse)").matches) return;
+
const title = element.getAttribute("title");
if (title) {
diff --git a/src/stores/settings.ts b/src/stores/settings.ts
index 24d9eef0..50dd4013 100644
--- a/src/stores/settings.ts
+++ b/src/stores/settings.ts
@@ -71,6 +71,7 @@ export interface Settings {
displayDataSaver: boolean;
debugDummyLists: boolean;
debugShowListTimings: boolean;
+ debugDryRunMutations: boolean;
displayScheduleFilterList: boolean;
displayReverseSort: boolean;
displayAnimeSort: "difference" | "start_date" | "end_date" | "time_remaining";
@@ -128,6 +129,7 @@ const defaultSettings: Settings = {
// Debug
debugDummyLists: false,
debugShowListTimings: false,
+ debugDryRunMutations: false,
// Calculation
calculateChaptersRoundedDown: true,
@@ -220,6 +222,7 @@ settings.subscribe((value) => {
if (data?.configuration && !isEqualsJson(data.configuration, value))
fetch(root(`/api/configuration`), {
method: "PUT",
+ headers: { "Content-Type": "application/json" },
body: JSON.stringify(value),
})
.then((response) => {
diff --git a/src/styles/input.css b/src/styles/input.css
index a46c9b13..aaadd9a2 100644
--- a/src/styles/input.css
+++ b/src/styles/input.css
@@ -66,6 +66,7 @@ select {
select {
padding: 0.25em 0.5em;
+ max-width: 100%;
}
input[type="checkbox"] {