mirror of
https://github.com/ThaUnknown/miru.git
synced 2026-01-12 00:03:44 +00:00
feat: license info
fix: thread tab and comments pagination
This commit is contained in:
parent
6a5937bd8c
commit
180ca53ac5
12 changed files with 270 additions and 100 deletions
|
|
@ -22,7 +22,7 @@ Before [creating an issue](https://help.github.com/en/github/managing-your-work-
|
|||
|
||||
### :lock: Reporting Security Issues
|
||||
|
||||
Review our [Security Policy](https://github.com/jessesquires/.github/blob/main/SECURITY.md). **Do not** file a public issue for security vulnerabilities.
|
||||
Review our [Security Policy](./SECURITY.md). **Do not** file a public issue for security vulnerabilities.
|
||||
|
||||
### :beetle: Bug Reports and Other Issues
|
||||
|
||||
|
|
@ -30,7 +30,7 @@ A great way to contribute to the project is to send a detailed issue when you en
|
|||
|
||||
In short, since you are most likely a developer, **provide a ticket that you would like to receive**.
|
||||
|
||||
* **Review the documentation and [Support Guide](https://github.com/jessesquires/.github/blob/main/SUPPORT.md)** before opening a new issue.
|
||||
* **Review the documentation and [Support Guide](./SUPPORT.md)** before opening a new issue.
|
||||
|
||||
* **Do not open a duplicate issue!** Search through existing issues to see if your issue has previously been reported. If your issue exists, comment with any additional information you have. You may simply note "I have this problem too", which helps prioritize the most common problems and requests.
|
||||
|
||||
|
|
|
|||
31
SUPPORT.md
Normal file
31
SUPPORT.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# Support and Help
|
||||
|
||||
Need help getting started or using a project? Here's how.
|
||||
|
||||
## How to get help
|
||||
|
||||
Generally, we do not use GitHub as a support forum. For any usage questions that are not specific to the project itself, please ask on [Stack Overflow](https://stackoverflow.com) instead. By doing so, you are more likely to quickly solve your problem, and you will allow anyone else with the same question to find the answer. This also allows maintainers to focus on improving the project for others.
|
||||
|
||||
Please seek support in the following ways:
|
||||
|
||||
1. :book: **Read the documentation and other guides** for the project to see if you can figure it out on your own. These should be located in a root `docs/` directory. If there is an example project, explore that to learn how it works to see if you can answer your question.
|
||||
|
||||
2. :bulb: **Search for answers and ask questions on [Stack Overflow](https://stackoverflow.com).** This is the most appropriate place for debugging issues specific to your use of the project, or figuring out how to use the project in a specific way.
|
||||
|
||||
3. :memo: As a **last resort**, you may open an issue on GitHub to ask for help. However, please clearly explain what you are trying to do, and list what you have already attempted to solve the problem. Provide code samples, but **do not** attach your entire project for someone else to debug. Review our [contributing guidelines](./CONTRIBUTING.md).
|
||||
|
||||
## What NOT to do
|
||||
|
||||
Please **do not** do any the following:
|
||||
|
||||
1. :x: Do not reach out to the author or contributor on Twitter (or other social media) by tweeting or sending a direct message.
|
||||
|
||||
2. :x: Do not email the author or contributor.
|
||||
|
||||
3. :x: Do not open duplicate issues or litter an existing issue with +1's.
|
||||
|
||||
These are not appropriate avenues for seeking help or support with an open-source project. Please follow the guidelines in the previous section. Public questions get public answers, which benefits everyone in the community. ✌️
|
||||
|
||||
## Customer Support
|
||||
|
||||
I do not provide any sort of "customer support" for open-source projects. However, I am available for hire.
|
||||
|
|
@ -67,6 +67,7 @@
|
|||
"lucide-svelte": "^0.452.0",
|
||||
"marked": "^15.0.11",
|
||||
"p2pt": "^1.5.1",
|
||||
"rollup-plugin-license": "^3.6.0",
|
||||
"semver": "^7.7.1",
|
||||
"simple-store-svelte": "^1.0.6",
|
||||
"svelte-keybinds": "^1.0.9",
|
||||
|
|
|
|||
110
pnpm-lock.yaml
110
pnpm-lock.yaml
|
|
@ -77,6 +77,9 @@ importers:
|
|||
p2pt:
|
||||
specifier: ^1.5.1
|
||||
version: 1.5.1
|
||||
rollup-plugin-license:
|
||||
specifier: ^3.6.0
|
||||
version: 3.6.0(picomatch@4.0.2)(rollup@4.27.2)
|
||||
semver:
|
||||
specifier: ^7.7.1
|
||||
version: 7.7.1
|
||||
|
|
@ -850,6 +853,10 @@ packages:
|
|||
resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
array-find-index@1.0.2:
|
||||
resolution: {integrity: sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
array-includes@3.1.8:
|
||||
resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
|
@ -1014,6 +1021,9 @@ packages:
|
|||
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
commenting@1.1.0:
|
||||
resolution: {integrity: sha512-YeNK4tavZwtH7jEgK1ZINXzLKm6DZdEMfsaaieOsCAN0S8vsY7UeuO3Q7d/M018EFgE+IeUAuBOKkFccBZsUZA==}
|
||||
|
||||
compact2string@1.4.1:
|
||||
resolution: {integrity: sha512-3D+EY5nsRhqnOwDxveBv5T8wGo4DEvYxjDtPGmdOX+gfr5gE92c2RC0w2wa+xEefm07QuVqqcF3nZJUZ92l/og==}
|
||||
|
||||
|
|
@ -1369,6 +1379,14 @@ packages:
|
|||
picomatch:
|
||||
optional: true
|
||||
|
||||
fdir@6.4.4:
|
||||
resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==}
|
||||
peerDependencies:
|
||||
picomatch: ^3 || ^4
|
||||
peerDependenciesMeta:
|
||||
picomatch:
|
||||
optional: true
|
||||
|
||||
file-entry-cache@8.0.0:
|
||||
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
|
|
@ -1728,6 +1746,9 @@ packages:
|
|||
lodash.merge@4.6.2:
|
||||
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
||||
|
||||
lodash@4.17.21:
|
||||
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
||||
|
||||
lower-case@2.0.2:
|
||||
resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==}
|
||||
|
||||
|
|
@ -1780,6 +1801,9 @@ packages:
|
|||
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
|
||||
engines: {node: '>=16 || 14 >=14.17'}
|
||||
|
||||
moment@2.30.1:
|
||||
resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==}
|
||||
|
||||
mri@1.2.0:
|
||||
resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
|
||||
engines: {node: '>=4'}
|
||||
|
|
@ -1887,6 +1911,10 @@ packages:
|
|||
package-json-from-dist@1.0.1:
|
||||
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
|
||||
|
||||
package-name-regex@2.0.6:
|
||||
resolution: {integrity: sha512-gFL35q7kbE/zBaPA3UKhp2vSzcPYx2ecbYuwv1ucE9Il6IIgBDweBlH8D68UFGZic2MkllKa2KHCfC1IQBQUYA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
parent-module@1.0.1:
|
||||
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
||||
engines: {node: '>=6'}
|
||||
|
|
@ -2069,6 +2097,12 @@ packages:
|
|||
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
|
||||
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
|
||||
|
||||
rollup-plugin-license@3.6.0:
|
||||
resolution: {integrity: sha512-1ieLxTCaigI5xokIfszVDRoy6c/Wmlot1fDEnea7Q/WXSR8AqOjYljHDLObAx7nFxHC2mbxT3QnTSPhaic2IYw==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
rollup: ^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.0
|
||||
|
||||
rollup@4.27.2:
|
||||
resolution: {integrity: sha512-KreA+PzWmk2yaFmZVwe6GB2uBD86nXl86OsDkt1bJS9p3vqWuEQ6HnJJ+j/mZi/q0920P99/MVRlB4L3crpF5w==}
|
||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||
|
|
@ -2178,6 +2212,27 @@ packages:
|
|||
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
spdx-compare@1.0.0:
|
||||
resolution: {integrity: sha512-C1mDZOX0hnu0ep9dfmuoi03+eOdDoz2yvK79RxbcrVEG1NO1Ph35yW102DHWKN4pk80nwCgeMmSY5L25VE4D9A==}
|
||||
|
||||
spdx-exceptions@2.5.0:
|
||||
resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==}
|
||||
|
||||
spdx-expression-parse@3.0.1:
|
||||
resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==}
|
||||
|
||||
spdx-expression-validate@2.0.0:
|
||||
resolution: {integrity: sha512-b3wydZLM+Tc6CFvaRDBOF9d76oGIHNCLYFeHbftFXUWjnfZWganmDmvtM5sm1cRwJc/VDBMLyGGrsLFd1vOxbg==}
|
||||
|
||||
spdx-license-ids@3.0.21:
|
||||
resolution: {integrity: sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==}
|
||||
|
||||
spdx-ranges@2.1.1:
|
||||
resolution: {integrity: sha512-mcdpQFV7UDAgLpXEE/jOMqvK4LBoO0uTQg0uvXUewmEFhpiZx5yJSZITHB8w1ZahKdhfZqP5GPEOKLyEq5p8XA==}
|
||||
|
||||
spdx-satisfies@5.0.1:
|
||||
resolution: {integrity: sha512-Nwor6W6gzFp8XX4neaKQ7ChV4wmpSh2sSDemMFSzHxpTw460jxFYeOn+jq4ybnSSw/5sc3pjka9MQPouksQNpw==}
|
||||
|
||||
sprintf-js@1.1.3:
|
||||
resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==}
|
||||
|
||||
|
|
@ -3187,6 +3242,8 @@ snapshots:
|
|||
call-bind: 1.0.7
|
||||
is-array-buffer: 3.0.4
|
||||
|
||||
array-find-index@1.0.2: {}
|
||||
|
||||
array-includes@3.1.8:
|
||||
dependencies:
|
||||
call-bind: 1.0.7
|
||||
|
|
@ -3412,6 +3469,8 @@ snapshots:
|
|||
|
||||
commander@4.1.1: {}
|
||||
|
||||
commenting@1.1.0: {}
|
||||
|
||||
compact2string@1.4.1:
|
||||
dependencies:
|
||||
ipaddr.js: 2.2.0
|
||||
|
|
@ -3884,6 +3943,10 @@ snapshots:
|
|||
optionalDependencies:
|
||||
picomatch: 4.0.2
|
||||
|
||||
fdir@6.4.4(picomatch@4.0.2):
|
||||
optionalDependencies:
|
||||
picomatch: 4.0.2
|
||||
|
||||
file-entry-cache@8.0.0:
|
||||
dependencies:
|
||||
flat-cache: 4.0.1
|
||||
|
|
@ -4218,6 +4281,8 @@ snapshots:
|
|||
|
||||
lodash.merge@4.6.2: {}
|
||||
|
||||
lodash@4.17.21: {}
|
||||
|
||||
lower-case@2.0.2:
|
||||
dependencies:
|
||||
tslib: 2.7.0
|
||||
|
|
@ -4261,6 +4326,8 @@ snapshots:
|
|||
|
||||
minipass@7.1.2: {}
|
||||
|
||||
moment@2.30.1: {}
|
||||
|
||||
mri@1.2.0: {}
|
||||
|
||||
mrmime@2.0.0: {}
|
||||
|
|
@ -4364,6 +4431,8 @@ snapshots:
|
|||
|
||||
package-json-from-dist@1.0.1: {}
|
||||
|
||||
package-name-regex@2.0.6: {}
|
||||
|
||||
parent-module@1.0.1:
|
||||
dependencies:
|
||||
callsites: 3.1.0
|
||||
|
|
@ -4518,6 +4587,20 @@ snapshots:
|
|||
|
||||
reusify@1.0.4: {}
|
||||
|
||||
rollup-plugin-license@3.6.0(picomatch@4.0.2)(rollup@4.27.2):
|
||||
dependencies:
|
||||
commenting: 1.1.0
|
||||
fdir: 6.4.4(picomatch@4.0.2)
|
||||
lodash: 4.17.21
|
||||
magic-string: 0.30.12
|
||||
moment: 2.30.1
|
||||
package-name-regex: 2.0.6
|
||||
rollup: 4.27.2
|
||||
spdx-expression-validate: 2.0.0
|
||||
spdx-satisfies: 5.0.1
|
||||
transitivePeerDependencies:
|
||||
- picomatch
|
||||
|
||||
rollup@4.27.2:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.6
|
||||
|
|
@ -4671,6 +4754,33 @@ snapshots:
|
|||
source-map@0.6.1:
|
||||
optional: true
|
||||
|
||||
spdx-compare@1.0.0:
|
||||
dependencies:
|
||||
array-find-index: 1.0.2
|
||||
spdx-expression-parse: 3.0.1
|
||||
spdx-ranges: 2.1.1
|
||||
|
||||
spdx-exceptions@2.5.0: {}
|
||||
|
||||
spdx-expression-parse@3.0.1:
|
||||
dependencies:
|
||||
spdx-exceptions: 2.5.0
|
||||
spdx-license-ids: 3.0.21
|
||||
|
||||
spdx-expression-validate@2.0.0:
|
||||
dependencies:
|
||||
spdx-expression-parse: 3.0.1
|
||||
|
||||
spdx-license-ids@3.0.21: {}
|
||||
|
||||
spdx-ranges@2.1.1: {}
|
||||
|
||||
spdx-satisfies@5.0.1:
|
||||
dependencies:
|
||||
spdx-compare: 1.0.0
|
||||
spdx-expression-parse: 3.0.1
|
||||
spdx-ranges: 2.1.1
|
||||
|
||||
sprintf-js@1.1.3: {}
|
||||
|
||||
streamx@2.20.1:
|
||||
|
|
|
|||
|
|
@ -1,52 +1,96 @@
|
|||
<script lang='ts'>
|
||||
|
||||
import { ChevronLeft, ChevronRight } from 'lucide-svelte'
|
||||
|
||||
import Pagination from '../../Pagination.svelte'
|
||||
import { Button } from '../button'
|
||||
|
||||
import { Comment } from './'
|
||||
|
||||
import type { client } from '$lib/modules/anilist'
|
||||
|
||||
export let comments: ReturnType<typeof client.comments>
|
||||
import { client } from '$lib/modules/anilist'
|
||||
import { isMobile } from '$lib/utils'
|
||||
|
||||
export let isLocked = false
|
||||
export let threadId: number
|
||||
|
||||
$: comments = client.comments(threadId, 1)
|
||||
|
||||
let currentPage = 1
|
||||
|
||||
const perPage = 15
|
||||
$: count = $comments.data?.Page?.pageInfo?.total ?? 0
|
||||
|
||||
$: console.log($comments.fetching, $comments.error, $comments.data?.Page?.threadComments)
|
||||
</script>
|
||||
|
||||
{#if $comments.fetching}
|
||||
{#each Array.from({ length: 4 }) as _, i (i)}
|
||||
<div class='px-4 py-[18px] shrink-0 h-28 w-full bg-neutral-950 rounded-md flex flex-col'>
|
||||
<div class='mb-3 h-2 w-[150px] bg-primary/5 animate-pulse rounded' />
|
||||
<div class='bg-primary/5 animate-pulse rounded h-1.5 w-28 mb-3' />
|
||||
<div class='bg-primary/5 animate-pulse rounded h-1.5 w-20' />
|
||||
<div class='mt-auto bg-primary/5 animate-pulse rounded h-2 w-24' />
|
||||
</div>
|
||||
{/each}
|
||||
{:else if $comments.error}
|
||||
<div class='p-5 flex items-center justify-center w-full h-80'>
|
||||
<div>
|
||||
<div class='mb-1 font-bold text-4xl text-center '>
|
||||
Ooops!
|
||||
<Pagination {count} {perPage} bind:currentPage let:pages let:hasNext let:hasPrev let:range let:setPage siblingCount={1}>
|
||||
{#if $comments.fetching}
|
||||
{#each Array.from({ length: 4 }) as _, i (i)}
|
||||
<div class='px-4 py-[18px] shrink-0 h-28 w-full bg-neutral-950 rounded-md flex flex-col'>
|
||||
<div class='mb-3 h-2 w-[150px] bg-primary/5 animate-pulse rounded' />
|
||||
<div class='bg-primary/5 animate-pulse rounded h-1.5 w-28 mb-3' />
|
||||
<div class='bg-primary/5 animate-pulse rounded h-1.5 w-20' />
|
||||
<div class='mt-auto bg-primary/5 animate-pulse rounded h-2 w-24' />
|
||||
</div>
|
||||
<div class='text-lg text-center text-muted-foreground'>
|
||||
Looks like something went wrong!
|
||||
</div>
|
||||
<div class='text-lg text-center text-muted-foreground'>
|
||||
{$comments.error.message}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
{#each $comments.data?.Page?.threadComments ?? [] as comment, i (comment?.id ?? i)}
|
||||
{#if comment}
|
||||
<Comment {comment} {isLocked} {threadId} />
|
||||
{/if}
|
||||
{:else}
|
||||
{/each}
|
||||
{:else if $comments.error}
|
||||
<div class='p-5 flex items-center justify-center w-full h-80'>
|
||||
<div>
|
||||
<div class='mb-1 font-bold text-4xl text-center '>
|
||||
Ooops!
|
||||
</div>
|
||||
<div class='text-lg text-center text-muted-foreground'>
|
||||
Looks like there's nothing here yet!
|
||||
Looks like something went wrong!
|
||||
</div>
|
||||
<div class='text-lg text-center text-muted-foreground'>
|
||||
{$comments.error.message}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
{:else}
|
||||
{#each $comments.data?.Page?.threadComments ?? [] as comment, i (comment?.id ?? i)}
|
||||
{#if comment}
|
||||
<Comment {comment} {isLocked} {threadId} />
|
||||
{/if}
|
||||
{:else}
|
||||
<div class='p-5 flex items-center justify-center w-full h-80'>
|
||||
<div>
|
||||
<div class='mb-1 font-bold text-4xl text-center '>
|
||||
Ooops!
|
||||
</div>
|
||||
<div class='text-lg text-center text-muted-foreground'>
|
||||
Looks like there's nothing here yet!
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
<div class='flex flex-row items-center justify-between w-full py-3'>
|
||||
<p class='text-center text-[13px] text-muted-foreground hidden md:block'>
|
||||
Showing <span class='font-bold'>{range.start + 1}</span> to <span class='font-bold'>{range.end}</span> of <span class='font-bold'>{count}</span> comments
|
||||
</p>
|
||||
<div class='w-full md:w-auto gap-2 flex items-center'>
|
||||
<Button size='icon' variant='ghost' on:click={() => setPage(currentPage - 1)} disabled={!hasPrev}>
|
||||
<ChevronLeft class='h-4 w-4' />
|
||||
</Button>
|
||||
{#if !$isMobile}
|
||||
{#each pages as { page, type } (page)}
|
||||
{#if type === 'ellipsis'}
|
||||
<span class='h-9 w-9 text-center'>...</span>
|
||||
{:else}
|
||||
<Button size='icon' variant={page === currentPage ? 'outline' : 'ghost'} on:click={() => setPage(page)}>
|
||||
{page}
|
||||
</Button>
|
||||
{/if}
|
||||
{/each}
|
||||
{:else}
|
||||
<p class='text-center text-[13px] text-muted-foreground w-full block md:hidden'>
|
||||
Showing <span class='font-bold'>{range.start + 1}</span> to <span class='font-bold'>{range.end}</span> of <span class='font-bold'>{count}</span> comments
|
||||
</p>
|
||||
{/if}
|
||||
<Button size='icon' variant='ghost' on:click={() => setPage(currentPage + 1)} disabled={!hasNext}>
|
||||
<ChevronRight class='h-4 w-4' />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Pagination>
|
||||
|
|
|
|||
|
|
@ -10,40 +10,25 @@
|
|||
|
||||
export let media: Media
|
||||
|
||||
let threads = [client.threads(media.id)]
|
||||
const threads = client.threads(media.id)
|
||||
|
||||
function getPage (page: number): ReturnType<typeof client.threads> {
|
||||
const index = page - 1
|
||||
const slice = threads[index]
|
||||
if (slice) return slice
|
||||
const missing = client.threads(media.id, page)
|
||||
threads[index] = missing
|
||||
|
||||
// eslint-disable-next-line no-self-assign
|
||||
threads = threads
|
||||
return missing
|
||||
}
|
||||
let currentPage = 1
|
||||
|
||||
$: currentPageStore = getPage(currentPage)
|
||||
|
||||
$: hasNextPage = $currentPageStore.data?.Page?.pageInfo?.hasNextPage ?? false
|
||||
|
||||
const perPage = 16
|
||||
$: count = (threads.length + Number(hasNextPage)) * perPage
|
||||
$: count = $threads.data?.Page?.pageInfo?.total ?? 0
|
||||
</script>
|
||||
|
||||
<Pagination {count} {perPage} bind:currentPage let:pages let:hasNext let:hasPrev let:range let:setPage siblingCount={1}>
|
||||
<div class='pt-3'>
|
||||
<div class='grid grid-cols-1 sm:grid-cols-[repeat(auto-fit,minmax(500px,1fr))] place-items-center gap-x-10 gap-y-7 justify-center align-middle'>
|
||||
{#if $currentPageStore.fetching}
|
||||
{#if $threads.fetching}
|
||||
{#each Array.from({ length: 4 }) as _, i (i)}
|
||||
<div class='px-4 py-[18px] shrink-0 h-[75px] w-full bg-neutral-950 rounded-md flex flex-col'>
|
||||
<div class='bg-primary/5 animate-pulse rounded h-2 w-28' />
|
||||
<div class='mt-auto bg-primary/5 animate-pulse rounded h-2 w-20' />
|
||||
</div>
|
||||
{/each}
|
||||
{:else if $currentPageStore.error}
|
||||
{:else if $threads.error}
|
||||
<div class='p-5 flex items-center justify-center w-full h-80'>
|
||||
<div>
|
||||
<div class='mb-1 font-bold text-4xl text-center '>
|
||||
|
|
@ -53,12 +38,12 @@
|
|||
Looks like something went wrong!
|
||||
</div>
|
||||
<div class='text-lg text-center text-muted-foreground'>
|
||||
{$currentPageStore.error.message}
|
||||
{$threads.error.message}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
{#each $currentPageStore.data?.Page?.threads ?? [] as thread, i (thread?.id ?? i)}
|
||||
{#each $threads.data?.Page?.threads ?? [] as thread, i (thread?.id ?? i)}
|
||||
{#if thread}
|
||||
<a href='./thread/{thread.id}' class= 'select:scale-[1.05] select:shadow-lg scale-100 transition-[transform,box-shadow] duration-200 shrink-0 ease-out focus-visible:ring-ring focus-visible:ring-1 rounded-md bg-neutral-950 text-secondary-foreground select:bg-neutral-900 flex w-full max-h-28 relative overflow-hidden cursor-pointer'>
|
||||
<div class='flex-grow py-3 px-4 flex flex-col'>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
<TabsPrimitive.Trigger
|
||||
class={cn(
|
||||
'ring-offset-background focus-visible:ring-ring data-[state=active]:bg-background data-[state=active]:text-foreground inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow',
|
||||
'ring-offset-background focus-visible:ring-ring data-[state=active]:bg-foreground data-[state=active]:text-background inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow',
|
||||
className
|
||||
)}
|
||||
{value}
|
||||
|
|
|
|||
8
src/lib/modules/anilist/graphql-turbo.d.ts
vendored
8
src/lib/modules/anilist/graphql-turbo.d.ts
vendored
|
|
@ -36,14 +36,14 @@ declare module 'gql.tada' {
|
|||
TadaDocumentNode<{ ToggleFavourite: { anime: { nodes: ({ id: number; } | null)[] | null; } | null; } | null; }, { id: number; }, void>;
|
||||
"\n fragment ThreadFrag on Thread @_unmask {\n id,\n title,\n body,\n userId,\n replyCount,\n viewCount,\n isLocked,\n isSubscribed,\n isLiked,\n likeCount,\n repliedAt,\n createdAt,\n user {\n ...UserFrag\n },\n categories {\n id,\n name\n }\n }\n":
|
||||
TadaDocumentNode<{ id: number; title: string | null; body: string | null; userId: number; replyCount: number | null; viewCount: number | null; isLocked: boolean | null; isSubscribed: boolean | null; isLiked: boolean | null; likeCount: number; repliedAt: number | null; createdAt: number; user: { id: number; bannerImage: string | null; about: string | null; isFollowing: boolean | null; isFollower: boolean | null; donatorBadge: string | null; options: { profileColor: string | null; } | null; createdAt: number | null; name: string; avatar: { medium: string | null; } | null; statistics: { anime: { count: number; minutesWatched: number; episodesWatched: number; genres: ({ genre: string | null; count: number; } | null)[] | null; } | null; } | null; } | null; categories: ({ id: number; name: string; } | null)[] | null; }, {}, { fragment: "ThreadFrag"; on: "Thread"; masked: false; }>;
|
||||
"\n query Threads($id: Int!, $page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n pageInfo {\n hasNextPage\n },\n threads(mediaCategoryId: $id, sort: ID_DESC) {\n ...ThreadFrag\n }\n }\n }\n":
|
||||
TadaDocumentNode<{ Page: { pageInfo: { hasNextPage: boolean | null; } | null; threads: ({ id: number; title: string | null; body: string | null; userId: number; replyCount: number | null; viewCount: number | null; isLocked: boolean | null; isSubscribed: boolean | null; isLiked: boolean | null; likeCount: number; repliedAt: number | null; createdAt: number; user: { id: number; bannerImage: string | null; about: string | null; isFollowing: boolean | null; isFollower: boolean | null; donatorBadge: string | null; options: { profileColor: string | null; } | null; createdAt: number | null; name: string; avatar: { medium: string | null; } | null; statistics: { anime: { count: number; minutesWatched: number; episodesWatched: number; genres: ({ genre: string | null; count: number; } | null)[] | null; } | null; } | null; } | null; categories: ({ id: number; name: string; } | null)[] | null; } | null)[] | null; } | null; }, { perPage?: number | null | undefined; page?: number | null | undefined; id: number; }, void>;
|
||||
"\n query Threads($id: Int!, $page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n pageInfo {\n hasNextPage,\n total\n },\n threads(mediaCategoryId: $id, sort: ID_DESC) {\n ...ThreadFrag\n }\n }\n }\n":
|
||||
TadaDocumentNode<{ Page: { pageInfo: { hasNextPage: boolean | null; total: number | null; } | null; threads: ({ id: number; title: string | null; body: string | null; userId: number; replyCount: number | null; viewCount: number | null; isLocked: boolean | null; isSubscribed: boolean | null; isLiked: boolean | null; likeCount: number; repliedAt: number | null; createdAt: number; user: { id: number; bannerImage: string | null; about: string | null; isFollowing: boolean | null; isFollower: boolean | null; donatorBadge: string | null; options: { profileColor: string | null; } | null; createdAt: number | null; name: string; avatar: { medium: string | null; } | null; statistics: { anime: { count: number; minutesWatched: number; episodesWatched: number; genres: ({ genre: string | null; count: number; } | null)[] | null; } | null; } | null; } | null; categories: ({ id: number; name: string; } | null)[] | null; } | null)[] | null; } | null; }, { perPage?: number | null | undefined; page?: number | null | undefined; id: number; }, void>;
|
||||
"\n query Thread($threadId: Int!) {\n Thread(id: $threadId) {\n ...ThreadFrag\n }\n }\n":
|
||||
TadaDocumentNode<{ Thread: { id: number; title: string | null; body: string | null; userId: number; replyCount: number | null; viewCount: number | null; isLocked: boolean | null; isSubscribed: boolean | null; isLiked: boolean | null; likeCount: number; repliedAt: number | null; createdAt: number; user: { id: number; bannerImage: string | null; about: string | null; isFollowing: boolean | null; isFollower: boolean | null; donatorBadge: string | null; options: { profileColor: string | null; } | null; createdAt: number | null; name: string; avatar: { medium: string | null; } | null; statistics: { anime: { count: number; minutesWatched: number; episodesWatched: number; genres: ({ genre: string | null; count: number; } | null)[] | null; } | null; } | null; } | null; categories: ({ id: number; name: string; } | null)[] | null; } | null; }, { threadId: number; }, void>;
|
||||
"\n fragment CommentFrag on ThreadComment @_unmask {\n id,\n comment,\n isLiked,\n likeCount,\n createdAt,\n user {\n ...UserFrag\n },\n childComments,\n isLocked\n }\n":
|
||||
TadaDocumentNode<{ id: number; comment: string | null; isLiked: boolean | null; likeCount: number; createdAt: number; user: { id: number; bannerImage: string | null; about: string | null; isFollowing: boolean | null; isFollower: boolean | null; donatorBadge: string | null; options: { profileColor: string | null; } | null; createdAt: number | null; name: string; avatar: { medium: string | null; } | null; statistics: { anime: { count: number; minutesWatched: number; episodesWatched: number; genres: ({ genre: string | null; count: number; } | null)[] | null; } | null; } | null; } | null; childComments: unknown; isLocked: boolean | null; }, {}, { fragment: "CommentFrag"; on: "ThreadComment"; masked: false; }>;
|
||||
"\n query Comments($threadId: Int, $page: Int) {\n Page(page: $page, perPage: 15) {\n pageInfo {\n hasNextPage\n }\n threadComments(threadId: $threadId) {\n id,\n comment,\n isLiked,\n likeCount,\n createdAt,\n user {\n id,\n name,\n avatar {\n medium\n },\n },\n childComments,\n isLocked\n }\n }\n }\n":
|
||||
TadaDocumentNode<{ Page: { pageInfo: { hasNextPage: boolean | null; } | null; threadComments: ({ id: number; comment: string | null; isLiked: boolean | null; likeCount: number; createdAt: number; user: { id: number; name: string; avatar: { medium: string | null; } | null; } | null; childComments: unknown; isLocked: boolean | null; } | null)[] | null; } | null; }, { page?: number | null | undefined; threadId?: number | null | undefined; }, void>;
|
||||
"\n query Comments($threadId: Int, $page: Int) {\n Page(page: $page, perPage: 15) {\n pageInfo {\n hasNextPage,\n total\n }\n threadComments(threadId: $threadId) {\n id,\n comment,\n isLiked,\n likeCount,\n createdAt,\n user {\n id,\n bannerImage,\n about,\n isFollowing,\n isFollower,\n donatorBadge,\n options {\n profileColor\n },\n createdAt,\n name,\n avatar {\n medium\n },\n statistics {\n anime {\n count,\n minutesWatched,\n episodesWatched,\n genres(limit: 3, sort: COUNT_DESC) {\n genre,\n count\n }\n }\n }\n },\n childComments,\n isLocked\n }\n }\n }\n":
|
||||
TadaDocumentNode<{ Page: { pageInfo: { hasNextPage: boolean | null; total: number | null; } | null; threadComments: ({ id: number; comment: string | null; isLiked: boolean | null; likeCount: number; createdAt: number; user: { id: number; bannerImage: string | null; about: string | null; isFollowing: boolean | null; isFollower: boolean | null; donatorBadge: string | null; options: { profileColor: string | null; } | null; createdAt: number | null; name: string; avatar: { medium: string | null; } | null; statistics: { anime: { count: number; minutesWatched: number; episodesWatched: number; genres: ({ genre: string | null; count: number; } | null)[] | null; } | null; } | null; } | null; childComments: unknown; isLocked: boolean | null; } | null)[] | null; } | null; }, { page?: number | null | undefined; threadId?: number | null | undefined; }, void>;
|
||||
"\n mutation ToggleLike ($id: Int!, $type: LikeableType!) {\n ToggleLikeV2(id: $id, type: $type) {\n ... on Thread {\n id\n likeCount\n isLiked\n }\n ... on ThreadComment {\n id\n likeCount\n isLiked\n }\n }\n }\n":
|
||||
TadaDocumentNode<{ ToggleLikeV2: { __typename?: "ActivityReply" | undefined; } | { __typename?: "ListActivity" | undefined; } | { __typename?: "MessageActivity" | undefined; } | { __typename?: "TextActivity" | undefined; } | { __typename?: "Thread" | undefined; id: number; likeCount: number; isLiked: boolean | null; } | { __typename?: "ThreadComment" | undefined; id: number; likeCount: number; isLiked: boolean | null; } | null; }, { type: "THREAD" | "THREAD_COMMENT" | "ACTIVITY" | "ACTIVITY_REPLY"; id: number; }, void>;
|
||||
"\n mutation SaveThreadComment ($id: Int, $threadId: Int, $parentCommentId: Int, $comment: String) {\n SaveThreadComment(id: $id, threadId: $threadId, parentCommentId: $parentCommentId, comment: $comment) {\n ...CommentFrag\n }\n }\n":
|
||||
|
|
|
|||
|
|
@ -351,7 +351,8 @@ export const Threads = gql(`
|
|||
query Threads($id: Int!, $page: Int, $perPage: Int) {
|
||||
Page(page: $page, perPage: $perPage) {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
hasNextPage,
|
||||
total
|
||||
},
|
||||
threads(mediaCategoryId: $id, sort: ID_DESC) {
|
||||
...ThreadFrag
|
||||
|
|
@ -388,7 +389,8 @@ export const Comments = gql(`
|
|||
query Comments($threadId: Int, $page: Int) {
|
||||
Page(page: $page, perPage: 15) {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
hasNextPage,
|
||||
total
|
||||
}
|
||||
threadComments(threadId: $threadId) {
|
||||
id,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
import { Button } from '$lib/components/ui/button'
|
||||
import { Threads } from '$lib/components/ui/forums'
|
||||
import { Load } from '$lib/components/ui/img'
|
||||
import * as Tabs from '$lib/components/ui/tabs'
|
||||
import { format, relation } from '$lib/modules/anilist'
|
||||
import { authAggregator } from '$lib/modules/auth'
|
||||
import { dragScroll } from '$lib/modules/navigate'
|
||||
|
|
@ -27,6 +28,8 @@
|
|||
$: following = authAggregator.following(mediaID)
|
||||
|
||||
$: eps = data.eps
|
||||
|
||||
let value: string
|
||||
</script>
|
||||
|
||||
{#if relations?.length}
|
||||
|
|
@ -56,17 +59,20 @@
|
|||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<div class='w-full flex flex-col'>
|
||||
<div class='flex justify-between items-center'>
|
||||
<div class='text-[20px] md:text-2xl font-bold'>Episodes</div>
|
||||
<Tabs.Root bind:value class='w-full'>
|
||||
<div class='flex justify-between items-center gap-3 sm:flex-row flex-col'>
|
||||
<Tabs.List class='flex'>
|
||||
<Tabs.Trigger value='episodes' class='px-8 data-[state=active]:font-bold'>Episodes</Tabs.Trigger>
|
||||
<Tabs.Trigger value='threads' class='px-8 data-[state=active]:font-bold'>Threads</Tabs.Trigger>
|
||||
<Tabs.Trigger value='themes' class='px-8 data-[state=active]:font-bold' disabled>Themes</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
</div>
|
||||
<EpisodesList {media} {eps} {following} />
|
||||
</div>
|
||||
<div class='w-full flex flex-col'>
|
||||
<div class='flex justify-between items-center'>
|
||||
<div class='text-[20px] md:text-2xl font-bold'>Threads</div>
|
||||
</div>
|
||||
{#key mediaID}
|
||||
<Threads {media} />
|
||||
{/key}
|
||||
</div>
|
||||
<Tabs.Content value='episodes' tabindex={-1}>
|
||||
<EpisodesList {media} {eps} {following} />
|
||||
</Tabs.Content>
|
||||
<Tabs.Content value='threads' tabindex={-1}>
|
||||
{#key mediaID}
|
||||
<Threads {media} />
|
||||
{/key}
|
||||
</Tabs.Content>
|
||||
</Tabs.Root>
|
||||
|
|
|
|||
|
|
@ -15,25 +15,9 @@
|
|||
$: threadStore = data.thread
|
||||
$: thread = $threadStore.Thread!
|
||||
|
||||
let page = 0
|
||||
|
||||
let commentQueries: Array<ReturnType<typeof client.comments>> = []
|
||||
|
||||
function loadComments () {
|
||||
page += 1
|
||||
commentQueries[page - 1] = client.comments(thread.id, page)
|
||||
// eslint-disable-next-line no-self-assign
|
||||
commentQueries = commentQueries
|
||||
}
|
||||
|
||||
$: commentQueries = [client.comments(thread.id, 1)]
|
||||
|
||||
$: anime = data.anime
|
||||
$: media = $anime.Media!
|
||||
|
||||
$: latestQuery = commentQueries[commentQueries.length - 1]
|
||||
$: hasMore = $latestQuery?.data?.Page?.pageInfo?.hasNextPage ?? false
|
||||
|
||||
const viewer = client.viewer
|
||||
</script>
|
||||
|
||||
|
|
@ -91,9 +75,4 @@
|
|||
<div class='flex justify-between items-center w-full text-[20px] md:text-2xl font-bold'>
|
||||
{thread.replyCount} Replies
|
||||
</div>
|
||||
{#each commentQueries as comments, i (i)}
|
||||
<Comments bind:comments isLocked={!!thread.isLocked} threadId={thread.id} />
|
||||
{/each}
|
||||
{#if hasMore}
|
||||
<Button size='lg' class='w-full font-bold' on:click={loadComments}>Load more comments</Button>
|
||||
{/if}
|
||||
<Comments isLocked={!!thread.isLocked} threadId={thread.id} />
|
||||
|
|
|
|||
|
|
@ -1,8 +1,20 @@
|
|||
import { resolve } from 'node:path'
|
||||
|
||||
import { sveltekit } from '@sveltejs/kit/vite'
|
||||
import license from 'rollup-plugin-license'
|
||||
import { defineConfig } from 'vite'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [sveltekit()],
|
||||
plugins: [
|
||||
sveltekit(),
|
||||
license({
|
||||
thirdParty: {
|
||||
allow: '(MIT OR Apache-2.0 OR ISC OR BSD-3-Clause OR BSD-2-Clause)',
|
||||
output: resolve(__dirname, './build/LICENSE.txt'),
|
||||
includeSelf: true
|
||||
}
|
||||
})
|
||||
],
|
||||
server: { port: 7344 },
|
||||
build: {
|
||||
target: 'esnext'
|
||||
|
|
|
|||
Loading…
Reference in a new issue