aboutsummaryrefslogtreecommitdiff
path: root/src/lib/Media/Anime/Airing
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-04-28 05:32:28 -0700
committerFuwn <[email protected]>2026-04-28 05:32:28 -0700
commitae074b7dfeb2265fcaf2ac69692362b133f4069e (patch)
tree18ba0d6057f10ad6919ffbcaa94088276113eb6a /src/lib/Media/Anime/Airing
parentfix(airing): round residual hour and minute in AiringTime component (diff)
downloaddue.moe-ae074b7dfeb2265fcaf2ac69692362b133f4069e.tar.xz
due.moe-ae074b7dfeb2265fcaf2ac69692362b133f4069e.zip
refactor(airing): unify countdown formatter into shared helper
Diffstat (limited to 'src/lib/Media/Anime/Airing')
-rw-r--r--src/lib/Media/Anime/Airing/AiringTime.svelte61
-rw-r--r--src/lib/Media/Anime/Airing/format.ts61
-rw-r--r--src/lib/Media/Anime/Airing/time.ts66
3 files changed, 67 insertions, 121 deletions
diff --git a/src/lib/Media/Anime/Airing/AiringTime.svelte b/src/lib/Media/Anime/Airing/AiringTime.svelte
index 02e80d1f..6553c769 100644
--- a/src/lib/Media/Anime/Airing/AiringTime.svelte
+++ b/src/lib/Media/Anime/Airing/AiringTime.svelte
@@ -5,6 +5,7 @@ import type { MediaPrequel } from "$lib/Data/AniList/prequels";
import tooltip from "$lib/Tooltip/tooltip";
import locale from "$stores/locale";
import airingNow from "$stores/airingNow";
+import { formatCountdown } from "./format";
export let originalAnime: Media;
export let upcoming = false;
@@ -25,8 +26,6 @@ const setAiringTime = () => {
const untilAiring = airingAt
? Math.round((airingAt - $airingNow / 1000) * 100) / 100
: undefined;
- let hours = null;
- const shortenCountdown = $settings.displayShortCountdown;
time = new Date(airingAt ? airingAt * 1000 : 0).toLocaleTimeString([], {
hour12: !$settings.display24HourTime,
@@ -49,62 +48,10 @@ const setAiringTime = () => {
).toLocaleDateString()}</span>`;
if (untilAiring !== undefined) {
- let minutes = Math.round(untilAiring / 60);
-
- few = true;
-
- if (minutes > 60) {
- hours = minutes / 60;
-
- if (hours > 24) {
- const totalHours = Math.round(hours);
- const days = Math.floor(totalHours / 24);
- const residualHours = totalHours % 24;
- const weeks = Math.floor(days / 7);
- const residualDays = days % 7;
-
- few = false;
-
- if (weeks >= 1.5) {
- timeFrame = `${weeks}${shortenCountdown ? "w" : " week"}${
- weeks === 1 || shortenCountdown ? "" : "s"
- }`;
-
- if (residualDays > 0)
- timeFrame += `${shortenCountdown ? "" : " "}${residualDays}${
- shortenCountdown ? "d" : " day"
- }${residualDays === 1 || shortenCountdown ? "" : "s"}`;
- } else {
- timeFrame += `${days}${shortenCountdown ? "d" : " day"}${
- days === 1 || shortenCountdown ? "" : "s"
- }`;
- }
-
- if (residualHours > 0)
- timeFrame += `${shortenCountdown ? "" : " "}${residualHours}${
- shortenCountdown ? "h" : " hour"
- }${residualHours === 1 || shortenCountdown ? "" : "s"}`;
- } else {
- const displayHours = Math.floor(minutes / 60);
- const residualMinutes = minutes % 60;
-
- timeFrame += `${displayHours}${shortenCountdown ? "h" : " hour"}${
- displayHours === 1 || shortenCountdown ? "" : "s"
- }`;
-
- if (residualMinutes > 0)
- timeFrame += `${shortenCountdown ? "" : " "}${residualMinutes}${
- shortenCountdown ? "m" : " minute"
- }${residualMinutes === 1 || shortenCountdown ? "" : "s"}`;
- }
- } else {
- minutes = Math.round(minutes);
-
- timeFrame += `${minutes}${shortenCountdown ? "m" : " minute"}${
- minutes === 1 || shortenCountdown ? "" : "s"
- }`;
- }
+ const formatted = formatCountdown(untilAiring);
+ timeFrame = formatted.text;
+ few = formatted.few;
opacity = Math.max(50, 100 - (untilAiring / 60 / 60 / 24 / 7) * 50);
dateString = $locale().dateFormatter(
new Date(airingAt ? airingAt * 1000 : 0),
diff --git a/src/lib/Media/Anime/Airing/format.ts b/src/lib/Media/Anime/Airing/format.ts
new file mode 100644
index 00000000..5fec3cc8
--- /dev/null
+++ b/src/lib/Media/Anime/Airing/format.ts
@@ -0,0 +1,61 @@
+import settings from "$stores/settings";
+import { get } from "svelte/store";
+
+type Options = {
+ forceDays?: boolean;
+};
+
+type Result = {
+ text: string;
+ few: boolean;
+};
+
+const plural = (value: number, short: boolean, longUnit: string, shortUnit: string) =>
+ `${value}${short ? shortUnit : ` ${longUnit}`}${value === 1 || short ? "" : "s"}`;
+
+export const formatCountdown = (
+ secondsUntil: number,
+ options: Options = {},
+): Result => {
+ const short = get(settings).displayShortCountdown;
+ const minutes = Math.round(secondsUntil / 60);
+
+ if (minutes <= 60)
+ return { text: plural(minutes, short, "minute", "m"), few: true };
+
+ const hours = minutes / 60;
+
+ if (hours <= 24) {
+ const displayHours = Math.floor(minutes / 60);
+ const residualMinutes = minutes % 60;
+
+ let text = plural(displayHours, short, "hour", "h");
+
+ if (residualMinutes > 0)
+ text += `${short ? "" : " "}${plural(residualMinutes, short, "minute", "m")}`;
+
+ return { text, few: true };
+ }
+
+ const totalHours = Math.round(hours);
+ const days = Math.floor(totalHours / 24);
+ const residualHours = totalHours % 24;
+ const weeks = Math.floor(days / 7);
+ const residualDays = days % 7;
+
+ let text: string;
+
+ if (weeks >= 2 && !options.forceDays) {
+ text = plural(weeks, short, "week", "w");
+
+ if (residualDays > 0)
+ text += `${short ? "" : " "}${plural(residualDays, short, "day", "d")}`;
+ } else {
+ text = plural(days, short, "day", "d");
+ }
+
+ if (residualHours > 0)
+ text += `${short ? "" : " "}${plural(residualHours, short, "hour", "h")}`;
+
+ return { text, few: false };
+};
diff --git a/src/lib/Media/Anime/Airing/time.ts b/src/lib/Media/Anime/Airing/time.ts
index 7f0e896a..b7547dd2 100644
--- a/src/lib/Media/Anime/Airing/time.ts
+++ b/src/lib/Media/Anime/Airing/time.ts
@@ -2,6 +2,7 @@ import type { Media } from "$lib/Data/AniList/media";
import type { MediaPrequel } from "$lib/Data/AniList/prequels";
import type { SubsPlease } from "$lib/Media/Anime/Airing/Subtitled/subsPlease";
import settings from "$stores/settings";
+import { formatCountdown } from "./format";
import { injectAiringTime } from "./Subtitled/match";
import { totalEpisodes } from "../episodes";
import { get } from "svelte/store";
@@ -22,9 +23,6 @@ export const airingTime = (
hour: "numeric",
minute: "2-digit",
});
- let timeFrame = "";
- let hours = null;
- const shortenCountdown = get(settings).displayShortCountdown;
if (
(anime as unknown as MediaPrequel).startDate &&
@@ -41,67 +39,7 @@ export const airingTime = (
).toLocaleDateString()}</span>`;
if (untilAiring !== undefined) {
- let minutes = untilAiring / 60;
- let few = true;
-
- if (minutes > 60) {
- hours = minutes / 60;
-
- if (hours > 24) {
- let weeks = Math.floor(hours / 24) / 7;
-
- few = false;
-
- if (weeks >= 1.5 && !forceDays) {
- weeks = Math.round(weeks);
-
- timeFrame = `${weeks}${shortenCountdown ? "w" : " week"}${
- weeks === 1 || shortenCountdown ? "" : "s"
- }`;
- } else {
- let days = Math.floor(hours / 24);
- let residualHours = Math.round(hours - days * 24);
-
- if (residualHours === 24) {
- days += 1;
- residualHours = 0;
- }
-
- timeFrame += `${days.toFixed(0)}${shortenCountdown ? "d" : " day"}${
- days === 1 || shortenCountdown ? "" : "s"
- }`;
-
- if (residualHours > 0)
- timeFrame += `${shortenCountdown ? "" : " "}${residualHours}${
- shortenCountdown ? "h" : " hour"
- }${residualHours === 1 || shortenCountdown ? "" : "s"}`;
- }
- } else {
- let displayHours = Math.floor(hours);
- let residualMinutes = Math.round(minutes - displayHours * 60);
-
- if (residualMinutes === 60) {
- displayHours += 1;
- residualMinutes = 0;
- }
-
- timeFrame += `${displayHours}${shortenCountdown ? "h" : " hour"}${
- displayHours === 1 || shortenCountdown ? "" : "s"
- }`;
-
- if (residualMinutes > 0)
- timeFrame += `${shortenCountdown ? "" : " "}${residualMinutes}${
- shortenCountdown ? "m" : " minute"
- }${residualMinutes === 1 || shortenCountdown ? "" : "s"}`;
- }
- } else {
- minutes = Math.round(minutes);
-
- timeFrame += `${minutes}${shortenCountdown ? "m" : " minute"}${
- minutes === 1 || shortenCountdown ? "" : "s"
- }`;
- }
-
+ const { text: timeFrame, few } = formatCountdown(untilAiring, { forceDays });
const opacity = Math.max(50, 100 - (untilAiring / 60 / 60 / 24 / 7) * 50);
const nextEpisode =
anime.nextAiringEpisode?.nativeAiringAt &&