mirror of
https://github.com/danbulant/Mangades
synced 2026-06-19 14:21:23 +00:00
add nsfw; save show type and nsfw mode to local storage
This commit is contained in:
parent
8056f1a1c3
commit
c6c3cf57f0
6 changed files with 159 additions and 32 deletions
|
|
@ -1,5 +1,6 @@
|
|||
<script lang="ts">
|
||||
import SvelteMarkdown from "svelte-markdown";
|
||||
import { showNsfw } from "./showNsfwChooser.svelte";
|
||||
import { showType } from "./showTypeChooser.svelte";
|
||||
|
||||
export var r18: boolean = false;
|
||||
|
|
@ -18,15 +19,13 @@
|
|||
</script>
|
||||
|
||||
<div on:click class="item" class:grid={$showType == "grid"} class:comfortable={$showType == "comfortable"} class:coverOnly={$showType == "cover-only"} class:list={$showType == "list"}>
|
||||
<div class="flex" class:r18>
|
||||
<div class="flex" class:r18={r18 && $showNsfw !== "show"}>
|
||||
{#if cover}
|
||||
<div class="cover-container" width={coverWidth} height={coverHeight}>
|
||||
<img class="cover" style="{coverColor ? "--box-shadow-color: " + coverColor : ""}" draggable="false" src="{cover}" alt="{title}" {title} width={coverWidth} height={coverHeight}>
|
||||
{#if $showType == "grid"}
|
||||
<div class="over">
|
||||
{title}
|
||||
</div>
|
||||
{/if}
|
||||
<div class="over" class:hidden={$showType !== "grid"}>
|
||||
{title}
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
Broken art
|
||||
|
|
@ -47,11 +46,9 @@
|
|||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{#if $showType == "comfortable"}
|
||||
<div>
|
||||
{title}
|
||||
</div>
|
||||
{/if}
|
||||
<div class="comfortable-div" class:hidden={$showType !== "comfortable"}>
|
||||
{title}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
|
@ -113,6 +110,27 @@
|
|||
color: white;
|
||||
padding: 1.5rem 0.5rem 0.5rem 0.5rem;
|
||||
border-radius: 5px;
|
||||
user-select: initial;
|
||||
opacity: 1;
|
||||
transition: .3s opacity;
|
||||
}
|
||||
.comfortable-div {
|
||||
user-select: initial;
|
||||
opacity: 1;
|
||||
transition: .3s opacity;
|
||||
line-height: 1.5rem;
|
||||
display: -webkit-box;
|
||||
max-height: calc(2rem + 3*1.4rem);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
transition: max-height .3s;
|
||||
}
|
||||
.hidden {
|
||||
opacity: 0;
|
||||
user-select: none;
|
||||
max-height: 0;
|
||||
}
|
||||
.item img {
|
||||
--box-shadow-color: white;
|
||||
|
|
@ -121,10 +139,10 @@
|
|||
width: auto;
|
||||
max-width: 100%;
|
||||
box-shadow: 0 0 0 var(--box-shadow-color);
|
||||
transition: .4s box-shadow, .3s height, .4s filter;
|
||||
transition: .4s box-shadow, .3s max-height, .4s filter;
|
||||
}
|
||||
.item.list img.cover {
|
||||
height: 8rem;
|
||||
max-height: 8rem;
|
||||
}
|
||||
.item:hover img {
|
||||
box-shadow: 0 0 20px var(--box-shadow-color);
|
||||
|
|
|
|||
34
src/lib/components/showNsfwChooser.svelte
Normal file
34
src/lib/components/showNsfwChooser.svelte
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<script lang="ts" context="module">
|
||||
import type { Writable } from "svelte/store";
|
||||
import { storedWritable } from "../util/storedWritable";
|
||||
|
||||
export const showNsfw: Writable<"hide" | "blur" | "show"> = storedWritable("nsfw-mode", "blur");
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<button on:click={() => $showNsfw = "hide"} class:active={$showNsfw == "hide"}>Hide</button><!--
|
||||
--><button on:click={() => $showNsfw = "blur"} class:active={$showNsfw == "blur"}>Blur</button><!--
|
||||
--><button on:click={() => $showNsfw = "show"} class:active={$showNsfw == "show"}>Show</button>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
div {
|
||||
width: max-content;
|
||||
user-select: none;
|
||||
}
|
||||
button {
|
||||
border-radius: 0;
|
||||
margin: 0;
|
||||
}
|
||||
button:first-child {
|
||||
border-top-left-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
}
|
||||
button:last-child {
|
||||
border-top-right-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
}
|
||||
button.active {
|
||||
background: rgb(202, 202, 202);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
<script context="module" lang="ts">
|
||||
import { writable, type Writable } from "svelte/store";
|
||||
import { storedWritable } from "$lib/util/storedWritable";
|
||||
import type { Writable } from "svelte/store";
|
||||
|
||||
export var showType: Writable<"grid" | "comfortable" | "list" | "cover-only"> = writable("grid");
|
||||
export var showType: Writable<"grid" | "comfortable" | "list" | "cover-only"> = storedWritable("show-mode", "grid");
|
||||
</script>
|
||||
|
||||
<div>
|
||||
|
|
@ -15,6 +16,7 @@
|
|||
div {
|
||||
margin-left: auto;
|
||||
width: max-content;
|
||||
user-select: none;
|
||||
}
|
||||
button {
|
||||
border-radius: 0;
|
||||
|
|
|
|||
74
src/lib/util/storedWritable.ts
Normal file
74
src/lib/util/storedWritable.ts
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
import {writable as internal, get, type Writable} from 'svelte/store'
|
||||
|
||||
declare type Updater<T> = (value: T) => T;
|
||||
declare type StoreDict<T> = { [key: string]: Writable<T> }
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
const stores: StoreDict<any> = {}
|
||||
|
||||
interface Serializer<T> {
|
||||
parse(text: string): T
|
||||
stringify(object: T): string
|
||||
}
|
||||
|
||||
type StorageType = 'local' | 'session'
|
||||
|
||||
interface Options<T> {
|
||||
serializer?: Serializer<T>
|
||||
storage?: StorageType
|
||||
}
|
||||
|
||||
function getStorage(type: StorageType) {
|
||||
return type === 'local' ? localStorage : sessionStorage
|
||||
}
|
||||
|
||||
export function storedWritable<T>(key: string, initialValue: T, options?: Options<T>): Writable<T> {
|
||||
const serializer = options?.serializer ?? JSON
|
||||
const storageType = options?.storage ?? 'local'
|
||||
const browser = typeof(window) !== 'undefined' && typeof(document) !== 'undefined'
|
||||
|
||||
function updateStorage(key: string, value: T) {
|
||||
if (!browser) return
|
||||
|
||||
getStorage(storageType).setItem(key, serializer.stringify(value))
|
||||
}
|
||||
|
||||
if (!stores[key]) {
|
||||
const store = internal(initialValue, (set) => {
|
||||
const json = browser ? getStorage(storageType).getItem(key) : null
|
||||
|
||||
if (json) {
|
||||
set(<T>serializer.parse(json))
|
||||
}
|
||||
|
||||
if (browser) {
|
||||
const handleStorage = (event: StorageEvent) => {
|
||||
if (event.key === key)
|
||||
set(event.newValue ? serializer.parse(event.newValue) : null)
|
||||
}
|
||||
|
||||
window.addEventListener("storage", handleStorage)
|
||||
|
||||
return () => window.removeEventListener("storage", handleStorage)
|
||||
}
|
||||
})
|
||||
|
||||
const {subscribe, set} = store
|
||||
|
||||
stores[key] = {
|
||||
set(value: T) {
|
||||
updateStorage(key, value)
|
||||
set(value)
|
||||
},
|
||||
update(updater: Updater<T>) {
|
||||
const value = updater(get(store))
|
||||
|
||||
updateStorage(key, value)
|
||||
set(value)
|
||||
},
|
||||
subscribe
|
||||
}
|
||||
}
|
||||
|
||||
return stores[key]
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
import type { load } from "./+page";
|
||||
import Navbar from "$lib/components/navbar.svelte";
|
||||
import ShowNsfwChooser, { showNsfw } from "$lib/components/showNsfwChooser.svelte";
|
||||
export var data: Awaited<ReturnType<typeof load>>;
|
||||
|
||||
var name: string = typeof window === "undefined" ? "" : data.url.searchParams.get("search") || "";
|
||||
|
|
@ -18,8 +19,6 @@
|
|||
history.replaceState(history.state, "", url.toString());
|
||||
}
|
||||
|
||||
var allowNSFW = false;
|
||||
|
||||
const filters = {
|
||||
contentRating: ["safe", "suggestive"],
|
||||
demographic: [],
|
||||
|
|
@ -28,7 +27,7 @@
|
|||
sortValue: "desc"
|
||||
};
|
||||
|
||||
filters.contentRating = allowNSFW ? [] : ["safe", "suggestive"];
|
||||
filters.contentRating = $showNsfw !== "hide" ? ["safe", "suggestive", "erotica", "pornographic"] : ["safe", "suggestive"];
|
||||
|
||||
/**
|
||||
* Searches for results
|
||||
|
|
@ -109,7 +108,7 @@
|
|||
$: if(userDetails) {
|
||||
userDetails.then(data => {
|
||||
if(data.data.User.options.nsfwContent) {
|
||||
allowNSFW = true;
|
||||
$showNsfw = "show";
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -130,10 +129,7 @@
|
|||
<div class="flex" style="margin-top: 1rem;">
|
||||
<div>
|
||||
<div class="nsfw">
|
||||
<input id="nsfw" type="checkbox" bind:checked={allowNSFW}>
|
||||
<label for="nsfw">
|
||||
Allow NSFW
|
||||
</label>
|
||||
<ShowNsfwChooser />
|
||||
</div>
|
||||
<a href="https://discord.gg/XKPbz5xRuK">Made by TechmandanCZ#3372</a>
|
||||
</div>
|
||||
|
|
@ -170,20 +166,12 @@
|
|||
</main>
|
||||
|
||||
<style lang="postcss">
|
||||
.nsfw > input, .nsfw > label {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
}
|
||||
.flex {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
input {
|
||||
width: 100%;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
a:not(.button) {
|
||||
color: rgb(86, 139, 255);
|
||||
text-decoration: underline;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<script lang="ts">
|
||||
import Item from "$lib/components/item.svelte";
|
||||
import ShowNsfwChooser, { showNsfw } from "$lib/components/showNsfwChooser.svelte";
|
||||
import ShowTypeChooser, { showType } from "$lib/components/showTypeChooser.svelte";
|
||||
import request, { imageproxy } from "$lib/util/request";
|
||||
import { createEventDispatcher, tick } from "svelte";
|
||||
|
|
@ -86,11 +87,16 @@
|
|||
|
||||
$: {
|
||||
$showType;
|
||||
$showNsfw;
|
||||
setTimeout(() => dispatch("slideToClosest"), 100);
|
||||
}
|
||||
</script>
|
||||
|
||||
<ShowTypeChooser />
|
||||
<div class="flex">
|
||||
<ShowNsfwChooser />
|
||||
|
||||
<ShowTypeChooser />
|
||||
</div>
|
||||
|
||||
<div class="grid" class:list={$showType == "list"}>
|
||||
{#each mangaRelations.filter(t => $showNsfw !== "hide" || ["safe", "suggestive"].includes(t.attributes.contentRating)) as manga (manga.id)}
|
||||
|
|
@ -109,6 +115,11 @@
|
|||
</div>
|
||||
|
||||
<style>
|
||||
.flex {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, 11rem);
|
||||
|
|
|
|||
Loading…
Reference in a new issue