|
1 | 1 | <script lang="ts">
|
| 2 | + import { untrack } from "svelte"; |
2 | 3 | import { page } from "$app/state";
|
3 | 4 | import { ArrowUpRight } from "@lucide/svelte";
|
4 | 5 | import { confetti } from "@neoconfetti/svelte";
|
|
54 | 55 | );
|
55 | 56 | let isOlderThanAWeek = $derived(releaseDate.getTime() < Date.now() - 1000 * 60 * 60 * 24 * 7);
|
56 | 57 |
|
| 58 | + $effect(() => { |
| 59 | + const interval = setInterval( |
| 60 | + () => (releaseDate = new Date(releaseDate)), |
| 61 | + // this can become wrong when the unit changes |
| 62 | + // and refresh too frequently for the now greater |
| 63 | + // unit as this function is only call on client render, |
| 64 | + // but it's an edge case I'm ready to accept |
| 65 | + // as the user experience remains "live", it's just |
| 66 | + // a matter of a small optimization |
| 67 | + getRefreshPeriod(untrack(() => releaseDate)) |
| 68 | + ); |
| 69 | +
|
| 70 | + return () => clearInterval(interval); |
| 71 | + }); |
| 72 | +
|
| 73 | + /** |
| 74 | + * Small utility function to get the diff between two dates |
| 75 | + * @param first The initial date |
| 76 | + * @param second The date to compare the first one to |
| 77 | + * @returns A diff object with every unit from second to year |
| 78 | + */ |
| 79 | + function getDiffBetween(first: Date, second: Date) { |
| 80 | + return { |
| 81 | + get seconds() { |
| 82 | + return (second.getTime() - first.getTime()) / 1000; |
| 83 | + }, |
| 84 | + get minutes() { |
| 85 | + return Math.floor(this.seconds / 60); |
| 86 | + }, |
| 87 | + get hours() { |
| 88 | + return Math.floor(this.minutes / 60); |
| 89 | + }, |
| 90 | + get days() { |
| 91 | + return Math.floor(this.hours / 24); |
| 92 | + }, |
| 93 | + get months() { |
| 94 | + return Math.floor(this.days / 30); |
| 95 | + }, |
| 96 | + get years() { |
| 97 | + return Math.floor(this.months / 12); |
| 98 | + } |
| 99 | + }; |
| 100 | + } |
| 101 | +
|
| 102 | + /** |
| 103 | + * Get the refresh period needed for a "live feel" |
| 104 | + * when displaying a date. Mainly meant to be used inside |
| 105 | + * a {@link setInterval}. |
| 106 | + * |
| 107 | + * @param date The date to get the period for |
| 108 | + * @returns The number of milliseconds to wait for before refreshing |
| 109 | + */ |
| 110 | + function getRefreshPeriod(date: Date) { |
| 111 | + const { minutes, hours, days, months, years } = getDiffBetween(date, new Date()); |
| 112 | +
|
| 113 | + if (years > 0) { |
| 114 | + return 12 * 30 * 24 * 60 * 60 * 1_000; |
| 115 | + } else if (months > 0) { |
| 116 | + return 30 * 24 * 60 * 60 * 1_000; |
| 117 | + } else if (days > 0) { |
| 118 | + return 24 * 60 * 60 * 1_000; |
| 119 | + } else if (hours > 0) { |
| 120 | + return 60 * 60 * 1_000; |
| 121 | + } else if (minutes > 0) { |
| 122 | + return 60 * 1_000; |
| 123 | + } |
| 124 | + return 1_000; |
| 125 | + } |
| 126 | +
|
57 | 127 | /**
|
58 | 128 | * Converts a date to a relative date string.
|
59 | 129 | * e.g., "2 days ago", "3 hours ago", "1 minute ago"
|
|
63 | 133 | * @returns the relative date
|
64 | 134 | */
|
65 | 135 | function timeAgo(date: Date, locale = "en") {
|
66 |
| - const diff = (Date.now() - date.getTime()) / 1000; |
67 |
| - const minutes = Math.floor(diff / 60); |
68 |
| - const hours = Math.floor(minutes / 60); |
69 |
| - const days = Math.floor(hours / 24); |
70 |
| - const months = Math.floor(days / 30); |
71 |
| - const years = Math.floor(months / 12); |
| 136 | + const { seconds, minutes, hours, days, months, years } = getDiffBetween(date, new Date()); |
72 | 137 | const formatter = new Intl.RelativeTimeFormat(locale, { numeric: "auto" });
|
73 | 138 |
|
74 | 139 | if (years > 0) {
|
|
82 | 147 | } else if (minutes > 0) {
|
83 | 148 | return formatter.format(-minutes, "minute");
|
84 | 149 | }
|
85 |
| - return formatter.format(0, "second"); // "now" if < 1 minute |
| 150 | + return formatter.format(-Math.floor(seconds), "second"); |
86 | 151 | }
|
87 | 152 | </script>
|
88 | 153 |
|
|
0 commit comments