mirror of
https://github.com/danbulant/Mangades
synced 2026-07-05 02:50:34 +00:00
use filters
This commit is contained in:
parent
e1d90cc5f5
commit
dcd831cecc
4 changed files with 162 additions and 8 deletions
62
src/components/multiSelect.svelte
Normal file
62
src/components/multiSelect.svelte
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
<script>
|
||||||
|
export var value = [];
|
||||||
|
export var id;
|
||||||
|
export var name = id;
|
||||||
|
|
||||||
|
export var options;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {MouseEvent} e
|
||||||
|
* @param {string} name
|
||||||
|
*/
|
||||||
|
function select(e, name) {
|
||||||
|
if(value.includes(name)) {
|
||||||
|
value.splice(value.indexOf(name), 1);
|
||||||
|
value = value;
|
||||||
|
} else {
|
||||||
|
value.push(name);
|
||||||
|
value = value;
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
/** @type {HTMLOptionElement} */
|
||||||
|
const opt = e.target;
|
||||||
|
opt.parentElement.focus();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {MouseEvent} e
|
||||||
|
* @param {string} name
|
||||||
|
*/
|
||||||
|
function dragSelect(e, name) {
|
||||||
|
if(!(e.buttons & 1)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!value.includes(name)) {
|
||||||
|
value.push(name);
|
||||||
|
value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<select multiple {name} {id} bind:value={value}>
|
||||||
|
{#each Object.entries(options) as [name, option]}
|
||||||
|
<option value={name} on:mousemove={e => dragSelect(e, name)} on:mousedown={(e) => select(e, name)}>{option}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
select {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 5px;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
select option {
|
||||||
|
padding: 2px 5px;
|
||||||
|
}
|
||||||
|
select option:checked {
|
||||||
|
background-color: rgb(0, 38, 255);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
|
|
||||||
var progress = 0;
|
var progress = 0;
|
||||||
var state = "idle";
|
var state = "idle";
|
||||||
const defaultText = "Choose a chapter to view online or download EPUB";
|
const defaultText = "Choose a chapter to view online or download";
|
||||||
var text = defaultText;
|
var text = defaultText;
|
||||||
var pagesDone = 0;
|
var pagesDone = 0;
|
||||||
var totalPages = 0;
|
var totalPages = 0;
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
import request from "../util/request";
|
import request from "../util/request";
|
||||||
import ratelimit from "../util/ratelimit";
|
import ratelimit from "../util/ratelimit";
|
||||||
import { goto, url } from '@roxi/routify/runtime/helpers';
|
import { goto, url } from '@roxi/routify/runtime/helpers';
|
||||||
|
import MultiSelect from '../components/multiSelect.svelte';
|
||||||
|
|
||||||
var name = $params.search;
|
var name = $params.search;
|
||||||
$: {
|
$: {
|
||||||
|
|
@ -15,10 +16,21 @@
|
||||||
* @param {string} title
|
* @param {string} title
|
||||||
* @returns {Promise<object>}
|
* @returns {Promise<object>}
|
||||||
*/
|
*/
|
||||||
async function search(title, offset=0) {
|
async function search(title, filters, offset=0) {
|
||||||
var query = "";
|
var query = new URLSearchParams();
|
||||||
if(title) query += "title=" + encodeURIComponent(name) + "&";
|
if(title) query.set("title", title);
|
||||||
query += "contentRating[]=safe&contentRating[]=suggestive&limit=100&offset=" + offset;
|
query.set("limit", 100);
|
||||||
|
query.set("offset", offset);
|
||||||
|
for(const rating of filters.contentRating) {
|
||||||
|
query.append("contentRating[]", rating);
|
||||||
|
}
|
||||||
|
for(const demographic of filters.demographic) {
|
||||||
|
query.append("demographic[]", demographic);
|
||||||
|
}
|
||||||
|
for(const status of filters.status) {
|
||||||
|
query.append("status[]", status);
|
||||||
|
}
|
||||||
|
query.set("order[" + filters.sort + "]", filters.sortValue);
|
||||||
const res = await request("manga", query);
|
const res = await request("manga", query);
|
||||||
for(const manga of res.results) {
|
for(const manga of res.results) {
|
||||||
for(const relation of manga.relationships) {
|
for(const relation of manga.relationships) {
|
||||||
|
|
@ -30,7 +42,7 @@
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
$: result = ratelimit(search, name);
|
$: result = ratelimit(search, name, filters);
|
||||||
|
|
||||||
var scrollSearch = null;
|
var scrollSearch = null;
|
||||||
/**
|
/**
|
||||||
|
|
@ -40,7 +52,7 @@
|
||||||
if(scrollSearch !== null) return;
|
if(scrollSearch !== null) return;
|
||||||
if(document.body.scrollHeight - window.scrollY - window.innerHeight < 300 && (await result).results.length < (await result).total) {
|
if(document.body.scrollHeight - window.scrollY - window.innerHeight < 300 && (await result).results.length < (await result).total) {
|
||||||
scrollSearch = name;
|
scrollSearch = name;
|
||||||
const res = await search(name, (await result).results.length);
|
const res = await search(name, filters, (await result).results.length);
|
||||||
if(scrollSearch === name && res.results.length) {
|
if(scrollSearch === name && res.results.length) {
|
||||||
(await result).results.push(...res.results);
|
(await result).results.push(...res.results);
|
||||||
result = result; // trigger reload
|
result = result; // trigger reload
|
||||||
|
|
@ -57,6 +69,35 @@
|
||||||
const res = await request("manga/random");
|
const res = await request("manga/random");
|
||||||
$goto("./" + res.data.id);
|
$goto("./" + res.data.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const filters = {
|
||||||
|
contentRating: ["safe", "suggestive"],
|
||||||
|
demographic: [],
|
||||||
|
status: [],
|
||||||
|
sort: "updatedAt",
|
||||||
|
sortValue: "desc"
|
||||||
|
};
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
contentRating: {
|
||||||
|
safe: "Safe",
|
||||||
|
suggestive: "Suggestive",
|
||||||
|
erotica: "Erotica",
|
||||||
|
pornographic: "Pornographic"
|
||||||
|
},
|
||||||
|
demographic: {
|
||||||
|
shounen: "Shounen",
|
||||||
|
shoujo: "Shoujo",
|
||||||
|
josei: "Josei",
|
||||||
|
none: "None"
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
ongoing: "Ongoing",
|
||||||
|
completed: "Completed",
|
||||||
|
hiatus: "Hiatus - Paused",
|
||||||
|
cancelled: "Cancelled"
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:window on:scroll={scroll} />
|
<svelte:window on:scroll={scroll} />
|
||||||
|
|
@ -71,6 +112,33 @@
|
||||||
<h1>MANGADEX</h1>
|
<h1>MANGADEX</h1>
|
||||||
<input type="text" bind:value={name}>
|
<input type="text" bind:value={name}>
|
||||||
|
|
||||||
|
<div class="filters">
|
||||||
|
<div class="grow">
|
||||||
|
<label for="content-rating">Content rating</label>
|
||||||
|
<MultiSelect id="content-rating" bind:value={filters.contentRating} options={options.contentRating} />
|
||||||
|
</div>
|
||||||
|
<div class="grow">
|
||||||
|
<label for="demographic">Demographic</label>
|
||||||
|
<MultiSelect id="demographic" bind:value={filters.demographic} options={options.demographic} />
|
||||||
|
</div>
|
||||||
|
<div class="grow">
|
||||||
|
<label for="status">Manga Status</label>
|
||||||
|
<MultiSelect id="status" bind:value={filters.status} options={options.status} />
|
||||||
|
</div>
|
||||||
|
<p style="margin: 0;"><i>Sorts currently don't work (mangadex shows the same results).</i></p>
|
||||||
|
<div class="flex">
|
||||||
|
<label for="sort">Sort by:</label>
|
||||||
|
<select name="sort" id="sort" class="flex-grow" bind:value={filters.sort}>
|
||||||
|
<option value="createdAt">Creation date</option>
|
||||||
|
<option value="updatedAt">Update date</option>
|
||||||
|
</select>
|
||||||
|
<select name="sort-type" id="sort-type" class="flex-grow" bind:value={filters.sortValue}>
|
||||||
|
<option value="asc">Ascending</option>
|
||||||
|
<option value="desc">Descending</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{#await result}
|
{#await result}
|
||||||
Loading...
|
Loading...
|
||||||
{:then result}
|
{:then result}
|
||||||
|
|
@ -115,6 +183,30 @@
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.filters {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.flex {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.flex select {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.grow {
|
||||||
|
flex-grow: 1;
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
.flex select.flex-grow {
|
||||||
|
flex-grow: 1;
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
input {
|
input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ export const proxy = "https://cors-anywhere.danbulant.workers.dev/?";
|
||||||
export const base = proxy + "https://api.mangadex.org/";
|
export const base = proxy + "https://api.mangadex.org/";
|
||||||
|
|
||||||
function request(endpoint, query, type = "GET", body) {
|
function request(endpoint, query, type = "GET", body) {
|
||||||
return fetch(base + endpoint + (query ? "?" + query : ""), {
|
return fetch(base + endpoint + (query ? "?" + query.toString() : ""), {
|
||||||
method: type,
|
method: type,
|
||||||
body: body ? JSON.stringify(body) : undefined
|
body: body ? JSON.stringify(body) : undefined
|
||||||
}).then(resp => resp.json());
|
}).then(resp => resp.json());
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue