diff --git a/.vscode/settings.json b/.vscode/settings.json index 2f98304c..d81e0312 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -137,5 +137,6 @@ "codeium.enableInComments": false, "codeium.enableSearch": true, "tailwind-fold.unfoldIfLineSelected": true, - "tailwind-fold.foldLengthThreshold": 5 + "tailwind-fold.foldLengthThreshold": 80, + "tailwind-fold.unfoldedTextOpacity": 1 } diff --git a/src/lib/AppNavData.ts b/src/lib/AppNavData.ts index 9f1cbad8..499ac05d 100644 --- a/src/lib/AppNavData.ts +++ b/src/lib/AppNavData.ts @@ -29,6 +29,14 @@ export const AppNavData = [ return page.includes('/browse'); } }, + { + href: '/history', + title: 'History', + icon: 'mdi:history', + match: (page: string) => { + return page.includes('/history'); + } + }, { href: '/downloads', title: 'Downloads', @@ -46,3 +54,18 @@ export const AppNavData = [ } } ]; + +export const SmallAppNavData = [ + AppNavData[0], + AppNavData[1], + AppNavData[2], + AppNavData[3], + { + href: '/more', + title: 'More', + icon: 'mdi:dots-horizontal', + match: (page: string) => { + return page.includes('/more'); + } + } +]; diff --git a/src/lib/components/MobileAppNavigation.svelte b/src/lib/components/MobileAppNavigation.svelte index 44f62632..d6596808 100644 --- a/src/lib/components/MobileAppNavigation.svelte +++ b/src/lib/components/MobileAppNavigation.svelte @@ -10,7 +10,9 @@ import { page } from '$app/stores'; import { TabGroup, TabAnchor } from '@skeletonlabs/skeleton'; import IconWrapper from './IconWrapper.svelte'; - import { AppNavData } from '../AppNavData'; + import { AppNavData, SmallAppNavData } from '../AppNavData'; + import { screens } from '$lib/screens'; + import MediaQuery from './MediaQuery.svelte'; - {#each AppNavData as Loc} - -
- - {Loc.title} -
-
- {/each} + + {@const Nav = matches ? SmallAppNavData : AppNavData} + {#each Nav as Loc} + +
+ + {Loc.title} +
+
+ {/each} +
diff --git a/src/lib/gql/Queries.ts b/src/lib/gql/Queries.ts index c9e8091a..338f5800 100644 --- a/src/lib/gql/Queries.ts +++ b/src/lib/gql/Queries.ts @@ -495,3 +495,32 @@ export const categoryMangaNotInLibrary = graphql( `, [] ); + +export const History = graphql(` + query History($offset: Int = 0) { + chapters( + condition: { isRead: true } + orderByType: DESC + orderBy: LAST_READ_AT + first: 100 + offset: $offset + ) { + totalCount + nodes { + id + name + lastReadAt + isDownloaded + isBookmarked + manga { + thumbnailUrl + id + title + } + } + pageInfo { + hasNextPage + } + } + } +`); diff --git a/src/lib/util.ts b/src/lib/util.ts index 4e1ccfc4..2ab23c44 100644 --- a/src/lib/util.ts +++ b/src/lib/util.ts @@ -298,3 +298,31 @@ type hasEnumVals = { function isENUM(enu: unknown): enu is hasEnumVals { return enu instanceof Object && 'enumValues' in enu; } + +export function formatDate(date: Date) { + const diff = new Date().valueOf() - date.valueOf(); // the difference in milliseconds + const msPerMinute = 60 * 1000; + const msPerHour = msPerMinute * 60; + const msPerDay = msPerHour * 24; + const msPerMonth = msPerDay * 30; + const msPerYear = msPerDay * 365; + + if (date.toDateString() === new Date().toDateString()) { + return date.toLocaleTimeString(); + } + if (diff < msPerMinute) { + return Math.round(diff / 1000) + ' seconds ago'; + } else if (diff < msPerHour) { + return Math.round(diff / msPerMinute) + ' minutes ago'; + } else if (diff < msPerDay) { + return Math.round(diff / msPerHour) + ' hours ago'; + } else if (diff < msPerMonth) { + return Math.round(diff / msPerDay) + ' days ago'; + } else if (diff < msPerYear) { + return Math.round(diff / msPerMonth) + ' months ago'; + } else { + return Math.round(diff / msPerYear) + ' years ago'; + } + + return date.toLocaleString(); +} diff --git a/src/routes/(app)/history/+page.svelte b/src/routes/(app)/history/+page.svelte new file mode 100644 index 00000000..fcd2e625 --- /dev/null +++ b/src/routes/(app)/history/+page.svelte @@ -0,0 +1,178 @@ + + + + +{#if !$all && $CurrentHistory.fetching} +
+ {#each new Array(110) as _} +
+
+ {#if $Meta.Display === display.Comfortable} +
+ {/if} +
+ {/each} +
+{:else if !$all && $CurrentHistory.error} +
+ {JSON.stringify($CurrentHistory.error, null, 4)} +
+{:else if $all?.nodes} +
+ {#each $all.nodes as updat} + + {#if intersecting} + + + {#if $Meta.Display === display.Compact} +
+
+ {updat.manga.title} +
+
+ {updat.name} +
+
+ {formatDate(new Date(parseInt(updat.lastReadAt) * 1000))} +
+
+ {/if} +
+ {#if updat.isDownloaded} + + {/if} + {#if updat.isBookmarked} + + {/if} +
+
+ {#if $Meta.Display === display.Comfortable} +
+
+ {updat.manga.title} +
+
+ {updat.name} +
+
+ {new Date(parseInt(updat.lastReadAt) * 1000).toLocaleString()} +
+
+ {/if} +
+ {/if} +
+ {/each} + {#if !$CurrentHistory.fetching && $all.pageInfo.hasNextPage} + { + if (e.detail) $page = $all?.nodes.length ?? 0; + }} + /> + {/if} + {#if $CurrentHistory.fetching && $all.pageInfo.hasNextPage} + {#each new Array(10) as _} +
+
+ {#if $Meta.Display === display.Comfortable} +
+ {/if} +
+ {/each} + {/if} +
+{/if} diff --git a/src/routes/(app)/manga/[MangaID]/(manga)/chaptersSide.svelte b/src/routes/(app)/manga/[MangaID]/(manga)/chaptersSide.svelte index 1d6971e5..ba75bd75 100644 --- a/src/routes/(app)/manga/[MangaID]/(manga)/chaptersSide.svelte +++ b/src/routes/(app)/manga/[MangaID]/(manga)/chaptersSide.svelte @@ -186,6 +186,46 @@ component: { ref: ChaptersFilterModal, props: { MangaID } } }); } + + let scrollTo = true; + let _scrollToChaps: HTMLDivElement[] = []; + let scrollToChaps: HTMLDivElement | undefined; + $: if (scrollTo && _scrollToChaps) handelScrollToChaps(); + + function handelScrollToChaps() { + let ind = _scrollToChaps + .filter((e) => e) + .findIndex((e) => e.id === location.hash); + + if (ind !== -1 && ind - 3 > 0) { + scrollToChaps = _scrollToChaps[ind - 3]; + } + } + + $: if (scrollTo && scrollToChaps) scroll(); + + function isScrollable(elem: HTMLElement | undefined) { + if (!elem) return false; + return ( + elem.scrollWidth > elem.clientWidth || + elem.scrollHeight > elem.clientHeight + ); + } + + function scroll() { + scrollTo = false; + const { top, height } = scrollToChaps?.getBoundingClientRect() ?? { + top: 0 + }; + let scrollElement; + if (isScrollable(chapterSideElement)) { + scrollElement = chapterSideElement; + } else scrollElement = document.querySelector('#page'); + scrollElement?.scrollTo({ + top: top + scrollElement.scrollTop + (height ?? 0) / 2, + behavior: 'smooth' + }); + } {#if !$manga || $manga.fetching} @@ -337,7 +377,7 @@
- {#each sortedChapters as chapter (chapter.id)} + {#each sortedChapters as chapter, index (chapter.id)} - {#if intersecting} - $selectMode || LongHandler()} - href="/manga/{mangaFrag?.id}/chapter/{chapter.id}" - on:click={(e) => { - if (e.ctrlKey) return; - if ($selectMode) { - e.preventDefault(); - e.stopPropagation(); - lastSelected = HelpDoSelect( - chapter, - e, - lastSelected, - sortedChapters, - selected - ); - } - }} - > - {#if chapter.isBookmarked} - - {/if} -
-
- {$mangaMeta.ChapterTitle === ChapterTitle['Source Title'] - ? chapter.name - : `Chapter ${chapter.chapterNumber}`} -
-
- {new Date( - $mangaMeta.ChapterFetchUpload - ? parseInt(chapter.uploadDate) - : parseInt(chapter.fetchedAt) * 1000 - ).toLocaleDateString()}{chapter.isDownloaded - ? ' • Downloaded' - : ''}{chapter.scanlator ? ` • ${chapter.scanlator}` : ''} + - e.chapter.id === chapter.id - )} - /> + e.chapter.id === chapter.id + )} + /> - {#if $selectMode} - + {/if} + - {/if} - - -
-
- {#if chapter.isDownloaded} - - {:else} - - {/if} - {#if chapter.isBookmarked} - - {:else} + +
+
+ {#if chapter.isDownloaded} + + {:else} + + {/if} + {#if chapter.isBookmarked} + + {:else} + + {/if} + {#if chapter.isRead} + + {:else} + + {/if} - {/if} - {#if chapter.isRead} - - {:else} - - {/if} - +
+
-
-
- {/if} + {/if} +
{/each} diff --git a/src/routes/(app)/more/+page.svelte b/src/routes/(app)/more/+page.svelte new file mode 100644 index 00000000..c4620ebd --- /dev/null +++ b/src/routes/(app)/more/+page.svelte @@ -0,0 +1,26 @@ + + + + + +
Downloads
+
+ + + +
Settings
+
diff --git a/src/routes/(app)/updates/+page.svelte b/src/routes/(app)/updates/+page.svelte index 6b17cc7e..9c45d058 100644 --- a/src/routes/(app)/updates/+page.svelte +++ b/src/routes/(app)/updates/+page.svelte @@ -19,6 +19,7 @@ import type { UpdateNode } from './UpdatesStores'; import { dlreabook, + formatDate, gridValues, HelpDoSelect, HelpSelectAll @@ -188,9 +189,7 @@ parseInt(updat.fetchedAt) * 1000 ).toLocaleString()} > - {new Date( - parseInt(updat.fetchedAt) * 1000 - ).toLocaleString()} + {formatDate(new Date(parseInt(updat.fetchedAt) * 1000))}
{/if} diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts index 6025074b..bc83edc8 100644 --- a/src/routes/+layout.ts +++ b/src/routes/+layout.ts @@ -4,5 +4,5 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -export const ssr = true; -export const prerender = 'auto'; +export const ssr = false; +export const prerender = false;